From d9c292e8bbd51c84cb9ecd86cb89b8a1b35a2a82 Mon Sep 17 00:00:00 2001 From: "Stephen R. van den Berg" Date: Sun, 27 Apr 2008 19:32:46 +0200 Subject: [PATCH 001/202] Simplify and fix --first-parent implementation The purpose of --first-parent is to view the tree without looking at side branche. This is accomplished by pretending there are no other parents than the first parent when encountering a merge. The current code marks the other parents as seen, which means that the tree traversal will behave differently depending on the order merges are handled. When a fast forward is artificially recorded as a merge, ----- / \ D---E---F---G master the current first-parent code considers E to be seen and stops the traversal after showing G and F. Signed-off-by: Stephen R. van den Berg Signed-off-by: Junio C Hamano --- revision.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/revision.c b/revision.c index 4231ea2cce..bcfcd2a82b 100644 --- a/revision.c +++ b/revision.c @@ -415,7 +415,6 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit, str { struct commit_list *parent = commit->parents; unsigned left_flag; - int add, rest; if (commit->object.flags & ADDED) return 0; @@ -462,19 +461,18 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit, str left_flag = (commit->object.flags & SYMMETRIC_LEFT); - rest = !revs->first_parent_only; - for (parent = commit->parents, add = 1; parent; add = rest) { + for (parent = commit->parents; parent; parent = parent->next) { struct commit *p = parent->item; - parent = parent->next; if (parse_commit(p) < 0) return -1; p->object.flags |= left_flag; if (p->object.flags & SEEN) continue; p->object.flags |= SEEN; - if (add) - insert_by_date(p, list); + insert_by_date(p, list); + if(revs->first_parent_only) + break; } return 0; } From 3613f9b4c02c3e3d35f4c6c06e40d5c176e7eb6d Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 2 May 2008 15:11:45 -0400 Subject: [PATCH 002/202] pack-objects: small cleanup Better encapsulate delta creation for writing. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 777f272668..8691c99518 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -102,21 +102,24 @@ static uint32_t written, written_delta; static uint32_t reused, reused_delta; -static void *delta_against(void *buf, unsigned long size, struct object_entry *entry) +static void *get_delta(struct object_entry *entry) { - unsigned long othersize, delta_size; + unsigned long size, base_size, delta_size; + void *buf, *base_buf, *delta_buf; enum object_type type; - void *otherbuf = read_sha1_file(entry->delta->idx.sha1, &type, &othersize); - void *delta_buf; - if (!otherbuf) + buf = read_sha1_file(entry->idx.sha1, &type, &size); + if (!buf) + die("unable to read %s", sha1_to_hex(entry->idx.sha1)); + base_buf = read_sha1_file(entry->delta->idx.sha1, &type, &base_size); + if (!base_buf) die("unable to read %s", sha1_to_hex(entry->delta->idx.sha1)); - delta_buf = diff_delta(otherbuf, othersize, + delta_buf = diff_delta(base_buf, base_size, buf, size, &delta_size, 0); - if (!delta_buf || delta_size != entry->delta_size) + if (!delta_buf || delta_size != entry->delta_size) die("delta size changed"); - free(buf); - free(otherbuf); + free(buf); + free(base_buf); return delta_buf; } @@ -223,7 +226,6 @@ static unsigned long write_object(struct sha1file *f, off_t write_offset) { unsigned long size; - enum object_type type; void *buf; unsigned char header[10]; unsigned char dheader[10]; @@ -281,10 +283,7 @@ static unsigned long write_object(struct sha1file *f, obj_type = (allow_ofs_delta && entry->delta->idx.offset) ? OBJ_OFS_DELTA : OBJ_REF_DELTA; } else { - buf = read_sha1_file(entry->idx.sha1, &type, &size); - if (!buf) - die("unable to read %s", sha1_to_hex(entry->idx.sha1)); - buf = delta_against(buf, size, entry); + buf = get_delta(entry); size = entry->delta_size; obj_type = (allow_ofs_delta && entry->delta->idx.offset) ? OBJ_OFS_DELTA : OBJ_REF_DELTA; From a7de713089082005d14679b694fd3999978fb735 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 2 May 2008 15:11:46 -0400 Subject: [PATCH 003/202] pack-objects: remove some double negative logic Parsing !no_reuse_delta everywhere makes my brain spend extra cycles wondering each time. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 8691c99518..afbf3ddc68 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -65,7 +65,8 @@ static struct pack_idx_entry **written_list; static uint32_t nr_objects, nr_alloc, nr_result, nr_written; static int non_empty; -static int no_reuse_delta, no_reuse_object, keep_unreachable, include_tag; +static int reuse_delta = 1, reuse_object = 1; +static int keep_unreachable, include_tag; static int local; static int incremental; static int allow_ofs_delta; @@ -251,7 +252,7 @@ static unsigned long write_object(struct sha1file *f, crc32_begin(f); obj_type = entry->type; - if (no_reuse_object) + if (!reuse_object) to_reuse = 0; /* explicit */ else if (!entry->in_pack) to_reuse = 0; /* can't reuse what we don't have */ @@ -1021,7 +1022,7 @@ static void check_object(struct object_entry *entry) unuse_pack(&w_curs); return; case OBJ_REF_DELTA: - if (!no_reuse_delta && !entry->preferred_base) + if (reuse_delta && !entry->preferred_base) base_ref = use_pack(p, &w_curs, entry->in_pack_offset + used, NULL); entry->in_pack_header_size = used + 20; @@ -1044,7 +1045,7 @@ static void check_object(struct object_entry *entry) die("delta base offset out of bound for %s", sha1_to_hex(entry->idx.sha1)); ofs = entry->in_pack_offset - ofs; - if (!no_reuse_delta && !entry->preferred_base) { + if (reuse_delta && !entry->preferred_base) { struct revindex_entry *revidx; revidx = find_pack_revindex(p, ofs); base_ref = nth_packed_object_sha1(p, revidx->nr); @@ -1232,7 +1233,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, * We do not bother to try a delta that we discarded * on an earlier try, but only when reusing delta data. */ - if (!no_reuse_delta && trg_entry->in_pack && + if (reuse_delta && trg_entry->in_pack && trg_entry->in_pack == src_entry->in_pack && trg_entry->in_pack_type != OBJ_REF_DELTA && trg_entry->in_pack_type != OBJ_OFS_DELTA) @@ -1687,7 +1688,7 @@ static void prepare_pack(int window, int depth) if (entry->delta) /* This happens if we decided to reuse existing - * delta from a pack. "!no_reuse_delta &&" is implied. + * delta from a pack. "reuse_delta &&" is implied. */ continue; @@ -2049,11 +2050,11 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) continue; } if (!strcmp("--no-reuse-delta", arg)) { - no_reuse_delta = 1; + reuse_delta = 0; continue; } if (!strcmp("--no-reuse-object", arg)) { - no_reuse_object = no_reuse_delta = 1; + reuse_object = reuse_delta = 0; continue; } if (!strcmp("--delta-base-offset", arg)) { From bcd7954e217994fafabe5106e56829c8a2cb7e99 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 2 May 2008 15:11:47 -0400 Subject: [PATCH 004/202] pack-objects: simplify the condition associated with --all-progress Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index afbf3ddc68..b4a63d2468 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -452,11 +452,10 @@ static void write_pack_file(void) struct sha1file *f; off_t offset, offset_one, last_obj_offset = 0; struct pack_header hdr; - int do_progress = progress >> pack_to_stdout; uint32_t nr_remaining = nr_result; time_t last_mtime = 0; - if (do_progress) + if (progress > pack_to_stdout) progress_state = start_progress("Writing objects", nr_result); written_list = xmalloc(nr_objects * sizeof(*written_list)); From 2c5ef824637cffa574bdce7149791a9cb520595e Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 2 May 2008 15:11:48 -0400 Subject: [PATCH 005/202] pack-objects: clean up write_object() a bit ... for improved readability. No functional changes. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 64 ++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index b4a63d2468..69ac27788b 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -226,41 +226,43 @@ static unsigned long write_object(struct sha1file *f, struct object_entry *entry, off_t write_offset) { - unsigned long size; + unsigned long size, limit; void *buf; - unsigned char header[10]; - unsigned char dheader[10]; + unsigned char header[10], dheader[10]; unsigned hdrlen; off_t datalen; - enum object_type obj_type; - int to_reuse = 0; - /* write limit if limited packsize and not first object */ - unsigned long limit = pack_size_limit && nr_written ? - pack_size_limit - write_offset : 0; - /* no if no delta */ - int usable_delta = !entry->delta ? 0 : - /* yes if unlimited packfile */ - !pack_size_limit ? 1 : - /* no if base written to previous pack */ - entry->delta->idx.offset == (off_t)-1 ? 0 : - /* otherwise double-check written to this - * pack, like we do below - */ - entry->delta->idx.offset ? 1 : 0; + enum object_type type; + int usable_delta, to_reuse; if (!pack_to_stdout) crc32_begin(f); - obj_type = entry->type; + type = entry->type; + + /* write limit if limited packsize and not first object */ + limit = pack_size_limit && nr_written ? + pack_size_limit - write_offset : 0; + + if (!entry->delta) + usable_delta = 0; /* no delta */ + else if (!pack_size_limit) + usable_delta = 1; /* unlimited packfile */ + else if (entry->delta->idx.offset == (off_t)-1) + usable_delta = 0; /* base was written to another pack */ + else if (entry->delta->idx.offset) + usable_delta = 1; /* base already exists in this pack */ + else + usable_delta = 0; /* base could end up in another pack */ + if (!reuse_object) to_reuse = 0; /* explicit */ else if (!entry->in_pack) to_reuse = 0; /* can't reuse what we don't have */ - else if (obj_type == OBJ_REF_DELTA || obj_type == OBJ_OFS_DELTA) + else if (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA) /* check_object() decided it for us ... */ to_reuse = usable_delta; /* ... but pack split may override that */ - else if (obj_type != entry->in_pack_type) + else if (type != entry->in_pack_type) to_reuse = 0; /* pack has delta which is unusable */ else if (entry->delta) to_reuse = 0; /* we want to pack afresh */ @@ -274,19 +276,19 @@ static unsigned long write_object(struct sha1file *f, unsigned long maxsize; void *out; if (!usable_delta) { - buf = read_sha1_file(entry->idx.sha1, &obj_type, &size); + buf = read_sha1_file(entry->idx.sha1, &type, &size); if (!buf) die("unable to read %s", sha1_to_hex(entry->idx.sha1)); } else if (entry->delta_data) { size = entry->delta_size; buf = entry->delta_data; entry->delta_data = NULL; - obj_type = (allow_ofs_delta && entry->delta->idx.offset) ? + type = (allow_ofs_delta && entry->delta->idx.offset) ? OBJ_OFS_DELTA : OBJ_REF_DELTA; } else { buf = get_delta(entry); size = entry->delta_size; - obj_type = (allow_ofs_delta && entry->delta->idx.offset) ? + type = (allow_ofs_delta && entry->delta->idx.offset) ? OBJ_OFS_DELTA : OBJ_REF_DELTA; } /* compress the data to store and put compressed length in datalen */ @@ -308,9 +310,9 @@ static unsigned long write_object(struct sha1file *f, * The object header is a byte of 'type' followed by zero or * more bytes of length. */ - hdrlen = encode_header(obj_type, size, header); + hdrlen = encode_header(type, size, header); - if (obj_type == OBJ_OFS_DELTA) { + if (type == OBJ_OFS_DELTA) { /* * Deltas with relative base contain an additional * encoding of the relative offset for the delta @@ -329,7 +331,7 @@ static unsigned long write_object(struct sha1file *f, sha1write(f, header, hdrlen); sha1write(f, dheader + pos, sizeof(dheader) - pos); hdrlen += sizeof(dheader) - pos; - } else if (obj_type == OBJ_REF_DELTA) { + } else if (type == OBJ_REF_DELTA) { /* * Deltas with a base reference contain * an additional 20 bytes for the base sha1. @@ -361,11 +363,11 @@ static unsigned long write_object(struct sha1file *f, off_t offset; if (entry->delta) { - obj_type = (allow_ofs_delta && entry->delta->idx.offset) ? + type = (allow_ofs_delta && entry->delta->idx.offset) ? OBJ_OFS_DELTA : OBJ_REF_DELTA; reused_delta++; } - hdrlen = encode_header(obj_type, entry->size, header); + hdrlen = encode_header(type, entry->size, header); offset = entry->in_pack_offset; revidx = find_pack_revindex(p, offset); datalen = revidx[1].offset - offset; @@ -374,7 +376,7 @@ static unsigned long write_object(struct sha1file *f, die("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1)); offset += entry->in_pack_header_size; datalen -= entry->in_pack_header_size; - if (obj_type == OBJ_OFS_DELTA) { + if (type == OBJ_OFS_DELTA) { off_t ofs = entry->idx.offset - entry->delta->idx.offset; unsigned pos = sizeof(dheader) - 1; dheader[pos] = ofs & 127; @@ -385,7 +387,7 @@ static unsigned long write_object(struct sha1file *f, sha1write(f, header, hdrlen); sha1write(f, dheader + pos, sizeof(dheader) - pos); hdrlen += sizeof(dheader) - pos; - } else if (obj_type == OBJ_REF_DELTA) { + } else if (type == OBJ_REF_DELTA) { if (limit && hdrlen + 20 + datalen + 20 >= limit) return 0; sha1write(f, header, hdrlen); From 30ebb40aa1acdccbebe3a04cfb198b2b6f033afc Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 2 May 2008 15:11:49 -0400 Subject: [PATCH 006/202] pack-objects: move compression code in a separate function A later patch will make use of that code too. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 53 +++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 69ac27788b..3395acada4 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -124,6 +124,32 @@ static void *get_delta(struct object_entry *entry) return delta_buf; } +static unsigned long do_compress(void **pptr, unsigned long size) +{ + z_stream stream; + void *in, *out; + unsigned long maxsize; + + memset(&stream, 0, sizeof(stream)); + deflateInit(&stream, pack_compression_level); + maxsize = deflateBound(&stream, size); + + in = *pptr; + out = xmalloc(maxsize); + *pptr = out; + + stream.next_in = in; + stream.avail_in = size; + stream.next_out = out; + stream.avail_out = maxsize; + while (deflate(&stream, Z_FINISH) == Z_OK) + ; /* nothing */ + deflateEnd(&stream); + + free(in); + return stream.total_out; +} + /* * The per-object header is a pretty dense thing, which is * - first byte: low four bits are "size", then three bits of "type", @@ -226,11 +252,10 @@ static unsigned long write_object(struct sha1file *f, struct object_entry *entry, off_t write_offset) { - unsigned long size, limit; + unsigned long size, limit, datalen; void *buf; unsigned char header[10], dheader[10]; unsigned hdrlen; - off_t datalen; enum object_type type; int usable_delta, to_reuse; @@ -272,9 +297,6 @@ static unsigned long write_object(struct sha1file *f, */ if (!to_reuse) { - z_stream stream; - unsigned long maxsize; - void *out; if (!usable_delta) { buf = read_sha1_file(entry->idx.sha1, &type, &size); if (!buf) @@ -291,20 +313,7 @@ static unsigned long write_object(struct sha1file *f, type = (allow_ofs_delta && entry->delta->idx.offset) ? OBJ_OFS_DELTA : OBJ_REF_DELTA; } - /* compress the data to store and put compressed length in datalen */ - memset(&stream, 0, sizeof(stream)); - deflateInit(&stream, pack_compression_level); - maxsize = deflateBound(&stream, size); - out = xmalloc(maxsize); - /* Compress it */ - stream.next_in = buf; - stream.avail_in = size; - stream.next_out = out; - stream.avail_out = maxsize; - while (deflate(&stream, Z_FINISH) == Z_OK) - /* nothing */; - deflateEnd(&stream); - datalen = stream.total_out; + datalen = do_compress(&buf, size); /* * The object header is a byte of 'type' followed by zero or @@ -324,7 +333,6 @@ static unsigned long write_object(struct sha1file *f, while (ofs >>= 7) dheader[--pos] = 128 | (--ofs & 127); if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) { - free(out); free(buf); return 0; } @@ -337,7 +345,6 @@ static unsigned long write_object(struct sha1file *f, * an additional 20 bytes for the base sha1. */ if (limit && hdrlen + 20 + datalen + 20 >= limit) { - free(out); free(buf); return 0; } @@ -346,14 +353,12 @@ static unsigned long write_object(struct sha1file *f, hdrlen += 20; } else { if (limit && hdrlen + datalen + 20 >= limit) { - free(out); free(buf); return 0; } sha1write(f, header, hdrlen); } - sha1write(f, out, datalen); - free(out); + sha1write(f, buf, datalen); free(buf); } else { From ed4a9031ea75858d6f8ec387993e8ff731e29d1a Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 2 May 2008 15:11:50 -0400 Subject: [PATCH 007/202] pack-objects: allow for early delta deflating When the delta data is cached in memory until it is written to a pack file on disk, it is best to compress it right away in find_deltas() for the following reasons: - we have to compress that data anyway; - this allows for caching more deltas with the same cache size limit; - compression is potentially threaded. This last point is especially relevant for SMP run time. For example, repacking the Linux repo on a quad core processor using 4 threads with all default settings produce the following results before this change: real 2m27.929s user 4m36.492s sys 0m3.091s And with this change applied: real 2m13.787s user 4m37.486s sys 0m3.159s So the actual execution time stayed more or less the same but the wall clock time is shorter. This is however not a good thing to do when generating a pack for network transmission. In that case, the network is most likely to throttle the data throughput, so it is best to make find_deltas() faster in order to start writing data ASAP since we can afford spending more time between writes to compress the data at that point. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 3395acada4..4a0c9c907b 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -43,6 +43,7 @@ struct object_entry { */ void *delta_data; /* cached delta (uncompressed) */ unsigned long delta_size; /* delta data size (uncompressed) */ + unsigned long z_delta_size; /* delta data size (compressed) */ unsigned int hash; /* name hint hash */ enum object_type type; enum object_type in_pack_type; /* could be delta */ @@ -301,6 +302,13 @@ static unsigned long write_object(struct sha1file *f, buf = read_sha1_file(entry->idx.sha1, &type, &size); if (!buf) die("unable to read %s", sha1_to_hex(entry->idx.sha1)); + /* + * make sure no cached delta data remains from a + * previous attempt before a pack split occured. + */ + free(entry->delta_data); + entry->delta_data = NULL; + entry->z_delta_size = 0; } else if (entry->delta_data) { size = entry->delta_size; buf = entry->delta_data; @@ -313,7 +321,11 @@ static unsigned long write_object(struct sha1file *f, type = (allow_ofs_delta && entry->delta->idx.offset) ? OBJ_OFS_DELTA : OBJ_REF_DELTA; } - datalen = do_compress(&buf, size); + + if (entry->z_delta_size) + datalen = entry->z_delta_size; + else + datalen = do_compress(&buf, size); /* * The object header is a byte of 'type' followed by zero or @@ -1447,6 +1459,29 @@ static void find_deltas(struct object_entry **list, unsigned *list_size, best_base = other_idx; } + /* + * If we decided to cache the delta data, then it is best + * to compress it right away. First because we have to do + * it anyway, and doing it here while we're threaded will + * save a lot of time in the non threaded write phase, + * as well as allow for caching more deltas within + * the same cache size limit. + * ... + * But only if not writing to stdout, since in that case + * the network is most likely throttling writes anyway, + * and therefore it is best to go to the write phase ASAP + * instead, as we can afford spending more time compressing + * between writes at that moment. + */ + if (entry->delta_data && !pack_to_stdout) { + entry->z_delta_size = do_compress(&entry->delta_data, + entry->delta_size); + cache_lock(); + delta_cache_size -= entry->delta_size; + delta_cache_size += entry->z_delta_size; + cache_unlock(); + } + /* if we made n a delta, and if n is already at max * depth, leaving it in the window is pointless. we * should evict it first. From 70baf5d41a933c7972375ae2583aad8c8b92633f Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 2 May 2008 15:11:51 -0400 Subject: [PATCH 008/202] pack-objects: fix early eviction for max depth delta objects The 'depth' variable doesn't reflect the actual maximum depth used when other objects already depend on the current one. 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 4a0c9c907b..e20851e1c9 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1486,7 +1486,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size, * depth, leaving it in the window is pointless. we * should evict it first. */ - if (entry->delta && depth <= n->depth) + if (entry->delta && max_depth <= n->depth) continue; /* From a83619d692deeb2565335144078465acb2dd1457 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 28 Apr 2008 11:32:12 -0400 Subject: [PATCH 009/202] add special "matching refs" refspec This patch provides a way to specify "push matching heads" using a special refspec ":". This is useful because it allows "push = +:" as a way to specify that matching refs will be pushed but, in addition, forced updates will be allowed, which was not possible before. Signed-off-by: Paolo Bonzini Signed-off-by: Junio C Hamano --- Documentation/git-push.txt | 13 +++--- builtin-send-pack.c | 10 ++++- remote.c | 81 +++++++++++++++++++++++++------------- remote.h | 1 + t/t5511-refspec.sh | 5 ++- t/t5516-fetch-push.sh | 41 +++++++++++++++++++ 6 files changed, 116 insertions(+), 35 deletions(-) diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 0585949137..7d31263aa2 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -46,12 +46,6 @@ specified, the same ref that referred to locally). If the optional leading plus `+` is used, the remote ref is updated even if it does not result in a fast forward update. + -Note: If no explicit refspec is found, (that is neither -on the command line nor in any Push line of the -corresponding remotes file---see below), then "matching" heads are -pushed: for every head that exists on the local side, the remote side is -updated if a head of the same name already exists on the remote side. -+ `tag ` means the same as `refs/tags/:refs/tags/`. + A parameter without a colon pushes the from the source @@ -59,6 +53,13 @@ repository to the destination repository under the same name. + Pushing an empty allows you to delete the ref from the remote repository. ++ +The special refspec `:` (or `+:` to allow non-fast forward updates) +directs git to push "matching" heads: for every head that exists on +the local side, the remote side is updated if a head of the same name +already exists on the remote side. This is the default operation mode +if no explicit refspec is found (that is neither on the command line +nor in any Push line of the corresponding remotes file---see below). \--all:: Instead of naming each ref to push, specifies that all diff --git a/builtin-send-pack.c b/builtin-send-pack.c index bb9c33a650..d76260c09e 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -537,9 +537,17 @@ static void verify_remote_names(int nr_heads, const char **heads) int i; for (i = 0; i < nr_heads; i++) { + const char *local = heads[i]; const char *remote = strrchr(heads[i], ':'); - remote = remote ? (remote + 1) : heads[i]; + if (*local == '+') + local++; + + /* A matching refspec is okay. */ + if (remote == local && remote[1] == '\0') + continue; + + remote = remote ? (remote + 1) : local; switch (check_ref_format(remote)) { case 0: /* ok */ case CHECK_REF_FORMAT_ONELEVEL: diff --git a/remote.c b/remote.c index 2d9af4023e..7c496f981e 100644 --- a/remote.c +++ b/remote.c @@ -429,6 +429,16 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp } rhs = strrchr(lhs, ':'); + + /* + * Before going on, special case ":" (or "+:") as a refspec + * for matching refs. + */ + if (!fetch && rhs == lhs && rhs[1] == '\0') { + rs[i].matching = 1; + continue; + } + if (rhs) { rhs++; rlen = strlen(rhs); @@ -842,7 +852,7 @@ static int match_explicit(struct ref *src, struct ref *dst, const char *dst_value = rs->dst; char *dst_guess; - if (rs->pattern) + if (rs->pattern || rs->matching) return errs; matched_src = matched_dst = NULL; @@ -932,13 +942,23 @@ static const struct refspec *check_pattern_match(const struct refspec *rs, const struct ref *src) { int i; + int matching_refs = -1; for (i = 0; i < rs_nr; i++) { + if (rs[i].matching && + (matching_refs == -1 || rs[i].force)) { + matching_refs = i; + continue; + } + if (rs[i].pattern && !prefixcmp(src->name, rs[i].src) && src->name[strlen(rs[i].src)] == '/') return rs + i; } - return NULL; + if (matching_refs != -1) + return rs + matching_refs; + else + return NULL; } /* @@ -949,11 +969,16 @@ static const struct refspec *check_pattern_match(const struct refspec *rs, int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, int nr_refspec, const char **refspec, int flags) { - struct refspec *rs = - parse_push_refspec(nr_refspec, (const char **) refspec); + struct refspec *rs; int send_all = flags & MATCH_REFS_ALL; int send_mirror = flags & MATCH_REFS_MIRROR; + static const char *default_refspec[] = { ":", 0 }; + if (!nr_refspec) { + nr_refspec = 1; + refspec = default_refspec; + } + rs = parse_push_refspec(nr_refspec, (const char **) refspec); if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec)) return -1; @@ -964,48 +989,50 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, char *dst_name; if (src->peer_ref) continue; - if (nr_refspec) { - pat = check_pattern_match(rs, nr_refspec, src); - if (!pat) - continue; - } - else if (!send_mirror && prefixcmp(src->name, "refs/heads/")) + + pat = check_pattern_match(rs, nr_refspec, src); + if (!pat) + continue; + + if (pat->matching) { /* * "matching refs"; traditionally we pushed everything * including refs outside refs/heads/ hierarchy, but * that does not make much sense these days. */ - continue; + if (!send_mirror && prefixcmp(src->name, "refs/heads/")) + continue; + dst_name = xstrdup(src->name); - if (pat) { + } else { const char *dst_side = pat->dst ? pat->dst : pat->src; dst_name = xmalloc(strlen(dst_side) + strlen(src->name) - strlen(pat->src) + 2); strcpy(dst_name, dst_side); strcat(dst_name, src->name + strlen(pat->src)); - } else - dst_name = xstrdup(src->name); + } dst_peer = find_ref_by_name(dst, dst_name); - if (dst_peer && dst_peer->peer_ref) - /* We're already sending something to this ref. */ - goto free_name; + if (dst_peer) { + if (dst_peer->peer_ref) + /* We're already sending something to this ref. */ + goto free_name; + + } else { + if (pat->matching && !(send_all || send_mirror)) + /* + * Remote doesn't have it, and we have no + * explicit pattern, and we don't have + * --all nor --mirror. + */ + goto free_name; - if (!dst_peer && !nr_refspec && !(send_all || send_mirror)) - /* - * Remote doesn't have it, and we have no - * explicit pattern, and we don't have - * --all nor --mirror. - */ - goto free_name; - if (!dst_peer) { /* Create a new one and link it */ dst_peer = make_linked_ref(dst_name, dst_tail); hashcpy(dst_peer->new_sha1, src->new_sha1); } dst_peer->peer_ref = src; - if (pat) - dst_peer->force = pat->force; + dst_peer->force = pat->force; free_name: free(dst_name); } diff --git a/remote.h b/remote.h index a38774bbdc..f7b7be84b4 100644 --- a/remote.h +++ b/remote.h @@ -46,6 +46,7 @@ int remote_has_url(struct remote *remote, const char *url); struct refspec { unsigned force : 1; unsigned pattern : 1; + unsigned matching : 1; char *src; char *dst; diff --git a/t/t5511-refspec.sh b/t/t5511-refspec.sh index 670a8f1c99..22ba380034 100755 --- a/t/t5511-refspec.sh +++ b/t/t5511-refspec.sh @@ -23,10 +23,13 @@ test_refspec () { } test_refspec push '' invalid -test_refspec push ':' invalid +test_refspec push ':' +test_refspec push '::' invalid +test_refspec push '+:' test_refspec fetch '' test_refspec fetch ':' +test_refspec fetch '::' invalid test_refspec push 'refs/heads/*:refs/remotes/frotz/*' test_refspec push 'refs/heads/*:refs/remotes/frotz' invalid diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 0a757d5b9f..53e47e1198 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -165,6 +165,47 @@ test_expect_success 'push with matching heads' ' ' +test_expect_success 'push with matching heads on the command line' ' + + mk_test heads/master && + git push testrepo : && + check_push_result $the_commit heads/master + +' + +test_expect_success 'failed (non-fast-forward) push with matching heads' ' + + mk_test heads/master && + git push testrepo : && + git commit --amend -massaged && + ! git push testrepo && + check_push_result $the_commit heads/master && + git reset --hard $the_commit + +' + +test_expect_success 'push --force with matching heads' ' + + mk_test heads/master && + git push testrepo : && + git commit --amend -massaged && + git push --force testrepo && + ! check_push_result $the_commit heads/master && + git reset --hard $the_commit + +' + +test_expect_success 'push with matching heads and forced update' ' + + mk_test heads/master && + git push testrepo : && + git commit --amend -massaged && + git push testrepo +: && + ! check_push_result $the_commit heads/master && + git reset --hard $the_commit + +' + test_expect_success 'push with no ambiguity (1)' ' mk_test heads/master && From 2d5c298f91b4b76a8b51b9b66283ef5a872736a0 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Thu, 17 Apr 2008 19:32:22 -0400 Subject: [PATCH 010/202] Mark the list of refs to fetch as const Fetching the objects doesn't actually modify the list in any of the code paths, so this will allow code that fetches the entire (const) list of available refs to just pass the list in directly. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- transport.c | 16 ++++++++-------- transport.h | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/transport.c b/transport.c index 393e0e8fe2..a3b805258f 100644 --- a/transport.c +++ b/transport.c @@ -203,7 +203,7 @@ static struct ref *get_refs_via_rsync(struct transport *transport) } static int fetch_objs_via_rsync(struct transport *transport, - int nr_objs, struct ref **to_fetch) + int nr_objs, const struct ref **to_fetch) { struct strbuf buf = STRBUF_INIT; struct child_process rsync; @@ -350,7 +350,7 @@ static int rsync_transport_push(struct transport *transport, #ifndef NO_CURL /* http fetch is the only user */ static int fetch_objs_via_walker(struct transport *transport, - int nr_objs, struct ref **to_fetch) + int nr_objs, const struct ref **to_fetch) { char *dest = xstrdup(transport->url); struct walker *walker = transport->data; @@ -504,7 +504,7 @@ static struct ref *get_refs_via_curl(struct transport *transport) } static int fetch_objs_via_curl(struct transport *transport, - int nr_objs, struct ref **to_fetch) + int nr_objs, const struct ref **to_fetch) { if (!transport->data) transport->data = get_http_walker(transport->url, @@ -542,7 +542,7 @@ static struct ref *get_refs_from_bundle(struct transport *transport) } static int fetch_refs_from_bundle(struct transport *transport, - int nr_heads, struct ref **to_fetch) + int nr_heads, const struct ref **to_fetch) { struct bundle_transport_data *data = transport->data; return unbundle(&data->header, data->fd); @@ -616,7 +616,7 @@ static struct ref *get_refs_via_connect(struct transport *transport) } static int fetch_refs_via_pack(struct transport *transport, - int nr_heads, struct ref **to_fetch) + int nr_heads, const struct ref **to_fetch) { struct git_transport_data *data = transport->data; char **heads = xmalloc(nr_heads * sizeof(*heads)); @@ -784,12 +784,12 @@ const struct ref *transport_get_remote_refs(struct transport *transport) return transport->remote_refs; } -int transport_fetch_refs(struct transport *transport, struct ref *refs) +int transport_fetch_refs(struct transport *transport, const struct ref *refs) { int rc; int nr_heads = 0, nr_alloc = 0; - struct ref **heads = NULL; - struct ref *rm; + const struct ref **heads = NULL; + const struct ref *rm; for (rm = refs; rm; rm = rm->next) { if (rm->peer_ref && diff --git a/transport.h b/transport.h index 8abfc0ae60..d0b52053ff 100644 --- a/transport.h +++ b/transport.h @@ -19,7 +19,7 @@ struct transport { const char *value); struct ref *(*get_refs_list)(struct transport *transport); - int (*fetch)(struct transport *transport, int refs_nr, struct ref **refs); + int (*fetch)(struct transport *transport, int refs_nr, const struct ref **refs); int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags); int (*disconnect)(struct transport *connection); @@ -68,7 +68,7 @@ int transport_push(struct transport *connection, const struct ref *transport_get_remote_refs(struct transport *transport); -int transport_fetch_refs(struct transport *transport, struct ref *refs); +int transport_fetch_refs(struct transport *transport, const struct ref *refs); void transport_unlock_pack(struct transport *transport); int transport_disconnect(struct transport *transport); From ea3cd5c7c63fadacd66c364ae4b8c6d01e5809b1 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Thu, 17 Apr 2008 19:32:26 -0400 Subject: [PATCH 011/202] Add a lockfile function to append to a file This takes care of copying the original contents into the replacement file after the lock is held, so that concurrent additions can't miss each other's changes. [jc: munged to drop mmap in favor of copy_file.] Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- cache.h | 1 + lockfile.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/cache.h b/cache.h index 5a28dddec9..396eabf6ed 100644 --- a/cache.h +++ b/cache.h @@ -391,6 +391,7 @@ struct lock_file { char filename[PATH_MAX]; }; extern int hold_lock_file_for_update(struct lock_file *, const char *path, int); +extern int hold_lock_file_for_append(struct lock_file *, const char *path, int); extern int commit_lock_file(struct lock_file *); extern int hold_locked_index(struct lock_file *, int); diff --git a/lockfile.c b/lockfile.c index 663f18f9c4..e9e0095e9f 100644 --- a/lockfile.c +++ b/lockfile.c @@ -160,6 +160,34 @@ int hold_lock_file_for_update(struct lock_file *lk, const char *path, int die_on return fd; } +int hold_lock_file_for_append(struct lock_file *lk, const char *path, int die_on_error) +{ + int fd, orig_fd; + + fd = lock_file(lk, path); + if (fd < 0) { + if (die_on_error) + die("unable to create '%s.lock': %s", path, strerror(errno)); + return fd; + } + + orig_fd = open(path, O_RDONLY); + if (orig_fd < 0) { + if (errno != ENOENT) { + if (die_on_error) + die("cannot open '%s' for copying", path); + close(fd); + return error("cannot open '%s' for copying", path); + } + } else if (copy_fd(orig_fd, fd)) { + if (die_on_error) + exit(128); + close(fd); + return -1; + } + return fd; +} + int close_lock_file(struct lock_file *lk) { int fd = lk->fd; From bef70b22ba63d71c1ae2e070e64ff9863ea1ad14 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Thu, 17 Apr 2008 19:32:30 -0400 Subject: [PATCH 012/202] Add a library function to add an alternate to the alternates file This is in the core so that, if the alternates file has already been read, the addition can be parsed and put into effect for the current process. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- cache.h | 1 + sha1_file.c | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/cache.h b/cache.h index 396eabf6ed..9da9179afd 100644 --- a/cache.h +++ b/cache.h @@ -599,6 +599,7 @@ extern struct alternate_object_database { char base[FLEX_ARRAY]; /* more */ } *alt_odb_list; extern void prepare_alt_odb(void); +extern void add_to_alternates_file(const char *reference); struct pack_window { struct pack_window *next; diff --git a/sha1_file.c b/sha1_file.c index 3516777bc7..d21e23b464 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -380,6 +380,18 @@ static void read_info_alternates(const char * relative_base, int depth) munmap(map, mapsz); } +void add_to_alternates_file(const char *reference) +{ + struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); + int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), 1); + char *alt = mkpath("%s/objects\n", reference); + write_or_die(fd, alt, strlen(alt)); + if (commit_lock_file(lock)) + die("could not close alternates file"); + if (alt_odb_tail) + link_alt_odb_entries(alt, alt + strlen(alt), '\n', NULL, 0); +} + void prepare_alt_odb(void) { const char *alt; From e0aaa29ff324c40a6428d5cc26867392eedf94ad Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Thu, 17 Apr 2008 19:32:35 -0400 Subject: [PATCH 013/202] Have a constant extern refspec for "--tags" The refspec refs/tags/*:refs/tags/* is sufficiently common and generic to merit having a constant instead of generating it as needed. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- builtin-fetch.c | 10 ++-------- remote.c | 9 +++++++++ remote.h | 2 ++ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/builtin-fetch.c b/builtin-fetch.c index 167f948036..30aa42df6e 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -127,14 +127,8 @@ static struct ref *get_ref_map(struct transport *transport, /* Merge everything on the command line, but not --tags */ for (rm = ref_map; rm; rm = rm->next) rm->merge = 1; - if (tags == TAGS_SET) { - struct refspec refspec; - refspec.src = "refs/tags/"; - refspec.dst = "refs/tags/"; - refspec.pattern = 1; - refspec.force = 0; - get_fetch_map(remote_refs, &refspec, &tail, 0); - } + if (tags == TAGS_SET) + get_fetch_map(remote_refs, tag_refspec, &tail, 0); } else { /* Use the defaults */ struct remote *remote = transport->remote; diff --git a/remote.c b/remote.c index 2d9af4023e..9cb40afd0e 100644 --- a/remote.c +++ b/remote.c @@ -2,6 +2,15 @@ #include "remote.h" #include "refs.h" +static struct refspec s_tag_refspec = { + 0, + 1, + "refs/tags/", + "refs/tags/" +}; + +const struct refspec *tag_refspec = &s_tag_refspec; + struct counted_string { size_t len; const char *s; diff --git a/remote.h b/remote.h index a38774bbdc..f0a79de210 100644 --- a/remote.h +++ b/remote.h @@ -51,6 +51,8 @@ struct refspec { char *dst; }; +extern const struct refspec *tag_refspec; + struct ref *alloc_ref(unsigned namelen); struct ref *copy_ref_list(const struct ref *ref); From e142a3c61d22c3a3c081cb2c693e4d3725c21522 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Sun, 27 Apr 2008 13:39:24 -0400 Subject: [PATCH 014/202] Allow for having for_each_ref() list extra refs These refs can be anything, but they are most likely useful as pointing to objects that you know are in the object database but don't have any regular refs for. For example, when cloning with --reference, the refs in this repository should be listed as objects that we have, even though we don't have refs in our newly-created repository for them yet. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- refs.c | 18 ++++++++++++++++++ refs.h | 9 +++++++++ 2 files changed, 27 insertions(+) diff --git a/refs.c b/refs.c index 4db73ed8f0..8300cc1b54 100644 --- a/refs.c +++ b/refs.c @@ -159,6 +159,8 @@ static struct cached_refs { } cached_refs; static struct ref_list *current_ref; +static struct ref_list *extra_refs; + static void free_ref_list(struct ref_list *list) { struct ref_list *next; @@ -215,6 +217,17 @@ static void read_packed_refs(FILE *f, struct cached_refs *cached_refs) cached_refs->packed = sort_ref_list(list); } +void add_extra_ref(const char *name, const unsigned char *sha1, int flag) +{ + extra_refs = add_ref(name, sha1, flag, extra_refs, NULL); +} + +void clear_extra_refs(void) +{ + free_ref_list(extra_refs); + extra_refs = NULL; +} + static struct ref_list *get_packed_refs(void) { if (!cached_refs.did_packed) { @@ -536,6 +549,11 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, struct ref_list *packed = get_packed_refs(); struct ref_list *loose = get_loose_refs(); + struct ref_list *extra; + + for (extra = extra_refs; extra; extra = extra->next) + retval = do_one_ref(base, fn, trim, cb_data, extra); + while (packed && loose) { struct ref_list *entry; int cmp = strcmp(packed->name, loose->name); diff --git a/refs.h b/refs.h index 06abee1526..06ad260556 100644 --- a/refs.h +++ b/refs.h @@ -24,6 +24,15 @@ extern int for_each_tag_ref(each_ref_fn, void *); extern int for_each_branch_ref(each_ref_fn, void *); extern int for_each_remote_ref(each_ref_fn, void *); +/* + * Extra refs will be listed by for_each_ref() before any actual refs + * for the duration of this process or until clear_extra_refs() is + * called. Only extra refs added before for_each_ref() is called will + * be listed on a given call of for_each_ref(). + */ +extern void add_extra_ref(const char *refname, const unsigned char *sha1, int flags); +extern void clear_extra_refs(void); + extern int peel_ref(const char *, unsigned char *); /** Locks a "refs/" ref returning the lock on success and NULL on failure. **/ From 19757d80e522e9f04c125ad5977c3a79db99b192 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Sun, 27 Apr 2008 13:39:21 -0400 Subject: [PATCH 015/202] Add a function to set a non-default work tree This function may only be used before the work tree is used. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- cache.h | 1 + environment.c | 24 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/cache.h b/cache.h index 9da9179afd..aba029315c 100644 --- a/cache.h +++ b/cache.h @@ -311,6 +311,7 @@ extern char *get_index_file(void); extern char *get_graft_file(void); extern int set_git_dir(const char *path); extern const char *get_git_work_tree(void); +extern void set_git_work_tree(const char *tree); #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES" diff --git a/environment.c b/environment.c index 6739a3f417..0bff432049 100644 --- a/environment.c +++ b/environment.c @@ -41,7 +41,7 @@ enum branch_track git_branch_track = BRANCH_TRACK_REMOTE; /* This is set by setup_git_dir_gently() and/or git_default_config() */ char *git_work_tree_cfg; -static const char *work_tree; +static char *work_tree; static const char *git_dir; static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file; @@ -81,10 +81,26 @@ const char *get_git_dir(void) return git_dir; } +static int git_work_tree_initialized; + +/* + * Note. This works only before you used a work tree. This was added + * primarily to support git-clone to work in a new repository it just + * created, and is not meant to flip between different work trees. + */ +void set_git_work_tree(const char *new_work_tree) +{ + if (is_bare_repository_cfg >= 0) + die("cannot set work tree after initialization"); + git_work_tree_initialized = 1; + free(work_tree); + work_tree = xstrdup(make_absolute_path(new_work_tree)); + is_bare_repository_cfg = 0; +} + const char *get_git_work_tree(void) { - static int initialized = 0; - if (!initialized) { + if (!git_work_tree_initialized) { work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT); /* core.bare = true overrides implicit and config work tree */ if (!work_tree && is_bare_repository_cfg < 1) { @@ -94,7 +110,7 @@ const char *get_git_work_tree(void) work_tree = xstrdup(make_absolute_path(git_path(work_tree))); } else if (work_tree) work_tree = xstrdup(make_absolute_path(work_tree)); - initialized = 1; + git_work_tree_initialized = 1; if (work_tree) is_bare_repository_cfg = 0; } From f225aeb278eb1c8c028cd2ff1fdfe7a45067b8e1 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Sun, 27 Apr 2008 13:39:27 -0400 Subject: [PATCH 016/202] Provide API access to init_db() The caller first calls set_git_dir() to specify the GIT_DIR, and then calls init_db() to initialize it. This also cleans up various parts of the code to account for the fact that everything is done with GIT_DIR set, so it's unnecessary to pass the specified directory around. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- builtin-init-db.c | 251 ++++++++++++++++++++++++---------------------- cache.h | 4 + 2 files changed, 134 insertions(+), 121 deletions(-) diff --git a/builtin-init-db.c b/builtin-init-db.c index a76f5d3474..5650685e4e 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -104,12 +104,14 @@ static void copy_templates_1(char *path, int baselen, } } -static void copy_templates(const char *git_dir, int len, const char *template_dir) +static void copy_templates(const char *template_dir) { char path[PATH_MAX]; char template_path[PATH_MAX]; int template_len; DIR *dir; + const char *git_dir = get_git_dir(); + int len = strlen(git_dir); if (!template_dir) template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT); @@ -156,6 +158,8 @@ static void copy_templates(const char *git_dir, int len, const char *template_di } memcpy(path, git_dir, len); + if (len && path[len - 1] != '/') + path[len++] = '/'; path[len] = 0; copy_templates_1(path, len, template_path, template_len, @@ -163,8 +167,9 @@ static void copy_templates(const char *git_dir, int len, const char *template_di closedir(dir); } -static int create_default_files(const char *git_dir, const char *template_path) +static int create_default_files(const char *template_path) { + const char *git_dir = get_git_dir(); unsigned len = strlen(git_dir); static char path[PATH_MAX]; struct stat st1; @@ -183,19 +188,15 @@ static int create_default_files(const char *git_dir, const char *template_path) /* * Create .git/refs/{heads,tags} */ - strcpy(path + len, "refs"); - safe_create_dir(path, 1); - strcpy(path + len, "refs/heads"); - safe_create_dir(path, 1); - strcpy(path + len, "refs/tags"); - safe_create_dir(path, 1); + safe_create_dir(git_path("refs"), 1); + safe_create_dir(git_path("refs/heads"), 1); + safe_create_dir(git_path("refs/tags"), 1); /* First copy the templates -- we might have the default * config file there, in which case we would want to read * from it after installing. */ - path[len] = 0; - copy_templates(path, len, template_path); + copy_templates(template_path); git_config(git_default_config); @@ -204,14 +205,10 @@ static int create_default_files(const char *git_dir, const char *template_path) * shared-repository settings, we would need to fix them up. */ if (shared_repository) { - path[len] = 0; - adjust_shared_perm(path); - strcpy(path + len, "refs"); - adjust_shared_perm(path); - strcpy(path + len, "refs/heads"); - adjust_shared_perm(path); - strcpy(path + len, "refs/tags"); - adjust_shared_perm(path); + adjust_shared_perm(get_git_dir()); + adjust_shared_perm(git_path("refs")); + adjust_shared_perm(git_path("refs/heads")); + adjust_shared_perm(git_path("refs/tags")); } /* @@ -251,8 +248,10 @@ static int create_default_files(const char *git_dir, const char *template_path) /* allow template config file to override the default */ if (log_all_ref_updates == -1) git_config_set("core.logallrefupdates", "true"); - if (work_tree != git_work_tree_cfg) + if (prefixcmp(git_dir, work_tree) || + strcmp(git_dir + strlen(work_tree), "/.git")) { git_config_set("core.worktree", work_tree); + } } /* Check if symlink is supported in the work tree */ @@ -272,106 +271,13 @@ static int create_default_files(const char *git_dir, const char *template_path) return reinit; } -static void guess_repository_type(const char *git_dir) +int init_db(const char *template_dir, unsigned int flags) { - char cwd[PATH_MAX]; - const char *slash; - - if (0 <= is_bare_repository_cfg) - return; - if (!git_dir) - return; - - /* - * "GIT_DIR=. git init" is always bare. - * "GIT_DIR=`pwd` git init" too. - */ - if (!strcmp(".", git_dir)) - goto force_bare; - if (!getcwd(cwd, sizeof(cwd))) - die("cannot tell cwd"); - if (!strcmp(git_dir, cwd)) - goto force_bare; - /* - * "GIT_DIR=.git or GIT_DIR=something/.git is usually not. - */ - if (!strcmp(git_dir, ".git")) - return; - slash = strrchr(git_dir, '/'); - if (slash && !strcmp(slash, "/.git")) - return; - - /* - * Otherwise it is often bare. At this point - * we are just guessing. - */ - force_bare: - is_bare_repository_cfg = 1; - return; -} - -static const char init_db_usage[] = -"git-init [-q | --quiet] [--template=] [--shared]"; - -/* - * If you want to, you can share the DB area with any number of branches. - * That has advantages: you can save space by sharing all the SHA1 objects. - * On the other hand, it might just make lookup slower and messier. You - * be the judge. The default case is to have one DB per managed directory. - */ -int cmd_init_db(int argc, const char **argv, const char *prefix) -{ - const char *git_dir; const char *sha1_dir; - const char *template_dir = NULL; char *path; - int len, i, reinit; - int quiet = 0; + int len, reinit; - for (i = 1; i < argc; i++, argv++) { - const char *arg = argv[1]; - if (!prefixcmp(arg, "--template=")) - template_dir = arg+11; - else if (!strcmp(arg, "--shared")) - shared_repository = PERM_GROUP; - else if (!prefixcmp(arg, "--shared=")) - shared_repository = git_config_perm("arg", arg+9); - else if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet")) - quiet = 1; - else - usage(init_db_usage); - } - - /* - * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR - * without --bare. Catch the error early. - */ - git_dir = getenv(GIT_DIR_ENVIRONMENT); - if ((!git_dir || is_bare_repository_cfg == 1) - && getenv(GIT_WORK_TREE_ENVIRONMENT)) - die("%s (or --work-tree=) not allowed without " - "specifying %s (or --git-dir=)", - GIT_WORK_TREE_ENVIRONMENT, - GIT_DIR_ENVIRONMENT); - - guess_repository_type(git_dir); - - if (is_bare_repository_cfg <= 0) { - git_work_tree_cfg = xcalloc(PATH_MAX, 1); - if (!getcwd(git_work_tree_cfg, PATH_MAX)) - die ("Cannot access current working directory."); - if (access(get_git_work_tree(), X_OK)) - die ("Cannot access work tree '%s'", - get_git_work_tree()); - } - - /* - * Set up the default .git directory contents - */ - git_dir = getenv(GIT_DIR_ENVIRONMENT); - if (!git_dir) - git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; - safe_create_dir(git_dir, 0); + safe_create_dir(get_git_dir(), 0); /* Check to see if the repository version is right. * Note that a newly created repository does not have @@ -380,11 +286,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) */ check_repository_format(); - reinit = create_default_files(git_dir, template_dir); + reinit = create_default_files(template_dir); - /* - * And set up the object store. - */ sha1_dir = get_object_directory(); len = strlen(sha1_dir); path = xmalloc(len + 40); @@ -414,11 +317,117 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) git_config_set("receive.denyNonFastforwards", "true"); } - if (!quiet) + if (!(flags & INIT_DB_QUIET)) printf("%s%s Git repository in %s/\n", reinit ? "Reinitialized existing" : "Initialized empty", shared_repository ? " shared" : "", - git_dir); + get_git_dir()); return 0; } + +static int guess_repository_type(const char *git_dir) +{ + char cwd[PATH_MAX]; + const char *slash; + + /* + * "GIT_DIR=. git init" is always bare. + * "GIT_DIR=`pwd` git init" too. + */ + if (!strcmp(".", git_dir)) + return 1; + if (!getcwd(cwd, sizeof(cwd))) + die("cannot tell cwd"); + if (!strcmp(git_dir, cwd)) + return 1; + /* + * "GIT_DIR=.git or GIT_DIR=something/.git is usually not. + */ + if (!strcmp(git_dir, ".git")) + return 0; + slash = strrchr(git_dir, '/'); + if (slash && !strcmp(slash, "/.git")) + return 0; + + /* + * Otherwise it is often bare. At this point + * we are just guessing. + */ + return 1; +} + +static const char init_db_usage[] = +"git-init [-q | --quiet] [--template=] [--shared]"; + +/* + * If you want to, you can share the DB area with any number of branches. + * That has advantages: you can save space by sharing all the SHA1 objects. + * On the other hand, it might just make lookup slower and messier. You + * be the judge. The default case is to have one DB per managed directory. + */ +int cmd_init_db(int argc, const char **argv, const char *prefix) +{ + const char *git_dir; + const char *template_dir = NULL; + unsigned int flags = 0; + int i; + + for (i = 1; i < argc; i++, argv++) { + const char *arg = argv[1]; + if (!prefixcmp(arg, "--template=")) + template_dir = arg+11; + else if (!strcmp(arg, "--shared")) + shared_repository = PERM_GROUP; + else if (!prefixcmp(arg, "--shared=")) + shared_repository = git_config_perm("arg", arg+9); + else if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet")) + flags |= INIT_DB_QUIET; + else + usage(init_db_usage); + } + + /* + * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR + * without --bare. Catch the error early. + */ + git_dir = getenv(GIT_DIR_ENVIRONMENT); + if ((!git_dir || is_bare_repository_cfg == 1) + && getenv(GIT_WORK_TREE_ENVIRONMENT)) + die("%s (or --work-tree=) not allowed without " + "specifying %s (or --git-dir=)", + GIT_WORK_TREE_ENVIRONMENT, + GIT_DIR_ENVIRONMENT); + + /* + * Set up the default .git directory contents + */ + if (!git_dir) + git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; + + if (is_bare_repository_cfg < 0) + is_bare_repository_cfg = guess_repository_type(git_dir); + + if (!is_bare_repository_cfg) { + if (git_dir) { + const char *git_dir_parent = strrchr(git_dir, '/'); + if (git_dir_parent) { + char *rel = xstrndup(git_dir, git_dir_parent - git_dir); + git_work_tree_cfg = xstrdup(make_absolute_path(rel)); + free(rel); + } + } + if (!git_work_tree_cfg) { + git_work_tree_cfg = xcalloc(PATH_MAX, 1); + if (!getcwd(git_work_tree_cfg, PATH_MAX)) + die ("Cannot access current working directory."); + } + if (access(get_git_work_tree(), X_OK)) + die ("Cannot access work tree '%s'", + get_git_work_tree()); + } + + set_git_dir(make_absolute_path(git_dir)); + + return init_db(template_dir, flags); +} diff --git a/cache.h b/cache.h index aba029315c..f3ad9741cc 100644 --- a/cache.h +++ b/cache.h @@ -324,6 +324,10 @@ extern const char *prefix_filename(const char *prefix, int len, const char *path extern void verify_filename(const char *prefix, const char *name); extern void verify_non_filename(const char *prefix, const char *name); +#define INIT_DB_QUIET 0x0001 + +extern int init_db(const char *template_dir, unsigned int flags); + #define alloc_nr(x) (((x)+16)*3/2) /* From 8434c2f1afedb936e0ea8c07ce25733013c2f743 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Sun, 27 Apr 2008 13:39:30 -0400 Subject: [PATCH 017/202] Build in clone Thanks to Johannes Schindelin for various comments and improvements, including supporting cloning full bundles. Signed-off-by: Junio C Hamano --- Makefile | 2 +- builtin-clone.c | 544 ++++++++++++++++++ builtin.h | 1 + git-clone.sh => contrib/examples/git-clone.sh | 0 git.c | 1 + 5 files changed, 547 insertions(+), 1 deletion(-) create mode 100644 builtin-clone.c rename git-clone.sh => contrib/examples/git-clone.sh (100%) diff --git a/Makefile b/Makefile index 9d84c8d799..d5f763b515 100644 --- a/Makefile +++ b/Makefile @@ -235,7 +235,6 @@ BASIC_LDFLAGS = SCRIPT_SH += git-am.sh SCRIPT_SH += git-bisect.sh -SCRIPT_SH += git-clone.sh SCRIPT_SH += git-filter-branch.sh SCRIPT_SH += git-lost-found.sh SCRIPT_SH += git-merge-octopus.sh @@ -481,6 +480,7 @@ BUILTIN_OBJS += builtin-check-ref-format.o BUILTIN_OBJS += builtin-checkout-index.o BUILTIN_OBJS += builtin-checkout.o BUILTIN_OBJS += builtin-clean.o +BUILTIN_OBJS += builtin-clone.o BUILTIN_OBJS += builtin-commit-tree.o BUILTIN_OBJS += builtin-commit.o BUILTIN_OBJS += builtin-config.o diff --git a/builtin-clone.c b/builtin-clone.c new file mode 100644 index 0000000000..a7c075d0e2 --- /dev/null +++ b/builtin-clone.c @@ -0,0 +1,544 @@ +/* + * Builtin "git clone" + * + * Copyright (c) 2007 Kristian Høgsberg , + * 2008 Daniel Barkalow + * Based on git-commit.sh by Junio C Hamano and Linus Torvalds + * + * Clone a repository into a different directory that does not yet exist. + */ + +#include "cache.h" +#include "parse-options.h" +#include "fetch-pack.h" +#include "refs.h" +#include "tree.h" +#include "tree-walk.h" +#include "unpack-trees.h" +#include "transport.h" +#include "strbuf.h" +#include "dir.h" + +/* + * Overall FIXMEs: + * - respect DB_ENVIRONMENT for .git/objects. + * + * Implementation notes: + * - dropping use-separate-remote and no-separate-remote compatibility + * + */ +static const char * const builtin_clone_usage[] = { + "git-clone [options] [--] []", + NULL +}; + +static int option_quiet, option_no_checkout, option_bare; +static int option_local, option_no_hardlinks, option_shared; +static char *option_template, *option_reference, *option_depth; +static char *option_origin = NULL; +static char *option_upload_pack = "git-upload-pack"; + +static struct option builtin_clone_options[] = { + OPT__QUIET(&option_quiet), + OPT_BOOLEAN('n', "no-checkout", &option_no_checkout, + "don't create a checkout"), + OPT_BOOLEAN(0, "bare", &option_bare, "create a bare repository"), + OPT_BOOLEAN(0, "naked", &option_bare, "create a bare repository"), + OPT_BOOLEAN('l', "local", &option_local, + "to clone from a local repository"), + OPT_BOOLEAN(0, "no-hardlinks", &option_no_hardlinks, + "don't use local hardlinks, always copy"), + OPT_BOOLEAN('s', "shared", &option_shared, + "setup as shared repository"), + OPT_STRING(0, "template", &option_template, "path", + "path the template repository"), + OPT_STRING(0, "reference", &option_reference, "repo", + "reference repository"), + OPT_STRING('o', "origin", &option_origin, "branch", + "use instead or 'origin' to track upstream"), + OPT_STRING('u', "upload-pack", &option_upload_pack, "path", + "path to git-upload-pack on the remote"), + OPT_STRING(0, "depth", &option_depth, "depth", + "create a shallow clone of that depth"), + + OPT_END() +}; + +static char *get_repo_path(const char *repo, int *is_bundle) +{ + static char *suffix[] = { "/.git", ".git", "" }; + static char *bundle_suffix[] = { ".bundle", "" }; + struct stat st; + int i; + + for (i = 0; i < ARRAY_SIZE(suffix); i++) { + const char *path; + path = mkpath("%s%s", repo, suffix[i]); + if (!stat(path, &st) && S_ISDIR(st.st_mode)) { + *is_bundle = 0; + return xstrdup(make_absolute_path(path)); + } + } + + for (i = 0; i < ARRAY_SIZE(bundle_suffix); i++) { + const char *path; + path = mkpath("%s%s", repo, bundle_suffix[i]); + if (!stat(path, &st) && S_ISREG(st.st_mode)) { + *is_bundle = 1; + return xstrdup(make_absolute_path(path)); + } + } + + return NULL; +} + +static char *guess_dir_name(const char *repo, int is_bundle) +{ + const char *p, *start, *end, *limit; + int after_slash_or_colon; + + /* Guess dir name from repository: strip trailing '/', + * strip trailing '[:/]*.{git,bundle}', strip leading '.*[/:]'. */ + + after_slash_or_colon = 1; + limit = repo + strlen(repo); + start = repo; + end = limit; + for (p = repo; p < limit; p++) { + const char *prefix = is_bundle ? ".bundle" : ".git"; + if (!prefixcmp(p, prefix)) { + if (!after_slash_or_colon) + end = p; + p += strlen(prefix) - 1; + } else if (!prefixcmp(p, ".bundle")) { + if (!after_slash_or_colon) + end = p; + p += 7; + } else if (*p == '/' || *p == ':') { + if (end == limit) + end = p; + after_slash_or_colon = 1; + } else if (after_slash_or_colon) { + start = p; + end = limit; + after_slash_or_colon = 0; + } + } + + return xstrndup(start, end - start); +} + +static int is_directory(const char *path) +{ + struct stat buf; + + return !stat(path, &buf) && S_ISDIR(buf.st_mode); +} + +static void setup_reference(const char *repo) +{ + const char *ref_git; + char *ref_git_copy; + + struct remote *remote; + struct transport *transport; + const struct ref *extra; + + ref_git = make_absolute_path(option_reference); + + if (is_directory(mkpath("%s/.git/objects", ref_git))) + ref_git = mkpath("%s/.git", ref_git); + else if (!is_directory(mkpath("%s/objects", ref_git))) + die("reference repository '%s' is not a local directory.", + option_reference); + + ref_git_copy = xstrdup(ref_git); + + add_to_alternates_file(ref_git_copy); + + remote = remote_get(ref_git_copy); + transport = transport_get(remote, ref_git_copy); + for (extra = transport_get_remote_refs(transport); extra; + extra = extra->next) + add_extra_ref(extra->name, extra->old_sha1, 0); + + transport_disconnect(transport); + + free(ref_git_copy); +} + +static void copy_or_link_directory(char *src, char *dest) +{ + struct dirent *de; + struct stat buf; + int src_len, dest_len; + DIR *dir; + + dir = opendir(src); + if (!dir) + die("failed to open %s\n", src); + + if (mkdir(dest, 0777)) { + if (errno != EEXIST) + die("failed to create directory %s\n", dest); + else if (stat(dest, &buf)) + die("failed to stat %s\n", dest); + else if (!S_ISDIR(buf.st_mode)) + die("%s exists and is not a directory\n", dest); + } + + src_len = strlen(src); + src[src_len] = '/'; + dest_len = strlen(dest); + dest[dest_len] = '/'; + + while ((de = readdir(dir)) != NULL) { + strcpy(src + src_len + 1, de->d_name); + strcpy(dest + dest_len + 1, de->d_name); + if (stat(src, &buf)) { + warning ("failed to stat %s\n", src); + continue; + } + if (S_ISDIR(buf.st_mode)) { + if (de->d_name[0] != '.') + copy_or_link_directory(src, dest); + continue; + } + + if (unlink(dest) && errno != ENOENT) + die("failed to unlink %s\n", dest); + if (option_no_hardlinks) { + if (copy_file(dest, src, 0666)) + die("failed to copy file to %s\n", dest); + } else { + if (link(src, dest)) + die("failed to create link %s\n", dest); + } + } +} + +static const struct ref *clone_local(const char *src_repo, + const char *dest_repo) +{ + const struct ref *ret; + char src[PATH_MAX]; + char dest[PATH_MAX]; + struct remote *remote; + struct transport *transport; + + if (option_shared) + add_to_alternates_file(src_repo); + else { + snprintf(src, PATH_MAX, "%s/objects", src_repo); + snprintf(dest, PATH_MAX, "%s/objects", dest_repo); + copy_or_link_directory(src, dest); + } + + remote = remote_get(src_repo); + transport = transport_get(remote, src_repo); + ret = transport_get_remote_refs(transport); + transport_disconnect(transport); + return ret; +} + +static const char *junk_work_tree; +static const char *junk_git_dir; +pid_t junk_pid; + +static void remove_junk(void) +{ + struct strbuf sb; + if (getpid() != junk_pid) + return; + strbuf_init(&sb, 0); + if (junk_git_dir) { + strbuf_addstr(&sb, junk_git_dir); + remove_dir_recursively(&sb, 0); + strbuf_reset(&sb); + } + if (junk_work_tree) { + strbuf_addstr(&sb, junk_work_tree); + remove_dir_recursively(&sb, 0); + strbuf_reset(&sb); + } +} + +static void remove_junk_on_signal(int signo) +{ + remove_junk(); + signal(SIGINT, SIG_DFL); + raise(signo); +} + +static const struct ref *locate_head(const struct ref *refs, + const struct ref *mapped_refs, + const struct ref **remote_head_p) +{ + const struct ref *remote_head = NULL; + const struct ref *remote_master = NULL; + const struct ref *r; + for (r = refs; r; r = r->next) + if (!strcmp(r->name, "HEAD")) + remote_head = r; + + for (r = mapped_refs; r; r = r->next) + if (!strcmp(r->name, "refs/heads/master")) + remote_master = r; + + if (remote_head_p) + *remote_head_p = remote_head; + + /* If there's no HEAD value at all, never mind. */ + if (!remote_head) + return NULL; + + /* If refs/heads/master could be right, it is. */ + if (remote_master && !hashcmp(remote_master->old_sha1, + remote_head->old_sha1)) + return remote_master; + + /* Look for another ref that points there */ + for (r = mapped_refs; r; r = r->next) + if (r != remote_head && + !hashcmp(r->old_sha1, remote_head->old_sha1)) + return r; + + /* Nothing is the same */ + return NULL; +} + +static struct ref *write_remote_refs(const struct ref *refs, + struct refspec *refspec, const char *reflog) +{ + struct ref *local_refs = NULL; + struct ref **tail = &local_refs; + struct ref *r; + + get_fetch_map(refs, refspec, &tail, 0); + get_fetch_map(refs, tag_refspec, &tail, 0); + + for (r = local_refs; r; r = r->next) + update_ref(reflog, + r->peer_ref->name, r->old_sha1, NULL, 0, DIE_ON_ERR); + return local_refs; +} + +int cmd_clone(int argc, const char **argv, const char *prefix) +{ + int use_local_hardlinks = 1; + int use_separate_remote = 1; + int is_bundle = 0; + struct stat buf; + const char *repo_name, *repo, *work_tree, *git_dir; + char *path, *dir; + const struct ref *refs, *head_points_at, *remote_head, *mapped_refs; + char branch_top[256], key[256], value[256]; + struct strbuf reflog_msg; + + struct refspec refspec; + + junk_pid = getpid(); + + argc = parse_options(argc, argv, builtin_clone_options, + builtin_clone_usage, 0); + + if (argc == 0) + die("You must specify a repository to clone."); + + if (option_no_hardlinks) + use_local_hardlinks = 0; + + if (option_bare) { + if (option_origin) + die("--bare and --origin %s options are incompatible.", + option_origin); + option_no_checkout = 1; + use_separate_remote = 0; + } + + if (!option_origin) + option_origin = "origin"; + + repo_name = argv[0]; + + path = get_repo_path(repo_name, &is_bundle); + if (path) + repo = path; + else if (!strchr(repo_name, ':')) + repo = xstrdup(make_absolute_path(repo_name)); + else + repo = repo_name; + + if (argc == 2) + dir = xstrdup(argv[1]); + else + dir = guess_dir_name(repo_name, is_bundle); + + if (!stat(dir, &buf)) + die("destination directory '%s' already exists.", dir); + + strbuf_init(&reflog_msg, 0); + strbuf_addf(&reflog_msg, "clone: from %s", repo); + + if (option_bare) + work_tree = NULL; + else { + work_tree = getenv("GIT_WORK_TREE"); + if (work_tree && !stat(work_tree, &buf)) + die("working tree '%s' already exists.", work_tree); + } + + if (option_bare || work_tree) + git_dir = xstrdup(dir); + else { + work_tree = dir; + git_dir = xstrdup(mkpath("%s/.git", dir)); + } + + if (!option_bare) { + junk_work_tree = work_tree; + if (mkdir(work_tree, 0755)) + die("could not create work tree dir '%s'.", work_tree); + set_git_work_tree(work_tree); + } + junk_git_dir = git_dir; + atexit(remove_junk); + signal(SIGINT, remove_junk_on_signal); + + setenv(CONFIG_ENVIRONMENT, xstrdup(mkpath("%s/config", git_dir)), 1); + + set_git_dir(make_absolute_path(git_dir)); + + fprintf(stderr, "Initialize %s\n", git_dir); + init_db(option_template, option_quiet ? INIT_DB_QUIET : 0); + + if (option_reference) + setup_reference(git_dir); + + git_config(git_default_config); + + if (option_bare) { + strcpy(branch_top, "refs/heads/"); + + git_config_set("core.bare", "true"); + } else { + snprintf(branch_top, sizeof(branch_top), + "refs/remotes/%s/", option_origin); + + /* Configure the remote */ + snprintf(key, sizeof(key), "remote.%s.url", option_origin); + git_config_set(key, repo); + + snprintf(key, sizeof(key), "remote.%s.fetch", option_origin); + snprintf(value, sizeof(value), + "+refs/heads/*:%s*", branch_top); + git_config_set_multivar(key, value, "^$", 0); + } + + refspec.force = 0; + refspec.pattern = 1; + refspec.src = "refs/heads/"; + refspec.dst = branch_top; + + if (path && !is_bundle) + refs = clone_local(path, git_dir); + else { + struct remote *remote = remote_get(argv[0]); + struct transport *transport = transport_get(remote, argv[0]); + + transport_set_option(transport, TRANS_OPT_KEEP, "yes"); + + if (option_depth) + transport_set_option(transport, TRANS_OPT_DEPTH, + option_depth); + + if (option_quiet) + transport->verbose = -1; + + refs = transport_get_remote_refs(transport); + transport_fetch_refs(transport, refs); + } + + clear_extra_refs(); + + mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf); + + head_points_at = locate_head(refs, mapped_refs, &remote_head); + + if (head_points_at) { + /* Local default branch link */ + create_symref("HEAD", head_points_at->name, NULL); + + if (!option_bare) { + struct strbuf head_ref; + const char *head = head_points_at->name; + + if (!prefixcmp(head, "refs/heads/")) + head += 11; + + /* Set up the initial local branch */ + + /* Local branch initial value */ + update_ref(reflog_msg.buf, "HEAD", + head_points_at->old_sha1, + NULL, 0, DIE_ON_ERR); + + strbuf_init(&head_ref, 0); + strbuf_addstr(&head_ref, branch_top); + strbuf_addstr(&head_ref, "HEAD"); + + /* Remote branch link */ + create_symref(head_ref.buf, + head_points_at->peer_ref->name, + reflog_msg.buf); + + snprintf(key, sizeof(key), "branch.%s.remote", head); + git_config_set(key, option_origin); + snprintf(key, sizeof(key), "branch.%s.merge", head); + git_config_set(key, head_points_at->name); + } + } else if (remote_head) { + /* Source had detached HEAD pointing somewhere. */ + if (!option_bare) + update_ref(reflog_msg.buf, "HEAD", + remote_head->old_sha1, + NULL, REF_NODEREF, DIE_ON_ERR); + } else { + /* Nothing to checkout out */ + if (!option_no_checkout) + warning("remote HEAD refers to nonexistent ref, " + "unable to checkout.\n"); + option_no_checkout = 1; + } + + if (!option_no_checkout) { + struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); + struct unpack_trees_options opts; + struct tree *tree; + struct tree_desc t; + int fd; + + /* We need to be in the new work tree for the checkout */ + setup_work_tree(); + + fd = hold_locked_index(lock_file, 1); + + memset(&opts, 0, sizeof opts); + opts.update = 1; + opts.verbose_update = !option_quiet; + opts.dst_index = &the_index; + + tree = parse_tree_indirect(remote_head->old_sha1); + parse_tree(tree); + init_tree_desc(&t, tree->buffer, tree->size); + unpack_trees(1, &t, &opts); + + if (write_cache(fd, active_cache, active_nr) || + commit_locked_index(lock_file)) + die("unable to write new index file"); + } + + strbuf_release(&reflog_msg); + junk_pid = 0; + return 0; +} diff --git a/builtin.h b/builtin.h index 95126fd0c1..23a90ded7d 100644 --- a/builtin.h +++ b/builtin.h @@ -24,6 +24,7 @@ extern int cmd_check_attr(int argc, const char **argv, const char *prefix); extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix); extern int cmd_cherry(int argc, const char **argv, const char *prefix); extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix); +extern int cmd_clone(int argc, const char **argv, const char *prefix); extern int cmd_clean(int argc, const char **argv, const char *prefix); extern int cmd_commit(int argc, const char **argv, const char *prefix); extern int cmd_commit_tree(int argc, const char **argv, const char *prefix); diff --git a/git-clone.sh b/contrib/examples/git-clone.sh similarity index 100% rename from git-clone.sh rename to contrib/examples/git-clone.sh diff --git a/git.c b/git.c index 89b431fa28..2c9004f02e 100644 --- a/git.c +++ b/git.c @@ -286,6 +286,7 @@ static void handle_internal_command(int argc, const char **argv) { "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE }, { "cherry", cmd_cherry, RUN_SETUP }, { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE }, + { "clone", cmd_clone }, { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE }, { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE }, { "commit-tree", cmd_commit_tree, RUN_SETUP }, From de451dff15a3404e1c7025ebf72c70e624cf2f5c Mon Sep 17 00:00:00 2001 From: Avery Pennarun Date: Tue, 15 Apr 2008 21:04:16 -0400 Subject: [PATCH 018/202] git-svn: add documentation for --use-log-author option. Signed-off-by: Avery Pennarun Signed-off-by: Junio C Hamano --- Documentation/git-svn.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index bec9accc89..4ad567a309 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -61,6 +61,10 @@ COMMANDS Set the 'useSvnsyncProps' option in the [svn-remote] config. --rewrite-root=;; Set the 'rewriteRoot' option in the [svn-remote] config. +--use-log-author;; + When retrieving svn commits into git (as part of fetch, rebase, or + dcommit operations), look for the first From: or Signed-off-by: line + in the log message and use that as the author string. --username=;; For transports that SVN handles authentication for (http, https, and plain svn), specify the username. For other From 6aa9ba14a0259d86eec2077308358caf891c162a Mon Sep 17 00:00:00 2001 From: Avery Pennarun Date: Tue, 15 Apr 2008 21:04:17 -0400 Subject: [PATCH 019/202] git-svn: Add --add-author-from option. This option adds a From: line (based on the commit's author information) at the beginning of the body of the commit log message when sending to svn, if a From: or Signed-off-by: header does not exist. This, combined with --use-log-author, can retain the author field of commits through a round trip from git to svn and back. Signed-off-by: Avery Pennarun Signed-off-by: Junio C Hamano --- git-svn.perl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/git-svn.perl b/git-svn.perl index b70f8efaaa..262acd9b36 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -82,6 +82,7 @@ my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent, 'repack-flags|repack-args|repack-opts=s' => \$Git::SVN::_repack_flags, 'use-log-author' => \$Git::SVN::_use_log_author, + 'add-author-from' => \$Git::SVN::_add_author_from, %remote_opts ); my ($_trunk, $_tags, $_branches, $_stdlayout); @@ -1011,17 +1012,28 @@ sub get_commit_entry { my ($msg_fh, $ctx) = command_output_pipe('cat-file', $type, $treeish); my $in_msg = 0; + my $author; + my $saw_from = 0; while (<$msg_fh>) { if (!$in_msg) { $in_msg = 1 if (/^\s*$/); + $author = $1 if (/^author (.*>)/); } elsif (/^git-svn-id: /) { # skip this for now, we regenerate the # correct one on re-fetch anyways # TODO: set *:merge properties or like... } else { + if (/^From:/ || /^Signed-off-by:/) { + $saw_from = 1; + } print $log_fh $_ or croak $!; } } + if ($Git::SVN::_add_author_from && defined($author) + && !$saw_from) { + print $log_fh "\nFrom: $author\n" + or croak $!; + } command_close_pipe($msg_fh, $ctx); } close $log_fh or croak $!; @@ -1248,7 +1260,7 @@ use constant rev_map_fmt => 'NH40'; use vars qw/$default_repo_id $default_ref_id $_no_metadata $_follow_parent $_repack $_repack_flags $_use_svm_props $_head $_use_svnsync_props $no_reuse_existing $_minimize_url - $_use_log_author/; + $_use_log_author $_add_author_from/; use Carp qw/croak/; use File::Path qw/mkpath/; use File::Copy qw/copy/; From 9e72732e4b4f93ee74ed794abdd381dddb3f280e Mon Sep 17 00:00:00 2001 From: Avery Pennarun Date: Tue, 15 Apr 2008 21:04:18 -0400 Subject: [PATCH 020/202] git-svn: add documentation for --add-author-from option. Signed-off-by: Avery Pennarun Signed-off-by: Junio C Hamano --- Documentation/git-svn.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 4ad567a309..acd77eb3b2 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -65,6 +65,12 @@ COMMANDS When retrieving svn commits into git (as part of fetch, rebase, or dcommit operations), look for the first From: or Signed-off-by: line in the log message and use that as the author string. +--add-author-from;; + When committing to svn from git (as part of commit or dcommit + operations), if the existing log message doesn't already have a + From: or Signed-off-by: line, append a From: line based on the + git commit's author string. If you use this, then --use-log-author + will retrieve a valid author string for all commits. --username=;; For transports that SVN handles authentication for (http, https, and plain svn), specify the username. For other From 885cf808998c4e6d685c669a44f02b37f6965fd9 Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Sun, 4 May 2008 03:36:52 -0700 Subject: [PATCH 021/202] revision API: split parent rewriting and parent printing options This change allows parent rewriting to be performed without causing the log and rev-list commands to print the parents. Signed-off-by: Adam Simpkins Signed-off-by: Junio C Hamano --- builtin-rev-list.c | 2 +- log-tree.c | 4 ++-- revision.c | 7 ++++--- revision.h | 3 ++- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/builtin-rev-list.c b/builtin-rev-list.c index edc0bd35bb..476a870c7d 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -77,7 +77,7 @@ static void show_commit(struct commit *commit) stdout); else fputs(sha1_to_hex(commit->object.sha1), stdout); - if (revs.parents) { + if (revs.print_parents) { struct commit_list *parents = commit->parents; while (parents) { printf(" %s", sha1_to_hex(parents->item->object.sha1)); diff --git a/log-tree.c b/log-tree.c index d3fb0e520c..74829d720f 100644 --- a/log-tree.c +++ b/log-tree.c @@ -231,7 +231,7 @@ void show_log(struct rev_info *opt) putchar('>'); } fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout); - if (opt->parents) + if (opt->print_parents) show_parents(commit, abbrev_commit); show_decorations(commit); putchar(opt->diffopt.line_termination); @@ -271,7 +271,7 @@ void show_log(struct rev_info *opt) } fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout); - if (opt->parents) + if (opt->print_parents) show_parents(commit, abbrev_commit); if (parent) printf(" (from %s)", diff --git a/revision.c b/revision.c index 4231ea2cce..a813304162 100644 --- a/revision.c +++ b/revision.c @@ -1105,7 +1105,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch } } if (!strcmp(arg, "--parents")) { - revs->parents = 1; + revs->rewrite_parents = 1; + revs->print_parents = 1; continue; } if (!strcmp(arg, "--dense")) { @@ -1524,13 +1525,13 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit) /* Commit without changes? */ if (commit->object.flags & TREESAME) { /* drop merges unless we want parenthood */ - if (!revs->parents) + if (!revs->rewrite_parents) return commit_ignore; /* non-merge - always ignore it */ if (!commit->parents || !commit->parents->next) return commit_ignore; } - if (revs->parents && rewrite_parents(revs, commit) < 0) + if (revs->rewrite_parents && rewrite_parents(revs, commit) < 0) return commit_error; } return commit_show; diff --git a/revision.h b/revision.h index 31217f8c67..201bd97c58 100644 --- a/revision.h +++ b/revision.h @@ -46,7 +46,8 @@ struct rev_info { unpacked:1, /* see also ignore_packed below */ boundary:2, left_right:1, - parents:1, + rewrite_parents:1, + print_parents:1, reverse:1, cherry_pick:1, first_parent_only:1; From c12172d2eab91b79b8181b04ab5a5332a96e34a8 Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Sun, 4 May 2008 03:36:53 -0700 Subject: [PATCH 022/202] Add history graph API This new API allows the commit history to be displayed as a text-based graphical representation. Signed-off-by: Adam Simpkins Signed-off-by: Junio C Hamano --- Documentation/technical/api-history-graph.txt | 176 ++++ Makefile | 2 + graph.c | 907 ++++++++++++++++++ graph.h | 121 +++ 4 files changed, 1206 insertions(+) create mode 100644 Documentation/technical/api-history-graph.txt create mode 100644 graph.c create mode 100644 graph.h diff --git a/Documentation/technical/api-history-graph.txt b/Documentation/technical/api-history-graph.txt new file mode 100644 index 0000000000..5f6465fa25 --- /dev/null +++ b/Documentation/technical/api-history-graph.txt @@ -0,0 +1,176 @@ +history graph API +================= + +The graph API is used to draw a text-based representation of the commit +history. The API generates the graph in a line-by-line fashion. + +Functions +--------- + +Core functions: + +* `graph_init()` creates a new `struct git_graph` + +* `graph_release()` destroys a `struct git_graph`, and frees the memory + associated with it. + +* `graph_update()` moves the graph to a new commit. + +* `graph_next_line()` outputs the next line of the graph into a strbuf. It + does not add a terminating newline. + +* `graph_padding_line()` outputs a line of vertical padding in the graph. It + is similar to `graph_next_line()`, but is guaranteed to never print the line + containing the current commit. Where `graph_next_line()` would print the + commit line next, `graph_padding_line()` prints a line that simply extends + all branch lines downwards one row, leaving their positions unchanged. + +* `graph_is_commit_finished()` determines if the graph has output all lines + necessary for the current commit. If `graph_update()` is called before all + lines for the current commit have been printed, the next call to + `graph_next_line()` will output an ellipsis, to indicate that a portion of + the graph was omitted. + +The following utility functions are wrappers around `graph_next_line()` and +`graph_is_commit_finished()`. They always print the output to stdout. +They can all be called with a NULL graph argument, in which case no graph +output will be printed. + +* `graph_show_commit()` calls `graph_next_line()` until it returns non-zero. + This prints all graph lines up to, and including, the line containing this + commit. Output is printed to stdout. The last line printed does not contain + a terminating newline. This should not be called if the commit line has + already been printed, or it will loop forever. + +* `graph_show_oneline()` calls `graph_next_line()` and prints the result to + stdout. The line printed does not contain a terminating newline. + +* `graph_show_padding()` calls `graph_padding_line()` and prints the result to + stdout. The line printed does not contain a terminating newline. + +* `graph_show_remainder()` calls `graph_next_line()` until + `graph_is_commit_finished()` returns non-zero. Output is printed to stdout. + The last line printed does not contain a terminating newline. Returns 1 if + output was printed, and 0 if no output was necessary. + +* `graph_show_strbuf()` prints the specified strbuf to stdout, prefixing all + lines but the first with a graph line. The caller is responsible for + ensuring graph output for the first line has already been printed to stdout. + (This can be done with `graph_show_commit()` or `graph_show_oneline()`.) If + a NULL graph is supplied, the strbuf is printed as-is. + +* `graph_show_commit_msg()` is similar to `graph_show_strbuf()`, but it also + prints the remainder of the graph, if more lines are needed after the strbuf + ends. It is better than directly calling `graph_show_strbuf()` followed by + `graph_show_remainder()` since it properly handles buffers that do not end in + a terminating newline. The output printed by `graph_show_commit_msg()` will + end in a newline if and only if the strbuf ends in a newline. + +Data structure +-------------- +`struct git_graph` is an opaque data type used to store the current graph +state. + +Calling sequence +---------------- + +* Create a `struct git_graph` by calling `graph_init()`. + +* Use the revision walking API to walk through a group of contiguous commits. + +* For each commit traversed, call `graph_update()` to move the graph to the + next commit. Once `graph_update()` has been called, call `graph_next_line()` + repeatedly, until `graph_is_commit_finished()` returns non-zero. Each call + to `graph_next_line()` will output a single line of the graph. The resulting + lines will not contain any newlines. `graph_next_line()` returns 1 if the + resulting line contains the current commit, or 0 if this is merely a line + needed to adjust the graph before or after the current commit. This return + value can be used to determine where to print the commit summary information + alongside the graph output. + +Limitations +----------- + +* `graph_update()` must be called with commits in topological order. It should + not be called on a commit if it has already been invoked with an ancestor of + that commit, or the graph output will be incorrect. + +* `graph_update()` must be called on a contiguous group of commits. If + `graph_update()` is called on a particular commit, it should later be called + on all parents of that commit. Parents must not be skipped, or the graph + output will appear incorrect. ++ +`graph_update()` may be used on a pruned set of commits only if the parent list +has been rewritten so as to include only ancestors from the pruned set. + +* The graph API does not currently support reverse commit ordering. In + order to implement reverse ordering, the graphing API needs an + (efficient) mechanism to find the children of a commit. + +Sample usage +------------ + +------------ +struct commit *commit; +struct git_graph *graph = graph_init(); + +while ((commit = get_revision(opts)) != NULL) { + graph_update(graph, commit); + while (!graph_is_commit_finished(graph)) + { + struct strbuf sb; + int is_commit_line; + + strbuf_init(&sb, 0); + is_commit_line = graph_next_line(graph, &sb); + fputs(sb.buf, stdout); + + if (is_commit_line) + log_tree_commit(opts, commit); + else + putchar(opts->diffopt.line_termination); + } +} + +graph_release(graph); +------------ + +Sample output +------------- + +The following is an example of the output from the graph API. This output does +not include any commit summary information--callers are responsible for +outputting that information, if desired. + +------------ +* +* +M +|\ +* | +| | * +| \ \ +| \ \ +M-. \ \ +|\ \ \ \ +| | * | | +| | | | | * +| | | | | * +| | | | | M +| | | | | |\ +| | | | | | * +| * | | | | | +| | | | | M \ +| | | | | |\ | +| | | | * | | | +| | | | * | | | +* | | | | | | | +| |/ / / / / / +|/| / / / / / +* | | | | | | +|/ / / / / / +* | | | | | +| | | | | * +| | | | |/ +| | | | * +------------ diff --git a/Makefile b/Makefile index 9d84c8d799..d42b1173a2 100644 --- a/Makefile +++ b/Makefile @@ -346,6 +346,7 @@ LIB_H += diff.h LIB_H += dir.h LIB_H += fsck.h LIB_H += git-compat-util.h +LIB_H += graph.h LIB_H += grep.h LIB_H += hash.h LIB_H += list-objects.h @@ -411,6 +412,7 @@ LIB_OBJS += entry.o LIB_OBJS += environment.o LIB_OBJS += exec_cmd.o LIB_OBJS += fsck.o +LIB_OBJS += graph.o LIB_OBJS += grep.o LIB_OBJS += hash.o LIB_OBJS += help.o diff --git a/graph.c b/graph.c new file mode 100644 index 0000000000..616e18b13f --- /dev/null +++ b/graph.c @@ -0,0 +1,907 @@ +#include "cache.h" +#include "commit.h" +#include "graph.h" +#include "diff.h" +#include "revision.h" + +/* + * TODO: + * - Add colors to the graph. + * Pick a color for each column, and print all characters + * in that column with the specified color. + * + * - Limit the number of columns, similar to the way gitk does. + * If we reach more than a specified number of columns, omit + * sections of some columns. + * + * - The output during the GRAPH_PRE_COMMIT and GRAPH_COLLAPSING states + * could be made more compact by printing horizontal lines, instead of + * long diagonal lines. For example, during collapsing, something like + * this: instead of this: + * | | | | | | | | | | + * | |_|_|/ | | | |/ + * |/| | | | | |/| + * | | | | | |/| | + * |/| | | + * | | | | + * + * If there are several parallel diagonal lines, they will need to be + * replaced with horizontal lines on subsequent rows. + */ + +struct column { + /* + * The parent commit of this column. + */ + struct commit *commit; + /* + * XXX: Once we add support for colors, struct column could also + * contain the color of its branch line. + */ +}; + +enum graph_state { + GRAPH_PADDING, + GRAPH_SKIP, + GRAPH_PRE_COMMIT, + GRAPH_COMMIT, + GRAPH_POST_MERGE, + GRAPH_COLLAPSING +}; + +struct git_graph { + /* + * The commit currently being processed + */ + struct commit *commit; + /* + * The number of parents this commit has. + * (Stored so we don't have to walk over them each time we need + * this number) + */ + int num_parents; + /* + * The next expansion row to print + * when state is GRAPH_PRE_COMMIT + */ + int expansion_row; + /* + * The current output state. + * This tells us what kind of line graph_next_line() should output. + */ + enum graph_state state; + /* + * The maximum number of columns that can be stored in the columns + * and new_columns arrays. This is also half the number of entries + * that can be stored in the mapping and new_mapping arrays. + */ + int column_capacity; + /* + * The number of columns (also called "branch lines" in some places) + */ + int num_columns; + /* + * The number of columns in the new_columns array + */ + int num_new_columns; + /* + * The number of entries in the mapping array + */ + int mapping_size; + /* + * The column state before we output the current commit. + */ + struct column *columns; + /* + * The new column state after we output the current commit. + * Only valid when state is GRAPH_COLLAPSING. + */ + struct column *new_columns; + /* + * An array that tracks the current state of each + * character in the output line during state GRAPH_COLLAPSING. + * Each entry is -1 if this character is empty, or a non-negative + * integer if the character contains a branch line. The value of + * the integer indicates the target position for this branch line. + * (I.e., this array maps the current column positions to their + * desired positions.) + * + * The maximum capacity of this array is always + * sizeof(int) * 2 * column_capacity. + */ + int *mapping; + /* + * A temporary array for computing the next mapping state + * while we are outputting a mapping line. This is stored as part + * of the git_graph simply so we don't have to allocate a new + * temporary array each time we have to output a collapsing line. + */ + int *new_mapping; +}; + +struct git_graph *graph_init(void) +{ + struct git_graph *graph = xmalloc(sizeof(struct git_graph)); + graph->commit = NULL; + graph->num_parents = 0; + graph->expansion_row = 0; + graph->state = GRAPH_PADDING; + graph->num_columns = 0; + graph->num_new_columns = 0; + graph->mapping_size = 0; + + /* + * Allocate a reasonably large default number of columns + * We'll automatically grow columns later if we need more room. + */ + graph->column_capacity = 30; + graph->columns = xmalloc(sizeof(struct column) * + graph->column_capacity); + graph->new_columns = xmalloc(sizeof(struct column) * + graph->column_capacity); + graph->mapping = xmalloc(sizeof(int) * 2 * graph->column_capacity); + graph->new_mapping = xmalloc(sizeof(int) * 2 * graph->column_capacity); + + return graph; +} + +void graph_release(struct git_graph *graph) +{ + free(graph->columns); + free(graph->new_columns); + free(graph->mapping); + free(graph); +} + +static void graph_ensure_capacity(struct git_graph *graph, int num_columns) +{ + if (graph->column_capacity >= num_columns) + return; + + do { + graph->column_capacity *= 2; + } while (graph->column_capacity < num_columns); + + graph->columns = xrealloc(graph->columns, + sizeof(struct column) * + graph->column_capacity); + graph->new_columns = xrealloc(graph->new_columns, + sizeof(struct column) * + graph->column_capacity); + graph->mapping = xrealloc(graph->mapping, + sizeof(int) * 2 * graph->column_capacity); + graph->new_mapping = xrealloc(graph->new_mapping, + sizeof(int) * 2 * graph->column_capacity); +} + +static void graph_insert_into_new_columns(struct git_graph *graph, + struct commit *commit, + int *mapping_index) +{ + int i; + + /* + * Ignore uinteresting and pruned commits + */ + if (commit->object.flags & (UNINTERESTING | TREESAME)) + return; + + /* + * If the commit is already in the new_columns list, we don't need to + * add it. Just update the mapping correctly. + */ + for (i = 0; i < graph->num_new_columns; i++) { + if (graph->new_columns[i].commit == commit) { + graph->mapping[*mapping_index] = i; + *mapping_index += 2; + return; + } + } + + /* + * This commit isn't already in new_columns. Add it. + */ + graph->new_columns[graph->num_new_columns].commit = commit; + graph->mapping[*mapping_index] = graph->num_new_columns; + *mapping_index += 2; + graph->num_new_columns++; +} + +static void graph_update_columns(struct git_graph *graph) +{ + struct commit_list *parent; + struct column *tmp_columns; + int max_new_columns; + int mapping_idx; + int i, seen_this; + + /* + * Swap graph->columns with graph->new_columns + * graph->columns contains the state for the previous commit, + * and new_columns now contains the state for our commit. + * + * We'll re-use the old columns array as storage to compute the new + * columns list for the commit after this one. + */ + tmp_columns = graph->columns; + graph->columns = graph->new_columns; + graph->num_columns = graph->num_new_columns; + + graph->new_columns = tmp_columns; + graph->num_new_columns = 0; + + /* + * Now update new_columns and mapping with the information for the + * commit after this one. + * + * First, make sure we have enough room. At most, there will + * be graph->num_columns + graph->num_parents columns for the next + * commit. + */ + max_new_columns = graph->num_columns + graph->num_parents; + graph_ensure_capacity(graph, max_new_columns); + + /* + * Clear out graph->mapping + */ + graph->mapping_size = 2 * max_new_columns; + for (i = 0; i < graph->mapping_size; i++) + graph->mapping[i] = -1; + + /* + * Populate graph->new_columns and graph->mapping + * + * Some of the parents of this commit may already be in + * graph->columns. If so, graph->new_columns should only contain a + * single entry for each such commit. graph->mapping should + * contain information about where each current branch line is + * supposed to end up after the collapsing is performed. + */ + seen_this = 0; + mapping_idx = 0; + for (i = 0; i <= graph->num_columns; i++) { + struct commit *col_commit; + if (i == graph->num_columns) { + if (seen_this) + break; + col_commit = graph->commit; + } else { + col_commit = graph->columns[i].commit; + } + + if (col_commit == graph->commit) { + seen_this = 1; + for (parent = graph->commit->parents; + parent; + parent = parent->next) { + graph_insert_into_new_columns(graph, + parent->item, + &mapping_idx); + } + } else { + graph_insert_into_new_columns(graph, col_commit, + &mapping_idx); + } + } + + /* + * Shrink mapping_size to be the minimum necessary + */ + while (graph->mapping_size > 1 && + graph->mapping[graph->mapping_size - 1] < 0) + graph->mapping_size--; +} + +void graph_update(struct git_graph *graph, struct commit *commit) +{ + struct commit_list *parent; + + /* + * Set the new commit + */ + graph->commit = commit; + + /* + * Count how many parents this commit has + */ + graph->num_parents = 0; + for (parent = commit->parents; parent; parent = parent->next) + graph->num_parents++; + + /* + * Call graph_update_columns() to update + * columns, new_columns, and mapping. + */ + graph_update_columns(graph); + + graph->expansion_row = 0; + + /* + * Update graph->state. + * + * If the previous commit didn't get to the GRAPH_PADDING state, + * it never finished its output. Goto GRAPH_SKIP, to print out + * a line to indicate that portion of the graph is missing. + * + * Otherwise, if there are 3 or more parents, we need to print + * extra rows before the commit, to expand the branch lines around + * it and make room for it. + * + * If there are less than 3 parents, we can immediately print the + * commit line. + */ + if (graph->state != GRAPH_PADDING) + graph->state = GRAPH_SKIP; + else if (graph->num_parents >= 3) + graph->state = GRAPH_PRE_COMMIT; + else + graph->state = GRAPH_COMMIT; +} + +static int graph_is_mapping_correct(struct git_graph *graph) +{ + int i; + + /* + * The mapping is up to date if each entry is at its target, + * or is 1 greater than its target. + * (If it is 1 greater than the target, '/' will be printed, so it + * will look correct on the next row.) + */ + for (i = 0; i < graph->mapping_size; i++) { + int target = graph->mapping[i]; + if (target < 0) + continue; + if (target == (i / 2)) + continue; + return 0; + } + + return 1; +} + +static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb) +{ + /* + * Add additional spaces to the end of the strbuf, so that all + * lines for a particular commit have the same width. + * + * This way, fields printed to the right of the graph will remain + * aligned for the entire commit. + * + * This computation results in 3 extra space to the right in most + * cases, but only 1 extra space if the commit doesn't have any + * children that have already been displayed in the graph (i.e., + * if the current commit isn't in graph->columns). + */ + size_t extra; + size_t final_width = graph->num_columns + graph->num_parents; + if (graph->num_parents < 1) + final_width++; + final_width *= 2; + + if (sb->len >= final_width) + return; + + extra = final_width - sb->len; + strbuf_addf(sb, "%*s", (int) extra, ""); +} + +static void graph_output_padding_line(struct git_graph *graph, + struct strbuf *sb) +{ + int i; + + /* + * We could conceivable be called with a NULL commit + * if our caller has a bug, and invokes graph_next_line() + * immediately after graph_init(), without first calling + * graph_update(). Return without outputting anything in this + * case. + */ + if (!graph->commit) + return; + + /* + * Output a padding row, that leaves all branch lines unchanged + */ + for (i = 0; i < graph->num_new_columns; i++) { + strbuf_addstr(sb, "| "); + } + + graph_pad_horizontally(graph, sb); +} + +static void graph_output_skip_line(struct git_graph *graph, struct strbuf *sb) +{ + /* + * Output an ellipsis to indicate that a portion + * of the graph is missing. + */ + strbuf_addstr(sb, "..."); + graph_pad_horizontally(graph, sb); + + if (graph->num_parents >= 3) + graph->state = GRAPH_PRE_COMMIT; + else + graph->state = GRAPH_COMMIT; +} + +static void graph_output_pre_commit_line(struct git_graph *graph, + struct strbuf *sb) +{ + int num_expansion_rows; + int i, seen_this; + + /* + * This function formats a row that increases the space around a commit + * with multiple parents, to make room for it. It should only be + * called when there are 3 or more parents. + * + * We need 2 extra rows for every parent over 2. + */ + assert(graph->num_parents >= 3); + num_expansion_rows = (graph->num_parents - 2) * 2; + + /* + * graph->expansion_row tracks the current expansion row we are on. + * It should be in the range [0, num_expansion_rows - 1] + */ + assert(0 <= graph->expansion_row && + graph->expansion_row < num_expansion_rows); + + /* + * Output the row + */ + seen_this = 0; + for (i = 0; i < graph->num_columns; i++) { + struct column *col = &graph->columns[i]; + if (col->commit == graph->commit) { + seen_this = 1; + strbuf_addf(sb, "| %*s", graph->expansion_row, ""); + } else if (seen_this) { + strbuf_addstr(sb, "\\ "); + } else { + strbuf_addstr(sb, "| "); + } + } + + graph_pad_horizontally(graph, sb); + + /* + * Increment graph->expansion_row, + * and move to state GRAPH_COMMIT if necessary + */ + graph->expansion_row++; + if (graph->expansion_row >= num_expansion_rows) + graph->state = GRAPH_COMMIT; +} + +void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) +{ + int seen_this = 0; + int i, j; + + /* + * Output the row containing this commit + * Iterate up to and including graph->num_columns, + * since the current commit may not be in any of the existing + * columns. (This happens when the current commit doesn't have any + * children that we have already processed.) + */ + seen_this = 0; + for (i = 0; i <= graph->num_columns; i++) { + struct commit *col_commit; + if (i == graph->num_columns) { + if (seen_this) + break; + col_commit = graph->commit; + } else { + col_commit = graph->columns[i].commit; + } + + if (col_commit == graph->commit) { + seen_this = 1; + if (graph->num_parents > 1) + strbuf_addch(sb, 'M'); + else + strbuf_addch(sb, '*'); + + if (graph->num_parents < 2) + strbuf_addch(sb, ' '); + else if (graph->num_parents == 2) + strbuf_addstr(sb, " "); + else { + int num_dashes = + ((graph->num_parents - 2) * 2) - 1; + for (j = 0; j < num_dashes; j++) + strbuf_addch(sb, '-'); + strbuf_addstr(sb, ". "); + } + } else if (seen_this && (graph->num_parents > 1)) { + strbuf_addstr(sb, "\\ "); + } else { + strbuf_addstr(sb, "| "); + } + } + + graph_pad_horizontally(graph, sb); + + /* + * Update graph->state + */ + if (graph->num_parents > 1) + graph->state = GRAPH_POST_MERGE; + else if (graph_is_mapping_correct(graph)) + graph->state = GRAPH_PADDING; + else + graph->state = GRAPH_COLLAPSING; +} + +void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb) +{ + int seen_this = 0; + int i, j; + + /* + * Output the post-merge row + */ + for (i = 0; i <= graph->num_columns; i++) { + struct commit *col_commit; + if (i == graph->num_columns) { + if (seen_this) + break; + col_commit = graph->commit; + } else { + col_commit = graph->columns[i].commit; + } + + if (col_commit == graph->commit) { + seen_this = 1; + strbuf_addch(sb, '|'); + for (j = 0; j < graph->num_parents - 1; j++) + strbuf_addstr(sb, "\\ "); + if (graph->num_parents == 2) + strbuf_addch(sb, ' '); + } else if (seen_this && (graph->num_parents > 2)) { + strbuf_addstr(sb, "\\ "); + } else { + strbuf_addstr(sb, "| "); + } + } + + graph_pad_horizontally(graph, sb); + + /* + * Update graph->state + */ + if (graph_is_mapping_correct(graph)) + graph->state = GRAPH_PADDING; + else + graph->state = GRAPH_COLLAPSING; +} + +void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb) +{ + int i; + int *tmp_mapping; + + /* + * Clear out the new_mapping array + */ + for (i = 0; i < graph->mapping_size; i++) + graph->new_mapping[i] = -1; + + for (i = 0; i < graph->mapping_size; i++) { + int target = graph->mapping[i]; + if (target < 0) + continue; + + /* + * Since update_columns() always inserts the leftmost + * column first, each branch's target location should + * always be either its current location or to the left of + * its current location. + * + * We never have to move branches to the right. This makes + * the graph much more legible, since whenever branches + * cross, only one is moving directions. + */ + assert(target * 2 <= i); + + if (target * 2 == i) { + /* + * This column is already in the + * correct place + */ + assert(graph->new_mapping[i] == -1); + graph->new_mapping[i] = target; + } else if (graph->new_mapping[i - 1] < 0) { + /* + * Nothing is to the left. + * Move to the left by one + */ + graph->new_mapping[i - 1] = target; + } else if (graph->new_mapping[i - 1] == target) { + /* + * There is a branch line to our left + * already, and it is our target. We + * combine with this line, since we share + * the same parent commit. + * + * We don't have to add anything to the + * output or new_mapping, since the + * existing branch line has already taken + * care of it. + */ + } else { + /* + * There is a branch line to our left, + * but it isn't our target. We need to + * cross over it. + * + * The space just to the left of this + * branch should always be empty. + */ + assert(graph->new_mapping[i - 1] > target); + assert(graph->new_mapping[i - 2] < 0); + graph->new_mapping[i - 2] = target; + } + } + + /* + * The new mapping may be 1 smaller than the old mapping + */ + if (graph->new_mapping[graph->mapping_size - 1] < 0) + graph->mapping_size--; + + /* + * Output out a line based on the new mapping info + */ + for (i = 0; i < graph->mapping_size; i++) { + int target = graph->new_mapping[i]; + if (target < 0) + strbuf_addch(sb, ' '); + else if (target * 2 == i) + strbuf_addch(sb, '|'); + else + strbuf_addch(sb, '/'); + } + + graph_pad_horizontally(graph, sb); + + /* + * Swap mapping and new_mapping + */ + tmp_mapping = graph->mapping; + graph->mapping = graph->new_mapping; + graph->new_mapping = tmp_mapping; + + /* + * If graph->mapping indicates that all of the branch lines + * are already in the correct positions, we are done. + * Otherwise, we need to collapse some branch lines together. + */ + if (graph_is_mapping_correct(graph)) + graph->state = GRAPH_PADDING; +} + +int graph_next_line(struct git_graph *graph, struct strbuf *sb) +{ + switch (graph->state) { + case GRAPH_PADDING: + graph_output_padding_line(graph, sb); + return 0; + case GRAPH_SKIP: + graph_output_skip_line(graph, sb); + return 0; + case GRAPH_PRE_COMMIT: + graph_output_pre_commit_line(graph, sb); + return 0; + case GRAPH_COMMIT: + graph_output_commit_line(graph, sb); + return 1; + case GRAPH_POST_MERGE: + graph_output_post_merge_line(graph, sb); + return 0; + case GRAPH_COLLAPSING: + graph_output_collapsing_line(graph, sb); + return 0; + } + + assert(0); + return 0; +} + +void graph_padding_line(struct git_graph *graph, struct strbuf *sb) +{ + int i, j; + + if (graph->state != GRAPH_COMMIT) { + graph_next_line(graph, sb); + return; + } + + /* + * Output the row containing this commit + * Iterate up to and including graph->num_columns, + * since the current commit may not be in any of the existing + * columns. (This happens when the current commit doesn't have any + * children that we have already processed.) + */ + for (i = 0; i < graph->num_columns; i++) { + struct commit *col_commit = graph->columns[i].commit; + if (col_commit == graph->commit) { + strbuf_addch(sb, '|'); + + if (graph->num_parents < 3) + strbuf_addch(sb, ' '); + else { + int num_spaces = ((graph->num_parents - 2) * 2); + for (j = 0; j < num_spaces; j++) + strbuf_addch(sb, ' '); + } + } else { + strbuf_addstr(sb, "| "); + } + } + + graph_pad_horizontally(graph, sb); +} + +int graph_is_commit_finished(struct git_graph const *graph) +{ + return (graph->state == GRAPH_PADDING); +} + +void graph_show_commit(struct git_graph *graph) +{ + struct strbuf msgbuf; + int shown_commit_line = 0; + + if (!graph) + return; + + strbuf_init(&msgbuf, 0); + + while (!shown_commit_line) { + shown_commit_line = graph_next_line(graph, &msgbuf); + fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout); + if (!shown_commit_line) + putchar('\n'); + strbuf_setlen(&msgbuf, 0); + } + + strbuf_release(&msgbuf); +} + +void graph_show_oneline(struct git_graph *graph) +{ + struct strbuf msgbuf; + + if (!graph) + return; + + strbuf_init(&msgbuf, 0); + graph_next_line(graph, &msgbuf); + fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout); + strbuf_release(&msgbuf); +} + +void graph_show_padding(struct git_graph *graph) +{ + struct strbuf msgbuf; + + if (!graph) + return; + + strbuf_init(&msgbuf, 0); + graph_padding_line(graph, &msgbuf); + fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout); + strbuf_release(&msgbuf); +} + +int graph_show_remainder(struct git_graph *graph) +{ + struct strbuf msgbuf; + int shown = 0; + + if (!graph) + return 0; + + if (graph_is_commit_finished(graph)) + return 0; + + strbuf_init(&msgbuf, 0); + for (;;) { + graph_next_line(graph, &msgbuf); + fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout); + strbuf_setlen(&msgbuf, 0); + shown = 1; + + if (!graph_is_commit_finished(graph)) + putchar('\n'); + else + break; + } + strbuf_release(&msgbuf); + + return shown; +} + + +void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb) +{ + char *p; + + if (!graph) { + fwrite(sb->buf, sizeof(char), sb->len, stdout); + return; + } + + /* + * Print the strbuf line by line, + * and display the graph info before each line but the first. + */ + p = sb->buf; + while (p) { + size_t len; + char *next_p = strchr(p, '\n'); + if (next_p) { + next_p++; + len = next_p - p; + } else { + len = (sb->buf + sb->len) - p; + } + fwrite(p, sizeof(char), len, stdout); + if (next_p && *next_p != '\0') + graph_show_oneline(graph); + p = next_p; + } +} + +void graph_show_commit_msg(struct git_graph *graph, + struct strbuf const *sb) +{ + int newline_terminated; + + if (!graph) { + /* + * If there's no graph, just print the message buffer. + * + * The message buffer for CMIT_FMT_ONELINE and + * CMIT_FMT_USERFORMAT are already missing a terminating + * newline. All of the other formats should have it. + */ + fwrite(sb->buf, sizeof(char), sb->len, stdout); + return; + } + + newline_terminated = (sb->len && sb->buf[sb->len - 1] == '\n'); + + /* + * Show the commit message + */ + graph_show_strbuf(graph, sb); + + /* + * If there is more output needed for this commit, show it now + */ + if (!graph_is_commit_finished(graph)) { + /* + * If sb doesn't have a terminating newline, print one now, + * so we can start the remainder of the graph output on a + * new line. + */ + if (!newline_terminated) + putchar('\n'); + + graph_show_remainder(graph); + + /* + * If sb ends with a newline, our output should too. + */ + if (newline_terminated) + putchar('\n'); + } +} diff --git a/graph.h b/graph.h new file mode 100644 index 0000000000..a7748a5b22 --- /dev/null +++ b/graph.h @@ -0,0 +1,121 @@ +#ifndef GRAPH_H +#define GRAPH_H + +/* A graph is a pointer to this opaque structure */ +struct git_graph; + +/* + * Create a new struct git_graph. + * The graph should be freed with graph_release() when no longer needed. + */ +struct git_graph *graph_init(); + +/* + * Destroy a struct git_graph and free associated memory. + */ +void graph_release(struct git_graph *graph); + +/* + * Update a git_graph with a new commit. + * This will cause the graph to begin outputting lines for the new commit + * the next time graph_next_line() is called. + * + * If graph_update() is called before graph_is_commit_finished() returns 1, + * the next call to graph_next_line() will output an ellipsis ("...") + * to indicate that a portion of the graph is missing. + */ +void graph_update(struct git_graph *graph, struct commit *commit); + +/* + * Output the next line for a graph. + * This formats the next graph line into the specified strbuf. It is not + * terminated with a newline. + * + * Returns 1 if the line includes the current commit, and 0 otherwise. + * graph_next_line() will return 1 exactly once for each time + * graph_update() is called. + */ +int graph_next_line(struct git_graph *graph, struct strbuf *sb); + +/* + * Output a padding line in the graph. + * This is similar to graph_next_line(). However, it is guaranteed to + * never print the current commit line. Instead, if the commit line is + * next, it will simply output a line of vertical padding, extending the + * branch lines downwards, but leaving them otherwise unchanged. + */ +void graph_padding_line(struct git_graph *graph, struct strbuf *sb); + +/* + * Determine if a graph has finished outputting lines for the current + * commit. + * + * Returns 1 if graph_next_line() needs to be called again before + * graph_update() should be called. Returns 0 if no more lines are needed + * for this commit. If 0 is returned, graph_next_line() may still be + * called without calling graph_update(), and it will merely output + * appropriate "vertical padding" in the graph. + */ +int graph_is_commit_finished(struct git_graph const *graph); + + +/* + * graph_show_*: helper functions for printing to stdout + */ + + +/* + * If the graph is non-NULL, print the history graph to stdout, + * up to and including the line containing this commit. + * Does not print a terminating newline on the last line. + */ +void graph_show_commit(struct git_graph *graph); + +/* + * If the graph is non-NULL, print one line of the history graph to stdout. + * Does not print a terminating newline on the last line. + */ +void graph_show_oneline(struct git_graph *graph); + +/* + * If the graph is non-NULL, print one line of vertical graph padding to + * stdout. Does not print a terminating newline on the last line. + */ +void graph_show_padding(struct git_graph *graph); + +/* + * If the graph is non-NULL, print the rest of the history graph for this + * commit to stdout. Does not print a terminating newline on the last line. + */ +int graph_show_remainder(struct git_graph *graph); + +/* + * Print a strbuf to stdout. If the graph is non-NULL, all lines but the + * first will be prefixed with the graph output. + * + * If the strbuf ends with a newline, the output will end after this + * newline. A new graph line will not be printed after the final newline. + * If the strbuf is empty, no output will be printed. + * + * Since the first line will not include the graph ouput, the caller is + * responsible for printing this line's graph (perhaps via + * graph_show_commit() or graph_show_oneline()) before calling + * graph_show_strbuf(). + */ +void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb); + +/* + * Print a commit message strbuf and the remainder of the graph to stdout. + * + * This is similar to graph_show_strbuf(), but it always prints the + * remainder of the graph. + * + * If the strbuf ends with a newline, the output printed by + * graph_show_commit_msg() will end with a newline. If the strbuf is + * missing a terminating newline (including if it is empty), the output + * printed by graph_show_commit_msg() will also be missing a terminating + * newline. + */ +void graph_show_commit_msg(struct git_graph *graph, struct strbuf const *sb); + +#endif /* GRAPH_H */ From 7fefda5cc7a5faf7962092367bedb321a634d54d Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Sun, 4 May 2008 03:36:54 -0700 Subject: [PATCH 023/202] log and rev-list: add --graph option This new option causes a text-based representation of the history to be printed to the left of the normal output. Signed-off-by: Adam Simpkins Signed-off-by: Junio C Hamano --- Documentation/rev-list-options.txt | 10 +++ Documentation/technical/api-history-graph.txt | 13 ++-- builtin-rev-list.c | 46 ++++++++++- log-tree.c | 76 +++++++++++++++++-- revision.c | 26 ++++++- revision.h | 6 +- 6 files changed, 161 insertions(+), 16 deletions(-) diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 2648a55085..ce6a1017a3 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -75,6 +75,16 @@ you would get an output line this: -xxxxxxx... 1st on a ----------------------------------------------------------------------- +--graph:: + + Draw a text-based graphical representation of the commit history + on the left hand side of the output. This may cause extra lines + to be printed in between commits, in order for the graph history + to be drawn properly. ++ +This implies the '--topo-order' option by default, but the +'--date-order' option may also be specified. + Diff Formatting ~~~~~~~~~~~~~~~ diff --git a/Documentation/technical/api-history-graph.txt b/Documentation/technical/api-history-graph.txt index 5f6465fa25..ce1c08ee86 100644 --- a/Documentation/technical/api-history-graph.txt +++ b/Documentation/technical/api-history-graph.txt @@ -74,14 +74,17 @@ state. Calling sequence ---------------- -* Create a `struct git_graph` by calling `graph_init()`. +* Create a `struct git_graph` by calling `graph_init()`. When using the + revision walking API, this is done automatically by `setup_revisions()` if + the '--graph' option is supplied. * Use the revision walking API to walk through a group of contiguous commits. + The `get_revision()` function automatically calls `graph_update()` each time + it is invoked. -* For each commit traversed, call `graph_update()` to move the graph to the - next commit. Once `graph_update()` has been called, call `graph_next_line()` - repeatedly, until `graph_is_commit_finished()` returns non-zero. Each call - to `graph_next_line()` will output a single line of the graph. The resulting +* For each commit, call `graph_next_line()` repeatedly, until + `graph_is_commit_finished()` returns non-zero. Each call go + `graph_next_line()` will output a single line of the graph. The resulting lines will not contain any newlines. `graph_next_line()` returns 1 if the resulting line contains the current commit, or 0 if this is merely a line needed to adjust the graph before or after the current commit. This return diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 476a870c7d..54d55cc3a3 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -10,6 +10,7 @@ #include "list-objects.h" #include "builtin.h" #include "log-tree.h" +#include "graph.h" /* bits #0-15 in revision.h */ @@ -58,6 +59,8 @@ static const char *header_prefix; static void finish_commit(struct commit *commit); static void show_commit(struct commit *commit) { + graph_show_commit(revs.graph); + if (show_timestamp) printf("%lu ", commit->date); if (header_prefix) @@ -96,9 +99,48 @@ static void show_commit(struct commit *commit) pretty_print_commit(revs.commit_format, commit, &buf, revs.abbrev, NULL, NULL, revs.date_mode, 0); - if (buf.len) - printf("%s%c", buf.buf, hdr_termination); + if (revs.graph) { + if (buf.len) { + if (revs.commit_format != CMIT_FMT_ONELINE) + graph_show_oneline(revs.graph); + + graph_show_commit_msg(revs.graph, &buf); + + /* + * Add a newline after the commit message. + * + * Usually, this newline produces a blank + * padding line between entries, in which case + * we need to add graph padding on this line. + * + * However, the commit message may not end in a + * newline. In this case the newline simply + * ends the last line of the commit message, + * and we don't need any graph output. (This + * always happens with CMIT_FMT_ONELINE, and it + * happens with CMIT_FMT_USERFORMAT when the + * format doesn't explicitly end in a newline.) + */ + if (buf.len && buf.buf[buf.len - 1] == '\n') + graph_show_padding(revs.graph); + putchar('\n'); + } else { + /* + * If the message buffer is empty, just show + * the rest of the graph output for this + * commit. + */ + if (graph_show_remainder(revs.graph)) + putchar('\n'); + } + } else { + if (buf.len) + printf("%s%c", buf.buf, hdr_termination); + } strbuf_release(&buf); + } else { + if (graph_show_remainder(revs.graph)) + putchar('\n'); } maybe_flush_or_die(stdout, "stdout"); finish_commit(commit); diff --git a/log-tree.c b/log-tree.c index 74829d720f..1474d1f5d9 100644 --- a/log-tree.c +++ b/log-tree.c @@ -1,6 +1,7 @@ #include "cache.h" #include "diff.h" #include "commit.h" +#include "graph.h" #include "log-tree.h" #include "reflog-walk.h" @@ -165,11 +166,16 @@ void log_write_email_headers(struct rev_info *opt, const char *name, } printf("From %s Mon Sep 17 00:00:00 2001\n", name); - if (opt->message_id) + graph_show_oneline(opt->graph); + if (opt->message_id) { printf("Message-Id: <%s>\n", opt->message_id); - if (opt->ref_message_id) + graph_show_oneline(opt->graph); + } + if (opt->ref_message_id) { printf("In-Reply-To: <%s>\nReferences: <%s>\n", opt->ref_message_id, opt->ref_message_id); + graph_show_oneline(opt->graph); + } if (opt->mime_boundary) { static char subject_buffer[1024]; static char buffer[1024]; @@ -220,6 +226,8 @@ void show_log(struct rev_info *opt) opt->loginfo = NULL; if (!opt->verbose_header) { + graph_show_commit(opt->graph); + if (commit->object.flags & BOUNDARY) putchar('-'); else if (commit->object.flags & UNINTERESTING) @@ -234,6 +242,10 @@ void show_log(struct rev_info *opt) if (opt->print_parents) show_parents(commit, abbrev_commit); show_decorations(commit); + if (opt->graph && !graph_is_commit_finished(opt->graph)) { + putchar('\n'); + graph_show_remainder(opt->graph); + } putchar(opt->diffopt.line_termination); return; } @@ -243,10 +255,32 @@ void show_log(struct rev_info *opt) * Otherwise, add a diffopt.line_termination character before all * entries but the first. (IOW, as a separator between entries) */ - if (opt->shown_one && !opt->use_terminator) + if (opt->shown_one && !opt->use_terminator) { + /* + * If entries are separated by a newline, the output + * should look human-readable. If the last entry ended + * with a newline, print the graph output before this + * newline. Otherwise it will end up as a completely blank + * line and will look like a gap in the graph. + * + * If the entry separator is not a newline, the output is + * primarily intended for programmatic consumption, and we + * never want the extra graph output before the entry + * separator. + */ + if (opt->diffopt.line_termination == '\n' && + !opt->missing_newline) + graph_show_padding(opt->graph); putchar(opt->diffopt.line_termination); + } opt->shown_one = 1; + /* + * If the history graph was requested, + * print the graph, up to this commit's line + */ + graph_show_commit(opt->graph); + /* * Print header line of header.. */ @@ -279,8 +313,19 @@ void show_log(struct rev_info *opt) abbrev_commit)); show_decorations(commit); printf("%s", diff_get_color_opt(&opt->diffopt, DIFF_RESET)); - putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n'); + if (opt->commit_format == CMIT_FMT_ONELINE) { + putchar(' '); + } else { + putchar('\n'); + graph_show_oneline(opt->graph); + } if (opt->reflog_info) { + /* + * setup_revisions() ensures that opt->reflog_info + * and opt->graph cannot both be set, + * so we don't need to worry about printing the + * graph info here. + */ show_reflog_message(opt->reflog_info, opt->commit_format == CMIT_FMT_ONELINE, opt->date_mode); @@ -304,13 +349,30 @@ void show_log(struct rev_info *opt) if (opt->add_signoff) append_signoff(&msgbuf, opt->add_signoff); - if (opt->show_log_size) + if (opt->show_log_size) { printf("log size %i\n", (int)msgbuf.len); + graph_show_oneline(opt->graph); + } - if (msgbuf.len) + /* + * Set opt->missing_newline if msgbuf doesn't + * end in a newline (including if it is empty) + */ + if (!msgbuf.len || msgbuf.buf[msgbuf.len - 1] != '\n') + opt->missing_newline = 1; + else + opt->missing_newline = 0; + + if (opt->graph) + graph_show_commit_msg(opt->graph, &msgbuf); + else fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout); - if (opt->use_terminator) + if (opt->use_terminator) { + if (!opt->missing_newline) + graph_show_padding(opt->graph); putchar('\n'); + } + strbuf_release(&msgbuf); } diff --git a/revision.c b/revision.c index a813304162..c947e0fa1e 100644 --- a/revision.c +++ b/revision.c @@ -6,6 +6,7 @@ #include "diff.h" #include "refs.h" #include "revision.h" +#include "graph.h" #include "grep.h" #include "reflog-walk.h" #include "patch-ids.h" @@ -1203,6 +1204,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch get_commit_format(arg+8, revs); continue; } + if (!prefixcmp(arg, "--graph")) { + revs->topo_order = 1; + revs->rewrite_parents = 1; + revs->graph = graph_init(); + continue; + } if (!strcmp(arg, "--root")) { revs->show_root_diff = 1; continue; @@ -1397,6 +1404,15 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch if (revs->reverse && revs->reflog_info) die("cannot combine --reverse with --walk-reflogs"); + /* + * Limitations on the graph functionality + */ + if (revs->reverse && revs->graph) + die("cannot combine --reverse with --graph"); + + if (revs->reflog_info && revs->graph) + die("cannot combine --walk-reflogs with --graph"); + return left; } @@ -1598,7 +1614,7 @@ static void gc_boundary(struct object_array *array) } } -struct commit *get_revision(struct rev_info *revs) +static struct commit *get_revision_internal(struct rev_info *revs) { struct commit *c = NULL; struct commit_list *l; @@ -1705,3 +1721,11 @@ struct commit *get_revision(struct rev_info *revs) return c; } + +struct commit *get_revision(struct rev_info *revs) +{ + struct commit *c = get_revision_internal(revs); + if (c && revs->graph) + graph_update(revs->graph, c); + return c; +} diff --git a/revision.h b/revision.h index 201bd97c58..abce5001f1 100644 --- a/revision.h +++ b/revision.h @@ -66,7 +66,8 @@ struct rev_info { /* Format info */ unsigned int shown_one:1, abbrev_commit:1, - use_terminator:1; + use_terminator:1, + missing_newline:1; enum date_mode date_mode; const char **ignore_packed; /* pretend objects in these are unpacked */ @@ -89,6 +90,9 @@ struct rev_info { /* Filter by commit log message */ struct grep_opt *grep_filter; + /* Display history graph */ + struct git_graph *graph; + /* special limits */ int skip_count; int max_count; From 0724cb86c51a230090ef12d6bfe17a67351631a8 Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Mon, 5 May 2008 00:57:03 -0700 Subject: [PATCH 024/202] graph API: eliminate unnecessary indentation This change improves the calculation of the amount of horizontal padding, so that there is always exactly 1 space of padding. Previously, most commits had 3 spaces of padding, but commits that didn't have any children in the graph had only 1 space of padding. Signed-off-by: Adam Simpkins Signed-off-by: Junio C Hamano --- graph.c | 66 +++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/graph.c b/graph.c index 616e18b13f..9d6ed30b0b 100644 --- a/graph.c +++ b/graph.c @@ -60,6 +60,12 @@ struct git_graph { * this number) */ int num_parents; + /* + * The width of the graph output for this commit. + * All rows for this commit are padded to this width, so that + * messages printed after the graph output are aligned. + */ + int width; /* * The next expansion row to print * when state is GRAPH_PRE_COMMIT @@ -207,13 +213,48 @@ static void graph_insert_into_new_columns(struct git_graph *graph, graph->num_new_columns++; } +static void graph_update_width(struct git_graph *graph, + int is_commit_in_existing_columns) +{ + /* + * Compute the width needed to display the graph for this commit. + * This is the maximum width needed for any row. All other rows + * will be padded to this width. + * + * Compute the number of columns in the widest row: + * Count each existing column (graph->num_columns), and each new + * column added by this commit. + */ + int max_cols = graph->num_columns + graph->num_parents; + + /* + * Even if the current commit has no parents, it still takes up a + * column for itself. + */ + if (graph->num_parents < 1) + max_cols++; + + /* + * We added a column for the the current commit as part of + * graph->num_parents. If the current commit was already in + * graph->columns, then we have double counted it. + */ + if (is_commit_in_existing_columns) + max_cols--; + + /* + * Each column takes up 2 spaces + */ + graph->width = max_cols * 2; +} + static void graph_update_columns(struct git_graph *graph) { struct commit_list *parent; struct column *tmp_columns; int max_new_columns; int mapping_idx; - int i, seen_this; + int i, seen_this, is_commit_in_columns; /* * Swap graph->columns with graph->new_columns @@ -259,11 +300,13 @@ static void graph_update_columns(struct git_graph *graph) */ seen_this = 0; mapping_idx = 0; + is_commit_in_columns = 1; for (i = 0; i <= graph->num_columns; i++) { struct commit *col_commit; if (i == graph->num_columns) { if (seen_this) break; + is_commit_in_columns = 0; col_commit = graph->commit; } else { col_commit = graph->columns[i].commit; @@ -290,6 +333,11 @@ static void graph_update_columns(struct git_graph *graph) while (graph->mapping_size > 1 && graph->mapping[graph->mapping_size - 1] < 0) graph->mapping_size--; + + /* + * Compute graph->width for this commit + */ + graph_update_width(graph, is_commit_in_columns); } void graph_update(struct git_graph *graph, struct commit *commit) @@ -368,22 +416,12 @@ static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb) * * This way, fields printed to the right of the graph will remain * aligned for the entire commit. - * - * This computation results in 3 extra space to the right in most - * cases, but only 1 extra space if the commit doesn't have any - * children that have already been displayed in the graph (i.e., - * if the current commit isn't in graph->columns). */ - size_t extra; - size_t final_width = graph->num_columns + graph->num_parents; - if (graph->num_parents < 1) - final_width++; - final_width *= 2; - - if (sb->len >= final_width) + int extra; + if (sb->len >= graph->width) return; - extra = final_width - sb->len; + extra = graph->width - sb->len; strbuf_addf(sb, "%*s", (int) extra, ""); } From b335d3f121536733fff3b71d4270c74a9d6e9e91 Mon Sep 17 00:00:00 2001 From: Adam Roben Date: Wed, 23 Apr 2008 15:17:43 -0400 Subject: [PATCH 025/202] Add tests for git cat-file Signed-off-by: Adam Roben Signed-off-by: Junio C Hamano --- t/t1006-cat-file.sh | 121 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100755 t/t1006-cat-file.sh diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh new file mode 100755 index 0000000000..0f7cc0371d --- /dev/null +++ b/t/t1006-cat-file.sh @@ -0,0 +1,121 @@ +#!/bin/sh + +test_description='git cat-file' + +. ./test-lib.sh + +echo_without_newline () { + printf '%s' "$*" +} + +strlen () { + echo_without_newline "$1" | wc -c | sed -e 's/^ *//' +} + +maybe_remove_timestamp () { + if test -z "$2"; then + echo_without_newline "$1" + else + echo_without_newline "$(printf '%s\n' "$1" | sed -e 's/ [0-9][0-9]* [-+][0-9][0-9][0-9][0-9]$//')" + fi +} + +run_tests () { + type=$1 + sha1=$2 + size=$3 + content=$4 + pretty_content=$5 + no_ts=$6 + + test_expect_success "$type exists" ' + git cat-file -e $sha1 + ' + + test_expect_success "Type of $type is correct" ' + test $type = "$(git cat-file -t $sha1)" + ' + + test_expect_success "Size of $type is correct" ' + test $size = "$(git cat-file -s $sha1)" + ' + + test -z "$content" || + test_expect_success "Content of $type is correct" ' + expect="$(maybe_remove_timestamp "$content" $no_ts)" + actual="$(maybe_remove_timestamp "$(git cat-file $type $sha1)" $no_ts)" + + if test "z$expect" = "z$actual" + then + : happy + else + echo "Oops: expected $expect" + echo "but got $actual" + false + fi + ' + + test_expect_success "Pretty content of $type is correct" ' + expect="$(maybe_remove_timestamp "$pretty_content" $no_ts)" + actual="$(maybe_remove_timestamp "$(git cat-file -p $sha1)" $no_ts)" + if test "z$expect" = "z$actual" + then + : happy + else + echo "Oops: expected $expect" + echo "but got $actual" + false + fi + ' +} + +hello_content="Hello World" +hello_size=$(strlen "$hello_content") +hello_sha1=$(echo_without_newline "$hello_content" | git hash-object --stdin) + +test_expect_success "setup" ' + echo_without_newline "$hello_content" > hello && + git update-index --add hello +' + +run_tests 'blob' $hello_sha1 $hello_size "$hello_content" "$hello_content" + +tree_sha1=$(git write-tree) +tree_size=33 +tree_pretty_content="100644 blob $hello_sha1 hello" + +run_tests 'tree' $tree_sha1 $tree_size "" "$tree_pretty_content" + +commit_message="Intial commit" +commit_sha1=$(echo_without_newline "$commit_message" | git commit-tree $tree_sha1) +commit_size=176 +commit_content="tree $tree_sha1 +author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 0000000000 +0000 +committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 0000000000 +0000 + +$commit_message" + +run_tests 'commit' $commit_sha1 $commit_size "$commit_content" "$commit_content" 1 + +tag_header_without_timestamp="object $hello_sha1 +type blob +tag hellotag +tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" +tag_description="This is a tag" +tag_content="$tag_header_without_timestamp 0000000000 +0000 + +$tag_description" +tag_pretty_content="$tag_header_without_timestamp Thu Jan 1 00:00:00 1970 +0000 + +$tag_description" + +tag_sha1=$(echo_without_newline "$tag_content" | git mktag) +tag_size=$(strlen "$tag_content") + +run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_pretty_content" 1 + +test_expect_success \ + "Reach a blob from a tag pointing to it" \ + "test '$hello_content' = \"\$(git cat-file blob $tag_sha1)\"" + +test_done From 9cf71b176cb175589c3ea828f7364893cc45b82f Mon Sep 17 00:00:00 2001 From: Adam Roben Date: Wed, 23 Apr 2008 15:17:44 -0400 Subject: [PATCH 026/202] git-cat-file: Small refactor of cmd_cat_file I separated the logic of parsing the arguments from the logic of fetching and outputting the data. cat_one_file now does the latter. Signed-off-by: Adam Roben Signed-off-by: Junio C Hamano --- builtin-cat-file.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/builtin-cat-file.c b/builtin-cat-file.c index f132d583d3..34a63d1280 100644 --- a/builtin-cat-file.c +++ b/builtin-cat-file.c @@ -76,31 +76,16 @@ static void pprint_tag(const unsigned char *sha1, const char *buf, unsigned long write_or_die(1, cp, endp - cp); } -int cmd_cat_file(int argc, const char **argv, const char *prefix) +static int cat_one_file(int opt, const char *exp_type, const char *obj_name) { unsigned char sha1[20]; enum object_type type; void *buf; unsigned long size; - int opt; - const char *exp_type, *obj_name; - - git_config(git_default_config); - if (argc != 3) - usage("git-cat-file [-t|-s|-e|-p|] "); - exp_type = argv[1]; - obj_name = argv[2]; if (get_sha1(obj_name, sha1)) die("Not a valid object name %s", obj_name); - opt = 0; - if ( exp_type[0] == '-' ) { - opt = exp_type[1]; - if ( !opt || exp_type[2] ) - opt = -1; /* Not a single character option */ - } - buf = NULL; switch (opt) { case 't': @@ -157,3 +142,24 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) write_or_die(1, buf, size); return 0; } + +int cmd_cat_file(int argc, const char **argv, const char *prefix) +{ + int opt; + const char *exp_type, *obj_name; + + git_config(git_default_config); + if (argc != 3) + usage("git-cat-file [-t|-s|-e|-p|] "); + exp_type = argv[1]; + obj_name = argv[2]; + + opt = 0; + if ( exp_type[0] == '-' ) { + opt = exp_type[1]; + if ( !opt || exp_type[2] ) + opt = -1; /* Not a single character option */ + } + + return cat_one_file(opt, exp_type, obj_name); +} From 4814dbe830baf5a72d8abbbfcc60eeea478b47c2 Mon Sep 17 00:00:00 2001 From: Adam Roben Date: Wed, 23 Apr 2008 15:17:45 -0400 Subject: [PATCH 027/202] git-cat-file: Make option parsing a little more flexible This will make it easier to add newer options later. Signed-off-by: Adam Roben Signed-off-by: Junio C Hamano --- builtin-cat-file.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/builtin-cat-file.c b/builtin-cat-file.c index 34a63d1280..a76bb16b49 100644 --- a/builtin-cat-file.c +++ b/builtin-cat-file.c @@ -143,23 +143,41 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) return 0; } +static const char cat_file_usage[] = "git-cat-file [-t|-s|-e|-p|] "; + int cmd_cat_file(int argc, const char **argv, const char *prefix) { - int opt; - const char *exp_type, *obj_name; + int i, opt = 0; + const char *exp_type = NULL, *obj_name = NULL; git_config(git_default_config); - if (argc != 3) - usage("git-cat-file [-t|-s|-e|-p|] "); - exp_type = argv[1]; - obj_name = argv[2]; - opt = 0; - if ( exp_type[0] == '-' ) { - opt = exp_type[1]; - if ( !opt || exp_type[2] ) - opt = -1; /* Not a single character option */ + for (i = 1; i < argc; ++i) { + const char *arg = argv[i]; + + if (!strcmp(arg, "-t") || !strcmp(arg, "-s") || !strcmp(arg, "-e") || !strcmp(arg, "-p")) { + exp_type = arg; + opt = exp_type[1]; + continue; + } + + if (arg[0] == '-') + usage(cat_file_usage); + + if (!exp_type) { + exp_type = arg; + continue; + } + + if (obj_name) + usage(cat_file_usage); + + obj_name = arg; + break; } + if (!exp_type || !obj_name) + usage(cat_file_usage); + return cat_one_file(opt, exp_type, obj_name); } From 05d5667fec9650b049f47edd8cca23a43b135365 Mon Sep 17 00:00:00 2001 From: Adam Roben Date: Wed, 23 Apr 2008 15:17:46 -0400 Subject: [PATCH 028/202] git-cat-file: Add --batch-check option This new option allows multiple objects to be specified on stdin. For each object specified, a line of the following form is printed: SP SP LF If the object does not exist in the repository, a line of the following form is printed: SP missing LF Signed-off-by: Adam Roben Signed-off-by: Junio C Hamano --- Documentation/git-cat-file.txt | 31 +++++++++++--- builtin-cat-file.c | 74 +++++++++++++++++++++++++++++++++- t/t1006-cat-file.sh | 65 +++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 7 deletions(-) diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt index df42cb10f2..d5821af309 100644 --- a/Documentation/git-cat-file.txt +++ b/Documentation/git-cat-file.txt @@ -9,12 +9,16 @@ git-cat-file - Provide content or type/size information for repository objects SYNOPSIS -------- 'git-cat-file' [-t | -s | -e | -p | ] +'git-cat-file' --batch-check < DESCRIPTION ----------- -Provides content or type of objects in the repository. The type -is required unless '-t' or '-p' is used to find the object type, -or '-s' is used to find the object size. +In the first form, provides content or type of objects in the repository. The +type is required unless '-t' or '-p' is used to find the object type, or '-s' +is used to find the object size. + +In the second form, a list of object (separated by LFs) is provided on stdin, +and the SHA1, type, and size of each object is printed on stdout. OPTIONS ------- @@ -46,6 +50,10 @@ OPTIONS or to ask for a "blob" with being a tag object that points at it. +--batch-check:: + Print the SHA1, type, and size of each object provided on stdin. May not be + combined with any other options or arguments. + OUTPUT ------ If '-t' is specified, one of the . @@ -56,9 +64,22 @@ If '-e' is specified, no output. If '-p' is specified, the contents of are pretty-printed. -Otherwise the raw (though uncompressed) contents of the will -be returned. +If is specified, the raw (though uncompressed) contents of the +will be returned. +If '--batch-check' is specified, output of the following form is printed for +each object specified fon stdin: + +------------ + SP SP LF +------------ + +Additionally, output of the following form is printed for each object specified +on stdin that does not exist in the repository: + +------------ + SP missing LF +------------ Author ------ diff --git a/builtin-cat-file.c b/builtin-cat-file.c index a76bb16b49..832cfd184a 100644 --- a/builtin-cat-file.c +++ b/builtin-cat-file.c @@ -143,11 +143,48 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) return 0; } -static const char cat_file_usage[] = "git-cat-file [-t|-s|-e|-p|] "; +static int batch_one_object(const char *obj_name) +{ + unsigned char sha1[20]; + enum object_type type; + unsigned long size; + + if (!obj_name) + return 1; + + if (get_sha1(obj_name, sha1)) { + printf("%s missing\n", obj_name); + return 0; + } + + type = sha1_object_info(sha1, &size); + if (type <= 0) + return 1; + + printf("%s %s %lu\n", sha1_to_hex(sha1), typename(type), size); + + return 0; +} + +static int batch_objects(void) +{ + struct strbuf buf; + + strbuf_init(&buf, 0); + while (strbuf_getline(&buf, stdin, '\n') != EOF) { + int error = batch_one_object(buf.buf); + if (error) + return error; + } + + return 0; +} + +static const char cat_file_usage[] = "git-cat-file [ [-t|-s|-e|-p|] | --batch-check < ]"; int cmd_cat_file(int argc, const char **argv, const char *prefix) { - int i, opt = 0; + int i, opt = 0, batch_check = 0; const char *exp_type = NULL, *obj_name = NULL; git_config(git_default_config); @@ -155,7 +192,28 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) for (i = 1; i < argc; ++i) { const char *arg = argv[i]; + if (!strcmp(arg, "--batch-check")) { + if (opt) { + error("git-cat-file: Can't use --batch-check with -%c", opt); + usage(cat_file_usage); + } else if (exp_type) { + error("git-cat-file: Can't use --batch-check when a type (\"%s\") is specified", exp_type); + usage(cat_file_usage); + } else if (obj_name) { + error("git-cat-file: Can't use --batch-check when an object (\"%s\") is specified", obj_name); + usage(cat_file_usage); + } + + batch_check = 1; + continue; + } + if (!strcmp(arg, "-t") || !strcmp(arg, "-s") || !strcmp(arg, "-e") || !strcmp(arg, "-p")) { + if (batch_check) { + error("git-cat-file: Can't use %s with --batch-check", arg); + usage(cat_file_usage); + } + exp_type = arg; opt = exp_type[1]; continue; @@ -165,6 +223,11 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) usage(cat_file_usage); if (!exp_type) { + if (batch_check) { + error("git-cat-file: Can't specify a type (\"%s\") with --batch-check", arg); + usage(cat_file_usage); + } + exp_type = arg; continue; } @@ -172,10 +235,17 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) if (obj_name) usage(cat_file_usage); + // We should have hit one of the earlier if (batch_check) cases before + // getting here. + assert(!batch_check); + obj_name = arg; break; } + if (batch_check) + return batch_objects(); + if (!exp_type || !obj_name) usage(cat_file_usage); diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh index 0f7cc0371d..decba02740 100755 --- a/t/t1006-cat-file.sh +++ b/t/t1006-cat-file.sh @@ -67,6 +67,19 @@ run_tests () { false fi ' + + test_expect_success "--batch-check output of $type is correct" ' + expect="$sha1 $type $size" + actual="$(echo_without_newline $sha1 | git cat-file --batch-check)" + if test "z$expect" = "z$actual" + then + : happy + else + echo "Oops: expected $expect" + echo "but got $actual" + false + fi + ' } hello_content="Hello World" @@ -118,4 +131,56 @@ test_expect_success \ "Reach a blob from a tag pointing to it" \ "test '$hello_content' = \"\$(git cat-file blob $tag_sha1)\"" +for opt in t s e p +do + test_expect_success "Passing -$opt with --batch-check fails" ' + test_must_fail git cat-file --batch-check -$opt $hello_sha1 + ' + + test_expect_success "Passing --batch-check with -$opt fails" ' + test_must_fail git cat-file -$opt --batch-check $hello_sha1 + ' +done + +test_expect_success "Passing with --batch-check fails" ' + test_must_fail git cat-file --batch-check blob $hello_sha1 +' + +test_expect_success "Passing --batch-check with fails" ' + test_must_fail git cat-file blob --batch-check $hello_sha1 +' + +test_expect_success "Passing sha1 with --batch-check fails" ' + test_must_fail git cat-file --batch-check $hello_sha1 +' + +test_expect_success "--batch-check for a non-existent object" ' + test "deadbeef missing" = \ + "$(echo_without_newline deadbeef | git cat-file --batch-check)" +' + +test_expect_success "--batch-check for an emtpy line" ' + test " missing" = "$(echo | git cat-file --batch-check)" +' + +batch_check_input="$hello_sha1 +$tree_sha1 +$commit_sha1 +$tag_sha1 +deadbeef + +" + +batch_check_output="$hello_sha1 blob $hello_size +$tree_sha1 tree $tree_size +$commit_sha1 commit $commit_size +$tag_sha1 tag $tag_size +deadbeef missing + missing" + +test_expect_success "--batch-check with multiple sha1s gives correct format" ' + test "$batch_check_output" = \ + "$(echo_without_newline "$batch_check_input" | git cat-file --batch-check)" +' + test_done From a8128ed62858063e29edc066b14b8b0fa6257cc2 Mon Sep 17 00:00:00 2001 From: Adam Roben Date: Wed, 23 Apr 2008 15:17:47 -0400 Subject: [PATCH 029/202] git-cat-file: Add --batch option --batch is similar to --batch-check, except that the contents of each object is also printed. The output's form is: SP SP LF LF Signed-off-by: Adam Roben Signed-off-by: Junio C Hamano --- Documentation/git-cat-file.txt | 18 +++++++-- builtin-cat-file.c | 63 ++++++++++++++++++++--------- t/t1006-cat-file.sh | 74 ++++++++++++++++++++++++++-------- 3 files changed, 117 insertions(+), 38 deletions(-) diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt index d5821af309..f6c394c482 100644 --- a/Documentation/git-cat-file.txt +++ b/Documentation/git-cat-file.txt @@ -9,7 +9,7 @@ git-cat-file - Provide content or type/size information for repository objects SYNOPSIS -------- 'git-cat-file' [-t | -s | -e | -p | ] -'git-cat-file' --batch-check < +'git-cat-file' [--batch | --batch-check] < DESCRIPTION ----------- @@ -50,6 +50,10 @@ OPTIONS or to ask for a "blob" with being a tag object that points at it. +--batch:: + Print the SHA1, type, size, and contents of each object provided on + stdin. May not be combined with any other options or arguments. + --batch-check:: Print the SHA1, type, and size of each object provided on stdin. May not be combined with any other options or arguments. @@ -67,6 +71,14 @@ If '-p' is specified, the contents of are pretty-printed. If is specified, the raw (though uncompressed) contents of the will be returned. +If '--batch' is specified, output of the following form is printed for each +object specified on stdin: + +------------ + SP SP LF + LF +------------ + If '--batch-check' is specified, output of the following form is printed for each object specified fon stdin: @@ -74,8 +86,8 @@ each object specified fon stdin: SP SP LF ------------ -Additionally, output of the following form is printed for each object specified -on stdin that does not exist in the repository: +For both '--batch' and '--batch-check', output of the following form is printed +for each object specified on stdin that does not exist in the repository: ------------ SP missing LF diff --git a/builtin-cat-file.c b/builtin-cat-file.c index 832cfd184a..b4d0c2545f 100644 --- a/builtin-cat-file.c +++ b/builtin-cat-file.c @@ -143,11 +143,12 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) return 0; } -static int batch_one_object(const char *obj_name) +static int batch_one_object(const char *obj_name, int print_contents) { unsigned char sha1[20]; enum object_type type; unsigned long size; + void *contents = contents; if (!obj_name) return 1; @@ -157,22 +158,33 @@ static int batch_one_object(const char *obj_name) return 0; } - type = sha1_object_info(sha1, &size); + if (print_contents) + contents = read_sha1_file(sha1, &type, &size); + else + type = sha1_object_info(sha1, &size); + if (type <= 0) return 1; printf("%s %s %lu\n", sha1_to_hex(sha1), typename(type), size); + fflush(stdout); + + if (print_contents) { + write_or_die(1, contents, size); + printf("\n"); + fflush(stdout); + } return 0; } -static int batch_objects(void) +static int batch_objects(int print_contents) { struct strbuf buf; strbuf_init(&buf, 0); while (strbuf_getline(&buf, stdin, '\n') != EOF) { - int error = batch_one_object(buf.buf); + int error = batch_one_object(buf.buf, print_contents); if (error) return error; } @@ -180,37 +192,51 @@ static int batch_objects(void) return 0; } -static const char cat_file_usage[] = "git-cat-file [ [-t|-s|-e|-p|] | --batch-check < ]"; +static const char cat_file_usage[] = "git-cat-file [ [-t|-s|-e|-p|] | [--batch|--batch-check] < ]"; int cmd_cat_file(int argc, const char **argv, const char *prefix) { - int i, opt = 0, batch_check = 0; + int i, opt = 0, batch = 0, batch_check = 0; const char *exp_type = NULL, *obj_name = NULL; git_config(git_default_config); for (i = 1; i < argc; ++i) { const char *arg = argv[i]; + int is_batch = 0, is_batch_check = 0; - if (!strcmp(arg, "--batch-check")) { + is_batch = !strcmp(arg, "--batch"); + if (!is_batch) + is_batch_check = !strcmp(arg, "--batch-check"); + + if (is_batch || is_batch_check) { if (opt) { - error("git-cat-file: Can't use --batch-check with -%c", opt); + error("git-cat-file: Can't use %s with -%c", arg, opt); usage(cat_file_usage); } else if (exp_type) { - error("git-cat-file: Can't use --batch-check when a type (\"%s\") is specified", exp_type); + error("git-cat-file: Can't use %s when a type (\"%s\") is specified", arg, exp_type); usage(cat_file_usage); } else if (obj_name) { - error("git-cat-file: Can't use --batch-check when an object (\"%s\") is specified", obj_name); + error("git-cat-file: Can't use %s when an object (\"%s\") is specified", arg, obj_name); usage(cat_file_usage); } - batch_check = 1; + if ((is_batch && batch_check) || (is_batch_check && batch)) { + error("git-cat-file: Can't use %s with %s", arg, is_batch ? "--batch-check" : "--batch"); + usage(cat_file_usage); + } + + if (is_batch) + batch = 1; + else + batch_check = 1; + continue; } if (!strcmp(arg, "-t") || !strcmp(arg, "-s") || !strcmp(arg, "-e") || !strcmp(arg, "-p")) { - if (batch_check) { - error("git-cat-file: Can't use %s with --batch-check", arg); + if (batch || batch_check) { + error("git-cat-file: Can't use %s with %s", arg, batch ? "--batch" : "--batch-check"); usage(cat_file_usage); } @@ -223,8 +249,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) usage(cat_file_usage); if (!exp_type) { - if (batch_check) { - error("git-cat-file: Can't specify a type (\"%s\") with --batch-check", arg); + if (batch || batch_check) { + error("git-cat-file: Can't specify a type (\"%s\") with %s", arg, batch ? "--batch" : "--batch-check"); usage(cat_file_usage); } @@ -235,16 +261,17 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) if (obj_name) usage(cat_file_usage); - // We should have hit one of the earlier if (batch_check) cases before + // We should have hit one of the earlier if (batch || batch_check) cases before // getting here. + assert(!batch); assert(!batch_check); obj_name = arg; break; } - if (batch_check) - return batch_objects(); + if (batch || batch_check) + return batch_objects(batch); if (!exp_type || !obj_name) usage(cat_file_usage); diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh index decba02740..d5696765b1 100755 --- a/t/t1006-cat-file.sh +++ b/t/t1006-cat-file.sh @@ -28,6 +28,9 @@ run_tests () { pretty_content=$5 no_ts=$6 + batch_output="$sha1 $type $size +$content" + test_expect_success "$type exists" ' git cat-file -e $sha1 ' @@ -68,6 +71,20 @@ run_tests () { fi ' + test -z "$content" || + test_expect_success "--batch output of $type is correct" ' + expect="$(maybe_remove_timestamp "$batch_output" $no_ts)" + actual="$(maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" no_ts)" + if test "z$expect" = "z$actual" + then + : happy + else + echo "Oops: expected $expect" + echo "but got $actual" + false + fi + ' + test_expect_success "--batch-check output of $type is correct" ' expect="$sha1 $type $size" actual="$(echo_without_newline $sha1 | git cat-file --batch-check)" @@ -131,29 +148,32 @@ test_expect_success \ "Reach a blob from a tag pointing to it" \ "test '$hello_content' = \"\$(git cat-file blob $tag_sha1)\"" -for opt in t s e p +for batch in batch batch-check do - test_expect_success "Passing -$opt with --batch-check fails" ' - test_must_fail git cat-file --batch-check -$opt $hello_sha1 + for opt in t s e p + do + test_expect_success "Passing -$opt with --$batch fails" ' + test_must_fail git cat-file --$batch -$opt $hello_sha1 + ' + + test_expect_success "Passing --$batch with -$opt fails" ' + test_must_fail git cat-file -$opt --$batch $hello_sha1 + ' + done + + test_expect_success "Passing with --$batch fails" ' + test_must_fail git cat-file --$batch blob $hello_sha1 ' - test_expect_success "Passing --batch-check with -$opt fails" ' - test_must_fail git cat-file -$opt --batch-check $hello_sha1 + test_expect_success "Passing --$batch with fails" ' + test_must_fail git cat-file blob --$batch $hello_sha1 + ' + + test_expect_success "Passing sha1 with --$batch fails" ' + test_must_fail git cat-file --$batch $hello_sha1 ' done -test_expect_success "Passing with --batch-check fails" ' - test_must_fail git cat-file --batch-check blob $hello_sha1 -' - -test_expect_success "Passing --batch-check with fails" ' - test_must_fail git cat-file blob --batch-check $hello_sha1 -' - -test_expect_success "Passing sha1 with --batch-check fails" ' - test_must_fail git cat-file --batch-check $hello_sha1 -' - test_expect_success "--batch-check for a non-existent object" ' test "deadbeef missing" = \ "$(echo_without_newline deadbeef | git cat-file --batch-check)" @@ -163,6 +183,26 @@ test_expect_success "--batch-check for an emtpy line" ' test " missing" = "$(echo | git cat-file --batch-check)" ' +batch_input="$hello_sha1 +$commit_sha1 +$tag_sha1 +deadbeef + +" + +batch_output="$hello_sha1 blob $hello_size +$hello_content +$commit_sha1 commit $commit_size +$commit_content +$tag_sha1 tag $tag_size +$tag_content +deadbeef missing + missing" + +test_expect_success \ + "--batch with multiple sha1s gives correct format" \ + "test \"\$(maybe_remove_timestamp \"$batch_output\" 1)\" = \"\$(maybe_remove_timestamp \"\$(echo_without_newline \"$batch_input\" | git cat-file --batch)\" 1)\"" + batch_check_input="$hello_sha1 $tree_sha1 $commit_sha1 From ccc1297226b184c40459e9d373cc9eebfb7bd898 Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Fri, 9 May 2008 23:01:55 -0500 Subject: [PATCH 030/202] repack: modify behavior of -A option to leave unreferenced objects unpacked The previous behavior of the -A option was to retain any previously packed objects which had become unreferenced, and place them into the newly created pack file. Since git-gc, when run automatically with the --auto option, calls repack with the -A option, this had the effect of retaining unreferenced packed objects indefinitely. To avoid this scenario, the user was required to run git-gc with the little known --prune option or to manually run repack with the -a option. This patch changes the behavior of the -A option so that unreferenced objects that exist in any pack file being replaced, will be unpacked into the repository. The unreferenced loose objects can then be garbage collected by git-gc (i.e. git-prune) based on the gc.pruneExpire setting. Also add new tests for checking whether unreferenced objects which were previously packed are properly left in the repository unpacked after repacking. Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- git-repack.sh | 18 ++++++++--- t/t7701-repack-unpack-unreachable.sh | 47 ++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 5 deletions(-) create mode 100755 t/t7701-repack-unpack-unreachable.sh diff --git a/git-repack.sh b/git-repack.sh index e18eb3f5dc..a0e06ed076 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -30,7 +30,7 @@ do -n) no_update_info=t ;; -a) all_into_one=t ;; -A) all_into_one=t - keep_unreachable=--keep-unreachable ;; + keep_unreachable=t ;; -d) remove_redundant=t ;; -q) quiet=-q ;; -f) no_reuse=--no-reuse-object ;; @@ -78,9 +78,6 @@ case ",$all_into_one," in if test -z "$args" then args='--unpacked --incremental' - elif test -n "$keep_unreachable" - then - args="$args $keep_unreachable" fi ;; esac @@ -130,7 +127,18 @@ then do case " $fullbases " in *" $e "*) ;; - *) rm -f "$e.pack" "$e.idx" "$e.keep" ;; + *) + rm -f "$e.idx" "$e.keep" + if test -n "$keep_unreachable" && + test -f "$e.pack" + then + git unpack-objects < "$e.pack" || { + echo >&2 "Failed unpacking unreachable objects from redundant pack file $e.pack" + exit 1 + } + fi + rm -f "$e.pack" + ;; esac done ) diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh new file mode 100755 index 0000000000..6a5211f187 --- /dev/null +++ b/t/t7701-repack-unpack-unreachable.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +test_description='git-repack works correctly' + +. ./test-lib.sh + +test_expect_success '-A option leaves unreachable objects unpacked' ' + echo content > file1 && + git add . && + git commit -m initial_commit && + # create a transient branch with unique content + git checkout -b transient_branch && + echo more content >> file1 && + # record the objects created in the database for file, commit, tree + fsha1=$(git hash-object file1) && + git commit -a -m more_content && + csha1=$(git rev-parse HEAD^{commit}) && + tsha1=$(git rev-parse HEAD^{tree}) && + git checkout master && + echo even more content >> file1 && + git commit -a -m even_more_content && + # delete the transient branch + git branch -D transient_branch && + # pack the repo + git repack -A -d -l && + # verify objects are packed in repository + test 3 = $(git verify-pack -v -- .git/objects/pack/*.idx | + grep -e "^$fsha1 " -e "^$csha1 " -e "^$tsha1 " | + sort | uniq | wc -l) && + git show $fsha1 && + git show $csha1 && + git show $tsha1 && + # now expire the reflog + sleep 1 && + git reflog expire --expire-unreachable=now --all && + # and repack + git repack -A -d -l && + # verify objects are retained unpacked + test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx | + grep -e "^$fsha1 " -e "^$csha1 " -e "^$tsha1 " | + sort | uniq | wc -l) && + git show $fsha1 && + git show $csha1 && + git show $tsha1 +' + +test_done From a37cce3b23afa2c88baa2f1369c585e05bcf4526 Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Fri, 9 May 2008 23:01:56 -0500 Subject: [PATCH 031/202] git-gc: always use -A when manually repacking Now that repack -A will leave unreferenced objects unpacked, there is no reason to use the -a option to repack (which will discard unreferenced objects). The unpacked unreferenced objects will not be repacked by a subsequent repack, and will eventually be pruned by git-gc based on the gc.pruneExpire config option. --- builtin-gc.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/builtin-gc.c b/builtin-gc.c index f99ebc7926..6db2f513b7 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -256,17 +256,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix) "performance. You may also\n" "run \"git gc\" manually. See " "\"git help gc\" for more information.\n"); - } else { - /* - * Use safer (for shared repos) "-A" option to - * repack when not pruning. Auto-gc makes its - * own decision. - */ - if (prune) - append_option(argv_repack, "-a", MAX_ADD); - else - append_option(argv_repack, "-A", MAX_ADD); - } + } else + append_option(argv_repack, "-A", MAX_ADD); if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD)) return error(FAILED_RUN, argv_pack_refs[0]); From 9e7d501990a9c63db264f3185fc311f446196427 Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Fri, 9 May 2008 23:01:57 -0500 Subject: [PATCH 032/202] builtin-gc.c: deprecate --prune, it now really has no effect --- builtin-gc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/builtin-gc.c b/builtin-gc.c index 6db2f513b7..48f7d95f97 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -219,7 +219,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) char buf[80]; struct option builtin_gc_options[] = { - OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects"), + OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects (deprecated)"), OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"), OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"), OPT_BOOLEAN('q', "quiet", &quiet, "suppress progress reports"), @@ -249,7 +249,6 @@ int cmd_gc(int argc, const char **argv, const char *prefix) /* * Auto-gc should be least intrusive as possible. */ - prune = 0; if (!need_to_gc()) return 0; fprintf(stderr, "Auto packing your repository for optimum " From 2455406ab1b0e2b0ad9e5273b9aef6c739d5b8fe Mon Sep 17 00:00:00 2001 From: Dmitry Potapov Date: Sun, 11 May 2008 18:16:39 +0200 Subject: [PATCH 033/202] git-init: autodetect core.ignorecase We already detect if symbolic links are supported by the filesystem. This patch adds autodetect for case-insensitive filesystems, such as VFAT and others. Signed-off-by: Dmitry Potapov Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano --- builtin-init-db.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/builtin-init-db.c b/builtin-init-db.c index a76f5d3474..b061317275 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -255,8 +255,8 @@ static int create_default_files(const char *git_dir, const char *template_path) git_config_set("core.worktree", work_tree); } - /* Check if symlink is supported in the work tree */ if (!reinit) { + /* Check if symlink is supported in the work tree */ path[len] = 0; strcpy(path + len, "tXXXXXX"); if (!close(xmkstemp(path)) && @@ -267,6 +267,12 @@ static int create_default_files(const char *git_dir, const char *template_path) unlink(path); /* good */ else git_config_set("core.symlinks", "false"); + + /* Check if the filesystem is case-insensitive */ + path[len] = 0; + strcpy(path + len, "CoNfIg"); + if (!access(path, F_OK)) + git_config_set("core.ignorecase", "true"); } return reinit; From 1c51c7d7d97acb447050a820dc39d242dc29c5df Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Sun, 11 May 2008 18:16:40 +0200 Subject: [PATCH 034/202] t0050: Test autodetect core.ignorecase Verify if core.ignorecase is automatically set to 'true' during repository initialization if the file system is case insensitive, and unset or 'false' otherwise. Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano --- t/t0050-filesystem.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh index 3fbad77811..66d3647bda 100755 --- a/t/t0050-filesystem.sh +++ b/t/t0050-filesystem.sh @@ -7,6 +7,7 @@ test_description='Various filesystem issues' auml=`printf '\xc3\xa4'` aumlcdiar=`printf '\x61\xcc\x88'` +case_insensitive= test_expect_success 'see if we expect ' ' test_case=test_expect_success @@ -17,6 +18,7 @@ test_expect_success 'see if we expect ' ' if test "$(cat junk/CamelCase)" != good then test_case=test_expect_failure + case_insensitive=t say "will test on a case insensitive filesystem" fi && rm -fr junk && @@ -32,6 +34,20 @@ test_expect_success 'see if we expect ' ' rm -fr junk ' +if test "$case_insensitive" +then +test_expect_success "detection of case insensitive filesystem during repo init" ' + + test $(git config --bool core.ignorecase) = true +' +else +test_expect_success "detection of case insensitive filesystem during repo init" ' + + ! git config --bool core.ignorecase >/dev/null || + test $(git config --bool core.ignorecase) = false +' +fi + test_expect_success "setup case tests" ' touch camelcase && From b4a299d87ca7a1d39c699ee2af28305e57f3b0ae Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Sun, 11 May 2008 18:16:41 +0200 Subject: [PATCH 035/202] t0050: Set core.ignorecase case to activate case insensitivity Case insensitive file handling is only active when core.ignorecase = true. Hence, we need to set it to give the tests in t0050 a chance to succeed. Setting core.ignorecase explicitly allows to test some aspects of case handling even on case sensitive file systems. Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano --- t/t0050-filesystem.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh index 66d3647bda..399b45df1f 100755 --- a/t/t0050-filesystem.sh +++ b/t/t0050-filesystem.sh @@ -50,6 +50,7 @@ fi test_expect_success "setup case tests" ' + git config core.ignorecase true && touch camelcase && git add camelcase && git commit -m "initial" && From 8a19aaab6394df2c6138d5b8b1411eb00bfcf442 Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Sun, 11 May 2008 18:16:42 +0200 Subject: [PATCH 036/202] t0050: Add test for case insensitive add Add should recognize if a file is added with a different case and add the file using its original name. Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano --- t/t0050-filesystem.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh index 399b45df1f..0e33c4bdc7 100755 --- a/t/t0050-filesystem.sh +++ b/t/t0050-filesystem.sh @@ -77,6 +77,16 @@ $test_case 'merge (case change)' ' ' +$test_case 'add (with different case)' ' + + git reset --hard initial && + rm camelcase && + echo 1 >CamelCase && + git add CamelCase && + test $(git-ls-files | grep -i camelcase | wc -l) = 1 + +' + test_expect_success "setup unicode normalization tests" ' test_create_repo unicode && From ad1012ebde8be471098b5d476a98a02c76c8e75a Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Mon, 12 May 2008 17:12:36 +0200 Subject: [PATCH 037/202] revision.c: really honor --first-parent In add_parents_to_list, if any parent of a revision had already been SEEN, the current code would continue with the next parent, skipping the test for --first-parent. This patch inverts the test for SEEN so that the test for --first-parent is always performed. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano --- revision.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/revision.c b/revision.c index bcfcd2a82b..6f5b548a32 100644 --- a/revision.c +++ b/revision.c @@ -467,10 +467,10 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit, str if (parse_commit(p) < 0) return -1; p->object.flags |= left_flag; - if (p->object.flags & SEEN) - continue; - p->object.flags |= SEEN; - insert_by_date(p, list); + if (!(p->object.flags & SEEN)) { + p->object.flags |= SEEN; + insert_by_date(p, list); + } if(revs->first_parent_only) break; } From 27554e900dd0e2b91b578f2e01c38b3eba746a77 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 12 May 2008 17:09:49 -0700 Subject: [PATCH 038/202] git-svn: add test for --add-author-from and --use-log-author This adds a minimalistic set of tests to recently added --add-author-from option and existing --use-log-author option to git-svn. Signed-off-by: Junio C Hamano --- t/t9122-git-svn-author.sh | 70 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100755 t/t9122-git-svn-author.sh diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh new file mode 100755 index 0000000000..8c58f0b8d7 --- /dev/null +++ b/t/t9122-git-svn-author.sh @@ -0,0 +1,70 @@ +#!/bin/sh + +test_description='git svn authorship' +. ./lib-git-svn.sh + +test_expect_success 'setup svn repository' ' + svn checkout "$svnrepo" work.svn && + ( + cd work.svn && + echo >file + svn add file + svn commit -m "first commit" file + ) +' + +test_expect_success 'interact with it via git-svn' ' + mkdir work.git && + ( + cd work.git && + git svn init "$svnrepo" + git svn fetch && + + echo modification >file && + test_tick && + git commit -a -m second && + + test_tick && + git svn dcommit && + + echo "further modification" >file && + test_tick && + git commit -a -m third && + + test_tick && + git svn --add-author-from dcommit && + + echo "yet further modification" >file && + test_tick && + git commit -a -m fourth && + + test_tick && + git svn --add-author-from --use-log-author dcommit && + + git log && + + git show -s HEAD^^ >../actual.2 && + git show -s HEAD^ >../actual.3 && + git show -s HEAD >../actual.4 + + ) && + + # Make sure that --add-author-from without --use-log-author + # did not affect the authorship information + myself=$(grep "^Author: " actual.2) && + unaffected=$(grep "^Author: " actual.3) && + test "z$myself" = "z$unaffected" && + + # Make sure lack of --add-author-from did not add cruft + ! grep "^ From: A U Thor " actual.2 && + + # Make sure --add-author-from added cruft + grep "^ From: A U Thor " actual.3 && + grep "^ From: A U Thor " actual.4 && + + # Make sure --add-author-from with --use-log-author affected + # the authorship information + grep "^Author: A U Thor " actual.4 +' + +test_done From 0a02186f924aee1bd69f18ed01f645aa332ce0d1 Mon Sep 17 00:00:00 2001 From: Dustin Sallings Date: Sat, 10 May 2008 15:36:29 -0700 Subject: [PATCH 039/202] Allow tracking branches to set up rebase by default. Change cd67e4d4 introduced a new configuration parameter that told pull to automatically perform a rebase instead of a merge. This change provides a configuration option to enable this feature automatically when creating a new branch. If the variable branch.autosetuprebase applies for a branch that's being created, that branch will have branch..rebase set to true. Signed-off-by: Dustin Sallings Signed-off-by: Junio C Hamano --- Documentation/config.txt | 15 +++ branch.c | 22 +++- cache.h | 8 ++ config.c | 15 +++ environment.c | 1 + t/t3200-branch.sh | 234 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 294 insertions(+), 1 deletion(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index a6fc5a2cfd..217980f48d 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -399,6 +399,21 @@ branch.autosetupmerge:: done when the starting point is either a local branch or remote branch. This option defaults to true. +branch.autosetuprebase:: + When a new branch is created with `git-branch` or `git-checkout` + that tracks another branch, this variable tells git to set + up pull to rebase instead of merge (see "branch..rebase"). + When `never`, rebase is never automatically set to true. + When `local`, rebase is set to true for tracked branches of + other local branches. + When `remote`, rebase is set to true for tracked branches of + remote branches. + When `always`, rebase will be set to true for all tracking + branches. + See "branch.autosetupmerge" for details on how to set up a + branch to track another branch. + This option defaults to never. + branch..remote:: When in branch , it tells `git fetch` which remote to fetch. If this option is not given, `git fetch` defaults to remote "origin". diff --git a/branch.c b/branch.c index daf862e728..56e949232c 100644 --- a/branch.c +++ b/branch.c @@ -32,6 +32,21 @@ static int find_tracked_branch(struct remote *remote, void *priv) return 0; } +static int should_setup_rebase(const struct tracking *tracking) +{ + switch (autorebase) { + case AUTOREBASE_NEVER: + return 0; + case AUTOREBASE_LOCAL: + return tracking->remote == NULL; + case AUTOREBASE_REMOTE: + return tracking->remote != NULL; + case AUTOREBASE_ALWAYS: + return 1; + } + return 0; +} + /* * This is called when new_ref is branched off of orig_ref, and tries * to infer the settings for branch..{remote,merge} from the @@ -69,9 +84,14 @@ static int setup_tracking(const char *new_ref, const char *orig_ref, git_config_set(key, tracking.remote ? tracking.remote : "."); sprintf(key, "branch.%s.merge", new_ref); git_config_set(key, tracking.src ? tracking.src : orig_ref); - free(tracking.src); printf("Branch %s set up to track %s branch %s.\n", new_ref, tracking.remote ? "remote" : "local", orig_ref); + if (should_setup_rebase(&tracking)) { + sprintf(key, "branch.%s.rebase", new_ref); + git_config_set(key, "true"); + printf("This branch will rebase on pull.\n"); + } + free(tracking.src); return 0; } diff --git a/cache.h b/cache.h index 7fb8f3359d..3442130a59 100644 --- a/cache.h +++ b/cache.h @@ -434,7 +434,15 @@ enum branch_track { BRANCH_TRACK_EXPLICIT, }; +enum rebase_setup_type { + AUTOREBASE_NEVER = 0, + AUTOREBASE_LOCAL, + AUTOREBASE_REMOTE, + AUTOREBASE_ALWAYS, +}; + extern enum branch_track git_branch_track; +extern enum rebase_setup_type autorebase; #define GIT_REPO_VERSION 0 extern int repository_format_version; diff --git a/config.c b/config.c index b0ada515b9..cf2bfd35c8 100644 --- a/config.c +++ b/config.c @@ -487,6 +487,21 @@ int git_default_config(const char *var, const char *value) git_branch_track = git_config_bool(var, value); return 0; } + if (!strcmp(var, "branch.autosetuprebase")) { + if (!value) + return config_error_nonbool(var); + else if (!strcmp(value, "never")) + autorebase = AUTOREBASE_NEVER; + else if (!strcmp(value, "local")) + autorebase = AUTOREBASE_LOCAL; + else if (!strcmp(value, "remote")) + autorebase = AUTOREBASE_REMOTE; + else if (!strcmp(value, "always")) + autorebase = AUTOREBASE_ALWAYS; + else + return error("Malformed value for %s", var); + return 0; + } /* Add other config variables here and to Documentation/config.txt. */ return 0; diff --git a/environment.c b/environment.c index fcd1ee5ef8..4892a302bd 100644 --- a/environment.c +++ b/environment.c @@ -38,6 +38,7 @@ int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */ enum safe_crlf safe_crlf = SAFE_CRLF_WARN; unsigned whitespace_rule_cfg = WS_DEFAULT_RULE; enum branch_track git_branch_track = BRANCH_TRACK_REMOTE; +enum rebase_setup_type autorebase = AUTOREBASE_NEVER; /* This is set by setup_git_dir_gently() and/or git_default_config() */ char *git_work_tree_cfg; diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index cb5f7a4441..8d8768688d 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -224,4 +224,238 @@ test_expect_success 'avoid ambiguous track' ' test -z "$(git config branch.all1.merge)" ' +test_expect_success 'autosetuprebase local on a tracked local branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + git config branch.autosetuprebase local && + (git show-ref -q refs/remotes/local/o || git-fetch local) && + git branch mybase && + git branch --track myr1 mybase && + test "$(git config branch.myr1.remote)" = . && + test "$(git config branch.myr1.merge)" = refs/heads/mybase && + test "$(git config branch.myr1.rebase)" = true +' + +test_expect_success 'autosetuprebase always on a tracked local branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + git config branch.autosetuprebase always && + (git show-ref -q refs/remotes/local/o || git-fetch local) && + git branch mybase2 && + git branch --track myr2 mybase && + test "$(git config branch.myr2.remote)" = . && + test "$(git config branch.myr2.merge)" = refs/heads/mybase && + test "$(git config branch.myr2.rebase)" = true +' + +test_expect_success 'autosetuprebase remote on a tracked local branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + git config branch.autosetuprebase remote && + (git show-ref -q refs/remotes/local/o || git-fetch local) && + git branch mybase3 && + git branch --track myr3 mybase2 && + test "$(git config branch.myr3.remote)" = . && + test "$(git config branch.myr3.merge)" = refs/heads/mybase2 && + ! test "$(git config branch.myr3.rebase)" = true +' + +test_expect_success 'autosetuprebase never on a tracked local branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + git config branch.autosetuprebase never && + (git show-ref -q refs/remotes/local/o || git-fetch local) && + git branch mybase4 && + git branch --track myr4 mybase2 && + test "$(git config branch.myr4.remote)" = . && + test "$(git config branch.myr4.merge)" = refs/heads/mybase2 && + ! test "$(git config branch.myr4.rebase)" = true +' + +test_expect_success 'autosetuprebase local on a tracked remote branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + git config branch.autosetuprebase local && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --track myr5 local/master && + test "$(git config branch.myr5.remote)" = local && + test "$(git config branch.myr5.merge)" = refs/heads/master && + ! test "$(git config branch.myr5.rebase)" = true +' + +test_expect_success 'autosetuprebase never on a tracked remote branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + git config branch.autosetuprebase never && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --track myr6 local/master && + test "$(git config branch.myr6.remote)" = local && + test "$(git config branch.myr6.merge)" = refs/heads/master && + ! test "$(git config branch.myr6.rebase)" = true +' + +test_expect_success 'autosetuprebase remote on a tracked remote branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + git config branch.autosetuprebase remote && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --track myr7 local/master && + test "$(git config branch.myr7.remote)" = local && + test "$(git config branch.myr7.merge)" = refs/heads/master && + test "$(git config branch.myr7.rebase)" = true +' + +test_expect_success 'autosetuprebase always on a tracked remote branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + git config branch.autosetuprebase remote && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --track myr8 local/master && + test "$(git config branch.myr8.remote)" = local && + test "$(git config branch.myr8.merge)" = refs/heads/master && + test "$(git config branch.myr8.rebase)" = true +' + +test_expect_success 'autosetuprebase unconfigured on a tracked remote branch' ' + git config --unset branch.autosetuprebase && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --track myr9 local/master && + test "$(git config branch.myr9.remote)" = local && + test "$(git config branch.myr9.merge)" = refs/heads/master && + test "z$(git config branch.myr9.rebase)" = z +' + +test_expect_success 'autosetuprebase unconfigured on a tracked local branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/o || git-fetch local) && + git branch mybase10 && + git branch --track myr10 mybase2 && + test "$(git config branch.myr10.remote)" = . && + test "$(git config branch.myr10.merge)" = refs/heads/mybase2 && + test "z$(git config branch.myr10.rebase)" = z +' + +test_expect_success 'autosetuprebase unconfigured on untracked local branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr11 mybase2 && + test "z$(git config branch.myr11.remote)" = z && + test "z$(git config branch.myr11.merge)" = z && + test "z$(git config branch.myr11.rebase)" = z +' + +test_expect_success 'autosetuprebase unconfigured on untracked remote branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr12 local/master && + test "z$(git config branch.myr12.remote)" = z && + test "z$(git config branch.myr12.merge)" = z && + test "z$(git config branch.myr12.rebase)" = z +' + +test_expect_success 'autosetuprebase never on an untracked local branch' ' + git config branch.autosetuprebase never && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr13 mybase2 && + test "z$(git config branch.myr13.remote)" = z && + test "z$(git config branch.myr13.merge)" = z && + test "z$(git config branch.myr13.rebase)" = z +' + +test_expect_success 'autosetuprebase local on an untracked local branch' ' + git config branch.autosetuprebase local && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr14 mybase2 && + test "z$(git config branch.myr14.remote)" = z && + test "z$(git config branch.myr14.merge)" = z && + test "z$(git config branch.myr14.rebase)" = z +' + +test_expect_success 'autosetuprebase remote on an untracked local branch' ' + git config branch.autosetuprebase remote && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr15 mybase2 && + test "z$(git config branch.myr15.remote)" = z && + test "z$(git config branch.myr15.merge)" = z && + test "z$(git config branch.myr15.rebase)" = z +' + +test_expect_success 'autosetuprebase always on an untracked local branch' ' + git config branch.autosetuprebase always && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr16 mybase2 && + test "z$(git config branch.myr16.remote)" = z && + test "z$(git config branch.myr16.merge)" = z && + test "z$(git config branch.myr16.rebase)" = z +' + +test_expect_success 'autosetuprebase never on an untracked remote branch' ' + git config branch.autosetuprebase never && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr17 local/master && + test "z$(git config branch.myr17.remote)" = z && + test "z$(git config branch.myr17.merge)" = z && + test "z$(git config branch.myr17.rebase)" = z +' + +test_expect_success 'autosetuprebase local on an untracked remote branch' ' + git config branch.autosetuprebase local && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr18 local/master && + test "z$(git config branch.myr18.remote)" = z && + test "z$(git config branch.myr18.merge)" = z && + test "z$(git config branch.myr18.rebase)" = z +' + +test_expect_success 'autosetuprebase remote on an untracked remote branch' ' + git config branch.autosetuprebase remote && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr19 local/master && + test "z$(git config branch.myr19.remote)" = z && + test "z$(git config branch.myr19.merge)" = z && + test "z$(git config branch.myr19.rebase)" = z +' + +test_expect_success 'autosetuprebase always on an untracked remote branch' ' + git config branch.autosetuprebase always && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr20 local/master && + test "z$(git config branch.myr20.remote)" = z && + test "z$(git config branch.myr20.merge)" = z && + test "z$(git config branch.myr20.rebase)" = z +' + +test_expect_success 'detect misconfigured autosetuprebase (bad value)' ' + git config branch.autosetuprebase garbage && + test_must_fail git branch +' + +test_expect_success 'detect misconfigured autosetuprebase (no value)' ' + git config --unset branch.autosetuprebase && + echo "[branch] autosetuprebase" >> .git/config && + test_must_fail git branch && + git config --unset branch.autosetuprebase +' + test_done From 960b8ad1b1824b1b82c2b09a000c2119f97633a0 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Mon, 12 May 2008 19:57:45 +0200 Subject: [PATCH 040/202] Make the exit code of add_file_to_index actually useful Update the programs which used the function (as add_file_to_cache). Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- builtin-add.c | 6 ++++-- builtin-commit.c | 7 ++++--- builtin-mv.c | 3 ++- read-cache.c | 6 +++--- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index 4a91e3eb11..4d72ab678d 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -94,7 +94,8 @@ static void update_callback(struct diff_queue_struct *q, case DIFF_STATUS_UNMERGED: case DIFF_STATUS_MODIFIED: case DIFF_STATUS_TYPE_CHANGED: - add_file_to_cache(path, verbose); + if (add_file_to_cache(path, verbose)) + die("updating files failed"); break; case DIFF_STATUS_DELETED: remove_file_from_cache(path); @@ -254,7 +255,8 @@ int cmd_add(int argc, const char **argv, const char *prefix) } for (i = 0; i < dir.nr; i++) - add_file_to_cache(dir.entries[i]->name, verbose); + if (add_file_to_cache(dir.entries[i]->name, verbose)) + die("adding files failed"); finish: if (active_cache_changed) { diff --git a/builtin-commit.c b/builtin-commit.c index a65c2b8c37..ae29d35d76 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -178,9 +178,10 @@ static void add_remove_files(struct path_list *list) struct stat st; struct path_list_item *p = &(list->items[i]); - if (!lstat(p->path, &st)) - add_to_cache(p->path, &st, 0); - else + if (!lstat(p->path, &st)) { + if (add_to_cache(p->path, &st, 0)) + die("updating files failed"); + } else remove_file_from_cache(p->path); } } diff --git a/builtin-mv.c b/builtin-mv.c index 94f6dd2aad..fb8ffb41aa 100644 --- a/builtin-mv.c +++ b/builtin-mv.c @@ -256,7 +256,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix) for (i = 0; i < added.nr; i++) { const char *path = added.items[i].path; - add_file_to_cache(path, verbose); + if (add_file_to_cache(path, verbose)) + die("updating index entries failed"); } for (i = 0; i < deleted.nr; i++) diff --git a/read-cache.c b/read-cache.c index 0382804e76..8b467f8f41 100644 --- a/read-cache.c +++ b/read-cache.c @@ -470,7 +470,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st, unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY; if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode)) - die("%s: can only add regular files, symbolic links or git-directories", path); + return error("%s: can only add regular files, symbolic links or git-directories", path); namelen = strlen(path); if (S_ISDIR(st_mode)) { @@ -505,12 +505,12 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st, return 0; } if (index_path(ce->sha1, path, st, 1)) - die("unable to index file %s", path); + return error("unable to index file %s", path); if (ignore_case && alias && different_name(ce, alias)) ce = create_alias_ce(ce, alias); ce->ce_flags |= CE_ADDED; if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE)) - die("unable to add %s to index",path); + return error("unable to add %s to index",path); if (verbose) printf("add '%s'\n", path); return 0; From 7ae02a30e817eda16ea362c6304b6ae28c3a7644 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Mon, 12 May 2008 19:58:10 +0200 Subject: [PATCH 041/202] Extend interface of add_files_to_cache to allow ignore indexing errors Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- builtin-add.c | 37 ++++++++++++++++++++++++++++--------- builtin-checkout.c | 2 +- builtin-commit.c | 2 +- cache.h | 8 +++++++- 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index 4d72ab678d..786280818a 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -79,12 +79,18 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec, prune_directory(dir, pathspec, baselen); } +struct update_callback_data +{ + int flags; + int add_errors; +}; + static void update_callback(struct diff_queue_struct *q, struct diff_options *opt, void *cbdata) { - int i, verbose; + int i; + struct update_callback_data *data = cbdata; - verbose = *((int *)cbdata); for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; const char *path = p->one->path; @@ -94,28 +100,35 @@ static void update_callback(struct diff_queue_struct *q, case DIFF_STATUS_UNMERGED: case DIFF_STATUS_MODIFIED: case DIFF_STATUS_TYPE_CHANGED: - if (add_file_to_cache(path, verbose)) - die("updating files failed"); + if (add_file_to_cache(path, data->flags & ADD_FILES_VERBOSE)) { + if (!(data->flags & ADD_FILES_IGNORE_ERRORS)) + die("updating files failed"); + data->add_errors++; + } break; case DIFF_STATUS_DELETED: remove_file_from_cache(path); - if (verbose) + if (data->flags & ADD_FILES_VERBOSE) printf("remove '%s'\n", path); break; } } } -void add_files_to_cache(int verbose, const char *prefix, const char **pathspec) +int add_files_to_cache(const char *prefix, const char **pathspec, int flags) { + struct update_callback_data data; struct rev_info rev; init_revisions(&rev, prefix); setup_revisions(0, NULL, &rev, NULL); rev.prune_data = pathspec; rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = update_callback; - rev.diffopt.format_callback_data = &verbose; + data.flags = flags; + data.add_errors = 0; + rev.diffopt.format_callback_data = &data; run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); + return !!data.add_errors; } static void refresh(int verbose, const char **pathspec) @@ -193,6 +206,7 @@ static struct option builtin_add_options[] = { int cmd_add(int argc, const char **argv, const char *prefix) { + int exit_status = 0; int i, newfd; const char **pathspec; struct dir_struct dir; @@ -209,11 +223,16 @@ int cmd_add(int argc, const char **argv, const char *prefix) newfd = hold_locked_index(&lock_file, 1); if (take_worktree_changes) { + int flags = 0; const char **pathspec; if (read_cache() < 0) die("index file corrupt"); pathspec = get_pathspec(prefix, argv); - add_files_to_cache(verbose, prefix, pathspec); + + if (verbose) + flags |= ADD_FILES_VERBOSE; + + exit_status = add_files_to_cache(prefix, pathspec, flags); goto finish; } @@ -265,5 +284,5 @@ int cmd_add(int argc, const char **argv, const char *prefix) die("Unable to write new index file"); } - return 0; + return exit_status; } diff --git a/builtin-checkout.c b/builtin-checkout.c index 10ec137cce..05c06421b6 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -282,7 +282,7 @@ static int merge_working_tree(struct checkout_opts *opts, * entries in the index. */ - add_files_to_cache(0, NULL, NULL); + add_files_to_cache(NULL, NULL, 0); work = write_tree_from_memory(); ret = reset_to_new(new->commit->tree, opts->quiet); diff --git a/builtin-commit.c b/builtin-commit.c index ae29d35d76..6a2f5c3d96 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -246,7 +246,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix) */ if (all || (also && pathspec && *pathspec)) { int fd = hold_locked_index(&index_lock, 1); - add_files_to_cache(0, also ? prefix : NULL, pathspec); + add_files_to_cache(also ? prefix : NULL, pathspec, 0); refresh_cache(REFRESH_QUIET); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) diff --git a/cache.h b/cache.h index 9cee9a5f3f..4fb629000e 100644 --- a/cache.h +++ b/cache.h @@ -781,7 +781,13 @@ extern int convert_to_git(const char *path, const char *src, size_t len, extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst); /* add */ -void add_files_to_cache(int verbose, const char *prefix, const char **pathspec); +#define ADD_FILES_VERBOSE 01 +#define ADD_FILES_IGNORE_ERRORS 02 +/* + * return 0 if success, 1 - if addition of a file failed and + * ADD_FILES_IGNORE_ERRORS was specified in flags + */ +int add_files_to_cache(const char *prefix, const char **pathspec, int flags); /* diff.c */ extern int diff_auto_refresh_index; From 984b83ef23fdcf6a933f635f182e7bc10130094a Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Mon, 12 May 2008 19:58:29 +0200 Subject: [PATCH 042/202] Add --ignore-errors to git-add to allow it to skip files with read errors Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- Documentation/git-add.txt | 7 ++++++- builtin-add.c | 11 +++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index e0e730b6c4..bb4abe26bb 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git-add' [-n] [-v] [-f] [--interactive | -i] [--patch | -p] [-u] [--refresh] - [--] ... + [--ignore-errors] [--] ... DESCRIPTION ----------- @@ -83,6 +83,11 @@ OPTIONS Don't add the file(s), but only refresh their stat() information in the index. +\--ignore-errors:: + If some files could not be added because of errors indexing + them, do not abort the operation, but continue adding the + others. The command shall still exit with non-zero status. + \--:: This option can be used to separate command-line options from the list of files, (useful when filenames might be mistaken diff --git a/builtin-add.c b/builtin-add.c index 786280818a..522519ec86 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -191,6 +191,7 @@ static const char ignore_error[] = "The following paths are ignored by one of your .gitignore files:\n"; static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0; +static int ignore_add_errors; static struct option builtin_add_options[] = { OPT__DRY_RUN(&show_only), @@ -201,6 +202,7 @@ static struct option builtin_add_options[] = { OPT_BOOLEAN('f', NULL, &ignored_too, "allow adding otherwise ignored files"), OPT_BOOLEAN('u', NULL, &take_worktree_changes, "update tracked files"), OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"), + OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"), OPT_END(), }; @@ -231,6 +233,8 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (verbose) flags |= ADD_FILES_VERBOSE; + if (ignore_add_errors) + flags |= ADD_FILES_IGNORE_ERRORS; exit_status = add_files_to_cache(prefix, pathspec, flags); goto finish; @@ -274,8 +278,11 @@ int cmd_add(int argc, const char **argv, const char *prefix) } for (i = 0; i < dir.nr; i++) - if (add_file_to_cache(dir.entries[i]->name, verbose)) - die("adding files failed"); + if (add_file_to_cache(dir.entries[i]->name, verbose)) { + if (!ignore_add_errors) + die("adding files failed"); + exit_status = 1; + } finish: if (active_cache_changed) { From 89597436450d4665ea8f9f782e71d42539d9f24b Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Mon, 12 May 2008 19:58:48 +0200 Subject: [PATCH 043/202] Add a test for git-add --ignore-errors Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- t/t3700-add.sh | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/t/t3700-add.sh b/t/t3700-add.sh index 287e058e37..01e4d62513 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -179,4 +179,26 @@ test_expect_success 'git add --refresh' ' test -z "`git diff-index HEAD -- foo`" ' +test_expect_success 'git add should fail atomically upon an unreadable file' ' + git reset --hard && + date >foo1 && + date >foo2 && + chmod 0 foo2 && + test_must_fail git add --verbose . && + ! ( git ls-files foo1 | grep foo1 ) +' + +rm -f foo2 + +test_expect_success 'git add --ignore-errors' ' + git reset --hard && + date >foo1 && + date >foo2 && + chmod 0 foo2 && + test_must_fail git add --verbose --ignore-errors . && + git ls-files foo1 | grep foo1 +' + +rm -f foo2 + test_done From dad25e4a7c34a3ece1355f84b8c4661438754531 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Mon, 12 May 2008 19:59:23 +0200 Subject: [PATCH 044/202] Add a config option to ignore errors for git-add Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- builtin-add.c | 11 ++++++++++- t/t3700-add.sh | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/builtin-add.c b/builtin-add.c index 522519ec86..73235ed08a 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -206,6 +206,15 @@ static struct option builtin_add_options[] = { OPT_END(), }; +static int add_config(const char *var, const char *value) +{ + if (!strcasecmp(var, "add.ignore-errors")) { + ignore_add_errors = git_config_bool(var, value); + return 0; + } + return git_default_config(var, value); +} + int cmd_add(int argc, const char **argv, const char *prefix) { int exit_status = 0; @@ -220,7 +229,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (add_interactive) exit(interactive_add(argc, argv, prefix)); - git_config(git_default_config); + git_config(add_config); newfd = hold_locked_index(&lock_file, 1); diff --git a/t/t3700-add.sh b/t/t3700-add.sh index 01e4d62513..5b46ba3560 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -201,4 +201,25 @@ test_expect_success 'git add --ignore-errors' ' rm -f foo2 +test_expect_success 'git add (add.ignore-errors)' ' + git config add.ignore-errors 1 && + git reset --hard && + date >foo1 && + date >foo2 && + chmod 0 foo2 && + test_must_fail git add --verbose . && + git ls-files foo1 | grep foo1 +' +rm -f foo2 + +test_expect_success 'git add (add.ignore-errors = false)' ' + git config add.ignore-errors 0 && + git reset --hard && + date >foo1 && + date >foo2 && + chmod 0 foo2 && + test_must_fail git add --verbose . && + ! ( git ls-files foo1 | grep foo1 ) +' + test_done From bbac73117ebed9f02ccae3df45f4baa0c793dab7 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 14 May 2008 01:32:48 -0400 Subject: [PATCH 045/202] add a force_object_loose() function This is meant to force the creation of a loose object even if it already exists packed. Needed for the next commit. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- cache.h | 1 + sha1_file.c | 60 +++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/cache.h b/cache.h index 7fb8f3359d..dcc7e98825 100644 --- a/cache.h +++ b/cache.h @@ -506,6 +506,7 @@ extern void * read_sha1_file(const unsigned char *sha1, enum object_type *type, extern int hash_sha1_file(const 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 int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *); +extern int force_object_loose(const unsigned char *sha1, time_t mtime); 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 3516777bc7..141f5a713a 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2102,26 +2102,16 @@ int hash_sha1_file(const void *buf, unsigned long len, const char *type, return 0; } -int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1) +static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen, + void *buf, unsigned long len, time_t mtime) { - int size, ret; + int fd, size, ret; unsigned char *compressed; z_stream stream; - unsigned char sha1[20]; char *filename; static char tmpfile[PATH_MAX]; - char hdr[32]; - int fd, hdrlen; - /* Normally if we have it in the pack then we do not bother writing - * it out into .git/objects/??/?{38} file. - */ - 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)) - return 0; fd = open(filename, O_RDONLY); if (fd >= 0) { /* @@ -2182,9 +2172,53 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha die("unable to write sha1 file"); free(compressed); + if (mtime) { + struct utimbuf utb; + utb.actime = mtime; + utb.modtime = mtime; + if (utime(tmpfile, &utb) < 0) + warning("failed utime() on %s: %s", + tmpfile, strerror(errno)); + } + return move_temp_to_file(tmpfile, filename); } +int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1) +{ + unsigned char sha1[20]; + char hdr[32]; + int hdrlen; + + /* Normally if we have it in the pack then we do not bother writing + * it out into .git/objects/??/?{38} file. + */ + write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen); + if (returnsha1) + hashcpy(returnsha1, sha1); + if (has_sha1_file(sha1)) + return 0; + return write_loose_object(sha1, hdr, hdrlen, buf, len, 0); +} + +int force_object_loose(const unsigned char *sha1, time_t mtime) +{ + struct stat st; + void *buf; + unsigned long len; + enum object_type type; + char hdr[32]; + int hdrlen; + + if (find_sha1_file(sha1, &st)) + return 0; + buf = read_packed_sha1(sha1, &type, &len); + if (!buf) + return error("cannot read sha1_file for %s", sha1_to_hex(sha1)); + hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1; + return write_loose_object(sha1, hdr, hdrlen, buf, len, mtime); +} + /* * We need to unpack and recompress the object for writing * it out to a different file. From ca11b212eb3e31d6fee12e9974c67dc774c1bc7c Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 14 May 2008 01:33:53 -0400 Subject: [PATCH 046/202] let pack-objects do the writing of unreachable objects as loose objects Commit ccc1297226b184c40459e9d373cc9eebfb7bd898 changed the behavior of 'git repack -A' so unreachable objects are stored as loose objects. However it did so in a naive and inn efficient way by making packs about to be deleted inaccessible and feeding their content through 'git unpack-objects'. While this works, there are major flaws with this approach: - It is unacceptably sloooooooooooooow. In the Linux kernel repository with no actual unreachable objects, doing 'git repack -A -d' before: real 2m33.220s user 2m21.675s sys 0m3.510s And with this change: real 0m36.849s user 0m24.365s sys 0m1.950s For reference, here's the timing for 'git repack -a -d': real 0m35.816s user 0m22.571s sys 0m2.011s This is explained by the fact that 'git unpack-objects' was used to unpack _every_ objects even if (almost) 100% of them were thrown away. - There is a black out period. Between the removal of the .idx file for the redundant pack and the completion of its unpacking, the unreachable objects become completely unaccessible. This is not a big issue as we're talking about unreachable objects, but some consistency is always good. - There is no way to easily set a sensible mtime for the newly created unreachable loose objects. So, while having a command called "pack-objects" to perform object unpacking looks really odd, this is probably the best compromize to be able to solve the above issues in an efficient way. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 40 ++++++++++++++++++++++++++++++++++++++-- git-repack.sh | 22 +++++++--------------- 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 777f272668..5a10119fbd 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -28,7 +28,8 @@ git-pack-objects [{ -q | --progress | --all-progress }] \n\ [--window=N] [--window-memory=N] [--depth=N] \n\ [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\ [--threads=N] [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\ - [--stdout | base-name] [--include-tag] [--keep-unreachable] \n\ + [--stdout | base-name] [--include-tag] \n\ + [--keep-unreachable | --unpack-unreachable] \n\ [next) { + for (i = 0; i < revs->num_ignore_packed; i++) { + if (matches_pack_name(p, revs->ignore_packed[i])) + break; + } + if (revs->num_ignore_packed <= i) + continue; + + if (open_pack_index(p)) + die("cannot open pack index"); + + for (i = 0; i < p->num_objects; i++) { + sha1 = nth_packed_object_sha1(p, i); + if (!locate_object_entry(sha1)) + if (force_object_loose(sha1, p->mtime)) + die("unable to force loose object"); + } + } +} + static void get_object_list(int ac, const char **av) { struct rev_info revs; @@ -1939,6 +1966,8 @@ static void get_object_list(int ac, const char **av) if (keep_unreachable) add_objects_in_unpacked_packs(&revs); + if (unpack_unreachable) + loosen_unused_packed_objects(&revs); } static int adjust_perm(const char *path, mode_t mode) @@ -2073,6 +2102,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) keep_unreachable = 1; continue; } + if (!strcmp("--unpack-unreachable", arg)) { + unpack_unreachable = 1; + continue; + } if (!strcmp("--include-tag", arg)) { include_tag = 1; continue; @@ -2138,6 +2171,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (!pack_to_stdout && thin) die("--thin cannot be used to build an indexable pack."); + if (keep_unreachable && unpack_unreachable) + die("--keep-unreachable and --unpack-unreachable are incompatible."); + #ifdef THREADED_DELTA_SEARCH if (!delta_search_threads) /* --threads=0 means autodetect */ delta_search_threads = online_cpus(); diff --git a/git-repack.sh b/git-repack.sh index a0e06ed076..607f217b78 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -8,7 +8,7 @@ OPTIONS_SPEC="\ git-repack [options] -- a pack everything in a single pack -A same as -a, and keep unreachable objects too +A same as -a, and turn unreachable objects loose d remove redundant packs, and run git-prune-packed f pass --no-reuse-delta to git-pack-objects q,quiet be quiet @@ -22,7 +22,7 @@ max-pack-size= maximum size of each packfile SUBDIRECTORY_OK='Yes' . git-sh-setup -no_update_info= all_into_one= remove_redundant= keep_unreachable= +no_update_info= all_into_one= remove_redundant= unpack_unreachable= local= quiet= no_reuse= extra= while test $# != 0 do @@ -30,7 +30,7 @@ do -n) no_update_info=t ;; -a) all_into_one=t ;; -A) all_into_one=t - keep_unreachable=t ;; + unpack_unreachable=--unpack-unreachable ;; -d) remove_redundant=t ;; -q) quiet=-q ;; -f) no_reuse=--no-reuse-object ;; @@ -78,6 +78,9 @@ case ",$all_into_one," in if test -z "$args" then args='--unpacked --incremental' + elif test -n "$unpack_unreachable" + then + args="$args $unpack_unreachable" fi ;; esac @@ -127,18 +130,7 @@ then do case " $fullbases " in *" $e "*) ;; - *) - rm -f "$e.idx" "$e.keep" - if test -n "$keep_unreachable" && - test -f "$e.pack" - then - git unpack-objects < "$e.pack" || { - echo >&2 "Failed unpacking unreachable objects from redundant pack file $e.pack" - exit 1 - } - fi - rm -f "$e.pack" - ;; + *) rm -f "$e.pack" "$e.idx" "$e.keep" ;; esac done ) From ef90d6d4208a5130185b04f06e5f90a5f9959fe3 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 14 May 2008 18:46:53 +0100 Subject: [PATCH 047/202] Provide git_config with a callback-data parameter git_config() only had a function parameter, but no callback data parameter. This assumes that all callback functions only modify global variables. With this patch, every callback gets a void * parameter, and it is hoped that this will help the libification effort. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- alias.c | 5 +++-- archive-tar.c | 6 +++--- builtin-add.c | 2 +- builtin-apply.c | 6 +++--- builtin-blame.c | 6 +++--- builtin-branch.c | 6 +++--- builtin-cat-file.c | 2 +- builtin-checkout-index.c | 2 +- builtin-checkout.c | 2 +- builtin-clean.c | 6 +++--- builtin-commit-tree.c | 2 +- builtin-commit.c | 8 ++++---- builtin-config.c | 26 ++++++++++++++------------ builtin-diff-files.c | 2 +- builtin-diff-index.c | 2 +- builtin-diff-tree.c | 2 +- builtin-diff.c | 2 +- builtin-fast-export.c | 2 +- builtin-fetch-pack.c | 6 +++--- builtin-fmt-merge-msg.c | 4 ++-- builtin-gc.c | 6 +++--- builtin-http-fetch.c | 2 +- builtin-init-db.c | 4 ++-- builtin-log.c | 18 +++++++++--------- builtin-ls-files.c | 2 +- builtin-ls-tree.c | 2 +- builtin-mailinfo.c | 2 +- builtin-merge-base.c | 2 +- builtin-merge-recursive.c | 6 +++--- builtin-mv.c | 2 +- builtin-name-rev.c | 2 +- builtin-pack-objects.c | 6 +++--- builtin-read-tree.c | 4 +--- builtin-reflog.c | 6 +++--- builtin-remote.c | 8 ++++---- builtin-rerere.c | 6 +++--- builtin-reset.c | 2 +- builtin-rev-list.c | 2 +- builtin-rev-parse.c | 2 +- builtin-revert.c | 2 +- builtin-rm.c | 2 +- builtin-show-branch.c | 6 +++--- builtin-symbolic-ref.c | 2 +- builtin-tag.c | 6 +++--- builtin-unpack-objects.c | 2 +- builtin-update-index.c | 2 +- builtin-update-ref.c | 2 +- builtin-verify-pack.c | 2 +- builtin-verify-tag.c | 2 +- builtin-write-tree.c | 2 +- cache.h | 10 +++++----- color.c | 4 ++-- color.h | 2 +- config.c | 27 ++++++++++++++------------- connect.c | 7 ++++--- convert.c | 4 ++-- daemon.c | 4 ++-- diff.c | 8 ++++---- diff.h | 4 ++-- fast-import.c | 6 +++--- hash-object.c | 2 +- help.c | 6 +++--- http.c | 6 +++--- imap-send.c | 4 ++-- index-pack.c | 6 +++--- ll-merge.c | 4 ++-- pager.c | 2 +- receive-pack.c | 6 +++--- remote.c | 4 ++-- setup.c | 4 ++-- unpack-file.c | 2 +- var.c | 8 ++++---- wt-status.c | 4 ++-- wt-status.h | 2 +- 74 files changed, 176 insertions(+), 173 deletions(-) diff --git a/alias.c b/alias.c index 116cac87c3..995f3e6a0a 100644 --- a/alias.c +++ b/alias.c @@ -2,7 +2,8 @@ static const char *alias_key; static char *alias_val; -static int alias_lookup_cb(const char *k, const char *v) + +static int alias_lookup_cb(const char *k, const char *v, void *cb) { if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) { if (!v) @@ -17,6 +18,6 @@ char *alias_lookup(const char *alias) { alias_key = alias; alias_val = NULL; - git_config(alias_lookup_cb); + git_config(alias_lookup_cb, NULL); return alias_val; } diff --git a/archive-tar.c b/archive-tar.c index 4add80284e..d7598f907d 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -220,7 +220,7 @@ static void write_global_extended_header(const unsigned char *sha1) strbuf_release(&ext_header); } -static int git_tar_config(const char *var, const char *value) +static int git_tar_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "tar.umask")) { if (value && !strcmp(value, "user")) { @@ -231,7 +231,7 @@ static int git_tar_config(const char *var, const char *value) } return 0; } - return git_default_config(var, value); + return git_default_config(var, value, cb); } static int write_tar_entry(const unsigned char *sha1, @@ -268,7 +268,7 @@ int write_tar_archive(struct archiver_args *args) { int plen = args->base ? strlen(args->base) : 0; - git_config(git_tar_config); + git_config(git_tar_config, NULL); archive_time = args->time; verbose = args->verbose; diff --git a/builtin-add.c b/builtin-add.c index 4a91e3eb11..15def7dada 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -203,7 +203,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (add_interactive) exit(interactive_add(argc, argv, prefix)); - git_config(git_default_config); + git_config(git_default_config, NULL); newfd = hold_locked_index(&lock_file, 1); diff --git a/builtin-apply.c b/builtin-apply.c index 1103625a4a..bbdf08a10a 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2979,11 +2979,11 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof) return 0; } -static int git_apply_config(const char *var, const char *value) +static int git_apply_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "apply.whitespace")) return git_config_string(&apply_default_whitespace, var, value); - return git_default_config(var, value); + return git_default_config(var, value, cb); } @@ -2999,7 +2999,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) prefix = setup_git_directory_gently(&is_not_gitdir); prefix_length = prefix ? strlen(prefix) : 0; - git_config(git_apply_config); + git_config(git_apply_config, NULL); if (apply_default_whitespace) parse_whitespace_option(apply_default_whitespace); diff --git a/builtin-blame.c b/builtin-blame.c index bfd562d7d2..b451f6c64d 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -1993,7 +1993,7 @@ static void prepare_blame_range(struct scoreboard *sb, usage(blame_usage); } -static int git_blame_config(const char *var, const char *value) +static int git_blame_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "blame.showroot")) { show_root = git_config_bool(var, value); @@ -2003,7 +2003,7 @@ static int git_blame_config(const char *var, const char *value) blank_boundary = git_config_bool(var, value); return 0; } - return git_default_config(var, value); + return git_default_config(var, value, cb); } static struct commit *fake_working_tree_commit(const char *path, const char *contents_from) @@ -2141,7 +2141,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) cmd_is_annotate = !strcmp(argv[0], "annotate"); - git_config(git_blame_config); + git_config(git_blame_config, NULL); save_commit_buffer = 0; opt = 0; diff --git a/builtin-branch.c b/builtin-branch.c index 19c508a608..d279702ba9 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -63,7 +63,7 @@ static int parse_branch_color_slot(const char *var, int ofs) die("bad config variable '%s'", var); } -static int git_branch_config(const char *var, const char *value) +static int git_branch_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "color.branch")) { branch_use_color = git_config_colorbool(var, value, -1); @@ -76,7 +76,7 @@ static int git_branch_config(const char *var, const char *value) color_parse(value, var, branch_colors[slot]); return 0; } - return git_color_default_config(var, value); + return git_color_default_config(var, value, cb); } static const char *branch_get_color(enum color_branch ix) @@ -461,7 +461,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT_END(), }; - git_config(git_branch_config); + git_config(git_branch_config, NULL); if (branch_use_color == -1) branch_use_color = git_use_color_default; diff --git a/builtin-cat-file.c b/builtin-cat-file.c index f132d583d3..b488fad431 100644 --- a/builtin-cat-file.c +++ b/builtin-cat-file.c @@ -85,7 +85,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) int opt; const char *exp_type, *obj_name; - git_config(git_default_config); + git_config(git_default_config, NULL); if (argc != 3) usage("git-cat-file [-t|-s|-e|-p|] "); exp_type = argv[1]; diff --git a/builtin-checkout-index.c b/builtin-checkout-index.c index 7e42024c67..eb1fc9aa6f 100644 --- a/builtin-checkout-index.c +++ b/builtin-checkout-index.c @@ -166,7 +166,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) int read_from_stdin = 0; int prefix_length; - git_config(git_default_config); + git_config(git_default_config, NULL); state.base_dir = ""; prefix_length = prefix ? strlen(prefix) : 0; diff --git a/builtin-checkout.c b/builtin-checkout.c index 10ec137cce..c077134e62 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -514,7 +514,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) memset(&opts, 0, sizeof(opts)); memset(&new, 0, sizeof(new)); - git_config(git_default_config); + git_config(git_default_config, NULL); opts.track = git_branch_track; diff --git a/builtin-clean.c b/builtin-clean.c index 6778a03ae4..80a7ff9ae4 100644 --- a/builtin-clean.c +++ b/builtin-clean.c @@ -19,11 +19,11 @@ static const char *const builtin_clean_usage[] = { NULL }; -static int git_clean_config(const char *var, const char *value) +static int git_clean_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "clean.requireforce")) force = !git_config_bool(var, value); - return git_default_config(var, value); + return git_default_config(var, value, cb); } int cmd_clean(int argc, const char **argv, const char *prefix) @@ -50,7 +50,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) OPT_END() }; - git_config(git_clean_config); + git_config(git_clean_config, NULL); if (force < 0) force = 0; else diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c index 6610d18358..e5e4bdbe86 100644 --- a/builtin-commit-tree.c +++ b/builtin-commit-tree.c @@ -60,7 +60,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) struct strbuf buffer; int encoding_is_utf8; - git_config(git_default_config); + git_config(git_default_config, NULL); if (argc < 2) usage(commit_tree_usage); diff --git a/builtin-commit.c b/builtin-commit.c index a65c2b8c37..9f0026ed00 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -773,7 +773,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) const char *index_file; int commitable; - git_config(git_status_config); + git_config(git_status_config, NULL); if (wt_status_use_color == -1) wt_status_use_color = git_use_color_default; @@ -827,7 +827,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1) } } -int git_commit_config(const char *k, const char *v) +int git_commit_config(const char *k, const char *v, void *cb) { if (!strcmp(k, "commit.template")) { if (!v) @@ -836,7 +836,7 @@ int git_commit_config(const char *k, const char *v) return 0; } - return git_status_config(k, v); + return git_status_config(k, v, cb); } static const char commit_utf8_warn[] = @@ -864,7 +864,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) unsigned char commit_sha1[20]; struct ref_lock *ref_lock; - git_config(git_commit_config); + git_config(git_commit_config, NULL); argc = parse_and_validate_options(argc, argv, builtin_commit_usage); diff --git a/builtin-config.c b/builtin-config.c index 8ee01bdecd..3a441ef648 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -18,7 +18,7 @@ static char key_delim = ' '; static char term = '\n'; static enum { T_RAW, T_INT, T_BOOL, T_BOOL_OR_INT } type = T_RAW; -static int show_all_config(const char *key_, const char *value_) +static int show_all_config(const char *key_, const char *value_, void *cb) { if (value_) printf("%s%c%s%c", key_, delim, value_, term); @@ -27,7 +27,7 @@ static int show_all_config(const char *key_, const char *value_) return 0; } -static int show_config(const char* key_, const char* value_) +static int show_config(const char* key_, const char* value_, void *cb) { char value[256]; const char *vptr = value; @@ -121,14 +121,14 @@ static int get_value(const char* key_, const char* regex_) } if (do_all && system_wide) - git_config_from_file(show_config, system_wide); + git_config_from_file(show_config, system_wide, NULL); if (do_all && global) - git_config_from_file(show_config, global); - git_config_from_file(show_config, local); + git_config_from_file(show_config, global, NULL); + git_config_from_file(show_config, local, NULL); if (!do_all && !seen && global) - git_config_from_file(show_config, global); + git_config_from_file(show_config, global, NULL); if (!do_all && !seen && system_wide) - git_config_from_file(show_config, system_wide); + git_config_from_file(show_config, system_wide, NULL); free(key); if (regexp) { @@ -182,7 +182,7 @@ static int get_color_found; static const char *get_color_slot; static char parsed_color[COLOR_MAXLEN]; -static int git_get_color_config(const char *var, const char *value) +static int git_get_color_config(const char *var, const char *value, void *cb) { if (!strcmp(var, get_color_slot)) { if (!value) @@ -218,7 +218,7 @@ static int get_color(int argc, const char **argv) get_color_found = 0; parsed_color[0] = '\0'; - git_config(git_get_color_config); + git_config(git_get_color_config, NULL); if (!get_color_found && def_color) color_parse(def_color, "command line", parsed_color); @@ -230,7 +230,8 @@ static int get_color(int argc, const char **argv) static int stdout_is_tty; static int get_colorbool_found; static int get_diff_color_found; -static int git_get_colorbool_config(const char *var, const char *value) +static int git_get_colorbool_config(const char *var, const char *value, + void *cb) { if (!strcmp(var, get_color_slot)) { get_colorbool_found = @@ -265,7 +266,7 @@ static int get_colorbool(int argc, const char **argv) get_colorbool_found = -1; get_diff_color_found = -1; get_color_slot = argv[0]; - git_config(git_get_colorbool_config); + git_config(git_get_colorbool_config, NULL); if (get_colorbool_found < 0) { if (!strcmp(get_color_slot, "color.diff")) @@ -298,7 +299,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) { if (argc != 2) usage(git_config_set_usage); - if (git_config(show_all_config) < 0 && file && errno) + if (git_config(show_all_config, NULL) < 0 && + file && errno) die("unable to read config file %s: %s", file, strerror(errno)); return 0; diff --git a/builtin-diff-files.c b/builtin-diff-files.c index e2306c162a..907392a1f3 100644 --- a/builtin-diff-files.c +++ b/builtin-diff-files.c @@ -21,7 +21,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) prefix = setup_git_directory_gently(&nongit); init_revisions(&rev, prefix); - git_config(git_diff_basic_config); /* no "diff" UI options */ + git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ rev.abbrev = 0; if (!setup_diff_no_index(&rev, argc, argv, nongit, prefix)) diff --git a/builtin-diff-index.c b/builtin-diff-index.c index 2b955deb91..2f44ebfcdd 100644 --- a/builtin-diff-index.c +++ b/builtin-diff-index.c @@ -17,7 +17,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix) int result; init_revisions(&rev, prefix); - git_config(git_diff_basic_config); /* no "diff" UI options */ + git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ rev.abbrev = 0; argc = setup_revisions(argc, argv, &rev, NULL); diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c index 832797ff3b..9d2a48fd68 100644 --- a/builtin-diff-tree.c +++ b/builtin-diff-tree.c @@ -68,7 +68,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) int read_stdin = 0; init_revisions(opt, prefix); - git_config(git_diff_basic_config); /* no "diff" UI options */ + git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ nr_sha1 = 0; opt->abbrev = 0; opt->diff = 1; diff --git a/builtin-diff.c b/builtin-diff.c index 7c2a8412fa..583291a9c0 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -234,7 +234,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) */ prefix = setup_git_directory_gently(&nongit); - git_config(git_diff_ui_config); + git_config(git_diff_ui_config, NULL); if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; diff --git a/builtin-fast-export.c b/builtin-fast-export.c index e1c56303e5..ff759cc578 100755 --- a/builtin-fast-export.c +++ b/builtin-fast-export.c @@ -372,7 +372,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) }; /* we handle encodings */ - git_config(git_default_config); + git_config(git_default_config, NULL); init_revisions(&revs, prefix); argc = setup_revisions(argc, argv, &revs, NULL); diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index c97a42739d..de1e8d1365 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -635,7 +635,7 @@ static int remove_duplicates(int nr_heads, char **heads) return dst; } -static int fetch_pack_config(const char *var, const char *value) +static int fetch_pack_config(const char *var, const char *value, void *cb) { if (strcmp(var, "fetch.unpacklimit") == 0) { fetch_unpack_limit = git_config_int(var, value); @@ -647,7 +647,7 @@ static int fetch_pack_config(const char *var, const char *value) return 0; } - return git_default_config(var, value); + return git_default_config(var, value, cb); } static struct lock_file lock; @@ -657,7 +657,7 @@ static void fetch_pack_setup(void) static int did_setup; if (did_setup) return; - git_config(fetch_pack_config); + git_config(fetch_pack_config, NULL); if (0 <= transfer_unpack_limit) unpack_limit = transfer_unpack_limit; else if (0 <= fetch_unpack_limit) diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index b72cb59e6a..b892621ab5 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -10,7 +10,7 @@ static const char *fmt_merge_msg_usage = static int merge_summary; -static int fmt_merge_msg_config(const char *key, const char *value) +static int fmt_merge_msg_config(const char *key, const char *value, void *cb) { static int found_merge_log = 0; if (!strcmp("merge.log", key)) { @@ -260,7 +260,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) unsigned char head_sha1[20]; const char *current_branch; - git_config(fmt_merge_msg_config); + git_config(fmt_merge_msg_config, NULL); while (argc > 1) { if (!strcmp(argv[1], "--log") || !strcmp(argv[1], "--summary")) diff --git a/builtin-gc.c b/builtin-gc.c index f99ebc7926..e1a329917a 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -35,7 +35,7 @@ static const char *argv_repack[MAX_ADD] = {"repack", "-d", "-l", NULL}; static const char *argv_prune[] = {"prune", "--expire", NULL, NULL}; static const char *argv_rerere[] = {"rerere", "gc", NULL}; -static int gc_config(const char *var, const char *value) +static int gc_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "gc.packrefs")) { if (value && !strcmp(value, "notbare")) @@ -67,7 +67,7 @@ static int gc_config(const char *var, const char *value) prune_expire = xstrdup(value); return 0; } - return git_default_config(var, value); + return git_default_config(var, value, cb); } static void append_option(const char **cmd, const char *opt, int max_length) @@ -226,7 +226,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) OPT_END() }; - git_config(gc_config); + git_config(gc_config, NULL); if (pack_refs < 0) pack_refs = !is_bare_repository(); diff --git a/builtin-http-fetch.c b/builtin-http-fetch.c index b1f33891c3..3a062487a7 100644 --- a/builtin-http-fetch.c +++ b/builtin-http-fetch.c @@ -18,7 +18,7 @@ int cmd_http_fetch(int argc, const char **argv, const char *prefix) int get_verbosely = 0; int get_recover = 0; - git_config(git_default_config); + git_config(git_default_config, NULL); while (arg < argc && argv[arg][0] == '-') { if (argv[arg][1] == 't') { diff --git a/builtin-init-db.c b/builtin-init-db.c index a76f5d3474..f6aa353529 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -142,7 +142,7 @@ static void copy_templates(const char *git_dir, int len, const char *template_di strcpy(template_path + template_len, "config"); repository_format_version = 0; git_config_from_file(check_repository_format_version, - template_path); + template_path, NULL); template_path[template_len] = 0; if (repository_format_version && @@ -197,7 +197,7 @@ static int create_default_files(const char *git_dir, const char *template_path) path[len] = 0; copy_templates(path, len, template_path); - git_config(git_default_config); + git_config(git_default_config, NULL); /* * We would have created the above under user's umask -- under diff --git a/builtin-log.c b/builtin-log.c index 80a01f8d44..addc7098cf 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -222,7 +222,7 @@ static int cmd_log_walk(struct rev_info *rev) return 0; } -static int git_log_config(const char *var, const char *value) +static int git_log_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "format.pretty")) return git_config_string(&fmt_pretty, var, value); @@ -236,14 +236,14 @@ static int git_log_config(const char *var, const char *value) default_show_root = git_config_bool(var, value); return 0; } - return git_diff_ui_config(var, value); + return git_diff_ui_config(var, value, cb); } int cmd_whatchanged(int argc, const char **argv, const char *prefix) { struct rev_info rev; - git_config(git_log_config); + git_config(git_log_config, NULL); if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; @@ -319,7 +319,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) struct object_array_entry *objects; int i, count, ret = 0; - git_config(git_log_config); + git_config(git_log_config, NULL); if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; @@ -383,7 +383,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix) { struct rev_info rev; - git_config(git_log_config); + git_config(git_log_config, NULL); if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; @@ -416,7 +416,7 @@ int cmd_log(int argc, const char **argv, const char *prefix) { struct rev_info rev; - git_config(git_log_config); + git_config(git_log_config, NULL); if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; @@ -471,7 +471,7 @@ static void add_header(const char *value) extra_hdr[extra_hdr_nr++] = xstrndup(value, len); } -static int git_format_config(const char *var, const char *value) +static int git_format_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "format.headers")) { if (!value) @@ -497,7 +497,7 @@ static int git_format_config(const char *var, const char *value) return 0; } - return git_log_config(var, value); + return git_log_config(var, value, cb); } @@ -764,7 +764,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) char *add_signoff = NULL; struct strbuf buf; - git_config(git_format_config); + git_config(git_format_config, NULL); init_revisions(&rev, prefix); rev.commit_format = CMIT_FMT_EMAIL; rev.verbose_header = 1; diff --git a/builtin-ls-files.c b/builtin-ls-files.c index dc7eab89b3..75ba42246e 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -437,7 +437,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) memset(&dir, 0, sizeof(dir)); if (prefix) prefix_offset = strlen(prefix); - git_config(git_default_config); + git_config(git_default_config, NULL); for (i = 1; i < argc; i++) { const char *arg = argv[i]; diff --git a/builtin-ls-tree.c b/builtin-ls-tree.c index 7abe333ce9..f4a75ddbc3 100644 --- a/builtin-ls-tree.c +++ b/builtin-ls-tree.c @@ -122,7 +122,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) unsigned char sha1[20]; struct tree *tree; - git_config(git_default_config); + git_config(git_default_config, NULL); ls_tree_prefix = prefix; if (prefix && *prefix) chomp_prefix = strlen(prefix); diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index 11f154b31f..6e23ffd8af 100644 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@ -962,7 +962,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix) /* NEEDSWORK: might want to do the optional .git/ directory * discovery */ - git_config(git_default_config); + git_config(git_default_config, NULL); def_charset = (git_commit_encoding ? git_commit_encoding : "utf-8"); metainfo_charset = def_charset; diff --git a/builtin-merge-base.c b/builtin-merge-base.c index 0108e22ade..bcf9395aaf 100644 --- a/builtin-merge-base.c +++ b/builtin-merge-base.c @@ -28,7 +28,7 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix) unsigned char rev1key[20], rev2key[20]; int show_all = 0; - git_config(git_default_config); + git_config(git_default_config, NULL); while (1 < argc && argv[1][0] == '-') { const char *arg = argv[1]; diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c index 910c0d20e7..e16f5e2a4e 100644 --- a/builtin-merge-recursive.c +++ b/builtin-merge-recursive.c @@ -1336,7 +1336,7 @@ static struct commit *get_ref(const char *ref) return (struct commit *)object; } -static int merge_config(const char *var, const char *value) +static int merge_config(const char *var, const char *value, void *cb) { if (!strcasecmp(var, "merge.verbosity")) { verbosity = git_config_int(var, value); @@ -1346,7 +1346,7 @@ static int merge_config(const char *var, const char *value) rename_limit = git_config_int(var, value); return 0; } - return git_default_config(var, value); + return git_default_config(var, value, cb); } int cmd_merge_recursive(int argc, const char **argv, const char *prefix) @@ -1367,7 +1367,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix) subtree_merge = 1; } - git_config(merge_config); + git_config(merge_config, NULL); if (getenv("GIT_MERGE_VERBOSITY")) verbosity = strtol(getenv("GIT_MERGE_VERBOSITY"), NULL, 10); diff --git a/builtin-mv.c b/builtin-mv.c index 94f6dd2aad..3edebef45f 100644 --- a/builtin-mv.c +++ b/builtin-mv.c @@ -81,7 +81,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) struct path_list deleted = {NULL, 0, 0, 0}; struct path_list changed = {NULL, 0, 0, 0}; - git_config(git_default_config); + git_config(git_default_config, NULL); newfd = hold_locked_index(&lock_file, 1); if (read_cache() < 0) diff --git a/builtin-name-rev.c b/builtin-name-rev.c index 384da4db13..cde5de56fa 100644 --- a/builtin-name-rev.c +++ b/builtin-name-rev.c @@ -195,7 +195,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) OPT_END(), }; - git_config(git_default_config); + git_config(git_default_config, NULL); argc = parse_options(argc, argv, opts, name_rev_usage, 0); if (!!all + !!transform_stdin + !!argc > 1) { error("Specify either a list, or --all, not both!"); diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 777f272668..a8dc225a72 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1718,7 +1718,7 @@ static void prepare_pack(int window, int depth) free(delta_list); } -static int git_pack_config(const char *k, const char *v) +static int git_pack_config(const char *k, const char *v, void *cb) { if(!strcmp(k, "pack.window")) { window = git_config_int(k, v); @@ -1771,7 +1771,7 @@ static int git_pack_config(const char *k, const char *v) pack_size_limit_cfg = git_config_ulong(k, v); return 0; } - return git_default_config(k, v); + return git_default_config(k, v, cb); } static void read_object_list_from_stdin(void) @@ -1963,7 +1963,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) rp_av[1] = "--objects"; /* --thin will make it --objects-edge */ rp_ac = 2; - git_config(git_pack_config); + git_config(git_pack_config, NULL); if (!pack_compression_seen && core_compression_seen) pack_compression_level = core_compression_level; diff --git a/builtin-read-tree.c b/builtin-read-tree.c index 7ac30883bc..5a09e17f1a 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -104,12 +104,10 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) opts.src_index = &the_index; opts.dst_index = &the_index; - git_config(git_default_config); + git_config(git_default_config, NULL); newfd = hold_locked_index(&lock_file, 1); - git_config(git_default_config); - for (i = 1; i < argc; i++) { const char *arg = argv[i]; diff --git a/builtin-reflog.c b/builtin-reflog.c index 280e24e151..897d1dcac6 100644 --- a/builtin-reflog.c +++ b/builtin-reflog.c @@ -329,7 +329,7 @@ static int collect_reflog(const char *ref, const unsigned char *sha1, int unused return 0; } -static int reflog_expire_config(const char *var, const char *value) +static int reflog_expire_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "gc.reflogexpire")) { if (!value) @@ -343,7 +343,7 @@ static int reflog_expire_config(const char *var, const char *value) default_reflog_expire_unreachable = approxidate(value); return 0; } - return git_default_config(var, value); + return git_default_config(var, value, cb); } static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) @@ -352,7 +352,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) unsigned long now = time(NULL); int i, status, do_all; - git_config(reflog_expire_config); + git_config(reflog_expire_config, NULL); save_commit_buffer = 0; do_all = status = 0; diff --git a/builtin-remote.c b/builtin-remote.c index 8b63619ef0..99a34dfe86 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -153,7 +153,7 @@ struct branch_info { static struct path_list branch_list; -static int config_read_branches(const char *key, const char *value) +static int config_read_branches(const char *key, const char *value, void *cb) { if (!prefixcmp(key, "branch.")) { char *name; @@ -200,7 +200,7 @@ static void read_branches(void) { if (branch_list.nr) return; - git_config(config_read_branches); + git_config(config_read_branches, NULL); sort_path_list(&branch_list); } @@ -514,7 +514,7 @@ struct remote_group { struct path_list *list; } remote_group; -static int get_remote_group(const char *key, const char *value) +static int get_remote_group(const char *key, const char *value, void *cb) { if (!prefixcmp(key, "remotes.") && !strcmp(key + 8, remote_group.name)) { @@ -546,7 +546,7 @@ static int update(int argc, const char **argv) remote_group.list = &list; for (i = 1; i < argc; i++) { remote_group.name = argv[i]; - result = git_config(get_remote_group); + result = git_config(get_remote_group, NULL); } if (!result && !list.nr && argc == 2 && !strcmp(argv[1], "default")) diff --git a/builtin-rerere.c b/builtin-rerere.c index c607aade63..5c811423cc 100644 --- a/builtin-rerere.c +++ b/builtin-rerere.c @@ -339,7 +339,7 @@ tail_optimization: return write_rr(rr, fd); } -static int git_rerere_config(const char *var, const char *value) +static int git_rerere_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "gc.rerereresolved")) cutoff_resolve = git_config_int(var, value); @@ -348,7 +348,7 @@ static int git_rerere_config(const char *var, const char *value) else if (!strcmp(var, "rerere.enabled")) rerere_enabled = git_config_bool(var, value); else - return git_default_config(var, value); + return git_default_config(var, value, cb); return 0; } @@ -376,7 +376,7 @@ static int setup_rerere(struct path_list *merge_rr) { int fd; - git_config(git_rerere_config); + git_config(git_rerere_config, NULL); if (!is_rerere_enabled()) return -1; diff --git a/builtin-reset.c b/builtin-reset.c index 79424bb26e..e32ddd90ac 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -186,7 +186,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) OPT_END() }; - git_config(git_default_config); + git_config(git_default_config, NULL); argc = parse_options(argc, argv, options, git_reset_usage, PARSE_OPT_KEEP_DASHDASH); diff --git a/builtin-rev-list.c b/builtin-rev-list.c index edc0bd35bb..274299044e 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -546,7 +546,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) int bisect_find_all = 0; int quiet = 0; - git_config(git_default_config); + git_config(git_default_config, NULL); init_revisions(&revs, prefix); revs.abbrev = 0; revs.commit_format = CMIT_FMT_UNSPECIFIED; diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index f8d8548e9c..1d019b3c16 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -381,7 +381,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) return cmd_parseopt(argc - 1, argv + 1, prefix); prefix = setup_git_directory(); - git_config(git_default_config); + git_config(git_default_config, NULL); for (i = 1; i < argc; i++) { const char *arg = argv[i]; diff --git a/builtin-revert.c b/builtin-revert.c index 2b57525d72..0270f9b85a 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -269,7 +269,7 @@ static int revert_or_cherry_pick(int argc, const char **argv) const char *message, *encoding; const char *defmsg = xstrdup(git_path("MERGE_MSG")); - git_config(git_default_config); + git_config(git_default_config, NULL); me = action == REVERT ? "revert" : "cherry-pick"; setenv(GIT_REFLOG_ACTION, me, 0); parse_args(argc, argv); diff --git a/builtin-rm.c b/builtin-rm.c index c0a8bb6cf5..22c9bd1c6c 100644 --- a/builtin-rm.c +++ b/builtin-rm.c @@ -144,7 +144,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) const char **pathspec; char *seen; - git_config(git_default_config); + git_config(git_default_config, NULL); newfd = hold_locked_index(&lock_file, 1); diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 019abd3527..ee4269dd33 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -533,7 +533,7 @@ static void append_one_rev(const char *av) die("bad sha1 reference %s", av); } -static int git_show_branch_config(const char *var, const char *value) +static int git_show_branch_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "showbranch.default")) { if (!value) @@ -547,7 +547,7 @@ static int git_show_branch_config(const char *var, const char *value) return 0; } - return git_default_config(var, value); + return git_default_config(var, value, cb); } static int omit_in_dense(struct commit *commit, struct commit **rev, int n) @@ -611,7 +611,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) int reflog = 0; const char *reflog_base = NULL; - git_config(git_show_branch_config); + git_config(git_show_branch_config, NULL); /* If nothing is specified, try the default first */ if (ac == 1 && default_num) { diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c index d33982b967..b49bdb6900 100644 --- a/builtin-symbolic-ref.c +++ b/builtin-symbolic-ref.c @@ -35,7 +35,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) OPT_END(), }; - git_config(git_default_config); + git_config(git_default_config, NULL); argc = parse_options(argc, argv, options, git_symbolic_ref_usage, 0); if (msg &&!*msg) die("Refusing to perform update with empty message"); diff --git a/builtin-tag.c b/builtin-tag.c index 129ff57f11..e675206de3 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -256,7 +256,7 @@ static void set_signingkey(const char *value) die("signing key value too long (%.10s...)", value); } -static int git_tag_config(const char *var, const char *value) +static int git_tag_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "user.signingkey")) { if (!value) @@ -265,7 +265,7 @@ static int git_tag_config(const char *var, const char *value) return 0; } - return git_default_config(var, value); + return git_default_config(var, value, cb); } static void write_tag_body(int fd, const unsigned char *sha1) @@ -408,7 +408,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) OPT_END() }; - git_config(git_tag_config); + git_config(git_tag_config, NULL); argc = parse_options(argc, argv, options, git_tag_usage, 0); diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c index fecf0be779..85043d1fde 100644 --- a/builtin-unpack-objects.c +++ b/builtin-unpack-objects.c @@ -493,7 +493,7 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix) int i; unsigned char sha1[20]; - git_config(git_default_config); + git_config(git_default_config, NULL); quiet = !isatty(2); diff --git a/builtin-update-index.c b/builtin-update-index.c index a8795d3d5f..e1ca8deac2 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -567,7 +567,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) int lock_error = 0; struct lock_file *lock_file; - git_config(git_default_config); + git_config(git_default_config, NULL); /* We can't free this memory, it becomes part of a linked list parsed atexit() */ lock_file = xcalloc(1, sizeof(struct lock_file)); diff --git a/builtin-update-ref.c b/builtin-update-ref.c index e90737c350..93c127196d 100644 --- a/builtin-update-ref.c +++ b/builtin-update-ref.c @@ -22,7 +22,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) OPT_END(), }; - git_config(git_default_config); + git_config(git_default_config, NULL); argc = parse_options(argc, argv, options, git_update_ref_usage, 0); if (msg && !*msg) die("Refusing to perform update with empty message."); diff --git a/builtin-verify-pack.c b/builtin-verify-pack.c index 4958bbbf11..4c515a0570 100644 --- a/builtin-verify-pack.c +++ b/builtin-verify-pack.c @@ -55,7 +55,7 @@ int cmd_verify_pack(int argc, const char **argv, const char *prefix) int no_more_options = 0; int nothing_done = 1; - git_config(git_default_config); + git_config(git_default_config, NULL); while (1 < argc) { if (!no_more_options && argv[1][0] == '-') { if (!strcmp("-v", argv[1])) diff --git a/builtin-verify-tag.c b/builtin-verify-tag.c index db81496b46..92eaa89a45 100644 --- a/builtin-verify-tag.c +++ b/builtin-verify-tag.c @@ -90,7 +90,7 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix) { int i = 1, verbose = 0, had_error = 0; - git_config(git_default_config); + git_config(git_default_config, NULL); if (argc == 1) usage(builtin_verify_tag_usage); diff --git a/builtin-write-tree.c b/builtin-write-tree.c index e838d01233..c218799744 100644 --- a/builtin-write-tree.c +++ b/builtin-write-tree.c @@ -18,7 +18,7 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix) unsigned char sha1[20]; const char *me = "git-write-tree"; - git_config(git_default_config); + git_config(git_default_config, NULL); while (1 < argc) { const char *arg = argv[1]; if (!strcmp(arg, "--missing-ok")) diff --git a/cache.h b/cache.h index 9cee9a5f3f..ebe4031b65 100644 --- a/cache.h +++ b/cache.h @@ -711,10 +711,10 @@ extern int matches_pack_name(struct packed_git *p, const char *name); /* Dumb servers support */ extern int update_server_info(int); -typedef int (*config_fn_t)(const char *, const char *); -extern int git_default_config(const char *, const char *); -extern int git_config_from_file(config_fn_t fn, const char *); -extern int git_config(config_fn_t fn); +typedef int (*config_fn_t)(const char *, const char *, void *); +extern int git_default_config(const char *, const char *, void *); +extern int git_config_from_file(config_fn_t fn, const char *, void *); +extern int git_config(config_fn_t fn, void *); extern int git_parse_long(const char *, long *); extern int git_parse_ulong(const char *, unsigned long *); extern int git_config_int(const char *, const char *); @@ -726,7 +726,7 @@ extern int git_config_set(const char *, const char *); extern int git_config_set_multivar(const char *, const char *, const char *, int); extern int git_config_rename_section(const char *, const char *); extern const char *git_etc_gitconfig(void); -extern int check_repository_format_version(const char *var, const char *value); +extern int check_repository_format_version(const char *var, const char *value, void *cb); extern int git_env_bool(const char *, int); extern int git_config_system(void); extern int git_config_global(void); diff --git a/color.c b/color.c index 12a6453f90..fc0b72ad59 100644 --- a/color.c +++ b/color.c @@ -145,14 +145,14 @@ int git_config_colorbool(const char *var, const char *value, int stdout_is_tty) return 0; } -int git_color_default_config(const char *var, const char *value) +int git_color_default_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "color.ui")) { git_use_color_default = git_config_colorbool(var, value, -1); return 0; } - return git_default_config(var, value); + return git_default_config(var, value, cb); } static int color_vfprintf(FILE *fp, const char *color, const char *fmt, diff --git a/color.h b/color.h index ecda5569a2..6cf5c88aaf 100644 --- a/color.h +++ b/color.h @@ -13,7 +13,7 @@ extern int git_use_color_default; /* * Use this instead of git_default_config if you need the value of color.ui. */ -int git_color_default_config(const char *var, const char *value); +int git_color_default_config(const char *var, const char *value, void *cb); int git_config_colorbool(const char *var, const char *value, int stdout_is_tty); void color_parse(const char *var, const char *value, char *dst); diff --git a/config.c b/config.c index 0e22c7a642..5431e2dc01 100644 --- a/config.c +++ b/config.c @@ -111,7 +111,7 @@ static inline int iskeychar(int c) return isalnum(c) || c == '-'; } -static int get_value(config_fn_t fn, char *name, unsigned int len) +static int get_value(config_fn_t fn, void *data, char *name, unsigned int len) { int c; char *value; @@ -139,7 +139,7 @@ static int get_value(config_fn_t fn, char *name, unsigned int len) if (!value) return -1; } - return fn(name, value); + return fn(name, value, data); } static int get_extended_base_var(char *name, int baselen, int c) @@ -197,7 +197,7 @@ static int get_base_var(char *name) } } -static int git_parse_file(config_fn_t fn) +static int git_parse_file(config_fn_t fn, void *data) { int comment = 0; int baselen = 0; @@ -228,7 +228,7 @@ static int git_parse_file(config_fn_t fn) if (!isalpha(c)) break; var[baselen] = tolower(c); - if (get_value(fn, var, baselen+1) < 0) + if (get_value(fn, data, var, baselen+1) < 0) break; } die("bad config file line %d in %s", config_linenr, config_file_name); @@ -332,7 +332,7 @@ int git_config_string(const char **dest, const char *var, const char *value) return 0; } -int git_default_config(const char *var, const char *value) +int git_default_config(const char *var, const char *value, void *dummy) { /* This needs a better name */ if (!strcmp(var, "core.filemode")) { @@ -512,7 +512,7 @@ int git_default_config(const char *var, const char *value) return 0; } -int git_config_from_file(config_fn_t fn, const char *filename) +int git_config_from_file(config_fn_t fn, const char *filename, void *data) { int ret; FILE *f = fopen(filename, "r"); @@ -523,7 +523,7 @@ int git_config_from_file(config_fn_t fn, const char *filename) config_file_name = filename; config_linenr = 1; config_file_eof = 0; - ret = git_parse_file(fn); + ret = git_parse_file(fn, data); fclose(f); config_file_name = NULL; } @@ -561,7 +561,7 @@ int git_config_global(void) return !git_env_bool("GIT_CONFIG_NOGLOBAL", 0); } -int git_config(config_fn_t fn) +int git_config(config_fn_t fn, void *data) { int ret = 0; char *repo_config = NULL; @@ -574,7 +574,8 @@ int git_config(config_fn_t fn) filename = getenv(CONFIG_ENVIRONMENT); if (!filename) { if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) - ret += git_config_from_file(fn, git_etc_gitconfig()); + ret += git_config_from_file(fn, git_etc_gitconfig(), + data); home = getenv("HOME"); filename = getenv(CONFIG_LOCAL_ENVIRONMENT); if (!filename) @@ -584,11 +585,11 @@ int git_config(config_fn_t fn) if (git_config_global() && home) { char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); if (!access(user_config, R_OK)) - ret = git_config_from_file(fn, user_config); + ret = git_config_from_file(fn, user_config, data); free(user_config); } - ret += git_config_from_file(fn, filename); + ret += git_config_from_file(fn, filename, data); free(repo_config); return ret; } @@ -618,7 +619,7 @@ static int matches(const char* key, const char* value) !regexec(store.value_regex, value, 0, NULL, 0))); } -static int store_aux(const char* key, const char* value) +static int store_aux(const char* key, const char* value, void *cb) { const char *ep; size_t section_len; @@ -947,7 +948,7 @@ int git_config_set_multivar(const char* key, const char* value, * As a side effect, we make sure to transform only a valid * existing config file. */ - if (git_config_from_file(store_aux, config_filename)) { + if (git_config_from_file(store_aux, config_filename, NULL)) { error("invalid config file %s", config_filename); free(store.key); if (store.value_regex != NULL) { diff --git a/connect.c b/connect.c index d12b105970..e92af29735 100644 --- a/connect.c +++ b/connect.c @@ -360,7 +360,8 @@ static char *git_proxy_command; static const char *rhost_name; static int rhost_len; -static int git_proxy_command_options(const char *var, const char *value) +static int git_proxy_command_options(const char *var, const char *value, + void *cb) { if (!strcmp(var, "core.gitproxy")) { const char *for_pos; @@ -404,7 +405,7 @@ static int git_proxy_command_options(const char *var, const char *value) return 0; } - return git_default_config(var, value); + return git_default_config(var, value, cb); } static int git_use_proxy(const char *host) @@ -412,7 +413,7 @@ static int git_use_proxy(const char *host) rhost_name = host; rhost_len = strlen(host); git_proxy_command = getenv("GIT_PROXY_COMMAND"); - git_config(git_proxy_command_options); + git_config(git_proxy_command_options, NULL); rhost_name = NULL; return (git_proxy_command && *git_proxy_command); } diff --git a/convert.c b/convert.c index d8c94cb3ed..1c66844783 100644 --- a/convert.c +++ b/convert.c @@ -323,7 +323,7 @@ static struct convert_driver { char *clean; } *user_convert, **user_convert_tail; -static int read_convert_config(const char *var, const char *value) +static int read_convert_config(const char *var, const char *value, void *cb) { const char *ep, *name; int namelen; @@ -385,7 +385,7 @@ static void setup_convert_check(struct git_attr_check *check) attr_ident = git_attr("ident", 5); attr_filter = git_attr("filter", 6); user_convert_tail = &user_convert; - git_config(read_convert_config); + git_config(read_convert_config, NULL); } check[0].attr = attr_crlf; check[1].attr = attr_ident; diff --git a/daemon.c b/daemon.c index 2b4a6f145c..63cd12cd9c 100644 --- a/daemon.c +++ b/daemon.c @@ -306,7 +306,7 @@ struct daemon_service { static struct daemon_service *service_looking_at; static int service_enabled; -static int git_daemon_config(const char *var, const char *value) +static int git_daemon_config(const char *var, const char *value, void *cb) { if (!prefixcmp(var, "daemon.") && !strcmp(var + 7, service_looking_at->config_name)) { @@ -356,7 +356,7 @@ static int run_service(struct interp *itable, struct daemon_service *service) if (service->overridable) { service_looking_at = service; service_enabled = -1; - git_config(git_daemon_config); + git_config(git_daemon_config, NULL); if (0 <= service_enabled) enabled = service_enabled; } diff --git a/diff.c b/diff.c index e35384b444..1f46ff0f08 100644 --- a/diff.c +++ b/diff.c @@ -129,7 +129,7 @@ static int parse_funcname_pattern(const char *var, const char *ep, const char *v * never be affected by the setting of diff.renames * the user happens to have in the configuration file. */ -int git_diff_ui_config(const char *var, const char *value) +int git_diff_ui_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "diff.renamelimit")) { diff_rename_limit_default = git_config_int(var, value); @@ -166,10 +166,10 @@ int git_diff_ui_config(const char *var, const char *value) return parse_lldiff_command(var, ep, value); } - return git_diff_basic_config(var, value); + return git_diff_basic_config(var, value, cb); } -int git_diff_basic_config(const char *var, const char *value) +int git_diff_basic_config(const char *var, const char *value, void *cb) { if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) { int slot = parse_diff_color_slot(var, 11); @@ -190,7 +190,7 @@ int git_diff_basic_config(const char *var, const char *value) } } - return git_color_default_config(var, value); + return git_color_default_config(var, value, cb); } static char *quote_two(const char *one, const char *two) diff --git a/diff.h b/diff.h index 1bd94a4807..1cf1eff485 100644 --- a/diff.h +++ b/diff.h @@ -180,8 +180,8 @@ extern void diff_unmerge(struct diff_options *, #define DIFF_SETUP_USE_CACHE 2 #define DIFF_SETUP_USE_SIZE_CACHE 4 -extern int git_diff_basic_config(const char *var, const char *value); -extern int git_diff_ui_config(const char *var, const char *value); +extern int git_diff_basic_config(const char *var, const char *value, void *cb); +extern int git_diff_ui_config(const char *var, const char *value, void *cb); extern int diff_use_color_default; extern void diff_setup(struct diff_options *); extern int diff_opt_parse(struct diff_options *, const char **, int); diff --git a/fast-import.c b/fast-import.c index 73e5439d97..36ec5b87f8 100644 --- a/fast-import.c +++ b/fast-import.c @@ -2352,7 +2352,7 @@ static void import_marks(const char *input_file) fclose(f); } -static int git_pack_config(const char *k, const char *v) +static int git_pack_config(const char *k, const char *v, void *cb) { if (!strcmp(k, "pack.depth")) { max_depth = git_config_int(k, v); @@ -2370,7 +2370,7 @@ static int git_pack_config(const char *k, const char *v) pack_compression_seen = 1; return 0; } - return git_default_config(k, v); + return git_default_config(k, v, cb); } static const char fast_import_usage[] = @@ -2381,7 +2381,7 @@ int main(int argc, const char **argv) unsigned int i, show_stats = 1; setup_git_directory(); - git_config(git_pack_config); + git_config(git_pack_config, NULL); if (!pack_compression_seen && core_compression_seen) pack_compression_level = core_compression_level; diff --git a/hash-object.c b/hash-object.c index 61e7160b36..3d773900ff 100644 --- a/hash-object.c +++ b/hash-object.c @@ -43,7 +43,7 @@ int main(int argc, char **argv) int no_more_flags = 0; int hashstdin = 0; - git_config(git_default_config); + git_config(git_default_config, NULL); for (i = 1 ; i < argc; i++) { if (!no_more_flags && argv[i][0] == '-') { diff --git a/help.c b/help.c index af80979fcb..d89d43796f 100644 --- a/help.c +++ b/help.c @@ -252,7 +252,7 @@ static int add_man_viewer_info(const char *var, const char *value) return 0; } -static int git_help_config(const char *var, const char *value) +static int git_help_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "help.format")) { if (!value) @@ -269,7 +269,7 @@ static int git_help_config(const char *var, const char *value) if (!prefixcmp(var, "man.")) return add_man_viewer_info(var, value); - return git_default_config(var, value); + return git_default_config(var, value, cb); } /* most GUI terminals set COLUMNS (although some don't export it) */ @@ -641,7 +641,7 @@ int cmd_help(int argc, const char **argv, const char *prefix) const char *alias; setup_git_directory_gently(&nongit); - git_config(git_help_config); + git_config(git_help_config, NULL); argc = parse_options(argc, argv, builtin_help_options, builtin_help_usage, 0); diff --git a/http.c b/http.c index acf746a12d..2a21ccbb76 100644 --- a/http.c +++ b/http.c @@ -90,7 +90,7 @@ static void process_curl_messages(void) } #endif -static int http_options(const char *var, const char *value) +static int http_options(const char *var, const char *value, void *cb) { if (!strcmp("http.sslverify", var)) { if (curl_ssl_verify == -1) { @@ -169,7 +169,7 @@ static int http_options(const char *var, const char *value) } /* Fall back on the default ones */ - return git_default_config(var, value); + return git_default_config(var, value, cb); } static CURL* get_curl_handle(void) @@ -263,7 +263,7 @@ void http_init(struct remote *remote) if (low_speed_time != NULL) curl_low_speed_time = strtol(low_speed_time, NULL, 10); - git_config(http_options); + git_config(http_options, NULL); if (curl_ssl_verify == -1) curl_ssl_verify = 1; diff --git a/imap-send.c b/imap-send.c index db6559725e..1ec1310921 100644 --- a/imap-send.c +++ b/imap-send.c @@ -1247,7 +1247,7 @@ static imap_server_conf_t server = static char *imap_folder; static int -git_imap_config(const char *key, const char *val) +git_imap_config(const char *key, const char *val, void *cb) { char imap_key[] = "imap."; @@ -1296,7 +1296,7 @@ main(int argc, char **argv) /* init the random number generator */ arc4_init(); - git_config( git_imap_config ); + git_config(git_imap_config, NULL); if (!imap_folder) { fprintf( stderr, "no imap store specified\n" ); diff --git a/index-pack.c b/index-pack.c index 9c0c27813f..aaba9443cc 100644 --- a/index-pack.c +++ b/index-pack.c @@ -765,7 +765,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name, } } -static int git_index_pack_config(const char *k, const char *v) +static int git_index_pack_config(const char *k, const char *v, void *cb) { if (!strcmp(k, "pack.indexversion")) { pack_idx_default_version = git_config_int(k, v); @@ -773,7 +773,7 @@ static int git_index_pack_config(const char *k, const char *v) die("bad pack.indexversion=%d", pack_idx_default_version); return 0; } - return git_default_config(k, v); + return git_default_config(k, v, cb); } int main(int argc, char **argv) @@ -786,7 +786,7 @@ int main(int argc, char **argv) struct pack_idx_entry **idx_objects; unsigned char sha1[20]; - git_config(git_index_pack_config); + git_config(git_index_pack_config, NULL); for (i = 1; i < argc; i++) { char *arg = argv[i]; diff --git a/ll-merge.c b/ll-merge.c index 5ae74331bc..9837c842a3 100644 --- a/ll-merge.c +++ b/ll-merge.c @@ -225,7 +225,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn, static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail; static const char *default_ll_merge; -static int read_merge_config(const char *var, const char *value) +static int read_merge_config(const char *var, const char *value, void *cb) { struct ll_merge_driver *fn; const char *ep, *name; @@ -309,7 +309,7 @@ static void initialize_ll_merge(void) if (ll_user_merge_tail) return; ll_user_merge_tail = &ll_user_merge; - git_config(read_merge_config); + git_config(read_merge_config, NULL); } static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr) diff --git a/pager.c b/pager.c index ca002f9f79..dbd941421b 100644 --- a/pager.c +++ b/pager.c @@ -33,7 +33,7 @@ void setup_pager(void) return; if (!pager) { if (!pager_program) - git_config(git_default_config); + git_config(git_default_config, NULL); pager = pager_program; } if (!pager) diff --git a/receive-pack.c b/receive-pack.c index 828d49001d..b26f2e3a41 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -19,7 +19,7 @@ static int report_status; static char capabilities[] = " report-status delete-refs "; static int capabilities_sent; -static int receive_pack_config(const char *var, const char *value) +static int receive_pack_config(const char *var, const char *value, void *cb) { if (strcmp(var, "receive.denynonfastforwards") == 0) { deny_non_fast_forwards = git_config_bool(var, value); @@ -41,7 +41,7 @@ static int receive_pack_config(const char *var, const char *value) return 0; } - return git_default_config(var, value); + return git_default_config(var, value, cb); } static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data) @@ -489,7 +489,7 @@ int main(int argc, char **argv) if (is_repository_shallow()) die("attempt to push into a shallow repository"); - git_config(receive_pack_config); + git_config(receive_pack_config, NULL); if (0 <= transfer_unpack_limit) unpack_limit = transfer_unpack_limit; diff --git a/remote.c b/remote.c index 91cbb72dde..dff6c5f686 100644 --- a/remote.c +++ b/remote.c @@ -288,7 +288,7 @@ static void read_branches_file(struct remote *remote) remote->fetch_tags = 1; /* always auto-follow */ } -static int handle_config(const char *key, const char *value) +static int handle_config(const char *key, const char *value, void *cb) { const char *name; const char *subkey; @@ -410,7 +410,7 @@ static void read_config(void) current_branch = make_branch(head_ref + strlen("refs/heads/"), 0); } - git_config(handle_config); + git_config(handle_config, NULL); alias_all_urls(); } diff --git a/setup.c b/setup.c index b8fd476395..d630e374e7 100644 --- a/setup.c +++ b/setup.c @@ -300,7 +300,7 @@ void setup_work_tree(void) static int check_repository_format_gently(int *nongit_ok) { - git_config(check_repository_format_version); + git_config(check_repository_format_version, NULL); if (GIT_REPO_VERSION < repository_format_version) { if (!nongit_ok) die ("Expected git repo version <= %d, found %d", @@ -524,7 +524,7 @@ int git_config_perm(const char *var, const char *value) return i & 0666; } -int check_repository_format_version(const char *var, const char *value) +int check_repository_format_version(const char *var, const char *value, void *cb) { if (strcmp(var, "core.repositoryformatversion") == 0) repository_format_version = git_config_int(var, value); diff --git a/unpack-file.c b/unpack-file.c index 65c66eb0bf..bcdc8bbb3b 100644 --- a/unpack-file.c +++ b/unpack-file.c @@ -31,7 +31,7 @@ int main(int argc, char **argv) die("Not a valid object name %s", argv[1]); setup_git_directory(); - git_config(git_default_config); + git_config(git_default_config, NULL); puts(create_temp_file(sha1)); return 0; diff --git a/var.c b/var.c index c20ac919bd..724ba87a7c 100644 --- a/var.c +++ b/var.c @@ -39,13 +39,13 @@ static const char *read_var(const char *var) return val; } -static int show_config(const char *var, const char *value) +static int show_config(const char *var, const char *value, void *cb) { if (value) printf("%s=%s\n", var, value); else printf("%s\n", var); - return git_default_config(var, value); + return git_default_config(var, value, cb); } int main(int argc, char **argv) @@ -60,11 +60,11 @@ int main(int argc, char **argv) val = NULL; if (strcmp(argv[1], "-l") == 0) { - git_config(show_config); + git_config(show_config, NULL); list_vars(); return 0; } - git_config(git_default_config); + git_config(git_default_config, NULL); val = read_var(argv[1]); if (!val) usage(var_usage); diff --git a/wt-status.c b/wt-status.c index 532b4ea2c1..c932b39d20 100644 --- a/wt-status.c +++ b/wt-status.c @@ -362,7 +362,7 @@ void wt_status_print(struct wt_status *s) } } -int git_status_config(const char *k, const char *v) +int git_status_config(const char *k, const char *v, void *cb) { if (!strcmp(k, "status.submodulesummary")) { int is_bool; @@ -386,5 +386,5 @@ int git_status_config(const char *k, const char *v) wt_status_relative_paths = git_config_bool(k, v); return 0; } - return git_color_default_config(k, v); + return git_color_default_config(k, v, cb); } diff --git a/wt-status.h b/wt-status.h index 7d61410b17..f2c71302cb 100644 --- a/wt-status.h +++ b/wt-status.h @@ -27,7 +27,7 @@ struct wt_status { const char *prefix; }; -int git_status_config(const char *var, const char *value); +int git_status_config(const char *var, const char *value, void *cb); extern int wt_status_use_color; extern int wt_status_relative_paths; void wt_status_prepare(struct wt_status *s); From a73bc1275bb0939c51c496b1d50c516e6314eab2 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 15 May 2008 10:48:25 +0100 Subject: [PATCH 048/202] builtin-clone: fix initial checkout Somewhere in the process of finishing up builtin-clone, the update of the working tree was lost. This was due to not using the option "merge" for unpack_trees(). Breakage noticed by Kevin Ballard. Signed-off-by: Johannes Schindelin Tested-by: Jeff King Acked-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- builtin-clone.c | 3 +++ t/t5601-clone.sh | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/builtin-clone.c b/builtin-clone.c index a7c075d0e2..8713128e72 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -525,7 +525,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix) memset(&opts, 0, sizeof opts); opts.update = 1; + opts.merge = 1; + opts.fn = oneway_merge; opts.verbose_update = !option_quiet; + opts.src_index = &the_index; opts.dst_index = &the_index; tree = parse_tree_indirect(remote_head->old_sha1); diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index dc9d63dbf9..593d1a3877 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -23,4 +23,11 @@ test_expect_success 'clone with excess parameters' ' ' +test_expect_success 'clone checks out files' ' + + git clone src dst && + test -f dst/file + +' + test_done From 57e0e3ebd6fac2bf5fe46fd946dae6129b07f474 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 14 May 2008 23:30:43 +0100 Subject: [PATCH 049/202] cvsexportcommit: chomp only removes trailing whitespace In commit fef3a7cc(cvsexportcommit: be graceful when "cvs status" reorders the arguments), caution was taken to get the status even for files with leading or trailing whitespace. However, the author of that commit missed that chomp() removes only trailing newlines. With help of the mailing list, the author realized his mistake and provided this patch. The idea is that we do not want to rely on a certain layout of the output of "cvs status". Therefore we only call it with files that are unambiguous after stripping leading and trailing whitespace. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-cvsexportcommit.perl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl index b6036bd4d3..317a890271 100755 --- a/git-cvsexportcommit.perl +++ b/git-cvsexportcommit.perl @@ -210,7 +210,8 @@ if (@canstatusfiles) { my $basename = basename($name); $basename = "no file " . $basename if (exists($added{$basename})); - chomp($basename); + $basename =~ s/^\s+//; + $basename =~ s/\s+$//; if (!exists($fullname{$basename})) { $fullname{$basename} = $name; From 50fd9bd8430a957ea6c6674ce6112f375985abbc Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 14 May 2008 18:03:31 +0100 Subject: [PATCH 050/202] diff options: Introduce --ignore-submodules The new option --ignore-submodules can now be used to ignore changes in submodules. Why? Sometimes it is not interesting when a submodule changed. For example, when reordering some commits in the superproject, a dirty submodule is usually totally uninteresting. So we will use this option in git-rebase to test for a dirty working tree. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Documentation/diff-options.txt | 3 +++ diff.c | 9 +++++++++ diff.h | 1 + 3 files changed, 13 insertions(+) diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index 13234fa280..859d67990a 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -228,6 +228,9 @@ endif::git-format-patch[] --no-ext-diff:: Disallow external diff drivers. +--ignore-submodules:: + Ignore changes to submodules in the diff generation. + --src-prefix=:: Show the given source prefix instead of "a/". diff --git a/diff.c b/diff.c index 439d4746ca..d57bc299ee 100644 --- a/diff.c +++ b/diff.c @@ -2496,6 +2496,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) DIFF_OPT_SET(options, ALLOW_EXTERNAL); else if (!strcmp(arg, "--no-ext-diff")) DIFF_OPT_CLR(options, ALLOW_EXTERNAL); + else if (!strcmp(arg, "--ignore-submodules")) + DIFF_OPT_SET(options, IGNORE_SUBMODULES); /* misc options */ else if (!strcmp(arg, "-z")) @@ -3355,6 +3357,9 @@ void diff_addremove(struct diff_options *options, char concatpath[PATH_MAX]; struct diff_filespec *one, *two; + if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(mode)) + return; + /* This may look odd, but it is a preparation for * feeding "there are unchanged files which should * not produce diffs, but when you are doing copy @@ -3399,6 +3404,10 @@ void diff_change(struct diff_options *options, char concatpath[PATH_MAX]; struct diff_filespec *one, *two; + if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(old_mode) + && S_ISGITLINK(new_mode)) + return; + if (DIFF_OPT_TST(options, REVERSE_DIFF)) { unsigned tmp; const unsigned char *tmp_c; diff --git a/diff.h b/diff.h index 3a02d38d12..1dfe1f98b1 100644 --- a/diff.h +++ b/diff.h @@ -63,6 +63,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q, #define DIFF_OPT_REVERSE_DIFF (1 << 15) #define DIFF_OPT_CHECK_FAILED (1 << 16) #define DIFF_OPT_RELATIVE_NAME (1 << 17) +#define DIFF_OPT_IGNORE_SUBMODULES (1 << 18) #define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag) #define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag) #define DIFF_OPT_CLR(opts, flag) ((opts)->flags &= ~DIFF_OPT_##flag) From 5fdeacb0ca3935923ab988c81414c16080db6a32 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 14 May 2008 18:03:45 +0100 Subject: [PATCH 051/202] Teach update-index about --ignore-submodules Like with the diff machinery, update-index should sometimes just ignore submodules (e.g. to determine a clean state before a rebase). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Documentation/git-update-index.txt | 5 +++++ builtin-update-index.c | 4 ++++ cache.h | 1 + read-cache.c | 4 ++++ 4 files changed, 14 insertions(+) diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index 66be18ef36..06640603c4 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -15,6 +15,7 @@ SYNOPSIS [--cacheinfo ]\* [--chmod=(+|-)x] [--assume-unchanged | --no-assume-unchanged] + [--ignore-submodules] [--really-refresh] [--unresolve] [--again | -g] [--info-only] [--index-info] [-z] [--stdin] @@ -54,6 +55,10 @@ OPTIONS default behavior is to error out. This option makes git-update-index continue anyway. +--ignore-submodules: + Do not try to update submodules. This option is only respected + when passed before --refresh. + --unmerged:: If --refresh finds unmerged changes in the index, the default behavior is to error out. This option makes git-update-index diff --git a/builtin-update-index.c b/builtin-update-index.c index a8795d3d5f..d4c85c0cbc 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -593,6 +593,10 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) refresh_flags |= REFRESH_QUIET; continue; } + if (!strcmp(path, "--ignore-submodules")) { + refresh_flags |= REFRESH_IGNORE_SUBMODULES; + continue; + } if (!strcmp(path, "--add")) { allow_add = 1; continue; diff --git a/cache.h b/cache.h index 093f04cec0..b753b49497 100644 --- a/cache.h +++ b/cache.h @@ -388,6 +388,7 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); #define REFRESH_UNMERGED 0x0002 /* allow unmerged */ #define REFRESH_QUIET 0x0004 /* be quiet about it */ #define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */ +#define REFRESH_IGNORE_SUBMODULES 0x0008 /* ignore submodules */ extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen); struct lock_file { diff --git a/read-cache.c b/read-cache.c index 0382804e76..47dd2013fb 100644 --- a/read-cache.c +++ b/read-cache.c @@ -942,6 +942,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p int allow_unmerged = (flags & REFRESH_UNMERGED) != 0; int quiet = (flags & REFRESH_QUIET) != 0; int not_new = (flags & REFRESH_IGNORE_MISSING) != 0; + int ignore_submodules = (flags & REFRESH_IGNORE_SUBMODULES) != 0; unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0; for (i = 0; i < istate->cache_nr; i++) { @@ -949,6 +950,9 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p int cache_errno = 0; ce = istate->cache[i]; + if (ignore_submodules && S_ISGITLINK(ce->ce_mode)) + continue; + if (ce_stage(ce)) { while ((i < istate->cache_nr) && ! strcmp(istate->cache[i]->name, ce->name)) From 6848d58c60b7af365ce54cf3e3b274a2f9da2e7e Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 14 May 2008 18:03:59 +0100 Subject: [PATCH 052/202] Ignore dirty submodule states during rebase and stash When rebasing or stashing, chances are that you do not care about dirty submodules, since they are not updated by those actions anyway. So ignore the submodules' states. Note: the submodule states -- as committed in the superproject -- will still be stashed and rebased, it is _just_ the state of the submodule in the working tree which is ignored. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 11 +++-- git-rebase.sh | 8 ++-- git-stash.sh | 6 +-- t/t7402-submodule-rebase.sh | 92 +++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 12 deletions(-) create mode 100755 t/t7402-submodule-rebase.sh diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 8aa73712ca..8ee08ff2fd 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -56,9 +56,9 @@ output () { require_clean_work_tree () { # test if working tree is dirty git rev-parse --verify HEAD > /dev/null && - git update-index --refresh && - git diff-files --quiet && - git diff-index --cached --quiet HEAD -- || + git update-index --ignore-submodules --refresh && + git diff-files --quiet --ignore-submodules && + git diff-index --cached --quiet HEAD --ignore-submodules -- || die "Working tree is dirty" } @@ -377,11 +377,12 @@ do # Sanity check git rev-parse --verify HEAD >/dev/null || die "Cannot read HEAD" - git update-index --refresh && git diff-files --quiet || + git update-index --ignore-submodules --refresh && + git diff-files --quiet --ignore-submodules || die "Working tree is dirty" # do we have anything to commit? - if git diff-index --cached --quiet HEAD -- + if git diff-index --cached --quiet --ignore-submodules HEAD -- then : Nothing to commit -- skip this else diff --git a/git-rebase.sh b/git-rebase.sh index 68855c18ae..dd7dfe123c 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -60,7 +60,7 @@ continue_merge () { fi cmt=`cat "$dotest/current"` - if ! git diff-index --quiet HEAD -- + if ! git diff-index --quiet --ignore-submodules HEAD -- then if ! git commit --no-verify -C "$cmt" then @@ -150,7 +150,7 @@ while test $# != 0 do case "$1" in --continue) - git diff-files --quiet || { + git diff-files --quiet --ignore-submodules || { echo "You must edit all merge conflicts and then" echo "mark them as resolved using git add" exit 1 @@ -282,8 +282,8 @@ else fi # The tree must be really really clean. -git update-index --refresh || exit -diff=$(git diff-index --cached --name-status -r HEAD --) +git update-index --ignore-submodules --refresh || exit +diff=$(git diff-index --cached --name-status -r --ignore-submodules HEAD --) case "$diff" in ?*) echo "cannot rebase: your index is not up-to-date" echo "$diff" diff --git a/git-stash.sh b/git-stash.sh index c2b68205a2..4938ade589 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -15,8 +15,8 @@ trap 'rm -f "$TMP-*"' 0 ref_stash=refs/stash no_changes () { - git diff-index --quiet --cached HEAD -- && - git diff-files --quiet + git diff-index --quiet --cached HEAD --ignore-submodules -- && + git diff-files --quiet --ignore-submodules } clear_stash () { @@ -130,7 +130,7 @@ show_stash () { } apply_stash () { - git diff-files --quiet || + git diff-files --quiet --ignore-submodules || die 'Cannot restore on top of a dirty state' unstash_index= diff --git a/t/t7402-submodule-rebase.sh b/t/t7402-submodule-rebase.sh new file mode 100755 index 0000000000..5becb3ec54 --- /dev/null +++ b/t/t7402-submodule-rebase.sh @@ -0,0 +1,92 @@ +#!/bin/sh +# +# Copyright (c) 2008 Johannes Schindelin +# + +test_description='Test rebasing and stashing with dirty submodules' + +. ./test-lib.sh + +test_expect_success setup ' + + echo file > file && + git add file && + test_tick && + git commit -m initial && + git clone . submodule && + git add submodule && + test_tick && + git commit -m submodule && + echo second line >> file && + (cd submodule && git pull) && + test_tick && + git commit -m file-and-submodule -a + +' + +test_expect_success 'rebase with a dirty submodule' ' + + (cd submodule && + echo 3rd line >> file && + test_tick && + git commit -m fork -a) && + echo unrelated >> file2 && + git add file2 && + test_tick && + git commit -m unrelated file2 && + echo other line >> file && + test_tick && + git commit -m update file && + CURRENT=$(cd submodule && git rev-parse HEAD) && + EXPECTED=$(git rev-parse HEAD~2:submodule) && + GIT_TRACE=1 git rebase --onto HEAD~2 HEAD^ && + STORED=$(git rev-parse HEAD:submodule) && + test $EXPECTED = $STORED && + test $CURRENT = $(cd submodule && git rev-parse HEAD) + +' + +cat > fake-editor.sh << \EOF +#!/bin/sh +echo $EDITOR_TEXT +EOF +chmod a+x fake-editor.sh + +test_expect_success 'interactive rebase with a dirty submodule' ' + + test submodule = $(git diff --name-only) && + HEAD=$(git rev-parse HEAD) && + GIT_EDITOR="\"$(pwd)/fake-editor.sh\"" EDITOR_TEXT="pick $HEAD" \ + git rebase -i HEAD^ && + test submodule = $(git diff --name-only) + +' + +test_expect_success 'rebase with dirty file and submodule fails' ' + + echo yet another line >> file && + test_tick && + git commit -m next file && + echo rewrite > file && + test_tick && + git commit -m rewrite file && + echo dirty > file && + ! git rebase --onto HEAD~2 HEAD^ + +' + +test_expect_success 'stash with a dirty submodule' ' + + echo new > file && + CURRENT=$(cd submodule && git rev-parse HEAD) && + git stash && + test new != $(cat file) && + test submodule = $(git diff --name-only) && + test $CURRENT = $(cd submodule && git rev-parse HEAD) && + git stash apply && + test new = $(cat file) && + test $CURRENT = $(cd submodule && git rev-parse HEAD) + +' + +test_done From d775734c40afed216160437c59a45c93bdf28689 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 14 May 2008 15:29:49 +0100 Subject: [PATCH 053/202] cvsexportcommit: introduce -W for shared working trees (between Git and CVS) If you have a CVS checkout, it is easy to import the CVS history by calling "git cvsimport". However, interacting with the CVS repository using "git cvsexportcommit" was cumbersome, since that script assumes separate working directories for Git and CVS. Now, you can call cvsexportcommit with the -W option. This will automatically discover the GIT_DIR, and it will check out the parent commit before exporting the commit. The intended workflow is this: $ CVSROOT=$URL cvs co module $ cd module $ git cvsimport hack, hack, hack, making two commits, cleaning them up using rebase -i. $ git cvsexportcommit -W -c -p -u HEAD^ $ git cvsexportcommit -W -c -p -u HEAD Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Documentation/git-cvsexportcommit.txt | 7 +++++- git-cvsexportcommit.perl | 35 +++++++++++++++++++++++---- t/t9200-git-cvsexportcommit.sh | 17 +++++++++++++ 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/Documentation/git-cvsexportcommit.txt b/Documentation/git-cvsexportcommit.txt index 9a47b4c397..b78c9f36f3 100644 --- a/Documentation/git-cvsexportcommit.txt +++ b/Documentation/git-cvsexportcommit.txt @@ -8,7 +8,7 @@ git-cvsexportcommit - Export a single commit to a CVS checkout SYNOPSIS -------- -'git-cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-w cvsworkdir] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID +'git-cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-w cvsworkdir] [-W] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID DESCRIPTION @@ -67,6 +67,11 @@ OPTIONS option does not require GIT_DIR to be set before execution if the current directory is within a git repository. +-W:: + Tell cvsexportcommit that the current working directory is not only + a Git checkout, but also the CVS checkout. Therefore, Git will + reset the working directory to the parent commit before proceeding. + -v:: Verbose. diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl index 317a890271..eaa3218e79 100755 --- a/git-cvsexportcommit.perl +++ b/git-cvsexportcommit.perl @@ -7,15 +7,15 @@ use Data::Dumper; use File::Basename qw(basename dirname); use File::Spec; -our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u, $opt_w); +our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u, $opt_w, $opt_W); -getopts('uhPpvcfam:d:w:'); +getopts('uhPpvcfam:d:w:W'); $opt_h && usage(); die "Need at least one commit identifier!" unless @ARGV; -if ($opt_w) { +if ($opt_w || $opt_W) { # Remember where GIT_DIR is before changing to CVS checkout unless ($ENV{GIT_DIR}) { # No GIT_DIR set. Figure it out for ourselves @@ -25,7 +25,9 @@ if ($opt_w) { } # Make sure GIT_DIR is absolute $ENV{GIT_DIR} = File::Spec->rel2abs($ENV{GIT_DIR}); +} +if ($opt_w) { if (! -d $opt_w."/CVS" ) { die "$opt_w is not a CVS checkout"; } @@ -116,6 +118,15 @@ if ($parent) { } } +my $go_back_to = 0; + +if ($opt_W) { + $opt_v && print "Resetting to $parent\n"; + $go_back_to = `git symbolic-ref HEAD 2> /dev/null || + git rev-parse HEAD` || die "Could not determine current branch"; + system("git checkout -q $parent^0") && die "Could not check out $parent^0"; +} + $opt_v && print "Applying to CVS commit $commit from parent $parent\n"; # grab the commit message @@ -260,7 +271,11 @@ if ($dirty) { } print "Applying\n"; -`GIT_DIR= git-apply $context --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch"; +if ($opt_W) { + system("git checkout -q $commit^0") && die "cannot patch"; +} else { + `GIT_DIR= git-apply $context --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch"; +} print "Patch applied successfully. Adding new files and directories to CVS\n"; my $dirtypatch = 0; @@ -313,7 +328,9 @@ if ($dirtypatch) { print "using a patch program. After applying the patch and resolving the\n"; print "problems you may commit using:"; print "\n cd \"$opt_w\"" if $opt_w; - print "\n $cmd\n\n"; + print "\n $cmd\n"; + print "\n git checkout $go_back_to\n" if $go_back_to; + print "\n"; exit(1); } @@ -333,6 +350,14 @@ if ($opt_c) { # clean up unlink(".cvsexportcommit.diff"); +if ($opt_W) { + system("git checkout $go_back_to") && die "cannot move back to $go_back_to"; + if (!($go_back_to =~ /^[0-9a-fA-F]{40}$/)) { + system("git symbolic-ref HEAD $go_back_to") && + die "cannot move back to $go_back_to"; + } +} + # CVS version 1.11.x and 1.12.x sleeps the wrong way to ensure the timestamp # used by CVS and the one set by subsequence file modifications are different. # If they are not different CVS will not detect changes. diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh index 42b144b1b3..b1dc32d056 100755 --- a/t/t9200-git-cvsexportcommit.sh +++ b/t/t9200-git-cvsexportcommit.sh @@ -297,4 +297,21 @@ test_expect_success 'commit a file with leading spaces in the name' ' ' +test_expect_success 'use the same checkout for Git and CVS' ' + + (mkdir shared && + cd shared && + unset GIT_DIR && + cvs co . && + git init && + git add " space" && + git commit -m "fake initial commit" && + echo Hello >> " space" && + git commit -m "Another change" " space" && + git cvsexportcommit -W -p -u -c HEAD && + grep Hello " space" && + git diff-files) + +' + test_done From 044182ef82f1a371c469a26a6d64c7b823aea764 Mon Sep 17 00:00:00 2001 From: Matthew Ogilvie Date: Wed, 14 May 2008 22:35:46 -0600 Subject: [PATCH 054/202] git-cvsserver: add mechanism for managing working tree and current directory There are various reasons git-cvsserver needs to manipulate the current directory, and this patch attempts to clarify and validate such changes: 1. Temporary empty working directory (with index) for certain operations that require an index file to work. 2. Use a temporary directory with temporary file names for doing merges of user's dirty sandbox state with latest changes in repository. 3. Coming up soon: Set up an index and either a valid or empty working directory when calling git-check-attr to decide if a file should be marked binary (-kb). Signed-off-by: Matthew Ogilvie Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 252 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 213 insertions(+), 39 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 29dbfc940b..674892b816 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -21,6 +21,7 @@ use bytes; use Fcntl; use File::Temp qw/tempdir tempfile/; +use File::Path qw/rmtree/; use File::Basename; use Getopt::Long qw(:config require_order no_ignore_case); @@ -86,6 +87,17 @@ my $methods = { # $state holds all the bits of information the clients sends us that could # potentially be useful when it comes to actually _doing_ something. my $state = { prependdir => '' }; + +# Work is for managing temporary working directory +my $work = + { + state => undef, # undef, 1 (empty), 2 (with stuff) + workDir => undef, + index => undef, + emptyDir => undef, + tmpDir => undef + }; + $log->info("--------------- STARTING -----------------"); my $usage = @@ -189,6 +201,9 @@ while () $log->debug("Processing time : user=" . (times)[0] . " system=" . (times)[1]); $log->info("--------------- FINISH -----------------"); +chdir '/'; +exit 0; + # Magic catchall method. # This is the method that will handle all commands we haven't yet # implemented. It simply sends a warning to the log file indicating a @@ -1101,10 +1116,10 @@ sub req_update $log->info("Updating '$filename'"); my ( $filepart, $dirpart ) = filenamesplit($meta->{name},1); - my $dir = tempdir( DIR => $TEMP_DIR, CLEANUP => 1 ) . "/"; + my $mergeDir = setupTmpDir(); - chdir $dir; my $file_local = $filepart . ".mine"; + my $mergedFile = "$mergeDir/$file_local"; system("ln","-s",$state->{entries}{$filename}{modified_filename}, $file_local); my $file_old = $filepart . "." . $oldmeta->{revision}; transmitfile($oldmeta->{filehash}, { targetfile => $file_old }); @@ -1115,11 +1130,13 @@ sub req_update $log->info("Merging $file_local, $file_old, $file_new"); print "M Merging differences between 1.$oldmeta->{revision} and 1.$meta->{revision} into $filename\n"; - $log->debug("Temporary directory for merge is $dir"); + $log->debug("Temporary directory for merge is $mergeDir"); my $return = system("git", "merge-file", $file_local, $file_old, $file_new); $return >>= 8; + cleanupTmpDir(); + if ( $return == 0 ) { $log->info("Merged successfully"); @@ -1168,13 +1185,11 @@ sub req_update # transmit file, format is single integer on a line by itself (file # size) followed by the file contents # TODO : we should copy files in blocks - my $data = `cat $file_local`; + my $data = `cat $mergedFile`; $log->debug("File size : " . length($data)); print length($data) . "\n"; print $data; } - - chdir "/"; } } @@ -1195,6 +1210,7 @@ sub req_ci if ( $state->{method} eq 'pserver') { print "error 1 pserver access cannot commit\n"; + cleanupWorkTree(); exit; } @@ -1202,6 +1218,7 @@ sub req_ci { $log->warn("file 'index' already exists in the git repository"); print "error 1 Index already exists in git repo\n"; + cleanupWorkTree(); exit; } @@ -1209,31 +1226,20 @@ sub req_ci my $updater = GITCVS::updater->new($state->{CVSROOT}, $state->{module}, $log); $updater->update(); - my $tmpdir = tempdir ( DIR => $TEMP_DIR ); - my ( undef, $file_index ) = tempfile ( DIR => $TEMP_DIR, OPEN => 0 ); - $log->info("Lockless commit start, basing commit on '$tmpdir', index file is '$file_index'"); - - $ENV{GIT_DIR} = $state->{CVSROOT} . "/"; - $ENV{GIT_WORK_TREE} = "."; - $ENV{GIT_INDEX_FILE} = $file_index; - # Remember where the head was at the beginning. my $parenthash = `git show-ref -s refs/heads/$state->{module}`; chomp $parenthash; if ($parenthash !~ /^[0-9a-f]{40}$/) { print "error 1 pserver cannot find the current HEAD of module"; + cleanupWorkTree(); exit; } - chdir $tmpdir; + setupWorkTree($parenthash); - # populate the temporary index - system("git-read-tree", $parenthash); - unless ($? == 0) - { - die "Error running git-read-tree $state->{module} $file_index $!"; - } - $log->info("Created index '$file_index' for head $state->{module} - exit status $?"); + $log->info("Lockless commit start, basing commit on '$work->{workDir}', index file is '$work->{index}'"); + + $log->info("Created index '$work->{index}' for head $state->{module} - exit status $?"); my @committedfiles = (); my %oldmeta; @@ -1271,7 +1277,7 @@ sub req_ci { # fail everything if an up to date check fails print "error 1 Up to date check failed for $filename\n"; - chdir "/"; + cleanupWorkTree(); exit; } @@ -1313,7 +1319,7 @@ sub req_ci { print "E No files to commit\n"; print "ok\n"; - chdir "/"; + cleanupWorkTree(); return; } @@ -1336,7 +1342,7 @@ sub req_ci { $log->warn("Commit failed (Invalid commit hash)"); print "error 1 Commit failed (unknown reason)\n"; - chdir "/"; + cleanupWorkTree(); exit; } @@ -1348,7 +1354,7 @@ sub req_ci { $log->warn("Commit failed (update hook declined to update ref)"); print "error 1 Commit failed (update hook declined)\n"; - chdir "/"; + cleanupWorkTree(); exit; } } @@ -1358,6 +1364,7 @@ sub req_ci "refs/heads/$state->{module}", $commithash, $parenthash)) { $log->warn("update-ref for $state->{module} failed."); print "error 1 Cannot commit -- update first\n"; + cleanupWorkTree(); exit; } @@ -1414,7 +1421,7 @@ sub req_ci } } - chdir "/"; + cleanupWorkTree(); print "ok\n"; } @@ -1757,15 +1764,9 @@ sub req_annotate argsfromdir($updater); # we'll need a temporary checkout dir - my $tmpdir = tempdir ( DIR => $TEMP_DIR ); - my ( undef, $file_index ) = tempfile ( DIR => $TEMP_DIR, OPEN => 0 ); - $log->info("Temp checkoutdir creation successful, basing annotate session work on '$tmpdir', index file is '$file_index'"); + setupWorkTree(); - $ENV{GIT_DIR} = $state->{CVSROOT} . "/"; - $ENV{GIT_WORK_TREE} = "."; - $ENV{GIT_INDEX_FILE} = $file_index; - - chdir $tmpdir; + $log->info("Temp checkoutdir creation successful, basing annotate session work on '$work->{workDir}', index file is '$ENV{GIT_INDEX_FILE}'"); # foreach file specified on the command line ... foreach my $filename ( @{$state->{args}} ) @@ -1789,10 +1790,10 @@ sub req_annotate system("git-read-tree", $lastseenin); unless ($? == 0) { - print "E error running git-read-tree $lastseenin $file_index $!\n"; + print "E error running git-read-tree $lastseenin $ENV{GIT_INDEX_FILE} $!\n"; return; } - $log->info("Created index '$file_index' with commit $lastseenin - exit status $?"); + $log->info("Created index '$ENV{GIT_INDEX_FILE}' with commit $lastseenin - exit status $?"); # do a checkout of the file system('git-checkout-index', '-f', '-u', $filename); @@ -1808,7 +1809,7 @@ sub req_annotate # git-jsannotate telling us about commits we are hiding # from the client. - my $a_hints = "$tmpdir/.annotate_hints"; + my $a_hints = "$work->{workDir}/.annotate_hints"; if (!open(ANNOTATEHINTS, '>', $a_hints)) { print "E failed to open '$a_hints' for writing: $!\n"; return; @@ -1862,7 +1863,7 @@ sub req_annotate } # done; get out of the tempdir - chdir "/"; + cleanupWorkDir(); print "ok\n"; @@ -2115,6 +2116,179 @@ sub filecleanup return $filename; } +sub validateGitDir +{ + if( !defined($state->{CVSROOT}) ) + { + print "error 1 CVSROOT not specified\n"; + cleanupWorkTree(); + exit; + } + if( $ENV{GIT_DIR} ne ($state->{CVSROOT} . '/') ) + { + print "error 1 Internally inconsistent CVSROOT\n"; + cleanupWorkTree(); + exit; + } +} + +# Setup working directory in a work tree with the requested version +# loaded in the index. +sub setupWorkTree +{ + my ($ver) = @_; + + validateGitDir(); + + if( ( defined($work->{state}) && $work->{state} != 1 ) || + defined($work->{tmpDir}) ) + { + $log->warn("Bad work tree state management"); + print "error 1 Internal setup multiple work trees without cleanup\n"; + cleanupWorkTree(); + exit; + } + + $work->{workDir} = tempdir ( DIR => $TEMP_DIR ); + + if( !defined($work->{index}) ) + { + (undef, $work->{index}) = tempfile ( DIR => $TEMP_DIR, OPEN => 0 ); + } + + chdir $work->{workDir} or + die "Unable to chdir to $work->{workDir}\n"; + + $log->info("Setting up GIT_WORK_TREE as '.' in '$work->{workDir}', index file is '$work->{index}'"); + + $ENV{GIT_WORK_TREE} = "."; + $ENV{GIT_INDEX_FILE} = $work->{index}; + $work->{state} = 2; + + if($ver) + { + system("git","read-tree",$ver); + unless ($? == 0) + { + $log->warn("Error running git-read-tree"); + die "Error running git-read-tree $ver in $work->{workDir} $!\n"; + } + } + # else # req_annotate reads tree for each file +} + +# Ensure current directory is in some kind of working directory, +# with a recent version loaded in the index. +sub ensureWorkTree +{ + if( defined($work->{tmpDir}) ) + { + $log->warn("Bad work tree state management [ensureWorkTree()]"); + print "error 1 Internal setup multiple dirs without cleanup\n"; + cleanupWorkTree(); + exit; + } + if( $work->{state} ) + { + return; + } + + validateGitDir(); + + if( !defined($work->{emptyDir}) ) + { + $work->{emptyDir} = tempdir ( DIR => $TEMP_DIR, OPEN => 0); + } + chdir $work->{emptyDir} or + die "Unable to chdir to $work->{emptyDir}\n"; + + my $ver = `git show-ref -s refs/heads/$state->{module}`; + chomp $ver; + if ($ver !~ /^[0-9a-f]{40}$/) + { + $log->warn("Error from git show-ref -s refs/head$state->{module}"); + print "error 1 cannot find the current HEAD of module"; + cleanupWorkTree(); + exit; + } + + if( !defined($work->{index}) ) + { + (undef, $work->{index}) = tempfile ( DIR => $TEMP_DIR, OPEN => 0 ); + } + + $ENV{GIT_WORK_TREE} = "."; + $ENV{GIT_INDEX_FILE} = $work->{index}; + $work->{state} = 1; + + system("git","read-tree",$ver); + unless ($? == 0) + { + die "Error running git-read-tree $ver $!\n"; + } +} + +# Cleanup working directory that is not needed any longer. +sub cleanupWorkTree +{ + if( ! $work->{state} ) + { + return; + } + + chdir "/" or die "Unable to chdir '/'\n"; + + if( defined($work->{workDir}) ) + { + rmtree( $work->{workDir} ); + undef $work->{workDir}; + } + undef $work->{state}; +} + +# Setup a temporary directory (not a working tree), typically for +# merging dirty state as in req_update. +sub setupTmpDir +{ + $work->{tmpDir} = tempdir ( DIR => $TEMP_DIR ); + chdir $work->{tmpDir} or die "Unable to chdir $work->{tmpDir}\n"; + + return $work->{tmpDir}; +} + +# Clean up a previously setupTmpDir. Restore previous work tree if +# appropriate. +sub cleanupTmpDir +{ + if ( !defined($work->{tmpDir}) ) + { + $log->warn("cleanup tmpdir that has not been setup"); + die "Cleanup tmpDir that has not been setup\n"; + } + if( defined($work->{state}) ) + { + if( $work->{state} == 1 ) + { + chdir $work->{emptyDir} or + die "Unable to chdir to $work->{emptyDir}\n"; + } + elsif( $work->{state} == 2 ) + { + chdir $work->{workDir} or + die "Unable to chdir to $work->{emptyDir}\n"; + } + else + { + $log->warn("Inconsistent work dir state"); + die "Inconsistent work dir state\n"; + } + } + else + { + chdir "/" or die "Unable to chdir '/'\n"; + } +} + # Given a path, this function returns a string containing the kopts # that should go into that path's Entries line. For example, a binary # file should get -kb. From 8a06a632976ead891115ad9ac9dea7b99c52158e Mon Sep 17 00:00:00 2001 From: Matthew Ogilvie Date: Wed, 14 May 2008 22:35:47 -0600 Subject: [PATCH 055/202] implement gitcvs.usecrlfattr If gitcvs.usecrlfattr is set to true, git-cvsserver will consult the "crlf" for each file to determine if it should mark the file as binary (-kb). Signed-off-by: Matthew Ogilvie Signed-off-by: Junio C Hamano --- Documentation/config.txt | 23 +++-- Documentation/git-cvsserver.txt | 26 ++++- git-cvsserver.perl | 71 +++++++++++-- t/t9401-git-cvsserver-crlf.sh | 178 ++++++++++++++++++++++++++++++++ 4 files changed, 276 insertions(+), 22 deletions(-) create mode 100755 t/t9401-git-cvsserver-crlf.sh diff --git a/Documentation/config.txt b/Documentation/config.txt index 217980f48d..036e61e2f6 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -660,11 +660,21 @@ gitcvs.logfile:: Path to a log file where the CVS server interface well... logs various stuff. See linkgit:git-cvsserver[1]. +gitcvs.usecrlfattr + If true, the server will look up the `crlf` attribute for + files to determine the '-k' modes to use. If `crlf` is set, + the '-k' mode will be left blank, so cvs clients will + treat it as text. If `crlf` is explicitly unset, the file + will be set with '-kb' mode, which supresses any newline munging + the client might otherwise do. If `crlf` is not specified, + then 'gitcvs.allbinary' is used. See linkgit:gitattribute[5]. + gitcvs.allbinary:: - If true, all files are sent to the client in mode '-kb'. This - causes the client to treat all files as binary files which suppresses - any newline munging it otherwise might do. A work-around for the - fact that there is no way yet to set single files to mode '-kb'. + If true, all files not otherwise specified using + 'gitcvs.usecrlfattr' and an explicitly set or unset `crlf` + attribute are sent to the client in mode '-kb'. This + causes the client to treat them as binary files which + suppresses any newline munging it otherwise might do. gitcvs.dbname:: Database used by git-cvsserver to cache revision information @@ -695,8 +705,9 @@ gitcvs.dbTableNamePrefix:: linkgit:git-cvsserver[1] for details). Any non-alphabetic characters will be replaced with underscores. -All gitcvs variables except for 'gitcvs.allbinary' can also be -specified as 'gitcvs..' (where 'access_method' +All gitcvs variables except for 'gitcvs.usecrlfattr' and +'gitcvs.allbinary' can also be specified as +'gitcvs..' (where 'access_method' is one of "ext" and "pserver") to make them apply only for the given access method. diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt index b1106714b2..4888b8604d 100644 --- a/Documentation/git-cvsserver.txt +++ b/Documentation/git-cvsserver.txt @@ -301,11 +301,27 @@ checkout, diff, status, update, log, add, remove, commit. Legacy monitoring operations are not supported (edit, watch and related). Exports and tagging (tags and branches) are not supported at this stage. -The server should set the '-k' mode to binary when relevant, however, -this is not really implemented yet. For now, you can force the server -to set '-kb' for all files by setting the `gitcvs.allbinary` config -variable. In proper GIT tradition, the contents of the files are -always respected. No keyword expansion or newline munging is supported. +CRLF Line Ending Conversions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +By default the server leaves the '-k' mode blank for all files, +which causes the cvs client to treat them as a text files, subject +to crlf conversion on some platforms. + +You can make the server use `crlf` attributes to set the '-k' modes +for files by setting the `gitcvs.usecrlfattr` config variable. +In this case, if `crlf` is explicitly unset ('-crlf'), then the +will set '-kb' mode, for binary files. If it `crlf` is set, +then the '-k' mode will explicitly be left blank. See +also linkgit:gitattributes[5] for more information about the `crlf` +attribute. + +Alternatively, if `gitcvs.usecrlfattr` config is not enabled +or if the `crlf` attribute is unspecified for a filename, then +the server uses the `gitcvs.allbinary` for the default setting. +If `gitcvs.allbinary` is set, then the files not otherwise +specified will default to '-kb' mode. Otherwise the '-k' mode +is left blank. Dependencies ------------ diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 674892b816..58206aed7c 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -502,7 +502,7 @@ sub req_add print $state->{CVSROOT} . "/$state->{module}/$filename\n"; # this is an "entries" line - my $kopts = kopts_from_path($filepart); + my $kopts = kopts_from_path($filename); $log->debug("/$filepart/1.$meta->{revision}//$kopts/"); print "/$filepart/1.$meta->{revision}//$kopts/\n"; # permissions @@ -533,9 +533,25 @@ sub req_add print "Checked-in $dirpart\n"; print "$filename\n"; - my $kopts = kopts_from_path($filepart); + my $kopts = kopts_from_path($filename); print "/$filepart/0//$kopts/\n"; + my $requestedKopts = $state->{opt}{k}; + if(defined($requestedKopts)) + { + $requestedKopts = "-k$requestedKopts"; + } + else + { + $requestedKopts = ""; + } + if( $kopts ne $requestedKopts ) + { + $log->warn("Ignoring requested -k='$requestedKopts'" + . " for '$filename'; detected -k='$kopts' instead"); + #TODO: Also have option to send warning to user? + } + $addcount++; } @@ -615,7 +631,7 @@ sub req_remove print "Checked-in $dirpart\n"; print "$filename\n"; - my $kopts = kopts_from_path($filepart); + my $kopts = kopts_from_path($filename); print "/$filepart/-1.$wrev//$kopts/\n"; $rmcount++; @@ -785,6 +801,7 @@ sub req_co argsplit("co"); my $module = $state->{args}[0]; + $state->{module} = $module; my $checkout_path = $module; # use the user specified directory if we're given it @@ -862,6 +879,7 @@ sub req_co # Don't want to check out deleted files next if ( $git->{filehash} eq "deleted" ); + my $fullName = $git->{name}; ( $git->{name}, $git->{dir} ) = filenamesplit($git->{name}); if (length($git->{dir}) && $git->{dir} ne './' @@ -892,7 +910,7 @@ sub req_co print $state->{CVSROOT} . "/$module/" . ( defined ( $git->{dir} ) and $git->{dir} ne "./" ? $git->{dir} . "/" : "" ) . "$git->{name}\n"; # this is an "entries" line - my $kopts = kopts_from_path($git->{name}); + my $kopts = kopts_from_path($fullName); print "/$git->{name}/1.$git->{revision}//$kopts/\n"; # permissions print "u=$git->{mode},g=$git->{mode},o=$git->{mode}\n"; @@ -1101,7 +1119,7 @@ sub req_update print $state->{CVSROOT} . "/$state->{module}/$filename\n"; # this is an "entries" line - my $kopts = kopts_from_path($filepart); + my $kopts = kopts_from_path($filename); $log->debug("/$filepart/1.$meta->{revision}//$kopts/"); print "/$filepart/1.$meta->{revision}//$kopts/\n"; @@ -1149,7 +1167,7 @@ sub req_update print "Merged $dirpart\n"; $log->debug($state->{CVSROOT} . "/$state->{module}/$filename"); print $state->{CVSROOT} . "/$state->{module}/$filename\n"; - my $kopts = kopts_from_path($filepart); + my $kopts = kopts_from_path("$dirpart/$filepart"); $log->debug("/$filepart/1.$meta->{revision}//$kopts/"); print "/$filepart/1.$meta->{revision}//$kopts/\n"; } @@ -1165,7 +1183,7 @@ sub req_update { print "Merged $dirpart\n"; print $state->{CVSROOT} . "/$state->{module}/$filename\n"; - my $kopts = kopts_from_path($filepart); + my $kopts = kopts_from_path("$dirpart/$filepart"); print "/$filepart/1.$meta->{revision}/+/$kopts/\n"; } } @@ -1416,7 +1434,7 @@ sub req_ci } print "Checked-in $dirpart\n"; print "$filename\n"; - my $kopts = kopts_from_path($filepart); + my $kopts = kopts_from_path($filename); print "/$filepart/1.$meta->{revision}//$kopts/\n"; } } @@ -2296,10 +2314,24 @@ sub kopts_from_path { my ($path) = @_; - # Once it exists, the git attributes system should be used to look up - # what attributes apply to this path. + if ( defined ( $cfg->{gitcvs}{usecrlfattr} ) and + $cfg->{gitcvs}{usecrlfattr} =~ /\s*(1|true|yes)\s*$/i ) + { + my ($val) = check_attr( "crlf", $path ); + if ( $val eq "set" ) + { + return ""; + } + elsif ( $val eq "unset" ) + { + return "-kb" + } + else + { + $log->info("Unrecognized check_attr crlf $path : $val"); + } + } - # Until then, take the setting from the config file unless ( defined ( $cfg->{gitcvs}{allbinary} ) and $cfg->{gitcvs}{allbinary} =~ /^\s*(1|true|yes)\s*$/i ) { # Return "" to give no special treatment to any path @@ -2311,6 +2343,23 @@ sub kopts_from_path } } +sub check_attr +{ + my ($attr,$path) = @_; + ensureWorkTree(); + if ( open my $fh, '-|', "git", "check-attr", $attr, "--", $path ) + { + my $val = <$fh>; + close $fh; + $val =~ s/.*: ([^:\r\n]*)\s*$/$1/; + return $val; + } + else + { + return undef; + } +} + # Generate a CVS author name from Git author information, by taking # the first eight characters of the user part of the email address. sub cvs_author diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh new file mode 100755 index 0000000000..b7a779b788 --- /dev/null +++ b/t/t9401-git-cvsserver-crlf.sh @@ -0,0 +1,178 @@ +#!/bin/sh +# +# Copyright (c) 2008 Matthew Ogilvie +# Parts adapted from other tests. +# + +test_description='git-cvsserver -kb modes + +tests -kb mode for binary files when accessing a git +repository using cvs CLI client via git-cvsserver server' + +. ./test-lib.sh + +q_to_nul () { + perl -pe 'y/Q/\000/' +} + +q_to_cr () { + tr Q '\015' +} + +marked_as () { + foundEntry="$(grep "^/$2/" "$1/CVS/Entries")" + if [ x"$foundEntry" = x"" ] ; then + echo "NOT FOUND: $1 $2 1 $3" >> "${WORKDIR}/marked.log" + return 1 + fi + test x"$(grep "^/$2/" "$1/CVS/Entries" | cut -d/ -f5)" = x"$3" + stat=$? + echo "$1 $2 $stat '$3'" >> "${WORKDIR}/marked.log" + return $stat +} + +not_present() { + foundEntry="$(grep "^/$2/" "$1/CVS/Entries")" + if [ -r "$1/$2" ] ; then + echo "Error: File still exists: $1 $2" >> "${WORKDIR}/marked.log" + return 1; + fi + if [ x"$foundEntry" != x"" ] ; then + echo "Error: should not have found: $1 $2" >> "${WORKDIR}/marked.log" + return 1; + else + echo "Correctly not found: $1 $2" >> "${WORKDIR}/marked.log" + return 0; + fi +} + +cvs >/dev/null 2>&1 +if test $? -ne 1 +then + test_expect_success 'skipping git-cvsserver tests, cvs not found' : + test_done + exit +fi +perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { + test_expect_success 'skipping git-cvsserver tests, Perl SQLite interface unavailable' : + test_done + exit +} + +unset GIT_DIR GIT_CONFIG +WORKDIR=$(pwd) +SERVERDIR=$(pwd)/gitcvs.git +git_config="$SERVERDIR/config" +CVSROOT=":fork:$SERVERDIR" +CVSWORK="$(pwd)/cvswork" +CVS_SERVER=git-cvsserver +export CVSROOT CVS_SERVER + +rm -rf "$CVSWORK" "$SERVERDIR" +test_expect_success 'setup' ' + echo "Simple text file" >textfile.c && + echo "File with embedded NUL: Q <- there" | q_to_nul > binfile.bin && + mkdir subdir && + echo "Another text file" > subdir/file.h && + echo "Another binary: Q (this time CR)" | q_to_cr > subdir/withCr.bin && + echo "Mixed up NUL, but marked text: Q <- there" | q_to_nul > mixedUp.c + echo "Unspecified" > subdir/unspecified.other && + echo "/*.bin -crlf" > .gitattributes && + echo "/*.c crlf" >> .gitattributes && + echo "subdir/*.bin -crlf" >> .gitattributes && + echo "subdir/*.c crlf" >> .gitattributes && + echo "subdir/file.h crlf" >> .gitattributes && + git add .gitattributes textfile.c binfile.bin mixedUp.c subdir/* && + git commit -q -m "First Commit" && + git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && + GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true && + GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" +' + +test_expect_success 'cvs co (default crlf)' ' + GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 && + test x"$(grep '/-k' cvswork/CVS/Entries cvswork/subdir/CVS/Entries)" = x"" +' + +rm -rf cvswork +test_expect_success 'cvs co (allbinary)' ' + GIT_DIR="$SERVERDIR" git config --bool gitcvs.allbinary true && + GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 && + marked_as cvswork textfile.c -kb && + marked_as cvswork binfile.bin -kb && + marked_as cvswork .gitattributes -kb && + marked_as cvswork mixedUp.c -kb && + marked_as cvswork/subdir withCr.bin -kb && + marked_as cvswork/subdir file.h -kb && + marked_as cvswork/subdir unspecified.other -kb +' + +rm -rf cvswork cvs.log +test_expect_success 'cvs co (use attributes/allbinary)' ' + GIT_DIR="$SERVERDIR" git config --bool gitcvs.usecrlfattr true && + GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 && + marked_as cvswork textfile.c "" && + marked_as cvswork binfile.bin -kb && + marked_as cvswork .gitattributes -kb && + marked_as cvswork mixedUp.c "" && + marked_as cvswork/subdir withCr.bin -kb && + marked_as cvswork/subdir file.h "" && + marked_as cvswork/subdir unspecified.other -kb +' + +rm -rf cvswork +test_expect_success 'cvs co (use attributes)' ' + GIT_DIR="$SERVERDIR" git config --bool gitcvs.allbinary false && + GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 && + marked_as cvswork textfile.c "" && + marked_as cvswork binfile.bin -kb && + marked_as cvswork .gitattributes "" && + marked_as cvswork mixedUp.c "" && + marked_as cvswork/subdir withCr.bin -kb && + marked_as cvswork/subdir file.h "" && + marked_as cvswork/subdir unspecified.other "" +' + +test_expect_success 'adding files' ' + cd cvswork/subdir && + echo "more text" > src.c && + GIT_CONFIG="$git_config" cvs -Q add src.c >cvs.log 2>&1 && + marked_as . src.c "" && + echo "psuedo-binary" > temp.bin && + cd .. && + GIT_CONFIG="$git_config" cvs -Q add subdir/temp.bin >cvs.log 2>&1 && + marked_as subdir temp.bin "-kb" && + cd subdir && + GIT_CONFIG="$git_config" cvs -Q ci -m "adding files" >cvs.log 2>&1 && + marked_as . temp.bin "-kb" && + marked_as . src.c "" +' + +cd "$WORKDIR" +test_expect_success 'updating' ' + git pull gitcvs.git && + echo 'hi' > subdir/newfile.bin && + echo 'junk' > subdir/file.h && + echo 'hi' > subdir/newfile.c && + echo 'hello' >> binfile.bin && + git add subdir/newfile.bin subdir/file.h subdir/newfile.c binfile.bin && + git commit -q -m "Add and change some files" && + git push gitcvs.git >/dev/null && + cd cvswork && + GIT_CONFIG="$git_config" cvs -Q update && + cd .. && + marked_as cvswork textfile.c "" && + marked_as cvswork binfile.bin -kb && + marked_as cvswork .gitattributes "" && + marked_as cvswork mixedUp.c "" && + marked_as cvswork/subdir withCr.bin -kb && + marked_as cvswork/subdir file.h "" && + marked_as cvswork/subdir unspecified.other "" && + marked_as cvswork/subdir newfile.bin -kb && + marked_as cvswork/subdir newfile.c "" && + echo "File with embedded NUL: Q <- there" | q_to_nul > tmpExpect1 && + echo "hello" >> tmpExpect1 && + cmp cvswork/binfile.bin tmpExpect1 +' + +test_done From 90948a42892779734f77d62f20326c868392fd8f Mon Sep 17 00:00:00 2001 From: Matthew Ogilvie Date: Wed, 14 May 2008 22:35:48 -0600 Subject: [PATCH 056/202] git-cvsserver: add ability to guess -kb from contents If "gitcvs.allbinary" is set to "guess", then any file that has not been explicitly marked as binary or text using the "crlf" attribute and the "gitcvs.usecrlfattr" config will guess binary based on the contents of the file. Signed-off-by: Matthew Ogilvie Signed-off-by: Junio C Hamano --- Documentation/config.txt | 13 ++- Documentation/git-cvsserver.txt | 14 ++- git-cvsserver.perl | 193 +++++++++++++++++++++++++++++--- t/t9401-git-cvsserver-crlf.sh | 159 ++++++++++++++++++++++++++ 4 files changed, 354 insertions(+), 25 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 036e61e2f6..917ef5b4fe 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -670,11 +670,14 @@ gitcvs.usecrlfattr then 'gitcvs.allbinary' is used. See linkgit:gitattribute[5]. gitcvs.allbinary:: - If true, all files not otherwise specified using - 'gitcvs.usecrlfattr' and an explicitly set or unset `crlf` - attribute are sent to the client in mode '-kb'. This - causes the client to treat them as binary files which - suppresses any newline munging it otherwise might do. + This is used if 'gitcvs.usecrlfattr' does not resolve + the correct '-kb' mode to use. If true, all + unresolved files are sent to the client in + mode '-kb'. This causes the client to treat them + as binary files, which suppresses any newline munging it + otherwise might do. Alternatively, if it is set to "guess", + then the contents of the file are examined to decide if + it is binary, similar to 'core.autocrlf'. gitcvs.dbname:: Database used by git-cvsserver to cache revision information diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt index 4888b8604d..cd0685ea67 100644 --- a/Documentation/git-cvsserver.txt +++ b/Documentation/git-cvsserver.txt @@ -311,17 +311,23 @@ to crlf conversion on some platforms. You can make the server use `crlf` attributes to set the '-k' modes for files by setting the `gitcvs.usecrlfattr` config variable. In this case, if `crlf` is explicitly unset ('-crlf'), then the -will set '-kb' mode, for binary files. If it `crlf` is set, +server will set '-kb' mode for binary files. If `crlf` is set, then the '-k' mode will explicitly be left blank. See also linkgit:gitattributes[5] for more information about the `crlf` attribute. Alternatively, if `gitcvs.usecrlfattr` config is not enabled or if the `crlf` attribute is unspecified for a filename, then -the server uses the `gitcvs.allbinary` for the default setting. -If `gitcvs.allbinary` is set, then the files not otherwise +the server uses the `gitcvs.allbinary` config for the default setting. +If `gitcvs.allbinary` is set, then file not otherwise specified will default to '-kb' mode. Otherwise the '-k' mode -is left blank. +is left blank. But if `gitcvs.allbinary` is set to "guess", then +the correct '-k' mode will be guessed based on the contents of +the file. + +For best consistency with cvs, it is probably best to override the +defaults by setting `gitcvs.usecrlfattr` to true, +and `gitcvs.allbinary` to "guess". Dependencies ------------ diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 58206aed7c..920bbe15a3 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -502,7 +502,7 @@ sub req_add print $state->{CVSROOT} . "/$state->{module}/$filename\n"; # this is an "entries" line - my $kopts = kopts_from_path($filename); + my $kopts = kopts_from_path($filename,"sha1",$meta->{filehash}); $log->debug("/$filepart/1.$meta->{revision}//$kopts/"); print "/$filepart/1.$meta->{revision}//$kopts/\n"; # permissions @@ -533,7 +533,8 @@ sub req_add print "Checked-in $dirpart\n"; print "$filename\n"; - my $kopts = kopts_from_path($filename); + my $kopts = kopts_from_path($filename,"file", + $state->{entries}{$filename}{modified_filename}); print "/$filepart/0//$kopts/\n"; my $requestedKopts = $state->{opt}{k}; @@ -631,7 +632,7 @@ sub req_remove print "Checked-in $dirpart\n"; print "$filename\n"; - my $kopts = kopts_from_path($filename); + my $kopts = kopts_from_path($filename,"sha1",$meta->{filehash}); print "/$filepart/-1.$wrev//$kopts/\n"; $rmcount++; @@ -910,7 +911,7 @@ sub req_co print $state->{CVSROOT} . "/$module/" . ( defined ( $git->{dir} ) and $git->{dir} ne "./" ? $git->{dir} . "/" : "" ) . "$git->{name}\n"; # this is an "entries" line - my $kopts = kopts_from_path($fullName); + my $kopts = kopts_from_path($fullName,"sha1",$git->{filehash}); print "/$git->{name}/1.$git->{revision}//$kopts/\n"; # permissions print "u=$git->{mode},g=$git->{mode},o=$git->{mode}\n"; @@ -1119,7 +1120,7 @@ sub req_update print $state->{CVSROOT} . "/$state->{module}/$filename\n"; # this is an "entries" line - my $kopts = kopts_from_path($filename); + my $kopts = kopts_from_path($filename,"sha1",$meta->{filehash}); $log->debug("/$filepart/1.$meta->{revision}//$kopts/"); print "/$filepart/1.$meta->{revision}//$kopts/\n"; @@ -1167,7 +1168,8 @@ sub req_update print "Merged $dirpart\n"; $log->debug($state->{CVSROOT} . "/$state->{module}/$filename"); print $state->{CVSROOT} . "/$state->{module}/$filename\n"; - my $kopts = kopts_from_path("$dirpart/$filepart"); + my $kopts = kopts_from_path("$dirpart/$filepart", + "file",$mergedFile); $log->debug("/$filepart/1.$meta->{revision}//$kopts/"); print "/$filepart/1.$meta->{revision}//$kopts/\n"; } @@ -1183,7 +1185,8 @@ sub req_update { print "Merged $dirpart\n"; print $state->{CVSROOT} . "/$state->{module}/$filename\n"; - my $kopts = kopts_from_path("$dirpart/$filepart"); + my $kopts = kopts_from_path("$dirpart/$filepart", + "file",$mergedFile); print "/$filepart/1.$meta->{revision}/+/$kopts/\n"; } } @@ -1434,7 +1437,7 @@ sub req_ci } print "Checked-in $dirpart\n"; print "$filename\n"; - my $kopts = kopts_from_path($filename); + my $kopts = kopts_from_path($filename,"sha1",$meta->{filehash}); print "/$filepart/1.$meta->{revision}//$kopts/\n"; } } @@ -2312,7 +2315,7 @@ sub cleanupTmpDir # file should get -kb. sub kopts_from_path { - my ($path) = @_; + my ($path, $srcType, $name) = @_; if ( defined ( $cfg->{gitcvs}{usecrlfattr} ) and $cfg->{gitcvs}{usecrlfattr} =~ /\s*(1|true|yes)\s*$/i ) @@ -2332,15 +2335,55 @@ sub kopts_from_path } } - unless ( defined ( $cfg->{gitcvs}{allbinary} ) and $cfg->{gitcvs}{allbinary} =~ /^\s*(1|true|yes)\s*$/i ) + if ( defined ( $cfg->{gitcvs}{allbinary} ) ) { - # Return "" to give no special treatment to any path - return ""; - } else { - # Alternatively, to have all files treated as if they are binary (which - # is more like git itself), always return the "-kb" option - return "-kb"; + if( ($cfg->{gitcvs}{allbinary} =~ /^\s*(1|true|yes)\s*$/i) ) + { + return "-kb"; + } + elsif( ($cfg->{gitcvs}{allbinary} =~ /^\s*guess\s*$/i) ) + { + if( $srcType eq "sha1Or-k" && + !defined($name) ) + { + my ($ret)=$state->{entries}{$path}{options}; + if( !defined($ret) ) + { + $ret=$state->{opt}{k}; + if(defined($ret)) + { + $ret="-k$ret"; + } + else + { + $ret=""; + } + } + if( ! ($ret=~/^(|-kb|-kkv|-kkvl|-kk|-ko|-kv)$/) ) + { + print "E Bad -k option\n"; + $log->warn("Bad -k option: $ret"); + die "Error: Bad -k option: $ret\n"; + } + + return $ret; + } + else + { + if( is_binary($srcType,$name) ) + { + $log->debug("... as binary"); + return "-kb"; + } + else + { + $log->debug("... as text"); + } + } + } } + # Return "" to give no special treatment to any path + return ""; } sub check_attr @@ -2360,6 +2403,124 @@ sub check_attr } } +# This should have the same heuristics as convert.c:is_binary() and related. +# Note that the bare CR test is done by callers in convert.c. +sub is_binary +{ + my ($srcType,$name) = @_; + $log->debug("is_binary($srcType,$name)"); + + # Minimize amount of interpreted code run in the inner per-character + # loop for large files, by totalling each character value and + # then analyzing the totals. + my @counts; + my $i; + for($i=0;$i<256;$i++) + { + $counts[$i]=0; + } + + my $fh = open_blob_or_die($srcType,$name); + my $line; + while( defined($line=<$fh>) ) + { + # Any '\0' and bare CR are considered binary. + if( $line =~ /\0|(\r[^\n])/ ) + { + close($fh); + return 1; + } + + # Count up each character in the line: + my $len=length($line); + for($i=0;$i<$len;$i++) + { + $counts[ord(substr($line,$i,1))]++; + } + } + close $fh; + + # Don't count CR and LF as either printable/nonprintable + $counts[ord("\n")]=0; + $counts[ord("\r")]=0; + + # Categorize individual character count into printable and nonprintable: + my $printable=0; + my $nonprintable=0; + for($i=0;$i<256;$i++) + { + if( $i < 32 && + $i != ord("\b") && + $i != ord("\t") && + $i != 033 && # ESC + $i != 014 ) # FF + { + $nonprintable+=$counts[$i]; + } + elsif( $i==127 ) # DEL + { + $nonprintable+=$counts[$i]; + } + else + { + $printable+=$counts[$i]; + } + } + + return ($printable >> 7) < $nonprintable; +} + +# Returns open file handle. Possible invocations: +# - open_blob_or_die("file",$filename); +# - open_blob_or_die("sha1",$filehash); +sub open_blob_or_die +{ + my ($srcType,$name) = @_; + my ($fh); + if( $srcType eq "file" ) + { + if( !open $fh,"<",$name ) + { + $log->warn("Unable to open file $name: $!"); + die "Unable to open file $name: $!\n"; + } + } + elsif( $srcType eq "sha1" || $srcType eq "sha1Or-k" ) + { + unless ( defined ( $name ) and $name =~ /^[a-zA-Z0-9]{40}$/ ) + { + $log->warn("Need filehash"); + die "Need filehash\n"; + } + + my $type = `git cat-file -t $name`; + chomp $type; + + unless ( defined ( $type ) and $type eq "blob" ) + { + $log->warn("Invalid type '$type' for '$name'"); + die ( "Invalid type '$type' (expected 'blob')" ) + } + + my $size = `git cat-file -s $name`; + chomp $size; + + $log->debug("open_blob_or_die($name) size=$size, type=$type"); + + unless( open $fh, '-|', "git", "cat-file", "blob", $name ) + { + $log->warn("Unable to open sha1 $name"); + die "Unable to open sha1 $name\n"; + } + } + else + { + $log->warn("Unknown type of blob source: $srcType"); + die "Unknown type of blob source: $srcType\n"; + } + return $fh; +} + # Generate a CVS author name from Git author information, by taking # the first eight characters of the user part of the email address. sub cvs_author diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh index b7a779b788..e27a1c5f85 100755 --- a/t/t9401-git-cvsserver-crlf.sh +++ b/t/t9401-git-cvsserver-crlf.sh @@ -175,4 +175,163 @@ test_expect_success 'updating' ' cmp cvswork/binfile.bin tmpExpect1 ' +rm -rf cvswork +test_expect_success 'cvs co (use attributes/guess)' ' + GIT_DIR="$SERVERDIR" git config gitcvs.allbinary guess && + GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 && + marked_as cvswork textfile.c "" && + marked_as cvswork binfile.bin -kb && + marked_as cvswork .gitattributes "" && + marked_as cvswork mixedUp.c "" && + marked_as cvswork/subdir withCr.bin -kb && + marked_as cvswork/subdir file.h "" && + marked_as cvswork/subdir unspecified.other "" && + marked_as cvswork/subdir newfile.bin -kb && + marked_as cvswork/subdir newfile.c "" +' + +test_expect_success 'setup multi-line files' ' + ( echo "line 1" && + echo "line 2" && + echo "line 3" && + echo "line 4 with NUL: Q <-" ) | q_to_nul > multiline.c && + git add multiline.c && + ( echo "line 1" && + echo "line 2" && + echo "line 3" && + echo "line 4" ) | q_to_nul > multilineTxt.c && + git add multilineTxt.c && + git commit -q -m "multiline files" && + git push gitcvs.git >/dev/null +' + +rm -rf cvswork +test_expect_success 'cvs co (guess)' ' + GIT_DIR="$SERVERDIR" git config --bool gitcvs.usecrlfattr false && + GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 && + marked_as cvswork textfile.c "" && + marked_as cvswork binfile.bin -kb && + marked_as cvswork .gitattributes "" && + marked_as cvswork mixedUp.c -kb && + marked_as cvswork multiline.c -kb && + marked_as cvswork multilineTxt.c "" && + marked_as cvswork/subdir withCr.bin -kb && + marked_as cvswork/subdir file.h "" && + marked_as cvswork/subdir unspecified.other "" && + marked_as cvswork/subdir newfile.bin "" && + marked_as cvswork/subdir newfile.c "" +' + +test_expect_success 'cvs co another copy (guess)' ' + GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 && + marked_as cvswork2 textfile.c "" && + marked_as cvswork2 binfile.bin -kb && + marked_as cvswork2 .gitattributes "" && + marked_as cvswork2 mixedUp.c -kb && + marked_as cvswork2 multiline.c -kb && + marked_as cvswork2 multilineTxt.c "" && + marked_as cvswork2/subdir withCr.bin -kb && + marked_as cvswork2/subdir file.h "" && + marked_as cvswork2/subdir unspecified.other "" && + marked_as cvswork2/subdir newfile.bin "" && + marked_as cvswork2/subdir newfile.c "" +' + +test_expect_success 'add text (guess)' ' + cd cvswork && + echo "simpleText" > simpleText.c && + GIT_CONFIG="$git_config" cvs -Q add simpleText.c && + cd .. && + marked_as cvswork simpleText.c "" +' + +test_expect_success 'add bin (guess)' ' + cd cvswork && + echo "simpleBin: NUL: Q <- there" | q_to_nul > simpleBin.bin && + GIT_CONFIG="$git_config" cvs -Q add simpleBin.bin && + cd .. && + marked_as cvswork simpleBin.bin -kb +' + +test_expect_success 'remove files (guess)' ' + cd cvswork && + GIT_CONFIG="$git_config" cvs -Q rm -f subdir/file.h && + cd subdir && + GIT_CONFIG="$git_config" cvs -Q rm -f withCr.bin && + cd ../.. && + marked_as cvswork/subdir withCr.bin -kb && + marked_as cvswork/subdir file.h "" +' + +test_expect_success 'cvs ci (guess)' ' + cd cvswork && + GIT_CONFIG="$git_config" cvs -Q ci -m "add/rm files" >cvs.log 2>&1 && + cd .. && + marked_as cvswork textfile.c "" && + marked_as cvswork binfile.bin -kb && + marked_as cvswork .gitattributes "" && + marked_as cvswork mixedUp.c -kb && + marked_as cvswork multiline.c -kb && + marked_as cvswork multilineTxt.c "" && + not_present cvswork/subdir withCr.bin && + not_present cvswork/subdir file.h && + marked_as cvswork/subdir unspecified.other "" && + marked_as cvswork/subdir newfile.bin "" && + marked_as cvswork/subdir newfile.c "" && + marked_as cvswork simpleBin.bin -kb && + marked_as cvswork simpleText.c "" +' + +test_expect_success 'update subdir of other copy (guess)' ' + cd cvswork2/subdir && + GIT_CONFIG="$git_config" cvs -Q update && + cd ../.. && + marked_as cvswork2 textfile.c "" && + marked_as cvswork2 binfile.bin -kb && + marked_as cvswork2 .gitattributes "" && + marked_as cvswork2 mixedUp.c -kb && + marked_as cvswork2 multiline.c -kb && + marked_as cvswork2 multilineTxt.c "" && + not_present cvswork2/subdir withCr.bin && + not_present cvswork2/subdir file.h && + marked_as cvswork2/subdir unspecified.other "" && + marked_as cvswork2/subdir newfile.bin "" && + marked_as cvswork2/subdir newfile.c "" && + not_present cvswork2 simpleBin.bin && + not_present cvswork2 simpleText.c +' + +echo "starting update/merge" >> "${WORKDIR}/marked.log" +test_expect_success 'update/merge full other copy (guess)' ' + git pull gitcvs.git master && + sed "s/3/replaced_3/" < multilineTxt.c > ml.temp && + mv ml.temp multilineTxt.c && + git add multilineTxt.c && + git commit -q -m "modify multiline file" >> "${WORKDIR}/marked.log" && + git push gitcvs.git >/dev/null && + cd cvswork2 && + sed "s/1/replaced_1/" < multilineTxt.c > ml.temp && + mv ml.temp multilineTxt.c && + GIT_CONFIG="$git_config" cvs update > cvs.log 2>&1 && + cd .. && + marked_as cvswork2 textfile.c "" && + marked_as cvswork2 binfile.bin -kb && + marked_as cvswork2 .gitattributes "" && + marked_as cvswork2 mixedUp.c -kb && + marked_as cvswork2 multiline.c -kb && + marked_as cvswork2 multilineTxt.c "" && + not_present cvswork2/subdir withCr.bin && + not_present cvswork2/subdir file.h && + marked_as cvswork2/subdir unspecified.other "" && + marked_as cvswork2/subdir newfile.bin "" && + marked_as cvswork2/subdir newfile.c "" && + marked_as cvswork2 simpleBin.bin -kb && + marked_as cvswork2 simpleText.c "" && + echo "line replaced_1" > tmpExpect2 && + echo "line 2" >> tmpExpect2 && + echo "line replaced_3" >> tmpExpect2 && + echo "line 4" | q_to_nul >> tmpExpect2 && + cmp cvswork2/multilineTxt.c tmpExpect2 +' + test_done From bbefaa1f38a47e06c32612edc2f64a0168cc40b2 Mon Sep 17 00:00:00 2001 From: Chris Frey Date: Thu, 15 May 2008 22:37:31 -0400 Subject: [PATCH 057/202] Documentation/git-repack.txt: document new -A behaviour Add paragraph for the -A option, and describe the new behaviour that makes unreachable objects loose. Signed-off-by: Chris Frey Signed-off-by: Junio C Hamano --- Documentation/git-repack.txt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index 3d957492f8..906d3c7054 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -8,7 +8,7 @@ git-repack - Pack unpacked objects in a repository SYNOPSIS -------- -'git-repack' [-a] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N] +'git-repack' [-a] [-A] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N] DESCRIPTION ----------- @@ -37,6 +37,18 @@ OPTIONS leaves behind, but `git fsck --full` shows as dangling. +-A:: + Same as `-a`, but any unreachable objects in a previous + pack become loose, unpacked objects, instead of being + left in the old pack. Unreachable objects are never + intentionally added to a pack, even when repacking. + When used with '-d', this option + prevents unreachable objects from being immediately + deleted by way of being left in the old pack and then + removed. Instead, the loose unreachable objects + will be pruned according to normal expiry rules + with the next linkgit:git-gc[1]. + -d:: After packing, if the newly created packs make some existing packs redundant, remove the redundant packs. From 88f6dbaf99f43053f86474b28beedd91e77c64d9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 17 May 2008 01:46:47 -0700 Subject: [PATCH 058/202] builtin-apply: typofix 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 1103625a4a..776e5963b7 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -418,7 +418,7 @@ static int guess_p_value(const char *nameline) } /* - * Get the name etc info from the --/+++ lines of a traditional patch header + * Get the name etc info from the ---/+++ lines of a traditional patch header * * FIXME! The end-of-filename heuristics are kind of screwy. For existing * files, we can happily check the index for a match, but for creating a From 5c47f4c6e71e6de08348f837f38a446a2f2b0ed7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 17 May 2008 01:51:31 -0700 Subject: [PATCH 059/202] builtin-apply: accept patch to an empty file A patch from a foreign SCM (or plain "diff" output) often have both preimage and postimage filename on ---/+++ lines even for a patch that creates a new file. However, when there is a filename for preimage, we used to insist the file to exist (either in the work tree and/or in the index). When we cannot be sure by parsing the patch that it is not a creation patch, we shouldn't complain when if there is no such a file. This commit fixes the logic. Refactor the code that validates the preimage file into a separate function while we are at it, as it is getting rather big. Signed-off-by: Junio C Hamano --- builtin-apply.c | 135 ++++++++++++++++++++++++++++-------------------- 1 file changed, 78 insertions(+), 57 deletions(-) diff --git a/builtin-apply.c b/builtin-apply.c index 776e5963b7..10b1f88a3c 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2267,6 +2267,79 @@ static int verify_index_match(struct cache_entry *ce, struct stat *st) return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID); } +static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st) +{ + const char *old_name = patch->old_name; + int stat_ret = 0; + unsigned st_mode = 0; + + /* + * Make sure that we do not have local modifications from the + * index when we are looking at the index. Also make sure + * we have the preimage file to be patched in the work tree, + * unless --cached, which tells git to apply only in the index. + */ + if (!old_name) + return 0; + + assert(patch->is_new <= 0); + if (!cached) { + stat_ret = lstat(old_name, st); + if (stat_ret && errno != ENOENT) + return error("%s: %s", old_name, strerror(errno)); + } + if (check_index) { + int pos = cache_name_pos(old_name, strlen(old_name)); + if (pos < 0) { + if (patch->is_new < 0) + goto is_new; + return error("%s: does not exist in index", old_name); + } + *ce = active_cache[pos]; + if (stat_ret < 0) { + struct checkout costate; + /* checkout */ + costate.base_dir = ""; + costate.base_dir_len = 0; + costate.force = 0; + costate.quiet = 0; + costate.not_new = 0; + costate.refresh_cache = 1; + if (checkout_entry(*ce, &costate, NULL) || + lstat(old_name, st)) + return -1; + } + if (!cached && verify_index_match(*ce, st)) + return error("%s: does not match index", old_name); + if (cached) + st_mode = (*ce)->ce_mode; + } else if (stat_ret < 0) { + if (patch->is_new < 0) + goto is_new; + return error("%s: %s", old_name, strerror(errno)); + } + + if (!cached) + st_mode = ce_mode_from_stat(*ce, st->st_mode); + + if (patch->is_new < 0) + patch->is_new = 0; + if (!patch->old_mode) + patch->old_mode = st_mode; + if ((st_mode ^ patch->old_mode) & S_IFMT) + return error("%s: wrong type", old_name); + if (st_mode != patch->old_mode) + fprintf(stderr, "warning: %s has type %o, expected %o\n", + old_name, st_mode, patch->old_mode); + return 0; + + is_new: + patch->is_new = 1; + patch->is_delete = 0; + patch->old_name = NULL; + return 0; +} + static int check_patch(struct patch *patch, struct patch *prev_patch) { struct stat st; @@ -2275,66 +2348,14 @@ static int check_patch(struct patch *patch, struct patch *prev_patch) const char *name = old_name ? old_name : new_name; struct cache_entry *ce = NULL; int ok_if_exists; + int status; patch->rejected = 1; /* we will drop this after we succeed */ - /* - * Make sure that we do not have local modifications from the - * index when we are looking at the index. Also make sure - * we have the preimage file to be patched in the work tree, - * unless --cached, which tells git to apply only in the index. - */ - if (old_name) { - int stat_ret = 0; - unsigned st_mode = 0; - - if (!cached) - stat_ret = lstat(old_name, &st); - if (check_index) { - int pos = cache_name_pos(old_name, strlen(old_name)); - if (pos < 0) - return error("%s: does not exist in index", - old_name); - ce = active_cache[pos]; - if (stat_ret < 0) { - struct checkout costate; - if (errno != ENOENT) - return error("%s: %s", old_name, - strerror(errno)); - /* checkout */ - costate.base_dir = ""; - costate.base_dir_len = 0; - costate.force = 0; - costate.quiet = 0; - costate.not_new = 0; - costate.refresh_cache = 1; - if (checkout_entry(ce, - &costate, - NULL) || - lstat(old_name, &st)) - return -1; - } - if (!cached && verify_index_match(ce, &st)) - return error("%s: does not match index", - old_name); - if (cached) - st_mode = ce->ce_mode; - } else if (stat_ret < 0) - return error("%s: %s", old_name, strerror(errno)); - - if (!cached) - st_mode = ce_mode_from_stat(ce, st.st_mode); - - if (patch->is_new < 0) - patch->is_new = 0; - if (!patch->old_mode) - patch->old_mode = st_mode; - if ((st_mode ^ patch->old_mode) & S_IFMT) - return error("%s: wrong type", old_name); - if (st_mode != patch->old_mode) - fprintf(stderr, "warning: %s has type %o, expected %o\n", - old_name, st_mode, patch->old_mode); - } + status = check_preimage(patch, &ce, &st); + if (status) + return status; + old_name = patch->old_name; if (new_name && prev_patch && 0 < prev_patch->is_delete && !strcmp(prev_patch->old_name, new_name)) From 0047dd2fd1fc1980913901c5fa098357482c2842 Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Thu, 15 May 2008 07:19:54 +0200 Subject: [PATCH 060/202] t0050: Fix merge test on case sensitive file systems On a case sensitive filesystem, "git reset --hard" might refuse to overwrite a file whose name differs only by case, even if core.ignorecase is set. It is not clear which circumstances cause this behavior. This commit simply works around the problem by removing the case changing file before running "git reset --hard". Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano --- t/t0050-filesystem.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh index 0e33c4bdc7..c5360e23d9 100755 --- a/t/t0050-filesystem.sh +++ b/t/t0050-filesystem.sh @@ -72,6 +72,8 @@ $test_case 'rename (case change)' ' $test_case 'merge (case change)' ' + rm -f CamelCase && + rm -f camelcase && git reset --hard initial && git merge topic From 032bea55a3fda805382398020657f738b8176729 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 17 May 2008 02:02:44 -0700 Subject: [PATCH 061/202] builtin-apply: do not declare patch is creation when we do not know it When we see no context nor deleted line in the patch, we used to declare that the patch creates a new file. But some people create an empty file and then apply a patch to it. Similarly, a patch that delete everything is not a deletion patch either. This commit corrects these two issues. Together with the previous commit, it allows a diff between an empty file and a line-ful file to be treated as both creation patch and "add stuff to an existing empty file", depending on the context. A new test t4126 demonstrates the fix. Signed-off-by: Junio C Hamano --- builtin-apply.c | 15 ----------- t/t4126-apply-empty.sh | 61 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 15 deletions(-) create mode 100755 t/t4126-apply-empty.sh diff --git a/builtin-apply.c b/builtin-apply.c index 10b1f88a3c..1540f28ab4 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -1143,21 +1143,6 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc if (patch->is_delete < 0 && (newlines || (patch->fragments && patch->fragments->next))) patch->is_delete = 0; - if (!unidiff_zero || context) { - /* If the user says the patch is not generated with - * --unified=0, or if we have seen context lines, - * then not having oldlines means the patch is creation, - * and not having newlines means the patch is deletion. - */ - if (patch->is_new < 0 && !oldlines) { - patch->is_new = 1; - patch->old_name = NULL; - } - if (patch->is_delete < 0 && !newlines) { - patch->is_delete = 1; - patch->new_name = NULL; - } - } if (0 < patch->is_new && oldlines) die("new file %s depends on old contents", patch->new_name); diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh new file mode 100755 index 0000000000..0cfd47cfcf --- /dev/null +++ b/t/t4126-apply-empty.sh @@ -0,0 +1,61 @@ +#!/bin/sh + +test_description='apply empty' + +. ./test-lib.sh + +test_expect_success setup ' + >empty && + git add empty && + test_tick && + git commit -m initial && + for i in a b c d e + do + echo $i + done >empty && + cat empty >expect && + git diff | + sed -e "/^diff --git/d" \ + -e "/^index /d" \ + -e "s|a/empty|empty.orig|" \ + -e "s|b/empty|empty|" >patch0 && + sed -e "s|empty|missing|" patch0 >patch1 && + >empty && + git update-index --refresh +' + +test_expect_success 'apply empty' ' + git reset --hard && + >empty && + rm -f missing && + git apply patch0 && + test_cmp expect empty +' + +test_expect_success 'apply --index empty' ' + git reset --hard && + >empty && + rm -f missing && + git apply --index patch0 && + test_cmp expect empty && + git diff --exit-code +' + +test_expect_success 'apply create' ' + git reset --hard && + >empty && + rm -f missing && + git apply patch1 && + test_cmp expect missing +' + +test_expect_success 'apply --index create' ' + git reset --hard && + >empty && + rm -f missing && + git apply --index patch1 && + test_cmp expect missing && + git diff --exit-code +' + +test_done From 94503a66c56c935e77a8fbe3622f1f56b7134ccc Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 19 May 2008 09:48:45 +1000 Subject: [PATCH 062/202] gitk: Fix "wrong # coordinates" error on reload This fixes the Tk error "wrong # coordinates: expected 0 or 4, got 2" that sometimes occurred when reloading. The problem was that we didn't unset the variables containing the canvas item id numbers for the displayed rows when we cleared the canvases. Thus make_secsel would think it had something to do when it didn't. Thanks to Michele Ballabio for finding a way to trigger the bug reliably. Signed-off-by: Paul Mackerras --- gitk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gitk b/gitk index 9ab6dbaa46..998a051942 100755 --- a/gitk +++ b/gitk @@ -5125,10 +5125,14 @@ proc drawvisible {} { proc clear_display {} { global iddrawn linesegs need_redisplay nrows_drawn global vhighlights fhighlights nhighlights rhighlights + global linehtag linentag linedtag allcanvs delete all catch {unset iddrawn} catch {unset linesegs} + catch {unset linehtag} + catch {unset linentag} + catch {unset linedtag} catch {unset vhighlights} catch {unset fhighlights} catch {unset nhighlights} From 64dc208c117d6a2e97176342969637e5daba6b8d Mon Sep 17 00:00:00 2001 From: Michele Ballabio Date: Sat, 17 May 2008 19:16:31 +0200 Subject: [PATCH 063/202] gitk: Move es.po where it belongs Signed-off-by: Michele Ballabio Signed-off-by: Paul Mackerras --- {gitk-git/po => po}/es.po | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {gitk-git/po => po}/es.po (100%) diff --git a/gitk-git/po/es.po b/po/es.po similarity index 100% rename from gitk-git/po/es.po rename to po/es.po From 689ef4d42e1adf409b632ed5c36beda4852a4720 Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Sat, 17 May 2008 23:00:01 -0500 Subject: [PATCH 064/202] builtin-clone.c: Need to closedir() in copy_or_link_directory() So not to leak file descriptors, close the directory after opening it. Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- builtin-clone.c | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin-clone.c b/builtin-clone.c index 8713128e72..8936a51810 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -215,6 +215,7 @@ static void copy_or_link_directory(char *src, char *dest) die("failed to create link %s\n", dest); } } + closedir(dir); } static const struct ref *clone_local(const char *src_repo, From 2a028a0cabefed8efcf41499e330adfc20f433f8 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Sun, 18 May 2008 17:27:00 +0200 Subject: [PATCH 065/202] Ensure that a test is run in the trash directory Exit with error if cd into the "trash directory" failed (error already reported, so just exit). Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- t/test-lib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index 5002fb04b5..d1018c99c6 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -419,7 +419,7 @@ rm -fr "$test" || { } test_create_repo "$test" -cd "$test" +cd "$test" || exit 1 this_test=$(expr "./$0" : '.*/\(t[0-9]*\)-[^/]*$') for skp in $GIT_SKIP_TESTS From 9231e3a953c0780846dacdcf3928f6658fa4fad0 Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Sat, 17 May 2008 23:03:03 -0500 Subject: [PATCH 066/202] t/Makefile: "trash" directory was renamed recently Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- t/Makefile | 2 +- t/README | 2 +- t/test-lib.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/t/Makefile b/t/Makefile index 72d7884232..c6a60ab165 100644 --- a/t/Makefile +++ b/t/Makefile @@ -20,7 +20,7 @@ $(T): @echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS) clean: - $(RM) -r trash + $(RM) -r 'trash directory' # we can test NO_OPTIMIZE_COMMITS independently of LC_ALL full-svn-test: diff --git a/t/README b/t/README index 73ed11bfe2..70841a4645 100644 --- a/t/README +++ b/t/README @@ -123,7 +123,7 @@ This test harness library does the following things: (or -h), it shows the test_description and exits. - Creates an empty test directory with an empty .git/objects - database and chdir(2) into it. This directory is 't/trash' + database and chdir(2) into it. This directory is 't/trash directory' if you must know, but I do not think you care. - Defines standard test helper functions for your scripts to diff --git a/t/test-lib.sh b/t/test-lib.sh index d1018c99c6..3bf570b068 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -368,7 +368,7 @@ test_done () { case "$test_failure" in 0) # We could: - # cd .. && rm -fr trash + # cd .. && rm -fr 'trash directory' # but that means we forbid any tests that use their own # subdirectory from calling test_done without coming back # to where they started from. From 8ccba008ee3e0b0582e64cb23ae4ebf734b9f75b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 17 May 2008 12:03:49 -0700 Subject: [PATCH 067/202] unpack-trees: allow Porcelain to give different error messages The plumbing output is sacred as it is an API. We _could_ change it if it is broken in such a way that it cannot convey necessary information fully, but we just do not _reword_ for the sake of rewording. If somebody does not like it, s/he is complaining too late. S/he should have been here in early May 2005 and make the language used by the API closer to what humans read. S/he wasn't here. Too bad, and it is too late. And people who complain should look at a bigger picture. Look at what was suggested by one of them and think for five seconds: $ git checkout mytopic -fatal: Entry 'frotz' not uptodate. Cannot merge. +fatal: Entry 'frotz' has local changes. Cannot merge. If you do not see something wrong with this output, your brain has already been rotten with use of git for too long a time. Nobody asked us to "merge" but why are we talking about "Cannot merge"? This patch introduces a mechanism to allow Porcelains to specify messages that are different from the ones that is given by the underlying plumbing implementation of read-tree, so that we can reword the message Porcelains give without disrupting the output from the plumbing. $ git-checkout pu error: You have local changes to 'Makefile'; cannot switch branches. There are other places that ask unpack_trees() to n-way merge, detect issues and let it issue error message on its own, but I did this as a demonstration and replaced only one message. Yes I know about C99 structure initializers. I'd love to use them but we try to be nice to compilers without it. Signed-off-by: Junio C Hamano --- builtin-checkout.c | 2 ++ unpack-trees.c | 55 ++++++++++++++++++++++++++++++++++------------ unpack-trees.h | 9 ++++++++ 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/builtin-checkout.c b/builtin-checkout.c index 10ec137cce..83da7ca9ff 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -236,6 +236,8 @@ static int merge_working_tree(struct checkout_opts *opts, topts.src_index = &the_index; topts.dst_index = &the_index; + topts.msgs.not_uptodate_file = "You have local changes to '%s'; cannot switch branches."; + refresh_cache(REFRESH_QUIET); if (unmerged_cache()) { diff --git a/unpack-trees.c b/unpack-trees.c index 1ab28fda45..0de5a31c0b 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -8,6 +8,36 @@ #include "progress.h" #include "refs.h" +/* + * Error messages expected by scripts out of plumbing commands such as + * read-tree. Non-scripted Porcelain is not required to use these messages + * and in fact are encouraged to reword them to better suit their particular + * situation better. See how "git checkout" replaces not_uptodate_file to + * explain why it does not allow switching between branches when you have + * local changes, for example. + */ +static struct unpack_trees_error_msgs unpack_plumbing_errors = { + /* would_overwrite */ + "Entry '%s' would be overwritten by merge. Cannot merge.", + + /* not_uptodate_file */ + "Entry '%s' not uptodate. Cannot merge.", + + /* not_uptodate_dir */ + "Updating '%s' would lose untracked files in it", + + /* would_lose_untracked */ + "Untracked working tree file '%s' would be %s by merge.", + + /* bind_overlap */ + "Entry '%s' overlaps with '%s'. Cannot bind.", +}; + +#define ERRORMSG(o,fld) \ + ( ((o) && (o)->msgs.fld) \ + ? ((o)->msgs.fld) \ + : (unpack_plumbing_errors.fld) ) + static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce, unsigned int set, unsigned int clear) { @@ -383,10 +413,9 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options /* Here come the merge functions */ -static int reject_merge(struct cache_entry *ce) +static int reject_merge(struct cache_entry *ce, struct unpack_trees_options *o) { - return error("Entry '%s' would be overwritten by merge. Cannot merge.", - ce->name); + return error(ERRORMSG(o, would_overwrite), ce->name); } static int same(struct cache_entry *a, struct cache_entry *b) @@ -430,7 +459,7 @@ static int verify_uptodate(struct cache_entry *ce, if (errno == ENOENT) return 0; return o->gently ? -1 : - error("Entry '%s' not uptodate. Cannot merge.", ce->name); + error(ERRORMSG(o, not_uptodate_file), ce->name); } static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o) @@ -517,8 +546,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action, i = read_directory(&d, ce->name, pathbuf, namelen+1, NULL); if (i) return o->gently ? -1 : - error("Updating '%s' would lose untracked files in it", - ce->name); + error(ERRORMSG(o, not_uptodate_dir), ce->name); free(pathbuf); return cnt; } @@ -618,8 +646,7 @@ static int verify_absent(struct cache_entry *ce, const char *action, } return o->gently ? -1 : - error("Untracked working tree file '%s' " - "would be %s by merge.", ce->name, action); + error(ERRORMSG(o, would_lose_untracked), ce->name, action); } return 0; } @@ -751,7 +778,7 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o) /* #14, #14ALT, #2ALT */ if (remote && !df_conflict_head && head_match && !remote_match) { if (index && !same(index, remote) && !same(index, head)) - return o->gently ? -1 : reject_merge(index); + return o->gently ? -1 : reject_merge(index, o); return merged_entry(remote, index, o); } /* @@ -759,7 +786,7 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o) * make sure that it matches head. */ if (index && !same(index, head)) - return o->gently ? -1 : reject_merge(index); + return o->gently ? -1 : reject_merge(index, o); if (head) { /* #5ALT, #15 */ @@ -901,11 +928,11 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o) else { /* all other failures */ if (oldtree) - return o->gently ? -1 : reject_merge(oldtree); + return o->gently ? -1 : reject_merge(oldtree, o); if (current) - return o->gently ? -1 : reject_merge(current); + return o->gently ? -1 : reject_merge(current, o); if (newtree) - return o->gently ? -1 : reject_merge(newtree); + return o->gently ? -1 : reject_merge(newtree, o); return -1; } } @@ -931,7 +958,7 @@ int bind_merge(struct cache_entry **src, o->merge_size); if (a && old) return o->gently ? -1 : - error("Entry '%s' overlaps with '%s'. Cannot bind.", a->name, old->name); + error(ERRORMSG(o, bind_overlap), a->name, old->name); if (!a) return keep_entry(old, o); else diff --git a/unpack-trees.h b/unpack-trees.h index d436d6ced9..94e567265a 100644 --- a/unpack-trees.h +++ b/unpack-trees.h @@ -8,6 +8,14 @@ struct unpack_trees_options; typedef int (*merge_fn_t)(struct cache_entry **src, struct unpack_trees_options *options); +struct unpack_trees_error_msgs { + const char *would_overwrite; + const char *not_uptodate_file; + const char *not_uptodate_dir; + const char *would_lose_untracked; + const char *bind_overlap; +}; + struct unpack_trees_options { unsigned int reset:1, merge:1, @@ -23,6 +31,7 @@ struct unpack_trees_options { int pos; struct dir_struct *dir; merge_fn_t fn; + struct unpack_trees_error_msgs msgs; int head_idx; int merge_size; From 69239728238c8ec6296d94c4cd7490161f4c89b2 Mon Sep 17 00:00:00 2001 From: Gustaf Hendeby Date: Tue, 20 May 2008 00:47:28 +0200 Subject: [PATCH 068/202] Documentation: Add missing git svn commands Signed-off-by: Gustaf Hendeby Acked-by: Eric Wong Signed-off-by: Junio C Hamano --- Documentation/git-svn.txt | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index c6b56b4ef3..3eae1ebb7d 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -196,10 +196,10 @@ Any other arguments are passed directly to `git log' independently of git-svn functions. 'create-ignore':: - Recursively finds the svn:ignore property on directories and creates matching .gitignore files. The resulting files are staged to - be committed, but are not committed. + be committed, but are not committed. Use -r/--revision to refer to a + specfic revision. 'show-ignore':: Recursively finds and lists the svn:ignore property on @@ -223,6 +223,19 @@ Any other arguments are passed directly to `git log' argument. Use the --url option to output only the value of the 'URL:' field. +'proplist':: + Lists the properties stored in the Subversion repository about a + given file or directory. Use -r/--revision to refer to a specific + Subversion revision. + +'propget':: + Gets the Subversion property given as the first argument, for a + file. A specific revision can be specified with -r/--revision. + +'show-externals':: + Shows the Subversion externals. Use -r/--revision to specify a + specific revision. + -- OPTIONS From 74190d2363a1be81b06992bebbff6bd59f943fe7 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 19 May 2008 16:08:33 -0400 Subject: [PATCH 069/202] doc/git-daemon: s/uploadarchive/uploadarch/ The git-daemon upload-archive feature has always used the config directive 'daemon.uploadarch'; the documentation which came later seems to have just mistakenly used the wrong name. Noticed by lionel@over-blog.com. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/git-daemon.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt index fd83bc7833..cf261dd40d 100644 --- a/Documentation/git-daemon.txt +++ b/Documentation/git-daemon.txt @@ -174,7 +174,7 @@ upload-pack:: upload-archive:: This serves `git-archive --remote`. It is disabled by default, but a repository can enable it by setting - `daemon.uploadarchive` configuration item to `true`. + `daemon.uploadarch` configuration item to `true`. receive-pack:: This serves `git-send-pack` clients, allowing anonymous @@ -257,7 +257,7 @@ selectively enable/disable services per repository:: ---------------------------------------------------------------- [daemon] uploadpack = false - uploadarchive = true + uploadarch = true ---------------------------------------------------------------- From e77b0b5d0fdac411607dbae11ccad87dccd332d3 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 19 May 2008 16:09:06 -0400 Subject: [PATCH 070/202] git-am: fix typo in usage message Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- git-am.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-am.sh b/git-am.sh index 851f85eb55..4dce87bdff 100755 --- a/git-am.sh +++ b/git-am.sh @@ -10,7 +10,7 @@ git-am [options] --skip -- d,dotest= use and not .dotest i,interactive run interactively -b,binary pass --allo-binary-replacement to git-apply +b,binary pass --allow-binary-replacement to git-apply 3,3way allow fall back on 3way merging if needed s,signoff add a Signed-off-by line to the commit message u,utf8 recode into utf8 (default) From 94b4a69f758131a6282363e1717235a03c3885ef Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 20 May 2008 20:51:06 +1000 Subject: [PATCH 071/202] gitk: Fix bug where current row number display stops working The display of the current row number would stop working if the user clicked on a line, or if selectedline got unset for any other reason, because the trace on it got lost when it was unselected. This fixes it by changing the places that unset selectedline to set it to the empty string instead, and the places that tested for it being set or unset to compare it with the empty string. Thus it never gets unset now. This actually simplified the code in a few places since it can be compared for equality with a row number now without first testing if it is set. Signed-off-by: Paul Mackerras --- gitk | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/gitk b/gitk index 998a051942..45ebcaf6dd 100755 --- a/gitk +++ b/gitk @@ -495,7 +495,7 @@ proc reloadcommits {} { stop_rev_list $curview } resetvarcs $curview - catch {unset selectedline} + set selectedline {} catch {unset currentid} catch {unset thickerline} catch {unset treediffs} @@ -927,7 +927,7 @@ proc removefakerow {id} { modify_arc $v $a $i if {[info exist currentid] && $id eq $currentid} { unset currentid - unset selectedline + set selectedline {} } if {[info exists targetid] && $targetid eq $id} { set targetid $p @@ -1838,7 +1838,7 @@ proc makewindow {} { pack .tf.bar.rowlabel .tf.bar.rownum .tf.bar.rowlabel2 .tf.bar.numcommits \ -side left global selectedline - trace add variable selectedline {write unset} selectedline_change + trace add variable selectedline write selectedline_change # Status label and progress bar set statusw .tf.bar.status @@ -2185,7 +2185,7 @@ proc windows_mousewheel_redirector {W X Y D} { proc selectedline_change {n1 n2 op} { global selectedline rownumsel - if {$op eq "unset"} { + if {$selectedline eq {}} { set rownumsel {} } else { set rownumsel [expr {$selectedline + 1}] @@ -3274,7 +3274,7 @@ proc showview {n} { set ytop [expr {[lindex $span 0] * $ymax}] set ybot [expr {[lindex $span 1] * $ymax}] set yscreen [expr {($ybot - $ytop) / 2}] - if {[info exists selectedline]} { + if {$selectedline ne {}} { set selid $currentid set y [yc $selectedline] if {$ytop < $y && $y < $ybot} { @@ -3388,7 +3388,7 @@ proc bolden {row font} { lappend boldrows $row $canv itemconf $linehtag($row) -font $font - if {[info exists selectedline] && $row == $selectedline} { + if {$row == $selectedline} { $canv delete secsel set t [eval $canv create rect [$canv bbox $linehtag($row)] \ -outline {{}} -tags secsel \ @@ -3402,7 +3402,7 @@ proc bolden_name {row font} { lappend boldnamerows $row $canv2 itemconf $linentag($row) -font $font - if {[info exists selectedline] && $row == $selectedline} { + if {$row == $selectedline} { $canv2 delete secsel set t [eval $canv2 create rect [$canv2 bbox $linentag($row)] \ -outline {{}} -tags secsel \ @@ -3831,7 +3831,7 @@ proc askrelhighlight {row id} { global descendent highlight_related iddrawn rhighlights global selectedline ancestor - if {![info exists selectedline]} return + if {$selectedline eq {}} return set isbold 0 if {$highlight_related eq [mc "Descendant"] || $highlight_related eq [mc "Not descendant"]} { @@ -4005,7 +4005,7 @@ proc visiblerows {} { proc layoutmore {} { global commitidx viewcomplete curview - global numcommits pending_select selectedline curview + global numcommits pending_select curview global lastscrollset lastscrollrows commitinterest if {$lastscrollrows < 100 || $viewcomplete($curview) || @@ -4916,7 +4916,7 @@ proc drawcmittext {id row col} { -text $name -font $nfont -tags text] set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \ -text $date -font mainfont -tags text] - if {[info exists selectedline] && $selectedline == $row} { + if {$selectedline == $row} { make_secsel $row } set xr [expr {$xt + [font measure $font $headline]}] @@ -5107,7 +5107,7 @@ proc drawvisible {} { if {$endrow >= $vrowmod($curview)} { update_arcrows $curview } - if {[info exists selectedline] && + if {$selectedline ne {} && $row <= $selectedline && $selectedline <= $endrow} { set targetrow $selectedline } elseif {[info exists targetid]} { @@ -5427,7 +5427,7 @@ proc dofind {{dirn 1} {wrap 1}} { } focus . if {$findstring eq {} || $numcommits == 0} return - if {![info exists selectedline]} { + if {$selectedline eq {}} { set findstartline [lindex [visiblerows] [expr {$dirn < 0}]] } else { set findstartline $selectedline @@ -5623,7 +5623,7 @@ proc markmatches {canv l str tag matches font row} { [expr {$x0+$xlen+2}] $y1 \ -outline {} -tags [list match$l matches] -fill yellow] $canv lower $t - if {[info exists selectedline] && $row == $selectedline} { + if {$row == $selectedline} { $canv raise $t secsel } } @@ -5782,7 +5782,7 @@ proc appendrefs {pos ids var} { proc dispneartags {delay} { global selectedline currentid showneartags tagphase - if {![info exists selectedline] || !$showneartags} return + if {$selectedline eq {} || !$showneartags} return after cancel dispnexttag if {$delay} { after 200 dispnexttag @@ -5796,7 +5796,7 @@ proc dispneartags {delay} { proc dispnexttag {} { global selectedline currentid showneartags tagphase ctext - if {![info exists selectedline] || !$showneartags} return + if {$selectedline eq {} || !$showneartags} return switch -- $tagphase { 0 { set dtags [desctags $currentid] @@ -6018,7 +6018,7 @@ proc sellastline {} { proc selnextline {dir} { global selectedline focus . - if {![info exists selectedline]} return + if {$selectedline eq {}} return set l [expr {$selectedline + $dir}] unmarkmatches selectline $l 1 @@ -6033,7 +6033,7 @@ proc selnextpage {dir} { } allcanvs yview scroll [expr {$dir * $lpp}] units drawvisible - if {![info exists selectedline]} return + if {$selectedline eq {}} return set l [expr {$selectedline + $dir * $lpp}] if {$l < 0} { set l 0 @@ -6047,7 +6047,7 @@ proc selnextpage {dir} { proc unselectline {} { global selectedline currentid - catch {unset selectedline} + set selectedline {} catch {unset currentid} allcanvs delete secsel rhighlight_none @@ -6056,7 +6056,7 @@ proc unselectline {} { proc reselectline {} { global selectedline - if {[info exists selectedline]} { + if {$selectedline ne {}} { selectline $selectedline 0 } } @@ -6868,7 +6868,7 @@ proc redisplay {} { setcanvscroll allcanvs yview moveto [lindex $span 0] drawvisible - if {[info exists selectedline]} { + if {$selectedline ne {}} { selectline $selectedline 0 allcanvs yview moveto [lindex $span 0] } @@ -7189,8 +7189,7 @@ proc rowmenu {x y id} { stopfinding set rowmenuid $id - if {![info exists selectedline] - || [rowofcommit $id] eq $selectedline} { + if {$selectedline eq {} || [rowofcommit $id] eq $selectedline} { set state disabled } else { set state normal @@ -7214,7 +7213,7 @@ proc rowmenu {x y id} { proc diffvssel {dirn} { global rowmenuid selectedline - if {![info exists selectedline]} return + if {$selectedline eq {}} return if {$dirn} { set oldid [commitonrow $selectedline] set newid $rowmenuid @@ -9890,6 +9889,7 @@ set viewperm(0) 0 set viewargs(0) {} set viewargscmd(0) {} +set selectedline {} set numcommits 0 set loginstance 0 set cmdlineok 0 From 16dd62ac4de10c9ae27d29bb1c906ad97ac8908f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 18 May 2008 13:08:17 -0400 Subject: [PATCH 072/202] git-gui: Add a --trace command line option Often new Git users want to know what commands git-gui uses to make changes, so they can learn the command line interface by mimicking what git-gui does in response to GUI actions. Showing the direct commands being executed is easy enough to implement but this is of little value to end-users because git-gui frequently directly calls plumbing, not porcelain. Since the code is already written and tested, its fairly harmless to include. It may not help a new end-user, but it can help with debugging git-gui or reverse-engineering its logic to further make changes to it or implement another GUI for Git. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 47 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 9df49710e1..6a8831a99e 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -122,6 +122,14 @@ set _reponame {} set _iscygwin {} set _search_path {} +set _trace [lsearch -exact $argv --trace] +if {$_trace >= 0} { + set argv [lreplace $argv $_trace $_trace] + set _trace 1 +} else { + set _trace 0 +} + proc appname {} { global _appname return $_appname @@ -245,6 +253,21 @@ proc get_config {name} { ## ## handy utils +proc _trace_exec {cmd} { + if {!$::_trace} return + set d {} + foreach v $cmd { + if {$d ne {}} { + append d { } + } + if {[regexp {[ \t\r\n'"$?*]} $v]} { + set v [sq $v] + } + append d $v + } + puts stderr $d +} + proc _git_cmd {name} { global _git_cmd_path @@ -339,7 +362,7 @@ proc _lappend_nice {cmd_var} { } proc git {args} { - set opt [list exec] + set opt [list] while {1} { switch -- [lindex $args 0] { @@ -359,12 +382,18 @@ proc git {args} { set cmdp [_git_cmd [lindex $args 0]] set args [lrange $args 1 end] - return [eval $opt $cmdp $args] + _trace_exec [concat $opt $cmdp $args] + set result [eval exec $opt $cmdp $args] + if {$::_trace} { + puts stderr "< $result" + } + return $result } proc _open_stdout_stderr {cmd} { + _trace_exec $cmd if {[catch { - set fd [open $cmd r] + set fd [open [concat [list | ] $cmd] r] } err]} { if { [lindex $cmd end] eq {2>@1} && $err eq {can not find channel named "1"} @@ -375,6 +404,7 @@ proc _open_stdout_stderr {cmd} { # to try to start it a second time. # set fd [open [concat \ + [list | ] \ [lrange $cmd 0 end-1] \ [list |& cat] \ ] r] @@ -387,7 +417,7 @@ proc _open_stdout_stderr {cmd} { } proc git_read {args} { - set opt [list |] + set opt [list] while {1} { switch -- [lindex $args 0] { @@ -415,7 +445,7 @@ proc git_read {args} { } proc git_write {args} { - set opt [list |] + set opt [list] while {1} { switch -- [lindex $args 0] { @@ -435,7 +465,8 @@ proc git_write {args} { set cmdp [_git_cmd [lindex $args 0]] set args [lrange $args 1 end] - return [open [concat $opt $cmdp $args] w] + _trace_exec [concat $opt $cmdp $args] + return [open [concat [list | ] $opt $cmdp $args] w] } proc githook_read {hook_name args} { @@ -455,12 +486,12 @@ proc githook_read {hook_name args} { } set scr {if test -x "$1";then exec "$@";fi} - set sh_c [list | $interp -c $scr $interp $pchook] + set sh_c [list $interp -c $scr $interp $pchook] return [_open_stdout_stderr [concat $sh_c $args]] } if {[file executable $pchook]} { - return [_open_stdout_stderr [concat [list | $pchook] $args]] + return [_open_stdout_stderr [concat [list $pchook] $args]] } return {} From 4be1fe1b944e568f47efcb9d1beb8639f50b723f Mon Sep 17 00:00:00 2001 From: Marcel Koeppen Date: Wed, 21 May 2008 18:25:54 +0200 Subject: [PATCH 073/202] Fix prepare-commit-msg hook and replace in-place sed The patterns to the case statement could never be matched, so the hook was a noop. This patch also replaces the non-portable use of in-place sed. Signed-off-by: Marcel Koeppen Signed-off-by: Junio C Hamano --- templates/hooks--prepare-commit-msg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/hooks--prepare-commit-msg b/templates/hooks--prepare-commit-msg index ff0f42a1d9..d3c1da34d2 100644 --- a/templates/hooks--prepare-commit-msg +++ b/templates/hooks--prepare-commit-msg @@ -20,11 +20,11 @@ # The third example adds a Signed-off-by line to the message, that can # still be edited. This is rarely a good idea. -case "$2 $3" in - merge) - sed -i '/^Conflicts:/,/#/!b;s/^/# &/;s/^# #/#/' "$1" ;; +case "$2,$3" in + merge,) + perl -i -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; -# ""|template) +# ,|template,) # perl -i -pe ' # print "\n" . `git diff --cached --name-status -r` # if /^#/ && $first++ == 0' "$1" ;; From 28f880241782024ecb212f1641c8dc487124523b Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Sun, 18 May 2008 16:57:27 +0200 Subject: [PATCH 074/202] Fix t6031 on filesystems without working exec bit The point of the test is not really to test the ability of the filesystem to keep the given x-bit, but to check is merge-recursive correctly handles it. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- t/t6031-merge-recursive.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/t/t6031-merge-recursive.sh b/t/t6031-merge-recursive.sh index c8310aee4f..8073e0c3ef 100755 --- a/t/t6031-merge-recursive.sh +++ b/t/t6031-merge-recursive.sh @@ -3,6 +3,9 @@ test_description='merge-recursive: handle file mode' . ./test-lib.sh +# Note that we follow "chmod +x F" with "update-index --chmod=+x F" to +# help filesystems that do not have the executable bit. + test_expect_success 'mode change in one branch: keep changed version' ' : >file1 && git add file1 && @@ -13,7 +16,7 @@ test_expect_success 'mode change in one branch: keep changed version' ' git commit -m a && git checkout -b b1 master && chmod +x file1 && - git add file1 && + git update-index --chmod=+x file1 && git commit -m b1 && git checkout a1 && git merge-recursive master -- a1 b1 && @@ -26,7 +29,7 @@ test_expect_success 'mode change in both branches: expect conflict' ' : >file2 && H=$(git hash-object file2) && chmod +x file2 && - git add file2 && + git update-index --add --chmod=+x file2 && git commit -m a2 && git checkout -b b2 master && : >file2 && From fdabc242f465771116e08d1ea3737fdb7453b2f1 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Tue, 20 May 2008 14:15:14 -0400 Subject: [PATCH 075/202] clone: fall back to copying if hardlinking fails Note that it stops trying hardlinks if any fail. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- builtin-clone.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/builtin-clone.c b/builtin-clone.c index 8936a51810..2a3f6732f2 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -207,13 +207,15 @@ static void copy_or_link_directory(char *src, char *dest) if (unlink(dest) && errno != ENOENT) die("failed to unlink %s\n", dest); - if (option_no_hardlinks) { - if (copy_file(dest, src, 0666)) - die("failed to copy file to %s\n", dest); - } else { - if (link(src, dest)) + if (!option_no_hardlinks) { + if (!link(src, dest)) + continue; + if (option_local) die("failed to create link %s\n", dest); + option_no_hardlinks = 1; } + if (copy_file(dest, src, 0666)) + die("failed to copy file to %s\n", dest); } closedir(dir); } From 26ec126a9166be39f79e746a3a8c92205c983820 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Tue, 20 May 2008 23:59:32 +0200 Subject: [PATCH 076/202] Fix t3701 if core.filemode disabled [jc: squashed in suggestions from Jeff King] Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- t/t3701-add-interactive.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index f15be93e77..fae64eae9f 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -66,6 +66,11 @@ test_expect_success 'revert works (commit)' ' grep "unchanged *+3/-0 file" output ' +if test "$(git config --bool core.filemode)" = false +then + say 'skipping filemode tests (filesystem does not properly support modes)' +else + test_expect_success 'patch does not affect mode' ' git reset --hard && echo content >>file && @@ -84,5 +89,7 @@ test_expect_success 'stage mode but not hunk' ' git diff file | grep "+content" ' +fi +# end of tests disabled when filemode is not usable test_done From 38ed1d89f759699de56004b08668e1764613f47b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 21 May 2008 12:04:34 -0700 Subject: [PATCH 077/202] "git-add -n -u" should not add but just report Signed-off-by: Junio C Hamano --- builtin-add.c | 22 +++++++++++++--------- builtin-checkout.c | 2 +- builtin-commit.c | 2 +- builtin-mv.c | 2 +- cache.h | 12 +++++++----- read-cache.c | 23 +++++++++++++++++------ t/t2200-add-update.sh | 17 +++++++++++++++++ 7 files changed, 57 insertions(+), 23 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index 4a91e3eb11..05af57f6ec 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -82,9 +82,9 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec, static void update_callback(struct diff_queue_struct *q, struct diff_options *opt, void *cbdata) { - int i, verbose; + int i, flags; - verbose = *((int *)cbdata); + flags = *((int *)cbdata); for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; const char *path = p->one->path; @@ -94,18 +94,19 @@ static void update_callback(struct diff_queue_struct *q, case DIFF_STATUS_UNMERGED: case DIFF_STATUS_MODIFIED: case DIFF_STATUS_TYPE_CHANGED: - add_file_to_cache(path, verbose); + add_file_to_cache(path, flags); break; case DIFF_STATUS_DELETED: - remove_file_from_cache(path); - if (verbose) + if (!(flags & ADD_CACHE_PRETEND)) + remove_file_from_cache(path); + if (flags) printf("remove '%s'\n", path); break; } } } -void add_files_to_cache(int verbose, const char *prefix, const char **pathspec) +void add_files_to_cache(const char *prefix, const char **pathspec, int flags) { struct rev_info rev; init_revisions(&rev, prefix); @@ -113,7 +114,7 @@ void add_files_to_cache(int verbose, const char *prefix, const char **pathspec) rev.prune_data = pathspec; rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = update_callback; - rev.diffopt.format_callback_data = &verbose; + rev.diffopt.format_callback_data = &flags; run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); } @@ -209,10 +210,13 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (take_worktree_changes) { const char **pathspec; + int flags = ((verbose ? ADD_CACHE_VERBOSE : 0) | + (show_only ? ADD_CACHE_PRETEND : 0)); + if (read_cache() < 0) die("index file corrupt"); pathspec = get_pathspec(prefix, argv); - add_files_to_cache(verbose, prefix, pathspec); + add_files_to_cache(prefix, pathspec, flags); goto finish; } @@ -254,7 +258,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) } for (i = 0; i < dir.nr; i++) - add_file_to_cache(dir.entries[i]->name, verbose); + add_file_to_cache(dir.entries[i]->name, verbose ? ADD_CACHE_VERBOSE : 0); finish: if (active_cache_changed) { diff --git a/builtin-checkout.c b/builtin-checkout.c index 10ec137cce..05c06421b6 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -282,7 +282,7 @@ static int merge_working_tree(struct checkout_opts *opts, * entries in the index. */ - add_files_to_cache(0, NULL, NULL); + add_files_to_cache(NULL, NULL, 0); work = write_tree_from_memory(); ret = reset_to_new(new->commit->tree, opts->quiet); diff --git a/builtin-commit.c b/builtin-commit.c index 0baec6db6a..924fca1953 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -246,7 +246,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix) */ if (all || (also && pathspec && *pathspec)) { int fd = hold_locked_index(&index_lock, 1); - add_files_to_cache(0, also ? prefix : NULL, pathspec); + add_files_to_cache(also ? prefix : NULL, pathspec, 0); refresh_cache(REFRESH_QUIET); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) diff --git a/builtin-mv.c b/builtin-mv.c index 94f6dd2aad..df9ea97744 100644 --- a/builtin-mv.c +++ b/builtin-mv.c @@ -256,7 +256,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) for (i = 0; i < added.nr; i++) { const char *path = added.items[i].path; - add_file_to_cache(path, verbose); + add_file_to_cache(path, verbose ? ADD_CACHE_VERBOSE : 0); } for (i = 0; i < deleted.nr; i++) diff --git a/cache.h b/cache.h index 093f04cec0..b1a8427b91 100644 --- a/cache.h +++ b/cache.h @@ -261,8 +261,8 @@ static inline void remove_name_hash(struct cache_entry *ce) #define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option)) #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos)) #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path)) -#define add_to_cache(path, st, verbose) add_to_index(&the_index, (path), (st), (verbose)) -#define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose)) +#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags)) +#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags)) #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL) #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options)) #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options)) @@ -366,8 +366,10 @@ extern int add_index_entry(struct index_state *, struct cache_entry *ce, int opt extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really); extern int remove_index_entry_at(struct index_state *, int pos); extern int remove_file_from_index(struct index_state *, const char *path); -extern int add_to_index(struct index_state *, const char *path, struct stat *, int verbose); -extern int add_file_to_index(struct index_state *, const char *path, int verbose); +#define ADD_CACHE_VERBOSE 1 +#define ADD_CACHE_PRETEND 2 +extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags); +extern int add_file_to_index(struct index_state *, const char *path, int flags); extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh); extern int ce_same_name(struct cache_entry *a, struct cache_entry *b); @@ -782,7 +784,7 @@ extern int convert_to_git(const char *path, const char *src, size_t len, extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst); /* add */ -void add_files_to_cache(int verbose, const char *prefix, const char **pathspec); +void add_files_to_cache(const char *prefix, const char **pathspec, int flags); /* diff.c */ extern int diff_auto_refresh_index; diff --git a/read-cache.c b/read-cache.c index 0382804e76..5d967e8ec9 100644 --- a/read-cache.c +++ b/read-cache.c @@ -462,12 +462,14 @@ static struct cache_entry *create_alias_ce(struct cache_entry *ce, struct cache_ return new; } -int add_to_index(struct index_state *istate, const char *path, struct stat *st, int verbose) +int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags) { - int size, namelen; + int size, namelen, was_same; mode_t st_mode = st->st_mode; struct cache_entry *ce, *alias; unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY; + int verbose = flags & (ADD_CACHE_VERBOSE | ADD_CACHE_PRETEND); + int pretend = flags & ADD_CACHE_PRETEND; if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode)) die("%s: can only add regular files, symbolic links or git-directories", path); @@ -509,19 +511,28 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st, if (ignore_case && alias && different_name(ce, alias)) ce = create_alias_ce(ce, alias); ce->ce_flags |= CE_ADDED; - if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE)) + + /* It was suspected to be recily clean, but it turns out to be Ok */ + was_same = (alias && + !ce_stage(alias) && + !hashcmp(alias->sha1, ce->sha1) && + ce->ce_mode == alias->ce_mode); + + if (pretend) + ; + else if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE)) die("unable to add %s to index",path); - if (verbose) + if (verbose && !was_same) printf("add '%s'\n", path); return 0; } -int add_file_to_index(struct index_state *istate, const char *path, int verbose) +int add_file_to_index(struct index_state *istate, const char *path, int flags) { struct stat st; if (lstat(path, &st)) die("%s: unable to stat (%s)", path, strerror(errno)); - return add_to_index(istate, path, &st, verbose); + return add_to_index(istate, path, &st, flags); } struct cache_entry *make_cache_entry(unsigned int mode, diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh index b664341926..f57a6e077c 100755 --- a/t/t2200-add-update.sh +++ b/t/t2200-add-update.sh @@ -111,4 +111,21 @@ test_expect_success 'touch and then add explicitly' ' ' +test_expect_success 'add -n -u should not add but just report' ' + + ( + echo "add '\''check'\''" && + echo "remove '\''top'\''" + ) >expect && + before=$(git ls-files -s check top) && + echo changed >>check && + rm -f top && + git add -n -u >actual && + after=$(git ls-files -s check top) && + + test "$before" = "$after" && + test_cmp expect actual + +' + test_done From c3170a83d6fee30c5d10afe33d88867ba0b68218 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Mon, 12 May 2008 19:22:56 +0200 Subject: [PATCH 078/202] git-merge: exclude unnecessary options from OPTIONS_SPEC gitcli(5) already documents them, and there are no options named --no-no-stat, --no-no-summary and --no-no-log. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- git-merge.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/git-merge.sh b/git-merge.sh index 69b35d87e6..5fc5f5201f 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -9,11 +9,9 @@ git-merge [options] ... git-merge [options] HEAD -- stat show a diffstat at the end of the merge -n,no-stat don't show a diffstat at the end of the merge +n don't show a diffstat at the end of the merge summary (synonym to --stat) -no-summary (synonym to --no-stat) log add list of one-line log to merge commit message -no-log don't add list of one-line log to merge commit message squash create a single commit instead of doing a merge commit perform a commit if the merge sucesses (default) ff allow fast forward (default) From 824b5dc29cfe4f58d052d2d9273b39d34bf018e0 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 13 May 2008 06:51:41 +0200 Subject: [PATCH 079/202] Documentation: rev-parse: add a few "--verify" and "--default" examples Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- Documentation/git-rev-parse.txt | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index b6b2fe92a1..69599ffb67 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -378,6 +378,31 @@ C? option C with an optional argument" eval `echo "$OPTS_SPEC" | git-rev-parse --parseopt -- "$@" || echo exit $?` ------------ +EXAMPLES +-------- + +* Print the object name of the current commit: ++ +------------ +$ git rev-parse --verify HEAD +------------ + +* Print the commit object name from the revision in the $REV shell variable: ++ +------------ +$ git rev-parse --verify $REV +------------ ++ +This will error out if $REV is empty or not a valid revision. + +* Same as above: ++ +------------ +$ git rev-parse --default master --verify $REV +------------ ++ +but if $REV is empty, the commit object name from master will be printed. + Author ------ From 0706bd19ef9b41e7519df2c73796ef93484272fd Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 28 Mar 2008 17:28:33 -0400 Subject: [PATCH 080/202] send-email: specify content-type of --compose body If the compose message contains non-ascii characters, then we assume it is in utf-8 and include the appropriate MIME headers. If the user has already included a MIME-Version header, then we assume they know what they are doing and don't add any headers. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- git-send-email.perl | 24 +++++++++++++++++++++++ t/t9001-send-email.sh | 44 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/git-send-email.perl b/git-send-email.perl index be4a20d7cd..71ba44d69d 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -518,8 +518,22 @@ EOT open(C,"<",$compose_filename) or die "Failed to open $compose_filename : " . $!; + my $need_8bit_cte = file_has_nonascii($compose_filename); + my $in_body = 0; while() { next if m/^GIT: /; + if (!$in_body && /^\n$/) { + $in_body = 1; + if ($need_8bit_cte) { + print C2 "MIME-Version: 1.0\n", + "Content-Type: text/plain; ", + "charset=utf-8\n", + "Content-Transfer-Encoding: 8bit\n"; + } + } + if (!$in_body && /^MIME-Version:/i) { + $need_8bit_cte = 0; + } print C2 $_; } close(C); @@ -956,3 +970,13 @@ sub validate_patch { } return undef; } + +sub file_has_nonascii { + my $fn = shift; + open(my $fh, '<', $fn) + or die "unable to open $fn: $!\n"; + while (my $line = <$fh>) { + return 1 if $line =~ /[^[:ascii:]]/; + } + return 0; +} diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index c0973b4e6e..e222c49320 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -166,4 +166,48 @@ test_expect_success 'second message is patch' ' grep "Subject:.*Second" msgtxt2 ' +test_expect_success '--compose adds MIME for utf8 body' ' + clean_fake_sendmail && + (echo "#!/bin/sh" && + echo "echo utf8 body: àéìöú >>\$1" + ) >fake-editor-utf8 && + chmod +x fake-editor-utf8 && + echo y | \ + GIT_EDITOR=$(pwd)/fake-editor-utf8 \ + GIT_SEND_EMAIL_NOTTY=1 \ + git send-email \ + --compose --subject foo \ + --from="Example " \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + $patches && + grep "^utf8 body" msgtxt1 && + grep "^Content-Type: text/plain; charset=utf-8" msgtxt1 +' + +test_expect_success '--compose respects user mime type' ' + clean_fake_sendmail && + (echo "#!/bin/sh" && + echo "(echo MIME-Version: 1.0" + echo " echo Content-Type: text/plain\\; charset=iso-8859-1" + echo " echo Content-Transfer-Encoding: 8bit" + echo " echo Subject: foo" + echo " echo " + echo " echo utf8 body: àéìöú) >\$1" + ) >fake-editor-utf8-mime && + chmod +x fake-editor-utf8-mime && + echo y | \ + GIT_EDITOR=$(pwd)/fake-editor-utf8-mime \ + GIT_SEND_EMAIL_NOTTY=1 \ + git send-email \ + --compose --subject foo \ + --from="Example " \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + $patches && + grep "^utf8 body" msgtxt1 && + grep "^Content-Type: text/plain; charset=iso-8859-1" msgtxt1 && + ! grep "^Content-Type: text/plain; charset=utf-8" msgtxt1 +' + test_done From d54eaaa268eb79a51ac11bfa8bbfb456c0b1a1fa Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 28 Mar 2008 17:29:01 -0400 Subject: [PATCH 081/202] send-email: rfc2047-quote subject lines with non-ascii characters We always use 'utf-8' as the encoding, since we currently have no way of getting the information from the user. This also refactors the quoting of recipient names, since both processes can share the rfc2047 quoting code. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- git-send-email.perl | 19 +++++++++++++++++-- t/t9001-send-email.sh | 15 +++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/git-send-email.perl b/git-send-email.perl index 71ba44d69d..455a570596 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -534,6 +534,14 @@ EOT if (!$in_body && /^MIME-Version:/i) { $need_8bit_cte = 0; } + if (!$in_body && /^Subject: ?(.*)/i) { + my $subject = $1; + $_ = "Subject: " . + ($subject =~ /[^[:ascii:]]/ ? + quote_rfc2047($subject) : + $subject) . + "\n"; + } print C2 $_; } close(C); @@ -624,6 +632,14 @@ sub unquote_rfc2047 { return wantarray ? ($_, $encoding) : $_; } +sub quote_rfc2047 { + local $_ = shift; + my $encoding = shift || 'utf-8'; + s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X", ord($1))/eg; + s/(.*)/=\?$encoding\?q\?$1\?=/; + return $_; +} + # use the simplest quoting being able to handle the recipient sub sanitize_address { @@ -641,8 +657,7 @@ sub sanitize_address # rfc2047 is needed if a non-ascii char is included if ($recipient_name =~ /[^[:ascii:]]/) { - $recipient_name =~ s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X", ord($1))/eg; - $recipient_name =~ s/(.*)/=\?utf-8\?q\?$1\?=/; + $recipient_name = quote_rfc2047($recipient_name); } # double quotes are needed if specials or CTLs are included diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index e222c49320..a4bcd282b6 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -210,4 +210,19 @@ test_expect_success '--compose respects user mime type' ' ! grep "^Content-Type: text/plain; charset=utf-8" msgtxt1 ' +test_expect_success '--compose adds MIME for utf8 subject' ' + clean_fake_sendmail && + echo y | \ + GIT_EDITOR=$(pwd)/fake-editor \ + GIT_SEND_EMAIL_NOTTY=1 \ + git send-email \ + --compose --subject utf8-sübjëct \ + --from="Example " \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + $patches && + grep "^fake edit" msgtxt1 && + grep "^Subject: =?utf-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1 +' + test_done From 18023c20656265364d4d1805f435e8420ab70687 Mon Sep 17 00:00:00 2001 From: "Horst H. von Brand" Date: Fri, 28 Mar 2008 11:09:04 -0300 Subject: [PATCH 082/202] Fix recipient santitization Need to quote all special characters, not just the first one Signed-off-by: Horst H. von Brand 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 be4a20d7cd..5630276f78 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -633,7 +633,7 @@ sub sanitize_address # double quotes are needed if specials or CTLs are included elsif ($recipient_name =~ /[][()<>@,;:\\".\000-\037\177]/) { - $recipient_name =~ s/(["\\\r])/\\$1/; + $recipient_name =~ s/(["\\\r])/\\$1/g; $recipient_name = "\"$recipient_name\""; } From 325abb7b1a5bf47b53e3ae1b585f11ebd6a9fea9 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Thu, 8 May 2008 14:26:55 -0700 Subject: [PATCH 083/202] cvsexportcommit: Create config option for CVS dir For a given project the directory used with the -w option is almost always the same each time. Let it be specified with 'cvsexportcommit.cvsdir' so it's not necessary to manually add it with -w each time. Signed-off-by: Trent Piepho Acked-by: Robin Rosenberg Signed-off-by: Junio C Hamano --- Documentation/git-cvsexportcommit.txt | 8 +++++++- git-cvsexportcommit.perl | 5 +++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Documentation/git-cvsexportcommit.txt b/Documentation/git-cvsexportcommit.txt index 9a47b4c397..363c36d694 100644 --- a/Documentation/git-cvsexportcommit.txt +++ b/Documentation/git-cvsexportcommit.txt @@ -65,11 +65,17 @@ OPTIONS -w:: Specify the location of the CVS checkout to use for the export. This option does not require GIT_DIR to be set before execution if the - current directory is within a git repository. + current directory is within a git repository. The default is the + value of 'cvsexportcommit.cvsdir'. -v:: Verbose. +CONFIGURATION +------------- +cvsexportcommit.cvsdir:: + The default location of the CVS checkout to use for the export. + EXAMPLES -------- diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl index b6036bd4d3..c93bd9c9b5 100755 --- a/git-cvsexportcommit.perl +++ b/git-cvsexportcommit.perl @@ -6,6 +6,7 @@ use File::Temp qw(tempdir); use Data::Dumper; use File::Basename qw(basename dirname); use File::Spec; +use Git; our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u, $opt_w); @@ -15,6 +16,10 @@ $opt_h && usage(); die "Need at least one commit identifier!" unless @ARGV; +# Get git-config settings +my $repo = Git->repository(); +$opt_w = $repo->config('cvsexportcommit.cvsdir') unless defined $opt_w; + if ($opt_w) { # Remember where GIT_DIR is before changing to CVS checkout unless ($ENV{GIT_DIR}) { From b592d88fb21fb3e3216d8eface9b748b2868323b Mon Sep 17 00:00:00 2001 From: Gustaf Hendeby Date: Thu, 22 May 2008 04:28:43 +0200 Subject: [PATCH 084/202] Documentation: Fix skipped section level With xmlto 0.0.18 it seems to demand that no section levels are skipped. The commit 'implement gitcvs.usecrlfattr' (8a06a632976ead) one such skip, which here is removed by increasing the level of the offender. Signed-off-by: Gustaf Hendeby Signed-off-by: Junio C Hamano --- Documentation/git-cvsserver.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt index cd0685ea67..a33382ec2d 100644 --- a/Documentation/git-cvsserver.txt +++ b/Documentation/git-cvsserver.txt @@ -302,7 +302,7 @@ Legacy monitoring operations are not supported (edit, watch and related). Exports and tagging (tags and branches) are not supported at this stage. CRLF Line Ending Conversions -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By default the server leaves the '-k' mode blank for all files, which causes the cvs client to treat them as a text files, subject From a977953326b5bc9556f2648a7327c623e676aaca Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 22 May 2008 21:12:19 +1000 Subject: [PATCH 085/202] gitk: Fix bug introduced by "gitk: Fix "wrong # coordinates" error on reload" Commit 94503a66c56c935e77a8fbe3622f1f56b7134ccc ("gitk: Fix "wrong # coordinates" error on reload") was correct as far as it went, but introduced a problem because it didn't also clear out boldrows and boldnamerows in clear_display. This resulted in Tcl errors after scrolling through the graph for a while if some rows were highlighted. This fixes it. Signed-off-by: Paul Mackerras --- gitk | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gitk b/gitk index 45ebcaf6dd..22bcd18a46 100755 --- a/gitk +++ b/gitk @@ -5125,7 +5125,7 @@ proc drawvisible {} { proc clear_display {} { global iddrawn linesegs need_redisplay nrows_drawn global vhighlights fhighlights nhighlights rhighlights - global linehtag linentag linedtag + global linehtag linentag linedtag boldrows boldnamerows allcanvs delete all catch {unset iddrawn} @@ -5133,6 +5133,8 @@ proc clear_display {} { catch {unset linehtag} catch {unset linentag} catch {unset linedtag} + set boldrows {} + set boldnamerows {} catch {unset vhighlights} catch {unset fhighlights} catch {unset nhighlights} From 7f83aa2d3df0623984e265052f96e7172e3efbeb Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 21 May 2008 16:40:10 -0400 Subject: [PATCH 086/202] git-gui: Handle workdir detection when CYGWIN=nowinsymlinks If the user has put nowinsymlinks into their CYGWIN environment variable any symlinks created by a Cygwin process (e.g. ln -s) will not have the ".lnk" suffix. In this case workdir is still a workdir, but our detection of looking for "info.lnk" fails as the symlink is actually a normal file called "info". Instead we just always use Cygwin's test executable to see if info/exclude is a file. If it is, we assume from there on it can be read by git-ls-files --others and is thus safe to use on the command line. Signed-off-by: Shawn O. Pearce --- git-gui.sh | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 6a8831a99e..e6e88902f1 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1127,27 +1127,18 @@ proc rescan {after {honor_trustmtime 1}} { } if {[is_Cygwin]} { - set is_git_info_link {} set is_git_info_exclude {} proc have_info_exclude {} { - global is_git_info_link is_git_info_exclude + global is_git_info_exclude - if {$is_git_info_link eq {}} { - set is_git_info_link [file isfile [gitdir info.lnk]] - } - - if {$is_git_info_link} { - if {$is_git_info_exclude eq {}} { - if {[catch {exec test -f [gitdir info exclude]}]} { - set is_git_info_exclude 0 - } else { - set is_git_info_exclude 1 - } + if {$is_git_info_exclude eq {}} { + if {[catch {exec test -f [gitdir info exclude]}]} { + set is_git_info_exclude 0 + } else { + set is_git_info_exclude 1 } - return $is_git_info_exclude - } else { - return [file readable [gitdir info exclude]] } + return $is_git_info_exclude } } else { proc have_info_exclude {} { From d3aca58562ee5c4af5266affd942c58dcb9f064d Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Fri, 23 May 2008 00:38:46 +0200 Subject: [PATCH 087/202] bisect: add test cases to check that "git bisect start" is atomic This patch adds some test cases to check that "git bisect start" doesn't leave us in a bad state, especially when it fails. These test cases show that "git bisect start" is not atomic when it fails and leave some files like .git/BISECT_START, and in some cases some refs, over. The test failures should be fixed in latter commits. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- t/t6030-bisect-porcelain.sh | 43 +++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 933f567983..7557fa1a1b 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -126,6 +126,49 @@ test_expect_success 'bisect reset removes packed refs' ' test -z "$(git for-each-ref "refs/heads/bisect")" ' +test_expect_success 'bisect start: back in good branch' ' + git branch > branch.output && + grep "* other" branch.output > /dev/null && + git bisect start $HASH4 $HASH1 -- && + git bisect good && + git bisect start $HASH4 $HASH1 -- && + git bisect bad && + git bisect reset && + git branch > branch.output && + grep "* other" branch.output > /dev/null +' + +test_expect_failure 'bisect start: no ".git/BISECT_START" if junk rev' ' + git bisect start $HASH4 $HASH1 -- && + git bisect good && + test_must_fail git bisect start $HASH4 foo -- && + git branch > branch.output && + grep "* other" branch.output > /dev/null && + test_must_fail test -e .git/BISECT_START +' + +test_expect_failure 'bisect start: no ".git/BISECT_START" if mistaken rev' ' + git bisect start $HASH4 $HASH1 -- && + git bisect good && + test_must_fail git bisect start $HASH1 $HASH4 -- && + git branch > branch.output && + grep "* other" branch.output > /dev/null && + test_must_fail test -e .git/BISECT_START +' + +test_expect_failure 'bisect start: no ".git/BISECT_START" if checkout error' ' + echo "temp stuff" > hello && + test_must_fail git bisect start $HASH4 $HASH1 -- && + git branch && + git branch > branch.output && + grep "* other" branch.output > /dev/null && + test_must_fail test -e .git/BISECT_START && + test -z "$(git for-each-ref "refs/bisect/*")" +' + +# This cleanup is needed whatever the result of the above test. +git checkout HEAD hello + # $HASH1 is good, $HASH4 is bad, we skip $HASH3 # but $HASH2 is bad, # so we should find $HASH2 as the first bad commit From 9d0cd91c4e497192e89177847d1511acea5793cd Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Fri, 23 May 2008 00:38:59 +0200 Subject: [PATCH 088/202] bisect: fix left over "BISECT_START" file when starting with junk rev Before this patch, when using for example: $ git bisect start with or that cannot be parsed as a revision, we could leave a ".git/BISECT_START" file, from a previous "git bisect start", alone. This patch makes sure that it does not happen by removing the "BISECT_START" file in "bisect_clean_state" and then always writing it again at the end of "bisect_start". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- git-bisect.sh | 16 +++++++--------- t/t6030-bisect-porcelain.sh | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 164e8ed81f..0dcb526f4b 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -81,8 +81,8 @@ bisect_start() { start_head='' case "$head" in refs/heads/bisect) - branch=`cat "$GIT_DIR/BISECT_START"` - git checkout $branch || exit + start_head=$(cat "$GIT_DIR/BISECT_START") + git checkout "$start_head" || exit ;; refs/heads/*|$_x40) # This error message should only be triggered by cogito usage, @@ -134,7 +134,7 @@ bisect_start() { done sq "$@" >"$GIT_DIR/BISECT_NAMES" - test -n "$start_head" && echo "$start_head" >"$GIT_DIR/BISECT_START" + echo "$start_head" >"$GIT_DIR/BISECT_START" eval "$eval" echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" bisect_auto_next @@ -392,12 +392,7 @@ bisect_reset() { *) usage ;; esac - if git checkout "$branch"; then - # Cleanup head-name if it got left by an old version of git-bisect - rm -f "$GIT_DIR/head-name" - rm -f "$GIT_DIR/BISECT_START" - bisect_clean_state - fi + git checkout "$branch" && bisect_clean_state } bisect_clean_state() { @@ -407,9 +402,12 @@ bisect_clean_state() { do git update-ref -d $ref $hash done + rm -f "$GIT_DIR/BISECT_START" rm -f "$GIT_DIR/BISECT_LOG" rm -f "$GIT_DIR/BISECT_NAMES" rm -f "$GIT_DIR/BISECT_RUN" + # Cleanup head-name if it got left by an old version of git-bisect + rm -f "$GIT_DIR/head-name" } bisect_replay () { diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 7557fa1a1b..68b5440917 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -138,7 +138,7 @@ test_expect_success 'bisect start: back in good branch' ' grep "* other" branch.output > /dev/null ' -test_expect_failure 'bisect start: no ".git/BISECT_START" if junk rev' ' +test_expect_success 'bisect start: no ".git/BISECT_START" if junk rev' ' git bisect start $HASH4 $HASH1 -- && git bisect good && test_must_fail git bisect start $HASH4 foo -- && From ba963de859e76a63d447345eeb3e134116d02433 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Fri, 23 May 2008 00:39:22 +0200 Subject: [PATCH 089/202] bisect: trap critical errors in "bisect_start" Before this patch, when using "git bisect start" with mistaken revs or when the checkout of the branch we want to test failed, we exited after having written files like ".git/BISECT_START", ".git/BISECT_NAMES" and after having written "refs/bisect/bad" and "refs/bisect/good-*" refs. With this patch we trap all errors that can happen when writing the new state and when we are in "bisect_next". So that we can try to clean up everything in case of problems, using "bisect_clean_state". This patch also contains a "bisect_write" cleanup to make it exit on error and return 0 otherwise. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- git-bisect.sh | 30 ++++++++++++++++++++++++------ t/t6030-bisect-porcelain.sh | 10 ++++------ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 0dcb526f4b..57168b0ae4 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -133,11 +133,29 @@ bisect_start() { esac done - sq "$@" >"$GIT_DIR/BISECT_NAMES" - echo "$start_head" >"$GIT_DIR/BISECT_START" - eval "$eval" - echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" + # + # Change state. + # In case of mistaken revs or checkout error, or signals received, + # "bisect_auto_next" below may exit or misbehave. + # We have to trap this to be able to clean up using + # "bisect_clean_state". + # + trap 'bisect_clean_state' 0 + trap 'exit 255' 1 2 3 15 + + # + # Write new start state. + # + sq "$@" >"$GIT_DIR/BISECT_NAMES" && + echo "$start_head" >"$GIT_DIR/BISECT_START" && + eval "$eval" && + echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit + # + # Check if we can proceed to the next bisect state. + # bisect_auto_next + + trap '-' 0 } bisect_write() { @@ -149,9 +167,9 @@ bisect_write() { good|skip) tag="$state"-"$rev" ;; *) die "Bad bisect_write argument: $state" ;; esac - git update-ref "refs/bisect/$tag" "$rev" + git update-ref "refs/bisect/$tag" "$rev" || exit echo "# $state: $(git show-branch $rev)" >>"$GIT_DIR/BISECT_LOG" - test -z "$nolog" && echo "git-bisect $state $rev" >>"$GIT_DIR/BISECT_LOG" + test -n "$nolog" || echo "git-bisect $state $rev" >>"$GIT_DIR/BISECT_LOG" } bisect_state() { diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 68b5440917..c4f074d738 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -147,7 +147,7 @@ test_expect_success 'bisect start: no ".git/BISECT_START" if junk rev' ' test_must_fail test -e .git/BISECT_START ' -test_expect_failure 'bisect start: no ".git/BISECT_START" if mistaken rev' ' +test_expect_success 'bisect start: no ".git/BISECT_START" if mistaken rev' ' git bisect start $HASH4 $HASH1 -- && git bisect good && test_must_fail git bisect start $HASH1 $HASH4 -- && @@ -156,19 +156,17 @@ test_expect_failure 'bisect start: no ".git/BISECT_START" if mistaken rev' ' test_must_fail test -e .git/BISECT_START ' -test_expect_failure 'bisect start: no ".git/BISECT_START" if checkout error' ' +test_expect_success 'bisect start: no ".git/BISECT_START" if checkout error' ' echo "temp stuff" > hello && test_must_fail git bisect start $HASH4 $HASH1 -- && git branch && git branch > branch.output && grep "* other" branch.output > /dev/null && test_must_fail test -e .git/BISECT_START && - test -z "$(git for-each-ref "refs/bisect/*")" + test -z "$(git for-each-ref "refs/bisect/*")" && + git checkout HEAD hello ' -# This cleanup is needed whatever the result of the above test. -git checkout HEAD hello - # $HASH1 is good, $HASH4 is bad, we skip $HASH3 # but $HASH2 is bad, # so we should find $HASH2 as the first bad commit From 634f246444e6a1675e351f31362e6a375dc44f70 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Fri, 23 May 2008 01:28:57 +0200 Subject: [PATCH 090/202] bisect: use a detached HEAD to bisect When "git bisect" was first written, it was not possible to checkout a detached HEAD. The detached feature appeared latter. That's why before this patch the "git bisect" process used a "bisect" branch to checkout new revisions to be tested (and also a "new-bisect" one to check if the checkouts could work). This patch makes "git bisect" checkout revisions to be tested on a detached HEAD. This simplifies the code a bit. The tests to check that "git bisect" does not start if a "bisect" or a "new-bisect" branch exists are removed as they are not relevant any more. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- git-bisect.sh | 64 ++++++++++++++++++------------------- t/t6030-bisect-porcelain.sh | 38 +++++++++++----------- 2 files changed, 50 insertions(+), 52 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 57168b0ae4..4bcbaceb8b 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -63,40 +63,40 @@ bisect_autostart() { bisect_start() { # - # Verify HEAD. If we were bisecting before this, reset to the - # top-of-line master first! + # Verify HEAD. # head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) || head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) || die "Bad HEAD - I need a HEAD" - # - # Check that we either already have BISECT_START, or that the - # branches bisect, new-bisect don't exist, to not override them. - # - test -s "$GIT_DIR/BISECT_START" || - if git show-ref --verify -q refs/heads/bisect || - git show-ref --verify -q refs/heads/new-bisect; then - die 'The branches "bisect" and "new-bisect" must not exist.' - fi - start_head='' - case "$head" in - refs/heads/bisect) - start_head=$(cat "$GIT_DIR/BISECT_START") - git checkout "$start_head" || exit - ;; - refs/heads/*|$_x40) - # This error message should only be triggered by cogito usage, - # and cogito users should understand it relates to cg-seek. - [ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree" - start_head="${head#refs/heads/}" - ;; - *) - die "Bad HEAD - strange symbolic ref" - ;; - esac # - # Get rid of any old bisect state + # Check if we are bisecting. + # + start_head='' + if test -s "$GIT_DIR/BISECT_START" + then + # Reset to the rev from where we started. + start_head=$(cat "$GIT_DIR/BISECT_START") + git checkout "$start_head" || exit + else + # Get rev from where we start. + case "$head" in + refs/heads/*|$_x40) + # This error message should only be triggered by + # cogito usage, and cogito users should understand + # it relates to cg-seek. + [ -s "$GIT_DIR/head-name" ] && + die "won't bisect on seeked tree" + start_head="${head#refs/heads/}" + ;; + *) + die "Bad HEAD - strange symbolic ref" + ;; + esac + fi + + # + # Get rid of any old bisect state. # bisect_clean_state @@ -118,7 +118,7 @@ bisect_start() { break ;; *) - rev=$(git rev-parse --verify "$arg^{commit}" 2>/dev/null) || { + rev=$(git rev-parse -q --verify "$arg^{commit}") || { test $has_double_dash -eq 1 && die "'$arg' does not appear to be a valid revision" break @@ -366,9 +366,7 @@ bisect_next() { exit_if_skipped_commits "$bisect_rev" echo "Bisecting: $bisect_nr revisions left to test after this" - git branch -D new-bisect 2> /dev/null - git checkout -q -b new-bisect "$bisect_rev" || exit - git branch -M new-bisect bisect + git checkout -q "$bisect_rev" || exit git show-branch "$bisect_rev" } @@ -415,7 +413,7 @@ bisect_reset() { bisect_clean_state() { # There may be some refs packed during bisection. - git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* refs/heads/bisect | + git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* | while read ref hash do git update-ref -d $ref $hash diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index c4f074d738..0626544823 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -322,25 +322,6 @@ test_expect_success 'bisect starting with a detached HEAD' ' test $HEAD = $(cat .git/BISECT_START) && git bisect reset && test $HEAD = $(git rev-parse --verify HEAD) - -' - -test_expect_success 'bisect refuses to start if branch bisect exists' ' - git bisect reset && - git branch bisect && - test_must_fail git bisect start && - git branch -d bisect && - git checkout -b bisect && - test_must_fail git bisect start && - git checkout master && - git branch -d bisect -' - -test_expect_success 'bisect refuses to start if branch new-bisect exists' ' - git bisect reset && - git branch new-bisect && - test_must_fail git bisect start && - git branch -d new-bisect ' test_expect_success 'bisect errors out if bad and good are mistaken' ' @@ -350,6 +331,25 @@ test_expect_success 'bisect errors out if bad and good are mistaken' ' git bisect reset ' +test_expect_success 'bisect does not create a "bisect" branch' ' + git bisect reset && + git bisect start $HASH7 $HASH1 && + git branch bisect && + rev_hash4=$(git rev-parse --verify HEAD) && + test "$rev_hash4" = "$HASH4" && + git branch -D bisect && + git bisect good && + git branch bisect && + rev_hash6=$(git rev-parse --verify HEAD) && + test "$rev_hash6" = "$HASH6" && + git bisect good > my_bisect_log.txt && + grep "$HASH7 is first bad commit" my_bisect_log.txt && + git bisect reset && + rev_hash6=$(git rev-parse --verify bisect) && + test "$rev_hash6" = "$HASH6" && + git branch -D bisect +' + # # test_done From dee2775a29440ca8a52bb5bd09a6de6cd29f69cc Mon Sep 17 00:00:00 2001 From: Anders Waldenborg Date: Wed, 21 May 2008 13:44:43 +0200 Subject: [PATCH 091/202] gitweb: Convert string to internal form before chopping in chop_str Fix chop_str not to cut in middle of utf8 multibyte chars. Without this fix at least author name in short log may cut in middle of a multibyte char. When the result comes to esc_html to_utf8 is called again, which doesn't find valid utf8 and decodes using $fallback_encoding making it even worse. This also have the nice side effect that it actually tries to show the first 10 _characters_, not the number of characters that happened to fit into 10 bytes. Signed-off-by: Anders Waldenborg 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 2facf2db7a..8308e2208e 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -866,6 +866,10 @@ sub chop_str { my $add_len = shift || 10; my $where = shift || 'right'; # 'left' | 'center' | 'right' + # Make sure perl knows it is utf8 encoded so we don't + # cut in the middle of a utf8 multibyte char. + $str = to_utf8($str); + # allow only $len chars, but don't cut a word if it would fit in $add_len # if it doesn't fit, cut it if it's still longer than the dots we would add # remove chopped character entities entirely From f9189cf8f2427b10a40b5b3e530e2c893bc64ae9 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 21 May 2008 12:32:16 +0100 Subject: [PATCH 092/202] pull --rebase: exit early when the working directory is dirty When rebasing fails during "pull --rebase", you cannot just clean up the working directory and call "pull --rebase" again, since the remote branch was already fetched. Therefore, die early when the working directory is dirty. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-pull.sh | 5 +++++ t/t5520-pull.sh | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/git-pull.sh b/git-pull.sh index bf0c2985af..809e537a4d 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -107,6 +107,11 @@ error_on_no_merge_candidates () { } test true = "$rebase" && { + git update-index --refresh && + git diff-files --quiet && + git diff-index --cached --quiet HEAD -- || + die "refusing to pull with rebase: your working tree is not up-to-date" + . git-parse-remote && origin="$1" test -z "$origin" && origin=$(get_default_remote) diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index 9484129ca5..997b2db827 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -92,4 +92,22 @@ test_expect_success '--rebase with rebased upstream' ' ' +test_expect_success 'pull --rebase dies early with dirty working directory' ' + + git update-ref refs/remotes/me/copy copy^ && + COPY=$(git rev-parse --verify me/copy) && + git rebase --onto $COPY copy && + git config branch.to-rebase.remote me && + git config branch.to-rebase.merge refs/heads/copy && + git config branch.to-rebase.rebase true && + echo dirty >> file && + git add file && + test_must_fail git pull && + test $COPY = $(git rev-parse --verify me/copy) && + git checkout HEAD -- file && + git pull && + test $COPY != $(git rev-parse --verify me/copy) + +' + test_done From c30f9936b0e5cdced3d9cb859ce0d325ebe9c91d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 22 May 2008 08:47:19 -0400 Subject: [PATCH 093/202] Clarify repack -n documentation While repacking a local repository a coworker thought the -n option was necessary to git-repack to keep it from updating some unknown file on the central server we all share. Explaining further what the option is (not) doing helps to make it clear the option does not impact any remote repositories the user may have configured. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- Documentation/git-repack.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index 3d957492f8..d14ab5154f 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -55,8 +55,11 @@ OPTIONS linkgit:git-pack-objects[1]. -n:: - Do not update the server information with - `git update-server-info`. + Do not update the server information with + `git update-server-info`. This option skips + updating local catalog files needed to publish + this repository (or a direct copy of it) + over HTTP or FTP. See gitlink:git-update-server-info[1]. --window=[N], --depth=[N]:: These two options affect how the objects contained in the pack are From 3a3e097b866b2785e44c008092884c853fd406cc Mon Sep 17 00:00:00 2001 From: Jon Loeliger Date: Thu, 22 May 2008 14:59:25 -0500 Subject: [PATCH 094/202] git-show.txt: Not very stubby these days. Signed-off-by: Jon Loeliger Signed-off-by: Junio C Hamano --- Documentation/git-show.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/Documentation/git-show.txt b/Documentation/git-show.txt index dccf0e20ec..29ed0acc62 100644 --- a/Documentation/git-show.txt +++ b/Documentation/git-show.txt @@ -79,8 +79,6 @@ Documentation ------------- Documentation by David Greaves, Petr Baudis and the git-list . -This manual page is a stub. You can help the git documentation by expanding it. - GIT --- Part of the linkgit:git[7] suite From 26b4d0039d84010bbfab2148395cc86f87b91286 Mon Sep 17 00:00:00 2001 From: Heikki Orsila Date: Thu, 22 May 2008 18:24:41 +0300 Subject: [PATCH 095/202] Add missing "short" alternative to --date in rev-list-options.txt Signed-off-by: Heikki Orsila Signed-off-by: Junio C Hamano --- Documentation/rev-list-options.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 2648a55085..9cd677105d 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -13,7 +13,7 @@ include::pretty-options.txt[] Synonym for `--date=relative`. ---date={relative,local,default,iso,rfc}:: +--date={relative,local,default,iso,rfc,short}:: Only takes effect for dates shown in human-readable format, such as when using "--pretty". From 4ba776c231f27e69435d76bac98d033db859cd6f Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Thu, 22 May 2008 18:03:08 -0400 Subject: [PATCH 096/202] Test that --reference actually suppresses fetching referenced objects Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- t/t5700-clone-reference.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh index b6a54867b4..58a97f1ed1 100755 --- a/t/t5700-clone-reference.sh +++ b/t/t5700-clone-reference.sh @@ -8,6 +8,8 @@ test_description='test clone --reference' base_dir=`pwd` +U=$base_dir/UPLOAD_LOG + test_expect_success 'preparing first repository' \ 'test_create_repo A && cd A && echo first > file1 && @@ -50,8 +52,13 @@ diff expected current' cd "$base_dir" +rm -f $U + test_expect_success 'cloning with reference (no -l -s)' \ -'git clone --reference B file://`pwd`/A D' +'GIT_DEBUG_SEND_PACK=3 git clone --reference B file://`pwd`/A D 3>$U' + +test_expect_success 'fetched no objects' \ +'! grep "^want" $U' cd "$base_dir" From fabb01996be9f7c8862ab1a6fdfd83c90be5324a Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Thu, 22 May 2008 18:03:05 -0400 Subject: [PATCH 097/202] Add a test for another combination of --reference In this case, the reference repository has some useful loose objects, but not all useful objects, and we make sure that we can find the objects we fetch from the repository we're cloning in the new repository, instead of potentially being distracted by the reference repository. Doing the wrong thing in a builtin-clone implementation would lead to this looking for an object in the wrong place, not finding it (because it's only in the right place), and crashing. Signed-off-by: Johan Herland Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- t/t5700-clone-reference.sh | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh index 58a97f1ed1..b53c3ff956 100755 --- a/t/t5700-clone-reference.sh +++ b/t/t5700-clone-reference.sh @@ -120,4 +120,25 @@ diff expected current' cd "$base_dir" +test_expect_success 'preparing alternate repository #1' \ +'test_create_repo F && cd F && +echo first > file1 && +git add file1 && +git commit -m initial' + +cd "$base_dir" + +test_expect_success 'cloning alternate repo #2 and adding changes to repo #1' \ +'git clone F G && cd F && +echo second > file2 && +git add file2 && +git commit -m addition' + +cd "$base_dir" + +test_expect_success 'cloning alternate repo #1, using #2 as reference' \ +'git clone --reference G F H' + +cd "$base_dir" + test_done From b50c8469cc9a336b22ef37b23711d4547a48bc2b Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Thu, 22 May 2008 18:03:00 -0400 Subject: [PATCH 098/202] Add test for cloning with "--reference" repo being a subset of source repo The first test in this series tests "git clone -l -s --reference B A C", where repo B is a superset of repo A (A has one commit, B has the same commit plus another). In this case, all objects to be cloned are already present in B. However, we should also test the case where the "--reference" repo is a _subset_ of the source repo (e.g. "git clone -l -s --reference A B C"), i.e. some objects are not available in the "--reference" repo, and will have to be found in the source repo. Signed-off-by: Johan Herland Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- t/t5700-clone-reference.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh index b53c3ff956..0112c218e0 100755 --- a/t/t5700-clone-reference.sh +++ b/t/t5700-clone-reference.sh @@ -141,4 +141,9 @@ test_expect_success 'cloning alternate repo #1, using #2 as reference' \ cd "$base_dir" +test_expect_success 'cloning with reference being subset of source (-l -s)' \ +'git clone -l -s --reference A B E' + +cd "$base_dir" + test_done From 950ce2e22db4a3c1ff9ad598b46ccb22c517a336 Mon Sep 17 00:00:00 2001 From: Chris Parsons Date: Thu, 22 May 2008 08:50:02 -0400 Subject: [PATCH 099/202] Updated status to show 'Not currently on any branch' in red This provides additional warning to users when attempting to commit to a detached HEAD. It is configurable in color.status.nobranch. Signed-off-by: Chris Parsons Acked-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/config.txt | 6 ++++-- wt-status.c | 11 ++++++++--- wt-status.h | 1 + 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 217980f48d..554977b895 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -523,8 +523,10 @@ color.status.:: one of `header` (the header text of the status message), `added` or `updated` (files which are added but not committed), `changed` (files which are changed but not added in the index), - or `untracked` (files which are not tracked by git). The values of - these variables may be specified as in color.branch.. + `untracked` (files which are not tracked by git), or + `nobranch` (the color the 'no branch' warning is shown in, defaulting + to red). The values of these variables may be specified as in + color.branch.. commit.template:: Specify a file to use as the template for new commit messages. diff --git a/wt-status.c b/wt-status.c index a44c543375..6e6516cd87 100644 --- a/wt-status.c +++ b/wt-status.c @@ -18,6 +18,7 @@ static char wt_status_colors[][COLOR_MAXLEN] = { "\033[32m", /* WT_STATUS_UPDATED: green */ "\033[31m", /* WT_STATUS_CHANGED: red */ "\033[31m", /* WT_STATUS_UNTRACKED: red */ + "\033[31m", /* WT_STATUS_NOBRANCH: red */ }; static const char use_add_msg[] = @@ -38,6 +39,8 @@ static int parse_status_slot(const char *var, int offset) return WT_STATUS_CHANGED; if (!strcasecmp(var+offset, "untracked")) return WT_STATUS_UNTRACKED; + if (!strcasecmp(var+offset, "nobranch")) + return WT_STATUS_NOBRANCH; die("bad config variable '%s'", var); } @@ -314,8 +317,9 @@ static void wt_status_print_verbose(struct wt_status *s) void wt_status_print(struct wt_status *s) { unsigned char sha1[20]; - s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0; + const char *branch_color = color(WT_STATUS_HEADER); + s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0; if (s->branch) { const char *on_what = "On branch "; const char *branch_name = s->branch; @@ -323,10 +327,11 @@ void wt_status_print(struct wt_status *s) branch_name += 11; else if (!strcmp(branch_name, "HEAD")) { branch_name = ""; + branch_color = color(WT_STATUS_NOBRANCH); on_what = "Not currently on any branch."; } - color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), - "# %s%s", on_what, branch_name); + color_fprintf(s->fp, color(WT_STATUS_HEADER), "# "); + color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name); } if (s->is_initial) { diff --git a/wt-status.h b/wt-status.h index 7d61410b17..f0675fdff3 100644 --- a/wt-status.h +++ b/wt-status.h @@ -8,6 +8,7 @@ enum color_wt_status { WT_STATUS_UPDATED, WT_STATUS_CHANGED, WT_STATUS_UNTRACKED, + WT_STATUS_NOBRANCH, }; struct wt_status { From 97e435adad0e0bf30e588b2ad7f206a20c734c06 Mon Sep 17 00:00:00 2001 From: Adam Roben Date: Fri, 23 May 2008 16:19:36 +0200 Subject: [PATCH 100/202] Move git-hash-object tests from t5303 to t1007 This is a more appropriate location according to t/README. Signed-off-by: Adam Roben Signed-off-by: Junio C Hamano --- t/{t5303-hash-object.sh => t1007-hash-object.sh} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename t/{t5303-hash-object.sh => t1007-hash-object.sh} (100%) diff --git a/t/t5303-hash-object.sh b/t/t1007-hash-object.sh similarity index 100% rename from t/t5303-hash-object.sh rename to t/t1007-hash-object.sh From 3ea5a1b33d99b6c4f5e745c0dd017307c58cff31 Mon Sep 17 00:00:00 2001 From: Adam Roben Date: Fri, 23 May 2008 16:19:37 +0200 Subject: [PATCH 101/202] Add more tests for git hash-object Signed-off-by: Adam Roben Signed-off-by: Junio C Hamano --- t/t1007-hash-object.sh | 116 ++++++++++++++++++++++++++++++++--------- 1 file changed, 91 insertions(+), 25 deletions(-) diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh index 543c0784bd..2019ea7891 100755 --- a/t/t1007-hash-object.sh +++ b/t/t1007-hash-object.sh @@ -4,32 +4,98 @@ test_description=git-hash-object . ./test-lib.sh -test_expect_success \ - 'git hash-object -w --stdin saves the object' \ - 'obname=$(echo foo | git hash-object -w --stdin) && - obpath=$(echo $obname | sed -e "s/\(..\)/\1\//") && - test -r .git/objects/"$obpath" && - rm -f .git/objects/"$obpath"' - -test_expect_success \ - 'git hash-object --stdin -w saves the object' \ - 'obname=$(echo foo | git hash-object --stdin -w) && - obpath=$(echo $obname | sed -e "s/\(..\)/\1\//") && - test -r .git/objects/"$obpath" && - rm -f .git/objects/"$obpath"' +echo_without_newline() { + printf '%s' "$*" +} -test_expect_success \ - 'git hash-object --stdin file1 file1 && - obname0=$(echo bar | git hash-object --stdin) && - obname1=$(git hash-object file1) && - obname0new=$(echo bar | git hash-object --stdin file1 | sed -n -e 1p) && - obname1new=$(echo bar | git hash-object --stdin file1 | sed -n -e 2p) && - test "$obname0" = "$obname0new" && - test "$obname1" = "$obname1new"' +test_blob_does_not_exist() { + test_expect_success 'blob does not exist in database' " + test_must_fail git cat-file blob $1 + " +} -test_expect_success \ - 'git hash-object refuses multiple --stdin arguments' \ - '! git hash-object --stdin --stdin < file1' +test_blob_exists() { + test_expect_success 'blob exists in database' " + git cat-file blob $1 + " +} + +hello_content="Hello World" +hello_sha1=5e1c309dae7f45e0f39b1bf3ac3cd9db12e7d689 + +example_content="This is an example" +example_sha1=ddd3f836d3e3fbb7ae289aa9ae83536f76956399 + +setup_repo() { + echo_without_newline "$hello_content" > hello + echo_without_newline "$example_content" > example +} + +test_repo=test +push_repo() { + test_create_repo $test_repo + cd $test_repo + + setup_repo +} + +pop_repo() { + cd .. + rm -rf $test_repo +} + +setup_repo + +# Argument checking + +test_expect_success "multiple '--stdin's are rejected" ' + test_must_fail git hash-object --stdin --stdin < example +' + +# Behavior + +push_repo + +test_expect_success 'hash a file' ' + test $hello_sha1 = $(git hash-object hello) +' + +test_blob_does_not_exist $hello_sha1 + +test_expect_success 'hash from stdin' ' + test $example_sha1 = $(git hash-object --stdin < example) +' + +test_blob_does_not_exist $example_sha1 + +test_expect_success 'hash a file and write to database' ' + test $hello_sha1 = $(git hash-object -w hello) +' + +test_blob_exists $hello_sha1 + +test_expect_success 'git hash-object --stdin file1 file1 && + obname0=$(echo bar | git hash-object --stdin) && + obname1=$(git hash-object file1) && + obname0new=$(echo bar | git hash-object --stdin file1 | sed -n -e 1p) && + obname1new=$(echo bar | git hash-object --stdin file1 | sed -n -e 2p) && + test "$obname0" = "$obname0new" && + test "$obname1" = "$obname1new" +' + +pop_repo + +for args in "-w --stdin" "--stdin -w"; do + push_repo + + test_expect_success "hash from stdin and write to database ($args)" ' + test $example_sha1 = $(git hash-object $args < example) + ' + + test_blob_exists $example_sha1 + + pop_repo +done test_done From d8ee4832509dd2d7448a49920f5cba2fc979283d Mon Sep 17 00:00:00 2001 From: Adam Roben Date: Fri, 23 May 2008 16:19:38 +0200 Subject: [PATCH 102/202] git-hash-object: Add --stdin-paths option This allows multiple paths to be specified on stdin. Signed-off-by: Adam Roben Signed-off-by: Junio C Hamano --- Documentation/git-hash-object.txt | 5 +++- hash-object.c | 45 ++++++++++++++++++++++++++++++- t/t1007-hash-object.sh | 32 ++++++++++++++++++++++ 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/Documentation/git-hash-object.txt b/Documentation/git-hash-object.txt index 33030c022f..99a21434b5 100644 --- a/Documentation/git-hash-object.txt +++ b/Documentation/git-hash-object.txt @@ -8,7 +8,7 @@ git-hash-object - Compute object ID and optionally creates a blob from a file SYNOPSIS -------- -'git-hash-object' [-t ] [-w] [--stdin] [--] ... +'git-hash-object' [-t ] [-w] [--stdin | --stdin-paths] [--] ... DESCRIPTION ----------- @@ -32,6 +32,9 @@ OPTIONS --stdin:: Read the object from standard input instead of from a file. +--stdin-paths:: + Read file names from stdin instead of from the command-line. + Author ------ Written by Junio C Hamano diff --git a/hash-object.c b/hash-object.c index 61e7160b36..0a7ac2fe2a 100644 --- a/hash-object.c +++ b/hash-object.c @@ -6,6 +6,7 @@ */ #include "cache.h" #include "blob.h" +#include "quote.h" static void hash_object(const char *path, enum object_type type, int write_object) { @@ -20,6 +21,7 @@ static void hash_object(const char *path, enum object_type type, int write_objec ? "Unable to add %s to database" : "Unable to hash %s", path); printf("%s\n", sha1_to_hex(sha1)); + maybe_flush_or_die(stdout, "hash to stdout"); } static void hash_stdin(const char *type, int write_object) @@ -30,8 +32,27 @@ static void hash_stdin(const char *type, int write_object) printf("%s\n", sha1_to_hex(sha1)); } +static void hash_stdin_paths(const char *type, int write_objects) +{ + struct strbuf buf, nbuf; + + strbuf_init(&buf, 0); + strbuf_init(&nbuf, 0); + while (strbuf_getline(&buf, stdin, '\n') != EOF) { + if (buf.buf[0] == '"') { + strbuf_reset(&nbuf); + if (unquote_c_style(&nbuf, buf.buf, NULL)) + die("line is badly quoted"); + strbuf_swap(&buf, &nbuf); + } + hash_object(buf.buf, type_from_string(type), write_objects); + } + strbuf_release(&buf); + strbuf_release(&nbuf); +} + static const char hash_object_usage[] = -"git-hash-object [-t ] [-w] [--stdin] ..."; +"git-hash-object [ [-t ] [-w] [--stdin] ... | --stdin-paths < ]"; int main(int argc, char **argv) { @@ -42,6 +63,7 @@ int main(int argc, char **argv) int prefix_length = -1; int no_more_flags = 0; int hashstdin = 0; + int stdin_paths = 0; git_config(git_default_config); @@ -65,7 +87,19 @@ int main(int argc, char **argv) } else if (!strcmp(argv[i], "--help")) usage(hash_object_usage); + else if (!strcmp(argv[i], "--stdin-paths")) { + if (hashstdin) { + error("Can't use --stdin-paths with --stdin"); + usage(hash_object_usage); + } + stdin_paths = 1; + + } else if (!strcmp(argv[i], "--stdin")) { + if (stdin_paths) { + error("Can't use %s with --stdin-paths", argv[i]); + usage(hash_object_usage); + } if (hashstdin) die("Multiple --stdin arguments are not supported"); hashstdin = 1; @@ -76,6 +110,11 @@ int main(int argc, char **argv) else { const char *arg = argv[i]; + if (stdin_paths) { + error("Can't specify files (such as \"%s\") with --stdin-paths", arg); + usage(hash_object_usage); + } + if (hashstdin) { hash_stdin(type, write_object); hashstdin = 0; @@ -87,6 +126,10 @@ int main(int argc, char **argv) no_more_flags = 1; } } + + if (stdin_paths) + hash_stdin_paths(type, write_object); + if (hashstdin) hash_stdin(type, write_object); return 0; diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh index 2019ea7891..05262954ab 100755 --- a/t/t1007-hash-object.sh +++ b/t/t1007-hash-object.sh @@ -52,6 +52,15 @@ test_expect_success "multiple '--stdin's are rejected" ' test_must_fail git hash-object --stdin --stdin < example ' +test_expect_success "Can't use --stdin and --stdin-paths together" ' + test_must_fail git hash-object --stdin --stdin-paths && + test_must_fail git hash-object --stdin-paths --stdin +' + +test_expect_success "Can't pass filenames as arguments with --stdin-paths" ' + test_must_fail git hash-object --stdin-paths hello < example +' + # Behavior push_repo @@ -98,4 +107,27 @@ for args in "-w --stdin" "--stdin -w"; do pop_repo done +filenames="hello +example" + +sha1s="$hello_sha1 +$example_sha1" + +test_expect_success "hash two files with names on stdin" ' + test "$sha1s" = "$(echo_without_newline "$filenames" | git hash-object --stdin-paths)" +' + +for args in "-w --stdin-paths" "--stdin-paths -w"; do + push_repo + + test_expect_success "hash two files with names on stdin and write to database ($args)" ' + test "$sha1s" = "$(echo_without_newline "$filenames" | git hash-object $args)" + ' + + test_blob_exists $hello_sha1 + test_blob_exists $example_sha1 + + pop_repo +done + test_done From d1a29af98e61de72841657bcaa86a93b38cda1c2 Mon Sep 17 00:00:00 2001 From: Adam Roben Date: Fri, 23 May 2008 16:19:39 +0200 Subject: [PATCH 103/202] Git.pm: Add command_bidi_pipe and command_close_bidi_pipe command_bidi_pipe hands back the stdin and stdout file handles from the executed command. command_close_bidi_pipe closes these handles and terminates the process. Signed-off-by: Adam Roben Signed-off-by: Junio C Hamano --- perl/Git.pm | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/perl/Git.pm b/perl/Git.pm index 2e7f896bae..d766974c96 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -51,6 +51,7 @@ require Exporter; # Methods which can be called as standalone functions as well: @EXPORT_OK = qw(command command_oneline command_noisy command_output_pipe command_input_pipe command_close_pipe + command_bidi_pipe command_close_bidi_pipe version exec_path hash_object git_cmd_try); @@ -92,6 +93,7 @@ increate nonwithstanding). use Carp qw(carp croak); # but croak is bad - throw instead use Error qw(:try); use Cwd qw(abs_path); +use IPC::Open2 qw(open2); } @@ -375,6 +377,60 @@ sub command_close_pipe { _cmd_close($fh, $ctx); } +=item command_bidi_pipe ( COMMAND [, ARGUMENTS... ] ) + +Execute the given C in the same way as command_output_pipe() +does but return both an input pipe filehandle and an output pipe filehandle. + +The function will return return C<($pid, $pipe_in, $pipe_out, $ctx)>. +See C for details. + +=cut + +sub command_bidi_pipe { + my ($pid, $in, $out); + $pid = open2($in, $out, 'git', @_); + return ($pid, $in, $out, join(' ', @_)); +} + +=item command_close_bidi_pipe ( PID, PIPE_IN, PIPE_OUT [, CTX] ) + +Close the C and C as returned from C, +checking whether the command finished successfully. The optional C +argument is required if you want to see the command name in the error message, +and it is the fourth value returned by C. The call idiom +is: + + my ($pid, $in, $out, $ctx) = $r->command_bidi_pipe('cat-file --batch-check'); + print "000000000\n" $out; + while (<$in>) { ... } + $r->command_close_bidi_pipe($pid, $in, $out, $ctx); + +Note that you should not rely on whatever actually is in C; +currently it is simply the command name but in future the context might +have more complicated structure. + +=cut + +sub command_close_bidi_pipe { + my ($pid, $in, $out, $ctx) = @_; + foreach my $fh ($in, $out) { + unless (close $fh) { + if ($!) { + carp "error closing pipe: $!"; + } elsif ($? >> 8) { + throw Git::Error::Command($ctx, $? >>8); + } + } + } + + waitpid $pid, 0; + + if ($? >> 8) { + throw Git::Error::Command($ctx, $? >>8); + } +} + =item command_noisy ( COMMAND [, ARGUMENTS... ] ) From 7182530d8cad5ffc396bed5d37f97cfb14b7e599 Mon Sep 17 00:00:00 2001 From: Adam Roben Date: Fri, 23 May 2008 16:19:40 +0200 Subject: [PATCH 104/202] Git.pm: Add hash_and_insert_object and cat_blob These functions are more efficient ways of executing `git hash-object -w` and `git cat-file blob` when you are dealing with many files/objects. Signed-off-by: Adam Roben Signed-off-by: Junio C Hamano --- perl/Git.pm | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 150 insertions(+), 2 deletions(-) diff --git a/perl/Git.pm b/perl/Git.pm index d766974c96..6ba8ee5c0d 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -39,6 +39,10 @@ $VERSION = '0.01'; my $lastrev = $repo->command_oneline( [ 'rev-list', '--all' ], STDERR => 0 ); + my $sha1 = $repo->hash_and_insert_object('file.txt'); + my $tempfile = tempfile(); + my $size = $repo->cat_blob($sha1, $tempfile); + =cut @@ -218,7 +222,6 @@ sub repository { bless $self, $class; } - =back =head1 METHODS @@ -734,6 +737,147 @@ sub hash_object { } +=item hash_and_insert_object ( FILENAME ) + +Compute the SHA1 object id of the given C and add the object to the +object database. + +The function returns the SHA1 hash. + +=cut + +# TODO: Support for passing FILEHANDLE instead of FILENAME +sub hash_and_insert_object { + my ($self, $filename) = @_; + + carp "Bad filename \"$filename\"" if $filename =~ /[\r\n]/; + + $self->_open_hash_and_insert_object_if_needed(); + my ($in, $out) = ($self->{hash_object_in}, $self->{hash_object_out}); + + unless (print $out $filename, "\n") { + $self->_close_hash_and_insert_object(); + throw Error::Simple("out pipe went bad"); + } + + chomp(my $hash = <$in>); + unless (defined($hash)) { + $self->_close_hash_and_insert_object(); + throw Error::Simple("in pipe went bad"); + } + + return $hash; +} + +sub _open_hash_and_insert_object_if_needed { + my ($self) = @_; + + return if defined($self->{hash_object_pid}); + + ($self->{hash_object_pid}, $self->{hash_object_in}, + $self->{hash_object_out}, $self->{hash_object_ctx}) = + command_bidi_pipe(qw(hash-object -w --stdin-paths)); +} + +sub _close_hash_and_insert_object { + my ($self) = @_; + + return unless defined($self->{hash_object_pid}); + + my @vars = map { 'hash_object_' . $_ } qw(pid in out ctx); + + command_close_bidi_pipe($self->{@vars}); + delete $self->{@vars}; +} + +=item cat_blob ( SHA1, FILEHANDLE ) + +Prints the contents of the blob identified by C to C and +returns the number of bytes printed. + +=cut + +sub cat_blob { + my ($self, $sha1, $fh) = @_; + + $self->_open_cat_blob_if_needed(); + my ($in, $out) = ($self->{cat_blob_in}, $self->{cat_blob_out}); + + unless (print $out $sha1, "\n") { + $self->_close_cat_blob(); + throw Error::Simple("out pipe went bad"); + } + + my $description = <$in>; + if ($description =~ / missing$/) { + carp "$sha1 doesn't exist in the repository"; + return 0; + } + + if ($description !~ /^[0-9a-fA-F]{40} \S+ (\d+)$/) { + carp "Unexpected result returned from git cat-file"; + return 0; + } + + my $size = $1; + + my $blob; + my $bytesRead = 0; + + while (1) { + my $bytesLeft = $size - $bytesRead; + last unless $bytesLeft; + + my $bytesToRead = $bytesLeft < 1024 ? $bytesLeft : 1024; + my $read = read($in, $blob, $bytesToRead, $bytesRead); + unless (defined($read)) { + $self->_close_cat_blob(); + throw Error::Simple("in pipe went bad"); + } + + $bytesRead += $read; + } + + # Skip past the trailing newline. + my $newline; + my $read = read($in, $newline, 1); + unless (defined($read)) { + $self->_close_cat_blob(); + throw Error::Simple("in pipe went bad"); + } + unless ($read == 1 && $newline eq "\n") { + $self->_close_cat_blob(); + throw Error::Simple("didn't find newline after blob"); + } + + unless (print $fh $blob) { + $self->_close_cat_blob(); + throw Error::Simple("couldn't write to passed in filehandle"); + } + + return $size; +} + +sub _open_cat_blob_if_needed { + my ($self) = @_; + + return if defined($self->{cat_blob_pid}); + + ($self->{cat_blob_pid}, $self->{cat_blob_in}, + $self->{cat_blob_out}, $self->{cat_blob_ctx}) = + command_bidi_pipe(qw(cat-file --batch)); +} + +sub _close_cat_blob { + my ($self) = @_; + + return unless defined($self->{cat_blob_pid}); + + my @vars = map { 'cat_blob_' . $_ } qw(pid in out ctx); + + command_close_bidi_pipe($self->{@vars}); + delete $self->{@vars}; +} =back @@ -951,7 +1095,11 @@ sub _cmd_close { } -sub DESTROY { } +sub DESTROY { + my ($self) = @_; + $self->_close_hash_and_insert_object(); + $self->_close_cat_blob(); +} # Pipe implementation for ActiveState Perl. From ffe256f9bac8a40ff751a9341a5869d98f72c285 Mon Sep 17 00:00:00 2001 From: Adam Roben Date: Fri, 23 May 2008 16:19:41 +0200 Subject: [PATCH 105/202] git-svn: Speed up fetch We were spending a lot of time forking/execing git-cat-file and git-hash-object. We now maintain a global Git repository object in order to use Git.pm's more efficient hash_and_insert_object and cat_blob methods. Signed-off-by: Adam Roben Signed-off-by: Junio C Hamano --- git-svn.perl | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/git-svn.perl b/git-svn.perl index b70f8efaaa..33e9266fe6 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -4,7 +4,7 @@ use warnings; use strict; use vars qw/ $AUTHOR $VERSION - $sha1 $sha1_short $_revision + $sha1 $sha1_short $_revision $_repository $_q $_authors %users/; $AUTHOR = 'Eric Wong '; $VERSION = '@@GIT_VERSION@@'; @@ -220,6 +220,7 @@ unless ($cmd && $cmd =~ /(?:clone|init|multi-init)$/) { } $ENV{GIT_DIR} = $git_dir; } + $_repository = Git->repository(Repository => $ENV{GIT_DIR}); } my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd); @@ -301,6 +302,7 @@ sub do_git_init_db { } } command_noisy(@init_db); + $_repository = Git->repository(Repository => ".git"); } my $set; my $pfx = "svn-remote.$Git::SVN::default_repo_id"; @@ -317,6 +319,7 @@ sub init_subdir { mkpath([$repo_path]) unless -d $repo_path; chdir $repo_path or die "Couldn't chdir to $repo_path: $!\n"; $ENV{GIT_DIR} = '.git'; + $_repository = Git->repository(Repository => $ENV{GIT_DIR}); } sub cmd_clone { @@ -3017,6 +3020,7 @@ use vars qw/@ISA/; use strict; use warnings; use Carp qw/croak/; +use File::Temp qw/tempfile/; use IO::File qw//; # file baton members: path, mode_a, mode_b, pool, fh, blob, base @@ -3172,14 +3176,9 @@ sub apply_textdelta { my $base = IO::File->new_tmpfile; $base->autoflush(1); if ($fb->{blob}) { - defined (my $pid = fork) or croak $!; - if (!$pid) { - open STDOUT, '>&', $base or croak $!; - print STDOUT 'link ' if ($fb->{mode_a} == 120000); - exec qw/git-cat-file blob/, $fb->{blob} or croak $!; - } - waitpid $pid, 0; - croak $? if $?; + print $base 'link ' if ($fb->{mode_a} == 120000); + my $size = $::_repository->cat_blob($fb->{blob}, $base); + die "Failed to read object $fb->{blob}" unless $size; if (defined $exp) { seek $base, 0, 0 or croak $!; @@ -3220,14 +3219,18 @@ sub close_file { sysseek($fh, 0, 0) or croak $!; } } - defined(my $pid = open my $out,'-|') or die "Can't fork: $!\n"; - if (!$pid) { - open STDIN, '<&', $fh or croak $!; - exec qw/git-hash-object -w --stdin/ or croak $!; + + my ($tmp_fh, $tmp_filename) = File::Temp::tempfile(UNLINK => 1); + my $result; + while ($result = sysread($fh, my $string, 1024)) { + syswrite($tmp_fh, $string, $result); } - chomp($hash = do { local $/; <$out> }); - close $out or croak $!; + defined $result or croak $!; + close $tmp_fh or croak $!; + close $fh or croak $!; + + $hash = $::_repository->hash_and_insert_object($tmp_filename); $hash =~ /^[a-f\d]{40}$/ or die "not a sha1: $hash\n"; close $fb->{base} or croak $!; } else { @@ -3553,13 +3556,8 @@ sub chg_file { } elsif ($m->{mode_a} =~ /^120/ && $m->{mode_b} !~ /^120/) { $self->change_file_prop($fbat,'svn:special',undef); } - defined(my $pid = fork) or croak $!; - if (!$pid) { - open STDOUT, '>&', $fh or croak $!; - exec qw/git-cat-file blob/, $m->{sha1_b} or croak $!; - } - waitpid $pid, 0; - croak $? if $?; + my $size = $::_repository->cat_blob($m->{sha1_b}, $fh); + croak "Failed to read object $m->{sha1_b}" unless $size; $fh->flush == 0 or croak $!; seek $fh, 0, 0 or croak $!; From 15d8e5651948e3914f4b765d7d1dc1bc2f5c4073 Mon Sep 17 00:00:00 2001 From: Michele Ballabio Date: Fri, 23 May 2008 16:19:42 +0200 Subject: [PATCH 106/202] builtin-cat-file.c: use parse_options() This simplifies the option parsing. Signed-off-by: Michele Ballabio Signed-off-by: Junio C Hamano --- builtin-cat-file.c | 119 ++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 73 deletions(-) diff --git a/builtin-cat-file.c b/builtin-cat-file.c index b4d0c2545f..5ef15a4fa9 100644 --- a/builtin-cat-file.c +++ b/builtin-cat-file.c @@ -8,6 +8,10 @@ #include "tag.h" #include "tree.h" #include "builtin.h" +#include "parse-options.h" + +#define BATCH 1 +#define BATCH_CHECK 2 static void pprint_tag(const unsigned char *sha1, const char *buf, unsigned long size) { @@ -158,7 +162,7 @@ static int batch_one_object(const char *obj_name, int print_contents) return 0; } - if (print_contents) + if (print_contents == BATCH) contents = read_sha1_file(sha1, &type, &size); else type = sha1_object_info(sha1, &size); @@ -169,7 +173,7 @@ static int batch_one_object(const char *obj_name, int print_contents) printf("%s %s %lu\n", sha1_to_hex(sha1), typename(type), size); fflush(stdout); - if (print_contents) { + if (print_contents == BATCH) { write_or_die(1, contents, size); printf("\n"); fflush(stdout); @@ -192,89 +196,58 @@ static int batch_objects(int print_contents) return 0; } -static const char cat_file_usage[] = "git-cat-file [ [-t|-s|-e|-p|] | [--batch|--batch-check] < ]"; +static const char * const cat_file_usage[] = { + "git-cat-file [-t|-s|-e|-p|] ", + "git-cat-file [--batch|--batch-check] < ", + NULL +}; int cmd_cat_file(int argc, const char **argv, const char *prefix) { - int i, opt = 0, batch = 0, batch_check = 0; + int opt = 0, batch = 0; const char *exp_type = NULL, *obj_name = NULL; + const struct option options[] = { + OPT_GROUP(" can be one of: blob, tree, commit, tag"), + OPT_SET_INT('t', NULL, &opt, "show object type", 't'), + OPT_SET_INT('s', NULL, &opt, "show object size", 's'), + OPT_SET_INT('e', NULL, &opt, + "exit with zero when there's no error", 'e'), + OPT_SET_INT('p', NULL, &opt, "pretty-print object's content", 'p'), + OPT_SET_INT(0, "batch", &batch, + "show info and content of objects feeded on stdin", BATCH), + OPT_SET_INT(0, "batch-check", &batch, + "show info about objects feeded on stdin", + BATCH_CHECK), + OPT_END() + }; + git_config(git_default_config); - for (i = 1; i < argc; ++i) { - const char *arg = argv[i]; - int is_batch = 0, is_batch_check = 0; + if (argc != 3 && argc != 2) + usage_with_options(cat_file_usage, options); - is_batch = !strcmp(arg, "--batch"); - if (!is_batch) - is_batch_check = !strcmp(arg, "--batch-check"); + argc = parse_options(argc, argv, options, cat_file_usage, 0); - if (is_batch || is_batch_check) { - if (opt) { - error("git-cat-file: Can't use %s with -%c", arg, opt); - usage(cat_file_usage); - } else if (exp_type) { - error("git-cat-file: Can't use %s when a type (\"%s\") is specified", arg, exp_type); - usage(cat_file_usage); - } else if (obj_name) { - error("git-cat-file: Can't use %s when an object (\"%s\") is specified", arg, obj_name); - usage(cat_file_usage); - } - - if ((is_batch && batch_check) || (is_batch_check && batch)) { - error("git-cat-file: Can't use %s with %s", arg, is_batch ? "--batch-check" : "--batch"); - usage(cat_file_usage); - } - - if (is_batch) - batch = 1; - else - batch_check = 1; - - continue; - } - - if (!strcmp(arg, "-t") || !strcmp(arg, "-s") || !strcmp(arg, "-e") || !strcmp(arg, "-p")) { - if (batch || batch_check) { - error("git-cat-file: Can't use %s with %s", arg, batch ? "--batch" : "--batch-check"); - usage(cat_file_usage); - } - - exp_type = arg; - opt = exp_type[1]; - continue; - } - - if (arg[0] == '-') - usage(cat_file_usage); - - if (!exp_type) { - if (batch || batch_check) { - error("git-cat-file: Can't specify a type (\"%s\") with %s", arg, batch ? "--batch" : "--batch-check"); - usage(cat_file_usage); - } - - exp_type = arg; - continue; - } - - if (obj_name) - usage(cat_file_usage); - - // We should have hit one of the earlier if (batch || batch_check) cases before - // getting here. - assert(!batch); - assert(!batch_check); - - obj_name = arg; - break; + if (opt) { + if (argc == 1) + obj_name = argv[0]; + else + usage_with_options(cat_file_usage, options); + } + if (!opt && !batch) { + if (argc == 2) { + exp_type = argv[0]; + obj_name = argv[1]; + } else + usage_with_options(cat_file_usage, options); + } + if (batch && (opt || argc)) { + usage_with_options(cat_file_usage, options); } - if (batch || batch_check) + if (batch) return batch_objects(batch); - if (!exp_type || !obj_name) - usage(cat_file_usage); - return cat_one_file(opt, exp_type, obj_name); } From 6c41e21d48c369f398ee2e24085e618b55ed916d Mon Sep 17 00:00:00 2001 From: Michele Ballabio Date: Fri, 23 May 2008 16:19:43 +0200 Subject: [PATCH 107/202] change quoting in test t1006-cat-file.sh Signed-off-by: Michele Ballabio Signed-off-by: Junio C Hamano --- t/t1006-cat-file.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh index d5696765b1..cb1fbe5820 100755 --- a/t/t1006-cat-file.sh +++ b/t/t1006-cat-file.sh @@ -199,9 +199,9 @@ $tag_content deadbeef missing missing" -test_expect_success \ - "--batch with multiple sha1s gives correct format" \ - "test \"\$(maybe_remove_timestamp \"$batch_output\" 1)\" = \"\$(maybe_remove_timestamp \"\$(echo_without_newline \"$batch_input\" | git cat-file --batch)\" 1)\"" +test_expect_success '--batch with multiple sha1s gives correct format' ' + test "$(maybe_remove_timestamp "$batch_output" 1)" = "$(maybe_remove_timestamp "$(echo_without_newline "$batch_input" | git cat-file --batch)" 1)" +' batch_check_input="$hello_sha1 $tree_sha1 From 205ffa94be8f6492eef4bdaa3315e7fdc7e64e0b Mon Sep 17 00:00:00 2001 From: Gustaf Hendeby Date: Thu, 22 May 2008 23:59:42 +0200 Subject: [PATCH 108/202] Make git add -n and git -u -n output consistent Output format from "git add -n $path" lists path to blobs that are going to be added on a single line, separated with SP. On the other hand, the suggested "git add -u -n" shows one path per line, like "add ''\n". Of course, these two are inconsistent. Plain "git add -n" can afford to only say names of paths, as all it does is to add (update). However, "git add -u" needs to be able to express "remove" somehow. So if we need to have them formatted the same way, we need to unify with the "git add -n -u" format. Incidentally, this is consistent with how 'update-index' says it. This changes the output from "git add -n $paths" but as a general principle, output from Porcelain commands is a fair game for improvements and not for script consumption. Signed-off-by: Gustaf Hendeby Signed-off-by: Junio C Hamano --- builtin-add.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index 05af57f6ec..8f81f3fbfb 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -196,6 +196,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) int i, newfd; const char **pathspec; struct dir_struct dir; + int flags; argc = parse_options(argc, argv, builtin_add_options, builtin_add_usage, 0); @@ -208,11 +209,11 @@ int cmd_add(int argc, const char **argv, const char *prefix) newfd = hold_locked_index(&lock_file, 1); + flags = ((verbose ? ADD_CACHE_VERBOSE : 0) | + (show_only ? ADD_CACHE_PRETEND : 0)); + if (take_worktree_changes) { const char **pathspec; - int flags = ((verbose ? ADD_CACHE_VERBOSE : 0) | - (show_only ? ADD_CACHE_PRETEND : 0)); - if (read_cache() < 0) die("index file corrupt"); pathspec = get_pathspec(prefix, argv); @@ -234,17 +235,6 @@ int cmd_add(int argc, const char **argv, const char *prefix) fill_directory(&dir, pathspec, ignored_too); - if (show_only) { - const char *sep = "", *eof = ""; - for (i = 0; i < dir.nr; i++) { - printf("%s%s", sep, dir.entries[i]->name); - sep = " "; - eof = "\n"; - } - fputs(eof, stdout); - return 0; - } - if (read_cache() < 0) die("index file corrupt"); @@ -258,7 +248,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) } for (i = 0; i < dir.nr; i++) - add_file_to_cache(dir.entries[i]->name, verbose ? ADD_CACHE_VERBOSE : 0); + add_file_to_cache(dir.entries[i]->name, flags); finish: if (active_cache_changed) { From e00f3790b88ce61f1bdc863011a122b98b43197e Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 23 May 2008 16:13:05 +0200 Subject: [PATCH 109/202] rev-parse --symbolic-full-name: don't print '^' if SHA1 is not a ref The intention of --symbolic-full-name is to not print anything if a revision is not an exact ref. But this command: $ git-rev-parse --symbolic-full-name --not master~1 still emitted a sole '^' to stdout (provided that there's no other ref at master~1). This fixes it. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- builtin-rev-parse.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index 90dbb9d7c1..1ae086ad17 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -96,6 +96,14 @@ static void show(const char *arg) puts(arg); } +/* Like show(), but with a negation prefix according to type */ +static void show_with_type(int type, const char *arg) +{ + if (type != show_type) + putchar('^'); + show(arg); +} + /* Output a revision, only if filter allows it */ static void show_rev(int type, const unsigned char *sha1, const char *name) { @@ -104,8 +112,6 @@ static void show_rev(int type, const unsigned char *sha1, const char *name) def = NULL; revs_count++; - if (type != show_type) - putchar('^'); if (symbolic && name) { if (symbolic == SHOW_SYMBOLIC_FULL) { unsigned char discard[20]; @@ -122,20 +128,20 @@ static void show_rev(int type, const unsigned char *sha1, const char *name) */ break; case 1: /* happy */ - show(full); + show_with_type(type, full); break; default: /* ambiguous */ error("refname '%s' is ambiguous", name); break; } } else { - show(name); + show_with_type(type, name); } } else if (abbrev) - show(find_unique_abbrev(sha1, abbrev)); + show_with_type(type, find_unique_abbrev(sha1, abbrev)); else - show(sha1_to_hex(sha1)); + show_with_type(type, sha1_to_hex(sha1)); } /* Output a flag, only if filter allows it. */ From dd0ffd5b31a672db90d8b775988d55829e6f5f9f Mon Sep 17 00:00:00 2001 From: Heikki Orsila Date: Thu, 22 May 2008 18:24:07 +0300 Subject: [PATCH 110/202] Add log.date config variable log.date config variable sets the default date-time mode for the log command. Setting log.date value is similar to using git log's --date option. Signed-off-by: Heikki Orsila Signed-off-by: Junio C Hamano --- Documentation/config.txt | 6 ++++++ Documentation/rev-list-options.txt | 3 ++- builtin-log.c | 10 ++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 554977b895..002a066893 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -817,6 +817,12 @@ instaweb.port:: The port number to bind the gitweb httpd to. See linkgit:git-instaweb[1]. +log.date:: + Set default date-time mode for the log command. Setting log.date + value is similar to using git log's --date option. The value is one of + following alternatives: {relative,local,default,iso,rfc,short}. + See linkgit:git-log[1]. + log.showroot:: If true, the initial commit will be shown as a big creation event. This is equivalent to a diff against an empty tree. diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index ce6a1017a3..3558348b72 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -16,7 +16,8 @@ include::pretty-options.txt[] --date={relative,local,default,iso,rfc}:: Only takes effect for dates shown in human-readable format, such - as when using "--pretty". + as when using "--pretty". `log.date` config variable sets a default + value for log command's --date option. + `--date=relative` shows dates relative to the current time, e.g. "2 hours ago". diff --git a/builtin-log.c b/builtin-log.c index 9d046b2e03..543855b7ad 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -18,6 +18,9 @@ #include "run-command.h" #include "shortlog.h" +/* Set a default date-time format for git log ("log.date" config variable) */ +static const char *default_date_mode = NULL; + static int default_show_root = 1; static const char *fmt_patch_subject_prefix = "PATCH"; static const char *fmt_pretty; @@ -61,7 +64,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix, DIFF_OPT_SET(&rev->diffopt, RECURSIVE); rev->show_root_diff = default_show_root; rev->subject_prefix = fmt_patch_subject_prefix; + + if (default_date_mode) + rev->date_mode = parse_date_format(default_date_mode); + argc = setup_revisions(argc, argv, rev, "HEAD"); + if (rev->diffopt.pickaxe || rev->diffopt.filter) rev->always_show_header = 0; if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) { @@ -232,6 +240,8 @@ static int git_log_config(const char *var, const char *value) fmt_patch_subject_prefix = xstrdup(value); return 0; } + if (!strcmp(var, "log.date")) + return git_config_string(&default_date_mode, var, value); if (!strcmp(var, "log.showroot")) { default_show_root = git_config_bool(var, value); return 0; From 3af828634fa5bdbca1b2061a81df8b3fa73b0d34 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 23 May 2008 22:28:56 -0700 Subject: [PATCH 111/202] tests: do not use implicit "git diff --no-index" As a general principle, we should not use "git diff" to validate the results of what git command that is being tested has done. We would not know if we are testing the command in question, or locating a bug in the cute hack of "git diff --no-index". Rather use test_cmp for that purpose. Signed-off-by: Junio C Hamano --- t/diff-lib.sh | 6 +- t/t0030-stripspace.sh | 156 ++++++++++----------- t/t0040-parse-options.sh | 18 +-- t/t1000-read-tree-m-3way.sh | 2 +- t/t1001-read-tree-m-2way.sh | 26 ++-- t/t1002-read-tree-m-u-2way.sh | 2 +- t/t1300-repo-config.sh | 8 +- t/t1303-wacky-config.sh | 2 +- t/t1502-rev-parse-parseopt.sh | 2 +- t/t3001-ls-files-others-exclude.sh | 4 +- t/t3002-ls-files-dashpath.sh | 10 +- t/t3030-merge-recursive.sh | 42 +++--- t/t3040-subprojects-basic.sh | 4 +- t/t3100-ls-tree-restrict.sh | 2 +- t/t3101-ls-tree-dirname.sh | 2 +- t/t3300-funny-names.sh | 30 ++--- t/t3900-i18n-commit.sh | 2 +- t/t4006-diff-mode.sh | 2 +- t/t4013-diff-various.sh | 2 +- t/t4014-format-patch.sh | 2 +- t/t4015-diff-whitespace.sh | 12 +- t/t4016-diff-quote.sh | 4 +- t/t4018-diff-funcname.sh | 8 +- t/t4100-apply-stat.sh | 14 +- t/t4104-apply-boundary.sh | 4 +- t/t4115-apply-symlink.sh | 4 +- t/t4116-apply-reverse.sh | 2 +- t/t4117-apply-reject.sh | 12 +- t/t4118-apply-empty-context.sh | 6 +- t/t4200-rerere.sh | 6 +- t/t5305-include-tag.sh | 4 +- t/t5400-send-pack.sh | 2 +- t/t5401-update-hooks.sh | 10 +- t/t5503-tagfollow.sh | 8 +- t/t5505-remote.sh | 10 +- t/t5515-fetch-merge-logic.sh | 4 +- t/t6006-rev-list-format.sh | 2 +- t/t6023-merge-file.sh | 8 +- t/t6024-recursive-merge.sh | 4 +- t/t6029-merge-subtree.sh | 4 +- t/t6120-describe.sh | 2 +- t/t6200-fmt-merge-msg.sh | 16 +-- t/t6300-for-each-ref.sh | 24 ++-- t/t7003-filter-branch.sh | 4 +- t/t7004-tag.sh | 210 ++++++++++++++--------------- t/t7102-reset.sh | 12 +- t/t7502-status.sh | 6 +- t/t9100-git-svn-basic.sh | 6 +- t/t9300-fast-import.sh | 34 ++--- t/t9600-cvsimport.sh | 8 +- 50 files changed, 387 insertions(+), 387 deletions(-) diff --git a/t/diff-lib.sh b/t/diff-lib.sh index 28b941c493..4bddeb591e 100644 --- a/t/diff-lib.sh +++ b/t/diff-lib.sh @@ -11,7 +11,7 @@ compare_diff_raw () { sed -e "$sanitize_diff_raw" <"$1" >.tmp-1 sed -e "$sanitize_diff_raw" <"$2" >.tmp-2 - git diff .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2 + test_cmp .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2 } sanitize_diff_raw_z='/^:/s/ '"$_x40"' '"$_x40"' \([A-Z]\)[0-9]*$/ X X \1#/' @@ -23,7 +23,7 @@ compare_diff_raw_z () { perl -pe 'y/\000/\012/' <"$1" | sed -e "$sanitize_diff_raw_z" >.tmp-1 perl -pe 'y/\000/\012/' <"$2" | sed -e "$sanitize_diff_raw_z" >.tmp-2 - git diff .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2 + test_cmp .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2 } compare_diff_patch () { @@ -37,5 +37,5 @@ compare_diff_patch () { /^[dis]*imilarity index [0-9]*%$/d /^index [0-9a-f]*\.\.[0-9a-f]/d ' <"$2" >.tmp-2 - git diff .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2 + test_cmp .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2 } diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh index 3ecdd6626a..ccb0a3cb61 100755 --- a/t/t0030-stripspace.sh +++ b/t/t0030-stripspace.sh @@ -16,96 +16,96 @@ test_expect_success \ 'long lines without spaces should be unchanged' ' echo "$ttt" >expect && git stripspace actual && - git diff expect actual && + test_cmp expect actual && echo "$ttt$ttt" >expect && git stripspace actual && - git diff expect actual && + test_cmp expect actual && echo "$ttt$ttt$ttt" >expect && git stripspace actual && - git diff expect actual && + test_cmp expect actual && echo "$ttt$ttt$ttt$ttt" >expect && git stripspace actual && - git diff expect actual + test_cmp expect actual ' test_expect_success \ 'lines with spaces at the beginning should be unchanged' ' echo "$sss$ttt" >expect && git stripspace actual && - git diff expect actual && + test_cmp expect actual && echo "$sss$sss$ttt" >expect && git stripspace actual && - git diff expect actual && + test_cmp expect actual && echo "$sss$sss$sss$ttt" >expect && git stripspace actual && - git diff expect actual + test_cmp expect actual ' test_expect_success \ 'lines with intermediate spaces should be unchanged' ' echo "$ttt$sss$ttt" >expect && git stripspace actual && - git diff expect actual && + test_cmp expect actual && echo "$ttt$sss$sss$ttt" >expect && git stripspace actual && - git diff expect actual + test_cmp expect actual ' test_expect_success \ 'consecutive blank lines should be unified' ' printf "$ttt\n\n$ttt\n" > expect && printf "$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt$ttt\n\n$ttt\n" > expect && printf "$ttt$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt$ttt$ttt\n\n$ttt\n" > expect && printf "$ttt$ttt$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n\n$ttt\n" > expect && printf "$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n\n$ttt$ttt\n" > expect && printf "$ttt\n\n\n\n\n$ttt$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n\n$ttt$ttt$ttt\n" > expect && printf "$ttt\n\n\n\n\n$ttt$ttt$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n\n$ttt\n" > expect && printf "$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt$ttt\n\n$ttt\n" > expect && printf "$ttt$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt$ttt$ttt\n\n$ttt\n" > expect && printf "$ttt$ttt$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n\n$ttt\n" > expect && printf "$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n\n$ttt$ttt\n" > expect && printf "$ttt\n\t\n \n\n \t\t\n$ttt$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n\n$ttt$ttt$ttt\n" > expect && printf "$ttt\n\t\n \n\n \t\t\n$ttt$ttt$ttt\n" | git stripspace >actual && - git diff expect actual + test_cmp expect actual ' test_expect_success \ @@ -113,114 +113,114 @@ test_expect_success \ > expect && printf "\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "\n\n\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$sss\n$sss\n$sss\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$sss$sss\n$sss\n\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "\n$sss\n$sss$sss\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$sss$sss$sss$sss\n\n\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "\n$sss$sss$sss$sss\n\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "\n\n$sss$sss$sss$sss\n" | git stripspace >actual && - git diff expect actual + test_cmp expect actual ' test_expect_success \ 'consecutive blank lines at the beginning should be removed' ' printf "$ttt\n" > expect && printf "\n$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n" > expect && printf "\n\n\n$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt$ttt\n" > expect && printf "\n\n\n$ttt$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt$ttt$ttt\n" > expect && printf "\n\n\n$ttt$ttt$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt$ttt$ttt$ttt\n" > expect && printf "\n\n\n$ttt$ttt$ttt$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n" > expect && printf "$sss\n$sss\n$sss\n$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "\n$sss\n$sss$sss\n$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$sss$sss\n$sss\n\n$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$sss$sss$sss\n\n\n$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "\n$sss$sss$sss\n\n$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "\n\n$sss$sss$sss\n$ttt\n" | git stripspace >actual && - git diff expect actual + test_cmp expect actual ' test_expect_success \ 'consecutive blank lines at the end should be removed' ' printf "$ttt\n" > expect && printf "$ttt\n\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n" > expect && printf "$ttt\n\n\n\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt$ttt\n" > expect && printf "$ttt$ttt\n\n\n\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt$ttt$ttt\n" > expect && printf "$ttt$ttt$ttt\n\n\n\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt$ttt$ttt$ttt\n" > expect && printf "$ttt$ttt$ttt$ttt\n\n\n\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n" > expect && printf "$ttt\n$sss\n$sss\n$sss\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n\n$sss\n$sss$sss\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n$sss$sss\n$sss\n\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n$sss$sss$sss\n\n\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n\n$sss$sss$sss\n\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n\n\n$sss$sss$sss\n" | git stripspace >actual && - git diff expect actual + test_cmp expect actual ' test_expect_success \ @@ -257,27 +257,27 @@ test_expect_success \ 'text plus spaces without newline should show the correct lines' ' printf "$ttt\n" >expect && printf "$ttt$sss" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n" >expect && printf "$ttt$sss$sss" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n" >expect && printf "$ttt$sss$sss$sss" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt$ttt\n" >expect && printf "$ttt$ttt$sss" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt$ttt\n" >expect && printf "$ttt$ttt$sss$sss" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt$ttt$ttt\n" >expect && printf "$ttt$ttt$ttt$sss" | git stripspace >actual && - git diff expect actual + test_cmp expect actual ' test_expect_success \ @@ -294,27 +294,27 @@ test_expect_success \ 'text plus spaces at end should be cleaned and newline must remain' ' echo "$ttt" >expect && echo "$ttt$sss" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && echo "$ttt" >expect && echo "$ttt$sss$sss" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && echo "$ttt" >expect && echo "$ttt$sss$sss$sss" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && echo "$ttt$ttt" >expect && echo "$ttt$ttt$sss" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && echo "$ttt$ttt" >expect && echo "$ttt$ttt$sss$sss" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && echo "$ttt$ttt$ttt" >expect && echo "$ttt$ttt$ttt$sss" | git stripspace >actual && - git diff expect actual + test_cmp expect actual ' # spaces only: @@ -324,19 +324,19 @@ test_expect_success \ printf "" >expect && echo | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && echo "$sss" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && echo "$sss$sss" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && echo "$sss$sss$sss" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && echo "$sss$sss$sss$sss" | git stripspace >actual && - git diff expect actual + test_cmp expect actual ' test_expect_success \ @@ -353,43 +353,43 @@ test_expect_success \ printf "" >expect && printf "" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$sss$sss" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$sss$sss$sss" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$sss$sss$sss$sss" | git stripspace >actual && - git diff expect actual + test_cmp expect actual ' test_expect_success \ 'consecutive text lines should be unchanged' ' printf "$ttt$ttt\n$ttt\n" >expect && printf "$ttt$ttt\n$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n$ttt$ttt\n$ttt\n" >expect && printf "$ttt\n$ttt$ttt\n$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n$ttt\n$ttt\n$ttt$ttt\n" >expect && printf "$ttt\n$ttt\n$ttt\n$ttt$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n$ttt\n\n$ttt$ttt\n$ttt\n" >expect && printf "$ttt\n$ttt\n\n$ttt$ttt\n$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt$ttt\n\n$ttt\n$ttt$ttt\n" >expect && printf "$ttt$ttt\n\n$ttt\n$ttt$ttt\n" | git stripspace >actual && - git diff expect actual && + test_cmp expect actual && printf "$ttt\n$ttt$ttt\n\n$ttt\n" >expect && printf "$ttt\n$ttt$ttt\n\n$ttt\n" | git stripspace >actual && - git diff expect actual + test_cmp expect actual ' test_expect_success 'strip comments, too' ' diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index c23f0ace85..9965cfa1dc 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -29,7 +29,7 @@ EOF test_expect_success 'test help' ' ! test-parse-options -h > output 2> output.err && test ! -s output && - git diff expect.err output.err + test_cmp expect.err output.err ' cat > expect << EOF @@ -40,7 +40,7 @@ EOF test_expect_success 'short options' ' test-parse-options -s123 -b -i 1729 -b > output 2> output.err && - git diff expect output && + test_cmp expect output && test ! -s output.err ' cat > expect << EOF @@ -53,7 +53,7 @@ test_expect_success 'long options' ' test-parse-options --boolean --integer 1729 --boolean --string2=321 \ > output 2> output.err && test ! -s output.err && - git diff expect output + test_cmp expect output ' cat > expect << EOF @@ -69,7 +69,7 @@ test_expect_success 'intermingled arguments' ' test-parse-options a1 --string 123 b1 --boolean -j 13 -- --boolean \ > output 2> output.err && test ! -s output.err && - git diff expect output + test_cmp expect output ' cat > expect << EOF @@ -81,13 +81,13 @@ EOF test_expect_success 'unambiguously abbreviated option' ' test-parse-options --int 2 --boolean --no-bo > output 2> output.err && test ! -s output.err && - git diff expect output + test_cmp expect output ' test_expect_success 'unambiguously abbreviated option with "="' ' test-parse-options --int=2 > output 2> output.err && test ! -s output.err && - git diff expect output + test_cmp expect output ' test_expect_success 'ambiguously abbreviated option' ' @@ -104,7 +104,7 @@ EOF test_expect_success 'non ambiguous option (after two options it abbreviates)' ' test-parse-options --st 123 > output 2> output.err && test ! -s output.err && - git diff expect output + test_cmp expect output ' cat > expect.err << EOF @@ -114,7 +114,7 @@ EOF test_expect_success 'detect possible typos' ' ! test-parse-options -boolean > output 2> output.err && test ! -s output && - git diff expect.err output.err + test_cmp expect.err output.err ' cat > expect < output 2> output.err && test ! -s output.err && - git diff expect output + test_cmp expect output ' test_done diff --git a/t/t1000-read-tree-m-3way.sh b/t/t1000-read-tree-m-3way.sh index 17f519f547..807fb83af8 100755 --- a/t/t1000-read-tree-m-3way.sh +++ b/t/t1000-read-tree-m-3way.sh @@ -131,7 +131,7 @@ _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" check_result () { git ls-files --stage | sed -e 's/ '"$_x40"' / X /' >current && - git diff expected current + test_cmp expected current } # This is done on an empty work directory, which is the normal diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh index b01b0037a0..4b44e131b2 100755 --- a/t/t1001-read-tree-m-2way.sh +++ b/t/t1001-read-tree-m-2way.sh @@ -33,7 +33,7 @@ compare_change () { -e '/^--- /d; /^+++ /d; /^@@ /d;' \ -e 's/^\([-+][0-7][0-7][0-7][0-7][0-7][0-7]\) '"$_x40"' /\1 X /p' \ "$1" - git diff expected current + test_cmp expected current } check_cache_at () { @@ -86,7 +86,7 @@ test_expect_success \ 'rm -f .git/index && read_tree_twoway $treeH $treeM && git ls-files --stage >1-3.out && - git diff M.out 1-3.out && + test_cmp M.out 1-3.out && check_cache_at bozbar dirty && check_cache_at frotz dirty && check_cache_at nitfol dirty' @@ -101,7 +101,7 @@ test_expect_success \ git update-index --add yomin && read_tree_twoway $treeH $treeM && git ls-files --stage >4.out || return 1 - git diff M.out 4.out >4diff.out + git diff --no-index M.out 4.out >4diff.out compare_change 4diff.out expected && check_cache_at yomin clean' @@ -115,7 +115,7 @@ test_expect_success \ echo yomin yomin >yomin && read_tree_twoway $treeH $treeM && git ls-files --stage >5.out || return 1 - git diff M.out 5.out >5diff.out + git diff --no-index M.out 5.out >5diff.out compare_change 5diff.out expected && check_cache_at yomin dirty' @@ -127,7 +127,7 @@ test_expect_success \ git update-index --add frotz && read_tree_twoway $treeH $treeM && git ls-files --stage >6.out && - git diff M.out 6.out && + test_cmp M.out 6.out && check_cache_at frotz clean' test_expect_success \ @@ -140,7 +140,7 @@ test_expect_success \ echo frotz frotz >frotz && read_tree_twoway $treeH $treeM && git ls-files --stage >7.out && - git diff M.out 7.out && + test_cmp M.out 7.out && check_cache_at frotz dirty' test_expect_success \ @@ -171,7 +171,7 @@ test_expect_success \ git update-index --add rezrov && read_tree_twoway $treeH $treeM && git ls-files --stage >10.out && - git diff M.out 10.out' + test_cmp M.out 10.out' test_expect_success \ '11 - dirty path removed.' \ @@ -216,7 +216,7 @@ test_expect_success \ git update-index --add nitfol && read_tree_twoway $treeH $treeM && git ls-files --stage >14.out || return 1 - git diff M.out 14.out >14diff.out + git diff --no-index M.out 14.out >14diff.out compare_change 14diff.out expected && check_cache_at nitfol clean' @@ -230,7 +230,7 @@ test_expect_success \ echo nitfol nitfol nitfol >nitfol && read_tree_twoway $treeH $treeM && git ls-files --stage >15.out || return 1 - git diff M.out 15.out >15diff.out + git diff --no-index M.out 15.out >15diff.out compare_change 15diff.out expected && check_cache_at nitfol dirty' @@ -262,7 +262,7 @@ test_expect_success \ git update-index --add bozbar && read_tree_twoway $treeH $treeM && git ls-files --stage >18.out && - git diff M.out 18.out && + test_cmp M.out 18.out && check_cache_at bozbar clean' test_expect_success \ @@ -275,7 +275,7 @@ test_expect_success \ echo gnusto gnusto >bozbar && read_tree_twoway $treeH $treeM && git ls-files --stage >19.out && - git diff M.out 19.out && + test_cmp M.out 19.out && check_cache_at bozbar dirty' test_expect_success \ @@ -287,7 +287,7 @@ test_expect_success \ git update-index --add bozbar && read_tree_twoway $treeH $treeM && git ls-files --stage >20.out && - git diff M.out 20.out && + test_cmp M.out 20.out && check_cache_at bozbar dirty' test_expect_success \ @@ -337,7 +337,7 @@ test_expect_success \ git update-index --add DF && read_tree_twoway $treeDF $treeDFDF && git ls-files --stage >DFDFcheck.out && - git diff DFDF.out DFDFcheck.out && + test_cmp DFDF.out DFDFcheck.out && check_cache_at DF/DF dirty && :' diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh index 42e5cf8181..e04990eafd 100755 --- a/t/t1002-read-tree-m-u-2way.sh +++ b/t/t1002-read-tree-m-u-2way.sh @@ -16,7 +16,7 @@ compare_change () { sed >current \ -e '/^--- /d; /^+++ /d; /^@@ /d;' \ -e 's/^\(.[0-7][0-7][0-7][0-7][0-7][0-7]\) '"$_x40"' /\1 X /' "$1" - git diff expected current + test_cmp expected current } check_cache_at () { diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index a675cbb51b..afe7e663fb 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -427,13 +427,13 @@ cat > expect << EOF weird EOF -test_expect_success "rename succeeded" "git diff expect .git/config" +test_expect_success "rename succeeded" "test_cmp expect .git/config" test_expect_success "rename non-existing section" ' ! git config --rename-section branch."world domination" branch.drei ' -test_expect_success "rename succeeded" "git diff expect .git/config" +test_expect_success "rename succeeded" "test_cmp expect .git/config" test_expect_success "rename another section" \ 'git config --rename-section branch."1 234 blabl/a" branch.drei' @@ -449,7 +449,7 @@ cat > expect << EOF weird EOF -test_expect_success "rename succeeded" "git diff expect .git/config" +test_expect_success "rename succeeded" "test_cmp expect .git/config" cat >> .git/config << EOF [branch "zwei"] a = 1 [branch "vier"] @@ -465,7 +465,7 @@ weird EOF test_expect_success "section was removed properly" \ - "git diff -u expect .git/config" + "test_cmp expect .git/config" rm .git/config diff --git a/t/t1303-wacky-config.sh b/t/t1303-wacky-config.sh index f366b53fb6..f98f4c5179 100755 --- a/t/t1303-wacky-config.sh +++ b/t/t1303-wacky-config.sh @@ -11,7 +11,7 @@ setup() { check() { echo "$2" >expected git config --get "$1" >actual - git diff actual expected + test_cmp actual expected } test_expect_success 'modify same key' ' diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh index 762af5faf7..d24a47d114 100755 --- a/t/t1502-rev-parse-parseopt.sh +++ b/t/t1502-rev-parse-parseopt.sh @@ -37,7 +37,7 @@ C? option C with an optional argument Extras extra1 line above used to cause a segfault but no longer does EOF - git diff expect.err output.err + test_cmp expect.err output.err ' test_done diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh index 55f057cebe..1caeacafa7 100755 --- a/t/t3001-ls-files-others-exclude.sh +++ b/t/t3001-ls-files-others-exclude.sh @@ -65,7 +65,7 @@ test_expect_success \ --exclude-per-directory=.gitignore \ --exclude-from=.git/ignore \ >output && - git diff expect output' + test_cmp expect output' # Test \r\n (MSDOS-like systems) printf '*.1\r\n/*.3\r\n!*.6\r\n' >.gitignore @@ -77,7 +77,7 @@ test_expect_success \ --exclude-per-directory=.gitignore \ --exclude-from=.git/ignore \ >output && - git diff expect output' + test_cmp expect output' cat > excludes-file << EOF *.[1-8] diff --git a/t/t3002-ls-files-dashpath.sh b/t/t3002-ls-files-dashpath.sh index 8687a01d2b..8704b04e1b 100755 --- a/t/t3002-ls-files-dashpath.sh +++ b/t/t3002-ls-files-dashpath.sh @@ -23,7 +23,7 @@ test_expect_success \ test_expect_success \ 'git ls-files without path restriction.' \ 'git ls-files --others >output && - git diff output - <output && - git diff output - <output && - git diff output - <output && - git diff output - <output && - git diff output - <expected && - git diff -u expected actual + test_cmp expected actual ' test_expect_success 'setup 2' ' @@ -61,7 +61,7 @@ test_expect_success 'setup 2' ' echo "100644 $o0 0 c" echo "100644 $o0 0 d/e" ) >expected && - git diff -u expected actual && + test_cmp expected actual && echo goodbye >>a && o2=$(git hash-object a) && @@ -82,7 +82,7 @@ test_expect_success 'setup 2' ' echo "100644 $o0 0 c" echo "100644 $o0 0 d/e" ) >expected && - git diff -u expected actual + test_cmp expected actual ' test_expect_success 'setup 3' ' @@ -100,7 +100,7 @@ test_expect_success 'setup 3' ' echo "100644 $o0 0 c" echo "100644 $o0 0 d/e" ) >expected && - git diff -u expected actual && + test_cmp expected actual && rm -f b && mkdir b && echo df-1 >b/c && git add b/c && o3=$(git hash-object b/c) && @@ -119,7 +119,7 @@ test_expect_success 'setup 3' ' echo "100644 $o0 0 c" echo "100644 $o0 0 d/e" ) >expected && - git diff -u expected actual + test_cmp expected actual ' test_expect_success 'setup 4' ' @@ -137,7 +137,7 @@ test_expect_success 'setup 4' ' echo "100644 $o0 0 c" echo "100644 $o0 0 d/e" ) >expected && - git diff -u expected actual && + test_cmp expected actual && rm -f a && mkdir a && echo df-2 >a/c && git add a/c && o4=$(git hash-object a/c) && @@ -156,7 +156,7 @@ test_expect_success 'setup 4' ' echo "100644 $o0 0 c" echo "100644 $o0 0 d/e" ) >expected && - git diff -u expected actual + test_cmp expected actual ' test_expect_success 'setup 5' ' @@ -174,7 +174,7 @@ test_expect_success 'setup 5' ' echo "100644 $o0 0 c" echo "100644 $o0 0 d/e" ) >expected && - git diff -u expected actual && + test_cmp expected actual && rm -f b && echo remove-conflict >a && @@ -195,7 +195,7 @@ test_expect_success 'setup 5' ' echo "100644 $o0 0 c" echo "100644 $o0 0 d/e" ) >expected && - git diff -u expected actual + test_cmp expected actual ' @@ -214,7 +214,7 @@ test_expect_success 'setup 6' ' echo "100644 $o0 0 c" echo "100644 $o0 0 d/e" ) >expected && - git diff -u expected actual && + test_cmp expected actual && rm -fr d && echo df-3 >d && git add d && o6=$(git hash-object d) && @@ -233,7 +233,7 @@ test_expect_success 'setup 6' ' echo "100644 $o0 0 c" echo "100644 $o6 0 d" ) >expected && - git diff -u expected actual + test_cmp expected actual ' test_expect_success 'merge-recursive simple' ' @@ -265,7 +265,7 @@ test_expect_success 'merge-recursive result' ' echo "100644 $o0 0 c" echo "100644 $o1 0 d/e" ) >expected && - git diff -u expected actual + test_cmp expected actual ' @@ -297,7 +297,7 @@ test_expect_success 'merge-recursive remove conflict' ' echo "100644 $o0 0 c" echo "100644 $o1 0 d/e" ) >expected && - git diff -u expected actual + test_cmp expected actual ' @@ -318,7 +318,7 @@ test_expect_success 'merge-recursive result' ' echo "100644 $o0 0 c" echo "100644 $o1 0 d/e" ) >expected && - git diff -u expected actual + test_cmp expected actual ' @@ -352,7 +352,7 @@ test_expect_success 'merge-recursive d/f conflict result' ' echo "100644 $o0 0 c" echo "100644 $o1 0 d/e" ) >expected && - git diff -u expected actual + test_cmp expected actual ' @@ -386,7 +386,7 @@ test_expect_success 'merge-recursive d/f conflict result the other way' ' echo "100644 $o0 0 c" echo "100644 $o1 0 d/e" ) >expected && - git diff -u expected actual + test_cmp expected actual ' @@ -420,7 +420,7 @@ test_expect_success 'merge-recursive d/f conflict result' ' echo "100644 $o0 1 d/e" echo "100644 $o1 2 d/e" ) >expected && - git diff -u expected actual + test_cmp expected actual ' @@ -454,7 +454,7 @@ test_expect_success 'merge-recursive d/f conflict result' ' echo "100644 $o0 1 d/e" echo "100644 $o1 3 d/e" ) >expected && - git diff -u expected actual + test_cmp expected actual ' @@ -480,7 +480,7 @@ test_expect_success 'reset and bind merge' ' echo "100644 $o0 0 c" echo "100644 $o1 0 d/e" ) >expected && - git diff -u expected actual && + test_cmp expected actual && git read-tree --prefix=a1/ master && git ls-files -s >actual && @@ -498,7 +498,7 @@ test_expect_success 'reset and bind merge' ' echo "100644 $o0 0 c" echo "100644 $o1 0 d/e" ) >expected && - git diff -u expected actual + test_cmp expected actual git read-tree --prefix=z/ master && git ls-files -s >actual && @@ -520,7 +520,7 @@ test_expect_success 'reset and bind merge' ' echo "100644 $o0 0 z/c" echo "100644 $o1 0 z/d/e" ) >expected && - git diff -u expected actual + test_cmp expected actual ' diff --git a/t/t3040-subprojects-basic.sh b/t/t3040-subprojects-basic.sh index 79b9f23654..f6973e96a5 100755 --- a/t/t3040-subprojects-basic.sh +++ b/t/t3040-subprojects-basic.sh @@ -24,7 +24,7 @@ test_expect_success 'create subprojects' \ git add sub2 && git commit -q -m "subprojects added" && git diff-tree --abbrev=5 HEAD^ HEAD |cut -d" " -f-3,5- >current && - git diff expected current' + test_cmp expected current' git branch save HEAD @@ -62,7 +62,7 @@ test_expect_success 'check if clone works' \ 'git ls-files -s >expected && git clone -l -s . cloned && ( cd cloned && git ls-files -s ) >current && - git diff expected current' + test_cmp expected current' test_expect_success 'removing and adding subproject' \ 'git update-index --force-remove -- sub2 && diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh index 46427e3f36..6e6a2542a2 100755 --- a/t/t3100-ls-tree-restrict.sh +++ b/t/t3100-ls-tree-restrict.sh @@ -35,7 +35,7 @@ _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" test_output () { sed -e "s/ $_x40 / X /" check - git diff expected check + test_cmp expected check } test_expect_success \ diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh index 70f9ce9d52..4dd7d12bac 100755 --- a/t/t3101-ls-tree-dirname.sh +++ b/t/t3101-ls-tree-dirname.sh @@ -43,7 +43,7 @@ _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" test_output () { sed -e "s/ $_x40 / X /" check - git diff expected check + test_cmp expected check } test_expect_success \ diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh index 24a00a9df1..0574ef1f10 100755 --- a/t/t3300-funny-names.sh +++ b/t/t3300-funny-names.sh @@ -35,7 +35,7 @@ no-funny' >expected test_expect_success 'git ls-files no-funny' \ 'git update-index --add "$p0" "$p2" && git ls-files >current && - git diff expected current' + test_cmp expected current' t0=`git write-tree` echo "$t0" >t0 @@ -48,14 +48,14 @@ EOF test_expect_success 'git ls-files with-funny' \ 'git update-index --add "$p1" && git ls-files >current && - git diff expected current' + test_cmp expected current' echo 'just space no-funny tabs ," (dq) and spaces' >expected test_expect_success 'git ls-files -z with-funny' \ 'git ls-files -z | perl -pe y/\\000/\\012/ >current && - git diff expected current' + test_cmp expected current' t1=`git write-tree` echo "$t1" >t1 @@ -67,28 +67,28 @@ no-funny EOF test_expect_success 'git ls-tree with funny' \ 'git ls-tree -r $t1 | sed -e "s/^[^ ]* //" >current && - git diff expected current' + test_cmp expected current' cat > expected <<\EOF A "tabs\t,\" (dq) and spaces" EOF test_expect_success 'git diff-index with-funny' \ 'git diff-index --name-status $t0 >current && - git diff expected current' + test_cmp expected current' test_expect_success 'git diff-tree with-funny' \ 'git diff-tree --name-status $t0 $t1 >current && - git diff expected current' + test_cmp expected current' echo 'A tabs ," (dq) and spaces' >expected test_expect_success 'git diff-index -z with-funny' \ 'git diff-index -z --name-status $t0 | perl -pe y/\\000/\\012/ >current && - git diff expected current' + test_cmp expected current' test_expect_success 'git diff-tree -z with-funny' \ 'git diff-tree -z --name-status $t0 $t1 | perl -pe y/\\000/\\012/ >current && - git diff expected current' + test_cmp expected current' cat > expected <<\EOF CNUM no-funny "tabs\t,\" (dq) and spaces" @@ -96,7 +96,7 @@ EOF test_expect_success 'git diff-tree -C with-funny' \ 'git diff-tree -C --find-copies-harder --name-status \ $t0 $t1 | sed -e 's/^C[0-9]*/CNUM/' >current && - git diff expected current' + test_cmp expected current' cat > expected <<\EOF RNUM no-funny "tabs\t,\" (dq) and spaces" @@ -105,7 +105,7 @@ test_expect_success 'git diff-tree delete with-funny' \ 'git update-index --force-remove "$p0" && git diff-index -M --name-status \ $t0 | sed -e 's/^R[0-9]*/RNUM/' >current && - git diff expected current' + test_cmp expected current' cat > expected <<\EOF diff --git a/no-funny "b/tabs\t,\" (dq) and spaces" @@ -116,7 +116,7 @@ EOF test_expect_success 'git diff-tree delete with-funny' \ 'git diff-index -M -p $t0 | sed -e "s/index [0-9]*%/index NUM%/" >current && - git diff expected current' + test_cmp expected current' chmod +x "$p1" cat > expected <<\EOF @@ -130,7 +130,7 @@ EOF test_expect_success 'git diff-tree delete with-funny' \ 'git diff-index -M -p $t0 | sed -e "s/index [0-9]*%/index NUM%/" >current && - git diff expected current' + test_cmp expected current' cat >expected <<\EOF "tabs\t,\" (dq) and spaces" @@ -139,7 +139,7 @@ EOF test_expect_success 'git diff-tree rename with-funny applied' \ 'git diff-index -M -p $t0 | git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current && - git diff expected current' + test_cmp expected current' cat > expected <<\EOF no-funny @@ -149,12 +149,12 @@ EOF test_expect_success 'git diff-tree delete with-funny applied' \ 'git diff-index -p $t0 | git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current && - git diff expected current' + test_cmp expected current' test_expect_success 'git apply non-git diff' \ 'git diff-index -p $t0 | sed -ne "/^[-+@]/p" | git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current && - git diff expected current' + test_cmp expected current' test_done diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh index 94b1c24b0a..883281dbd6 100755 --- a/t/t3900-i18n-commit.sh +++ b/t/t3900-i18n-commit.sh @@ -9,7 +9,7 @@ test_description='commit and log output encodings' compare_with () { git show -s $1 | sed -e '1,/^$/d' -e 's/^ //' >current && - git diff current "$2" + test_cmp current "$2" } test_expect_success setup ' diff --git a/t/t4006-diff-mode.sh b/t/t4006-diff-mode.sh index ab5406dd9f..4e92fce1d0 100755 --- a/t/t4006-diff-mode.sh +++ b/t/t4006-diff-mode.sh @@ -38,6 +38,6 @@ echo ":100644 100755 X X M rezrov" >expected test_expect_success \ 'verify' \ - 'git diff expected check' + 'test_cmp expected check' test_done diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index 6b4d1c52bb..4c038ccec1 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -112,7 +112,7 @@ do } >"$actual" && if test -f "$expect" then - git diff "$expect" "$actual" && + test_cmp "$expect" "$actual" && rm -f "$actual" else # this is to help developing new tests. diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index b2b7a8db85..3583e68e92 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -226,7 +226,7 @@ test_expect_success 'shortlog of cover-letter wraps overly-long onelines' ' git format-patch --cover-letter -2 && sed -e "1,/A U Thor/d" -e "/^$/q" < 0000-cover-letter.patch > output && - git diff expect output + test_cmp expect output ' diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh index 83c54b747f..ca0302f41b 100755 --- a/t/t4015-diff-whitespace.sh +++ b/t/t4015-diff-whitespace.sh @@ -43,13 +43,13 @@ index adf3937..6edc172 100644 EOF git diff > out -test_expect_success "Ray's example without options" 'git diff expect out' +test_expect_success "Ray's example without options" 'test_cmp expect out' git diff -w > out -test_expect_success "Ray's example with -w" 'git diff expect out' +test_expect_success "Ray's example with -w" 'test_cmp expect out' git diff -b > out -test_expect_success "Ray's example with -b" 'git diff expect out' +test_expect_success "Ray's example with -b" 'test_cmp expect out' tr 'Q' '\015' << EOF > x whitespace at beginning @@ -90,14 +90,14 @@ index d99af23..8b32fb5 100644 +CR at end EOF git diff > out -test_expect_success 'another test, without options' 'git diff expect out' +test_expect_success 'another test, without options' 'test_cmp 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' 'git diff expect out' +test_expect_success 'another test, with -w' 'test_cmp expect out' tr 'Q' '\015' << EOF > expect diff --git a/x b/x @@ -115,7 +115,7 @@ index d99af23..8b32fb5 100644 CR at endQ EOF git diff -b > out -test_expect_success 'another test, with -b' 'git diff expect out' +test_expect_success 'another test, with -b' 'test_cmp expect out' test_expect_success 'check mixed spaces and tabs in indent' ' diff --git a/t/t4016-diff-quote.sh b/t/t4016-diff-quote.sh index 5dbdc0c9fa..0950250c9b 100755 --- a/t/t4016-diff-quote.sh +++ b/t/t4016-diff-quote.sh @@ -49,7 +49,7 @@ cat >expect <<\EOF EOF test_expect_success 'git diff --summary -M HEAD' ' git diff --summary -M HEAD >actual && - git diff expect actual + test_cmp expect actual ' cat >expect <<\EOF @@ -64,7 +64,7 @@ cat >expect <<\EOF EOF test_expect_success 'git diff --stat -M HEAD' ' git diff --stat -M HEAD >actual && - git diff expect actual + test_cmp expect actual ' test_done diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh index f9db81d3ab..6d3ef6c60b 100755 --- a/t/t4018-diff-funcname.sh +++ b/t/t4018-diff-funcname.sh @@ -33,13 +33,13 @@ EOF sed 's/beer\\/beer,\\/' < Beer.java > Beer-correct.java test_expect_success 'default behaviour' ' - git diff Beer.java Beer-correct.java | + git diff --no-index Beer.java Beer-correct.java | grep "^@@.*@@ public class Beer" ' test_expect_success 'preset java pattern' ' echo "*.java diff=java" >.gitattributes && - git diff Beer.java Beer-correct.java | + git diff --no-index Beer.java Beer-correct.java | grep "^@@.*@@ public static void main(" ' @@ -48,13 +48,13 @@ git config diff.java.funcname '!static [^ ].*s.*' test_expect_success 'custom pattern' ' - git diff Beer.java Beer-correct.java | + git diff --no-index Beer.java Beer-correct.java | grep "^@@.*@@ int special;$" ' test_expect_success 'last regexp must not be negated' ' git config diff.java.funcname "!static" && - ! git diff Beer.java Beer-correct.java + ! git diff --no-index Beer.java Beer-correct.java ' test_done diff --git a/t/t4100-apply-stat.sh b/t/t4100-apply-stat.sh index 435f65b370..8073a5a1f2 100755 --- a/t/t4100-apply-stat.sh +++ b/t/t4100-apply-stat.sh @@ -11,36 +11,36 @@ test_description='git apply --stat --summary test. test_expect_success \ 'rename' \ 'git apply --stat --summary <../t4100/t-apply-1.patch >current && - git diff ../t4100/t-apply-1.expect current' + test_cmp ../t4100/t-apply-1.expect current' test_expect_success \ 'copy' \ 'git apply --stat --summary <../t4100/t-apply-2.patch >current && - git diff ../t4100/t-apply-2.expect current' + test_cmp ../t4100/t-apply-2.expect current' test_expect_success \ 'rewrite' \ 'git apply --stat --summary <../t4100/t-apply-3.patch >current && - git diff ../t4100/t-apply-3.expect current' + test_cmp ../t4100/t-apply-3.expect current' test_expect_success \ 'mode' \ 'git apply --stat --summary <../t4100/t-apply-4.patch >current && - git diff ../t4100/t-apply-4.expect current' + test_cmp ../t4100/t-apply-4.expect current' test_expect_success \ 'non git' \ 'git apply --stat --summary <../t4100/t-apply-5.patch >current && - git diff ../t4100/t-apply-5.expect current' + test_cmp ../t4100/t-apply-5.expect current' test_expect_success \ 'non git' \ 'git apply --stat --summary <../t4100/t-apply-6.patch >current && - git diff ../t4100/t-apply-6.expect current' + test_cmp ../t4100/t-apply-6.expect current' test_expect_success \ 'non git' \ 'git apply --stat --summary <../t4100/t-apply-7.patch >current && - git diff ../t4100/t-apply-7.expect current' + test_cmp ../t4100/t-apply-7.expect current' test_done diff --git a/t/t4104-apply-boundary.sh b/t/t4104-apply-boundary.sh index 43943ab8ca..e7e2913de7 100755 --- a/t/t4104-apply-boundary.sh +++ b/t/t4104-apply-boundary.sh @@ -90,7 +90,7 @@ do cat '"$kind-patch.$with"' (exit 1) } && - git diff '"$kind"'-expect victim + test_cmp '"$kind"'-expect victim ' done done @@ -108,7 +108,7 @@ do cat '"$kind-ng.without"' (exit 1) } && - git diff '"$kind"'-expect victim + test_cmp '"$kind"'-expect victim ' done diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh index a07ff42c2f..9ace578f17 100755 --- a/t/t4115-apply-symlink.sh +++ b/t/t4115-apply-symlink.sh @@ -33,7 +33,7 @@ test_expect_success 'apply symlink patch' ' git checkout side && git apply patch && git diff-files -p >patched && - git diff patch patched + test_cmp patch patched ' @@ -42,7 +42,7 @@ test_expect_success 'apply --index symlink patch' ' git checkout -f side && git apply --index patch && git diff-index --cached -p HEAD >patched && - git diff patch patched + test_cmp patch patched ' diff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh index c3f4579007..1459a90716 100755 --- a/t/t4116-apply-reverse.sh +++ b/t/t4116-apply-reverse.sh @@ -42,7 +42,7 @@ test_expect_success 'apply in reverse' ' git reset --hard second && git apply --reverse --binary --index patch && git diff >diff && - git diff /dev/null diff + test_cmp /dev/null diff ' diff --git a/t/t4117-apply-reject.sh b/t/t4117-apply-reject.sh index 659e17c92e..e9ccd161ee 100755 --- a/t/t4117-apply-reject.sh +++ b/t/t4117-apply-reject.sh @@ -54,7 +54,7 @@ test_expect_success 'apply without --reject should fail' ' exit 1 fi - git diff file1 saved.file1 + test_cmp file1 saved.file1 ' test_expect_success 'apply without --reject should fail' ' @@ -65,7 +65,7 @@ test_expect_success 'apply without --reject should fail' ' exit 1 fi - git diff file1 saved.file1 + test_cmp file1 saved.file1 ' test_expect_success 'apply with --reject should fail but update the file' ' @@ -79,7 +79,7 @@ test_expect_success 'apply with --reject should fail but update the file' ' exit 1 fi - git diff file1 expected && + test_cmp file1 expected && cat file1.rej && @@ -105,7 +105,7 @@ test_expect_success 'apply with --reject should fail but update the file' ' echo "file1 still exists?" exit 1 } - git diff file2 expected && + test_cmp file2 expected && cat file2.rej && @@ -132,7 +132,7 @@ test_expect_success 'the same test with --verbose' ' echo "file1 still exists?" exit 1 } - git diff file2 expected && + test_cmp file2 expected && cat file2.rej && @@ -151,7 +151,7 @@ test_expect_success 'apply cleanly with --verbose' ' git apply --verbose patch.1 && - git diff file1 clean + test_cmp file1 clean ' test_done diff --git a/t/t4118-apply-empty-context.sh b/t/t4118-apply-empty-context.sh index 1d531caf79..f92e259cc6 100755 --- a/t/t4118-apply-empty-context.sh +++ b/t/t4118-apply-empty-context.sh @@ -38,7 +38,7 @@ test_expect_success 'apply --numstat' ' echo "0 1 file1" && echo "0 1 file2" } >expect && - git diff expect actual + test_cmp expect actual ' @@ -48,8 +48,8 @@ test_expect_success 'apply --apply' ' cat file2.orig >file2 && git update-index file1 file2 && git apply --index diff.output && - git diff file1.mods file1 && - git diff file2.mods file2 + test_cmp file1.mods file1 && + test_cmp file2.mods file2 ' test_done diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh index 3cbfee704e..85d7e3edcd 100755 --- a/t/t4200-rerere.sh +++ b/t/t4200-rerere.sh @@ -101,7 +101,7 @@ cat > expect << EOF EOF git rerere diff > out -test_expect_success 'rerere diff' 'git diff expect out' +test_expect_success 'rerere diff' 'test_cmp expect out' cat > expect << EOF a1 @@ -109,7 +109,7 @@ EOF git rerere status > out -test_expect_success 'rerere status' 'git diff expect out' +test_expect_success 'rerere status' 'test_cmp expect out' test_expect_success 'commit succeeds' \ "git commit -q -a -m 'prefer first over second'" @@ -126,7 +126,7 @@ test_expect_success 'another conflicting merge' ' git show first:a1 | sed 's/To die: t/To die! T/' > expect test_expect_success 'rerere kicked in' "! grep ======= a1" -test_expect_success 'rerere prefers first change' 'git diff a1 expect' +test_expect_success 'rerere prefers first change' 'test_cmp a1 expect' rm $rr/postimage echo "$sha1 a1" | perl -pe 'y/\012/\000/' > .git/rr-cache/MERGE_RR diff --git a/t/t5305-include-tag.sh b/t/t5305-include-tag.sh index 0db27547ac..fb471a08c6 100755 --- a/t/t5305-include-tag.sh +++ b/t/t5305-include-tag.sh @@ -50,7 +50,7 @@ test_expect_success 'check unpacked result (have commit, no tag)' ' test_must_fail git cat-file -e $tag && git rev-list --objects $commit ) >list.actual && - git diff list.expect list.actual + test_cmp list.expect list.actual ' rm -rf clone.git @@ -78,7 +78,7 @@ test_expect_success 'check unpacked result (have commit, have tag)' ' export GIT_DIR && git rev-list --objects $tag ) >list.actual && - git diff list.expect list.actual + test_cmp list.expect list.actual ' test_done diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh index 2b6b6e3f71..68c2ae688c 100755 --- a/t/t5400-send-pack.sh +++ b/t/t5400-send-pack.sh @@ -110,7 +110,7 @@ test_expect_success \ cd .. && git update-ref refs/heads/master master^ || return 1 git-send-pack --force ./victim/.git/ master && return 1 - ! git diff .git/refs/heads/master victim/.git/refs/heads/master + ! test_cmp .git/refs/heads/master victim/.git/refs/heads/master ' test_expect_success \ diff --git a/t/t5401-update-hooks.sh b/t/t5401-update-hooks.sh index 9a12024241..2fff300153 100755 --- a/t/t5401-update-hooks.sh +++ b/t/t5401-update-hooks.sh @@ -83,23 +83,23 @@ test_expect_success 'hooks ran' ' test_expect_success 'pre-receive hook input' ' (echo $commit0 $commit1 refs/heads/master; echo $commit1 $commit0 refs/heads/tofail - ) | git diff - victim/.git/pre-receive.stdin + ) | test_cmp - victim/.git/pre-receive.stdin ' test_expect_success 'update hook arguments' ' (echo refs/heads/master $commit0 $commit1; echo refs/heads/tofail $commit1 $commit0 - ) | git diff - victim/.git/update.args + ) | test_cmp - victim/.git/update.args ' test_expect_success 'post-receive hook input' ' echo $commit0 $commit1 refs/heads/master | - git diff - victim/.git/post-receive.stdin + test_cmp - victim/.git/post-receive.stdin ' test_expect_success 'post-update hook arguments' ' echo refs/heads/master | - git diff - victim/.git/post-update.args + test_cmp - victim/.git/post-update.args ' test_expect_success 'all hook stdin is /dev/null' ' @@ -130,7 +130,7 @@ STDERR post-update EOF test_expect_success 'send-pack stderr contains hook messages' ' grep ^STD send.err >actual && - git diff - actual actual && - git diff expect actual + test_cmp expect actual ' test_expect_success "create tag T on A, create C on branch cat" ' @@ -82,7 +82,7 @@ test_expect_success 'fetch C, T (new branch, tag : 1 connection)' ' ) && test -s $U && cut -d" " -f1,2 $U >actual && - git diff expect actual + test_cmp expect actual ' test_expect_success "create commits O, B, tag S on B" ' @@ -118,7 +118,7 @@ test_expect_success 'fetch B, S (commit and tag : 1 connection)' ' ) && test -s $U && cut -d" " -f1,2 $U >actual && - git diff expect actual + test_cmp expect actual ' cat - <expect @@ -144,7 +144,7 @@ test_expect_success 'new clone fetch master and tags' ' ) && test -s $U && cut -d" " -f1,2 $U >actual && - git diff expect actual + test_cmp expect actual ' test_done diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index 48ff2d424d..a37b6f5213 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -135,7 +135,7 @@ test_expect_success 'show' ' git config --add remote.origin.push \ +refs/tags/lastbackup && git remote show origin > output && - git diff expect output) + test_cmp expect output) ' test_expect_success 'prune' ' @@ -179,7 +179,7 @@ test_expect_success 'update' ' git remote add apis ../mirror && git remote update && git branch -r > output && - git diff expect output) + test_cmp expect output) ' @@ -206,7 +206,7 @@ test_expect_success 'update with arguments' ' git config remotes.titanus manduca && git remote update phobaeticus titanus && git branch -r > output && - git diff expect output) + test_cmp expect output) ' @@ -229,7 +229,7 @@ test_expect_success 'update default' ' git config remote.drosophila.skipDefaultUpdate true && git remote update default && git branch -r > output && - git diff expect output) + test_cmp expect output) ' @@ -249,7 +249,7 @@ test_expect_success 'update default (overridden, with funny whitespace)' ' git config remotes.default "$(printf "\t drosophila \n")" && git remote update default && git branch -r > output && - git diff expect output) + test_cmp expect output) ' diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh index 65c37744aa..3def75eeb2 100755 --- a/t/t5515-fetch-merge-logic.sh +++ b/t/t5515-fetch-merge-logic.sh @@ -151,7 +151,7 @@ do git show-ref >"$actual_r" && if test -f "$expect_f" then - git diff -u "$expect_f" "$actual_f" && + test_cmp "$expect_f" "$actual_f" && rm -f "$actual_f" else # this is to help developing new tests. @@ -160,7 +160,7 @@ do fi && if test -f "$expect_r" then - git diff -u "$expect_r" "$actual_r" && + test_cmp "$expect_r" "$actual_r" && rm -f "$actual_r" else # this is to help developing new tests. diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh index 0dc915ea67..9176484db2 100755 --- a/t/t6006-rev-list-format.sh +++ b/t/t6006-rev-list-format.sh @@ -15,7 +15,7 @@ test_format() { cat >expect.$1 test_expect_success "format $1" " git rev-list --pretty=format:$2 master >output.$1 && -git diff expect.$1 output.$1 +test_cmp expect.$1 output.$1 " } diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh index 79dc58b2ce..74e9e6618e 100755 --- a/t/t6023-merge-file.sh +++ b/t/t6023-merge-file.sh @@ -63,7 +63,7 @@ test_expect_success "merge without conflict (missing LF at EOF)" \ "git merge-file test2.txt orig.txt new2.txt" test_expect_success "merge result added missing LF" \ - "git diff test.txt test2.txt" + "test_cmp test.txt test2.txt" cp test.txt backup.txt test_expect_success "merge with conflicts" \ @@ -86,7 +86,7 @@ non timebo mala, quoniam tu mecum es: virga tua et baculus tuus ipsa me consolata sunt. EOF -test_expect_success "expected conflict markers" "git diff test.txt expect.txt" +test_expect_success "expected conflict markers" "test_cmp test.txt expect.txt" cp backup.txt test.txt test_expect_success "merge with conflicts, using -L" \ @@ -110,7 +110,7 @@ virga tua et baculus tuus ipsa me consolata sunt. EOF test_expect_success "expected conflict markers, with -L" \ - "git diff test.txt expect.txt" + "test_cmp test.txt expect.txt" sed "s/ tu / TU /" < new1.txt > new5.txt test_expect_success "conflict in removed tail" \ @@ -132,7 +132,7 @@ virga tua et baculus tuus ipsa me consolata sunt. >>>>>>> new5.txt EOF -test_expect_success "expected conflict markers" "git diff expect out" +test_expect_success "expected conflict markers" "test_cmp expect out" test_expect_success 'binary files cannot be merged' ' ! git merge-file -p orig.txt ../test4012.png new1.txt 2> merge.err && diff --git a/t/t6024-recursive-merge.sh b/t/t6024-recursive-merge.sh index 23d24d3feb..6a6a13002d 100755 --- a/t/t6024-recursive-merge.sh +++ b/t/t6024-recursive-merge.sh @@ -70,7 +70,7 @@ G >>>>>>> G:a1 EOF -test_expect_success "result contains a conflict" "git diff expect a1" +test_expect_success "result contains a conflict" "test_cmp expect a1" git ls-files --stage > out cat > expect << EOF @@ -79,7 +79,7 @@ cat > expect << EOF 100644 fd7923529855d0b274795ae3349c5e0438333979 3 a1 EOF -test_expect_success "virtual trees were processed" "git diff expect out" +test_expect_success "virtual trees were processed" "test_cmp expect out" test_expect_success 'refuse to merge binary files' ' git reset --hard && diff --git a/t/t6029-merge-subtree.sh b/t/t6029-merge-subtree.sh index 43f5459c35..5bbfa44e8d 100755 --- a/t/t6029-merge-subtree.sh +++ b/t/t6029-merge-subtree.sh @@ -57,7 +57,7 @@ test_expect_success 'initial merge' ' echo "100644 $o1 0 git-gui/git-gui.sh" echo "100644 $o2 0 git.c" ) >expected && - git diff -u expected actual + test_cmp expected actual ' test_expect_success 'merge update' ' @@ -73,7 +73,7 @@ test_expect_success 'merge update' ' echo "100644 $o3 0 git-gui/git-gui.sh" echo "100644 $o2 0 git.c" ) >expected && - git diff -u expected actual + test_cmp expected actual ' test_done diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index 56bbd8519d..ea476a2582 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh @@ -108,7 +108,7 @@ warning: tag 'A' is really 'Q' here EOF check_describe A-* HEAD test_expect_success 'warning was displayed for Q' ' - git diff err.expect err.actual + test_cmp err.expect err.actual ' test_expect_success 'rename tag Q back to A' ' mv .git/refs/tags/Q .git/refs/tags/A diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh index c9bf6fdba3..bc74349416 100755 --- a/t/t6200-fmt-merge-msg.sh +++ b/t/t6200-fmt-merge-msg.sh @@ -79,7 +79,7 @@ test_expect_success 'merge-msg test #1' ' git fetch . left && git fmt-merge-msg <.git/FETCH_HEAD >actual && - git diff actual expected + test_cmp expected actual ' cat >expected <actual && - git diff actual expected + test_cmp expected actual ' cat >expected <<\EOF @@ -117,7 +117,7 @@ test_expect_success 'merge-msg test #3-1' ' git fetch . left && git fmt-merge-msg <.git/FETCH_HEAD >actual && - git diff actual expected + test_cmp expected actual ' test_expect_success 'merge-msg test #3-2' ' @@ -131,7 +131,7 @@ test_expect_success 'merge-msg test #3-2' ' git fetch . left && git fmt-merge-msg <.git/FETCH_HEAD >actual && - git diff actual expected + test_cmp expected actual ' cat >expected <<\EOF @@ -163,7 +163,7 @@ test_expect_success 'merge-msg test #4-1' ' git fetch . left right && git fmt-merge-msg <.git/FETCH_HEAD >actual && - git diff actual expected + test_cmp expected actual ' test_expect_success 'merge-msg test #4-2' ' @@ -177,7 +177,7 @@ test_expect_success 'merge-msg test #4-2' ' git fetch . left right && git fmt-merge-msg <.git/FETCH_HEAD >actual && - git diff actual expected + test_cmp expected actual ' test_expect_success 'merge-msg test #5-1' ' @@ -191,7 +191,7 @@ test_expect_success 'merge-msg test #5-1' ' git fetch . left right && git fmt-merge-msg <.git/FETCH_HEAD >actual && - git diff actual expected + test_cmp expected actual ' test_expect_success 'merge-msg test #5-2' ' @@ -205,7 +205,7 @@ test_expect_success 'merge-msg test #5-2' ' git fetch . left right && git fmt-merge-msg <.git/FETCH_HEAD >actual && - git diff actual expected + test_cmp expected actual ' test_done diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index f46ec93c83..91ea85d99b 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -75,14 +75,14 @@ EOF test_expect_success 'Check unformatted date fields output' ' (git for-each-ref --shell --format="%(refname) %(committerdate) %(authordate)" refs/heads && git for-each-ref --shell --format="%(refname) %(taggerdate)" refs/tags) >actual && - git diff expected actual + test_cmp expected actual ' test_expect_success 'Check format "default" formatted date fields output' ' f=default && (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual && - git diff expected actual + test_cmp expected actual ' # Don't know how to do relative check because I can't know when this script @@ -109,7 +109,7 @@ test_expect_success 'Check format "short" date fields output' ' f=short && (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual && - git diff expected actual + test_cmp expected actual ' cat >expected <<\EOF @@ -121,7 +121,7 @@ test_expect_success 'Check format "local" date fields output' ' f=local && (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual && - git diff expected actual + test_cmp expected actual ' cat >expected <<\EOF @@ -133,7 +133,7 @@ test_expect_success 'Check format "iso8601" date fields output' ' f=iso8601 && (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual && - git diff expected actual + test_cmp expected actual ' cat >expected <<\EOF @@ -145,7 +145,7 @@ test_expect_success 'Check format "rfc2822" date fields output' ' f=rfc2822 && (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual && - git diff expected actual + test_cmp expected actual ' cat >expected <<\EOF @@ -155,7 +155,7 @@ EOF test_expect_success 'Verify ascending sort' ' git-for-each-ref --format="%(refname)" --sort=refname >actual && - git diff expected actual + test_cmp expected actual ' @@ -166,7 +166,7 @@ EOF test_expect_success 'Verify descending sort' ' git-for-each-ref --format="%(refname)" --sort=-refname >actual && - git diff expected actual + test_cmp expected actual ' cat >expected <<\EOF @@ -176,17 +176,17 @@ EOF test_expect_success 'Quoting style: shell' ' git for-each-ref --shell --format="%(refname)" >actual && - git diff expected actual + test_cmp expected actual ' test_expect_success 'Quoting style: perl' ' git for-each-ref --perl --format="%(refname)" >actual && - git diff expected actual + test_cmp expected actual ' test_expect_success 'Quoting style: python' ' git for-each-ref --python --format="%(refname)" >actual && - git diff expected actual + test_cmp expected actual ' cat >expected <<\EOF @@ -196,7 +196,7 @@ EOF test_expect_success 'Quoting style: tcl' ' git for-each-ref --tcl --format="%(refname)" >actual && - git diff expected actual + test_cmp expected actual ' for i in "--perl --shell" "-s --python" "--python --tcl" "--tcl --perl"; do diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index 1639c7aa96..1e8a205e53 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -224,7 +224,7 @@ test_expect_success 'Tag name filtering retains tag message' ' git cat-file tag T > expect && git filter-branch -f --tag-name-filter cat && git cat-file tag T > actual && - git diff expect actual + test_cmp expect actual ' faux_gpg_tag='object XXXXXX @@ -248,7 +248,7 @@ test_expect_success 'Tag name filtering strips gpg signature' ' echo "$faux_gpg_tag" | sed -e s/XXXXXX/$sha1/ | head -n 6 > expect && git filter-branch -f --tag-name-filter cat && git cat-file tag S > actual && - git diff expect actual + test_cmp expect actual ' test_done diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 2dcee7ccc5..241c70dc66 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -116,9 +116,9 @@ mytag EOF test_expect_success \ 'trying to delete tags without params should succeed and do nothing' ' - git tag -l > actual && git diff expect actual && + git tag -l > actual && test_cmp expect actual && git-tag -d && - git tag -l > actual && git diff expect actual + git tag -l > actual && test_cmp expect actual ' test_expect_success \ @@ -173,9 +173,9 @@ test_expect_success 'listing all tags should print them ordered' ' git tag v1.0 && git tag t210 && git tag -l > actual && - git diff expect actual && + test_cmp expect actual && git tag > actual && - git diff expect actual + test_cmp expect actual ' cat >expect < actual && - git diff expect actual + test_cmp expect actual ' cat >expect < actual && - git diff expect actual + test_cmp expect actual ' cat >expect < actual && - git diff expect actual + test_cmp expect actual ' cat >expect < actual && - git diff expect actual + test_cmp expect actual ' cat >expect < actual && - git diff expect actual + test_cmp expect actual ' cat >expect < actual && - git diff expect actual + test_cmp expect actual ' >expect test_expect_success \ 'listing tags using v.* should print nothing because none have v.' ' git-tag -l "v.*" > actual && - git diff expect actual + test_cmp expect actual ' cat >expect < actual && - git diff expect actual + test_cmp expect actual ' # creating and verifying lightweight tags: @@ -302,7 +302,7 @@ test_expect_success \ 'creating an annotated tag with -m message should succeed' ' git-tag -m "A message" annotated-tag && get_tag_msg annotated-tag >actual && - git diff expect actual + test_cmp expect actual ' cat >msgfile <actual && - git diff expect actual + test_cmp expect actual ' cat >inputmsg <>expect test_expect_success 'creating an annotated tag with -F - should succeed' ' git-tag -F - stdin-annotated-tag actual && - git diff expect actual + test_cmp expect actual ' test_expect_success \ @@ -358,7 +358,7 @@ test_expect_success \ 'creating a tag with an empty -m message should succeed' ' git-tag -m "" empty-annotated-tag && get_tag_msg empty-annotated-tag >actual && - git diff expect actual + test_cmp expect actual ' >emptyfile @@ -367,7 +367,7 @@ test_expect_success \ 'creating a tag with an empty -F messagefile should succeed' ' git-tag -F emptyfile emptyfile-annotated-tag && get_tag_msg emptyfile-annotated-tag >actual && - git diff expect actual + test_cmp expect actual ' printf '\n\n \n\t\nLeading blank lines\n' >blanksfile @@ -388,7 +388,7 @@ test_expect_success \ 'extra blanks in the message for an annotated tag should be removed' ' git-tag -F blanksfile blanks-annotated-tag && get_tag_msg blanks-annotated-tag >actual && - git diff expect actual + test_cmp expect actual ' get_tag_header blank-annotated-tag $commit commit $time >expect @@ -396,7 +396,7 @@ test_expect_success \ 'creating a tag with blank -m message with spaces should succeed' ' git-tag -m " " blank-annotated-tag && get_tag_msg blank-annotated-tag >actual && - git diff expect actual + test_cmp expect actual ' echo ' ' >blankfile @@ -407,7 +407,7 @@ test_expect_success \ 'creating a tag with blank -F messagefile with spaces should succeed' ' git-tag -F blankfile blankfile-annotated-tag && get_tag_msg blankfile-annotated-tag >actual && - git diff expect actual + test_cmp expect actual ' printf ' ' >blanknonlfile @@ -416,7 +416,7 @@ test_expect_success \ 'creating a tag with -F file of spaces and no newline should succeed' ' git-tag -F blanknonlfile blanknonlfile-annotated-tag && get_tag_msg blanknonlfile-annotated-tag >actual && - git diff expect actual + test_cmp expect actual ' # messages with commented lines: @@ -451,7 +451,7 @@ test_expect_success \ 'creating a tag using a -F messagefile with #comments should succeed' ' git-tag -F commentsfile comments-annotated-tag && get_tag_msg comments-annotated-tag >actual && - git diff expect actual + test_cmp expect actual ' get_tag_header comment-annotated-tag $commit commit $time >expect @@ -459,7 +459,7 @@ test_expect_success \ 'creating a tag with a #comment in the -m message should succeed' ' git-tag -m "#comment" comment-annotated-tag && get_tag_msg comment-annotated-tag >actual && - git diff expect actual + test_cmp expect actual ' echo '#comment' >commentfile @@ -470,7 +470,7 @@ test_expect_success \ 'creating a tag with #comments in the -F messagefile should succeed' ' git-tag -F commentfile commentfile-annotated-tag && get_tag_msg commentfile-annotated-tag >actual && - git diff expect actual + test_cmp expect actual ' printf '#comment' >commentnonlfile @@ -479,7 +479,7 @@ test_expect_success \ 'creating a tag with a file of #comment and no newline should succeed' ' git-tag -F commentnonlfile commentnonlfile-annotated-tag && get_tag_msg commentnonlfile-annotated-tag >actual && - git diff expect actual + test_cmp expect actual ' # listing messages for annotated non-signed tags: @@ -490,23 +490,23 @@ test_expect_success \ echo "tag-one-line" >expect && git-tag -l | grep "^tag-one-line" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n0 -l | grep "^tag-one-line" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n0 -l tag-one-line >actual && - git diff expect actual && + test_cmp expect actual && echo "tag-one-line A msg" >expect && git-tag -n1 -l | grep "^tag-one-line" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n -l | grep "^tag-one-line" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n1 -l tag-one-line >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n2 -l tag-one-line >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n999 -l tag-one-line >actual && - git diff expect actual + test_cmp expect actual ' test_expect_success \ @@ -515,23 +515,23 @@ test_expect_success \ echo "tag-zero-lines" >expect && git-tag -l | grep "^tag-zero-lines" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n0 -l | grep "^tag-zero-lines" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n0 -l tag-zero-lines >actual && - git diff expect actual && + test_cmp expect actual && echo "tag-zero-lines " >expect && git-tag -n1 -l | grep "^tag-zero-lines" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n -l | grep "^tag-zero-lines" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n1 -l tag-zero-lines >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n2 -l tag-zero-lines >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n999 -l tag-zero-lines >actual && - git diff expect actual + test_cmp expect actual ' echo 'tag line one' >annotagmsg @@ -543,39 +543,39 @@ test_expect_success \ echo "tag-lines" >expect && git-tag -l | grep "^tag-lines" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n0 -l | grep "^tag-lines" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n0 -l tag-lines >actual && - git diff expect actual && + test_cmp expect actual && echo "tag-lines tag line one" >expect && git-tag -n1 -l | grep "^tag-lines" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n -l | grep "^tag-lines" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n1 -l tag-lines >actual && - git diff expect actual && + test_cmp expect actual && echo " tag line two" >>expect && git-tag -n2 -l | grep "^ *tag.line" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n2 -l tag-lines >actual && - git diff expect actual && + test_cmp expect actual && echo " tag line three" >>expect && git-tag -n3 -l | grep "^ *tag.line" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n3 -l tag-lines >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n4 -l | grep "^ *tag.line" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n4 -l tag-lines >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n99 -l | grep "^ *tag.line" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n99 -l tag-lines >actual && - git diff expect actual + test_cmp expect actual ' # subsequent tests require gpg; check if it is available @@ -635,7 +635,7 @@ echo '-----BEGIN PGP SIGNATURE-----' >>expect test_expect_success 'creating a signed tag with -m message should succeed' ' git-tag -s -m "A signed tag message" signed-tag && get_tag_msg signed-tag >actual && - git diff expect actual + test_cmp expect actual ' get_tag_header u-signed-tag $commit commit $time >expect @@ -645,7 +645,7 @@ test_expect_success 'sign with a given key id' ' git tag -u committer@example.com -m "Another message" u-signed-tag && get_tag_msg u-signed-tag >actual && - git diff expect actual + test_cmp expect actual ' @@ -675,7 +675,7 @@ echo '-----BEGIN PGP SIGNATURE-----' >>expect test_expect_success '-u implies signed tag' ' GIT_EDITOR=./fakeeditor git-tag -u CDDE430D implied-sign && get_tag_msg implied-sign >actual && - git diff expect actual + test_cmp expect actual ' cat >sigmsgfile <actual && - git diff expect actual + test_cmp expect actual ' cat >siginputmsg <>expect test_expect_success 'creating a signed tag with -F - should succeed' ' git-tag -s -F - stdin-signed-tag actual && - git diff expect actual + test_cmp expect actual ' get_tag_header implied-annotate $commit commit $time >expect @@ -711,7 +711,7 @@ echo '-----BEGIN PGP SIGNATURE-----' >>expect test_expect_success '-s implies annotated tag' ' GIT_EDITOR=./fakeeditor git-tag -s implied-annotate && get_tag_msg implied-annotate >actual && - git diff expect actual + test_cmp expect actual ' test_expect_success \ @@ -752,7 +752,7 @@ test_expect_success \ 'creating a signed tag with an empty -m message should succeed' ' git-tag -s -m "" empty-signed-tag && get_tag_msg empty-signed-tag >actual && - git diff expect actual && + test_cmp expect actual && git-tag -v empty-signed-tag ' @@ -763,7 +763,7 @@ test_expect_success \ 'creating a signed tag with an empty -F messagefile should succeed' ' git-tag -s -F sigemptyfile emptyfile-signed-tag && get_tag_msg emptyfile-signed-tag >actual && - git diff expect actual && + test_cmp expect actual && git-tag -v emptyfile-signed-tag ' @@ -786,7 +786,7 @@ test_expect_success \ 'extra blanks in the message for a signed tag should be removed' ' git-tag -s -F sigblanksfile blanks-signed-tag && get_tag_msg blanks-signed-tag >actual && - git diff expect actual && + test_cmp expect actual && git-tag -v blanks-signed-tag ' @@ -796,7 +796,7 @@ test_expect_success \ 'creating a signed tag with a blank -m message should succeed' ' git-tag -s -m " " blank-signed-tag && get_tag_msg blank-signed-tag >actual && - git diff expect actual && + test_cmp expect actual && git-tag -v blank-signed-tag ' @@ -809,7 +809,7 @@ test_expect_success \ 'creating a signed tag with blank -F file with spaces should succeed' ' git-tag -s -F sigblankfile blankfile-signed-tag && get_tag_msg blankfile-signed-tag >actual && - git diff expect actual && + test_cmp expect actual && git-tag -v blankfile-signed-tag ' @@ -820,7 +820,7 @@ test_expect_success \ 'creating a signed tag with spaces and no newline should succeed' ' git-tag -s -F sigblanknonlfile blanknonlfile-signed-tag && get_tag_msg blanknonlfile-signed-tag >actual && - git diff expect actual && + test_cmp expect actual && git-tag -v signed-tag ' @@ -857,7 +857,7 @@ test_expect_success \ 'creating a signed tag with a -F file with #comments should succeed' ' git-tag -s -F sigcommentsfile comments-signed-tag && get_tag_msg comments-signed-tag >actual && - git diff expect actual && + test_cmp expect actual && git-tag -v comments-signed-tag ' @@ -867,7 +867,7 @@ test_expect_success \ 'creating a signed tag with #commented -m message should succeed' ' git-tag -s -m "#comment" comment-signed-tag && get_tag_msg comment-signed-tag >actual && - git diff expect actual && + test_cmp expect actual && git-tag -v comment-signed-tag ' @@ -880,7 +880,7 @@ test_expect_success \ 'creating a signed tag with #commented -F messagefile should succeed' ' git-tag -s -F sigcommentfile commentfile-signed-tag && get_tag_msg commentfile-signed-tag >actual && - git diff expect actual && + test_cmp expect actual && git-tag -v commentfile-signed-tag ' @@ -891,7 +891,7 @@ test_expect_success \ 'creating a signed tag with a #comment and no newline should succeed' ' git-tag -s -F sigcommentnonlfile commentnonlfile-signed-tag && get_tag_msg commentnonlfile-signed-tag >actual && - git diff expect actual && + test_cmp expect actual && git-tag -v commentnonlfile-signed-tag ' @@ -903,23 +903,23 @@ test_expect_success \ echo "stag-one-line" >expect && git-tag -l | grep "^stag-one-line" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n0 -l | grep "^stag-one-line" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n0 -l stag-one-line >actual && - git diff expect actual && + test_cmp expect actual && echo "stag-one-line A message line signed" >expect && git-tag -n1 -l | grep "^stag-one-line" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n -l | grep "^stag-one-line" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n1 -l stag-one-line >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n2 -l stag-one-line >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n999 -l stag-one-line >actual && - git diff expect actual + test_cmp expect actual ' test_expect_success \ @@ -928,23 +928,23 @@ test_expect_success \ echo "stag-zero-lines" >expect && git-tag -l | grep "^stag-zero-lines" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n0 -l | grep "^stag-zero-lines" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n0 -l stag-zero-lines >actual && - git diff expect actual && + test_cmp expect actual && echo "stag-zero-lines " >expect && git-tag -n1 -l | grep "^stag-zero-lines" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n -l | grep "^stag-zero-lines" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n1 -l stag-zero-lines >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n2 -l stag-zero-lines >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n999 -l stag-zero-lines >actual && - git diff expect actual + test_cmp expect actual ' echo 'stag line one' >sigtagmsg @@ -956,39 +956,39 @@ test_expect_success \ echo "stag-lines" >expect && git-tag -l | grep "^stag-lines" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n0 -l | grep "^stag-lines" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n0 -l stag-lines >actual && - git diff expect actual && + test_cmp expect actual && echo "stag-lines stag line one" >expect && git-tag -n1 -l | grep "^stag-lines" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n -l | grep "^stag-lines" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n1 -l stag-lines >actual && - git diff expect actual && + test_cmp expect actual && echo " stag line two" >>expect && git-tag -n2 -l | grep "^ *stag.line" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n2 -l stag-lines >actual && - git diff expect actual && + test_cmp expect actual && echo " stag line three" >>expect && git-tag -n3 -l | grep "^ *stag.line" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n3 -l stag-lines >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n4 -l | grep "^ *stag.line" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n4 -l stag-lines >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n99 -l | grep "^ *stag.line" >actual && - git diff expect actual && + test_cmp expect actual && git-tag -n99 -l stag-lines >actual && - git diff expect actual + test_cmp expect actual ' # tags pointing to objects different from commits: @@ -1004,7 +1004,7 @@ test_expect_success \ 'creating a signed tag pointing to a tree should succeed' ' git-tag -s -m "A message for a tree" tree-signed-tag HEAD^{tree} && get_tag_msg tree-signed-tag >actual && - git diff expect actual + test_cmp expect actual ' get_tag_header blob-signed-tag $blob blob $time >expect @@ -1014,7 +1014,7 @@ test_expect_success \ 'creating a signed tag pointing to a blob should succeed' ' git-tag -s -m "A message for a blob" blob-signed-tag HEAD:foo && get_tag_msg blob-signed-tag >actual && - git diff expect actual + test_cmp expect actual ' get_tag_header tag-signed-tag $tag tag $time >expect @@ -1024,7 +1024,7 @@ test_expect_success \ 'creating a signed tag pointing to another tag should succeed' ' git-tag -s -m "A message for another tag" tag-signed-tag signed-tag && get_tag_msg tag-signed-tag >actual && - git diff expect actual + test_cmp expect actual ' # try to sign with bad user.signingkey @@ -1064,7 +1064,7 @@ test_expect_success \ git tag -a -m "An annotation to be reused" reuse && GIT_EDITOR=true git tag -f -a reuse && get_tag_msg reuse >actual && - git diff expect actual + test_cmp expect actual ' test_done diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index e5c9f30c73..39ba14148c 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -34,13 +34,13 @@ test_expect_success 'creating initial files and commits' ' check_changes () { test "$(git rev-parse HEAD)" = "$1" && - git diff | git diff .diff_expect - && - git diff --cached | git diff .cached_expect - && + git diff | test_cmp .diff_expect - && + git diff --cached | test_cmp .cached_expect - && for FILE in * do echo $FILE':' cat $FILE || return - done | git diff .cat_expect - + done | test_cmp .cat_expect - } >.diff_expect @@ -390,9 +390,9 @@ test_expect_success 'test --mixed ' ' git add file1 file3 file4 && ! git reset HEAD -- file1 file2 file3 && git diff > output && - git diff output expect && + test_cmp output expect && git diff --cached > output && - git diff output cached_expect + test_cmp output cached_expect ' test_expect_success 'test resetting the index at give paths' ' @@ -425,7 +425,7 @@ EOF test_expect_success '--mixed refreshes the index' ' echo 123 >> file2 && git reset --mixed HEAD > output && - git diff --exit-code expect output + test_cmp expect output ' test_done diff --git a/t/t7502-status.sh b/t/t7502-status.sh index e4bfcaece0..80a438d4d9 100755 --- a/t/t7502-status.sh +++ b/t/t7502-status.sh @@ -63,7 +63,7 @@ EOF test_expect_success 'status (2)' ' git status > output && - git diff expect output + test_cmp expect output ' @@ -93,7 +93,7 @@ EOF test_expect_success 'status with relative paths' ' (cd dir1 && git status) > output && - git diff expect output + test_cmp expect output ' @@ -124,7 +124,7 @@ test_expect_success 'status without relative paths' ' git config status.relativePaths false (cd dir1 && git status) > output && - git diff expect output + test_cmp expect output ' diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh index bdf29c1734..242cdf092a 100755 --- a/t/t9100-git-svn-basic.sh +++ b/t/t9100-git-svn-basic.sh @@ -169,7 +169,7 @@ test_expect_success "$name" ' svn up "$SVN_TREE" && test -f "$SVN_TREE"/exec-2.sh && test ! -L "$SVN_TREE"/exec-2.sh && - git diff help "$SVN_TREE"/exec-2.sh' + test_cmp help "$SVN_TREE"/exec-2.sh' if test "$have_utf8" = t then @@ -193,7 +193,7 @@ test_expect_success "$name" \ 'git-svn init "$svnrepo" && git-svn fetch && git rev-list --pretty=raw remotes/git-svn | grep ^tree | uniq > a && git rev-list --pretty=raw remotes/alt | grep ^tree | uniq > b && - git diff a b' + test_cmp a b' name='check imported tree checksums expected tree checksums' rm -f expected @@ -211,7 +211,7 @@ tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e tree 8f51f74cf0163afc9ad68a4b1537288c4558b5a4 EOF -test_expect_success "$name" "git diff a expected" +test_expect_success "$name" "test_cmp a expected" test_expect_success 'exit if remote refs are ambigious' " git config --add svn-remote.svn.fetch \ diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index c4f4465dc6..5edf56f198 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -74,7 +74,7 @@ EOF test_expect_success \ 'A: verify commit' \ 'git cat-file commit master | sed 1d >actual && - git diff expect actual' + test_cmp expect actual' cat >expect <actual && - git diff expect actual' + test_cmp expect actual' echo "$file2_data" >expect test_expect_success \ 'A: verify file2' \ - 'git cat-file blob master:file2 >actual && git diff expect actual' + 'git cat-file blob master:file2 >actual && test_cmp expect actual' echo "$file3_data" >expect test_expect_success \ 'A: verify file3' \ - 'git cat-file blob master:file3 >actual && git diff expect actual' + 'git cat-file blob master:file3 >actual && test_cmp expect actual' printf "$file4_data" >expect test_expect_success \ 'A: verify file4' \ - 'git cat-file blob master:file4 >actual && git diff expect actual' + 'git cat-file blob master:file4 >actual && test_cmp expect actual' cat >expect <expect <input <actual && - git diff expect actual' + test_cmp expect actual' cat >expect <expect test_expect_success \ 'D: verify file5' \ 'git cat-file blob branch:newdir/interesting >actual && - git diff expect actual' + test_cmp expect actual' echo "$file6_data" >expect test_expect_success \ 'D: verify file6' \ 'git cat-file blob branch:newdir/exec.sh >actual && - git diff expect actual' + test_cmp expect actual' ### ### series E @@ -358,7 +358,7 @@ EOF test_expect_success \ 'E: verify commit' \ 'git cat-file commit branch | sed 1,2d >actual && - git diff expect actual' + test_cmp expect actual' ### ### series F @@ -411,7 +411,7 @@ EOF test_expect_success \ 'F: verify other commit' \ 'git cat-file commit other >actual && - git diff expect actual' + test_cmp expect actual' ### ### series G @@ -489,7 +489,7 @@ echo "$file5_data" >expect test_expect_success \ 'H: verify file' \ 'git cat-file blob H:h/e/l/lo >actual && - git diff expect actual' + test_cmp expect actual' ### ### series I @@ -515,7 +515,7 @@ EOF test_expect_success \ 'I: verify edge list' \ 'sed -e s/pack-.*pack/pack-.pack/ edges.list >actual && - git diff expect actual' + test_cmp expect actual' ### ### series J @@ -625,7 +625,7 @@ test_expect_success \ 'L: verify internal tree sorting' \ 'git-fast-import output && - git diff expect output' + test_cmp expect output' ### ### series M @@ -885,7 +885,7 @@ test_expect_success \ test 8 = `find .git/objects/pack -type f | wc -l` && test `git rev-parse refs/tags/O3-2nd` = `git rev-parse O3^` && git log --reverse --pretty=oneline O3 | sed s/^.*z// >actual && - git diff expect actual' + test_cmp expect actual' cat >input <actual && grep "progress " expect && - git diff expect actual' + test_cmp expect actual' test_done diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh index 0b115a17ab..655f88270b 100755 --- a/t/t9600-cvsimport.sh +++ b/t/t9600-cvsimport.sh @@ -69,7 +69,7 @@ EOF test_expect_success 'import a trivial module' ' git cvsimport -a -z 0 -C module-git module && - git diff module-cvs/o_fortuna module-git/o_fortuna + test_cmp module-cvs/o_fortuna module-git/o_fortuna ' @@ -110,7 +110,7 @@ test_expect_success 'update git module' ' git cvsimport -a -z 0 module && git merge origin && cd .. && - git diff module-cvs/o_fortuna module-git/o_fortuna + test_cmp module-cvs/o_fortuna module-git/o_fortuna ' @@ -131,7 +131,7 @@ test_expect_success 'cvsimport.module config works' ' git cvsimport -a -z0 && git merge origin && cd .. && - git diff module-cvs/tick module-git/tick + test_cmp module-cvs/tick module-git/tick ' @@ -142,7 +142,7 @@ test_expect_success 'import from a CVS working tree' ' git cvsimport -a -z0 && echo 1 >expect && git log -1 --pretty=format:%s%n >actual && - git diff actual expect && + test_cmp actual expect && cd .. ' From 6304c29d518206b0780291a02f94f435abf82d74 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 23 May 2008 18:15:03 -0700 Subject: [PATCH 112/202] diff-files: do not play --no-index games Being able to say "git diff A B" outside a git repository and getting a colourful version of "diff -u A B" may be nice, but such a cute hack should not give bogus results to scripts that want to give two paths, either or both of which happen to have been removed from the work tree, to "git diff-files". Signed-off-by: Junio C Hamano --- Documentation/git-diff-files.txt | 5 +--- builtin-diff-files.c | 44 ++++++++++++++++++++++++++------ git.c | 2 +- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt index 6d2ea16a25..ca1bb6a8ee 100644 --- a/Documentation/git-diff-files.txt +++ b/Documentation/git-diff-files.txt @@ -8,7 +8,7 @@ git-diff-files - Compares files in the working tree and the index SYNOPSIS -------- -'git-diff-files' [-q] [-0|-1|-2|-3|-c|--cc|--no-index] [] [...] +'git-diff-files' [-q] [-0|-1|-2|-3|-c|--cc] [] [...] DESCRIPTION ----------- @@ -36,9 +36,6 @@ omit diff output for unmerged entries and just show "Unmerged". diff, similar to the way 'diff-tree' shows a merge commit with these flags. ---no-index:: - Compare the two given files / directories. - -q:: Remain silent even on nonexistent files diff --git a/builtin-diff-files.c b/builtin-diff-files.c index e2306c162a..3aa031f24f 100644 --- a/builtin-diff-files.c +++ b/builtin-diff-files.c @@ -10,26 +10,54 @@ #include "builtin.h" static const char diff_files_usage[] = -"git-diff-files [-q] [-0/-1/2/3 |-c|--cc|--no-index] [] [...]" +"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [] [...]" COMMON_DIFF_OPTIONS_HELP; int cmd_diff_files(int argc, const char **argv, const char *prefix) { struct rev_info rev; - int nongit; int result; + unsigned options = 0; - prefix = setup_git_directory_gently(&nongit); init_revisions(&rev, prefix); git_config(git_diff_basic_config); /* no "diff" UI options */ rev.abbrev = 0; - if (!setup_diff_no_index(&rev, argc, argv, nongit, prefix)) - argc = 0; - else - argc = setup_revisions(argc, argv, &rev, NULL); + argc = setup_revisions(argc, argv, &rev, NULL); + while (1 < argc && argv[1][0] == '-') { + if (!strcmp(argv[1], "--base")) + rev.max_count = 1; + else if (!strcmp(argv[1], "--ours")) + rev.max_count = 2; + else if (!strcmp(argv[1], "--theirs")) + rev.max_count = 3; + else if (!strcmp(argv[1], "-q")) + options |= DIFF_SILENT_ON_REMOVED; + else + usage(diff_files_usage); + argv++; argc--; + } if (!rev.diffopt.output_format) rev.diffopt.output_format = DIFF_FORMAT_RAW; - result = run_diff_files_cmd(&rev, argc, argv); + + /* + * Make sure there are NO revision (i.e. pending object) parameter, + * rev.max_count is reasonable (0 <= n <= 3), and + * there is no other revision filtering parameters. + */ + if (rev.pending.nr || + rev.min_age != -1 || rev.max_age != -1 || + 3 < rev.max_count) + usage(diff_files_usage); + + if (rev.max_count == -1 && + (rev.diffopt.output_format & DIFF_FORMAT_PATCH)) + rev.combine_merges = rev.dense_combined_merges = 1; + + if (read_cache() < 0) { + perror("read_cache"); + return -1; + } + result = run_diff_files(&rev, options); return diff_result_code(&rev.diffopt, result); } diff --git a/git.c b/git.c index 89b431fa28..4b79380b72 100644 --- a/git.c +++ b/git.c @@ -293,7 +293,7 @@ static void handle_internal_command(int argc, const char **argv) { "count-objects", cmd_count_objects, RUN_SETUP }, { "describe", cmd_describe, RUN_SETUP }, { "diff", cmd_diff }, - { "diff-files", cmd_diff_files }, + { "diff-files", cmd_diff_files, RUN_SETUP }, { "diff-index", cmd_diff_index, RUN_SETUP }, { "diff-tree", cmd_diff_tree, RUN_SETUP }, { "fast-export", cmd_fast_export, RUN_SETUP }, From 0569e9b8cea20d5eedfec66730a9711a0907ab0d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 23 May 2008 22:28:56 -0700 Subject: [PATCH 113/202] "git diff": do not ignore index without --no-index Even if "foo" and/or "bar" does not exist in index, "git diff foo bar" should not change behaviour drastically from "git diff foo bar baz" or "git diff foo". A feature that "sometimes works and is handy" is an unreliable cute hack. "git diff foo bar" outside a git repository continues to work as a more colourful alternative to "diff -u" as before. Signed-off-by: Junio C Hamano --- Makefile | 1 + builtin-diff.c | 55 ++- diff-lib.c | 323 ------------------ diff-no-index.c | 231 +++++++++++++ diff.h | 6 +- t/t4013-diff-various.sh | 1 + t/t4013/diff.diff_--name-status_dir2_dir | 1 - ...iff.diff_--no-index_--name-status_dir2_dir | 3 + 8 files changed, 283 insertions(+), 338 deletions(-) create mode 100644 diff-no-index.c create mode 100644 t/t4013/diff.diff_--no-index_--name-status_dir2_dir diff --git a/Makefile b/Makefile index 865e2bfcf1..ad09e6c8ba 100644 --- a/Makefile +++ b/Makefile @@ -405,6 +405,7 @@ LIB_OBJS += diffcore-order.o LIB_OBJS += diffcore-pickaxe.o LIB_OBJS += diffcore-rename.o LIB_OBJS += diff-delta.o +LIB_OBJS += diff-no-index.o LIB_OBJS += diff-lib.o LIB_OBJS += diff.o LIB_OBJS += dir.o diff --git a/builtin-diff.c b/builtin-diff.c index 7c2a8412fa..8b3b51eae0 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -202,6 +202,37 @@ static void refresh_index_quietly(void) rollback_lock_file(lock_file); } +static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv) +{ + int result; + unsigned int options = 0; + + while (1 < argc && argv[1][0] == '-') { + if (!strcmp(argv[1], "--base")) + revs->max_count = 1; + else if (!strcmp(argv[1], "--ours")) + revs->max_count = 2; + else if (!strcmp(argv[1], "--theirs")) + revs->max_count = 3; + else if (!strcmp(argv[1], "-q")) + options |= DIFF_SILENT_ON_REMOVED; + else + return error("invalid option: %s", argv[1]); + argv++; argc--; + } + + if (revs->max_count == -1 && + (revs->diffopt.output_format & DIFF_FORMAT_PATCH)) + revs->combine_merges = revs->dense_combined_merges = 1; + + if (read_cache() < 0) { + perror("read_cache"); + return -1; + } + result = run_diff_files(revs, options); + return diff_result_code(&revs->diffopt, result); +} + int cmd_diff(int argc, const char **argv, const char *prefix) { int i; @@ -230,6 +261,9 @@ int cmd_diff(int argc, const char **argv, const char *prefix) * N=2, M=0: * tree vs tree (diff-tree) * + * N=0, M=0, P=2: + * compare two filesystem entities (aka --no-index). + * * Other cases are errors. */ @@ -240,21 +274,21 @@ int cmd_diff(int argc, const char **argv, const char *prefix) diff_use_color_default = git_use_color_default; init_revisions(&rev, prefix); + + /* If this is a no-index diff, just run it and exit there. */ + diff_no_index(&rev, argc, argv, nongit, prefix); + + /* Otherwise, we are doing the usual "git" diff */ rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index; - if (!setup_diff_no_index(&rev, argc, argv, nongit, prefix)) - argc = 0; - else - argc = setup_revisions(argc, argv, &rev, NULL); + if (nongit) + die("Not a git repository"); + argc = setup_revisions(argc, argv, &rev, NULL); if (!rev.diffopt.output_format) { rev.diffopt.output_format = DIFF_FORMAT_PATCH; if (diff_setup_done(&rev.diffopt) < 0) die("diff_setup_done failed"); } - if (rev.diffopt.prefix && nongit) { - rev.diffopt.prefix = NULL; - rev.diffopt.prefix_length = 0; - } DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL); DIFF_OPT_SET(&rev.diffopt, RECURSIVE); @@ -265,7 +299,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix) if (!DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS)) setup_pager(); - /* Do we have --cached and not have a pending object, then + /* + * Do we have --cached and not have a pending object, then * default to HEAD by hand. Eek. */ if (!rev.pending.nr) { @@ -333,7 +368,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) if (!ents) { switch (blobs) { case 0: - result = run_diff_files_cmd(&rev, argc, argv); + result = builtin_diff_files(&rev, argc, argv); break; case 1: if (paths != 1) diff --git a/diff-lib.c b/diff-lib.c index fe2ccec7e6..b17722d66a 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -8,7 +8,6 @@ #include "diffcore.h" #include "revision.h" #include "cache-tree.h" -#include "path-list.h" #include "unpack-trees.h" #include "refs.h" @@ -16,328 +15,6 @@ * diff-files */ -static int read_directory(const char *path, struct path_list *list) -{ - DIR *dir; - struct dirent *e; - - if (!(dir = opendir(path))) - return error("Could not open directory %s", path); - - while ((e = readdir(dir))) - if (strcmp(".", e->d_name) && strcmp("..", e->d_name)) - path_list_insert(e->d_name, list); - - closedir(dir); - return 0; -} - -static int get_mode(const char *path, int *mode) -{ - struct stat st; - - if (!path || !strcmp(path, "/dev/null")) - *mode = 0; - else if (!strcmp(path, "-")) - *mode = create_ce_mode(0666); - else if (stat(path, &st)) - return error("Could not access '%s'", path); - else - *mode = st.st_mode; - return 0; -} - -static int queue_diff(struct diff_options *o, - const char *name1, const char *name2) -{ - int mode1 = 0, mode2 = 0; - - if (get_mode(name1, &mode1) || get_mode(name2, &mode2)) - return -1; - - if (mode1 && mode2 && S_ISDIR(mode1) != S_ISDIR(mode2)) - return error("file/directory conflict: %s, %s", name1, name2); - - if (S_ISDIR(mode1) || S_ISDIR(mode2)) { - char buffer1[PATH_MAX], buffer2[PATH_MAX]; - struct path_list p1 = {NULL, 0, 0, 1}, p2 = {NULL, 0, 0, 1}; - int len1 = 0, len2 = 0, i1, i2, ret = 0; - - if (name1 && read_directory(name1, &p1)) - return -1; - if (name2 && read_directory(name2, &p2)) { - path_list_clear(&p1, 0); - return -1; - } - - if (name1) { - len1 = strlen(name1); - if (len1 > 0 && name1[len1 - 1] == '/') - len1--; - memcpy(buffer1, name1, len1); - buffer1[len1++] = '/'; - } - - if (name2) { - len2 = strlen(name2); - if (len2 > 0 && name2[len2 - 1] == '/') - len2--; - memcpy(buffer2, name2, len2); - buffer2[len2++] = '/'; - } - - for (i1 = i2 = 0; !ret && (i1 < p1.nr || i2 < p2.nr); ) { - const char *n1, *n2; - int comp; - - if (i1 == p1.nr) - comp = 1; - else if (i2 == p2.nr) - comp = -1; - else - comp = strcmp(p1.items[i1].path, - p2.items[i2].path); - - if (comp > 0) - n1 = NULL; - else { - n1 = buffer1; - strncpy(buffer1 + len1, p1.items[i1++].path, - PATH_MAX - len1); - } - - if (comp < 0) - n2 = NULL; - else { - n2 = buffer2; - strncpy(buffer2 + len2, p2.items[i2++].path, - PATH_MAX - len2); - } - - ret = queue_diff(o, n1, n2); - } - path_list_clear(&p1, 0); - path_list_clear(&p2, 0); - - return ret; - } else { - struct diff_filespec *d1, *d2; - - if (DIFF_OPT_TST(o, REVERSE_DIFF)) { - unsigned tmp; - const char *tmp_c; - tmp = mode1; mode1 = mode2; mode2 = tmp; - tmp_c = name1; name1 = name2; name2 = tmp_c; - } - - if (!name1) - name1 = "/dev/null"; - if (!name2) - name2 = "/dev/null"; - d1 = alloc_filespec(name1); - d2 = alloc_filespec(name2); - fill_filespec(d1, null_sha1, mode1); - fill_filespec(d2, null_sha1, mode2); - - diff_queue(&diff_queued_diff, d1, d2); - return 0; - } -} - -/* - * Does the path name a blob in the working tree, or a directory - * in the working tree? - */ -static int is_in_index(const char *path) -{ - int len, pos; - struct cache_entry *ce; - - len = strlen(path); - while (path[len-1] == '/') - len--; - if (!len) - return 1; /* "." */ - pos = cache_name_pos(path, len); - if (0 <= pos) - return 1; - pos = -1 - pos; - while (pos < active_nr) { - ce = active_cache[pos++]; - if (ce_namelen(ce) <= len || - strncmp(ce->name, path, len) || - (ce->name[len] > '/')) - break; /* path cannot be a prefix */ - if (ce->name[len] == '/') - return 1; - } - return 0; -} - -static int handle_diff_files_args(struct rev_info *revs, - int argc, const char **argv, - unsigned int *options) -{ - *options = 0; - - /* revs->max_count == -2 means --no-index */ - while (1 < argc && argv[1][0] == '-') { - if (!strcmp(argv[1], "--base")) - revs->max_count = 1; - else if (!strcmp(argv[1], "--ours")) - revs->max_count = 2; - else if (!strcmp(argv[1], "--theirs")) - revs->max_count = 3; - else if (!strcmp(argv[1], "-n") || - !strcmp(argv[1], "--no-index")) { - revs->max_count = -2; - DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS); - DIFF_OPT_SET(&revs->diffopt, NO_INDEX); - } - else if (!strcmp(argv[1], "-q")) - *options |= DIFF_SILENT_ON_REMOVED; - else - return error("invalid option: %s", argv[1]); - argv++; argc--; - } - - if (revs->max_count == -1 && revs->diffopt.nr_paths == 2) { - /* - * If two files are specified, and at least one is untracked, - * default to no-index. - */ - read_cache(); - if (!is_in_index(revs->diffopt.paths[0]) || - !is_in_index(revs->diffopt.paths[1])) { - revs->max_count = -2; - DIFF_OPT_SET(&revs->diffopt, NO_INDEX); - } - } - - /* - * Make sure there are NO revision (i.e. pending object) parameter, - * rev.max_count is reasonable (0 <= n <= 3), - * there is no other revision filtering parameters. - */ - if (revs->pending.nr || revs->max_count > 3 || - revs->min_age != -1 || revs->max_age != -1) - return error("no revision allowed with diff-files"); - - if (revs->max_count == -1 && - (revs->diffopt.output_format & DIFF_FORMAT_PATCH)) - revs->combine_merges = revs->dense_combined_merges = 1; - - return 0; -} - -static int is_outside_repo(const char *path, int nongit, const char *prefix) -{ - int i; - if (nongit || !strcmp(path, "-") || is_absolute_path(path)) - return 1; - if (prefixcmp(path, "../")) - return 0; - if (!prefix) - return 1; - for (i = strlen(prefix); !prefixcmp(path, "../"); ) { - while (i > 0 && prefix[i - 1] != '/') - i--; - if (--i < 0) - return 1; - path += 3; - } - return 0; -} - -int setup_diff_no_index(struct rev_info *revs, - int argc, const char ** argv, int nongit, const char *prefix) -{ - int i; - for (i = 1; i < argc; i++) - if (argv[i][0] != '-' || argv[i][1] == '\0') - break; - else if (!strcmp(argv[i], "--")) { - i++; - break; - } else if (i < argc - 3 && !strcmp(argv[i], "--no-index")) { - i = argc - 3; - DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS); - break; - } - if (nongit && argc != i + 2) - die("git diff [--no-index] takes two paths"); - - if (argc != i + 2 || (!is_outside_repo(argv[i + 1], nongit, prefix) && - !is_outside_repo(argv[i], nongit, prefix))) - return -1; - - diff_setup(&revs->diffopt); - for (i = 1; i < argc - 2; ) - if (!strcmp(argv[i], "--no-index")) - i++; - else { - int j = diff_opt_parse(&revs->diffopt, - argv + i, argc - i); - if (!j) - die("invalid diff option/value: %s", argv[i]); - i += j; - } - - if (prefix) { - int len = strlen(prefix); - - revs->diffopt.paths = xcalloc(2, sizeof(char*)); - for (i = 0; i < 2; i++) { - const char *p = argv[argc - 2 + i]; - /* - * stdin should be spelled as '-'; if you have - * path that is '-', spell it as ./-. - */ - p = (strcmp(p, "-") - ? xstrdup(prefix_filename(prefix, len, p)) - : p); - revs->diffopt.paths[i] = p; - } - } - else - revs->diffopt.paths = argv + argc - 2; - revs->diffopt.nr_paths = 2; - DIFF_OPT_SET(&revs->diffopt, NO_INDEX); - revs->max_count = -2; - if (diff_setup_done(&revs->diffopt) < 0) - die("diff_setup_done failed"); - return 0; -} - -int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv) -{ - unsigned int options; - - if (handle_diff_files_args(revs, argc, argv, &options)) - return -1; - - if (DIFF_OPT_TST(&revs->diffopt, NO_INDEX)) { - if (revs->diffopt.nr_paths != 2) - return error("need two files/directories with --no-index"); - if (queue_diff(&revs->diffopt, revs->diffopt.paths[0], - revs->diffopt.paths[1])) - return -1; - diffcore_std(&revs->diffopt); - diff_flush(&revs->diffopt); - /* - * The return code for --no-index imitates diff(1): - * 0 = no changes, 1 = changes, else error - */ - return revs->diffopt.found_changes; - } - - if (read_cache() < 0) { - perror("read_cache"); - return -1; - } - return run_diff_files(revs, options); -} - /* * Has the work tree entity been removed? * diff --git a/diff-no-index.c b/diff-no-index.c new file mode 100644 index 0000000000..1b57feeb24 --- /dev/null +++ b/diff-no-index.c @@ -0,0 +1,231 @@ +/* + * "diff --no-index" support + * Copyright (c) 2007 by Johannes Schindelin + * Copyright (c) 2008 by Junio C Hamano + */ + +#include "cache.h" +#include "color.h" +#include "commit.h" +#include "blob.h" +#include "tag.h" +#include "diff.h" +#include "diffcore.h" +#include "revision.h" +#include "log-tree.h" +#include "builtin.h" +#include "path-list.h" + +static int read_directory(const char *path, struct path_list *list) +{ + DIR *dir; + struct dirent *e; + + if (!(dir = opendir(path))) + return error("Could not open directory %s", path); + + while ((e = readdir(dir))) + if (strcmp(".", e->d_name) && strcmp("..", e->d_name)) + path_list_insert(e->d_name, list); + + closedir(dir); + return 0; +} + +static int get_mode(const char *path, int *mode) +{ + struct stat st; + + if (!path || !strcmp(path, "/dev/null")) + *mode = 0; + else if (!strcmp(path, "-")) + *mode = create_ce_mode(0666); + else if (stat(path, &st)) + return error("Could not access '%s'", path); + else + *mode = st.st_mode; + return 0; +} + +static int queue_diff(struct diff_options *o, + const char *name1, const char *name2) +{ + int mode1 = 0, mode2 = 0; + + if (get_mode(name1, &mode1) || get_mode(name2, &mode2)) + return -1; + + if (mode1 && mode2 && S_ISDIR(mode1) != S_ISDIR(mode2)) + return error("file/directory conflict: %s, %s", name1, name2); + + if (S_ISDIR(mode1) || S_ISDIR(mode2)) { + char buffer1[PATH_MAX], buffer2[PATH_MAX]; + struct path_list p1 = {NULL, 0, 0, 1}, p2 = {NULL, 0, 0, 1}; + int len1 = 0, len2 = 0, i1, i2, ret = 0; + + if (name1 && read_directory(name1, &p1)) + return -1; + if (name2 && read_directory(name2, &p2)) { + path_list_clear(&p1, 0); + return -1; + } + + if (name1) { + len1 = strlen(name1); + if (len1 > 0 && name1[len1 - 1] == '/') + len1--; + memcpy(buffer1, name1, len1); + buffer1[len1++] = '/'; + } + + if (name2) { + len2 = strlen(name2); + if (len2 > 0 && name2[len2 - 1] == '/') + len2--; + memcpy(buffer2, name2, len2); + buffer2[len2++] = '/'; + } + + for (i1 = i2 = 0; !ret && (i1 < p1.nr || i2 < p2.nr); ) { + const char *n1, *n2; + int comp; + + if (i1 == p1.nr) + comp = 1; + else if (i2 == p2.nr) + comp = -1; + else + comp = strcmp(p1.items[i1].path, + p2.items[i2].path); + + if (comp > 0) + n1 = NULL; + else { + n1 = buffer1; + strncpy(buffer1 + len1, p1.items[i1++].path, + PATH_MAX - len1); + } + + if (comp < 0) + n2 = NULL; + else { + n2 = buffer2; + strncpy(buffer2 + len2, p2.items[i2++].path, + PATH_MAX - len2); + } + + ret = queue_diff(o, n1, n2); + } + path_list_clear(&p1, 0); + path_list_clear(&p2, 0); + + return ret; + } else { + struct diff_filespec *d1, *d2; + + if (DIFF_OPT_TST(o, REVERSE_DIFF)) { + unsigned tmp; + const char *tmp_c; + tmp = mode1; mode1 = mode2; mode2 = tmp; + tmp_c = name1; name1 = name2; name2 = tmp_c; + } + + if (!name1) + name1 = "/dev/null"; + if (!name2) + name2 = "/dev/null"; + d1 = alloc_filespec(name1); + d2 = alloc_filespec(name2); + fill_filespec(d1, null_sha1, mode1); + fill_filespec(d2, null_sha1, mode2); + + diff_queue(&diff_queued_diff, d1, d2); + return 0; + } +} + +void diff_no_index(struct rev_info *revs, + int argc, const char **argv, + int nongit, const char *prefix) +{ + int i; + int no_index = 0; + unsigned options = 0; + + /* Were we asked to do --no-index explicitly? */ + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "--")) + return; + if (!strcmp(argv[i], "--no-index")) + no_index = 1; + if (argv[i][0] != '-') + break; + } + + /* + * No explicit --no-index, but "git diff --opts A B" outside + * a git repository is a cute hack to support. + */ + if (!no_index && !nongit) + return; + + if (argc != i + 2) + die("git diff %s takes two paths", + no_index ? "--no-index" : "[--no-index]"); + + diff_setup(&revs->diffopt); + if (!revs->diffopt.output_format) + revs->diffopt.output_format = DIFF_FORMAT_PATCH; + for (i = 1; i < argc - 2; ) { + int j; + if (!strcmp(argv[i], "--no-index")) + i++; + else if (!strcmp(argv[1], "-q")) + options |= DIFF_SILENT_ON_REMOVED; + else { + j = diff_opt_parse(&revs->diffopt, argv + i, argc - i); + if (!j) + die("invalid diff option/value: %s", argv[i]); + i += j; + } + } + + if (prefix) { + int len = strlen(prefix); + + revs->diffopt.paths = xcalloc(2, sizeof(char*)); + for (i = 0; i < 2; i++) { + const char *p = argv[argc - 2 + i]; + /* + * stdin should be spelled as '-'; if you have + * path that is '-', spell it as ./-. + */ + p = (strcmp(p, "-") + ? xstrdup(prefix_filename(prefix, len, p)) + : p); + revs->diffopt.paths[i] = p; + } + } + else + revs->diffopt.paths = argv + argc - 2; + revs->diffopt.nr_paths = 2; + + DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS); + DIFF_OPT_SET(&revs->diffopt, NO_INDEX); + + revs->max_count = -2; + if (diff_setup_done(&revs->diffopt) < 0) + die("diff_setup_done failed"); + + if (queue_diff(&revs->diffopt, revs->diffopt.paths[0], + revs->diffopt.paths[1])) + exit(1); + diffcore_std(&revs->diffopt); + diff_flush(&revs->diffopt); + + /* + * The return code for --no-index imitates diff(1): + * 0 = no changes, 1 = changes, else error + */ + exit(revs->diffopt.found_changes); +} diff --git a/diff.h b/diff.h index 3a02d38d12..e019730638 100644 --- a/diff.h +++ b/diff.h @@ -250,10 +250,6 @@ extern const char *diff_unique_abbrev(const unsigned char *, int); /* report racily-clean paths as modified */ #define DIFF_RACY_IS_MODIFIED 02 extern int run_diff_files(struct rev_info *revs, unsigned int option); -extern int setup_diff_no_index(struct rev_info *revs, - int argc, const char ** argv, int nongit, const char *prefix); -extern int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv); - extern int run_diff_index(struct rev_info *revs, int cached); extern int do_diff_cache(const unsigned char *, struct diff_options *); @@ -261,4 +257,6 @@ extern int diff_flush_patch_id(struct diff_options *, unsigned char *); extern int diff_result_code(struct diff_options *, int); +extern void diff_no_index(struct rev_info *, int, const char **, int, const char *); + #endif /* DIFF_H */ diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index 4c038ccec1..9337b81064 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -257,6 +257,7 @@ diff --patch-with-raw initial..side diff --patch-with-stat -r initial..side diff --patch-with-raw -r initial..side diff --name-status dir2 dir +diff --no-index --name-status dir2 dir EOF test_done diff --git a/t/t4013/diff.diff_--name-status_dir2_dir b/t/t4013/diff.diff_--name-status_dir2_dir index ef7fdb7335..d0d96aaa91 100644 --- a/t/t4013/diff.diff_--name-status_dir2_dir +++ b/t/t4013/diff.diff_--name-status_dir2_dir @@ -1,3 +1,2 @@ $ git diff --name-status dir2 dir -A dir/sub $ diff --git a/t/t4013/diff.diff_--no-index_--name-status_dir2_dir b/t/t4013/diff.diff_--no-index_--name-status_dir2_dir new file mode 100644 index 0000000000..6a47584777 --- /dev/null +++ b/t/t4013/diff.diff_--no-index_--name-status_dir2_dir @@ -0,0 +1,3 @@ +$ git diff --no-index --name-status dir2 dir +A dir/sub +$ From 633f43e1f774337ce1d8b8cf01c5bbe82b086ef6 Mon Sep 17 00:00:00 2001 From: Heikki Orsila Date: Sat, 24 May 2008 01:43:55 +0300 Subject: [PATCH 114/202] Remove redundant code, eliminate one static variable Signed-off-by: Heikki Orsila Signed-off-by: Junio C Hamano --- sha1_file.c | 46 ++++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/sha1_file.c b/sha1_file.c index 141f5a713a..c3170961ce 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -176,21 +176,22 @@ char *sha1_file_name(const unsigned char *sha1) return base; } -char *sha1_pack_name(const unsigned char *sha1) +static char *sha1_get_pack_name(const unsigned char *sha1, + char **name, char **base) { static const char hex[] = "0123456789abcdef"; - static char *name, *base, *buf; + char *buf; int i; - if (!base) { + if (!*base) { const char *sha1_file_directory = get_object_directory(); int len = strlen(sha1_file_directory); - base = xmalloc(len + 60); - sprintf(base, "%s/pack/pack-1234567890123456789012345678901234567890.pack", sha1_file_directory); - name = base + len + 11; + *base = xmalloc(len + 60); + sprintf(*base, "%s/pack/pack-1234567890123456789012345678901234567890.pack", sha1_file_directory); + *name = *base + len + 11; } - buf = name; + buf = *name; for (i = 0; i < 20; i++) { unsigned int val = *sha1++; @@ -198,32 +199,21 @@ char *sha1_pack_name(const unsigned char *sha1) *buf++ = hex[val & 0xf]; } - return base; + return *base; +} + +char *sha1_pack_name(const unsigned char *sha1) +{ + static char *name, *base; + + return sha1_get_pack_name(sha1, &name, &base); } char *sha1_pack_index_name(const unsigned char *sha1) { - static const char hex[] = "0123456789abcdef"; - static char *name, *base, *buf; - int i; + static char *name, *base; - if (!base) { - const char *sha1_file_directory = get_object_directory(); - int len = strlen(sha1_file_directory); - base = xmalloc(len + 60); - sprintf(base, "%s/pack/pack-1234567890123456789012345678901234567890.idx", sha1_file_directory); - name = base + len + 11; - } - - buf = name; - - for (i = 0; i < 20; i++) { - unsigned int val = *sha1++; - *buf++ = hex[val >> 4]; - *buf++ = hex[val & 0xf]; - } - - return base; + return sha1_get_pack_name(sha1, &name, &base); } struct alternate_object_database *alt_odb_list; From 0b0b8cd7c2c3df72fc3959805b035e55e1bb1270 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Fri, 23 May 2008 01:26:09 +0200 Subject: [PATCH 115/202] CodingGuidelines: Add a note to avoid assignments inside if() Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- Documentation/CodingGuidelines | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index 994eb9159a..d2a0a76e6c 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -89,6 +89,8 @@ For C programs: of "else if" statements, it can make sense to add braces to single line blocks. + - We try to avoid assignments inside if(). + - Try to make your code understandable. You may put comments in, but comments invariably tend to stale out when the code they were describing changes. Often splitting a function From b27a23e35d8e532e47661595bda642ef3a7375f1 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 24 May 2008 20:56:44 +0200 Subject: [PATCH 116/202] Documentation: convert tutorials to man pages This patch renames the following documents and at the same time converts them to the man page format: cvs-migration.txt -> gitcvs-migration.txt tutorial.txt -> gittutorial.txt tutorial-2.txt -> gittutorial-2.txt These new man pages are put in section 7, and other documents that reference the above ones are change accordingly. [jc: with help from Nanako to clean things up] Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- Documentation/Makefile | 8 ++--- Documentation/core-tutorial.txt | 4 +-- Documentation/git.txt | 4 +-- ...cvs-migration.txt => gitcvs-migration.txt} | 29 ++++++++++++++--- .../{tutorial-2.txt => gittutorial-2.txt} | 30 ++++++++++++++--- .../{tutorial.txt => gittutorial.txt} | 32 ++++++++++++++++--- Documentation/user-manual.txt | 4 +-- 7 files changed, 87 insertions(+), 24 deletions(-) rename Documentation/{cvs-migration.txt => gitcvs-migration.txt} (93%) rename Documentation/{tutorial-2.txt => gittutorial-2.txt} (96%) rename Documentation/{tutorial.txt => gittutorial.txt} (97%) diff --git a/Documentation/Makefile b/Documentation/Makefile index 4144d1e086..9750334b97 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -3,7 +3,8 @@ MAN1_TXT= \ $(wildcard git-*.txt)) \ gitk.txt MAN5_TXT=gitattributes.txt gitignore.txt gitmodules.txt githooks.txt -MAN7_TXT=git.txt gitcli.txt +MAN7_TXT=git.txt gitcli.txt gittutorial.txt gittutorial-2.txt \ + gitcvs-migration.txt MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT) MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT)) @@ -11,10 +12,7 @@ MAN_HTML=$(patsubst %.txt,%.html,$(MAN_TXT)) DOC_HTML=$(MAN_HTML) -ARTICLES = tutorial -ARTICLES += tutorial-2 -ARTICLES += core-tutorial -ARTICLES += cvs-migration +ARTICLES = core-tutorial ARTICLES += diffcore ARTICLES += howto-index ARTICLES += repository-layout diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt index 5a5531222d..b50b5dd487 100644 --- a/Documentation/core-tutorial.txt +++ b/Documentation/core-tutorial.txt @@ -8,7 +8,7 @@ This tutorial explains how to use the "core" git programs to set up and work with a git repository. If you just need to use git as a revision control system you may prefer -to start with link:tutorial.html[a tutorial introduction to git] or +to start with linkgit:gittutorial[7][a tutorial introduction to git] or link:user-manual.html[the git user manual]. However, an understanding of these low-level tools can be helpful if @@ -1581,7 +1581,7 @@ suggested in the previous section may be new to you. You do not have to worry. git supports "shared public repository" style of cooperation you are probably more familiar with as well. -See link:cvs-migration.html[git for CVS users] for the details. +See linkgit:gitcvs-migration[7][git for CVS users] for the details. Bundling your work together --------------------------- diff --git a/Documentation/git.txt b/Documentation/git.txt index adcd3e00b2..735f0d19c8 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -20,10 +20,10 @@ Git is a fast, scalable, distributed revision control system with an unusually rich command set that provides both high-level operations and full access to internals. -See this link:tutorial.html[tutorial] to get started, then see +See this linkgit:gittutorial[7][tutorial] to get started, then see link:everyday.html[Everyday Git] for a useful minimum set of commands, and "man git-commandname" for documentation of each command. CVS users may -also want to read link:cvs-migration.html[CVS migration]. See +also want to read linkgit:gitcvs-migration[7][CVS migration]. See link:user-manual.html[Git User's Manual] for a more in-depth introduction. diff --git a/Documentation/cvs-migration.txt b/Documentation/gitcvs-migration.txt similarity index 93% rename from Documentation/cvs-migration.txt rename to Documentation/gitcvs-migration.txt index 374bc87b10..c410805027 100644 --- a/Documentation/cvs-migration.txt +++ b/Documentation/gitcvs-migration.txt @@ -1,5 +1,16 @@ -git for CVS users -================= +gitcvs-migration(7) +=================== + +NAME +---- +gitcvs-migration - git for CVS users + +SYNOPSIS +-------- +git cvsimport * + +DESCRIPTION +----------- Git differs from CVS in that every working tree contains a repository with a full copy of the project history, and no repository is inherently more @@ -8,7 +19,7 @@ designating a single shared repository which people can synchronize with; this document explains how to do that. Some basic familiarity with git is required. This -link:tutorial.html[tutorial introduction to git] and the +linkgit:gittutorial[7][tutorial introduction to git] and the link:glossary.html[git glossary] should be sufficient. Developing against a shared repository @@ -71,7 +82,7 @@ Setting Up a Shared Repository We assume you have already created a git repository for your project, possibly created from scratch or from a tarball (see the -link:tutorial.html[tutorial]), or imported from an already existing CVS +linkgit:gittutorial[7][tutorial]), or imported from an already existing CVS repository (see the next section). Assume your existing repo is at /home/alice/myproject. Create a new "bare" @@ -170,3 +181,13 @@ variants of this model. With a small group, developers may just pull changes from each other's repositories without the need for a central maintainer. + +SEE ALSO +-------- +linkgit:gittutorial[7], linkgit:gittutorial-2[7], +link:everyday.html[Everyday Git], +link:user-manual.html[The Git User's Manual] + +GIT +--- +Part of the linkgit:git[7] suite. diff --git a/Documentation/tutorial-2.txt b/Documentation/gittutorial-2.txt similarity index 96% rename from Documentation/tutorial-2.txt rename to Documentation/gittutorial-2.txt index 7fac47de8b..5bbbf43056 100644 --- a/Documentation/tutorial-2.txt +++ b/Documentation/gittutorial-2.txt @@ -1,7 +1,18 @@ -A tutorial introduction to git: part two -======================================== +gittutorial-2(7) +================ -You should work through link:tutorial.html[A tutorial introduction to +NAME +---- +gittutorial-2 - A tutorial introduction to git: part two + +SYNOPSIS +-------- +git * + +DESCRIPTION +----------- + +You should work through linkgit:gittutorial[7][A tutorial introduction to git] before reading this tutorial. The goal of this tutorial is to introduce two fundamental pieces of @@ -394,7 +405,7 @@ link:glossary.html[Glossary]. The link:user-manual.html[Git User's Manual] provides a more comprehensive introduction to git. -The link:cvs-migration.html[CVS migration] document explains how to +The linkgit:gitcvs-migration[7][CVS migration] document explains how to import a CVS repository into git, and shows how to use git in a CVS-like way. @@ -404,3 +415,14 @@ link:howto-index.html[howtos]. For git developers, the link:core-tutorial.html[Core tutorial] goes into detail on the lower-level git mechanisms involved in, for example, creating a new commit. + +SEE ALSO +-------- +linkgit:gittutorial[7], +linkgit:gitcvs-migration[7], +link:everyday.html[Everyday git], +link:user-manual.html[The Git User's Manual] + +GIT +--- +Part of the linkgit:git[7] suite. diff --git a/Documentation/tutorial.txt b/Documentation/gittutorial.txt similarity index 97% rename from Documentation/tutorial.txt rename to Documentation/gittutorial.txt index e2bbda53f0..898acdb533 100644 --- a/Documentation/tutorial.txt +++ b/Documentation/gittutorial.txt @@ -1,5 +1,16 @@ -A tutorial introduction to git (for version 1.5.1 or newer) -=========================================================== +gittutorial(7) +============== + +NAME +---- +gittutorial - A tutorial introduction to git (for version 1.5.1 or newer) + +SYNOPSIS +-------- +git * + +DESCRIPTION +----------- This tutorial explains how to import a new project into git, make changes to it, and share changes with other developers. @@ -381,7 +392,7 @@ see linkgit:git-pull[1] for details. Git can also be used in a CVS-like mode, with a central repository that various users push changes to; see linkgit:git-push[1] and -link:cvs-migration.html[git for CVS users]. +linkgit:gitcvs-migration[7][git for CVS users]. Exploring history ----------------- @@ -560,7 +571,7 @@ is based: used to create commits, check out working directories, and hold the various trees involved in a merge. -link:tutorial-2.html[Part two of this tutorial] explains the object +linkgit:gittutorial-2[7][Part two of this tutorial] explains the object database, the index file, and a few other odds and ends that you'll need to make the most of git. @@ -581,4 +592,15 @@ digressions that may be interesting at this point are: * link:everyday.html[Everyday GIT with 20 Commands Or So] - * link:cvs-migration.html[git for CVS users]. + * linkgit:gitcvs-migration[7][git for CVS users]. + +SEE ALSO +-------- +linkgit:gittutorial-2[7], +linkgit:gitcvs-migration[7], +link:everyday.html[Everyday git], +link:user-manual.html[The Git User's Manual] + +GIT +--- +Part of the linkgit:git[7] suite. diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index e2db850150..fd8cdb625a 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1993,7 +1993,7 @@ the right to push to the same repository. In that case, the correct solution is to retry the push after first updating your work by either a pull or a fetch followed by a rebase; see the <> and -link:cvs-migration.html[git for CVS users] for more. +linkgit:gitcvs-migration[7][git for CVS users] for more. [[setting-up-a-shared-repository]] Setting up a shared repository @@ -2002,7 +2002,7 @@ Setting up a shared repository Another way to collaborate is by using a model similar to that commonly used in CVS, where several developers with special rights all push to and pull from a single shared repository. See -link:cvs-migration.html[git for CVS users] for instructions on how to +linkgit:gitcvs-migration[7][git for CVS users] for instructions on how to set this up. However, while there is nothing wrong with git's support for shared From 2ecbd0a0db0f9d59a5df02a6daeb87e611171fa4 Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Fri, 23 May 2008 19:24:10 -0700 Subject: [PATCH 117/202] graph API: fix graph mis-alignment after uninteresting commits The graphing code had a bug that caused it to output branch lines incorrectly after ignoring an uninteresting commit. When computing how to match up the branch lines from the current commit to the next one, it forgot to take into account that it needed to initially start with 2 empty spaces where the missing commit would have gone. So, instead of drawing this, | * | <- Commit with uninteresting parent | / * | It used to incorrectly draw this: | * | <- Commit with uninteresting parent * | Signed-off-by: Adam Simpkins Signed-off-by: Junio C Hamano --- graph.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/graph.c b/graph.c index 9d6ed30b0b..400f0147bf 100644 --- a/graph.c +++ b/graph.c @@ -190,7 +190,10 @@ static void graph_insert_into_new_columns(struct git_graph *graph, * Ignore uinteresting and pruned commits */ if (commit->object.flags & (UNINTERESTING | TREESAME)) + { + *mapping_index += 2; return; + } /* * If the commit is already in the new_columns list, we don't need to From 37a75abc985a25a0612c2c176ed35d438722752d Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Fri, 23 May 2008 19:24:11 -0700 Subject: [PATCH 118/202] graph API: don't print branch lines for uninteresting merge parents Previously, the graphing code printed lines coming out of a merge commit for all of its parents, even if some of them were uninteresting. Now it only prints lines for interesting commits. For example, for a merge commit where only the first parent is interesting, the code now prints: * merge commit * interesting child instead of: M merge commit |\ * interesting child Signed-off-by: Adam Simpkins Signed-off-by: Junio C Hamano --- graph.c | 57 ++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/graph.c b/graph.c index 400f0147bf..add7e4477d 100644 --- a/graph.c +++ b/graph.c @@ -55,9 +55,11 @@ struct git_graph { */ struct commit *commit; /* - * The number of parents this commit has. - * (Stored so we don't have to walk over them each time we need - * this number) + * The number of interesting parents that this commit has. + * + * Note that this is not the same as the actual number of parents. + * This count excludes parents that won't be printed in the graph + * output, as determined by graph_is_interesting(). */ int num_parents; /* @@ -180,6 +182,18 @@ static void graph_ensure_capacity(struct git_graph *graph, int num_columns) sizeof(int) * 2 * graph->column_capacity); } +/* + * Returns 1 if the commit will be printed in the graph output, + * and 0 otherwise. + */ +static int graph_is_interesting(struct commit *commit) +{ + /* + * Uninteresting and pruned commits won't be printed + */ + return (commit->object.flags & (UNINTERESTING | TREESAME)) ? 0 : 1; +} + static void graph_insert_into_new_columns(struct git_graph *graph, struct commit *commit, int *mapping_index) @@ -187,13 +201,10 @@ static void graph_insert_into_new_columns(struct git_graph *graph, int i; /* - * Ignore uinteresting and pruned commits + * Ignore uinteresting commits */ - if (commit->object.flags & (UNINTERESTING | TREESAME)) - { - *mapping_index += 2; + if (!graph_is_interesting(commit)) return; - } /* * If the commit is already in the new_columns list, we don't need to @@ -231,8 +242,8 @@ static void graph_update_width(struct git_graph *graph, int max_cols = graph->num_columns + graph->num_parents; /* - * Even if the current commit has no parents, it still takes up a - * column for itself. + * Even if the current commit has no parents to be printed, it + * still takes up a column for itself. */ if (graph->num_parents < 1) max_cols++; @@ -316,6 +327,7 @@ static void graph_update_columns(struct git_graph *graph) } if (col_commit == graph->commit) { + int old_mapping_idx = mapping_idx; seen_this = 1; for (parent = graph->commit->parents; parent; @@ -324,6 +336,14 @@ static void graph_update_columns(struct git_graph *graph) parent->item, &mapping_idx); } + /* + * We always need to increment mapping_idx by at + * least 2, even if it has no interesting parents. + * The current commit always takes up at least 2 + * spaces. + */ + if (mapping_idx == old_mapping_idx) + mapping_idx += 2; } else { graph_insert_into_new_columns(graph, col_commit, &mapping_idx); @@ -353,11 +373,13 @@ void graph_update(struct git_graph *graph, struct commit *commit) graph->commit = commit; /* - * Count how many parents this commit has + * Count how many interesting parents this commit has */ graph->num_parents = 0; - for (parent = commit->parents; parent; parent = parent->next) - graph->num_parents++; + for (parent = commit->parents; parent; parent = parent->next) { + if (graph_is_interesting(parent->item)) + graph->num_parents++; + } /* * Call graph_update_columns() to update @@ -543,6 +565,15 @@ void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) if (col_commit == graph->commit) { seen_this = 1; + /* + * If the commit has more than 1 interesting + * parent, print 'M' to indicate that it is a + * merge. Otherwise, print '*'. + * + * Note that even if this is actually a merge + * commit, we still print '*' if less than 2 of its + * parents are interesting. + */ if (graph->num_parents > 1) strbuf_addch(sb, 'M'); else From 7528f27dd677bed65d758667a621034b853595b4 Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Sun, 25 May 2008 00:07:21 -0700 Subject: [PATCH 119/202] log --graph --left-right: show left/right information in place of '*' With the --graph option, the graph already outputs 'o' instead of '*' for boundary commits. Make it emit '<' or '>' when --left-right is specified. (This change also disables the '^' prefix for UNINTERESTING commits. The graph code currently doesn't print anything special for these commits, since it assumes no UNINTERESTING, non-BOUNDARY commits are displayed. This is potentially a bug if UNINTERESTING non-BOUNDARY commits can actually be displayed via some code path.) [jc: squashed the left-right change from Dscho and Adam's fixup into one] Signed-off-by: Adam Simpkins Signed-off-by: Junio C Hamano --- Documentation/technical/api-history-graph.txt | 2 +- builtin-rev-list.c | 21 ++++++---- graph.c | 22 +++++----- graph.h | 2 +- log-tree.c | 41 +++++++++++-------- revision.c | 2 +- 6 files changed, 49 insertions(+), 41 deletions(-) diff --git a/Documentation/technical/api-history-graph.txt b/Documentation/technical/api-history-graph.txt index ce1c08ee86..e9559790a3 100644 --- a/Documentation/technical/api-history-graph.txt +++ b/Documentation/technical/api-history-graph.txt @@ -115,7 +115,7 @@ Sample usage ------------ struct commit *commit; -struct git_graph *graph = graph_init(); +struct git_graph *graph = graph_init(opts); while ((commit = get_revision(opts)) != NULL) { graph_update(graph, commit); diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 54d55cc3a3..b474527402 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -65,15 +65,18 @@ static void show_commit(struct commit *commit) printf("%lu ", commit->date); if (header_prefix) fputs(header_prefix, stdout); - if (commit->object.flags & BOUNDARY) - putchar('-'); - else if (commit->object.flags & UNINTERESTING) - putchar('^'); - else if (revs.left_right) { - if (commit->object.flags & SYMMETRIC_LEFT) - putchar('<'); - else - putchar('>'); + + if (!revs.graph) { + if (commit->object.flags & BOUNDARY) + putchar('-'); + else if (commit->object.flags & UNINTERESTING) + putchar('^'); + else if (revs.left_right) { + if (commit->object.flags & SYMMETRIC_LEFT) + putchar('<'); + else + putchar('>'); + } } if (revs.abbrev_commit && revs.abbrev) fputs(find_unique_abbrev(commit->object.sha1, revs.abbrev), diff --git a/graph.c b/graph.c index add7e4477d..dc2c1ec5d7 100644 --- a/graph.c +++ b/graph.c @@ -54,6 +54,8 @@ struct git_graph { * The commit currently being processed */ struct commit *commit; + /* The rev-info used for the current traversal */ + struct rev_info *revs; /* * The number of interesting parents that this commit has. * @@ -127,10 +129,11 @@ struct git_graph { int *new_mapping; }; -struct git_graph *graph_init(void) +struct git_graph *graph_init(struct rev_info *opt) { struct git_graph *graph = xmalloc(sizeof(struct git_graph)); graph->commit = NULL; + graph->revs = opt; graph->num_parents = 0; graph->expansion_row = 0; graph->state = GRAPH_PADDING; @@ -565,16 +568,13 @@ void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) if (col_commit == graph->commit) { seen_this = 1; - /* - * If the commit has more than 1 interesting - * parent, print 'M' to indicate that it is a - * merge. Otherwise, print '*'. - * - * Note that even if this is actually a merge - * commit, we still print '*' if less than 2 of its - * parents are interesting. - */ - if (graph->num_parents > 1) + + if (graph->revs && graph->revs->left_right) { + if (graph->commit->object.flags & SYMMETRIC_LEFT) + strbuf_addch(sb, '<'); + else + strbuf_addch(sb, '>'); + } else if (graph->num_parents > 1) strbuf_addch(sb, 'M'); else strbuf_addch(sb, '*'); diff --git a/graph.h b/graph.h index a7748a5b22..eab4e3daba 100644 --- a/graph.h +++ b/graph.h @@ -8,7 +8,7 @@ struct git_graph; * Create a new struct git_graph. * The graph should be freed with graph_release() when no longer needed. */ -struct git_graph *graph_init(); +struct git_graph *graph_init(struct rev_info *opt); /* * Destroy a struct git_graph and free associated memory. diff --git a/log-tree.c b/log-tree.c index 1474d1f5d9..5505606ed6 100644 --- a/log-tree.c +++ b/log-tree.c @@ -228,15 +228,17 @@ void show_log(struct rev_info *opt) if (!opt->verbose_header) { graph_show_commit(opt->graph); - if (commit->object.flags & BOUNDARY) - putchar('-'); - else if (commit->object.flags & UNINTERESTING) - putchar('^'); - else if (opt->left_right) { - if (commit->object.flags & SYMMETRIC_LEFT) - putchar('<'); - else - putchar('>'); + if (!opt->graph) { + if (commit->object.flags & BOUNDARY) + putchar('-'); + else if (commit->object.flags & UNINTERESTING) + putchar('^'); + else if (opt->left_right) { + if (commit->object.flags & SYMMETRIC_LEFT) + putchar('<'); + else + putchar('>'); + } } fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout); if (opt->print_parents) @@ -293,15 +295,18 @@ void show_log(struct rev_info *opt) fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout); if (opt->commit_format != CMIT_FMT_ONELINE) fputs("commit ", stdout); - if (commit->object.flags & BOUNDARY) - putchar('-'); - else if (commit->object.flags & UNINTERESTING) - putchar('^'); - else if (opt->left_right) { - if (commit->object.flags & SYMMETRIC_LEFT) - putchar('<'); - else - putchar('>'); + + if (!opt->graph) { + if (commit->object.flags & BOUNDARY) + putchar('-'); + else if (commit->object.flags & UNINTERESTING) + putchar('^'); + else if (opt->left_right) { + if (commit->object.flags & SYMMETRIC_LEFT) + putchar('<'); + else + putchar('>'); + } } fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout); diff --git a/revision.c b/revision.c index 7142cf96cd..1341f3d125 100644 --- a/revision.c +++ b/revision.c @@ -1205,7 +1205,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch if (!prefixcmp(arg, "--graph")) { revs->topo_order = 1; revs->rewrite_parents = 1; - revs->graph = graph_init(); + revs->graph = graph_init(revs); continue; } if (!strcmp(arg, "--root")) { From 3c68d67b572bce7ff41de463e75ee093e9dd71b7 Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Sat, 24 May 2008 16:02:04 -0700 Subject: [PATCH 120/202] Fix output of "git log --graph --boundary" Previously the graphing API wasn't aware of the revs->boundary flag, and it always assumed that commits marked UNINTERESTING would not be displayed. As a result, the boundary commits were printed at the end of the log output, but they didn't have any branch lines connecting them to their children in the graph. There was also another bug in the get_revision() code that caused graph_update() to be called twice on the first boundary commit. This caused the graph API to think that a commit had been skipped, and print a "..." line in the output. Signed-off-by: Adam Simpkins Signed-off-by: Junio C Hamano --- graph.c | 79 +++++++++++++++++++++++++++++++++++++++++++++--------- revision.c | 2 +- 2 files changed, 67 insertions(+), 14 deletions(-) diff --git a/graph.c b/graph.c index dc2c1ec5d7..9b3495c461 100644 --- a/graph.c +++ b/graph.c @@ -189,8 +189,25 @@ static void graph_ensure_capacity(struct git_graph *graph, int num_columns) * Returns 1 if the commit will be printed in the graph output, * and 0 otherwise. */ -static int graph_is_interesting(struct commit *commit) +static int graph_is_interesting(struct git_graph *graph, struct commit *commit) { + /* + * If revs->boundary is set, commits whose children have + * been shown are always interesting, even if they have the + * UNINTERESTING or TREESAME flags set. + * + * However, ignore the commit if SHOWN is set. If SHOWN is set, + * the commit is interesting, but it has already been printed. + * This can happen because get_revision() doesn't return the + * boundary commits in topological order, even when + * revs->topo_order is set. + */ + if (graph->revs && graph->revs->boundary) { + if ((commit->object.flags & (SHOWN | CHILD_SHOWN)) == + CHILD_SHOWN) + return 1; + } + /* * Uninteresting and pruned commits won't be printed */ @@ -206,7 +223,7 @@ static void graph_insert_into_new_columns(struct git_graph *graph, /* * Ignore uinteresting commits */ - if (!graph_is_interesting(commit)) + if (!graph_is_interesting(graph, commit)) return; /* @@ -380,7 +397,7 @@ void graph_update(struct git_graph *graph, struct commit *commit) */ graph->num_parents = 0; for (parent = commit->parents; parent; parent = parent->next) { - if (graph_is_interesting(parent->item)) + if (graph_is_interesting(graph, parent->item)) graph->num_parents++; } @@ -543,6 +560,51 @@ static void graph_output_pre_commit_line(struct git_graph *graph, graph->state = GRAPH_COMMIT; } +static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb) +{ + /* + * For boundary commits, print 'o' + * (We should only see boundary commits when revs->boundary is set.) + */ + if (graph->commit->object.flags & BOUNDARY) { + assert(graph->revs->boundary); + strbuf_addch(sb, 'o'); + return; + } + + /* + * If revs->left_right is set, print '<' for commits that + * come from the left side, and '>' for commits from the right + * side. + */ + if (graph->revs && graph->revs->left_right) { + if (graph->commit->object.flags & SYMMETRIC_LEFT) + strbuf_addch(sb, '<'); + else + strbuf_addch(sb, '>'); + return; + } + + /* + * Print 'M' for merge commits + * + * Note that we don't check graph->num_parents to determine if the + * commit is a merge, since that only tracks the number of + * "interesting" parents. We want to print 'M' for merge commits + * even if they have less than 2 interesting parents. + */ + if (graph->commit->parents != NULL && + graph->commit->parents->next != NULL) { + strbuf_addch(sb, 'M'); + return; + } + + /* + * Print '*' in all other cases + */ + strbuf_addch(sb, '*'); +} + void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) { int seen_this = 0; @@ -568,16 +630,7 @@ void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) if (col_commit == graph->commit) { seen_this = 1; - - if (graph->revs && graph->revs->left_right) { - if (graph->commit->object.flags & SYMMETRIC_LEFT) - strbuf_addch(sb, '<'); - else - strbuf_addch(sb, '>'); - } else if (graph->num_parents > 1) - strbuf_addch(sb, 'M'); - else - strbuf_addch(sb, '*'); + graph_output_commit_char(graph, sb); if (graph->num_parents < 2) strbuf_addch(sb, ' '); diff --git a/revision.c b/revision.c index 1341f3d125..181fb0b954 100644 --- a/revision.c +++ b/revision.c @@ -1697,7 +1697,7 @@ static struct commit *get_revision_internal(struct rev_info *revs) * switch to boundary commits output mode. */ revs->boundary = 2; - return get_revision(revs); + return get_revision_internal(revs); } /* From 4603ec0f960e582a7da7241563d3f235ad7f0d3e Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Sat, 24 May 2008 16:02:05 -0700 Subject: [PATCH 121/202] get_revision(): honor the topo_order flag for boundary commits Now get_revision() sorts the boundary commits when topo_order is set. Since sort_in_topological_order() takes a struct commit_list, it first places the boundary commits into revs->commits. Signed-off-by: Adam Simpkins Signed-off-by: Junio C Hamano --- graph.c | 9 +------ revision.c | 73 ++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/graph.c b/graph.c index 9b3495c461..26b8c5209e 100644 --- a/graph.c +++ b/graph.c @@ -195,16 +195,9 @@ static int graph_is_interesting(struct git_graph *graph, struct commit *commit) * If revs->boundary is set, commits whose children have * been shown are always interesting, even if they have the * UNINTERESTING or TREESAME flags set. - * - * However, ignore the commit if SHOWN is set. If SHOWN is set, - * the commit is interesting, but it has already been printed. - * This can happen because get_revision() doesn't return the - * boundary commits in topological order, even when - * revs->topo_order is set. */ if (graph->revs && graph->revs->boundary) { - if ((commit->object.flags & (SHOWN | CHILD_SHOWN)) == - CHILD_SHOWN) + if (commit->object.flags & CHILD_SHOWN) return 1; } diff --git a/revision.c b/revision.c index 181fb0b954..fb9924e5af 100644 --- a/revision.c +++ b/revision.c @@ -1612,28 +1612,62 @@ static void gc_boundary(struct object_array *array) } } +static void create_boundary_commit_list(struct rev_info *revs) +{ + unsigned i; + struct commit *c; + struct object_array *array = &revs->boundary_commits; + struct object_array_entry *objects = array->objects; + + /* + * If revs->commits is non-NULL at this point, an error occurred in + * get_revision_1(). Ignore the error and continue printing the + * boundary commits anyway. (This is what the code has always + * done.) + */ + if (revs->commits) { + free_commit_list(revs->commits); + revs->commits = NULL; + } + + /* + * Put all of the actual boundary commits from revs->boundary_commits + * into revs->commits + */ + for (i = 0; i < array->nr; i++) { + c = (struct commit *)(objects[i].item); + if (!c) + continue; + if (!(c->object.flags & CHILD_SHOWN)) + continue; + if (c->object.flags & (SHOWN | BOUNDARY)) + continue; + c->object.flags |= BOUNDARY; + commit_list_insert(c, &revs->commits); + } + + /* + * If revs->topo_order is set, sort the boundary commits + * in topological order + */ + sort_in_topological_order(&revs->commits, revs->lifo); +} + static struct commit *get_revision_internal(struct rev_info *revs) { struct commit *c = NULL; struct commit_list *l; if (revs->boundary == 2) { - unsigned i; - struct object_array *array = &revs->boundary_commits; - struct object_array_entry *objects = array->objects; - for (i = 0; i < array->nr; i++) { - c = (struct commit *)(objects[i].item); - if (!c) - continue; - if (!(c->object.flags & CHILD_SHOWN)) - continue; - if (!(c->object.flags & SHOWN)) - break; - } - if (array->nr <= i) - return NULL; - - c->object.flags |= SHOWN | BOUNDARY; + /* + * All of the normal commits have already been returned, + * and we are now returning boundary commits. + * create_boundary_commit_list() has populated + * revs->commits with the remaining commits to return. + */ + c = pop_commit(&revs->commits); + if (c) + c->object.flags |= SHOWN; return c; } @@ -1697,6 +1731,13 @@ static struct commit *get_revision_internal(struct rev_info *revs) * switch to boundary commits output mode. */ revs->boundary = 2; + + /* + * Update revs->commits to contain the list of + * boundary commits. + */ + create_boundary_commit_list(revs); + return get_revision_internal(revs); } From 509792b94f71f199838fdeaf0801e215213a6d08 Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Fri, 23 May 2008 19:29:22 +0100 Subject: [PATCH 122/202] Make git-cvsimport remove ['s from tags, as bad_ref_char doesn't allow them. Signed-off-by: Paul Oliver Signed-off-by: Junio C Hamano --- git-cvsimport.perl | 1 + 1 file changed, 1 insertion(+) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index bdac5d51b6..5a0255052c 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -780,6 +780,7 @@ sub commit { $xtag =~ s/\s+\*\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY ** $xtag =~ tr/_/\./ if ( $opt_u ); $xtag =~ s/[\/]/$opt_s/g; + $xtag =~ s/\[//g; system('git-tag', '-f', $xtag, $cid) == 0 or die "Cannot create tag $xtag: $!\n"; From a1292939381507be9489451076d49a5b927e9cc4 Mon Sep 17 00:00:00 2001 From: Clemens Buchacher Date: Sun, 25 May 2008 20:26:50 +0200 Subject: [PATCH 123/202] Reset the signal being handled This did not cause any problems, because remove_lock_file_on_signal is only registered for SIGINT. Signed-off-by: Clemens Buchacher Signed-off-by: Junio C Hamano --- lockfile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lockfile.c b/lockfile.c index 663f18f9c4..b0118d0592 100644 --- a/lockfile.c +++ b/lockfile.c @@ -24,7 +24,7 @@ static void remove_lock_file(void) static void remove_lock_file_on_signal(int signo) { remove_lock_file(); - signal(SIGINT, SIG_DFL); + signal(signo, SIG_DFL); raise(signo); } From 6a491a176593a9ab3bd7ff24ef12323371671112 Mon Sep 17 00:00:00 2001 From: Clemens Buchacher Date: Sun, 25 May 2008 20:27:44 +0200 Subject: [PATCH 124/202] http-push: remove remote locks on exit signals If locks are not cleaned up the repository is inaccessible for 10 minutes. Signed-off-by: Clemens Buchacher Signed-off-by: Junio C Hamano --- http-push.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/http-push.c b/http-push.c index 42727c8a45..f173dcd64f 100644 --- a/http-push.c +++ b/http-push.c @@ -1349,6 +1349,24 @@ static int unlock_remote(struct remote_lock *lock) return rc; } +static void remove_locks(void) +{ + struct remote_lock *lock = remote->locks; + + fprintf(stderr, "Removing remote locks...\n"); + while (lock) { + unlock_remote(lock); + lock = lock->next; + } +} + +static void remove_locks_on_signal(int signo) +{ + remove_locks(); + signal(signo, SIG_DFL); + raise(signo); +} + static void remote_ls(const char *path, int flags, void (*userFunc)(struct remote_ls_ctx *ls), void *userData); @@ -2256,6 +2274,10 @@ int main(int argc, char **argv) goto cleanup; } + signal(SIGINT, remove_locks_on_signal); + signal(SIGHUP, remove_locks_on_signal); + signal(SIGQUIT, remove_locks_on_signal); + /* Check whether the remote has server info files */ remote->can_update_info_refs = 0; remote->has_info_refs = remote_exists("info/refs"); From cce8d6fdb4d7170a73763586daf6ac4f6b8fce2c Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 16 May 2008 14:03:30 +0100 Subject: [PATCH 125/202] mailsplit and mailinfo: gracefully handle NUL characters The function fgets() has a big problem with NUL characters: it reads them, but nobody will know if the NUL comes from the file stream, or was appended at the end of the line. So implement a custom read_line_with_nul() function. Noticed by Tommy Thorn. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-mailinfo.c | 24 +++++++++++++----------- builtin-mailsplit.c | 27 +++++++++++++++++++++++---- builtin.h | 1 + t/t5100-mailinfo.sh | 9 +++++++++ t/t5100/nul | Bin 0 -> 91 bytes 5 files changed, 46 insertions(+), 15 deletions(-) create mode 100644 t/t5100/nul diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index 11f154b31f..f0c420976f 100644 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@ -641,7 +641,7 @@ static void decode_transfer_encoding(char *line, unsigned linesize) } } -static int handle_filter(char *line, unsigned linesize); +static int handle_filter(char *line, unsigned linesize, int linelen); static int find_boundary(void) { @@ -669,7 +669,7 @@ again: "can't recover\n"); exit(1); } - handle_filter(newline, sizeof(newline)); + handle_filter(newline, sizeof(newline), strlen(newline)); /* skip to the next boundary */ if (!find_boundary()) @@ -759,14 +759,14 @@ static int handle_commit_msg(char *line, unsigned linesize) return 0; } -static int handle_patch(char *line) +static int handle_patch(char *line, int len) { - fputs(line, patchfile); + fwrite(line, 1, len, patchfile); patch_lines++; return 0; } -static int handle_filter(char *line, unsigned linesize) +static int handle_filter(char *line, unsigned linesize, int linelen) { static int filter = 0; @@ -779,7 +779,7 @@ static int handle_filter(char *line, unsigned linesize) break; filter++; case 1: - if (!handle_patch(line)) + if (!handle_patch(line, linelen)) break; filter++; default: @@ -794,6 +794,7 @@ static void handle_body(void) int rc = 0; static char newline[2000]; static char *np = newline; + int len = strlen(line); /* Skip up to the first boundary */ if (content_top->boundary) { @@ -807,7 +808,8 @@ static void handle_body(void) /* flush any leftover */ if ((transfer_encoding == TE_BASE64) && (np != newline)) { - handle_filter(newline, sizeof(newline)); + handle_filter(newline, sizeof(newline), + strlen(newline)); } if (!handle_boundary()) return; @@ -824,7 +826,7 @@ static void handle_body(void) /* binary data most likely doesn't have newlines */ if (message_type != TYPE_TEXT) { - rc = handle_filter(line, sizeof(newline)); + rc = handle_filter(line, sizeof(line), len); break; } @@ -841,7 +843,7 @@ static void handle_body(void) /* should be sitting on a new line */ *(++np) = 0; op++; - rc = handle_filter(newline, sizeof(newline)); + rc = handle_filter(newline, sizeof(newline), np - newline); np = newline; } } while (*op != 0); @@ -851,12 +853,12 @@ static void handle_body(void) break; } default: - rc = handle_filter(line, sizeof(newline)); + rc = handle_filter(line, sizeof(line), len); } if (rc) /* nothing left to filter */ break; - } while (fgets(line, sizeof(line), fin)); + } while ((len = read_line_with_nul(line, sizeof(line), fin))); return; } diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c index 46b27cdaea..e4d977bafb 100644 --- a/builtin-mailsplit.c +++ b/builtin-mailsplit.c @@ -45,6 +45,25 @@ static int is_from_line(const char *line, int len) /* Could be as small as 64, enough to hold a Unix "From " line. */ static char buf[4096]; +/* We cannot use fgets() because our lines can contain NULs */ +int read_line_with_nul(char *buf, int size, FILE *in) +{ + int len = 0, c; + + for (;;) { + c = getc(in); + buf[len++] = c; + if (c == EOF || c == '\n' || len + 1 >= size) + break; + } + + if (c == EOF) + len--; + buf[len] = '\0'; + + return len; +} + /* Called with the first line (potentially partial) * already in buf[] -- normally that should begin with * the Unix "From " line. Write it into the specified @@ -70,19 +89,19 @@ static int split_one(FILE *mbox, const char *name, int allow_bare) * "From " and having something that looks like a date format. */ for (;;) { - int is_partial = (buf[len-1] != '\n'); + int is_partial = len && buf[len-1] != '\n'; - if (fputs(buf, output) == EOF) + if (fwrite(buf, 1, len, output) != len) die("cannot write output"); - if (fgets(buf, sizeof(buf), mbox) == NULL) { + len = read_line_with_nul(buf, sizeof(buf), mbox); + if (len == 0) { if (feof(mbox)) { status = 1; break; } die("cannot read mbox"); } - len = strlen(buf); if (!is_partial && !is_bare && is_from_line(buf, len)) break; /* done with one message */ } diff --git a/builtin.h b/builtin.h index 95126fd0c1..48f1332001 100644 --- a/builtin.h +++ b/builtin.h @@ -9,6 +9,7 @@ extern const char git_usage_string[]; extern void list_common_cmds_help(void); extern void help_unknown_cmd(const char *cmd); extern void prune_packed_objects(int); +extern int read_line_with_nul(char *buf, int size, FILE *file); extern int cmd_add(int argc, const char **argv, const char *prefix); extern int cmd_annotate(int argc, const char **argv, const char *prefix); diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh index d6c55c1157..5a4610b860 100755 --- a/t/t5100-mailinfo.sh +++ b/t/t5100-mailinfo.sh @@ -25,4 +25,13 @@ do diff ../t5100/info$mail info$mail" done +test_expect_success 'respect NULs' ' + + git mailsplit -d3 -o. ../t5100/nul && + cmp ../t5100/nul 001 && + (cat 001 | git mailinfo msg patch) && + test 4 = $(wc -l < patch) + +' + test_done diff --git a/t/t5100/nul b/t/t5100/nul new file mode 100644 index 0000000000000000000000000000000000000000..3d40691787b855cc0133514a19052492eb853d21 GIT binary patch literal 91 zcmW;6y$ygM5C%|6a#MT@Tm%~v2e7kZ0t`Q)fHOej_C}MJcXX*}a!Gh_N`s3x>;_}@ mA68>55i?ULDS Date: Sun, 25 May 2008 01:16:05 -0700 Subject: [PATCH 126/202] mailinfo: apply the same fix not to lose NULs in BASE64 and QP codepaths Signed-off-by: Junio C Hamano --- builtin-mailinfo.c | 46 ++++++++++++++++++++++------------------- t/t5100-mailinfo.sh | 9 ++++++++ t/t5100/nul-b64.expect | Bin 0 -> 1672 bytes t/t5100/nul-b64.in | 37 +++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 21 deletions(-) create mode 100644 t/t5100/nul-b64.expect create mode 100644 t/t5100/nul-b64.in diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index f0c420976f..e1e094f29e 100644 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@ -434,6 +434,7 @@ static int read_one_header_line(char *line, int sz, FILE *in) static int decode_q_segment(char *in, char *ot, unsigned otsize, char *ep, int rfc2047) { + char *otbegin = ot; char *otend = ot + otsize; int c; while ((c = *in++) != 0 && (in <= ep)) { @@ -453,13 +454,14 @@ static int decode_q_segment(char *in, char *ot, unsigned otsize, char *ep, int r *ot++ = c; } *ot = 0; - return 0; + return (ot - otbegin); } static int decode_b_segment(char *in, char *ot, unsigned otsize, char *ep) { /* Decode in..ep, possibly in-place to ot */ int c, pos = 0, acc = 0; + char *otbegin = ot; char *otend = ot + otsize; while ((c = *in++) != 0 && (in <= ep)) { @@ -505,7 +507,7 @@ static int decode_b_segment(char *in, char *ot, unsigned otsize, char *ep) } } *ot = 0; - return 0; + return (ot - otbegin); } /* @@ -623,21 +625,20 @@ static void decode_header(char *it, unsigned itsize) convert_to_utf8(it, itsize, ""); } -static void decode_transfer_encoding(char *line, unsigned linesize) +static int decode_transfer_encoding(char *line, unsigned linesize, int inputlen) { char *ep; switch (transfer_encoding) { case TE_QP: - ep = line + strlen(line); - decode_q_segment(line, line, linesize, ep, 0); - break; + ep = line + inputlen; + return decode_q_segment(line, line, linesize, ep, 0); case TE_BASE64: - ep = line + strlen(line); - decode_b_segment(line, line, linesize, ep); - break; + ep = line + inputlen; + return decode_b_segment(line, line, linesize, ep); case TE_DONTCARE: - break; + default: + return inputlen; } } @@ -806,17 +807,19 @@ static void handle_body(void) /* process any boundary lines */ if (content_top->boundary && is_multipart_boundary(line)) { /* flush any leftover */ - if ((transfer_encoding == TE_BASE64) && - (np != newline)) { + if (np != newline) handle_filter(newline, sizeof(newline), - strlen(newline)); - } + np - newline); if (!handle_boundary()) return; } /* Unwrap transfer encoding */ - decode_transfer_encoding(line, sizeof(line)); + len = decode_transfer_encoding(line, sizeof(line), len); + if (len < 0) { + error("Malformed input line"); + return; + } switch (transfer_encoding) { case TE_BASE64: @@ -830,13 +833,13 @@ static void handle_body(void) break; } - /* this is a decoded line that may contain + /* + * This is a decoded line that may contain * multiple new lines. Pass only one chunk * at a time to handle_filter() */ - do { - while (*op != '\n' && *op != 0) + while (op < line + len && *op != '\n') *np++ = *op++; *np = *op; if (*np != 0) { @@ -846,9 +849,10 @@ static void handle_body(void) rc = handle_filter(newline, sizeof(newline), np - newline); np = newline; } - } while (*op != 0); - /* the partial chunk is saved in newline and - * will be appended by the next iteration of fgets + } while (op < line + len); + /* + * The partial chunk is saved in newline and will be + * appended by the next iteration of read_line_with_nul(). */ break; } diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh index 5a4610b860..a8b78ebf7d 100755 --- a/t/t5100-mailinfo.sh +++ b/t/t5100-mailinfo.sh @@ -34,4 +34,13 @@ test_expect_success 'respect NULs' ' ' +test_expect_success 'Preserve NULs out of MIME encoded message' ' + + git mailsplit -d5 -o. ../t5100/nul-b64.in && + cmp ../t5100/nul-b64.in 00001 && + git mailinfo msg patch <00001 && + cmp ../t5100/nul-b64.expect patch + +' + test_done diff --git a/t/t5100/nul-b64.expect b/t/t5100/nul-b64.expect new file mode 100644 index 0000000000000000000000000000000000000000..d7d680f631b14ea75adf34ba5052043be311e72f GIT binary patch literal 1672 zcmbVNU2hvV5cD&D#XdIB-Bn6#D}5=PAbkkX1_pxu0E%3l7T}Q=ei#kVAKxJ2!erMlL({H?ed*FN8ZMXNgw`v^6 zbG_PZHgsO|@Id2k*uAAqyFNT9`ZGjcNY+#qvcZK&scyA!9>}06HkotbNZQIO$%vnc zo#ctWmb9|Bk`k+zMU+cK8S%`SIAt;=rjp2M?88{~!O6Fx7g}_Qk|uhLniKo1#;4#) zUNveSV}O46t|b=Kd@0UpD$#37dKLBqS=RM2&Os9c&LPm(paDR&A`0*sAtWjAASl4Vk)#?PKWiGr2Toq>0*+UrSU z>qM8){T1Rtr~`XBoX*2v)aIu!ZY~%%7YvX$Wl6gFr&;}5rKHb}Oa{Tjz%Z_%S)AC* zQoFRWz@b%;?%IKT@K?j_zrI+%O5M#3)6~k!_E}h6QDlIS3;Y64C4{SO$Z~`O;Rg7m zn2aPsGjtMbB)&rTbFFGAz905TalPWPP6c6`=#N}o)vVPVIKYUpH{XN@^d-g`JDo8R zwn23lb_j*HPk?w%&gJ$zTajFZhaaz0~aZgG2xSqX!RdvmwX8d_kJ1yHxFw5Ye zyfEH5SM&CG(y9GPPUhl(*qnP3D279 +Date: Sun, 25 May 2008 00:38:18 -0700 +Subject: [PATCH] second +Content-Transfer-Encoding: base64 + +LS0tCiBmaWxlIHwgIEJpbiAxMzU3IC0+IDEzNTcgYnl0ZXMKIDEgZmlsZXMgY2hhbmdlZCwg +MCBpbnNlcnRpb25zKCspLCAwIGRlbGV0aW9ucygtKQoKZGlmZiAtLWdpdCBhL2ZpbGUgYi9m +aWxlCmluZGV4IDc3MzYxZDguLjllMDJiZTYgMTAwNjQ0Ci0tLSBhL2ZpbGUKKysrIGIvZmls +ZQpAQCAtMSwxMiArMSwxMiBAQAogTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNl +Y3RldHVlciBhZGlwaXNjaW5nIGVsaXQuIFN1c3BlbmRpc3NlCiBzaXQgYW1ldCB0dXJwaXMg +ZWdldCBlc3QgY3Vyc3VzIGxhb3JlZXQuIEFsaXF1YW0gbWF1cmlzLiBQcmFlc2VudAotdm9s +dXRwYXQuIFByb2luIGluIHB1cnVzLiBOdWxsYSB1cm5hIHNhcGllbiwgZGFwaWJ1cyBzaXQg +YW1ldCwKK3ZvbHV0cGF0LiBQcm9pbiBpbiBwdXJ1cy4gTnVsbGEgdXJuYSBzYXBpZW4sIGRh +cGkAdXMgc2l0IGFtZXQsCiBoZW5kcmVyaXQgbmVjLCB0ZW1wdXMgZXUsIG1pLiBVdCBwb3J0 +YSwgbGVvIGlkIHRpbmNpZHVudCB1bGxhbWNvcnBlciwKLXZlbGl0IGZlbGlzIHRyaXN0aXF1 +ZSBhbnRlLCBhdCBsb2JvcnRpcyBkaWFtIHBlZGUgdXQgZHVpLiBQcm9pbiBhYwordmVsaXQg +ZmVsaXMgdHJpc3RpcXVlIGFudGUsIGF0IGxvAG9ydGlzIGRpYW0gcGVkZSB1dCBkdWkuIFBy +b2luIGFjCiBsZWN0dXMuIERvbmVjIGF0IG1hc3NhIGFjIGlwc3VtIGhlbmRyZXJpdCBzb2xs +aWNpdHVkaW4uIE5hbSBkaWN0dW0KIG5pc2kgc2VkIG1pLiBEdWlzIHNlZCBhbnRlLiBVdCB2 +aXRhZSBlc3QgdXQgZHVpIHVsdHJpY2llcyBkaWduaXNzaW0uCiAKLUluIHZlbCBvZGlvIGVn +ZXQgbmlzbCBjb252YWxsaXMgdm9sdXRwYXQuIE1vcmJpIHZpdGFlIG5pYmguIE51bGxhbQor +SW4gdmVsIG9kaW8gZWdldCBuaXNsIGNvbnZhbGxpcyB2b2x1dHBhdC4gTW9yAGkgdml0YWUg +bmkAaC4gTnVsbGFtCiBhY2N1bXNhbiwgZG9sb3IgcXVpcyBhbGlxdWFtIHNjZWxlcmlzcXVl +LCBlbGl0IGVuaW0gY29uZGltZW50dW0KIG1hdXJpcywgbm9uIHRyaXN0aXF1ZSBtYXVyaXMg +dHVycGlzIGV0IG1hdXJpcy4gVXQgbm9uIG5pc2wuIE5hbSBkaWFtCiBtaSwgc2VtcGVyIHBv +c3VlcmUsIGVsZWlmZW5kIHV0LCBhdWN0b3IgdmVsLCBlcmF0LiBTZWQgcG9zdWVyZQpAQCAt +MTYsNyArMTYsNyBAQCBzZWQgZXN0LiBFdGlhbSBkaWFtIGZlbGlzLCBmZXJtZW50dW0gZWdl +dCwgYWRpcGlzY2luZyBhdCwgcG9zdWVyZSBpbiwKIGR1aS4gRXRpYW0gbHVjdHVzLgogCiBO +dWxsYSBpZCBhdWd1ZS4gTmFtIGlhY3VsaXMgYWNjdW1zYW4gbmlzaS4gU3VzcGVuZGlzc2Ug +cG90ZW50aS4gTnVuYwotdmFyaXVzIGF1Z3VlIG5lYyBvcmNpLiBVdCBjb25kaW1lbnR1bSBk +b2xvciBzYWdpdHRpcyBuaWJoLiBTdXNwZW5kaXNzZQordmFyaXVzIGF1Z3VlIG5lYyBvcmNp +LiBVdCBjb25kaW1lbnR1bSBkb2xvciBzYWdpdHRpcyBuaQBoLiBTdXNwZW5kaXNzZQogdGVt +cG9yIGxlY3R1cyBzZWQgbWFnbmEuIFN1c3BlbmRpc3NlIHBvdGVudGkuIE51bGxhbSB0ZW1w +b3IgaXBzdW0uIFNlZAogbW9sZXN0aWUgdGVsbHVzLiBQaGFzZWxsdXMgbGlndWxhLiBJbiB2 +ZWhpY3VsYSB1bHRyaWNlcwogbmlzaS4gU3VzcGVuZGlzc2UgZmVsaXMgYXVndWUsIHBlbGxl +bnRlc3F1ZSBhdCwgZGljdHVtIHZpdmVycmEsCi0tIAoxLjUuNS4xLjU0MC5nNTc3ODAKCg== From edc5594153d7246694c1ec5bdb8ccdaa97bf7daf Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 25 May 2008 01:23:55 -0700 Subject: [PATCH 127/202] mailsplit: minor clean-up in read_line_with_nul() Signed-off-by: Junio C Hamano --- builtin-mailsplit.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c index e4d977bafb..ae2b4cb21b 100644 --- a/builtin-mailsplit.c +++ b/builtin-mailsplit.c @@ -52,13 +52,12 @@ int read_line_with_nul(char *buf, int size, FILE *in) for (;;) { c = getc(in); + if (c == EOF) + break; buf[len++] = c; - if (c == EOF || c == '\n' || len + 1 >= size) + if (c == '\n' || len + 1 >= size) break; } - - if (c == EOF) - len--; buf[len] = '\0'; return len; From f53bc0953feaebe42ba056cbdbe5e91f930134a5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 25 May 2008 18:28:37 -0700 Subject: [PATCH 128/202] Update draft release notes for 1.5.6 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.6.txt | 49 +++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/Documentation/RelNotes-1.5.6.txt b/Documentation/RelNotes-1.5.6.txt index f3256fb82c..32af18b572 100644 --- a/Documentation/RelNotes-1.5.6.txt +++ b/Documentation/RelNotes-1.5.6.txt @@ -6,16 +6,30 @@ Updates since v1.5.5 (subsystems) +* Comes with updated gitk and git-gui. (portability) +* git will build on AIX better than before now. + +* core.ignorecase configuration variable can be used to work better on + filesystems that are not case sensitive. + +* "git init" now autodetects the case sensitivity of the filesystem and + sets core.ignorecase accordingly. (performance) +* "git clone" was rewritten in C. This will hopefully help cloning a + repository with insane number of refs. + * "git rebase --onto $there $from $branch" used to switch to the tip of $branch only to immediately reset back to $from, smudging work tree files unnecessarily. This has been optimized. +* Object creation codepath in "git-svn" has been optimized by enhancing + plumbing commands git-cat-file and git-hash-object. + (usability, bells and whistles) * "git add -p" (and the "patch" subcommand of "git add -i") can choose to @@ -23,20 +37,53 @@ Updates since v1.5.5 * "git bisect help" gives longer and more helpful usage information. +* "git bisect" does not use a special branch "bisect" anymore; instead, it + does its work on a detached HEAD. + +* "git branch" (and "git checkout -b") can be told to set up + branch..rebase automatically, so that later you can say "git pull" + and magically cause "git pull --rebase" to happen. + +* "git branch --merged" and "git branch --no-merged" can be used to list + branches that have already been merged (or not yet merged) to the + current branch. + +* "git cherry-pick" and "git revert" can add a sign-off. + +* "git commit" mentions the author identity when you are committing + somebody else's changes. + * "git diff/log --dirstat" output is consistent between binary and textual changes. +* "git filter-branch" rewrites signed tags by demoting them to annotated. + +* "git format-patch --no-binary" can produce a patch that lack binary + changes (i.e. cannot be used to propagate the whole changes) meant only + for reviewing. + * "git gc --auto" honors a new pre-aut-gc hook to temporarily disable it. * "git log --pretty=tformat:" gives a LF after each entry, instead of giving a LF between each pair of entries which is how "git log --pretty=format:" works. +* "git log" and friends learned the "--graph" option to show the ancestry + graph at the left margin of the output. + +* "git log" and friends can be told to use date format that is different + from the default via 'log.date' configuration variable. + * "git send-email" now can send out messages outside a git repository. +* "git send-email --compose" was made aware of rfc2047 quoting. + * "git status" can optionally include output from "git submodule summary". +* "git svn" learned --add-author-from option to propagate the authorship + by munging the commit log message. + * "gitweb" can read from a system-wide configuration file. (internal) @@ -54,6 +101,6 @@ this release, unless otherwise noted. -- exec >/var/tmp/1 -O=v1.5.5-56-g5f0734f +O=v1.5.6-rc0 echo O=`git describe refs/heads/master` git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint From 37869f40a817d4bf81f05cc62d9d39db7a3dc161 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 25 May 2008 20:19:25 -0700 Subject: [PATCH 129/202] log --graph: do not accept log --graphbogus An obvious fix to the argument parser. Signed-off-by: Junio C Hamano --- revision.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/revision.c b/revision.c index fb9924e5af..ac057e1e65 100644 --- a/revision.c +++ b/revision.c @@ -1202,7 +1202,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch get_commit_format(arg+8, revs); continue; } - if (!prefixcmp(arg, "--graph")) { + if (!strcmp(arg, "--graph")) { revs->topo_order = 1; revs->rewrite_parents = 1; revs->graph = graph_init(revs); From 48ded91674604c9756d53a7e657da0196795136f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 25 May 2008 20:19:30 -0700 Subject: [PATCH 130/202] log --pretty: do not accept bogus "--prettyshort" ... nor bogus "format.pretty = '=short'". Both are syntax errors. Signed-off-by: Junio C Hamano --- pretty.c | 2 -- revision.c | 7 ++++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pretty.c b/pretty.c index 687293224c..8eb39e915e 100644 --- a/pretty.c +++ b/pretty.c @@ -28,8 +28,6 @@ void get_commit_format(const char *arg, struct rev_info *rev) rev->commit_format = CMIT_FMT_DEFAULT; return; } - if (*arg == '=') - arg++; if (!prefixcmp(arg, "format:") || !prefixcmp(arg, "tformat:")) { const char *cp = strchr(arg, ':') + 1; free(user_format); diff --git a/revision.c b/revision.c index ac057e1e65..fc66755259 100644 --- a/revision.c +++ b/revision.c @@ -1197,11 +1197,16 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch revs->verbose_header = 1; continue; } - if (!prefixcmp(arg, "--pretty")) { + if (!strcmp(arg, "--pretty")) { revs->verbose_header = 1; get_commit_format(arg+8, revs); continue; } + if (!prefixcmp(arg, "--pretty=")) { + revs->verbose_header = 1; + get_commit_format(arg+9, revs); + continue; + } if (!strcmp(arg, "--graph")) { revs->topo_order = 1; revs->rewrite_parents = 1; From 97561fff3263add59ec25207a0c5a635b28ce9b9 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 25 May 2008 22:17:57 -0400 Subject: [PATCH 131/202] Don't diff empty tree on branch creation in paranoid update hook Listing all files in a branch during branch creation is silly; the user's file-level ACLs probably don't mean anything at this point. We now treat the base case of 0{40} as an empty diff, as this happens only when the user is creating the branch and there are file level ACLs that diff against the old value of the branch. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index 068fa37083..6e0d97c89f 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -225,14 +225,12 @@ sub load_diff ($) { local $/ = "\0"; my %this_diff; if ($base =~ /^0{40}$/) { - open(T,'-|','git','ls-tree', - '-r','--name-only','-z', - $new) or return undef; - while () { - chop; - $this_diff{$_} = 'A'; - } - close T or return undef; + # Don't load the diff at all; we are making the + # branch and have no base to compare to in this + # case. A file level ACL makes no sense in this + # context. Having an empty diff will allow the + # branch creation. + # } else { open(T,'-|','git','diff-tree', '-r','--name-status','-z', From 50b7b2ee99cb98265f847d91159cb3215c6f2379 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 25 May 2008 22:18:01 -0400 Subject: [PATCH 132/202] Don't load missing ACL files in paranoid update hook If a user or group ACL file does not exist in the current tip revision of the acl repository we will get an error from cat-file when we ask for that blob as it cannot be resolved. A quick look at the history by rev-list can tell us if there is a path there or not. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index 6e0d97c89f..ae94822cd3 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -136,6 +136,7 @@ sub parse_config ($$$$) { local $ENV{GIT_DIR} = shift; my $br = shift; my $fn = shift; + return unless git_value('rev-list','--max-count=1',$br,'--',$fn); info "Loading $br:$fn"; open(I,'-|','git','cat-file','blob',"$br:$fn"); my $section = ''; From fa620f1ac8191fa72e54b8b6acc3e424ecfae26e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 25 May 2008 22:18:05 -0400 Subject: [PATCH 133/202] Ignore no-op changes in paranoid update hook If the hook gets invoked with identical old and new ids there is no change taking place. We probably should not have been called, but instead of failing silently allow the no-op. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index ae94822cd3..d18b317b2f 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -259,6 +259,7 @@ deny "Refusing funny ref $ref" unless $ref =~ s,^refs/,,; deny "Bad old value $old" unless $old =~ /^[a-z0-9]{40}$/; deny "Bad new value $new" unless $new =~ /^[a-z0-9]{40}$/; deny "Cannot determine who you are." unless $this_user; +grant "No change requested." if $old eq $new; $repository_name = File::Spec->rel2abs($git_dir); $repository_name =~ m,/([^/]+)(?:\.git|/\.git)$,; From 20bd3b072f93a8a8a4151a08513cfc3622ac963d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 25 May 2008 21:11:24 -0700 Subject: [PATCH 134/202] Release Notes for 1.5.5.2 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.5.2.txt | 27 +++++++++++++++++++++++++++ RelNotes | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 Documentation/RelNotes-1.5.5.2.txt diff --git a/Documentation/RelNotes-1.5.5.2.txt b/Documentation/RelNotes-1.5.5.2.txt new file mode 100644 index 0000000000..391a7b02ea --- /dev/null +++ b/Documentation/RelNotes-1.5.5.2.txt @@ -0,0 +1,27 @@ +GIT v1.5.5.2 Release Notes +========================== + +Fixes since v1.5.5.1 +-------------------- + + * "git repack -n" was mistakenly made no-op earlier. + + * "git imap-send" wanted to always have imap.host even when use of + imap.tunnel made it unnecessary. + + * reflog syntax that uses time e.g. "HEAD@{10 seconds ago}:path" did not + stop parsing at the closing "}". + + * "git rev-parse --symbolic-full-name ^master^2" printed solitary "^", + but it should print nothing. + + * "git commit" did not detect when it failed to write tree objects. + + * "git fetch" sometimes transferred too many objects unnecessarily. + + * a path specification "a/b" in .gitattributes file should not match + "sub/a/b". + + * various gitweb fixes. + +Also comes with various documentation updates. diff --git a/RelNotes b/RelNotes index 730df98bdc..94f784eb97 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes-1.5.5.1.txt \ No newline at end of file +Documentation/RelNotes-1.5.5.2.txt \ No newline at end of file From 5070b49e529e2df30ec2e33073267d281450dde6 Mon Sep 17 00:00:00 2001 From: Pieter de Bie Date: Sun, 25 May 2008 01:21:53 +0200 Subject: [PATCH 135/202] builtin-fast-export: Only output a single parent per line According to the git-fast-import man-page, you can only put a single committish per merge: line, like this: merge :10 merge :11 However, git-fast-export puts all parents on a single line, like this: merge :10 :11 This changes fast-export to output a single parent per line. Otherwise neither git-fast-import nor bzr-fast-import can read its output. [jc: fix-up to remove excess LF in the output that makes fast-import barf] Signed-off-by: Pieter de Bie Signed-off-by: Junio C Hamano --- builtin-fast-export.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/builtin-fast-export.c b/builtin-fast-export.c index 724cff35d3..4bf5b58bfc 100755 --- a/builtin-fast-export.c +++ b/builtin-fast-export.c @@ -205,14 +205,10 @@ static void handle_commit(struct commit *commit, struct rev_info *rev) continue; if (i == 0) printf("from :%d\n", mark); - else if (i == 1) - printf("merge :%d", mark); else - printf(" :%d", mark); + printf("merge :%d\n", mark); i++; } - if (i > 1) - printf("\n"); log_tree_diff_flush(rev); rev->diffopt.output_format = saved_output_format; From 2d3922dc611e4615bc2a8a151f2e382d44971aed Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 25 May 2008 21:14:09 -0700 Subject: [PATCH 136/202] Documentation/git.txt: link to 1.5.5.2 documentation. Signed-off-by: Junio C Hamano --- Documentation/git.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/git.txt b/Documentation/git.txt index 735f0d19c8..1f68dec541 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -46,10 +46,11 @@ Documentation for older releases are available here: * link:v1.5.5/git.html[documentation for release 1.5.5] * release notes for + link:RelNotes-1.5.5.2.txt[1.5.5.2], link:RelNotes-1.5.5.1.txt[1.5.5.1], link:RelNotes-1.5.5.txt[1.5.5]. -* link:v1.5.5.1/git.html[documentation for release 1.5.5.1] +* link:v1.5.5.2/git.html[documentation for release 1.5.5.2] * link:v1.5.4.5/git.html[documentation for release 1.5.4.5] From 37a12dda24f7ab250869db7eb00157f78d40c724 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 26 May 2008 14:20:54 +0100 Subject: [PATCH 137/202] hg-to-git: add --verbose option This patch adds an option to make hg-to-git quiet by default. Note: it only suppresses those messages that would be printed when everything was up-to-date. Signed-off-by: Johannes Schindelin Acked-by: Stelian Pop Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index d72ffbb777..f68ef725d4 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -46,6 +46,7 @@ options: for incrementals -n, --nrepack=INT: number of changesets that will trigger a repack (default=0, -1 to deactivate) + -v, --verbose: be verbose required: hgprj: name of the HG project to import (directory) @@ -75,15 +76,18 @@ def getgitenv(user, date): state = '' opt_nrepack = 0 +verbose = False try: - opts, args = getopt.getopt(sys.argv[1:], 's:t:n:', ['gitstate=', 'tempdir=', 'nrepack=']) + opts, args = getopt.getopt(sys.argv[1:], 's:t:n:v', ['gitstate=', 'tempdir=', 'nrepack=', 'verbose']) for o, a in opts: if o in ('-s', '--gitstate'): state = a state = os.path.abspath(state) if o in ('-n', '--nrepack'): opt_nrepack = int(a) + if o in ('-v', '--verbose'): + verbose = True if len(args) != 1: raise('params') except: @@ -95,17 +99,20 @@ os.chdir(hgprj) if state: if os.path.exists(state): - print 'State does exist, reading' + if verbose: + print 'State does exist, reading' f = open(state, 'r') hgvers = pickle.load(f) else: print 'State does not exist, first run' tip = os.popen('hg tip --template "{rev}"').read() -print 'tip is', tip +if verbose: + print 'tip is', tip # Calculate the branches -print 'analysing the branches...' +if verbose: + print 'analysing the branches...' hgchildren["0"] = () hgparents["0"] = (None, None) hgbranch["0"] = "master" @@ -232,7 +239,8 @@ if hgnewcsets >= opt_nrepack and opt_nrepack != -1: # write the state for incrementals if state: - print 'Writing state' + if verbose: + print 'Writing state' f = open(state, 'w') pickle.dump(hgvers, f) From d3d20b846891ebca4f4edf4f0ce6ba6fc73878a7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 26 May 2008 14:56:19 -0700 Subject: [PATCH 138/202] Makefile: fix dependency on wt-status.h Noticed by Hannes, reported by Dscho. Signed-off-by: Junio C Hamano --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index f08d5f7bed..4e36487c3c 100644 --- a/Makefile +++ b/Makefile @@ -375,6 +375,7 @@ LIB_H += tree.h LIB_H += tree-walk.h LIB_H += unpack-trees.h LIB_H += utf8.h +LIB_H += wt-status.h LIB_OBJS += alias.o LIB_OBJS += alloc.o From f8fcb57bb45e8d3d8f7921377d6c2503590efc3c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 26 May 2008 15:09:51 -0700 Subject: [PATCH 139/202] show-branch --current: do not barf on detached HEAD The code assumed that there always is the current branch, but the result from resolve_ref() on detached HEAD does not even start with "refs/heads/". Originally noticed and fixed by Stephan Beyer. Signed-off-by: Junio C Hamano --- builtin-show-branch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 019abd3527..a383323184 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -782,8 +782,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) has_head++; } if (!has_head) { - int pfxlen = strlen("refs/heads/"); - append_one_rev(head + pfxlen); + int offset = !prefixcmp(head, "refs/heads/") ? 11 : 0; + append_one_rev(head + offset); } } From 95dcfa3633004da0049d3d0fa03f80589cbcaf31 Mon Sep 17 00:00:00 2001 From: Twiinz Date: Mon, 19 May 2008 00:01:58 -0400 Subject: [PATCH 140/202] git-gui: Vertically align textboxes with labels In git-gui after clicking either on 'Create New Repository' or 'Open Existing Repository' the form elements aren't centered like they are pretty much everywhere else in the app. At least when ran on a mac, haven't checked on other platforms. Using grid instead of pack seems to fix this. Signed-off-by: Shawn O. Pearce --- lib/choose_repository.tcl | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/choose_repository.tcl b/lib/choose_repository.tcl index ae4a4cd0a8..3180786158 100644 --- a/lib/choose_repository.tcl +++ b/lib/choose_repository.tcl @@ -388,9 +388,7 @@ method _do_new {} { -command [cb _new_local_path] set w_localpath $w_body.where.t - pack $w_body.where.b -side right - pack $w_body.where.l -side left - pack $w_body.where.t -fill x + grid $w_body.where.l $w_body.where.t $w_body.where.b -sticky ew pack $w_body.where -fill x trace add variable @local_path write [cb _write_local_path] @@ -987,9 +985,7 @@ method _do_open {} { -text [mc "Browse"] \ -command [cb _open_local_path] - pack $w_body.where.b -side right - pack $w_body.where.l -side left - pack $w_body.where.t -fill x + grid $w_body.where.l $w_body.where.t $w_body.where.b -sticky ew pack $w_body.where -fill x trace add variable @local_path write [cb _write_local_path] From df2740b066b2d6649449f2d8efdd0727647ab57c Mon Sep 17 00:00:00 2001 From: Michele Ballabio Date: Mon, 26 May 2008 15:24:02 +0200 Subject: [PATCH 141/202] Documentation: fix graph in git-rev-parse.txt Preformatted html and man pages show a mangled graph, caused by a backslash. Commit f1ec6b22a8c1ab1cca0f1875f85aea5d2434e5a6 fixed this same issue, but it seems that new versions of the Asciidoc toolchain changed their behaviour. Signed-off-by: Michele Ballabio Signed-off-by: Junio C Hamano --- Documentation/git-rev-parse.txt | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 6513c2efe1..5981c79f6a 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -238,16 +238,18 @@ Here is an illustration, by Jon Loeliger. Both commit nodes B and C are parents of commit node A. Parent commits are ordered left-to-right. - G H I J - \ / \ / - D E F - \ | / \ - \ | / | - \|/ | - B C - \ / - \ / - A +........................................ +G H I J + \ / \ / + D E F + \ | / \ + \ | / | + \|/ | + B C + \ / + \ / + A +........................................ A = = A^0 B = A^ = A^1 = A~1 From 040366076fa0dc540309531997a987cc6d83a577 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 26 May 2008 21:54:23 -0700 Subject: [PATCH 142/202] git-diff: allow --no-index semantics a bit more Even when inside a git work tree, if two paths are given and at least one is clearly outside the work tree, it cannot be a request to diff a tracked path anyway; allow such an invocation to use --no-index semantics. Signed-off-by: Junio C Hamano --- diff-no-index.c | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/diff-no-index.c b/diff-no-index.c index 1b57feeb24..b1ae7912c1 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -144,6 +144,25 @@ static int queue_diff(struct diff_options *o, } } +static int path_outside_repo(const char *path) +{ + /* + * We have already done setup_git_directory_gently() so we + * know we are inside a git work tree already. + */ + const char *work_tree; + size_t len; + + if (!is_absolute_path(path)) + return 0; + work_tree = get_git_work_tree(); + len = strlen(work_tree); + if (strncmp(path, work_tree, len) || + (path[len] != '\0' && path[len] != '/')) + return 1; + return 0; +} + void diff_no_index(struct rev_info *revs, int argc, const char **argv, int nongit, const char *prefix) @@ -162,13 +181,19 @@ void diff_no_index(struct rev_info *revs, break; } - /* - * No explicit --no-index, but "git diff --opts A B" outside - * a git repository is a cute hack to support. - */ - if (!no_index && !nongit) - return; - + if (!no_index && !nongit) { + /* + * Inside a git repository, without --no-index. Only + * when a path outside the repository is given, + * e.g. "git diff /var/tmp/[12]", or "git diff + * Makefile /var/tmp/Makefile", allow it to be used as + * a colourful "diff" replacement. + */ + if ((argc != i + 2) || + (!path_outside_repo(argv[i]) && + !path_outside_repo(argv[i+1]))) + return; + } if (argc != i + 2) die("git diff %s takes two paths", no_index ? "--no-index" : "[--no-index]"); From b3fde6ccb1cbdd362dec19e317905f71528b3a53 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 26 May 2008 22:35:07 -0700 Subject: [PATCH 143/202] git diff --no-index: default to page like other diff frontends Signed-off-by: Junio C Hamano --- diff-no-index.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/diff-no-index.c b/diff-no-index.c index b1ae7912c1..f6994cf5fb 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -198,6 +198,13 @@ void diff_no_index(struct rev_info *revs, die("git diff %s takes two paths", no_index ? "--no-index" : "[--no-index]"); + /* + * If the user asked for our exit code then don't start a + * pager or we would end up reporting its exit code instead. + */ + if (!DIFF_OPT_TST(&revs->diffopt, EXIT_WITH_STATUS)) + setup_pager(); + diff_setup(&revs->diffopt); if (!revs->diffopt.output_format) revs->diffopt.output_format = DIFF_FORMAT_PATCH; From dfd702ef0be17937651e925f8c0367b4b9d3de2f Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 27 May 2008 00:15:06 +0100 Subject: [PATCH 144/202] Fix t5100 for Windows On Windows, "nul" is not allowed as a file name. So bend over for that platform even more than we do already. Signed-off-by: Johannes Schindelin --- t/t5100-mailinfo.sh | 4 ++-- t/t5100/{nul => nul-plain} | Bin 2 files changed, 2 insertions(+), 2 deletions(-) rename t/t5100/{nul => nul-plain} (100%) diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh index a8b78ebf7d..577ecc210a 100755 --- a/t/t5100-mailinfo.sh +++ b/t/t5100-mailinfo.sh @@ -27,8 +27,8 @@ done test_expect_success 'respect NULs' ' - git mailsplit -d3 -o. ../t5100/nul && - cmp ../t5100/nul 001 && + git mailsplit -d3 -o. ../t5100/nul-plain && + cmp ../t5100/nul-plain 001 && (cat 001 | git mailinfo msg patch) && test 4 = $(wc -l < patch) diff --git a/t/t5100/nul b/t/t5100/nul-plain similarity index 100% rename from t/t5100/nul rename to t/t5100/nul-plain From e2829542f8d63138abe19ef638a0845d18695d5b Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 27 May 2008 08:55:25 +0200 Subject: [PATCH 145/202] Fix reference to $(template_dir) in Makefile. The brackets were missing, so that 'make install' would install into the directory template/emplate_dir. Signed-off-by: Johannes Sixt --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d8edd42a23..3fd53e4e03 100644 --- a/Makefile +++ b/Makefile @@ -1260,7 +1260,7 @@ remove-dashes: ifeq ($(firstword $(subst /, ,$(template_dir))),..) template_instdir = $(gitexecdir)/$(template_dir) else -template_instdir = $template_dir +template_instdir = $(template_dir) endif export template_instdir From 9ea874f89a6a90970364630ed8e80e1909ded409 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 27 May 2008 09:38:07 +0200 Subject: [PATCH 146/202] Skip tests that use chmod 0 - it does not make files unreadable on Windows. Signed-off-by: Johannes Sixt --- t/t3700-add.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/t/t3700-add.sh b/t/t3700-add.sh index 79cb00d1e8..55e6425a29 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -183,6 +183,10 @@ test_expect_success 'git add --refresh' ' test -z "`git diff-index HEAD -- foo`" ' +if case $(uname -s) in *MINGW*) :;; *) false;; esac then + say "chmod 0 does not make files unreadable - skipping tests" +else + test_expect_success 'git add should fail atomically upon an unreadable file' ' git reset --hard && date >foo1 && @@ -226,4 +230,6 @@ test_expect_success 'git add (add.ignore-errors = false)' ' ! ( git ls-files foo1 | grep foo1 ) ' +fi # skip chmod 0 tests + test_done From e00e4b2ef41c6622a4afcbdb8dc0725b6f1b459a Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 2 May 2008 21:46:54 +0200 Subject: [PATCH 147/202] Skip unwritable object directory tests. chmod a-w does not make a directory unwritable on Windows. Signed-off-by: Johannes Sixt --- t/t0004-unwritable.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/t/t0004-unwritable.sh b/t/t0004-unwritable.sh index 9255c63c08..f1385d6ded 100755 --- a/t/t0004-unwritable.sh +++ b/t/t0004-unwritable.sh @@ -4,6 +4,13 @@ test_description='detect unwritable repository and fail correctly' . ./test-lib.sh +case "$(uname -s)" in +*MINGW*) + say "chmod a-w .git/objects does not make directory unwritable - skipping tests" + test_done + ;; +esac + test_expect_success setup ' >file && From df177fba3c2da3e69780b7a56628a8a7a1de024d Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 21 Apr 2008 12:15:15 +0200 Subject: [PATCH 148/202] t5300-pack-object: Set up the objects for --strict tests only once. The two --strict tests use the exact same sequence to set up the repository. This factores the setup step into a test of its own. Signed-off-by: Johannes Sixt --- t/t5300-pack-object.sh | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index 83c67c4a5a..49817f1ce7 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -286,7 +286,7 @@ test_expect_success \ packname_4=$(git pack-objects test-4 LIST && - rm -f .git/index && - git update-index --index-info Date: Sun, 25 May 2008 18:14:29 +0200 Subject: [PATCH 149/202] Add some tests for git update-ref -d MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Karl Hasselström Signed-off-by: Junio C Hamano --- t/t1400-update-ref.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index 78cd41245b..b8b7ab4103 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -32,6 +32,14 @@ test_expect_success \ "create $m" \ "git update-ref $m $B $A && test $B"' = $(cat .git/'"$m"')' +test_expect_success "fail to delete $m with stale ref" ' + test_must_fail git update-ref -d $m $A && + test $B = "$(cat .git/$m)" +' +test_expect_success "delete $m" ' + git update-ref -d $m $B && + ! test -f .git/$m +' rm -f .git/$m test_expect_success \ @@ -49,6 +57,14 @@ test_expect_success \ "create $m (by HEAD)" \ "git update-ref HEAD $B $A && test $B"' = $(cat .git/'"$m"')' +test_expect_success "fail to delete $m (by HEAD) with stale ref" ' + test_must_fail git update-ref -d HEAD $A && + test $B = $(cat .git/$m) +' +test_expect_success "delete $m (by HEAD)" ' + git update-ref -d HEAD $B && + ! test -f .git/$m +' rm -f .git/$m test_expect_success '(not) create HEAD with old sha1' " From cb418b5a3882007616e00bc17b22e8653613ad5f Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 27 May 2008 09:36:22 +0200 Subject: [PATCH 150/202] t5700-clone-reference: Quote $U The new "trash directory" bites again. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- t/t5700-clone-reference.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh index e1ca7303ac..1c10916069 100755 --- a/t/t5700-clone-reference.sh +++ b/t/t5700-clone-reference.sh @@ -52,13 +52,13 @@ diff expected current' cd "$base_dir" -rm -f $U +rm -f "$U" test_expect_success 'cloning with reference (no -l -s)' \ -'GIT_DEBUG_SEND_PACK=3 git clone --reference B "file://$(pwd)/A" D 3>$U' +'GIT_DEBUG_SEND_PACK=3 git clone --reference B "file://$(pwd)/A" D 3>"$U"' test_expect_success 'fetched no objects' \ -'! grep "^want" $U' +'! grep "^want" "$U"' cd "$base_dir" From 37b78c25476d752953dc541e46fbb6bd5017edf7 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 27 May 2008 10:28:43 -0400 Subject: [PATCH 151/202] clone: make sure we support the transport type If we use an unsupported transport (e.g., http when curl support is not compiled in), transport_get reports an error to the user, but we still get a transport object. We need to manually check and abort the clone process at that point, or we end up with a segfault. Noticed by Thomas Rast. Signed-off-by: Jeff King Acked-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- builtin-clone.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/builtin-clone.c b/builtin-clone.c index 4740b13067..f4accbe541 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -449,6 +449,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix) struct remote *remote = remote_get(argv[0]); struct transport *transport = transport_get(remote, argv[0]); + if (!transport->get_refs_list || !transport->fetch) + die("Don't know how to clone %s", transport->url); + transport_set_option(transport, TRANS_OPT_KEEP, "yes"); if (option_depth) From 049c1ad2d6a6f000ff0e31a28fbd9e511b01fd24 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Tue, 27 May 2008 08:51:22 +0000 Subject: [PATCH 152/202] Documentation/git-bundle.txt: fix synopsis The are mandatory to git bundle create, not optional. The usage output of git bundle is already right on this. Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- Documentation/git-bundle.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-bundle.txt b/Documentation/git-bundle.txt index 505ac056e6..18330cdcd2 100644 --- a/Documentation/git-bundle.txt +++ b/Documentation/git-bundle.txt @@ -9,7 +9,7 @@ git-bundle - Move objects and refs by archive SYNOPSIS -------- [verse] -'git-bundle' create [git-rev-list args] +'git-bundle' create 'git-bundle' verify 'git-bundle' list-heads [refname...] 'git-bundle' unbundle [refname...] From a17171b473e0073f73b429abdf348de92394d6a6 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 26 May 2008 21:09:18 +0200 Subject: [PATCH 153/202] Revert "filter-branch: subdirectory filter needs --full-history" This reverts commit cfabd6eee1745cfec58cfcb794ce8847e43b888a. I had implemented it without understanding what --full-history does. Consider this history: C--M--N / / / A--B / \ / D-/ where B and C modify a path, X, in the same way so that the result is identical, and D does not modify it at all. With the path limiter X and without --full-history this is simplified to A--B i.e. only one of the paths via B or C is chosen. I had assumed that --full-history would keep both paths like this C--M / / A--B removing the path via D; but in fact it keeps the entire history. Currently, git does not have the capability to simplify to this intermediary case. However, the other extreme to keep the entire history is not wanted either in usual cases. I think we can expect that histories like the above are rare, and in the usual cases we want a simplified history. So let's remove --full-history again. (Concerning t7003, subsequent tests depend on what the test case sets up, so we can't just back out the entire test case.) Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- git-filter-branch.sh | 2 +- t/t7003-filter-branch.sh | 13 ++----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/git-filter-branch.sh b/git-filter-branch.sh index ea59015baa..d846cd9c92 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -234,7 +234,7 @@ case "$filter_subdir" in ;; *) git rev-list --reverse --topo-order --default HEAD \ - --parents --full-history "$@" -- "$filter_subdir" + --parents "$@" -- "$filter_subdir" esac > ../revs || die "Could not get the commits" commits=$(wc -l <../revs | tr -d " ") diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index efd658adb6..f126204d49 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -97,7 +97,7 @@ test_expect_success 'subdirectory filter result looks okay' ' test_must_fail git show sub:subdir ' -test_expect_success 'setup and filter history that requires --full-history' ' +test_expect_success 'more setup' ' git checkout master && mkdir subdir && echo A > subdir/new && @@ -107,16 +107,7 @@ test_expect_success 'setup and filter history that requires --full-history' ' git rm a && test_tick && git commit -m "again subdir on master" && - git merge branch && - git branch sub-master && - git-filter-branch -f --subdirectory-filter subdir sub-master -' - -test_expect_success 'subdirectory filter result looks okay' ' - test 3 = $(git rev-list -1 --parents sub-master | wc -w) && - git show sub-master^:new && - git show sub-master^2:new && - test_must_fail git show sub:subdir + git merge branch ' test_expect_success 'use index-filter to move into a subdirectory' ' From 1f684dc01c6a45341e8e47d8393f0ef66fdfc398 Mon Sep 17 00:00:00 2001 From: Lea Wiemann Date: Wed, 28 May 2008 01:25:42 +0200 Subject: [PATCH 154/202] gitweb: only display "next" links in logs if there is a next page There was a bug in the implementation of the "next" links in format_paging_nav (for log and shortlog), which caused the next links to always be displayed, even if there is no next page. This fixes it. Signed-off-by: Lea Wiemann Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 12843a4846..345acf4520 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2676,7 +2676,7 @@ sub git_print_page_nav { } sub format_paging_nav { - my ($action, $hash, $head, $page, $nrevs) = @_; + my ($action, $hash, $head, $page, $has_next_link) = @_; my $paging_nav; @@ -2694,7 +2694,7 @@ sub format_paging_nav { $paging_nav .= " ⋅ prev"; } - if ($nrevs >= (100 * ($page+1)-1)) { + if ($has_next_link) { $paging_nav .= " ⋅ " . $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); @@ -4585,7 +4585,7 @@ sub git_log { my @commitlist = parse_commits($hash, 101, (100 * $page)); - my $paging_nav = format_paging_nav('log', $hash, $head, $page, (100 * ($page+1))); + my $paging_nav = format_paging_nav('log', $hash, $head, $page, $#commitlist >= 100); git_header_html(); git_print_page_nav('log','', $hash,undef,undef, $paging_nav); @@ -5505,7 +5505,7 @@ sub git_shortlog { my @commitlist = parse_commits($hash, 101, (100 * $page)); - my $paging_nav = format_paging_nav('shortlog', $hash, $head, $page, (100 * ($page+1))); + my $paging_nav = format_paging_nav('shortlog', $hash, $head, $page, $#commitlist >= 100); my $next_link = ''; if ($#commitlist >= 100) { $next_link = From d5350fd2b3a4721885bf1b3353478970caef268c Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Tue, 27 May 2008 08:59:16 +0000 Subject: [PATCH 155/202] commit --interactive: properly update the index before commiting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When adding files through git commit --interactive, and 'quit' afterwards, the message in the editor of the commit message indicates that many (maybe all) files are deleted from the tree. Dismissing that and running git commit afterwards does the right thing. This commit fixes git commit --interactive to properly update the index before commiting. Reported by Jiří Paleček through http://bugs.debian.org/480429 Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- builtin-commit.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/builtin-commit.c b/builtin-commit.c index e3564a526a..b0fe69ecad 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -219,6 +219,8 @@ static char *prepare_index(int argc, const char **argv, const char *prefix) if (interactive) { interactive_add(argc, argv, prefix); + if (read_cache() < 0) + die("index file corrupt"); commit_style = COMMIT_AS_IS; return get_index_file(); } From 28bc30220f30850a10217d61f73e46d8a541e670 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 27 May 2008 22:20:53 -0700 Subject: [PATCH 156/202] GIT 1.5.5.3 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.5.3.txt | 12 ++++++++++++ Documentation/git.txt | 4 +++- GIT-VERSION-GEN | 2 +- RelNotes | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 Documentation/RelNotes-1.5.5.3.txt diff --git a/Documentation/RelNotes-1.5.5.3.txt b/Documentation/RelNotes-1.5.5.3.txt new file mode 100644 index 0000000000..f22f98b734 --- /dev/null +++ b/Documentation/RelNotes-1.5.5.3.txt @@ -0,0 +1,12 @@ +GIT v1.5.5.3 Release Notes +========================== + +Fixes since v1.5.5.2 +-------------------- + + * "git send-email --compose" did not notice that non-ascii contents + needed some MIME magic. + + * "git fast-export" did not export octopus merges correctly. + +Also comes with various documentation updates. diff --git a/Documentation/git.txt b/Documentation/git.txt index a070e078e7..ccaa655d1f 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -46,10 +46,12 @@ Documentation for older releases are available here: * link:v1.5.5/git.html[documentation for release 1.5.5] * release notes for + link:RelNotes-1.5.5.3.txt[1.5.5.3], + link:RelNotes-1.5.5.2.txt[1.5.5.2], link:RelNotes-1.5.5.1.txt[1.5.5.1], link:RelNotes-1.5.5.txt[1.5.5]. -* link:v1.5.5.1/git.html[documentation for release 1.5.5.1] +* link:v1.5.5.3/git.html[documentation for release 1.5.5.3] * link:v1.5.4.5/git.html[documentation for release 1.5.4.5] diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index f60bab896b..45fc112de6 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.5.5.GIT +DEF_VER=v1.5.5.3.GIT LF=' ' diff --git a/RelNotes b/RelNotes index 94f784eb97..6be0f7886f 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes-1.5.5.2.txt \ No newline at end of file +Documentation/RelNotes-1.5.5.3.txt \ No newline at end of file From b2a42f55bc419352b848751b0763b0a2d1198479 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 27 May 2008 23:12:29 -0700 Subject: [PATCH 157/202] t5100: Avoid filename "nul" There are broken filesystems that cannot have a file whose name is "nul" anywhere on it. Rename the test file to make ourselves more portable. Noticed by Mark Levedahl. Signed-off-by: Junio C Hamano --- t/t5100-mailinfo.sh | 4 ++-- t/t5100/{nul => nul-plain} | Bin 2 files changed, 2 insertions(+), 2 deletions(-) rename t/t5100/{nul => nul-plain} (100%) diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh index a8b78ebf7d..577ecc210a 100755 --- a/t/t5100-mailinfo.sh +++ b/t/t5100-mailinfo.sh @@ -27,8 +27,8 @@ done test_expect_success 'respect NULs' ' - git mailsplit -d3 -o. ../t5100/nul && - cmp ../t5100/nul 001 && + git mailsplit -d3 -o. ../t5100/nul-plain && + cmp ../t5100/nul-plain 001 && (cat 001 | git mailinfo msg patch) && test 4 = $(wc -l < patch) diff --git a/t/t5100/nul b/t/t5100/nul-plain similarity index 100% rename from t/t5100/nul rename to t/t5100/nul-plain From d683a0e00cd4734b4fab704baef1ee76205722be Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 27 May 2008 23:33:22 -0700 Subject: [PATCH 158/202] Git::cat_blob: allow using an empty blob to fix git-svn breakage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent "git-svn optimization" series introduced Git::cat_blob() subroutine whose interface was broken in that it returned the size of the blob but signalled an error by returning 0. You can never use an empty blob with such an interface. This fixes the interface to return a negative value to signal an error. Reported by Björn Steinbrink. Signed-off-by: Junio C Hamano --- git-svn.perl | 4 ++-- perl/Git.pm | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/git-svn.perl b/git-svn.perl index 37976f2505..3a6eb1cb9d 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3191,7 +3191,7 @@ sub apply_textdelta { if ($fb->{blob}) { print $base 'link ' if ($fb->{mode_a} == 120000); my $size = $::_repository->cat_blob($fb->{blob}, $base); - die "Failed to read object $fb->{blob}" unless $size; + die "Failed to read object $fb->{blob}" if ($size < 0); if (defined $exp) { seek $base, 0, 0 or croak $!; @@ -3570,7 +3570,7 @@ sub chg_file { $self->change_file_prop($fbat,'svn:special',undef); } my $size = $::_repository->cat_blob($m->{sha1_b}, $fh); - croak "Failed to read object $m->{sha1_b}" unless $size; + croak "Failed to read object $m->{sha1_b}" if ($size < 0); $fh->flush == 0 or croak $!; seek $fh, 0, 0 or croak $!; diff --git a/perl/Git.pm b/perl/Git.pm index 6ba8ee5c0d..d05b633b64 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -811,12 +811,12 @@ sub cat_blob { my $description = <$in>; if ($description =~ / missing$/) { carp "$sha1 doesn't exist in the repository"; - return 0; + return -1; } if ($description !~ /^[0-9a-fA-F]{40} \S+ (\d+)$/) { carp "Unexpected result returned from git cat-file"; - return 0; + return -1; } my $size = $1; From 6eec46bdda7393f1801df70a44d2e0577760a691 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 28 May 2008 09:47:43 -0700 Subject: [PATCH 159/202] fix sha1_pack_index_name() An earlier commit 633f43e (Remove redundant code, eliminate one static variable, 2008-05-24) had a thinko (perhaps an eyeno) that broke sha1_pack_index_name() function. One symptom of this was that the http walker is now completely broken. This should fix it. Signed-off-by: Junio C Hamano --- sha1_file.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sha1_file.c b/sha1_file.c index 9679040d62..adcf37c3f6 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -177,7 +177,7 @@ char *sha1_file_name(const unsigned char *sha1) } static char *sha1_get_pack_name(const unsigned char *sha1, - char **name, char **base) + char **name, char **base, const char *which) { static const char hex[] = "0123456789abcdef"; char *buf; @@ -187,7 +187,8 @@ static char *sha1_get_pack_name(const unsigned char *sha1, const char *sha1_file_directory = get_object_directory(); int len = strlen(sha1_file_directory); *base = xmalloc(len + 60); - sprintf(*base, "%s/pack/pack-1234567890123456789012345678901234567890.pack", sha1_file_directory); + sprintf(*base, "%s/pack/pack-1234567890123456789012345678901234567890.%s", + sha1_file_directory, which); *name = *base + len + 11; } @@ -206,14 +207,14 @@ char *sha1_pack_name(const unsigned char *sha1) { static char *name, *base; - return sha1_get_pack_name(sha1, &name, &base); + return sha1_get_pack_name(sha1, &name, &base, "pack"); } char *sha1_pack_index_name(const unsigned char *sha1) { static char *name, *base; - return sha1_get_pack_name(sha1, &name, &base); + return sha1_get_pack_name(sha1, &name, &base, "idx"); } struct alternate_object_database *alt_odb_list; From efb98b44536300f5daed25da3650033a1ce7fdef Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Wed, 28 May 2008 19:29:36 +0400 Subject: [PATCH 160/202] builtin-fetch.c (store_updated_refs): Honor update_local_ref() return value Sync with builtin-fetch--tool.c where append_fetch_head() honors update_local_ref() return value. This fixes non fast forward fetch exit status, http://bugzilla.altlinux.org/show_bug.cgi?id=15037 Signed-off-by: Dmitry V. Levin Signed-off-by: Junio C Hamano --- builtin-fetch.c | 6 +++--- t/t5518-fetch-exit-status.sh | 37 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) create mode 100755 t/t5518-fetch-exit-status.sh diff --git a/builtin-fetch.c b/builtin-fetch.c index bfe7711aa8..e81ee2d02b 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -286,7 +286,7 @@ static int store_updated_refs(const char *url, struct ref *ref_map) { FILE *fp; struct commit *commit; - int url_len, i, note_len, shown_url = 0; + int url_len, i, note_len, shown_url = 0, rc = 0; char note[1024]; const char *what, *kind; struct ref *rm; @@ -353,7 +353,7 @@ static int store_updated_refs(const char *url, struct ref *ref_map) note); if (ref) - update_local_ref(ref, what, verbose, note); + rc |= update_local_ref(ref, what, verbose, note); else sprintf(note, "* %-*s %-*s -> FETCH_HEAD", SUMMARY_WIDTH, *kind ? kind : "branch", @@ -368,7 +368,7 @@ static int store_updated_refs(const char *url, struct ref *ref_map) } } fclose(fp); - return 0; + return rc; } /* diff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh new file mode 100755 index 0000000000..c6bc65faa0 --- /dev/null +++ b/t/t5518-fetch-exit-status.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# +# Copyright (c) 2008 Dmitry V. Levin +# + +test_description='fetch exit status test' + +. ./test-lib.sh + +test_expect_success setup ' + + >file && + git add file && + git commit -m initial && + + git checkout -b side && + echo side >file && + git commit -a -m side && + + git checkout master && + echo next >file && + git commit -a -m next +' + +test_expect_success 'non fast forward fetch' ' + + test_must_fail git fetch . master:side + +' + +test_expect_success 'forced update' ' + + git fetch . +master:side + +' + +test_done From 823ea1211ca48afc229c84fcc06f66a87fa78303 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 28 May 2008 18:57:02 +0200 Subject: [PATCH 161/202] bisect: use "$GIT_DIR/BISECT_START" to check if we are bisecting It seems simpler and safer to use the BISECT_START file everywhere to decide if we are bisecting or not, instead of using it in some places and BISECT_NAMES in other places. In commit 6459c7c6786aa9bda0c7a095c9db66c36da0e5f0 (Nov 18 2007, Bisect: use "$GIT_DIR/BISECT_NAMES" to check if we are bisecting.), we decided to use BISECT_NAMES but code changed a lot and we now have to check BISECT_START first in the "bisect_start" function anyway. This patch also makes things a little bit safer by creating the BISECT_START file first and deleting it last, and also by adding checks in "bisect_clean_state". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- git-bisect.sh | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 4bcbaceb8b..991b2ef37e 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -44,7 +44,7 @@ sq() { } bisect_autostart() { - test -f "$GIT_DIR/BISECT_NAMES" || { + test -s "$GIT_DIR/BISECT_START" || { echo >&2 'You need to start by "git bisect start"' if test -t 0 then @@ -98,7 +98,7 @@ bisect_start() { # # Get rid of any old bisect state. # - bisect_clean_state + bisect_clean_state || exit # # Check for one bad and then some good revisions. @@ -146,8 +146,8 @@ bisect_start() { # # Write new start state. # - sq "$@" >"$GIT_DIR/BISECT_NAMES" && echo "$start_head" >"$GIT_DIR/BISECT_START" && + sq "$@" >"$GIT_DIR/BISECT_NAMES" && eval "$eval" && echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit # @@ -226,7 +226,7 @@ bisect_next_check() { ;; *) THEN='' - test -f "$GIT_DIR/BISECT_NAMES" || { + test -s "$GIT_DIR/BISECT_START" || { echo >&2 'You need to start by "git bisect start".' THEN='then ' } @@ -392,16 +392,12 @@ bisect_visualize() { } bisect_reset() { - test -f "$GIT_DIR/BISECT_NAMES" || { + test -s "$GIT_DIR/BISECT_START" || { echo "We are not bisecting." return } case "$#" in - 0) if [ -s "$GIT_DIR/BISECT_START" ]; then - branch=`cat "$GIT_DIR/BISECT_START"` - else - branch=master - fi ;; + 0) branch=$(cat "$GIT_DIR/BISECT_START") ;; 1) git show-ref --verify --quiet -- "refs/heads/$1" || die "$1 does not seem to be a valid branch" branch="$1" ;; @@ -416,14 +412,15 @@ bisect_clean_state() { git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* | while read ref hash do - git update-ref -d $ref $hash + git update-ref -d $ref $hash || exit done - rm -f "$GIT_DIR/BISECT_START" - rm -f "$GIT_DIR/BISECT_LOG" - rm -f "$GIT_DIR/BISECT_NAMES" - rm -f "$GIT_DIR/BISECT_RUN" + rm -f "$GIT_DIR/BISECT_LOG" && + rm -f "$GIT_DIR/BISECT_NAMES" && + rm -f "$GIT_DIR/BISECT_RUN" && # Cleanup head-name if it got left by an old version of git-bisect - rm -f "$GIT_DIR/head-name" + rm -f "$GIT_DIR/head-name" && + + rm -f "$GIT_DIR/BISECT_START" } bisect_replay () { From 56ae8df5c7b05ee8982c9447fa31116104dc568f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 28 May 2008 16:55:27 -0700 Subject: [PATCH 162/202] Manual subsection to refer to other pages is SEE ALSO Consistently say so in all caps as it is customary to do so. Signed-off-by: Junio C Hamano --- Documentation/git-add.txt | 2 +- Documentation/git-commit-tree.txt | 2 +- Documentation/git-format-patch.txt | 2 +- Documentation/git-gc.txt | 2 +- Documentation/git-gui.txt | 4 ++-- Documentation/git-ls-files.txt | 2 +- Documentation/git-pack-objects.txt | 2 +- Documentation/git-pack-redundant.txt | 2 +- Documentation/git-prune-packed.txt | 2 +- Documentation/git-prune.txt | 2 +- Documentation/git-read-tree.txt | 2 +- Documentation/git-remote.txt | 2 +- Documentation/git-repack.txt | 2 +- Documentation/git-rm.txt | 2 +- Documentation/git-status.txt | 2 +- Documentation/git-update-index.txt | 2 +- Documentation/git-var.txt | 2 +- Documentation/gitk.txt | 2 +- 18 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index bb4abe26bb..1afd0c69ed 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -246,7 +246,7 @@ characters that need C-quoting. `core.quotepath` configuration can be used to work this limitation around to some degree, but backslash, double-quote and control characters will still have problems. -See Also +SEE ALSO -------- linkgit:git-status[1] linkgit:git-rm[1] diff --git a/Documentation/git-commit-tree.txt b/Documentation/git-commit-tree.txt index 170803a6d0..700840d41f 100644 --- a/Documentation/git-commit-tree.txt +++ b/Documentation/git-commit-tree.txt @@ -88,7 +88,7 @@ Discussion include::i18n.txt[] -See Also +SEE ALSO -------- linkgit:git-write-tree[1] diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index c60ce123ec..fefcd269c6 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -223,7 +223,7 @@ as e-mailable patches: $ git format-patch -3 ------------ -See Also +SEE ALSO -------- linkgit:git-am[1], linkgit:git-send-email[1] diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt index b6b5ce1519..f4097007ff 100644 --- a/Documentation/git-gc.txt +++ b/Documentation/git-gc.txt @@ -119,7 +119,7 @@ If you are expecting some objects to be collected and they aren't, check all of those locations and decide whether it makes sense in your case to remove those references. -See Also +SEE ALSO -------- linkgit:git-prune[1] linkgit:git-reflog[1] diff --git a/Documentation/git-gui.txt b/Documentation/git-gui.txt index 6d6cd5d87c..bd5fe67b32 100644 --- a/Documentation/git-gui.txt +++ b/Documentation/git-gui.txt @@ -79,9 +79,9 @@ git gui browser maint:: selected in the browser can be viewed with the internal blame viewer. -See Also +SEE ALSO -------- -'gitk(1)':: +linkgit:gitk[1]:: The git repository browser. Shows branches, commit history and file differences. gitk is the utility started by git-gui's Repository Visualize actions. diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index da9ebf405c..4be45b001a 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -177,7 +177,7 @@ top of the directory tree. A pattern read from a file specified by --exclude-per-directory is relative to the directory that the pattern file appears in. -See Also +SEE ALSO -------- linkgit:git-read-tree[1], linkgit:gitignore[5] diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt index 3a1be08186..35d1856b55 100644 --- a/Documentation/git-pack-objects.txt +++ b/Documentation/git-pack-objects.txt @@ -200,7 +200,7 @@ Documentation ------------- Documentation by Junio C Hamano -See Also +SEE ALSO -------- linkgit:git-rev-list[1] linkgit:git-repack[1] diff --git a/Documentation/git-pack-redundant.txt b/Documentation/git-pack-redundant.txt index af4aa4a2e5..c7bc7b3362 100644 --- a/Documentation/git-pack-redundant.txt +++ b/Documentation/git-pack-redundant.txt @@ -46,7 +46,7 @@ Documentation -------------- Documentation by Lukas Sandström -See Also +SEE ALSO -------- linkgit:git-pack-objects[1] linkgit:git-repack[1] diff --git a/Documentation/git-prune-packed.txt b/Documentation/git-prune-packed.txt index 93ee82ae57..3219eb3ddc 100644 --- a/Documentation/git-prune-packed.txt +++ b/Documentation/git-prune-packed.txt @@ -42,7 +42,7 @@ Documentation -------------- Documentation by Ryan Anderson -See Also +SEE ALSO -------- linkgit:git-pack-objects[1] linkgit:git-repack[1] diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt index 3178bc44ca..82a03e3766 100644 --- a/Documentation/git-prune.txt +++ b/Documentation/git-prune.txt @@ -66,7 +66,7 @@ many other housekeeping tasks. For a description of which objects are considered for pruning, see git-fsck's --unreachable option. -See Also +SEE ALSO -------- linkgit:git-fsck[1], diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt index 8421d1fd78..2e097a140c 100644 --- a/Documentation/git-read-tree.txt +++ b/Documentation/git-read-tree.txt @@ -345,7 +345,7 @@ middle of doing, and when your working tree is ready (i.e. you have finished your work-in-progress), attempt the merge again. -See Also +SEE ALSO -------- linkgit:git-write-tree[1]; linkgit:git-ls-files[1]; linkgit:gitignore[5] diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index b20e851973..5c55290ee5 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -128,7 +128,7 @@ $ git merge origin ------------ -See Also +SEE ALSO -------- linkgit:git-fetch[1] linkgit:git-branch[1] diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index f81a6607de..ef578f0f8b 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -122,7 +122,7 @@ Documentation -------------- Documentation by Ryan Anderson -See Also +SEE ALSO -------- linkgit:git-pack-objects[1] linkgit:git-prune-packed[1] diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt index 9c81b72dbe..6481f4ae52 100644 --- a/Documentation/git-rm.txt +++ b/Documentation/git-rm.txt @@ -93,7 +93,7 @@ git-rm -f git-*.sh:: (i.e. you are listing the files explicitly), it does not remove `subdir/git-foo.sh`. -See Also +SEE ALSO -------- linkgit:git-add[1] diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt index ea4376a17f..057e2a167e 100644 --- a/Documentation/git-status.txt +++ b/Documentation/git-status.txt @@ -57,7 +57,7 @@ to -1 or an unlimited number), the submodule summary will be enabled and a summary of commits for modified submodules will be shown (see --summary-limit option of linkgit:git-submodule[1]). -See Also +SEE ALSO -------- linkgit:gitignore[5] diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index 06640603c4..77d312adca 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -312,7 +312,7 @@ The command looks at `core.ignorestat` configuration variable. See 'Using "assume unchanged" bit' section above. -See Also +SEE ALSO -------- linkgit:git-config[1], linkgit:git-add[1] diff --git a/Documentation/git-var.txt b/Documentation/git-var.txt index 2980283905..242a1d9bb6 100644 --- a/Documentation/git-var.txt +++ b/Documentation/git-var.txt @@ -45,7 +45,7 @@ Your parents must have hated you!:: Your sysadmin must hate you!:: The password(5) name field is longer than a giant static buffer. -See Also +SEE ALSO -------- linkgit:git-commit-tree[1] linkgit:git-tag[1] diff --git a/Documentation/gitk.txt b/Documentation/gitk.txt index 50d12da89f..b88fd9566a 100644 --- a/Documentation/gitk.txt +++ b/Documentation/gitk.txt @@ -85,7 +85,7 @@ Files Gitk creates the .gitk file in your $HOME directory to store preferences such as display options, font, and colors. -See Also +SEE ALSO -------- 'qgit(1)':: A repository browser written in C++ using Qt. From a7052d3521de1210026bf5a29d57c37ee7800166 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 28 May 2008 17:03:46 -0700 Subject: [PATCH 163/202] Documentation: git-cherry uses git-patch-id Geoffrey Irving noticed that git-cherry talks about comparing commits without hinting how they are compared. Signed-off-by: Junio C Hamano --- Documentation/git-cherry.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/git-cherry.txt b/Documentation/git-cherry.txt index b0468aa746..d8e0a5b843 100644 --- a/Documentation/git-cherry.txt +++ b/Documentation/git-cherry.txt @@ -13,6 +13,8 @@ DESCRIPTION ----------- The changeset (or "diff") of each commit between the fork-point and is compared against each commit between the fork-point and . +The commits are compared with their 'patch id', obtained from linkgit:git-patch-id[1] +program. Every commit that doesn't exist in the branch has its id (sha1) reported, prefixed by a symbol. The ones that have @@ -56,6 +58,10 @@ OPTIONS :: Do not report commits up to (and including) limit. +SEE ALSO +-------- +linkgit:git-patch-id[1] + Author ------ Written by Junio C Hamano From d2b3691b61d516a0ad2bf700a2a5d9113ceff0b1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 28 May 2008 14:48:57 -0700 Subject: [PATCH 164/202] "git checkout -- paths..." should error out when paths cannot be written When "git checkout -- paths..." cannot update work tree for whatever reason, checkout_entry() correctly issued an error message for the path to the end user, but the command ignored the error, causing the entire command to succeed. This fixes it. Signed-off-by: Junio C Hamano --- builtin-checkout.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/builtin-checkout.c b/builtin-checkout.c index 1ea017f5f7..00dc8cafd8 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -84,6 +84,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec) unsigned char rev[20]; int flag; struct commit *head; + int errs = 0; int newfd; struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); @@ -106,13 +107,14 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec) if (report_path_error(ps_matched, pathspec, 0)) return 1; + /* Now we are committed to check them out */ memset(&state, 0, sizeof(state)); state.force = 1; state.refresh_cache = 1; for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; if (pathspec_match(pathspec, NULL, ce->name, 0)) { - checkout_entry(ce, &state, NULL); + errs |= checkout_entry(ce, &state, NULL); } } @@ -123,7 +125,8 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec) resolve_ref("HEAD", rev, 0, &flag); head = lookup_commit_reference_gently(rev, 1); - return post_checkout_hook(head, head, 0); + errs |= post_checkout_hook(head, head, 0); + return errs; } static void show_local_changes(struct object *head) From 84a5750bc5e5211bd06857e4edb08e332d0e7adf Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 28 May 2008 14:54:02 -0700 Subject: [PATCH 165/202] checkout: make reset_clean_to_new() not die by itself Instead, have its error percolate up through the callchain and let it be the exit status of the main command. No semantic changes yet. Signed-off-by: Junio C Hamano --- builtin-checkout.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/builtin-checkout.c b/builtin-checkout.c index 00dc8cafd8..cc97724c87 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -172,7 +172,7 @@ static int reset_to_new(struct tree *tree, int quiet) return 0; } -static void reset_clean_to_new(struct tree *tree, int quiet) +static int reset_clean_to_new(struct tree *tree, int quiet) { struct unpack_trees_options opts; struct tree_desc tree_desc; @@ -189,7 +189,8 @@ static void reset_clean_to_new(struct tree *tree, int quiet) parse_tree(tree); init_tree_desc(&tree_desc, tree->buffer, tree->size); if (unpack_trees(1, &tree_desc, &opts)) - exit(128); + return 128; + return 0; } struct checkout_opts { @@ -295,7 +296,9 @@ static int merge_working_tree(struct checkout_opts *opts, return ret; merge_trees(new->commit->tree, work, old->commit->tree, new->name, "local", &result); - reset_clean_to_new(new->commit->tree, opts->quiet); + ret = reset_clean_to_new(new->commit->tree, opts->quiet); + if (ret) + return ret; } } From 74d3b23fe3771c769ccd5c97f6a46682b4061577 Mon Sep 17 00:00:00 2001 From: Luciano Rocha Date: Wed, 28 May 2008 19:53:57 +0100 Subject: [PATCH 166/202] git-init: accept --bare option It is unfortunate that "git init --bare" does not work and the only reason why "init" did not learn its own "--bare" option is because "git --bare init" already does the job (and as an option to the git 'potty', it is more generic solution). This teaches "git init" its own "--bare" option, so that both "git --bare init" and "git init --bare" works mostly the same way. [jc: rewrote the log message and added test] Signed-off-by: Luciano Rocha Signed-off-by: Junio C Hamano --- Documentation/git-init.txt | 7 ++++++- builtin-init-db.c | 9 +++++++-- t/t0001-init.sh | 11 +++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt index b17ae8485c..b48c312899 100644 --- a/Documentation/git-init.txt +++ b/Documentation/git-init.txt @@ -8,7 +8,7 @@ git-init - Create an empty git repository or reinitialize an existing one SYNOPSIS -------- -'git-init' [-q | --quiet] [--template=] [--shared[=]] +'git-init' [-q | --quiet] [--bare] [--template=] [--shared[=]] OPTIONS @@ -20,6 +20,11 @@ OPTIONS Only print error and warning messages, all other output will be suppressed. +--bare:: + +Create a bare repository. If GIT_DIR environment is not set, it is set to the +current working directory. + --template=:: Provide the directory from which templates will be used. The default template diff --git a/builtin-init-db.c b/builtin-init-db.c index d8bdf928b1..e23b8438c7 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -364,7 +364,7 @@ static int guess_repository_type(const char *git_dir) } static const char init_db_usage[] = -"git-init [-q | --quiet] [--template=] [--shared]"; +"git-init [-q | --quiet] [--bare] [--template=] [--shared[=]]"; /* * If you want to, you can share the DB area with any number of branches. @@ -383,7 +383,12 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) const char *arg = argv[1]; if (!prefixcmp(arg, "--template=")) template_dir = arg+11; - else if (!strcmp(arg, "--shared")) + else if (!strcmp(arg, "--bare")) { + static char git_dir[PATH_MAX+1]; + is_bare_repository_cfg = 1; + setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, + sizeof(git_dir)), 0); + } else if (!strcmp(arg, "--shared")) shared_repository = PERM_GROUP; else if (!prefixcmp(arg, "--shared=")) shared_repository = git_config_perm("arg", arg+9); diff --git a/t/t0001-init.sh b/t/t0001-init.sh index b0289e397a..d31887f9bf 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -79,6 +79,17 @@ test_expect_success 'GIT_DIR bare' ' check_config git-dir-bare.git true unset ' +test_expect_success 'init --bare' ' + + ( + unset GIT_DIR GIT_WORK_TREE GIT_CONFIG + mkdir git-init-bare.git && + cd git-init-bare.git && + git init --bare + ) && + check_config git-init-bare.git true unset +' + test_expect_success 'GIT_DIR non-bare' ' ( From 6286a08db3d1e718f4be6959d0f380215026800b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 28 May 2008 14:59:40 -0700 Subject: [PATCH 167/202] checkout: consolidate reset_{to_new,clean_to_new}() These two were very similar functions with only tiny bit of difference. Signed-off-by: Junio C Hamano --- builtin-checkout.c | 70 +++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 45 deletions(-) diff --git a/builtin-checkout.c b/builtin-checkout.c index cc97724c87..9af5197b60 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -151,48 +151,6 @@ static void describe_detached_head(char *msg, struct commit *commit) strbuf_release(&sb); } -static int reset_to_new(struct tree *tree, int quiet) -{ - struct unpack_trees_options opts; - struct tree_desc tree_desc; - - memset(&opts, 0, sizeof(opts)); - opts.head_idx = -1; - opts.update = 1; - opts.reset = 1; - opts.merge = 1; - opts.fn = oneway_merge; - opts.verbose_update = !quiet; - opts.src_index = &the_index; - opts.dst_index = &the_index; - parse_tree(tree); - init_tree_desc(&tree_desc, tree->buffer, tree->size); - if (unpack_trees(1, &tree_desc, &opts)) - return 128; - return 0; -} - -static int reset_clean_to_new(struct tree *tree, int quiet) -{ - struct unpack_trees_options opts; - struct tree_desc tree_desc; - - memset(&opts, 0, sizeof(opts)); - opts.head_idx = -1; - opts.skip_unmerged = 1; - opts.reset = 1; - opts.merge = 1; - opts.fn = oneway_merge; - opts.verbose_update = !quiet; - opts.src_index = &the_index; - opts.dst_index = &the_index; - parse_tree(tree); - init_tree_desc(&tree_desc, tree->buffer, tree->size); - if (unpack_trees(1, &tree_desc, &opts)) - return 128; - return 0; -} - struct checkout_opts { int quiet; int merge; @@ -203,6 +161,28 @@ struct checkout_opts { enum branch_track track; }; +static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree) +{ + struct unpack_trees_options opts; + struct tree_desc tree_desc; + + memset(&opts, 0, sizeof(opts)); + opts.head_idx = -1; + opts.update = worktree; + opts.skip_unmerged = !worktree; + opts.reset = 1; + opts.merge = 1; + opts.fn = oneway_merge; + opts.verbose_update = !o->quiet; + opts.src_index = &the_index; + opts.dst_index = &the_index; + parse_tree(tree); + init_tree_desc(&tree_desc, tree->buffer, tree->size); + if (unpack_trees(1, &tree_desc, &opts)) + return 128; + return 0; +} + struct branch_info { const char *name; /* The short name used */ const char *path; /* The full name of a real branch */ @@ -227,7 +207,7 @@ static int merge_working_tree(struct checkout_opts *opts, read_cache(); if (opts->force) { - ret = reset_to_new(new->commit->tree, opts->quiet); + ret = reset_tree(new->commit->tree, opts, 1); if (ret) return ret; } else { @@ -291,12 +271,12 @@ static int merge_working_tree(struct checkout_opts *opts, add_files_to_cache(NULL, NULL, 0); work = write_tree_from_memory(); - ret = reset_to_new(new->commit->tree, opts->quiet); + ret = reset_tree(new->commit->tree, opts, 1); if (ret) return ret; merge_trees(new->commit->tree, work, old->commit->tree, new->name, "local", &result); - ret = reset_clean_to_new(new->commit->tree, opts->quiet); + ret = reset_tree(new->commit->tree, opts, 0); if (ret) return ret; } From 2e2b887d1c2c2385825160e587d711ecb5935ef5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 28 May 2008 15:12:30 -0700 Subject: [PATCH 168/202] unpack_trees(): allow callers to differentiate worktree errors from merge errors Instead of uniformly returning -1 on any error, this teaches unpack_trees() to return -2 when the merge itself is Ok but worktree refuses to get updated. Signed-off-by: Junio C Hamano --- unpack-trees.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/unpack-trees.c b/unpack-trees.c index 0de5a31c0b..cba0aca062 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -358,8 +358,13 @@ static int unpack_failed(struct unpack_trees_options *o, const char *message) return -1; } +/* + * N-way merge "len" trees. Returns 0 on success, -1 on failure to manipulate the + * resulting index, -2 on failure to reflect the changes to the work tree. + */ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o) { + int ret; static struct cache_entry *dfc; if (len > MAX_UNPACK_TREES) @@ -404,11 +409,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options return unpack_failed(o, "Merge requires file-level merging"); o->src_index = NULL; - if (check_updates(o)) - return -1; + ret = check_updates(o) ? (-2) : 0; if (o->dst_index) *o->dst_index = o->result; - return 0; + return ret; } /* Here come the merge functions */ From 291d823e364cb51cab67f0786b809fe038b92aa8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 28 May 2008 15:26:59 -0700 Subject: [PATCH 169/202] checkout: "best effort" checkout When unpack_trees() returned an error while switching branches, we used to stop right there, exiting without writing the index out or switching HEAD. This is Ok when unpack_trees() returned an error because it detected untracked files or locally modified paths that could be overwritten by branch switching, because that error return is done before we start to modify the work tree. But it is undesirable if unpack_trees() already started to update the work tree and a failure is returned because some but not all paths are updated in the work tree, perhaps because a directory that some files need to go in was read-only by mistake, or a file that will be overwritten by branch switching had a mandatory lock on it and we failed to unlink it. This changes the behaviour upon such an error to complete the branch switching; the files updated in the work tree will hopefully be much more consistent with the index and HEAD derived from the switched-to branch. We still issue error messages, and exit the command with non-zero status, so scripted callers need to notice it. Signed-off-by: Junio C Hamano --- builtin-checkout.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/builtin-checkout.c b/builtin-checkout.c index 9af5197b60..93ea69bfaa 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -155,6 +155,7 @@ struct checkout_opts { int quiet; int merge; int force; + int writeout_error; char *new_branch; int new_branch_log; @@ -178,9 +179,20 @@ static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree) opts.dst_index = &the_index; parse_tree(tree); init_tree_desc(&tree_desc, tree->buffer, tree->size); - if (unpack_trees(1, &tree_desc, &opts)) + switch (unpack_trees(1, &tree_desc, &opts)) { + case -2: + o->writeout_error = 1; + /* + * We return 0 nevertheless, as the index is all right + * and more importantly we have made best efforts to + * update paths in the work tree, and we cannot revert + * them. + */ + case 0: + return 0; + default: return 128; - return 0; + } } struct branch_info { @@ -243,7 +255,8 @@ static int merge_working_tree(struct checkout_opts *opts, tree = parse_tree_indirect(new->commit->object.sha1); init_tree_desc(&trees[1], tree->buffer, tree->size); - if (unpack_trees(2, trees, &topts)) { + ret = unpack_trees(2, trees, &topts); + if (ret == -1) { /* * Unpack couldn't do a trivial merge; either * give up or do a real merge, depending on @@ -478,7 +491,8 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new) update_refs_for_switch(opts, &old, new); - return post_checkout_hook(old.commit, new->commit, 1); + ret = post_checkout_hook(old.commit, new->commit, 1); + return ret || opts->writeout_error; } int cmd_checkout(int argc, const char **argv, const char *prefix) From b7f685a754fc7ad313ecf7ad717f3ce35195ff33 Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Fri, 30 May 2008 13:14:24 +0200 Subject: [PATCH 170/202] Clearify the documentation for core.ignoreStat The previous documentation didn't make it clear that the "assume unchanged" was on per file basis, and not a global flag. Signed-off-by: Marius Storm-Olsen Signed-off-by: Junio C Hamano --- Documentation/config.txt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index c298dc21c5..5331b450ea 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -205,10 +205,13 @@ Can be overridden by the 'GIT_PROXY_COMMAND' environment variable handling). core.ignoreStat:: - The working copy files are assumed to stay unchanged until you - mark them otherwise manually - Git will not detect the file changes - by lstat() calls. This is useful on systems where those are very - slow, such as Microsoft Windows. See linkgit:git-update-index[1]. + If true, commands which modify both the working tree and the index + will mark the updated paths with the "assume unchanged" bit in the + index. These marked files are then assumed to stay unchanged in the + working copy, until you mark them otherwise manually - Git will not + detect the file changes by lstat() calls. This is useful on systems + where those are very slow, such as Microsoft Windows. + See linkgit:git-update-index[1]. False by default. core.preferSymlinkRefs:: From aa9349d449bbf6bd7d28a5279f30a9734f77da8f Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Fri, 30 May 2008 14:38:35 +0200 Subject: [PATCH 171/202] Add shortcut in refresh_cache_ent() for marked entries. When a cache entry has been marked as CE_VALID, the user has promised us that any change in the work tree does not matter. Just mark the entry as up-to-date, and continue. Signed-off-by: Marius Storm-Olsen Signed-off-by: Junio C Hamano --- read-cache.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/read-cache.c b/read-cache.c index ac9a8e7e32..8e5fbb6192 100644 --- a/read-cache.c +++ b/read-cache.c @@ -893,6 +893,15 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate, if (ce_uptodate(ce)) return ce; + /* + * CE_VALID means the user promised us that the change to + * the work tree does not matter and told us not to worry. + */ + if (!ignore_valid && (ce->ce_flags & CE_VALID)) { + ce_mark_uptodate(ce); + return ce; + } + if (lstat(ce->name, &st) < 0) { if (err) *err = errno; From 6ab69bf253848d641fb08348eca10b7cf79fd275 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 31 May 2008 20:28:18 +0200 Subject: [PATCH 172/202] gitweb: Remove gitweb/test/ directory Testing if gitweb handles filenames with spaces, filenames with plus sign ('+') which encodes spaces in CGI parameters (in URLs), and filenames with Unicode characters should be handled by gitweb tests. Those files are remainder of the time when gitweb was project on its own, not a part of git (with its testsuite). Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/test/Märchen | 2 -- gitweb/test/file with spaces | 4 ---- gitweb/test/file+plus+sign | 6 ------ 3 files changed, 12 deletions(-) delete mode 100644 gitweb/test/Märchen delete mode 100644 gitweb/test/file with spaces delete mode 100644 gitweb/test/file+plus+sign diff --git a/gitweb/test/Märchen b/gitweb/test/Märchen deleted file mode 100644 index 8f7a1d3e9c..0000000000 --- a/gitweb/test/Märchen +++ /dev/null @@ -1,2 +0,0 @@ -Märchen -Mrchen diff --git a/gitweb/test/file with spaces b/gitweb/test/file with spaces deleted file mode 100644 index f108543c4e..0000000000 --- a/gitweb/test/file with spaces +++ /dev/null @@ -1,4 +0,0 @@ -This -filename -contains -spaces. diff --git a/gitweb/test/file+plus+sign b/gitweb/test/file+plus+sign deleted file mode 100644 index fd05278808..0000000000 --- a/gitweb/test/file+plus+sign +++ /dev/null @@ -1,6 +0,0 @@ -This -filename -contains -+ -plus -chars. From c16570c42a748a29031281badd4762dd4e71d3d0 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sat, 31 May 2008 02:18:08 +0200 Subject: [PATCH 173/202] Revision walking documentation: document most important functions Unfortunately the list is not complete, but includes the essential ones. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- .../technical/api-revision-walking.txt | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/Documentation/technical/api-revision-walking.txt b/Documentation/technical/api-revision-walking.txt index 01a24551af..996da0503a 100644 --- a/Documentation/technical/api-revision-walking.txt +++ b/Documentation/technical/api-revision-walking.txt @@ -1,9 +1,67 @@ revision walking API ==================== +The revision walking API offers functions to build a list of revisions +and then iterate over that list. + +Calling sequence +---------------- + +The walking API has a given calling sequence: first you need to +initialize a rev_info structure, then add revisions to control what kind +of revision list do you want to get, finally you can iterate over the +revision list. + +Functions +--------- + +`init_revisions`:: + + Initialize a rev_info structure with default values. The second + parameter may be NULL or can be prefix path, and then the `.prefix` + variable will be set to it. This is typically the first function you + want to call when you want to deal with a revision list. After calling + this function, you are free to customize options, like set + `.ignore_merges` to 0 if you don't want to ignore merges, and so on. See + `revision.h` for a complete list of available options. + +`add_pending_object`:: + + This function can be used if you want to add commit objects as revision + information. You can use the `UNINTERESTING` object flag to indicate if + you want to include or exclude the given commit (and commits reachable + from the given commit) from the revision list. ++ +NOTE: If you have the commits as a string list then you probably want to +use setup_revisions(), instead of parsing each string and using this +function. + +`setup_revisions`:: + + Parse revision information, filling in the `rev_info` structure, and + removing the used arguments from the argument list. Returns the number + of arguments left that weren't recognized, which are also moved to the + head of the argument list. The last parameter is used in case no + parameter given by the first two arguments. + +`prepare_revision_walk`:: + + Prepares the rev_info structure for a walk. You should check if it + returns any error (non-zero return code) and if it does not, you can + start using get_revision() to do the iteration. + +`get_revision`:: + + Takes a pointer to a `rev_info` structure and iterates over it, + returning a `struct commit *` each time you call it. The end of the + revision list is indicated by returning a NULL pointer. + +Data structures +--------------- + Talk about , things like: * two diff_options, one for path limiting, another for output; -* calling sequence: init_revisions(), setup_revsions(), get_revision(); +* remaining functions; (Linus, JC, Dscho) From ad5fa3cc0e115a8b111868af2f727322feb144cb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 29 May 2008 16:55:53 +0200 Subject: [PATCH 174/202] rollback lock files on more signals than just SIGINT Other signals are also common, for example SIGTERM and SIGHUP. This patch modifies the lock file mechanism to catch more signals. It also modifies http-push.c which was missing SIGTERM. Signed-off-by: Paolo Bonzini Signed-off-by: Junio C Hamano --- http-push.c | 1 + lockfile.c | 3 +++ t/t7502-commit.sh | 14 ++++++++++++++ 3 files changed, 18 insertions(+) diff --git a/http-push.c b/http-push.c index f173dcd64f..c93e781c3b 100644 --- a/http-push.c +++ b/http-push.c @@ -2277,6 +2277,7 @@ int main(int argc, char **argv) signal(SIGINT, remove_locks_on_signal); signal(SIGHUP, remove_locks_on_signal); signal(SIGQUIT, remove_locks_on_signal); + signal(SIGTERM, remove_locks_on_signal); /* Check whether the remote has server info files */ remote->can_update_info_refs = 0; diff --git a/lockfile.c b/lockfile.c index cfc7335347..4023797b00 100644 --- a/lockfile.c +++ b/lockfile.c @@ -135,6 +135,9 @@ static int lock_file(struct lock_file *lk, const char *path) if (0 <= lk->fd) { if (!lock_file_list) { signal(SIGINT, remove_lock_file_on_signal); + signal(SIGHUP, remove_lock_file_on_signal); + signal(SIGTERM, remove_lock_file_on_signal); + signal(SIGQUIT, remove_lock_file_on_signal); atexit(remove_lock_file); } lk->owner = getpid(); diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh index 3531a992a9..46ec1ce8aa 100755 --- a/t/t7502-commit.sh +++ b/t/t7502-commit.sh @@ -212,4 +212,18 @@ test_expect_success 'do not fire editor in the presence of conflicts' ' test "`cat .git/result`" = "editor not started" ' +pwd=`pwd` +cat > .git/FAKE_EDITOR << EOF +#! /bin/sh +# kill -TERM command added below. +EOF + +test_expect_success 'a SIGTERM should break locks' ' + echo >>negative && + sh -c '\'' + echo kill -TERM $$ >> .git/FAKE_EDITOR + GIT_EDITOR=.git/FAKE_EDITOR exec git commit -a'\'' && exit 1 # should fail + ! test -f .git/index.lock +' + test_done From 5812473335161e3d8e5a1e085c5894a247472bdb Mon Sep 17 00:00:00 2001 From: "John J. Franey" Date: Thu, 29 May 2008 13:32:31 -0400 Subject: [PATCH 175/202] Clarify description of argument to pull/fetch for naming remotes. Alter the description of in OPTIONS section to explicitly state that a 'remote name' is accepted. Rewrite REMOTES section to more directly identify the different kinds of remote-name permitted. Signed-off-by: John J. Franey Signed-off-by: Junio C Hamano --- Documentation/pull-fetch-param.txt | 4 +- Documentation/urls-remotes.txt | 93 +++++++++++++++++++----------- 2 files changed, 63 insertions(+), 34 deletions(-) diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt index b6eb7fc618..cbee369947 100644 --- a/Documentation/pull-fetch-param.txt +++ b/Documentation/pull-fetch-param.txt @@ -1,6 +1,8 @@ :: The "remote" repository that is the source of a fetch - or pull operation. See the section <> below. + or pull operation. This parameter can be either a URL + (see the section <> below) or the name + of a remote (see the section <> below). :: The canonical format of a parameter is diff --git a/Documentation/urls-remotes.txt b/Documentation/urls-remotes.txt index 5dd1f836c6..99753006e2 100644 --- a/Documentation/urls-remotes.txt +++ b/Documentation/urls-remotes.txt @@ -1,11 +1,46 @@ include::urls.txt[] -REMOTES -------- +REMOTES[[REMOTES]] +------------------ -In addition to the above, as a short-hand, the name of a -file in `$GIT_DIR/remotes` directory can be given; the -named file should be in the following format: +The name of one of the following can be used instead +of a URL as `` argument: + +* a remote in the git configuration file: `$GIT_DIR/config`, +* a file in the `$GIT_DIR/remotes` directory, or +* a file in the `$GIT_DIR/branches` directory. + +All of these also allow you to omit the refspec from the command line +because they each contain a refspec which git will use by default. + +Named remote in configuration file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can choose to provide the name of a remote which you had previously +configured using linkgit:git-remote[1], linkgit:git-config[1] +or even by a manual edit to the `$GIT_DIR/config` file. The URL of +this remote will be used to access the repository. The refspec +of this remote will be used by default when you do +not provide a refspec on the command line. The entry in the +config file would appear like this: + +------------ + [remote ""] + url = + push = + fetch = +------------ + + +Named file in `$GIT_DIR/remotes` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can choose to provide the name of a +file in `$GIT_DIR/remotes`. The URL +in this file will be used to access the repository. The refspec +in this file will be used as default when you do not +provide a refspec on the command line. This file should have the +following format: ------------ URL: one of the above URL format @@ -14,42 +49,34 @@ named file should be in the following format: ------------ -Then such a short-hand is specified in place of - without parameters on the command -line, specified on `Push:` lines or `Pull:` -lines are used for `git-push` and `git-fetch`/`git-pull`, -respectively. Multiple `Push:` and `Pull:` lines may +`Push:` lines are used by `git-push` and +`Pull:` lines are used by `git-pull` and `git-fetch`. +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:`): +Named file in `$GIT_DIR/branches` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can choose to provide the name of a +file in `$GIT_DIR/branches`. +The URL in this file will be used to access the repository. +This file should have the following format: + ------------ - [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 -above formats, optionally followed by a hash `#` and the -name of remote head (URL fragment notation). -`$GIT_DIR/branches/` file that stores a -without the fragment is equivalent to have this in the -corresponding file in the `$GIT_DIR/remotes/` directory. +`` is required; `#` is optional. +When you do not provide a refspec on the command line, +git will use the following refspec, where `` defaults to `master`, +and `` is the name of this file +you provided in the command line. ------------ - URL: - Pull: refs/heads/master: - + refs/heads/: ------------ -while having `#` is equivalent to ------------- - URL: - Pull: refs/heads/: ------------- + + From 4c81b03e30d13dbc93ea7071438ef2da0acd4189 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 30 May 2008 08:42:16 -0700 Subject: [PATCH 176/202] Make pack creation always fsync() the result This means that we can depend on packs always being stable on disk, simplifying a lot of the object serialization worries. And unlike loose objects, serializing pack creation IO isn't going to be a performance killer. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 4 +++- cache.h | 1 + csum-file.c | 7 +++++-- csum-file.h | 6 +++++- fast-import.c | 2 +- index-pack.c | 1 + pack-write.c | 2 +- write_or_die.c | 7 +++++++ 8 files changed, 24 insertions(+), 6 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 70d2f5d416..4c2e0cd27c 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -515,10 +515,12 @@ static void write_pack_file(void) * If so, rewrite it like in fast-import */ if (pack_to_stdout || nr_written == nr_remaining) { - sha1close(f, sha1, 1); + unsigned flags = pack_to_stdout ? CSUM_CLOSE : CSUM_FSYNC; + sha1close(f, sha1, flags); } else { int fd = sha1close(f, NULL, 0); fixup_pack_header_footer(fd, sha1, pack_tmp_name, nr_written); + fsync_or_die(fd, pack_tmp_name); close(fd); } diff --git a/cache.h b/cache.h index eab1a172fe..092a997b07 100644 --- a/cache.h +++ b/cache.h @@ -761,6 +761,7 @@ extern ssize_t write_in_full(int fd, const void *buf, size_t count); extern void write_or_die(int fd, const void *buf, size_t count); extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg); extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg); +extern void fsync_or_die(int fd, const char *); /* pager.c */ extern void setup_pager(void); diff --git a/csum-file.c b/csum-file.c index 9728a99541..ace64f165e 100644 --- a/csum-file.c +++ b/csum-file.c @@ -32,21 +32,24 @@ static void sha1flush(struct sha1file *f, unsigned int count) } } -int sha1close(struct sha1file *f, unsigned char *result, int final) +int sha1close(struct sha1file *f, unsigned char *result, unsigned int flags) { int fd; unsigned offset = f->offset; + if (offset) { SHA1_Update(&f->ctx, f->buffer, offset); sha1flush(f, offset); f->offset = 0; } - if (final) { + if (flags & (CSUM_CLOSE | CSUM_FSYNC)) { /* write checksum and close fd */ SHA1_Final(f->buffer, &f->ctx); if (result) hashcpy(result, f->buffer); sha1flush(f, 20); + if (flags & CSUM_FSYNC) + fsync_or_die(f->fd, f->name); if (close(f->fd)) die("%s: sha1 file error on close (%s)", f->name, strerror(errno)); diff --git a/csum-file.h b/csum-file.h index 1af76562f3..72c9487f4f 100644 --- a/csum-file.h +++ b/csum-file.h @@ -16,9 +16,13 @@ struct sha1file { unsigned char buffer[8192]; }; +/* sha1close flags */ +#define CSUM_CLOSE 1 +#define CSUM_FSYNC 2 + extern struct sha1file *sha1fd(int fd, const char *name); extern struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp); -extern int sha1close(struct sha1file *, unsigned char *, int); +extern int sha1close(struct sha1file *, unsigned char *, unsigned int); extern int sha1write(struct sha1file *, void *, unsigned int); extern void crc32_begin(struct sha1file *); extern uint32_t crc32_end(struct sha1file *); diff --git a/fast-import.c b/fast-import.c index 93119bbd94..e72b286794 100644 --- a/fast-import.c +++ b/fast-import.c @@ -890,7 +890,7 @@ static char *create_index(void) SHA1_Update(&ctx, (*c)->sha1, 20); } sha1write(f, pack_data->sha1, sizeof(pack_data->sha1)); - sha1close(f, NULL, 1); + sha1close(f, NULL, CSUM_FSYNC); free(idx); SHA1_Final(pack_data->sha1, &ctx); return tmpfile; diff --git a/index-pack.c b/index-pack.c index aaba9443cc..5ac91baf98 100644 --- a/index-pack.c +++ b/index-pack.c @@ -694,6 +694,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name, if (!from_stdin) { close(input_fd); } else { + fsync_or_die(output_fd, curr_pack_name); err = close(output_fd); if (err) die("error while closing pack file: %s", strerror(errno)); diff --git a/pack-write.c b/pack-write.c index c66c8af725..f52cabe838 100644 --- a/pack-write.c +++ b/pack-write.c @@ -139,7 +139,7 @@ char *write_idx_file(char *index_name, struct pack_idx_entry **objects, } sha1write(f, sha1, 20); - sha1close(f, NULL, 1); + sha1close(f, NULL, CSUM_FSYNC); SHA1_Final(sha1, &ctx); return index_name; } diff --git a/write_or_die.c b/write_or_die.c index 32f9914020..630be4cb94 100644 --- a/write_or_die.c +++ b/write_or_die.c @@ -78,6 +78,13 @@ ssize_t write_in_full(int fd, const void *buf, size_t count) return total; } +void fsync_or_die(int fd, const char *msg) +{ + if (fsync(fd) < 0) { + die("%s: fsync error (%s)", msg, strerror(errno)); + } +} + void write_or_die(int fd, const void *buf, size_t count) { if (write_in_full(fd, buf, count) < 0) { From 54352bb2742cfbe53fa820eab53607a46d349ae4 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 30 May 2008 08:54:46 -0700 Subject: [PATCH 177/202] Remove now unnecessary 'sync()' calls Since the pack-files are now always created stably on disk, there is no need to sync() before pruning lose objects or old stale pack-files. [jc: with Nico's clean-up] Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 7 ++++--- builtin-prune-packed.c | 1 - builtin-prune.c | 1 - git-repack.sh | 1 - 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 4c2e0cd27c..447d492dbb 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -514,9 +514,10 @@ static void write_pack_file(void) * Did we write the wrong # entries in the header? * If so, rewrite it like in fast-import */ - if (pack_to_stdout || nr_written == nr_remaining) { - unsigned flags = pack_to_stdout ? CSUM_CLOSE : CSUM_FSYNC; - sha1close(f, sha1, flags); + if (pack_to_stdout) { + sha1close(f, sha1, CSUM_CLOSE); + } else if (nr_written == nr_remaining) { + sha1close(f, sha1, CSUM_FSYNC); } else { int fd = sha1close(f, NULL, 0); fixup_pack_header_footer(fd, sha1, pack_tmp_name, nr_written); diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c index 23faf3129f..241afbbab5 100644 --- a/builtin-prune-packed.c +++ b/builtin-prune-packed.c @@ -85,7 +85,6 @@ int cmd_prune_packed(int argc, const char **argv, const char *prefix) /* Handle arguments here .. */ usage(prune_packed_usage); } - sync(); prune_packed_objects(opts); return 0; } diff --git a/builtin-prune.c b/builtin-prune.c index 25f9304b82..bd3d2f67f3 100644 --- a/builtin-prune.c +++ b/builtin-prune.c @@ -156,7 +156,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix) mark_reachable_objects(&revs, 1); prune_object_dir(get_object_directory()); - sync(); prune_packed_objects(show_only); remove_temporary_files(); return 0; diff --git a/git-repack.sh b/git-repack.sh index 10f735cff5..072d1b40f7 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -125,7 +125,6 @@ then # We know $existing are all redundant. if [ -n "$existing" ] then - sync ( cd "$PACKDIR" && for e in $existing do From d3a7b8f5c5ed2a073936e09aa80c8a0b0cf10f5c Mon Sep 17 00:00:00 2001 From: Stephan Beyer Date: Sun, 1 Jun 2008 00:11:42 +0200 Subject: [PATCH 178/202] Add test cases for git-am Add t/t4151-am.sh that does basic testing of git-am functionality, including: * am applies patch correctly * am changes committer and keeps author * am --signoff adds Signed-off-by: line * am stays in branch * am --signoff does not add Signed-off-by: line if already there * am without --keep removes Re: and [PATCH] stuff * am --keep really keeps the subject * am -3 falls back to 3-way merge * am pauses on conflict * am --skip works * am --resolved works * am takes patches from a Pine mailbox * am fails on mail without patch * am fails on empty patch Signed-off-by: Stephan Beyer Signed-off-by: Junio C Hamano --- t/t4151-am.sh | 226 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100755 t/t4151-am.sh diff --git a/t/t4151-am.sh b/t/t4151-am.sh new file mode 100755 index 0000000000..ec1b4423c6 --- /dev/null +++ b/t/t4151-am.sh @@ -0,0 +1,226 @@ +#!/bin/sh + +test_description='git am running' + +. ./test-lib.sh + +cat >msg <failmail <pine < +Subject: DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA +Message-ID: + +This text is part of the internal format of your mail folder, and is not +a real message. It is created automatically by the mail system software. +If deleted, important folder data will be lost, and it will be re-created +with the data reset to initial values. + +EOF + +echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >expected + +test_expect_success setup ' + echo hello >file && + git add file && + test_tick && + git commit -m first && + git tag first && + echo world >>file && + git add file && + test_tick && + git commit -s -F msg && + git tag second && + git format-patch --stdout first >patch1 && + sed -n -e "3,\$p" msg >file && + git add file && + test_tick && + git commit -m third && + git format-patch --stdout first >patch2 && + git checkout -b lorem && + sed -n -e "11,\$p" msg >file && + head -n 9 msg >>file && + test_tick && + git commit -a -m "moved stuff" && + echo goodbye >another && + git add another && + test_tick && + git commit -m "added another file" && + git format-patch --stdout master >lorem-move.patch +' + +# reset time +unset test_tick +test_tick + +test_expect_success 'am applies patch correctly' ' + git checkout first && + test_tick && + git am " = \ + "$(git log -1 --pretty=format:"%cn <%ce>" HEAD)" +' + +test_expect_success 'am --signoff adds Signed-off-by: line' ' + git checkout -b master2 first && + git am --signoff " >>expected && + git cat-file commit HEAD^ | grep "Signed-off-by:" >actual && + test_cmp actual expected && + echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >expected && + git cat-file commit HEAD | grep "Signed-off-by:" >actual && + test_cmp actual expected +' + +test_expect_success 'am stays in branch' ' + test "refs/heads/master2" = "$(git symbolic-ref HEAD)" +' + +test_expect_success 'am --signoff does not add Signed-off-by: line if already there' ' + git format-patch --stdout HEAD^ >patch3 && + sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2," patch3 >patch4 + git checkout HEAD^ && + git am --signoff patch4 && + test "$(git cat-file commit HEAD | grep -c "^Signed-off-by:")" -eq 1 +' + +test_expect_success 'am without --keep removes Re: and [PATCH] stuff' ' + test "$(git rev-parse HEAD)" = "$(git rev-parse master2)" +' + +test_expect_success 'am --keep really keeps the subject' ' + git checkout HEAD^ && + git am --keep patch4 && + ! test -d .dotest && + git-cat-file commit HEAD | + grep -q -F "Re: Re: Re: [PATCH 1/5 v2] third" +' + +test_expect_success 'am -3 falls back to 3-way merge' ' + git checkout -b lorem2 master2 && + sed -n -e "3,\$p" msg >file && + head -n 9 msg >>file && + git add file && + test_tick && + git commit -m "copied stuff" && + git am -3 lorem-move.patch && + ! test -d .dotest && + test -z "$(git diff lorem)" +' + +test_expect_success 'am pauses on conflict' ' + git checkout lorem2^^ && + ! git am lorem-move.patch && + test -d .dotest +' + +test_expect_success 'am --skip works' ' + git am --skip && + ! test -d .dotest && + test -z "$(git diff lorem2^^ -- file)" && + test goodbye = "$(cat another)" +' + +test_expect_success 'am --resolved works' ' + git checkout lorem2^^ && + ! git am lorem-move.patch && + test -d .dotest && + echo resolved >>file && + git add file && + git am --resolved && + ! test -d .dotest && + test goodbye = "$(cat another)" +' + +test_expect_success 'am takes patches from a Pine mailbox' ' + git checkout first && + cat pine patch1 | git am && + ! test -d .dotest && + test -z "$(git diff master^..HEAD)" +' + +test_expect_success 'am fails on mail without patch' ' + ! git am >failmail && + ! git am Date: Sun, 1 Jun 2008 00:11:43 +0200 Subject: [PATCH 179/202] Merge t4150-am-subdir.sh and t4151-am.sh into t4150-am.sh This patch moves the am test cases in t4150-am.sh and the am subdirectory test cases from t/t4150-am-subdir.sh into t/4151-am.sh. Signed-off-by: Stephan Beyer Signed-off-by: Junio C Hamano --- t/t4150-am-subdir.sh | 72 ---------------------------------- t/{t4151-am.sh => t4150-am.sh} | 34 ++++++++++++++++ 2 files changed, 34 insertions(+), 72 deletions(-) delete mode 100755 t/t4150-am-subdir.sh rename t/{t4151-am.sh => t4150-am.sh} (90%) diff --git a/t/t4150-am-subdir.sh b/t/t4150-am-subdir.sh deleted file mode 100755 index 52069b469b..0000000000 --- a/t/t4150-am-subdir.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/sh - -test_description='git am running from a subdirectory' - -. ./test-lib.sh - -test_expect_success setup ' - echo hello >world && - git add world && - test_tick && - git commit -m initial && - git tag initial && - echo goodbye >world && - git add world && - test_tick && - git commit -m second && - git format-patch --stdout HEAD^ >patchfile && - : >expect -' - -test_expect_success 'am regularly from stdin' ' - git checkout initial && - git am actual && - test_cmp expect actual -' - -test_expect_success 'am regularly from file' ' - git checkout initial && - git am patchfile && - git diff master >actual && - test_cmp expect actual -' - -test_expect_success 'am regularly from stdin in subdirectory' ' - rm -fr subdir && - git checkout initial && - ( - mkdir -p subdir && - cd subdir && - git am <../patchfile - ) && - git diff master>actual && - test_cmp expect actual -' - -test_expect_success 'am regularly from file in subdirectory' ' - rm -fr subdir && - git checkout initial && - ( - mkdir -p subdir && - cd subdir && - git am ../patchfile - ) && - git diff master >actual && - test_cmp expect actual -' - -test_expect_success 'am regularly from file in subdirectory with full path' ' - rm -fr subdir && - git checkout initial && - P=$(pwd) && - ( - mkdir -p subdir && - cd subdir && - git am "$P/patchfile" - ) && - git diff master >actual && - test_cmp expect actual -' - -test_done diff --git a/t/t4151-am.sh b/t/t4150-am.sh similarity index 90% rename from t/t4151-am.sh rename to t/t4150-am.sh index ec1b4423c6..722ae96cd5 100755 --- a/t/t4151-am.sh +++ b/t/t4150-am.sh @@ -223,4 +223,38 @@ test_expect_success 'am fails on empty patch' ' ! test -d .dotest ' +test_expect_success 'am works from stdin in subdirectory' ' + rm -fr subdir && + git checkout first && + ( + mkdir -p subdir && + cd subdir && + git am <../patch1 + ) && + test -z "$(git diff second)" +' + +test_expect_success 'am works from file (relative path given) in subdirectory' ' + rm -fr subdir && + git checkout first && + ( + mkdir -p subdir && + cd subdir && + git am ../patch1 + ) && + test -z "$(git diff second)" +' + +test_expect_success 'am works from file (absolute path given) in subdirectory' ' + rm -fr subdir && + git checkout first && + P=$(pwd) && + ( + mkdir -p subdir && + cd subdir && + git am "$P/patch1" + ) && + test -z "$(git diff second)" +' + test_done From 5aa965a0c156c6c6d6d38b82c9361a98ba8ed825 Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Sat, 31 May 2008 18:10:58 -0700 Subject: [PATCH 180/202] git-reset: honor -q and do not show progress message When running git-reset in a non-interactive setting, the -q switch works for everything except the progress updates. This squelches it. Signed-off-by: Junio C Hamano --- builtin-reset.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/builtin-reset.c b/builtin-reset.c index e32ddd90ac..f34acb1915 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -49,13 +49,14 @@ static inline int is_merge(void) return !access(git_path("MERGE_HEAD"), F_OK); } -static int reset_index_file(const unsigned char *sha1, int is_hard_reset) +static int reset_index_file(const unsigned char *sha1, int is_hard_reset, int quiet) { int i = 0; const char *args[6]; args[i++] = "read-tree"; - args[i++] = "-v"; + if (!quiet) + args[i++] = "-v"; args[i++] = "--reset"; if (is_hard_reset) args[i++] = "-u"; @@ -182,7 +183,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) OPT_SET_INT(0, "hard", &reset_type, "reset HEAD, index and working tree", HARD), OPT_BOOLEAN('q', NULL, &quiet, - "disable showing new HEAD in hard reset"), + "disable showing new HEAD in hard reset and progress message"), OPT_END() }; @@ -231,7 +232,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (is_merge() || read_cache() < 0 || unmerged_cache()) die("Cannot do a soft reset in the middle of a merge."); } - else if (reset_index_file(sha1, (reset_type == HARD))) + else if (reset_index_file(sha1, (reset_type == HARD), quiet)) die("Could not reset index file to revision '%s'.", rev); /* Any resets update HEAD to the head being switched to, From 1bd9c648408e3dd79882ab4c23af3b791c9e3c21 Mon Sep 17 00:00:00 2001 From: Lea Wiemann Date: Sat, 31 May 2008 23:11:21 +0200 Subject: [PATCH 181/202] t/test-lib.sh: resolve symlinks in working directory, for pathname comparisons Without this, some tests will fail because they compare command output of subprocesses (such as git) with $PWD -- but subprocesses have the physical path as their working directory, whereas $PWD contains the symlinked path. This fixes it. Signed-off-by: Lea Wiemann Signed-off-by: Junio C Hamano --- t/test-lib.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index 3bf570b068..7a8bd27abc 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -419,7 +419,9 @@ rm -fr "$test" || { } test_create_repo "$test" -cd "$test" || exit 1 +# Use -P to resolve symlinks in our working directory so that the cwd +# in subprocesses like git equals our $PWD (for pathname comparisons). +cd -P "$test" || exit 1 this_test=$(expr "./$0" : '.*/\(t[0-9]*\)-[^/]*$') for skp in $GIT_SKIP_TESTS From 6a15bc0d220cf3b139d80326afa0d70411916aed Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 31 May 2008 23:57:48 -0400 Subject: [PATCH 182/202] Remove unused remote_prefix member in builtin-remote Not sure when this became unused, but no code references it, other than to populate the strbuf with an initial value. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-remote.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/builtin-remote.c b/builtin-remote.c index 99a34dfe86..c76fe2e0ed 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -206,7 +206,6 @@ static void read_branches(void) struct ref_states { struct remote *remote; - struct strbuf remote_prefix; struct path_list new, stale, tracked; }; @@ -262,8 +261,6 @@ static int get_ref_states(const struct ref *ref, struct ref_states *states) } free_refs(fetch_map); - strbuf_addf(&states->remote_prefix, - "refs/remotes/%s/", states->remote->name); for_each_ref(handle_one_branch, states); sort_path_list(&states->stale); From c175a7ad32ee978baaa6524304e9406684bd1286 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 31 May 2008 23:58:05 -0400 Subject: [PATCH 183/202] Make "git-remote prune" delete refs according to fetch specs A remote may be configured to fetch into tracking branches that do not match the remote name. For example a user may have created extra remotes that will fetch to the same tracking branch namespace, but from different URLs: [remote "origin"] url = git://git.kernel.org/pub/scm/git/git.git fetch = refs/heads/*:refs/remotes/origin/* [remote "alt"] url = git://repo.or.cz/alt-git.git fetch = refs/heads/*:refs/remotes/origin/* When running `git remote prune alt` we expect stale branches to be removed from "refs/remotes/origin/*" and not from the unused namespace of "refs/remotes/alt/*". Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-remote.c | 21 ++------------------- t/t5505-remote.sh | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/builtin-remote.c b/builtin-remote.c index c76fe2e0ed..e5cfc882b5 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -419,27 +419,10 @@ static int show_or_prune(int argc, const char **argv, int prune) states.remote->name); if (prune) { - struct strbuf buf; - int prefix_len; - - strbuf_init(&buf, 0); - if (states.remote->fetch_refspec_nr == 1 && - states.remote->fetch->pattern && - !strcmp(states.remote->fetch->src, - states.remote->fetch->dst)) - /* handle --mirror remote */ - strbuf_addstr(&buf, "refs/heads/"); - else - strbuf_addf(&buf, "refs/remotes/%s/", *argv); - prefix_len = buf.len; - for (i = 0; i < states.stale.nr; i++) { - strbuf_setlen(&buf, prefix_len); - strbuf_addstr(&buf, states.stale.items[i].path); - result |= delete_ref(buf.buf, NULL); + const char *refname = states.stale.items[i].util; + result |= delete_ref(refname, NULL); } - - strbuf_release(&buf); goto cleanup_states; } diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index a37b6f5213..0d7ed1f99b 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -164,6 +164,24 @@ test_expect_success 'add --mirror && prune' ' git rev-parse --verify refs/heads/side) ' +test_expect_success 'add alt && prune' ' + (mkdir alttst && + cd alttst && + git init && + git remote add -f origin ../one && + git config remote.alt.url ../one && + git config remote.alt.fetch "+refs/heads/*:refs/remotes/origin/*") && + (cd one && + git branch -m side side2) && + (cd alttst && + git rev-parse --verify refs/remotes/origin/side && + ! git rev-parse --verify refs/remotes/origin/side2 && + git fetch alt && + git remote prune alt && + ! git rev-parse --verify refs/remotes/origin/side && + git rev-parse --verify refs/remotes/origin/side2) +' + cat > one/expect << EOF apis/master apis/side From 7ad2458fadc6cafe25e23affd6cc46cd6494a42d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 1 Jun 2008 00:28:04 -0400 Subject: [PATCH 184/202] Make "git-remote rm" delete refs acccording to fetch specs A remote may be configured to fetch into tracking branches that don't match its name. A user may have created a remote by hand that will fetch to a different tracking branch namespace: [remote "alt"] url = git://repo.or.cz/alt-git.git fetch = refs/heads/*:refs/remotes/origin/* When deleting remote alt we should clean up the refs whose names start with "refs/remotes/origin/", even though the remote itself was named alt by the user. To avoid deleting refs used by another remote we only clear refs that are unique to this remote. This prevents `git prune rm alt` from removing the refs used by say origin if alt was just using a different URL for the same repository. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-remote.c | 68 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/builtin-remote.c b/builtin-remote.c index e5cfc882b5..c49f00f58b 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -267,28 +267,66 @@ static int get_ref_states(const struct ref *ref, struct ref_states *states) return 0; } +struct known_remote { + struct known_remote *next; + struct remote *remote; +}; + +struct known_remotes { + struct remote *to_delete; + struct known_remote *list; +}; + +static int add_known_remote(struct remote *remote, void *cb_data) +{ + struct known_remotes *all = cb_data; + struct known_remote *r; + + if (!strcmp(all->to_delete->name, remote->name)) + return 0; + + r = xmalloc(sizeof(*r)); + r->remote = remote; + r->next = all->list; + all->list = r; + return 0; +} + struct branches_for_remote { - const char *prefix; + struct remote *remote; struct path_list *branches; + struct known_remotes *keep; }; static int add_branch_for_removal(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { struct branches_for_remote *branches = cb_data; + struct refspec refspec; + struct path_list_item *item; + struct known_remote *kr; - if (!prefixcmp(refname, branches->prefix)) { - struct path_list_item *item; + memset(&refspec, 0, sizeof(refspec)); + refspec.dst = (char *)refname; + if (remote_find_tracking(branches->remote, &refspec)) + return 0; - /* make sure that symrefs are deleted */ - if (flags & REF_ISSYMREF) - return unlink(git_path(refname)); - - item = path_list_append(refname, branches->branches); - item->util = xmalloc(20); - hashcpy(item->util, sha1); + /* don't delete a branch if another remote also uses it */ + for (kr = branches->keep->list; kr; kr = kr->next) { + memset(&refspec, 0, sizeof(refspec)); + refspec.dst = (char *)refname; + if (!remote_find_tracking(kr->remote, &refspec)) + return 0; } + /* make sure that symrefs are deleted */ + if (flags & REF_ISSYMREF) + return unlink(git_path(refname)); + + item = path_list_append(refname, branches->branches); + item->util = xmalloc(20); + hashcpy(item->util, sha1); + return 0; } @@ -313,8 +351,9 @@ static int rm(int argc, const char **argv) }; struct remote *remote; struct strbuf buf; + struct known_remotes known_remotes = { NULL, NULL }; struct path_list branches = { NULL, 0, 0, 1 }; - struct branches_for_remote cb_data = { NULL, &branches }; + struct branches_for_remote cb_data = { NULL, &branches, &known_remotes }; int i; if (argc != 2) @@ -324,6 +363,9 @@ static int rm(int argc, const char **argv) if (!remote) die("No such remote: %s", argv[1]); + known_remotes.to_delete = remote; + for_each_remote(add_known_remote, &known_remotes); + strbuf_init(&buf, 0); strbuf_addf(&buf, "remote.%s", remote->name); if (git_config_rename_section(buf.buf, NULL) < 1) @@ -352,9 +394,7 @@ static int rm(int argc, const char **argv) * the branches one by one, since for_each_ref() relies on cached * refs, which are invalidated when deleting a branch. */ - strbuf_reset(&buf); - strbuf_addf(&buf, "refs/remotes/%s/", remote->name); - cb_data.prefix = buf.buf; + cb_data.remote = remote; i = for_each_ref(add_branch_for_removal, &cb_data); strbuf_release(&buf); From d72ab8c8921f74b64be029235d4d2cdd9dcb87b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Hasselstr=C3=B6m?= Date: Sat, 17 May 2008 17:07:09 +0200 Subject: [PATCH 185/202] Fix path duplication in git svn commit-diff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Given an SVN repo file:///tmp/svntest/repo, trying to commit changes to a file proj/trunk/foo.txt in that repo with this command line git svn commit-diff -r2 HEAD^ HEAD file:///tmp/svntest/repo/proj/trunk gave the error message Filesystem has no item: File not found: transaction '2-6', path '/proj/trunk/proj/trunk/foo.txt' This fixes the duplication. Signed-off-by: Karl Hasselström Acked-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/git-svn.perl b/git-svn.perl index 3a6eb1cb9d..ff83358799 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -745,7 +745,7 @@ sub cmd_commit_diff { my $usage = "Usage: $0 commit-diff -r ". " []"; fatal($usage) if (!defined $ta || !defined $tb); - my $svn_path; + my $svn_path = ''; if (!defined $url) { my $gs = eval { Git::SVN->new }; if (!$gs) { @@ -769,7 +769,6 @@ sub cmd_commit_diff { $_message ||= get_commit_entry($tb)->{log}; } my $ra ||= Git::SVN::Ra->new($url); - $svn_path ||= $ra->{svn_path}; my $r = $_revision; if ($r eq 'HEAD') { $r = $ra->get_latest_revnum; From 7d45e1468331e6253d087e77d9082026320410f5 Mon Sep 17 00:00:00 2001 From: Seth Falcon Date: Mon, 19 May 2008 20:29:17 -0700 Subject: [PATCH 186/202] Add a --dry-run option to git-svn rebase When working with multiple branches in an svn repository, it can be useful to verify the svn repository and local tracking branch that will be used for the rebase operation. Signed-off-by: Seth Falcon Acked-by: Eric Wong Signed-off-by: Junio C Hamano --- Documentation/git-svn.txt | 8 ++++++-- git-svn.perl | 6 ++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index c9e4efe7f4..f4cbd2f212 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -365,11 +365,15 @@ Passed directly to git-rebase when using 'dcommit' if a -n:: --dry-run:: -This is only used with the 'dcommit' command. +This can be used with the 'dcommit' and 'rebase' commands. -Print out the series of git arguments that would show +For 'dcommit', print out the series of git arguments that would show which diffs would be committed to SVN. +For 'rebase', display the local branch associated with the upstream svn +repository associated with the current branch and the URL of svn +repository that will be fetched from. + -- ADVANCED OPTIONS diff --git a/git-svn.perl b/git-svn.perl index ff83358799..dcb8b779c8 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -177,6 +177,7 @@ my %cmd = ( 'strategy|s=s' => \$_strategy, 'local|l' => \$_local, 'fetch-all|all' => \$_fetch_all, + 'dry-run|n' => \$_dry_run, %fc_opts } ], 'commit-diff' => [ \&cmd_commit_diff, 'Commit a diff between two trees', @@ -557,6 +558,11 @@ sub cmd_rebase { die "Unable to determine upstream SVN information from ", "working tree history\n"; } + if ($_dry_run) { + print "Remote Branch: " . $gs->refname . "\n"; + print "SVN URL: " . $url . "\n"; + return; + } if (command(qw/diff-index HEAD --/)) { print STDERR "Cannot rebase with uncommited changes:\n"; command_noisy('status'); From b7166cce8cf4053d9f21e69505bef8699da68903 Mon Sep 17 00:00:00 2001 From: Christian Engwer Date: Tue, 27 May 2008 08:46:55 +0000 Subject: [PATCH 187/202] git-svn fails in prop_walk if $self->{path} is not empty If url://repo/trunk is the current Git branch, prop_walk strips trunk from the path name. That is useful as, for example "git svn show-ignore" should not return results like trunk/foo but foo if svn:ignore for trunk includes foo. The problem now is that prop_walk strips trunk from the path and then calls itself recursively. But now trunk is missing in the path and get_dir fails, because it is called for a non existing path. The attached patch fixed the problem, by adding the previously stipped $self->{path} in the recursive call. I tested it with my current git-svn repository for the commands show-ignore and show-external. Patch was submitted through http://bugs.debian.org/477393 Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- git-svn.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-svn.perl b/git-svn.perl index dcb8b779c8..47b0c37d17 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -1923,7 +1923,7 @@ sub prop_walk { foreach (sort keys %$dirent) { next if $dirent->{$_}->{kind} != $SVN::Node::dir; - $self->prop_walk($p . $_, $rev, $sub); + $self->prop_walk($self->{path} . $p . $_, $rev, $sub); } } From 3395908ee481e91bbe3ba054a7419b071b09cdef Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Sun, 1 Jun 2008 13:56:57 -0700 Subject: [PATCH 188/202] graph API: improve display of merge commits This change improves the way merge commits are displayed, to eliminate a few visual artifacts. Previously, merge commits were displayed as: | M \ | |\ | As pointed out by Teemu Likonen, this didn't look nice if the rightmost branch line was displayed as '\' on the previous line, as it then appeared to have an extra space in it: | |\ | M \ | |\ | This change updates the code so that branch lines to the right of merge commits are printed slightly differently depending on how the previous line was displayed: | |\ | | | | | / | M \ | M | | M | | |\ \ | |\ \ | |\ \ Signed-off-by: Adam Simpkins Signed-off-by: Junio C Hamano --- graph.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 93 insertions(+), 17 deletions(-) diff --git a/graph.c b/graph.c index 26b8c5209e..332d1e8a13 100644 --- a/graph.c +++ b/graph.c @@ -80,6 +80,27 @@ struct git_graph { * This tells us what kind of line graph_next_line() should output. */ enum graph_state state; + /* + * The output state for the previous line of output. + * This is primarily used to determine how the first merge line + * should appear, based on the last line of the previous commit. + */ + enum graph_state prev_state; + /* + * The index of the column that refers to this commit. + * + * If none of the incoming columns refer to this commit, + * this will be equal to num_columns. + */ + int commit_index; + /* + * The commit_index for the previously displayed commit. + * + * This is used to determine how the first line of a merge + * graph output should appear, based on the last line of the + * previous commit. + */ + int prev_commit_index; /* * The maximum number of columns that can be stored in the columns * and new_columns arrays. This is also half the number of entries @@ -137,6 +158,9 @@ struct git_graph *graph_init(struct rev_info *opt) graph->num_parents = 0; graph->expansion_row = 0; graph->state = GRAPH_PADDING; + graph->prev_state = GRAPH_PADDING; + graph->commit_index = 0; + graph->prev_commit_index = 0; graph->num_columns = 0; graph->num_new_columns = 0; graph->mapping_size = 0; @@ -164,6 +188,12 @@ void graph_release(struct git_graph *graph) free(graph); } +static void graph_update_state(struct git_graph *graph, enum graph_state s) +{ + graph->prev_state = graph->state; + graph->state = s; +} + static void graph_ensure_capacity(struct git_graph *graph, int num_columns) { if (graph->column_capacity >= num_columns) @@ -342,6 +372,7 @@ static void graph_update_columns(struct git_graph *graph) if (col_commit == graph->commit) { int old_mapping_idx = mapping_idx; seen_this = 1; + graph->commit_index = i; for (parent = graph->commit->parents; parent; parent = parent->next) { @@ -394,6 +425,13 @@ void graph_update(struct git_graph *graph, struct commit *commit) graph->num_parents++; } + /* + * Store the old commit_index in prev_commit_index. + * graph_update_columns() will update graph->commit_index for this + * commit. + */ + graph->prev_commit_index = graph->commit_index; + /* * Call graph_update_columns() to update * columns, new_columns, and mapping. @@ -404,6 +442,9 @@ void graph_update(struct git_graph *graph, struct commit *commit) /* * Update graph->state. + * Note that we don't call graph_update_state() here, since + * we don't want to update graph->prev_state. No line for + * graph->state was ever printed. * * If the previous commit didn't get to the GRAPH_PADDING state, * it never finished its output. Goto GRAPH_SKIP, to print out @@ -498,9 +539,9 @@ static void graph_output_skip_line(struct git_graph *graph, struct strbuf *sb) graph_pad_horizontally(graph, sb); if (graph->num_parents >= 3) - graph->state = GRAPH_PRE_COMMIT; + graph_update_state(graph, GRAPH_PRE_COMMIT); else - graph->state = GRAPH_COMMIT; + graph_update_state(graph, GRAPH_COMMIT); } static void graph_output_pre_commit_line(struct git_graph *graph, @@ -535,7 +576,22 @@ static void graph_output_pre_commit_line(struct git_graph *graph, if (col->commit == graph->commit) { seen_this = 1; strbuf_addf(sb, "| %*s", graph->expansion_row, ""); - } else if (seen_this) { + } else if (seen_this && (graph->expansion_row == 0)) { + /* + * This is the first line of the pre-commit output. + * If the previous commit was a merge commit and + * ended in the GRAPH_POST_MERGE state, all branch + * lines after graph->prev_commit_index were + * printed as "\" on the previous line. Continue + * to print them as "\" on this line. Otherwise, + * print the branch lines as "|". + */ + if (graph->prev_state == GRAPH_POST_MERGE && + graph->prev_commit_index < i) + strbuf_addstr(sb, "\\ "); + else + strbuf_addstr(sb, "| "); + } else if (seen_this && (graph->expansion_row > 0)) { strbuf_addstr(sb, "\\ "); } else { strbuf_addstr(sb, "| "); @@ -550,7 +606,7 @@ static void graph_output_pre_commit_line(struct git_graph *graph, */ graph->expansion_row++; if (graph->expansion_row >= num_expansion_rows) - graph->state = GRAPH_COMMIT; + graph_update_state(graph, GRAPH_COMMIT); } static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb) @@ -625,10 +681,8 @@ void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) seen_this = 1; graph_output_commit_char(graph, sb); - if (graph->num_parents < 2) + if (graph->num_parents < 3) strbuf_addch(sb, ' '); - else if (graph->num_parents == 2) - strbuf_addstr(sb, " "); else { int num_dashes = ((graph->num_parents - 2) * 2) - 1; @@ -636,8 +690,27 @@ void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) strbuf_addch(sb, '-'); strbuf_addstr(sb, ". "); } - } else if (seen_this && (graph->num_parents > 1)) { + } else if (seen_this && (graph->num_parents > 2)) { strbuf_addstr(sb, "\\ "); + } else if (seen_this && (graph->num_parents == 2)) { + /* + * This is a 2-way merge commit. + * There is no GRAPH_PRE_COMMIT stage for 2-way + * merges, so this is the first line of output + * for this commit. Check to see what the previous + * line of output was. + * + * If it was GRAPH_POST_MERGE, the branch line + * coming into this commit may have been '\', + * and not '|' or '/'. If so, output the branch + * line as '\' on this line, instead of '|'. This + * makes the output look nicer. + */ + if (graph->prev_state == GRAPH_POST_MERGE && + graph->prev_commit_index < i) + strbuf_addstr(sb, "\\ "); + else + strbuf_addstr(sb, "| "); } else { strbuf_addstr(sb, "| "); } @@ -649,11 +722,11 @@ void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb) * Update graph->state */ if (graph->num_parents > 1) - graph->state = GRAPH_POST_MERGE; + graph_update_state(graph, GRAPH_POST_MERGE); else if (graph_is_mapping_correct(graph)) - graph->state = GRAPH_PADDING; + graph_update_state(graph, GRAPH_PADDING); else - graph->state = GRAPH_COLLAPSING; + graph_update_state(graph, GRAPH_COLLAPSING); } void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb) @@ -679,9 +752,7 @@ void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb) strbuf_addch(sb, '|'); for (j = 0; j < graph->num_parents - 1; j++) strbuf_addstr(sb, "\\ "); - if (graph->num_parents == 2) - strbuf_addch(sb, ' '); - } else if (seen_this && (graph->num_parents > 2)) { + } else if (seen_this) { strbuf_addstr(sb, "\\ "); } else { strbuf_addstr(sb, "| "); @@ -694,9 +765,9 @@ void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb) * Update graph->state */ if (graph_is_mapping_correct(graph)) - graph->state = GRAPH_PADDING; + graph_update_state(graph, GRAPH_PADDING); else - graph->state = GRAPH_COLLAPSING; + graph_update_state(graph, GRAPH_COLLAPSING); } void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb) @@ -801,7 +872,7 @@ void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb) * Otherwise, we need to collapse some branch lines together. */ if (graph_is_mapping_correct(graph)) - graph->state = GRAPH_PADDING; + graph_update_state(graph, GRAPH_PADDING); } int graph_next_line(struct git_graph *graph, struct strbuf *sb) @@ -865,6 +936,11 @@ void graph_padding_line(struct git_graph *graph, struct strbuf *sb) } graph_pad_horizontally(graph, sb); + + /* + * Update graph->prev_state since we have output a padding line + */ + graph->prev_state = GRAPH_PADDING; } int graph_is_commit_finished(struct git_graph const *graph) From f1979d6b3fba41fb6ca92290bf8e10d58ede8970 Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Sun, 1 Jun 2008 13:56:58 -0700 Subject: [PATCH 189/202] graph API: avoid printing unnecessary padding before some octopus merges When an octopus merge is printed, several lines are printed before it to move over existing branch lines to its right. This is needed to make room for the children of the octopus merge. For example: | | | | | | \ \ | | \ \ | | \ \ | M---. \ \ | |\ \ \ \ \ However, this step isn't necessary if there are no branch lines to the right of the octopus merge. Therefore, skip this step when it is not needed, to avoid printing extra lines that don't really serve any purpose. Signed-off-by: Adam Simpkins Signed-off-by: Junio C Hamano --- graph.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/graph.c b/graph.c index 332d1e8a13..edfab2d5b4 100644 --- a/graph.c +++ b/graph.c @@ -450,16 +450,18 @@ void graph_update(struct git_graph *graph, struct commit *commit) * it never finished its output. Goto GRAPH_SKIP, to print out * a line to indicate that portion of the graph is missing. * - * Otherwise, if there are 3 or more parents, we need to print - * extra rows before the commit, to expand the branch lines around - * it and make room for it. + * If there are 3 or more parents, we may need to print extra rows + * before the commit, to expand the branch lines around it and make + * room for it. We need to do this only if there is a branch row + * (or more) to the right of this commit. * * If there are less than 3 parents, we can immediately print the * commit line. */ if (graph->state != GRAPH_PADDING) graph->state = GRAPH_SKIP; - else if (graph->num_parents >= 3) + else if (graph->num_parents >= 3 && + graph->commit_index < (graph->num_columns - 1)) graph->state = GRAPH_PRE_COMMIT; else graph->state = GRAPH_COMMIT; @@ -538,7 +540,8 @@ static void graph_output_skip_line(struct git_graph *graph, struct strbuf *sb) strbuf_addstr(sb, "..."); graph_pad_horizontally(graph, sb); - if (graph->num_parents >= 3) + if (graph->num_parents >= 3 && + graph->commit_index < (graph->num_columns - 1)) graph_update_state(graph, GRAPH_PRE_COMMIT); else graph_update_state(graph, GRAPH_COMMIT); From 58c8dd217384b8d1a464a55a98c665ed108c6b15 Mon Sep 17 00:00:00 2001 From: Lea Wiemann Date: Sun, 1 Jun 2008 22:26:25 +0200 Subject: [PATCH 190/202] Git.pm: fix documentation of hash_object The documentation of hash_object incorrectly states that it accepts a file handle -- in fact it doesn't, and there is even a TODO comment for this. This fixes the documentation. Signed-off-by: Lea Wiemann Signed-off-by: Junio C Hamano --- perl/Git.pm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/perl/Git.pm b/perl/Git.pm index d05b633b64..e2141b6381 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -719,9 +719,8 @@ sub ident_person { =item hash_object ( TYPE, FILENAME ) -Compute the SHA1 object id of the given C (or data waiting in -C) considering it is of the C object type (C, -C, C). +Compute the SHA1 object id of the given C considering it is +of the C object type (C, C, C). The method can be called without any instance or on a specified Git repository, it makes zero difference. From 497c83314c1a595ef26dd4ea452022b0848d3219 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 29 May 2008 19:21:46 +0200 Subject: [PATCH 191/202] Documentation: convert "glossary" and "core-tutorial" to man pages This patch renames the following documents and at the same time converts them to the man format: core-tutorial.txt -> gitcore-tutorial.txt glossary.txt -> gitglossary.txt But as the glossary is included in the user manual and as the new gitglossary man page cannot be included as a whole in the user manual, the actual glossary content is now in its own "glossary-content.txt" new file. And this file is included by both the user manual and the gitglossary man page. Other documents that reference the above ones are changed accordingly and sometimes improved a little too. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- Documentation/Makefile | 6 ++--- Documentation/git.txt | 13 +++++++--- ...core-tutorial.txt => gitcore-tutorial.txt} | 26 ++++++++++++++++--- Documentation/gitcvs-migration.txt | 7 +++-- Documentation/gitglossary.txt | 25 ++++++++++++++++++ Documentation/gittutorial-2.txt | 8 +++--- Documentation/gittutorial.txt | 2 ++ .../{glossary.txt => glossary-content.txt} | 3 --- Documentation/user-manual.txt | 5 +++- 9 files changed, 75 insertions(+), 20 deletions(-) rename Documentation/{core-tutorial.txt => gitcore-tutorial.txt} (99%) create mode 100644 Documentation/gitglossary.txt rename Documentation/{glossary.txt => glossary-content.txt} (99%) diff --git a/Documentation/Makefile b/Documentation/Makefile index 9750334b97..ca4dadf940 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -4,7 +4,7 @@ MAN1_TXT= \ gitk.txt MAN5_TXT=gitattributes.txt gitignore.txt gitmodules.txt githooks.txt MAN7_TXT=git.txt gitcli.txt gittutorial.txt gittutorial-2.txt \ - gitcvs-migration.txt + gitcvs-migration.txt gitcore-tutorial.txt gitglossary.txt MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT) MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT)) @@ -12,13 +12,11 @@ MAN_HTML=$(patsubst %.txt,%.html,$(MAN_TXT)) DOC_HTML=$(MAN_HTML) -ARTICLES = core-tutorial -ARTICLES += diffcore +ARTICLES = diffcore ARTICLES += howto-index ARTICLES += repository-layout ARTICLES += everyday ARTICLES += git-tools -ARTICLES += glossary # with their own formatting rules. SP_ARTICLES = howto/revert-branch-rebase howto/using-merge-subtree user-manual API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technical/api-index.txt, $(wildcard technical/api-*.txt))) diff --git a/Documentation/git.txt b/Documentation/git.txt index 7182bb7f7e..dae1b4031c 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -174,7 +174,7 @@ See the references above to get started using git. The following is probably more detail than necessary for a first-time user. The link:user-manual.html#git-concepts[git concepts chapter of the -user-manual] and the link:core-tutorial.html[Core tutorial] both provide +user-manual] and the linkgit:gitcore-tutorial[7][Core tutorial] both provide introductions to the underlying git architecture. See also the link:howto-index.html[howto] documents for some useful @@ -374,7 +374,7 @@ Higher level SCMs may provide and manage additional information in the Terminology ----------- -Please see the link:glossary.html[glossary] document. +Please see the linkgit:gitglossary[7][glossary] document. Environment Variables @@ -518,7 +518,7 @@ Discussion[[Discussion]] More detail on the following is available from the link:user-manual.html#git-concepts[git concepts chapter of the -user-manual] and the link:core-tutorial.html[Core tutorial]. +user-manual] and the linkgit:gitcore-tutorial[7][Core tutorial]. A git project normally consists of a working directory with a ".git" subdirectory at the top level. The .git directory contains, among other @@ -579,6 +579,13 @@ The documentation for git suite was started by David Greaves , and later enhanced greatly by the contributors on the git-list . +SEE ALSO +-------- +linkgit:gittutorial[7], linkgit:gittutorial-2[7], +linkgit:giteveryday[7], linkgit:gitcvs-migration[7], +linkgit:gitglossary[7], linkgit:gitcore-tutorial[7], +link:user-manual.html[The Git User's Manual] + GIT --- Part of the linkgit:git[7] suite diff --git a/Documentation/core-tutorial.txt b/Documentation/gitcore-tutorial.txt similarity index 99% rename from Documentation/core-tutorial.txt rename to Documentation/gitcore-tutorial.txt index b50b5dd487..5995a2e152 100644 --- a/Documentation/core-tutorial.txt +++ b/Documentation/gitcore-tutorial.txt @@ -1,8 +1,16 @@ -A git core tutorial for developers -================================== +gitcore-tutorial(7) +=================== -Introduction ------------- +NAME +---- +gitcore-tutorial - A git core tutorial for developers + +SYNOPSIS +-------- +git * + +DESCRIPTION +----------- This tutorial explains how to use the "core" git programs to set up and work with a git repository. @@ -1679,3 +1687,13 @@ merge two at a time, documenting how you resolved the conflicts, and the reason why you preferred changes made in one side over the other. Otherwise it would make the project history harder to follow, not easier. + +SEE ALSO +-------- +linkgit:gittutorial[7], linkgit:gittutorial-2[7], +linkgit:giteveryday[7], linkgit:gitcvs-migration[7], +link:user-manual.html[The Git User's Manual] + +GIT +--- +Part of the linkgit:git[7] suite. diff --git a/Documentation/gitcvs-migration.txt b/Documentation/gitcvs-migration.txt index c410805027..de02a4268e 100644 --- a/Documentation/gitcvs-migration.txt +++ b/Documentation/gitcvs-migration.txt @@ -20,7 +20,7 @@ this document explains how to do that. Some basic familiarity with git is required. This linkgit:gittutorial[7][tutorial introduction to git] and the -link:glossary.html[git glossary] should be sufficient. +linkgit:gitglossary[7][git glossary] should be sufficient. Developing against a shared repository -------------------------------------- @@ -184,7 +184,10 @@ repositories without the need for a central maintainer. SEE ALSO -------- -linkgit:gittutorial[7], linkgit:gittutorial-2[7], +linkgit:gittutorial[7], +linkgit:gittutorial-2[7], +linkgit:gitcore-tutorial[7], +linkgit:gitglossary[7], link:everyday.html[Everyday Git], link:user-manual.html[The Git User's Manual] diff --git a/Documentation/gitglossary.txt b/Documentation/gitglossary.txt new file mode 100644 index 0000000000..e8475a042c --- /dev/null +++ b/Documentation/gitglossary.txt @@ -0,0 +1,25 @@ +gitglossary(7) +============== + +NAME +---- +gitglossary - A GIT Glossary + +SYNOPSIS +-------- +* + +DESCRIPTION +----------- + +include::glossary-content.txt[] + +SEE ALSO +-------- +linkgit:gittutorial[7], linkgit:gittutorial-2[7], +linkgit:giteveryday[7], linkgit:gitcvs-migration[7], +link:user-manual.html[The Git User's Manual] + +GIT +--- +Part of the linkgit:git[7] suite. diff --git a/Documentation/gittutorial-2.txt b/Documentation/gittutorial-2.txt index 5bbbf43056..4880ba9ae9 100644 --- a/Documentation/gittutorial-2.txt +++ b/Documentation/gittutorial-2.txt @@ -390,7 +390,7 @@ in the index file is identical to the one in the working directory. In addition to being the staging area for new commits, the index file is also populated from the object database when checking out a branch, and is used to hold the trees involved in a merge operation. -See the link:core-tutorial.html[core tutorial] and the relevant man +See the linkgit:gitcore-tutorial[7][core tutorial] and the relevant man pages for details. What next? @@ -400,7 +400,7 @@ At this point you should know everything necessary to read the man pages for any of the git commands; one good place to start would be with the commands mentioned in link:everyday.html[Everyday git]. You should be able to find any unknown jargon in the -link:glossary.html[Glossary]. +linkgit:gitglossary[7][Glossary]. The link:user-manual.html[Git User's Manual] provides a more comprehensive introduction to git. @@ -412,7 +412,7 @@ CVS-like way. For some interesting examples of git use, see the link:howto-index.html[howtos]. -For git developers, the link:core-tutorial.html[Core tutorial] goes +For git developers, the linkgit:gitcore-tutorial[7][Core tutorial] goes into detail on the lower-level git mechanisms involved in, for example, creating a new commit. @@ -420,6 +420,8 @@ SEE ALSO -------- linkgit:gittutorial[7], linkgit:gitcvs-migration[7], +linkgit:gitcore-tutorial[7], +linkgit:gitglossary[7], link:everyday.html[Everyday git], link:user-manual.html[The Git User's Manual] diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt index 898acdb533..722b323214 100644 --- a/Documentation/gittutorial.txt +++ b/Documentation/gittutorial.txt @@ -598,6 +598,8 @@ SEE ALSO -------- linkgit:gittutorial-2[7], linkgit:gitcvs-migration[7], +linkgit:gitcore-tutorial[7], +linkgit:gitglossary[7], link:everyday.html[Everyday git], link:user-manual.html[The Git User's Manual] diff --git a/Documentation/glossary.txt b/Documentation/glossary-content.txt similarity index 99% rename from Documentation/glossary.txt rename to Documentation/glossary-content.txt index 51b63532b6..f981fee4e5 100644 --- a/Documentation/glossary.txt +++ b/Documentation/glossary-content.txt @@ -1,6 +1,3 @@ -GIT Glossary -============ - [[def_alternate_object_database]]alternate object database:: Via the alternates mechanism, a <> can inherit part of its <> diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index fd8cdb625a..bfde507e0e 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -4252,7 +4252,10 @@ You see, Git is actually the best tool to find out about the source of Git itself! [[glossary]] -include::glossary.txt[] +GIT Glossary +============ + +include::glossary-content.txt[] [[git-quick-start]] Appendix A: Git Quick Reference From 850d3a7c05b4678af21b126ad30bc60510745ef1 Mon Sep 17 00:00:00 2001 From: Lea Wiemann Date: Sun, 1 Jun 2008 20:15:33 +0200 Subject: [PATCH 192/202] glossary: improve a few links They now point to more specific/appropriate targets. Signed-off-by: Lea Wiemann Signed-off-by: Junio C Hamano --- Documentation/glossary-content.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt index f981fee4e5..9b4a4f45e9 100644 --- a/Documentation/glossary-content.txt +++ b/Documentation/glossary-content.txt @@ -87,11 +87,10 @@ to point at the new commit. source code management tools. [[def_DAG]]DAG:: - Directed acyclic graph. The <> objects form a + Directed acyclic graph. The <> form a directed acyclic graph, because they have parents (directed), and the - graph of commit objects is acyclic (there is no - <> which begins and ends with the same - <>). + graph of commit objects is acyclic (there is no <> + which begins and ends with the same <>). [[def_dangling_object]]dangling object:: An <> which is not @@ -247,9 +246,10 @@ This commit is referred to as a "merge commit", or sometimes just a the <> of the object. [[def_object_type]]object type:: - One of the identifiers - "<>","<>","<>" or "<>" - describing the type of an <>. + One of the identifiers "<>", + "<>", "<>" or + "<>" describing the type of an + <>. [[def_octopus]]octopus:: To <> more than two <>. Also denotes an From 32d8050a86f8de0253430b12711354e66a317f29 Mon Sep 17 00:00:00 2001 From: Lea Wiemann Date: Sun, 1 Jun 2008 22:34:47 +0200 Subject: [PATCH 193/202] Git.pm: fix return value of config method If config is called in array context, it is supposed to return all values set for the given option key. This works for all cases except if there is no value set at all. In that case, it wrongly returns (undef) instead of (). This fixes the return statement so that it returns undef in scalar context but an empty array in array context. Signed-off-by: Lea Wiemann Signed-off-by: Junio C Hamano --- perl/Git.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/perl/Git.pm b/perl/Git.pm index e2141b6381..97e61efaff 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -565,7 +565,7 @@ sub config { my $E = shift; if ($E->value() == 1) { # Key not found. - return undef; + return; } else { throw $E; } From 6241360498eb811d4bb130328b1d13241c5e14b6 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 29 May 2008 17:34:50 -0400 Subject: [PATCH 194/202] make verify-pack a bit more useful with bad packs When a pack gets corrupted, its SHA1 checksum will fail. However, this is more useful to let the test go on in order to find the actual problem location than only complain about the SHA1 mismatch and bail out. Also, it is more useful to compare the stored pack SHA1 with the one in the index file instead of the computed SHA1 since the computed SHA1 from a corrupted pack won't match the one stored in the index either. Finally a few code and message cleanups were thrown in as a bonus. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- pack-check.c | 55 ++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/pack-check.c b/pack-check.c index 0f8ad2c00f..f4898732dd 100644 --- a/pack-check.c +++ b/pack-check.c @@ -25,10 +25,10 @@ static int verify_packfile(struct packed_git *p, off_t index_size = p->index_size; const unsigned char *index_base = p->index_data; SHA_CTX ctx; - unsigned char sha1[20]; - off_t offset = 0, pack_sig = p->pack_size - 20; + unsigned char sha1[20], *pack_sig; + off_t offset = 0, pack_sig_ofs = p->pack_size - 20; uint32_t nr_objects, i; - int err; + int err = 0; struct idx_entry *entries; /* Note that the pack header checks are actually performed by @@ -38,21 +38,22 @@ static int verify_packfile(struct packed_git *p, */ SHA1_Init(&ctx); - while (offset < pack_sig) { + while (offset < pack_sig_ofs) { unsigned int remaining; unsigned char *in = use_pack(p, w_curs, offset, &remaining); offset += remaining; - if (offset > pack_sig) - remaining -= (unsigned int)(offset - pack_sig); + if (offset > pack_sig_ofs) + remaining -= (unsigned int)(offset - pack_sig_ofs); SHA1_Update(&ctx, in, remaining); } SHA1_Final(sha1, &ctx); - if (hashcmp(sha1, use_pack(p, w_curs, pack_sig, NULL))) - return error("Packfile %s SHA1 mismatch with itself", - p->pack_name); - if (hashcmp(sha1, index_base + index_size - 40)) - return error("Packfile %s SHA1 mismatch with idx", - p->pack_name); + pack_sig = use_pack(p, w_curs, pack_sig_ofs, NULL); + if (hashcmp(sha1, pack_sig)) + err = error("%s SHA1 checksum mismatch", + p->pack_name); + if (hashcmp(index_base + index_size - 40, pack_sig)) + err = error("%s SHA1 does not match its inddex", + p->pack_name); unuse_pack(w_curs); /* Make sure everything reachable from idx is valid. Since we @@ -72,22 +73,23 @@ static int verify_packfile(struct packed_git *p, } qsort(entries, nr_objects, sizeof(*entries), compare_entries); - for (i = 0, err = 0; i < nr_objects; i++) { + for (i = 0; i < nr_objects; i++) { void *data; enum object_type type; unsigned long size; data = unpack_entry(p, entries[i].offset, &type, &size); if (!data) { - err = error("cannot unpack %s from %s", - sha1_to_hex(entries[i].sha1), p->pack_name); - continue; + err = error("cannot unpack %s from %s at offset %"PRIuMAX"", + sha1_to_hex(entries[i].sha1), p->pack_name, + (uintmax_t)entries[i].offset); + break; } if (check_sha1_signature(entries[i].sha1, data, size, typename(type))) { err = error("packed %s from %s is corrupt", sha1_to_hex(entries[i].sha1), p->pack_name); free(data); - continue; + break; } free(data); } @@ -158,31 +160,28 @@ int verify_pack(struct packed_git *p, int verbose) const unsigned char *index_base; SHA_CTX ctx; unsigned char sha1[20]; - int ret; + int err = 0; + struct pack_window *w_curs = NULL; if (open_pack_index(p)) return error("packfile %s index not opened", p->pack_name); index_size = p->index_size; index_base = p->index_data; - ret = 0; /* Verify SHA1 sum of the index file */ SHA1_Init(&ctx); SHA1_Update(&ctx, index_base, (unsigned int)(index_size - 20)); SHA1_Final(sha1, &ctx); if (hashcmp(sha1, index_base + index_size - 20)) - ret = error("Packfile index for %s SHA1 mismatch", + err = error("Packfile index for %s SHA1 mismatch", p->pack_name); - if (!ret) { - /* Verify pack file */ - struct pack_window *w_curs = NULL; - ret = verify_packfile(p, &w_curs); - unuse_pack(&w_curs); - } + /* Verify pack file */ + err |= verify_packfile(p, &w_curs); + unuse_pack(&w_curs); if (verbose) { - if (ret) + if (err) printf("%s: bad\n", p->pack_name); else { show_pack_info(p); @@ -190,5 +189,5 @@ int verify_pack(struct packed_git *p, int verbose) } } - return ret; + return err; } From 3db4723ead0f141540118f622dedac5106b07a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Hasselstr=C3=B6m?= Date: Tue, 3 Jun 2008 00:41:44 +0200 Subject: [PATCH 195/202] Revert "git.el: Set process-environment instead of invoking env" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit dbe48256b41c1e94d81f2458d7e84b1fdcb47026, which caused mis-encoding of non-ASCII author/committer names when the git-status mode is used to create commits. Signed-off-by: Karl Hasselström Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 2557a7667f..4fa853fae7 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -232,8 +232,10 @@ and returns the process output as a string, or nil if the git failed." (defun git-run-command-region (buffer start end env &rest args) "Run a git command with specified buffer region as input." - (unless (eq 0 (let ((process-environment (append (git-get-env-strings env) - process-environment))) + (unless (eq 0 (if env + (git-run-process-region + buffer start end "env" + (append (git-get-env-strings env) (list "git") args)) (git-run-process-region buffer start end "git" args))) (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))) @@ -248,8 +250,9 @@ and returns the process output as a string, or nil if the git failed." (erase-buffer) (cd dir) (setq status - (let ((process-environment (append (git-get-env-strings env) - process-environment))) + (if env + (apply #'call-process "env" nil (list buffer t) nil + (append (git-get-env-strings env) (list hook-name) args)) (apply #'call-process hook-name nil (list buffer t) nil args)))) (display-message-or-buffer buffer) (eq 0 status))))) From f70dda250e33dd17f6fdff17d15287391d8b0952 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Mon, 2 Jun 2008 11:54:41 +0200 Subject: [PATCH 196/202] gitweb: Fix "next" link on bottom of page Fix search form generation to not modify $cgi->param(...)'s. In git_header_html() we used to use $cgi->hidden(-name => "a") etc. to generate hidden fields; unfortunately to use this form it is required to modify $cgi->param("a") etc., which makes href(-replay,...) use wrong replay values. This for example made the "next" link on the bottom of the page has a=search instead of a=$action, and thus fails to get you to the next page. Because in CGI the value of a hidden field is "sticky", there is no way to modify it short of modifying $cgi->param(...). Therefore it got replaced by generating element [semi] directly. Alternate solution would be for href(-replay,...) to use values saved in global variables, such as $action etc., instead of (re)reading them from $cgi->param($symbol). The bad link was reported by Kai Blin through http://bugs.debian.org/481902 Reported-by: Kai Blin Signed-off-by: Jakub Narebski Tested-by: Gerrit Pape Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 57a19058a4..55fb100534 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2623,7 +2623,7 @@ EOF print "\n"; my ($have_search) = gitweb_check_feature('search'); - if ((defined $project) && ($have_search)) { + if (defined $project && $have_search) { if (!defined $searchtext) { $searchtext = ""; } @@ -2639,16 +2639,13 @@ EOF my ($use_pathinfo) = gitweb_check_feature('pathinfo'); if ($use_pathinfo) { $action .= "/".esc_url($project); - } else { - $cgi->param("p", $project); } - $cgi->param("a", "search"); - $cgi->param("h", $search_hash); print $cgi->startform(-method => "get", -action => $action) . "
\n" . - (!$use_pathinfo && $cgi->hidden(-name => "p") . "\n") . - $cgi->hidden(-name => "a") . "\n" . - $cgi->hidden(-name => "h") . "\n" . + (!$use_pathinfo && + $cgi->input({-name=>"p", -value=>$project, -type=>"hidden"}) . "\n") . + $cgi->input({-name=>"a", -value=>"search", -type=>"hidden"}) . "\n" . + $cgi->input({-name=>"h", -value=>$search_hash, -type=>"hidden"}) . "\n" . $cgi->popup_menu(-name => 'st', -default => 'commit', -values => ['commit', 'grep', 'author', 'committer', 'pickaxe']) . $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) . From c5833f6e1373e4e6a2c31a0cb6cd9050dc395643 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Fri, 30 May 2008 14:43:40 -0700 Subject: [PATCH 197/202] Documentation/git-filter-branch.txt: Fix description of --commit-filter The old description was misleading and logically impossible. It claimed that the ancestors of the original commit would be re-written to have the multiple emitted ids as parents. Not only would this modify existing objects, but it would create a cycle. What this actually does is pass the multiple emitted ids to the newly-created children to use as parents. Signed-off-by: Kevin Ballard Signed-off-by: Junio C Hamano --- Documentation/git-filter-branch.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index 506c37af70..35cb1677f7 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -113,7 +113,7 @@ OPTIONS stdin. The commit id is expected on stdout. + As a special extension, the commit filter may emit multiple -commit ids; in that case, ancestors of the original commit will +commit ids; in that case, the rewritten children of the original commit will have all of them as parents. + You can use the 'map' convenience function in this filter, and other From 69e66f5500ef6079e3374a6654ef1f6df0c6f360 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 2 Jun 2008 16:01:40 +0200 Subject: [PATCH 198/202] rebase --interactive: Compute upstream SHA1 before switching branches If the upstream argument to rebase (the first argument) was relative to HEAD and the name of the branch to rebase (the second argument) was given, the upstream would have been interpreted relative to the second argument. In particular, this command git rebase -i HEAD topic would always finish with "Nothing to do". (a1bf91e fixed the same issue for non-interactive rebase.) Signed-off-by: Johannes Sixt Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 8ee08ff2fd..0ca986f721 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -475,6 +475,9 @@ do require_clean_work_tree + UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" + test -z "$ONTO" && ONTO=$UPSTREAM + if test ! -z "$2" then output git show-ref --verify --quiet "refs/heads/$2" || @@ -484,12 +487,8 @@ do fi HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?" - UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" - mkdir "$DOTEST" || die "Could not create temporary $DOTEST" - test -z "$ONTO" && ONTO=$UPSTREAM - : > "$DOTEST"/interactive || die "Could not mark as interactive" git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null || echo "detached HEAD" > "$DOTEST"/head-name From 67bfc030d7b27c007853776e8028598bdfa2cae0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 2 Jun 2008 22:17:42 -0700 Subject: [PATCH 199/202] commit: drop duplicated parents The scripted version of git-commit internally used git-commit-tree which omitted duplicated parents given from the command line. This prevented a nonsensical octopus merge from getting created even when you said "git merge A B" while you are already on branch A. However, when git-commit was rewritten in C, this sanity check was lost. This resurrects it. Signed-off-by: Junio C Hamano --- builtin-commit.c | 9 +++++++++ t/t7502-commit.sh | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/builtin-commit.c b/builtin-commit.c index b294c1f88c..90200ed643 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -883,10 +883,19 @@ static void add_parent(struct strbuf *sb, const unsigned char *sha1) { struct object *obj = parse_object(sha1); const char *parent = sha1_to_hex(sha1); + const char *cp; + if (!obj) die("Unable to find commit parent %s", parent); if (obj->type != OBJ_COMMIT) die("Parent %s isn't a proper commit", parent); + + for (cp = sb->buf; cp && (cp = strstr(cp, "\nparent ")); cp += 8) { + if (!memcmp(cp + 8, parent, 40) && cp[48] == '\n') { + error("duplicate parent %s ignored", parent); + return; + } + } strbuf_addf(sb, "parent %s\n", parent); } diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh index 46ec1ce8aa..22a13f7aab 100755 --- a/t/t7502-commit.sh +++ b/t/t7502-commit.sh @@ -226,4 +226,18 @@ test_expect_success 'a SIGTERM should break locks' ' ! test -f .git/index.lock ' +rm -f .git/MERGE_MSG .git/COMMIT_EDITMSG +git reset -q --hard + +test_expect_success 'Hand committing of a redundant merge removes dups' ' + + git rev-parse second master >expect && + test_must_fail git merge second master && + git checkout master g && + EDITOR=: git commit -a && + git cat-file commit HEAD | sed -n -e "s/^parent //p" -e "/^$/q" >actual && + test_cmp expect actual + +' + test_done From 02c17196e323d64b2062eb90b661ff3c65de1a07 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 3 Jun 2008 00:17:22 -0700 Subject: [PATCH 200/202] GIT v1.5.6-rc1 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.6.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/RelNotes-1.5.6.txt b/Documentation/RelNotes-1.5.6.txt index 32af18b572..1855cf3923 100644 --- a/Documentation/RelNotes-1.5.6.txt +++ b/Documentation/RelNotes-1.5.6.txt @@ -62,6 +62,8 @@ Updates since v1.5.5 changes (i.e. cannot be used to propagate the whole changes) meant only for reviewing. +* "git init --bare" is a synonym for "git --bare init" now. + * "git gc --auto" honors a new pre-aut-gc hook to temporarily disable it. * "git log --pretty=tformat:" gives a LF after each entry, @@ -84,6 +86,8 @@ Updates since v1.5.5 * "git svn" learned --add-author-from option to propagate the authorship by munging the commit log message. +* new object creation and looking up in "git svn" has been optimized. + * "gitweb" can read from a system-wide configuration file. (internal) @@ -101,6 +105,6 @@ this release, unless otherwise noted. -- exec >/var/tmp/1 -O=v1.5.6-rc0 +O=v1.5.6-rc1 echo O=`git describe refs/heads/master` git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint From 97809f013ce9dea4781cfbd540c9b8d1b32bd4eb Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 2 Jun 2008 10:14:29 +0200 Subject: [PATCH 201/202] Add #defines for SIGHUP and SIGQUIT. Signed-off-by: Johannes Sixt --- compat/mingw.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compat/mingw.h b/compat/mingw.h index 0e18b78b14..6965e3f2df 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -24,6 +24,8 @@ typedef int pid_t; #define SIGKILL 0 #define SIGCHLD 0 #define SIGPIPE 0 +#define SIGHUP 0 +#define SIGQUIT 0 #define SIGALRM 100 #define F_GETFD 1 From f01c16a4bbb417160dca721aa732b179cd40a98c Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Wed, 4 Jun 2008 19:29:25 +0200 Subject: [PATCH 202/202] Remove the fake 'sync' command. It was added to silence git-repack, but it is no longer used there. Signed-off-by: Johannes Sixt --- git-sh-setup.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/git-sh-setup.sh b/git-sh-setup.sh index 822aa6f83a..9cceb21a82 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -153,9 +153,5 @@ case $(uname -s) in find () { /usr/bin/find "$@" } - # sync is missing - sync () { - : # no implementation - } ;; esac