From 42e18fbf5f94dd6bd303bf702e030a29fa39d6c4 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 16 Oct 2007 21:55:45 -0400 Subject: [PATCH 01/74] more compact progress display Each progress can be on a single line instead of two. [sp: Changed "Checking files out" to "Checking out files" at Johannes Sixt's suggestion as it better explains the action that is taking place] Signed-off-by: Nicolas Pitre Signed-off-by: Shawn O. Pearce --- builtin-pack-objects.c | 16 ++++-------- builtin-unpack-objects.c | 2 +- index-pack.c | 4 +-- progress.c | 53 +++++++++++++++++++--------------------- progress.h | 10 +++----- unpack-trees.c | 4 +-- 6 files changed, 39 insertions(+), 50 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 0be539ed7f..df69abd514 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -606,7 +606,7 @@ static void write_pack_file(void) uint32_t nr_remaining = nr_result; if (do_progress) - start_progress(&progress_state, "Writing %u objects...", "", nr_result); + start_progress(&progress_state, "Writing objects", nr_result); written_list = xmalloc(nr_objects * sizeof(struct object_entry *)); do { @@ -1717,9 +1717,8 @@ static void prepare_pack(int window, int depth) if (nr_deltas) { unsigned nr_done = 0; if (progress) - start_progress(&progress_state, - "Deltifying %u objects...", "", - nr_deltas); + start_progress(&progress_state, "Deltifying objects", + nr_deltas); qsort(delta_list, n, sizeof(*delta_list), type_size_sort); ll_find_deltas(delta_list, n, window+1, depth, &nr_done); if (progress) @@ -2135,23 +2134,18 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) prepare_packed_git(); if (progress) - start_progress(&progress_state, "Generating pack...", - "Counting objects: ", 0); + start_progress(&progress_state, "Counting objects", 0); if (!use_internal_rev_list) read_object_list_from_stdin(); else { rp_av[rp_ac] = NULL; get_object_list(rp_ac, rp_av); } - if (progress) { + if (progress) stop_progress(&progress_state); - fprintf(stderr, "Done counting %u objects.\n", nr_objects); - } if (non_empty && !nr_result) return 0; - if (progress && (nr_objects != nr_result)) - fprintf(stderr, "Result has %u objects.\n", nr_result); if (nr_result) prepare_pack(window, depth); write_pack_file(); diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c index a6ff62fd8c..2317b8f411 100644 --- a/builtin-unpack-objects.c +++ b/builtin-unpack-objects.c @@ -322,7 +322,7 @@ static void unpack_all(void) use(sizeof(struct pack_header)); if (!quiet) - start_progress(&progress, "Unpacking %u objects...", "", nr_objects); + start_progress(&progress, "Unpacking objects", nr_objects); obj_list = xmalloc(nr_objects * sizeof(*obj_list)); for (i = 0; i < nr_objects; i++) { unpack_one(i); diff --git a/index-pack.c b/index-pack.c index db58e05041..c784decd24 100644 --- a/index-pack.c +++ b/index-pack.c @@ -406,7 +406,7 @@ static void parse_pack_objects(unsigned char *sha1) * - remember base (SHA1 or offset) for all deltas. */ if (verbose) - start_progress(&progress, "Indexing %u objects...", "", nr_objects); + start_progress(&progress, "Indexing objects", nr_objects); for (i = 0; i < nr_objects; i++) { struct object_entry *obj = &objects[i]; data = unpack_raw_entry(obj, &delta->base); @@ -455,7 +455,7 @@ static void parse_pack_objects(unsigned char *sha1) * for some more deltas. */ if (verbose) - start_progress(&progress, "Resolving %u deltas...", "", nr_deltas); + start_progress(&progress, "Resolving deltas", nr_deltas); for (i = 0; i < nr_objects; i++) { struct object_entry *obj = &objects[i]; union delta_base base; diff --git a/progress.c b/progress.c index 4344f4eed5..7629e0572b 100644 --- a/progress.c +++ b/progress.c @@ -35,10 +35,11 @@ static void clear_progress_signal(void) progress_update = 0; } -int display_progress(struct progress *progress, unsigned n) +static int display(struct progress *progress, unsigned n, int done) { + char *eol; + if (progress->delay) { - char buf[80]; if (!progress_update || --progress->delay) return 0; if (progress->total) { @@ -51,60 +52,56 @@ int display_progress(struct progress *progress, unsigned n) return 0; } } - if (snprintf(buf, sizeof(buf), - progress->delayed_title, progress->total)) - fprintf(stderr, "%s\n", buf); } + + progress->last_value = n; + eol = done ? ", done. \n" : " \r"; if (progress->total) { unsigned percent = n * 100 / progress->total; if (percent != progress->last_percent || progress_update) { progress->last_percent = percent; - fprintf(stderr, "%s%4u%% (%u/%u) done\r", - progress->prefix, percent, n, progress->total); + fprintf(stderr, "%s: %3u%% (%u/%u)%s", progress->title, + percent, n, progress->total, eol); progress_update = 0; - progress->need_lf = 1; return 1; } } else if (progress_update) { - fprintf(stderr, "%s%u\r", progress->prefix, n); + fprintf(stderr, "%s: %u%s", progress->title, n, eol); progress_update = 0; - progress->need_lf = 1; return 1; } + return 0; } -void start_progress(struct progress *progress, const char *title, - const char *prefix, unsigned total) +int display_progress(struct progress *progress, unsigned n) { - char buf[80]; - progress->prefix = prefix; - progress->total = total; - progress->last_percent = -1; - progress->delay = 0; - progress->need_lf = 0; - if (snprintf(buf, sizeof(buf), title, total)) - fprintf(stderr, "%s\n", buf); - set_progress_signal(); + return display(progress, n, 0); } void start_progress_delay(struct progress *progress, const char *title, - const char *prefix, unsigned total, - unsigned percent_treshold, unsigned delay) + unsigned total, unsigned percent_treshold, unsigned delay) { - progress->prefix = prefix; + progress->title = title; progress->total = total; + progress->last_value = -1; progress->last_percent = -1; progress->delayed_percent_treshold = percent_treshold; - progress->delayed_title = title; progress->delay = delay; - progress->need_lf = 0; set_progress_signal(); } +void start_progress(struct progress *progress, const char *title, unsigned total) +{ + start_progress_delay(progress, title, total, 0, 0); +} + void stop_progress(struct progress *progress) { + if (progress->last_value != -1) { + /* Force the last update */ + progress_update = 1; + display(progress, progress->last_value, 1); + } clear_progress_signal(); - if (progress->need_lf) - fputc('\n', stderr); } diff --git a/progress.h b/progress.h index a7c17ca7c4..07b56bdbb5 100644 --- a/progress.h +++ b/progress.h @@ -2,21 +2,19 @@ #define PROGRESS_H struct progress { - const char *prefix; + const char *title; + int last_value; unsigned total; unsigned last_percent; unsigned delay; unsigned delayed_percent_treshold; - const char *delayed_title; - int need_lf; }; int display_progress(struct progress *progress, unsigned n); void start_progress(struct progress *progress, const char *title, - const char *prefix, unsigned total); + unsigned total); void start_progress_delay(struct progress *progress, const char *title, - const char *prefix, unsigned total, - unsigned percent_treshold, unsigned delay); + unsigned total, unsigned percent_treshold, unsigned delay); void stop_progress(struct progress *progress); #endif diff --git a/unpack-trees.c b/unpack-trees.c index ccfeb6e245..32251018b2 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -307,8 +307,8 @@ static void check_updates(struct cache_entry **src, int nr, total++; } - start_progress_delay(&progress, "Checking %u files out...", - "", total, 50, 2); + start_progress_delay(&progress, "Checking out files", + total, 50, 2); cnt = 0; } From ed1902ef5c6a30942def50809b52e41fbdcdf13f Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 16 Oct 2007 21:55:46 -0400 Subject: [PATCH 02/74] cope with multiple line breaks within sideband progress messages A single sideband packet may sometimes contain multiple lines of progress messages, but we prepend "remote: " only to the whole buffer which creates a messed up display in that case. Make sure that the "remote: " prefix is applied to every remote lines. Signed-off-by: Nicolas Pitre Signed-off-by: Shawn O. Pearce --- sideband.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/sideband.c b/sideband.c index 277fa3c10d..ab8a1e990d 100644 --- a/sideband.c +++ b/sideband.c @@ -17,7 +17,7 @@ int recv_sideband(const char *me, int in_stream, int out, int err) strcpy(buf, "remote:"); while (1) { int band, len; - len = packet_read_line(in_stream, buf+7, LARGE_PACKET_MAX); + len = packet_read_line(in_stream, buf+7, LARGE_PACKET_MAX); if (len == 0) break; if (len < 1) { @@ -35,7 +35,22 @@ int recv_sideband(const char *me, int in_stream, int out, int err) return SIDEBAND_REMOTE_ERROR; case 2: buf[7] = ' '; - safe_write(err, buf, 8+len); + len += 8; + while (1) { + int brk = 8; + while (brk < len) { + brk++; + if (buf[brk-1] == '\n' || + buf[brk-1] == '\r') + break; + } + safe_write(err, buf, brk); + if (brk < len) { + memmove(buf + 8, buf + brk, len - brk); + len = len - brk + 8; + } else + break; + } continue; case 1: safe_write(out, buf+8, len); From 2f8b89472cec19a530cb697c4ee20e0ddfd747f6 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 16 Oct 2007 21:55:47 -0400 Subject: [PATCH 03/74] pack-objects: no delta possible with only one object in the list ... so don't even try in that case, and save another useless line of progress display. Signed-off-by: Nicolas Pitre Signed-off-by: Shawn O. Pearce --- 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 df69abd514..d729cb7237 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1714,7 +1714,7 @@ static void prepare_pack(int window, int depth) delta_list[n++] = entry; } - if (nr_deltas) { + if (nr_deltas && n > 1) { unsigned nr_done = 0; if (progress) start_progress(&progress_state, "Deltifying objects", From 7ba502c47bda21d060844863991083f4c319d436 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 16 Oct 2007 21:55:48 -0400 Subject: [PATCH 04/74] pack-objects.c: fix some global variable abuse and memory leaks To keep things well layered, sha1close() now returns the file descriptor when it doesn't close the file. An ugly cast was added to the return of write_idx_file() to avoid a warning. A proper fix will come separately. Signed-off-by: Nicolas Pitre Signed-off-by: Shawn O. Pearce --- builtin-pack-objects.c | 29 +++++++++++++++-------------- csum-file.c | 23 ++++++++++++++--------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index d729cb7237..933c2526f8 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -65,8 +65,6 @@ static int no_reuse_delta, no_reuse_object, keep_unreachable; static int local; static int incremental; static int allow_ofs_delta; -static const char *pack_tmp_name, *idx_tmp_name; -static char tmpname[PATH_MAX]; static const char *base_name; static int progress = 1; static int window = 10; @@ -587,12 +585,6 @@ static off_t write_one(struct sha1file *f, return offset + size; } -static int open_object_dir_tmp(const char *path) -{ - snprintf(tmpname, sizeof(tmpname), "%s/%s", get_object_directory(), path); - return xmkstemp(tmpname); -} - /* forward declaration for write_pack_file */ static int adjust_perm(const char *path, mode_t mode); @@ -611,11 +603,16 @@ static void write_pack_file(void) do { unsigned char sha1[20]; + char *pack_tmp_name = NULL; if (pack_to_stdout) { f = sha1fd(1, ""); } else { - int fd = open_object_dir_tmp("tmp_pack_XXXXXX"); + char tmpname[PATH_MAX]; + int fd; + snprintf(tmpname, sizeof(tmpname), + "%s/tmp_pack_XXXXXX", get_object_directory()); + fd = xmkstemp(tmpname); pack_tmp_name = xstrdup(tmpname); f = sha1fd(fd, pack_tmp_name); } @@ -643,19 +640,21 @@ static void write_pack_file(void) if (pack_to_stdout || nr_written == nr_remaining) { sha1close(f, sha1, 1); } else { - sha1close(f, sha1, 0); - fixup_pack_header_footer(f->fd, sha1, pack_tmp_name, nr_written); - close(f->fd); + int fd = sha1close(f, NULL, 0); + fixup_pack_header_footer(fd, sha1, pack_tmp_name, nr_written); + close(fd); } if (!pack_to_stdout) { mode_t mode = umask(0); + char *idx_tmp_name, tmpname[PATH_MAX]; umask(mode); mode = 0444 & ~mode; - idx_tmp_name = write_idx_file(NULL, - (struct pack_idx_entry **) written_list, nr_written, sha1); + idx_tmp_name = (char *) write_idx_file(NULL, + (struct pack_idx_entry **) written_list, + nr_written, sha1); snprintf(tmpname, sizeof(tmpname), "%s-%s.pack", base_name, sha1_to_hex(sha1)); if (adjust_perm(pack_tmp_name, mode)) @@ -672,6 +671,8 @@ static void write_pack_file(void) if (rename(idx_tmp_name, tmpname)) die("unable to rename temporary index file: %s", strerror(errno)); + free(idx_tmp_name); + free(pack_tmp_name); puts(sha1_to_hex(sha1)); } diff --git a/csum-file.c b/csum-file.c index 9ab997120d..9929991dea 100644 --- a/csum-file.c +++ b/csum-file.c @@ -31,22 +31,27 @@ static void sha1flush(struct sha1file *f, unsigned int count) int sha1close(struct sha1file *f, unsigned char *result, int final) { + int fd; unsigned offset = f->offset; if (offset) { SHA1_Update(&f->ctx, f->buffer, offset); sha1flush(f, offset); f->offset = 0; } - if (!final) - return 0; /* only want to flush (no checksum write, no close) */ - SHA1_Final(f->buffer, &f->ctx); - if (result) - hashcpy(result, f->buffer); - sha1flush(f, 20); - if (close(f->fd)) - die("%s: sha1 file error on close (%s)", f->name, strerror(errno)); + if (final) { + /* write checksum and close fd */ + SHA1_Final(f->buffer, &f->ctx); + if (result) + hashcpy(result, f->buffer); + sha1flush(f, 20); + if (close(f->fd)) + die("%s: sha1 file error on close (%s)", + f->name, strerror(errno)); + fd = 0; + } else + fd = f->fd; free(f); - return 0; + return fd; } int sha1write(struct sha1file *f, void *buf, unsigned int count) From 4049b9cfc082affc6365539138f6f5c546bb5685 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 16 Oct 2007 21:55:49 -0400 Subject: [PATCH 05/74] fix const issues with some functions Two functions, namely write_idx_file() and open_pack_file(), currently return a const pointer. However that pointer is either a copy of the first argument, or set to a malloc'd buffer when that first argument is null. In the later case it is wrong to qualify that pointer as const since ownership of the buffer is transferred to the caller to dispose of, and obviously the free() function is not meant to be passed const pointers. Making the return pointer not const causes a warning when the first argument is returned since that argument is also marked const. The correct thing to do is therefore to remove the const qualifiers, avoiding the need for ugly casts only to silence some warnings. Signed-off-by: Nicolas Pitre Signed-off-by: Shawn O. Pearce --- builtin-pack-objects.c | 2 +- index-pack.c | 8 ++++---- pack-write.c | 3 ++- pack.h | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 933c2526f8..15d3750616 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -652,7 +652,7 @@ static void write_pack_file(void) umask(mode); mode = 0444 & ~mode; - idx_tmp_name = (char *) write_idx_file(NULL, + idx_tmp_name = write_idx_file(NULL, (struct pack_idx_entry **) written_list, nr_written, sha1); snprintf(tmpname, sizeof(tmpname), "%s-%s.pack", diff --git a/index-pack.c b/index-pack.c index c784decd24..60173d5192 100644 --- a/index-pack.c +++ b/index-pack.c @@ -106,7 +106,7 @@ static void use(int bytes) consumed_bytes += bytes; } -static const char *open_pack_file(const char *pack_name) +static char *open_pack_file(char *pack_name) { if (from_stdin) { input_fd = 0; @@ -686,15 +686,15 @@ static void final(const char *final_pack_name, const char *curr_pack_name, int main(int argc, char **argv) { int i, fix_thin_pack = 0; - const char *curr_pack, *pack_name = NULL; - const char *curr_index, *index_name = NULL; + char *curr_pack, *pack_name = NULL; + char *curr_index, *index_name = NULL; const char *keep_name = NULL, *keep_msg = NULL; char *index_name_buf = NULL, *keep_name_buf = NULL; struct pack_idx_entry **idx_objects; unsigned char sha1[20]; for (i = 1; i < argc; i++) { - const char *arg = argv[i]; + char *arg = argv[i]; if (*arg == '-') { if (!strcmp(arg, "--stdin")) { diff --git a/pack-write.c b/pack-write.c index e59b197e5e..d1ed3abe21 100644 --- a/pack-write.c +++ b/pack-write.c @@ -17,7 +17,8 @@ static int sha1_compare(const void *_a, const void *_b) * the SHA1 hash of sorted object names. The objects array passed in * will be sorted by SHA1 on exit. */ -const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1) +char *write_idx_file(char *index_name, struct pack_idx_entry **objects, + int nr_objects, unsigned char *sha1) { struct sha1file *f; struct pack_idx_entry **sorted_by_sha, **list, **last; diff --git a/pack.h b/pack.h index f357c9f428..cab4aa8381 100644 --- a/pack.h +++ b/pack.h @@ -55,7 +55,7 @@ struct pack_idx_entry { off_t offset; }; -extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1); +extern char *write_idx_file(char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1); extern int verify_pack(struct packed_git *, int); extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t); From c85228ed8f31eb739e19cf8abcff84fad44c1258 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 16 Oct 2007 21:55:50 -0400 Subject: [PATCH 06/74] fix for more minor memory leaks Now that some pointers have lost their const attribute, we can free their associated memory when done with them. This is more a correctness issue about the rule for freeing those pointers which isn't completely trivial more than the leak itself which didn't matter as the program is exiting anyway. Signed-off-by: Nicolas Pitre Signed-off-by: Shawn O. Pearce --- index-pack.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/index-pack.c b/index-pack.c index 60173d5192..2f149a40fd 100644 --- a/index-pack.c +++ b/index-pack.c @@ -815,6 +815,10 @@ int main(int argc, char **argv) free(objects); free(index_name_buf); free(keep_name_buf); + if (pack_name == NULL) + free(curr_pack); + if (index_name == NULL) + free(curr_index); return 0; } From 9c60a966f2c3784cdff31aa6ad9f2086df64d5ba Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 18 Oct 2007 20:42:20 -0400 Subject: [PATCH 07/74] Change 'Deltifying objects' to 'Compressing objects' Recently I was referred to the Grammar Police as the git-pack-objects progress message 'Deltifying %u objects' is considered to be not proper English to at least some small but vocal segment of the English speaking population. Techncially we are applying delta compression to these objects at this stage, so the new term is slightly more acceptable to the Grammar Police but is also just as correct. Signed-off-by: Shawn O. Pearce --- 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 15d3750616..21ba977df3 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1718,7 +1718,7 @@ static void prepare_pack(int window, int depth) if (nr_deltas && n > 1) { unsigned nr_done = 0; if (progress) - start_progress(&progress_state, "Deltifying objects", + start_progress(&progress_state, "Compressing objects", nr_deltas); qsort(delta_list, n, sizeof(*delta_list), type_size_sort); ll_find_deltas(delta_list, n, window+1, depth, &nr_done); From b5d72f0a4cd3cce945ca0d37e4fa0ebbfcdcdb52 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 19 Oct 2007 00:08:37 -0400 Subject: [PATCH 08/74] Teach prune-packed to use the standard progress meter Rather than reimplementing the progress meter logic and always showing 100 lines of output while pruning already packed objects we now use a delayed progress meter and only show it if there are enough objects to make us take a little while. Most users won't see the message anymore as it usually doesn't take very long to delete the already packed loose objects. This neatens the output of a git-gc or git-repack execution, which is especially important for a `git gc --auto` triggered from within another command. We perform the display_progress() call from within the very innermost loop in case we spend more than 1 second within any single object directory. This ensures that a progress_update event from the timer will still trigger in a timely fashion and allow the user to see the progress meter. While I'm in here I changed the message to be more descriptive of its actual task. "Removing unused objects" is a little scary for new users as they wonder where these unused objects came from and how they should avoid them. Truth is these objects aren't unused in the sense of what git-prune would call a dangling object, these are used but are just duplicates of things we have already stored in a packfile. Signed-off-by: Shawn O. Pearce --- builtin-prune-packed.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c index 977730064b..015c8bb7cc 100644 --- a/builtin-prune-packed.c +++ b/builtin-prune-packed.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "progress.h" static const char prune_packed_usage[] = "git-prune-packed [-n] [-q]"; @@ -7,6 +8,8 @@ static const char prune_packed_usage[] = #define DRY_RUN 01 #define VERBOSE 02 +static struct progress progress; + static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts) { struct dirent *de; @@ -23,6 +26,8 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts) if (!has_sha1_pack(sha1, NULL)) continue; memcpy(pathname + len, de->d_name, 38); + if (opts == VERBOSE) + display_progress(&progress, i + 1); if (opts & DRY_RUN) printf("rm -f %s\n", pathname); else if (unlink(pathname) < 0) @@ -39,6 +44,11 @@ void prune_packed_objects(int opts) const char *dir = get_object_directory(); int len = strlen(dir); + if (opts == VERBOSE) + start_progress_delay(&progress, + "Removing duplicate objects", + 256, 95, 2); + if (len > PATH_MAX - 42) die("impossible object directory"); memcpy(pathname, dir, len); @@ -49,16 +59,13 @@ void prune_packed_objects(int opts) sprintf(pathname + len, "%02x/", i); d = opendir(pathname); - if (opts == VERBOSE && (d || i == 255)) - fprintf(stderr, "Removing unused objects %d%%...\015", - ((i+1) * 100) / 256); if (!d) continue; prune_dir(i, d, pathname, len + 3, opts); closedir(d); } if (opts == VERBOSE) - fprintf(stderr, "\nDone.\n"); + stop_progress(&progress); } int cmd_prune_packed(int argc, const char **argv, const char *prefix) From 0e30404370c24047c5fa54b2a6c43e53629c916b Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 19 Oct 2007 01:01:40 -0400 Subject: [PATCH 09/74] Stop displaying "Pack pack-$ID created." during git-gc Discussion on the list tonight came to the conclusion that showing the name of the packfile we just created during git-repack is not a very useful message for any end-user. For the really technical folk who need to have the name of the newest packfile they can use something such as `ls -t .git/objects/pack | head -2` to find the most recently created packfile. Signed-off-by: Shawn O. Pearce --- git-repack.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/git-repack.sh b/git-repack.sh index e72adc4d91..7220635c96 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -83,9 +83,6 @@ for name in $names ; do fullbases="$fullbases pack-$name" chmod a-w "$PACKTMP-$name.pack" chmod a-w "$PACKTMP-$name.idx" - if test "$quiet" != '-q'; then - echo "Pack pack-$name created." - fi mkdir -p "$PACKDIR" || exit for sfx in pack idx From 7791ecbc62b792b3eaa6d722b6dadcea4d0f322d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 23 Oct 2007 13:33:26 -0700 Subject: [PATCH 10/74] revert/cherry-pick: work on merge commits as well Usually you cannot revert a merge because you do not know which side of the merge should be considered the mainline (iow, what change to reverse). With this patch, cherry-pick and revert learn -m (--mainline) option that lets you specify the parent number (starting from 1) of the mainline, so that you can: git revert -m 1 $merge to reverse the changes introduced by the $merge commit relative to its first parent, and: git cherry-pick -m 2 $merge to replay the changes introduced by the $merge commit relative to its second parent. Signed-off-by: Junio C Hamano --- Documentation/git-cherry-pick.txt | 9 ++++++- Documentation/git-revert.txt | 9 ++++++- builtin-revert.c | 42 +++++++++++++++++++++++++------ git-compat-util.h | 13 ++++++++++ 4 files changed, 64 insertions(+), 9 deletions(-) diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt index 76a2edfd9b..937c4a7926 100644 --- a/Documentation/git-cherry-pick.txt +++ b/Documentation/git-cherry-pick.txt @@ -7,7 +7,7 @@ git-cherry-pick - Apply the change introduced by an existing commit SYNOPSIS -------- -'git-cherry-pick' [--edit] [-n] [-x] +'git-cherry-pick' [--edit] [-n] [-m parent-number] [-x] DESCRIPTION ----------- @@ -44,6 +44,13 @@ OPTIONS described above, and `-r` was to disable it. Now the default is not to do `-x` so this option is a no-op. +-m parent-number|--mainline parent-number:: + Usually you cannot revert a merge because you do not know which + side of the merge should be considered the mainline. This + option specifies the parent number (starting from 1) of + the mainline and allows cherry-pick to replay the change + relative to the specified parent. + -n|--no-commit:: Usually the command automatically creates a commit with a commit log message stating which commit was diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt index 69db498447..3457c40787 100644 --- a/Documentation/git-revert.txt +++ b/Documentation/git-revert.txt @@ -7,7 +7,7 @@ git-revert - Revert an existing commit SYNOPSIS -------- -'git-revert' [--edit | --no-edit] [-n] +'git-revert' [--edit | --no-edit] [-n] [-m parent-number] DESCRIPTION ----------- @@ -27,6 +27,13 @@ OPTIONS message prior committing the revert. This is the default if you run the command from a terminal. +-m parent-number|--mainline parent-number:: + Usually you cannot revert a merge because you do not know which + side of the merge should be considered the mainline. This + option specifies the parent number (starting from 1) of + the mainline and allows revert to reverse the change + relative to the specified parent. + --no-edit:: With this option, `git-revert` will not start the commit message editor. diff --git a/builtin-revert.c b/builtin-revert.c index a655c8ee2a..bfed69d7e2 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -19,9 +19,9 @@ * Copyright (c) 2005 Junio C Hamano */ -static const char *revert_usage = "git-revert [--edit | --no-edit] [-n] "; +static const char *revert_usage = "git-revert [--edit | --no-edit] [-n] [-m parent-number] "; -static const char *cherry_pick_usage = "git-cherry-pick [--edit] [-n] [-r] [-x] "; +static const char *cherry_pick_usage = "git-cherry-pick [--edit] [-n] [-m parent-number] [-r] [-x] "; static int edit; static int replay; @@ -29,6 +29,7 @@ static enum { REVERT, CHERRY_PICK } action; static int no_commit; static struct commit *commit; static int needed_deref; +static int mainline; static const char *me; @@ -58,6 +59,12 @@ static void parse_options(int argc, const char **argv) else if (!strcmp(arg, "-x") || !strcmp(arg, "--i-really-want-" "to-expose-my-private-commit-object-name")) replay = 0; + else if (!strcmp(arg, "-m") || !strcmp(arg, "--mainline")) { + if (++i >= argc || + strtol_i(argv[i], 10, &mainline) || + mainline <= 0) + usage(usage_str); + } else if (strcmp(arg, "-r")) usage(usage_str); } @@ -234,7 +241,7 @@ static int merge_recursive(const char *base_sha1, static int revert_or_cherry_pick(int argc, const char **argv) { unsigned char head[20]; - struct commit *base, *next; + struct commit *base, *next, *parent; int i; char *oneline, *reencoded_message = NULL; const char *message, *encoding; @@ -269,8 +276,29 @@ static int revert_or_cherry_pick(int argc, const char **argv) if (!commit->parents) die ("Cannot %s a root commit", me); - if (commit->parents->next) - die ("Cannot %s a multi-parent commit.", me); + if (commit->parents->next) { + /* Reverting or cherry-picking a merge commit */ + int cnt; + struct commit_list *p; + + if (!mainline) + die("Commit %s is a merge but no -m option was given.", + sha1_to_hex(commit->object.sha1)); + + for (cnt = 1, p = commit->parents; + cnt != mainline && p; + cnt++) + p = p->next; + if (cnt != mainline || !p) + die("Commit %s does not have parent %d", + sha1_to_hex(commit->object.sha1), mainline); + parent = p->item; + } else if (0 < mainline) + die("Mainline was specified but commit %s is not a merge.", + sha1_to_hex(commit->object.sha1)); + else + parent = commit->parents->item; + if (!(message = commit->buffer)) die ("Cannot get commit message for %s", sha1_to_hex(commit->object.sha1)); @@ -299,14 +327,14 @@ static int revert_or_cherry_pick(int argc, const char **argv) char *oneline_body = strchr(oneline, ' '); base = commit; - next = commit->parents->item; + next = parent; add_to_msg("Revert \""); add_to_msg(oneline_body + 1); add_to_msg("\"\n\nThis reverts commit "); add_to_msg(sha1_to_hex(commit->object.sha1)); add_to_msg(".\n"); } else { - base = commit->parents->item; + base = parent; next = commit; set_author_ident_env(message); add_message_to_msg(message); diff --git a/git-compat-util.h b/git-compat-util.h index 474f1d1ffb..7b29d1b905 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -381,4 +381,17 @@ static inline int strtoul_ui(char const *s, int base, unsigned int *result) return 0; } +static inline int strtol_i(char const *s, int base, int *result) +{ + long ul; + char *p; + + errno = 0; + ul = strtol(s, &p, base); + if (errno || *p || p == s || (int) ul != ul) + return -1; + *result = ul; + return 0; +} + #endif From 4a59fd131229968b08af9bdf221c341f54c52983 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 15 Oct 2007 01:35:37 +0200 Subject: [PATCH 11/74] Add a simple option parser. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The option parser takes argc, argv, an array of struct option and a usage string. Each of the struct option elements in the array describes a valid option, its type and a pointer to the location where the value is written. The entry point is parse_options(), which scans through the given argv, and matches each option there against the list of valid options. During the scan, argv is rewritten to only contain the non-option command line arguments and the number of these is returned. Aggregation of single switches is allowed: -rC0 is the same as -r -C 0 (supposing that -C wants an arg). Every long option automatically support the option with the same name, prefixed with 'no-' to unset the switch. It assumes that initial value for strings are "NULL" and for integers is "0". Long options are supported either with '=' or without: --some-option=foo is the same as --some-option foo Acked-by: Kristian Høgsberg Signed-off-by: Pierre Habouzit Signed-off-by: Shawn O. Pearce --- Makefile | 4 +- parse-options.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++++ parse-options.h | 35 ++++++++++ 3 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 parse-options.c create mode 100644 parse-options.h diff --git a/Makefile b/Makefile index 72f5ef43ce..7e6e1d65f9 100644 --- a/Makefile +++ b/Makefile @@ -290,7 +290,7 @@ LIB_H = \ run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \ tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \ utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \ - mailmap.h remote.h transport.h + mailmap.h remote.h parse-options.h transport.h DIFF_OBJS = \ diff.o diff-lib.o diffcore-break.o diffcore-order.o \ @@ -313,7 +313,7 @@ LIB_OBJS = \ alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \ color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \ convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \ - transport.o bundle.o walker.o + transport.o bundle.o walker.o parse-options.o BUILTIN_OBJS = \ builtin-add.o \ diff --git a/parse-options.c b/parse-options.c new file mode 100644 index 0000000000..7bdffdbe51 --- /dev/null +++ b/parse-options.c @@ -0,0 +1,167 @@ +#include "git-compat-util.h" +#include "parse-options.h" +#include "strbuf.h" + +#define OPT_SHORT 1 +#define OPT_UNSET 2 + +struct optparse_t { + const char **argv; + int argc; + const char *opt; +}; + +static inline const char *get_arg(struct optparse_t *p) +{ + if (p->opt) { + const char *res = p->opt; + p->opt = NULL; + return res; + } + p->argc--; + return *++p->argv; +} + +static inline const char *skip_prefix(const char *str, const char *prefix) +{ + size_t len = strlen(prefix); + return strncmp(str, prefix, len) ? NULL : str + len; +} + +static int opterror(const struct option *opt, const char *reason, int flags) +{ + if (flags & OPT_SHORT) + return error("switch `%c' %s", opt->short_name, reason); + if (flags & OPT_UNSET) + return error("option `no-%s' %s", opt->long_name, reason); + return error("option `%s' %s", opt->long_name, reason); +} + +static int get_value(struct optparse_t *p, + const struct option *opt, int flags) +{ + const char *s; + + if (p->opt && (flags & OPT_UNSET)) + return opterror(opt, "takes no value", flags); + + switch (opt->type) { + case OPTION_BOOLEAN: + if (!(flags & OPT_SHORT) && p->opt) + return opterror(opt, "takes no value", flags); + if (flags & OPT_UNSET) + *(int *)opt->value = 0; + else + (*(int *)opt->value)++; + return 0; + + case OPTION_STRING: + if (flags & OPT_UNSET) { + *(const char **)opt->value = (const char *)NULL; + return 0; + } + if (!p->opt && p->argc <= 1) + return opterror(opt, "requires a value", flags); + *(const char **)opt->value = get_arg(p); + return 0; + + case OPTION_INTEGER: + if (flags & OPT_UNSET) { + *(int *)opt->value = 0; + return 0; + } + if (!p->opt && p->argc <= 1) + return opterror(opt, "requires a value", flags); + *(int *)opt->value = strtol(get_arg(p), (char **)&s, 10); + if (*s) + return opterror(opt, "expects a numerical value", flags); + return 0; + + default: + die("should not happen, someone must be hit on the forehead"); + } +} + +static int parse_short_opt(struct optparse_t *p, const struct option *options) +{ + for (; options->type != OPTION_END; options++) { + if (options->short_name == *p->opt) { + p->opt = p->opt[1] ? p->opt + 1 : NULL; + return get_value(p, options, OPT_SHORT); + } + } + return error("unknown switch `%c'", *p->opt); +} + +static int parse_long_opt(struct optparse_t *p, const char *arg, + const struct option *options) +{ + for (; options->type != OPTION_END; options++) { + const char *rest; + int flags = 0; + + if (!options->long_name) + continue; + + rest = skip_prefix(arg, options->long_name); + if (!rest) { + if (strncmp(arg, "no-", 3)) + continue; + flags |= OPT_UNSET; + rest = skip_prefix(arg + 3, options->long_name); + if (!rest) + continue; + } + if (*rest) { + if (*rest != '=') + continue; + p->opt = rest + 1; + } + return get_value(p, options, flags); + } + return error("unknown option `%s'", arg); +} + +int parse_options(int argc, const char **argv, const struct option *options, + const char *usagestr, int flags) +{ + struct optparse_t args = { argv + 1, argc - 1, NULL }; + int j = 0; + + for (; args.argc; args.argc--, args.argv++) { + const char *arg = args.argv[0]; + + if (*arg != '-' || !arg[1]) { + argv[j++] = args.argv[0]; + continue; + } + + if (arg[1] != '-') { + args.opt = arg + 1; + do { + if (*args.opt == 'h') + usage(usagestr); + if (parse_short_opt(&args, options) < 0) + usage(usagestr); + } while (args.opt); + continue; + } + + if (!arg[2]) { /* "--" */ + if (!(flags & PARSE_OPT_KEEP_DASHDASH)) { + args.argc--; + args.argv++; + } + break; + } + + if (!strcmp(arg + 2, "help")) + usage(usagestr); + if (parse_long_opt(&args, arg + 2, options)) + usage(usagestr); + } + + memmove(argv + j, args.argv, args.argc * sizeof(*argv)); + argv[j + args.argc] = NULL; + return j + args.argc; +} diff --git a/parse-options.h b/parse-options.h new file mode 100644 index 0000000000..76d73b299f --- /dev/null +++ b/parse-options.h @@ -0,0 +1,35 @@ +#ifndef PARSE_OPTIONS_H +#define PARSE_OPTIONS_H + +enum parse_opt_type { + OPTION_END, + OPTION_BOOLEAN, + OPTION_STRING, + OPTION_INTEGER, +}; + +enum parse_opt_flags { + PARSE_OPT_KEEP_DASHDASH = 1, +}; + +struct option { + enum parse_opt_type type; + int short_name; + const char *long_name; + void *value; +}; + +#define OPT_END() { OPTION_END } +#define OPT_BOOLEAN(s, l, v, h) { OPTION_BOOLEAN, (s), (l), (v) } +#define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v) } +#define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v) } + +/* parse_options() will filter out the processed options and leave the + * non-option argments in argv[]. + * Returns the number of arguments left in argv[]. + */ +extern int parse_options(int argc, const char **argv, + const struct option *options, + const char *usagestr, int flags); + +#endif From d7a38c54a6ccbcbcf29d8cf1110b2702c8b3f7f8 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 15 Oct 2007 01:38:30 +0200 Subject: [PATCH 12/74] parse-options: be able to generate usages automatically Signed-off-by: Pierre Habouzit Signed-off-by: Shawn O. Pearce --- parse-options.c | 73 +++++++++++++++++++++++++++++++++++++++++++++---- parse-options.h | 15 +++++++--- 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/parse-options.c b/parse-options.c index 7bdffdbe51..57a2a11266 100644 --- a/parse-options.c +++ b/parse-options.c @@ -123,7 +123,7 @@ static int parse_long_opt(struct optparse_t *p, const char *arg, } int parse_options(int argc, const char **argv, const struct option *options, - const char *usagestr, int flags) + const char * const usagestr[], int flags) { struct optparse_t args = { argv + 1, argc - 1, NULL }; int j = 0; @@ -140,9 +140,9 @@ int parse_options(int argc, const char **argv, const struct option *options, args.opt = arg + 1; do { if (*args.opt == 'h') - usage(usagestr); + usage_with_options(usagestr, options); if (parse_short_opt(&args, options) < 0) - usage(usagestr); + usage_with_options(usagestr, options); } while (args.opt); continue; } @@ -156,12 +156,75 @@ int parse_options(int argc, const char **argv, const struct option *options, } if (!strcmp(arg + 2, "help")) - usage(usagestr); + usage_with_options(usagestr, options); if (parse_long_opt(&args, arg + 2, options)) - usage(usagestr); + usage_with_options(usagestr, options); } memmove(argv + j, args.argv, args.argc * sizeof(*argv)); argv[j + args.argc] = NULL; return j + args.argc; } + +#define USAGE_OPTS_WIDTH 24 +#define USAGE_GAP 2 + +void usage_with_options(const char * const *usagestr, + const struct option *opts) +{ + struct strbuf sb; + + strbuf_init(&sb, 4096); + strbuf_addstr(&sb, *usagestr); + strbuf_addch(&sb, '\n'); + while (*++usagestr) + strbuf_addf(&sb, " %s\n", *usagestr); + + if (opts->type != OPTION_GROUP) + strbuf_addch(&sb, '\n'); + + for (; opts->type != OPTION_END; opts++) { + size_t pos; + int pad; + + if (opts->type == OPTION_GROUP) { + strbuf_addch(&sb, '\n'); + if (*opts->help) + strbuf_addf(&sb, "%s\n", opts->help); + continue; + } + + pos = sb.len; + strbuf_addstr(&sb, " "); + if (opts->short_name) + strbuf_addf(&sb, "-%c", opts->short_name); + if (opts->long_name && opts->short_name) + strbuf_addstr(&sb, ", "); + if (opts->long_name) + strbuf_addf(&sb, "--%s", opts->long_name); + + switch (opts->type) { + case OPTION_INTEGER: + strbuf_addstr(&sb, " "); + break; + case OPTION_STRING: + if (opts->argh) + strbuf_addf(&sb, " <%s>", opts->argh); + else + strbuf_addstr(&sb, " ..."); + break; + default: + break; + } + + pad = sb.len - pos; + if (pad <= USAGE_OPTS_WIDTH) + pad = USAGE_OPTS_WIDTH - pad; + else { + strbuf_addch(&sb, '\n'); + pad = USAGE_OPTS_WIDTH; + } + strbuf_addf(&sb, "%*s%s\n", pad + USAGE_GAP, "", opts->help); + } + usage(sb.buf); +} diff --git a/parse-options.h b/parse-options.h index 76d73b299f..3006a769cd 100644 --- a/parse-options.h +++ b/parse-options.h @@ -3,6 +3,7 @@ enum parse_opt_type { OPTION_END, + OPTION_GROUP, OPTION_BOOLEAN, OPTION_STRING, OPTION_INTEGER, @@ -17,12 +18,15 @@ struct option { int short_name; const char *long_name; void *value; + const char *argh; + const char *help; }; #define OPT_END() { OPTION_END } -#define OPT_BOOLEAN(s, l, v, h) { OPTION_BOOLEAN, (s), (l), (v) } -#define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v) } -#define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v) } +#define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) } +#define OPT_BOOLEAN(s, l, v, h) { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) } +#define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), NULL, (h) } +#define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v), (a), (h) } /* parse_options() will filter out the processed options and leave the * non-option argments in argv[]. @@ -30,6 +34,9 @@ struct option { */ extern int parse_options(int argc, const char **argv, const struct option *options, - const char *usagestr, int flags); + const char * const usagestr[], int flags); + +extern NORETURN void usage_with_options(const char * const *usagestr, + const struct option *options); #endif From beb474379315654566e78eea8a0e39c66ebcbb8a Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 13 Oct 2007 17:34:45 +0100 Subject: [PATCH 13/74] Add tests for parse-options.c Signed-off-by: Johannes Schindelin Signed-off-by: Shawn O. Pearce --- .gitignore | 1 + Makefile | 2 +- t/t0040-parse-options.sh | 70 ++++++++++++++++++++++++++++++++++++++++ test-parse-options.c | 35 ++++++++++++++++++++ 4 files changed, 107 insertions(+), 1 deletion(-) create mode 100755 t/t0040-parse-options.sh create mode 100644 test-parse-options.c diff --git a/.gitignore b/.gitignore index 62afef2347..249b451b87 100644 --- a/.gitignore +++ b/.gitignore @@ -154,6 +154,7 @@ test-delta test-dump-cache-tree test-genrandom test-match-trees +test-parse-options test-sha1 common-cmds.h *.tar.gz diff --git a/Makefile b/Makefile index 7e6e1d65f9..3c9af55f44 100644 --- a/Makefile +++ b/Makefile @@ -976,7 +976,7 @@ endif ### Testing rules -TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X +TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X test-parse-options$X all:: $(TEST_PROGRAMS) diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh new file mode 100755 index 0000000000..8e4d74b200 --- /dev/null +++ b/t/t0040-parse-options.sh @@ -0,0 +1,70 @@ +#!/bin/sh +# +# Copyright (c) 2007 Johannes Schindelin +# + +test_description='our own option parser' + +. ./test-lib.sh + +cat > expect.err << EOF +usage: test-parse-options + + -b, --boolean get a boolean + -i, --integer get a integer + -j get a integer, too + +string options + -s, --string + get a string + --string2 get another string + +EOF + +test_expect_success 'test help' ' + ! test-parse-options -h > output 2> output.err && + test ! -s output && + git diff expect.err output.err +' + +cat > expect << EOF +boolean: 2 +integer: 1729 +string: 123 +EOF + +test_expect_success 'short options' ' + test-parse-options -s123 -b -i 1729 -b > output 2> output.err && + git diff expect output && + test ! -s output.err +' +cat > expect << EOF +boolean: 2 +integer: 1729 +string: 321 +EOF + +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 +' + +cat > expect << EOF +boolean: 1 +integer: 13 +string: 123 +arg 00: a1 +arg 01: b1 +arg 02: --boolean +EOF + +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_done diff --git a/test-parse-options.c b/test-parse-options.c new file mode 100644 index 0000000000..277cfe4d6d --- /dev/null +++ b/test-parse-options.c @@ -0,0 +1,35 @@ +#include "cache.h" +#include "parse-options.h" + +static int boolean = 0; +static int integer = 0; +static char *string = NULL; + +int main(int argc, const char **argv) +{ + const char *usage[] = { + "test-parse-options ", + NULL + }; + struct option options[] = { + OPT_BOOLEAN('b', "boolean", &boolean, "get a boolean"), + OPT_INTEGER('i', "integer", &integer, "get a integer"), + OPT_INTEGER('j', NULL, &integer, "get a integer, too"), + OPT_GROUP("string options"), + OPT_STRING('s', "string", &string, "string", "get a string"), + OPT_STRING(0, "string2", &string, "str", "get another string"), + OPT_END(), + }; + int i; + + argc = parse_options(argc, argv, options, usage, 0); + + printf("boolean: %d\n", boolean); + printf("integer: %d\n", integer); + printf("string: %s\n", string ? string : "(not set)"); + + for (i = 0; i < argc; i++) + printf("arg %02d: %s\n", i, argv[i]); + + return 0; +} From f389c808b6774fb0a1fc54cf2563a7b3038dd1d4 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Sun, 14 Oct 2007 00:10:51 +0200 Subject: [PATCH 14/74] Rework make_usage to print the usage message immediately Signed-off-by: Alex Riesen Signed-off-by: Shawn O. Pearce --- parse-options.c | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/parse-options.c b/parse-options.c index 57a2a11266..89c5f52be5 100644 --- a/parse-options.c +++ b/parse-options.c @@ -1,6 +1,5 @@ #include "git-compat-util.h" #include "parse-options.h" -#include "strbuf.h" #define OPT_SHORT 1 #define OPT_UNSET 2 @@ -172,59 +171,57 @@ int parse_options(int argc, const char **argv, const struct option *options, void usage_with_options(const char * const *usagestr, const struct option *opts) { - struct strbuf sb; - - strbuf_init(&sb, 4096); - strbuf_addstr(&sb, *usagestr); - strbuf_addch(&sb, '\n'); - while (*++usagestr) - strbuf_addf(&sb, " %s\n", *usagestr); + fprintf(stderr, "usage: %s\n", *usagestr++); + while (*usagestr && **usagestr) + fprintf(stderr, " or: %s\n", *usagestr++); + while (*usagestr) + fprintf(stderr, " %s\n", *usagestr++); if (opts->type != OPTION_GROUP) - strbuf_addch(&sb, '\n'); + fputc('\n', stderr); for (; opts->type != OPTION_END; opts++) { size_t pos; int pad; if (opts->type == OPTION_GROUP) { - strbuf_addch(&sb, '\n'); + fputc('\n', stderr); if (*opts->help) - strbuf_addf(&sb, "%s\n", opts->help); + fprintf(stderr, "%s\n", opts->help); continue; } - pos = sb.len; - strbuf_addstr(&sb, " "); + pos = fprintf(stderr, " "); if (opts->short_name) - strbuf_addf(&sb, "-%c", opts->short_name); + pos += fprintf(stderr, "-%c", opts->short_name); if (opts->long_name && opts->short_name) - strbuf_addstr(&sb, ", "); + pos += fprintf(stderr, ", "); if (opts->long_name) - strbuf_addf(&sb, "--%s", opts->long_name); + pos += fprintf(stderr, "--%s", opts->long_name); switch (opts->type) { case OPTION_INTEGER: - strbuf_addstr(&sb, " "); + pos += fprintf(stderr, " "); break; case OPTION_STRING: if (opts->argh) - strbuf_addf(&sb, " <%s>", opts->argh); + pos += fprintf(stderr, " <%s>", opts->argh); else - strbuf_addstr(&sb, " ..."); + pos += fprintf(stderr, " ..."); break; default: break; } - pad = sb.len - pos; - if (pad <= USAGE_OPTS_WIDTH) - pad = USAGE_OPTS_WIDTH - pad; + if (pos <= USAGE_OPTS_WIDTH) + pad = USAGE_OPTS_WIDTH - pos; else { - strbuf_addch(&sb, '\n'); + fputc('\n', stderr); pad = USAGE_OPTS_WIDTH; } - strbuf_addf(&sb, "%*s%s\n", pad + USAGE_GAP, "", opts->help); + fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); } - usage(sb.buf); + fputc('\n', stderr); + + exit(129); } From ffe659f94d793375fca97dd296422fc10c155016 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 15 Oct 2007 01:45:45 +0200 Subject: [PATCH 15/74] parse-options: make some arguments optional, add callbacks. * add the possibility to use callbacks to parse some options, this can help implementing new options kinds with great flexibility. struct option gains a callback pointer and a `defval' where callbacks user can put either integers or pointers. callbacks also can use the `value' pointer for anything, preferably to the pointer to the final storage for the value though. * add a `flag' member to struct option to make explicit that this option may have an optional argument. The semantics depends on the option type. For INTEGERS, it means that if the switch is not used in its --long-form= form, and that there is no token after it or that the token does not starts with a digit, then it's assumed that the switch has no argument. For STRING or CALLBACK it works the same, except that the condition is that the next atom starts with a dash. This is needed to implement backward compatible behaviour with existing ways to parse the command line. Its use for new options is discouraged. Signed-off-by: Pierre Habouzit Signed-off-by: Shawn O. Pearce --- parse-options.c | 45 +++++++++++++++++++++++++++++++++++++-------- parse-options.h | 16 ++++++++++++++++ 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/parse-options.c b/parse-options.c index 89c5f52be5..c751ebf601 100644 --- a/parse-options.c +++ b/parse-options.c @@ -39,7 +39,8 @@ static int opterror(const struct option *opt, const char *reason, int flags) static int get_value(struct optparse_t *p, const struct option *opt, int flags) { - const char *s; + const char *s, *arg; + arg = p->opt ? p->opt : (p->argc > 1 ? p->argv[1] : NULL); if (p->opt && (flags & OPT_UNSET)) return opterror(opt, "takes no value", flags); @@ -59,17 +60,34 @@ static int get_value(struct optparse_t *p, *(const char **)opt->value = (const char *)NULL; return 0; } - if (!p->opt && p->argc <= 1) + if (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-')) { + *(const char **)opt->value = (const char *)opt->defval; + return 0; + } + if (!arg) return opterror(opt, "requires a value", flags); *(const char **)opt->value = get_arg(p); return 0; + case OPTION_CALLBACK: + if (flags & OPT_UNSET) + return (*opt->callback)(opt, NULL, 1); + if (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-')) + return (*opt->callback)(opt, NULL, 0); + if (!arg) + return opterror(opt, "requires a value", flags); + return (*opt->callback)(opt, get_arg(p), 0); + case OPTION_INTEGER: if (flags & OPT_UNSET) { *(int *)opt->value = 0; return 0; } - if (!p->opt && p->argc <= 1) + if (opt->flags & PARSE_OPT_OPTARG && (!arg || !isdigit(*arg))) { + *(int *)opt->value = opt->defval; + return 0; + } + if (!arg) return opterror(opt, "requires a value", flags); *(int *)opt->value = strtol(get_arg(p), (char **)&s, 10); if (*s) @@ -201,13 +219,24 @@ void usage_with_options(const char * const *usagestr, switch (opts->type) { case OPTION_INTEGER: - pos += fprintf(stderr, " "); + if (opts->flags & PARSE_OPT_OPTARG) + pos += fprintf(stderr, " []"); + else + pos += fprintf(stderr, " "); break; case OPTION_STRING: - if (opts->argh) - pos += fprintf(stderr, " <%s>", opts->argh); - else - pos += fprintf(stderr, " ..."); + case OPTION_CALLBACK: + if (opts->argh) { + if (opts->flags & PARSE_OPT_OPTARG) + pos += fprintf(stderr, " [<%s>]", opts->argh); + else + pos += fprintf(stderr, " <%s>", opts->argh); + } else { + if (opts->flags & PARSE_OPT_OPTARG) + pos += fprintf(stderr, " [...]"); + else + pos += fprintf(stderr, " ..."); + } break; default: break; diff --git a/parse-options.h b/parse-options.h index 3006a769cd..2b8e7624d6 100644 --- a/parse-options.h +++ b/parse-options.h @@ -7,12 +7,20 @@ enum parse_opt_type { OPTION_BOOLEAN, OPTION_STRING, OPTION_INTEGER, + OPTION_CALLBACK, }; enum parse_opt_flags { PARSE_OPT_KEEP_DASHDASH = 1, }; +enum parse_opt_option_flags { + PARSE_OPT_OPTARG = 1, +}; + +struct option; +typedef int parse_opt_cb(const struct option *, const char *arg, int unset); + struct option { enum parse_opt_type type; int short_name; @@ -20,6 +28,12 @@ struct option { void *value; const char *argh; const char *help; + + int flags; + parse_opt_cb *callback; + /* holds default value for PARSE_OPT_OPTARG, + though callbacks can use it like they want */ + intptr_t defval; }; #define OPT_END() { OPTION_END } @@ -27,6 +41,8 @@ struct option { #define OPT_BOOLEAN(s, l, v, h) { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) } #define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), NULL, (h) } #define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v), (a), (h) } +#define OPT_CALLBACK(s, l, v, a, h, f) \ + { OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) } /* parse_options() will filter out the processed options and leave the * non-option argments in argv[]. From 0ce865b134f8ccd60f6e584744144b0978a9fdf2 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sun, 14 Oct 2007 11:05:12 +0200 Subject: [PATCH 16/74] Add shortcuts for very often used options. It helps with consistency of the help strings, for example. Signed-off-by: Pierre Habouzit Signed-off-by: Shawn O. Pearce --- parse-options.c | 21 +++++++++++++++++++++ parse-options.h | 11 +++++++++++ 2 files changed, 32 insertions(+) diff --git a/parse-options.c b/parse-options.c index c751ebf601..12a9f9ea68 100644 --- a/parse-options.c +++ b/parse-options.c @@ -254,3 +254,24 @@ void usage_with_options(const char * const *usagestr, exit(129); } + +/*----- some often used options -----*/ +#include "cache.h" +int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset) +{ + int v; + + if (!arg) { + v = unset ? 0 : DEFAULT_ABBREV; + } else { + v = strtol(arg, (char **)&arg, 10); + if (*arg) + return opterror(opt, "expects a numerical value", 0); + if (v && v < MINIMUM_ABBREV) + v = MINIMUM_ABBREV; + else if (v > 40) + v = 40; + } + *(int *)(opt->value) = v; + return 0; +} diff --git a/parse-options.h b/parse-options.h index 2b8e7624d6..2558e00a7c 100644 --- a/parse-options.h +++ b/parse-options.h @@ -55,4 +55,15 @@ extern int parse_options(int argc, const char **argv, extern NORETURN void usage_with_options(const char * const *usagestr, const struct option *options); +/*----- some often used options -----*/ +extern int parse_opt_abbrev_cb(const struct option *, const char *, int); + +#define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose") +#define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet") +#define OPT__DRY_RUN(var) OPT_BOOLEAN('n', "dry-run", (var), "dry run") +#define OPT__ABBREV(var) \ + { OPTION_CALLBACK, 0, "abbrev", (var), "n", \ + "use digits to display SHA-1s", \ + PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 } + #endif From 7f275b91520d31bfbe43ec5a9bbaf8ac6e663ce0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 14 Oct 2007 17:54:06 +0100 Subject: [PATCH 17/74] parse-options: Allow abbreviated options when unambiguous When there is an option "--amend", the option parser now recognizes "--am" for that option, provided that there is no other option beginning with "--am". Signed-off-by: Johannes Schindelin Signed-off-by: Shawn O. Pearce --- parse-options.c | 37 +++++++++++++++++++++++++++++++++++++ t/t0040-parse-options.sh | 23 +++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/parse-options.c b/parse-options.c index 12a9f9ea68..b4a3b63e9f 100644 --- a/parse-options.c +++ b/parse-options.c @@ -113,6 +113,13 @@ static int parse_short_opt(struct optparse_t *p, const struct option *options) static int parse_long_opt(struct optparse_t *p, const char *arg, const struct option *options) { + const char *arg_end = strchr(arg, '='); + const struct option *abbrev_option = NULL; + int abbrev_flags = 0; + + if (!arg_end) + arg_end = arg + strlen(arg); + for (; options->type != OPTION_END; options++) { const char *rest; int flags = 0; @@ -122,10 +129,38 @@ static int parse_long_opt(struct optparse_t *p, const char *arg, rest = skip_prefix(arg, options->long_name); if (!rest) { + /* abbreviated? */ + if (!strncmp(options->long_name, arg, arg_end - arg)) { +is_abbreviated: + if (abbrev_option) + return error("Ambiguous option: %s " + "(could be --%s%s or --%s%s)", + arg, + (flags & OPT_UNSET) ? + "no-" : "", + options->long_name, + (abbrev_flags & OPT_UNSET) ? + "no-" : "", + abbrev_option->long_name); + if (!(flags & OPT_UNSET) && *arg_end) + p->opt = arg_end + 1; + abbrev_option = options; + abbrev_flags = flags; + continue; + } + /* negated and abbreviated very much? */ + if (!prefixcmp("no-", arg)) { + flags |= OPT_UNSET; + goto is_abbreviated; + } + /* negated? */ if (strncmp(arg, "no-", 3)) continue; flags |= OPT_UNSET; rest = skip_prefix(arg + 3, options->long_name); + /* abbreviated and negated? */ + if (!rest && !prefixcmp(options->long_name, arg + 3)) + goto is_abbreviated; if (!rest) continue; } @@ -136,6 +171,8 @@ static int parse_long_opt(struct optparse_t *p, const char *arg, } return get_value(p, options, flags); } + if (abbrev_option) + return get_value(p, abbrev_option, abbrev_flags); return error("unknown option `%s'", arg); } diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index 8e4d74b200..ae49424aa0 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -67,4 +67,27 @@ test_expect_success 'intermingled arguments' ' git diff expect output ' +cat > expect << EOF +boolean: 0 +integer: 2 +string: (not set) +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_expect_success 'unambiguously abbreviated option with "="' ' + test-parse-options --int=2 > output 2> output.err && + test ! -s output.err && + git diff expect output +' + +test_expect_failure 'ambiguously abbreviated option' ' + test-parse-options --strin 123; + test $? != 129 +' + test_done From f481e22a145ed17e85b68bf8ce2de4b80dfa4323 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Tue, 16 Oct 2007 00:32:38 +0200 Subject: [PATCH 18/74] parse-options: allow callbacks to take no arguments at all. Signed-off-by: Pierre Habouzit Signed-off-by: Shawn O. Pearce --- parse-options.c | 10 +++++++++- parse-options.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/parse-options.c b/parse-options.c index b4a3b63e9f..cc09c98ec3 100644 --- a/parse-options.c +++ b/parse-options.c @@ -72,6 +72,11 @@ static int get_value(struct optparse_t *p, case OPTION_CALLBACK: if (flags & OPT_UNSET) return (*opt->callback)(opt, NULL, 1); + if (opt->flags & PARSE_OPT_NOARG) { + if (p->opt && !(flags & OPT_SHORT)) + return opterror(opt, "takes no value", flags); + return (*opt->callback)(opt, NULL, 0); + } if (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-')) return (*opt->callback)(opt, NULL, 0); if (!arg) @@ -261,8 +266,11 @@ void usage_with_options(const char * const *usagestr, else pos += fprintf(stderr, " "); break; - case OPTION_STRING: case OPTION_CALLBACK: + if (opts->flags & PARSE_OPT_NOARG) + break; + /* FALLTHROUGH */ + case OPTION_STRING: if (opts->argh) { if (opts->flags & PARSE_OPT_OPTARG) pos += fprintf(stderr, " [<%s>]", opts->argh); diff --git a/parse-options.h b/parse-options.h index 2558e00a7c..3a470e5eb8 100644 --- a/parse-options.h +++ b/parse-options.h @@ -16,6 +16,7 @@ enum parse_opt_flags { enum parse_opt_option_flags { PARSE_OPT_OPTARG = 1, + PARSE_OPT_NOARG = 2, }; struct option; From 5c46f75437e6764532f46cbaf1e0bedb52f5acb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Wed, 3 Oct 2007 17:45:02 -0400 Subject: [PATCH 19/74] Port builtin-add.c to use the new option parser. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kristian Høgsberg Signed-off-by: Shawn O. Pearce --- builtin-add.c | 73 +++++++++++++++++++-------------------------------- 1 file changed, 27 insertions(+), 46 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index b8e6094b21..f61681c3ae 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -12,9 +12,13 @@ #include "diffcore.h" #include "commit.h" #include "revision.h" +#include "run-command.h" +#include "parse-options.h" -static const char builtin_add_usage[] = -"git-add [-n] [-v] [-f] [--interactive | -i] [-u] [--refresh] [--] ..."; +static const char * const builtin_add_usage[] = { + "git-add [options] [--] ...", + NULL +}; static int take_worktree_changes; static const char *excludes_file; @@ -156,23 +160,32 @@ static struct lock_file lock_file; 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 add_interactive = 0; + +static struct option builtin_add_options[] = { + OPT__DRY_RUN(&show_only), + OPT__VERBOSE(&verbose), + OPT_GROUP(""), + OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"), + 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_END(), +}; + int cmd_add(int argc, const char **argv, const char *prefix) { - int i, newfd; - int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0; + int i, newfd, orig_argc = argc; const char **pathspec; struct dir_struct dir; - int add_interactive = 0; - for (i = 1; i < argc; i++) { - if (!strcmp("--interactive", argv[i]) || - !strcmp("-i", argv[i])) - add_interactive++; - } + argc = parse_options(argc, argv, builtin_add_options, + builtin_add_usage, 0); if (add_interactive) { const char *args[] = { "add--interactive", NULL }; - if (add_interactive != 1 || argc != 2) + if (add_interactive != 1 || orig_argc != 2) die("add --interactive does not take any parameters"); execv_git_cmd(args); exit(1); @@ -182,49 +195,17 @@ int cmd_add(int argc, const char **argv, const char *prefix) newfd = hold_locked_index(&lock_file, 1); - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (arg[0] != '-') - break; - if (!strcmp(arg, "--")) { - i++; - break; - } - if (!strcmp(arg, "-n")) { - show_only = 1; - continue; - } - if (!strcmp(arg, "-f")) { - ignored_too = 1; - continue; - } - if (!strcmp(arg, "-v")) { - verbose = 1; - continue; - } - if (!strcmp(arg, "-u")) { - take_worktree_changes = 1; - continue; - } - if (!strcmp(arg, "--refresh")) { - refresh_only = 1; - continue; - } - usage(builtin_add_usage); - } - if (take_worktree_changes) { - update(verbose, prefix, argv + i); + update(verbose, prefix, argv); goto finish; } - if (argc <= i) { + if (argc == 0) { fprintf(stderr, "Nothing specified, nothing added.\n"); fprintf(stderr, "Maybe you wanted to say 'git add .'?\n"); return 0; } - pathspec = get_pathspec(prefix, argv + i); + pathspec = get_pathspec(prefix, argv); if (refresh_only) { refresh(verbose, pathspec); From f09985c265f8ea2195a6d6c3e151813e6890dd7d Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Fri, 5 Oct 2007 21:09:19 +0200 Subject: [PATCH 20/74] Make builtin-rm.c use parse_options. Signed-off-by: Pierre Habouzit Signed-off-by: Shawn O. Pearce --- builtin-rm.c | 54 ++++++++++++++++++++++------------------------------ 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/builtin-rm.c b/builtin-rm.c index 3b0677e44b..bca2bd9703 100644 --- a/builtin-rm.c +++ b/builtin-rm.c @@ -8,9 +8,12 @@ #include "dir.h" #include "cache-tree.h" #include "tree-walk.h" +#include "parse-options.h" -static const char builtin_rm_usage[] = -"git-rm [-f] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] ..."; +static const char * const builtin_rm_usage[] = { + "git-rm [options] [--] ...", + NULL +}; static struct { int nr, alloc; @@ -121,11 +124,23 @@ static int check_local_mod(unsigned char *head, int index_only) static struct lock_file lock_file; +static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0; +static int ignore_unmatch = 0; + +static struct option builtin_rm_options[] = { + OPT__DRY_RUN(&show_only), + OPT__QUIET(&quiet), + OPT_BOOLEAN( 0 , "cached", &index_only, "only remove from the index"), + OPT_BOOLEAN('f', NULL, &force, "override the up-to-date check"), + OPT_BOOLEAN('r', NULL, &recursive, "allow recursive removal"), + OPT_BOOLEAN( 0 , "ignore-unmatch", &ignore_unmatch, + "exit with a zero status even if nothing matched"), + OPT_END(), +}; + int cmd_rm(int argc, const char **argv, const char *prefix) { int i, newfd; - int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0; - int ignore_unmatch = 0; const char **pathspec; char *seen; @@ -136,34 +151,11 @@ int cmd_rm(int argc, const char **argv, const char *prefix) if (read_cache() < 0) die("index file corrupt"); - for (i = 1 ; i < argc ; i++) { - const char *arg = argv[i]; + argc = parse_options(argc, argv, builtin_rm_options, builtin_rm_usage, 0); + if (!argc) + usage_with_options(builtin_rm_usage, builtin_rm_options); - if (*arg != '-') - break; - else if (!strcmp(arg, "--")) { - i++; - break; - } - else if (!strcmp(arg, "-n")) - show_only = 1; - else if (!strcmp(arg, "--cached")) - index_only = 1; - else if (!strcmp(arg, "-f")) - force = 1; - else if (!strcmp(arg, "-r")) - recursive = 1; - else if (!strcmp(arg, "--quiet")) - quiet = 1; - else if (!strcmp(arg, "--ignore-unmatch")) - ignore_unmatch = 1; - else - usage(builtin_rm_usage); - } - if (argc <= i) - usage(builtin_rm_usage); - - pathspec = get_pathspec(prefix, argv + i); + pathspec = get_pathspec(prefix, argv); seen = NULL; for (i = 0; pathspec[i] ; i++) /* nothing */; From c7a20c117fedd40652d446e58b3eba7e2420fa47 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sun, 7 Oct 2007 14:19:33 +0200 Subject: [PATCH 21/74] Make builtin-mv.c use parse-options Signed-off-by: Pierre Habouzit Signed-off-by: Shawn O. Pearce --- builtin-mv.c | 84 ++++++++++++++++++++++------------------------------ 1 file changed, 35 insertions(+), 49 deletions(-) diff --git a/builtin-mv.c b/builtin-mv.c index b944651691..a3f9ad1744 100644 --- a/builtin-mv.c +++ b/builtin-mv.c @@ -8,9 +8,12 @@ #include "dir.h" #include "cache-tree.h" #include "path-list.h" +#include "parse-options.h" -static const char builtin_mv_usage[] = -"git-mv [-n] [-f] ( | [-k] ... )"; +static const char * const builtin_mv_usage[] = { + "git-mv [options] ... ", + NULL +}; static const char **copy_pathspec(const char *prefix, const char **pathspec, int count, int base_name) @@ -61,8 +64,14 @@ static struct lock_file lock_file; int cmd_mv(int argc, const char **argv, const char *prefix) { - int i, newfd, count; + int i, newfd; int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; + struct option builtin_mv_options[] = { + OPT__DRY_RUN(&show_only), + OPT_BOOLEAN('f', NULL, &force, "force move/rename even if target exists"), + OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"), + OPT_END(), + }; const char **source, **destination, **dest_path; enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; struct stat st; @@ -78,52 +87,29 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (read_cache() < 0) die("index file corrupt"); - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; + argc = parse_options(argc, argv, builtin_mv_options, builtin_mv_usage, 0); + if (--argc < 1) + usage_with_options(builtin_mv_usage, builtin_mv_options); - if (arg[0] != '-') - break; - if (!strcmp(arg, "--")) { - i++; - break; - } - if (!strcmp(arg, "-n")) { - show_only = 1; - continue; - } - if (!strcmp(arg, "-f")) { - force = 1; - continue; - } - if (!strcmp(arg, "-k")) { - ignore_errors = 1; - continue; - } - usage(builtin_mv_usage); - } - count = argc - i - 1; - if (count < 1) - usage(builtin_mv_usage); - - source = copy_pathspec(prefix, argv + i, count, 0); - modes = xcalloc(count, sizeof(enum update_mode)); - dest_path = copy_pathspec(prefix, argv + argc - 1, 1, 0); + source = copy_pathspec(prefix, argv, argc, 0); + modes = xcalloc(argc, sizeof(enum update_mode)); + dest_path = copy_pathspec(prefix, argv + argc, 1, 0); if (dest_path[0][0] == '\0') /* special case: "." was normalized to "" */ - destination = copy_pathspec(dest_path[0], argv + i, count, 1); + destination = copy_pathspec(dest_path[0], argv, argc, 1); else if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode)) { dest_path[0] = add_slash(dest_path[0]); - destination = copy_pathspec(dest_path[0], argv + i, count, 1); + destination = copy_pathspec(dest_path[0], argv, argc, 1); } else { - if (count != 1) - usage(builtin_mv_usage); + if (argc != 1) + usage_with_options(builtin_mv_usage, builtin_mv_options); destination = dest_path; } /* Checking */ - for (i = 0; i < count; i++) { + for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; int length, src_is_dir; const char *bad = NULL; @@ -167,13 +153,13 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (last - first > 0) { source = xrealloc(source, - (count + last - first) + (argc + last - first) * sizeof(char *)); destination = xrealloc(destination, - (count + last - first) + (argc + last - first) * sizeof(char *)); modes = xrealloc(modes, - (count + last - first) + (argc + last - first) * sizeof(enum update_mode)); } @@ -183,13 +169,13 @@ int cmd_mv(int argc, const char **argv, const char *prefix) for (j = 0; j < last - first; j++) { const char *path = active_cache[first + j]->name; - source[count + j] = path; - destination[count + j] = + source[argc + j] = path; + destination[argc + j] = prefix_path(dst, dst_len, path + length); - modes[count + j] = INDEX; + modes[argc + j] = INDEX; } - count += last - first; + argc += last - first; } } else if (lstat(dst, &st) == 0) { bad = "destination exists"; @@ -216,12 +202,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (bad) { if (ignore_errors) { - if (--count > 0) { + if (--argc > 0) { memmove(source + i, source + i + 1, - (count - i) * sizeof(char *)); + (argc - i) * sizeof(char *)); memmove(destination + i, destination + i + 1, - (count - i) * sizeof(char *)); + (argc - i) * sizeof(char *)); } } else die ("%s, source=%s, destination=%s", @@ -229,7 +215,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) } } - for (i = 0; i < count; i++) { + for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; enum update_mode mode = modes[i]; if (show_only || verbose) @@ -253,7 +239,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) path_list_insert(dst, &added); } - if (show_only) { + if (show_only) { show_list("Changed : ", &changed); show_list("Adding : ", &added); show_list("Deleting : ", &deleted); From a8dfd5eac40143030d47da6fd87906990d7bf28c Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sun, 7 Oct 2007 18:26:21 +0200 Subject: [PATCH 22/74] Make builtin-branch.c use parse_options. Signed-off-by: Pierre Habouzit Signed-off-by: Shawn O. Pearce --- builtin-branch.c | 143 +++++++++++++++++------------------------------ 1 file changed, 50 insertions(+), 93 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index 3da8b55b8f..d6d5cff6b8 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -11,9 +11,15 @@ #include "commit.h" #include "builtin.h" #include "remote.h" +#include "parse-options.h" -static const char builtin_branch_usage[] = - "git-branch [-r] (-d | -D) | [--track | --no-track] [-l] [-f] [] | (-m | -M) [] | [--color | --no-color] [-r | -a] [-v [--abbrev= | --no-abbrev]]"; +static const char * const builtin_branch_usage[] = { + "git-branch [options] [-r | -a]", + "git-branch [options] [-l] [-f] []", + "git-branch [options] [-r] (-d | -D) ", + "git-branch [options] (-m | -M) [] ", + NULL +}; #define REF_UNKNOWN_TYPE 0x00 #define REF_LOCAL_BRANCH 0x01 @@ -505,93 +511,45 @@ int cmd_branch(int argc, const char **argv, const char *prefix) int rename = 0, force_rename = 0; int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0; int reflog = 0, track; - int kinds = REF_LOCAL_BRANCH; - int i; + int kinds = REF_LOCAL_BRANCH, kind_remote = 0, kind_any = 0; + + struct option options[] = { + OPT_GROUP("Generic options"), + OPT__VERBOSE(&verbose), + OPT_BOOLEAN( 0 , "track", &track, "set up tracking mode (see git-pull(1))"), + OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"), + OPT_BOOLEAN('r', NULL, &kind_remote, "act on remote-tracking branches"), + OPT__ABBREV(&abbrev), + + OPT_GROUP("Specific git-branch actions:"), + OPT_BOOLEAN('a', NULL, &kind_any, "list both remote-tracking and local branches"), + OPT_BOOLEAN('d', NULL, &delete, "delete fully merged branch"), + OPT_BOOLEAN('D', NULL, &force_delete, "delete branch (even if not merged)"), + OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"), + OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"), + OPT_BOOLEAN('m', NULL, &rename, "move/rename a branch and its reflog"), + OPT_BOOLEAN('M', NULL, &force_rename, "move/rename a branch, even if target exists"), + OPT_END(), + }; git_config(git_branch_config); track = branch_track; + argc = parse_options(argc, argv, options, builtin_branch_usage, 0); - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (arg[0] != '-') - break; - if (!strcmp(arg, "--")) { - i++; - break; - } - if (!strcmp(arg, "--track")) { - track = 1; - continue; - } - if (!strcmp(arg, "--no-track")) { - track = 0; - continue; - } - if (!strcmp(arg, "-d")) { - delete = 1; - continue; - } - if (!strcmp(arg, "-D")) { - delete = 1; - force_delete = 1; - continue; - } - if (!strcmp(arg, "-f")) { - force_create = 1; - continue; - } - if (!strcmp(arg, "-m")) { - rename = 1; - continue; - } - if (!strcmp(arg, "-M")) { - rename = 1; - force_rename = 1; - continue; - } - if (!strcmp(arg, "-r")) { - kinds = REF_REMOTE_BRANCH; - continue; - } - if (!strcmp(arg, "-a")) { - kinds = REF_REMOTE_BRANCH | REF_LOCAL_BRANCH; - continue; - } - if (!strcmp(arg, "-l")) { - reflog = 1; - continue; - } - if (!prefixcmp(arg, "--no-abbrev")) { - abbrev = 0; - continue; - } - if (!prefixcmp(arg, "--abbrev=")) { - abbrev = strtoul(arg + 9, NULL, 10); - if (abbrev < MINIMUM_ABBREV) - abbrev = MINIMUM_ABBREV; - else if (abbrev > 40) - abbrev = 40; - continue; - } - if (!strcmp(arg, "-v")) { - verbose = 1; - continue; - } - if (!strcmp(arg, "--color")) { - branch_use_color = 1; - continue; - } - if (!strcmp(arg, "--no-color")) { - branch_use_color = 0; - continue; - } - usage(builtin_branch_usage); - } + delete |= force_delete; + rename |= force_rename; + if (kind_remote) + kinds = REF_REMOTE_BRANCH; + if (kind_any) + kinds = REF_REMOTE_BRANCH | REF_LOCAL_BRANCH; + if (abbrev && abbrev < MINIMUM_ABBREV) + abbrev = MINIMUM_ABBREV; + else if (abbrev > 40) + abbrev = 40; if ((delete && rename) || (delete && force_create) || (rename && force_create)) - usage(builtin_branch_usage); + usage_with_options(builtin_branch_usage, options); head = resolve_ref("HEAD", head_sha1, 0, NULL); if (!head) @@ -599,26 +557,25 @@ int cmd_branch(int argc, const char **argv, const char *prefix) head = xstrdup(head); if (!strcmp(head, "HEAD")) { detached = 1; - } - else { + } else { if (prefixcmp(head, "refs/heads/")) die("HEAD not found below refs/heads!"); head += 11; } if (delete) - return delete_branches(argc - i, argv + i, force_delete, kinds); - else if (i == argc) + return delete_branches(argc, argv, force_delete, kinds); + else if (argc == 0) print_ref_list(kinds, detached, verbose, abbrev); - else if (rename && (i == argc - 1)) - rename_branch(head, argv[i], force_rename); - else if (rename && (i == argc - 2)) - rename_branch(argv[i], argv[i + 1], force_rename); - else if (i == argc - 1 || i == argc - 2) - create_branch(argv[i], (i == argc - 2) ? argv[i+1] : head, + else if (rename && (argc == 1)) + rename_branch(head, argv[0], force_rename); + else if (rename && (argc == 2)) + rename_branch(argv[0], argv[1], force_rename); + else if (argc <= 2) + create_branch(argv[0], (argc == 2) ? argv[1] : head, force_create, reflog, track); else - usage(builtin_branch_usage); + usage_with_options(builtin_branch_usage, options); return 0; } From 166185be7c730a25308b251508aa8f1a39ea476b Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sun, 7 Oct 2007 20:54:08 +0200 Subject: [PATCH 23/74] Make builtin-describe.c use parse_options Signed-off-by: Pierre Habouzit Signed-off-by: Shawn O. Pearce --- builtin-describe.c | 70 +++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 41 deletions(-) diff --git a/builtin-describe.c b/builtin-describe.c index 669110cb06..6eeb9b5045 100644 --- a/builtin-describe.c +++ b/builtin-describe.c @@ -4,12 +4,15 @@ #include "refs.h" #include "builtin.h" #include "exec_cmd.h" +#include "parse-options.h" #define SEEN (1u<<0) #define MAX_TAGS (FLAG_BITS - 1) -static const char describe_usage[] = -"git-describe [--all] [--tags] [--abbrev=] *"; +static const char * const describe_usage[] = { + "git-describe [options] *", + NULL +}; static int debug; /* Display lots of verbose info */ static int all; /* Default to annotated tags only */ @@ -242,57 +245,42 @@ static void describe(const char *arg, int last_one) int cmd_describe(int argc, const char **argv, const char *prefix) { - int i; int contains = 0; + struct option options[] = { + OPT_BOOLEAN(0, "contains", &contains, "find the tag that comes after the commit"), + OPT_BOOLEAN(0, "debug", &debug, "debug search strategy on stderr"), + OPT_BOOLEAN(0, "all", &all, "use any ref in .git/refs"), + OPT_BOOLEAN(0, "tags", &tags, "use any tag in .git/refs/tags"), + OPT__ABBREV(&abbrev), + OPT_INTEGER(0, "candidates", &max_candidates, + "consider most recent tags (default: 10)"), + OPT_END(), + }; - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (*arg != '-') - break; - else if (!strcmp(arg, "--contains")) - contains = 1; - else if (!strcmp(arg, "--debug")) - debug = 1; - else if (!strcmp(arg, "--all")) - all = 1; - else if (!strcmp(arg, "--tags")) - tags = 1; - else if (!prefixcmp(arg, "--abbrev=")) { - abbrev = strtoul(arg + 9, NULL, 10); - if (abbrev != 0 && (abbrev < MINIMUM_ABBREV || 40 < abbrev)) - abbrev = DEFAULT_ABBREV; - } - else if (!prefixcmp(arg, "--candidates=")) { - max_candidates = strtoul(arg + 13, NULL, 10); - if (max_candidates < 1) - max_candidates = 1; - else if (max_candidates > MAX_TAGS) - max_candidates = MAX_TAGS; - } - else - usage(describe_usage); - } + argc = parse_options(argc, argv, options, describe_usage, 0); + if (max_candidates < 1) + max_candidates = 1; + else if (max_candidates > MAX_TAGS) + max_candidates = MAX_TAGS; save_commit_buffer = 0; if (contains) { - const char **args = xmalloc((4 + argc - i) * sizeof(char*)); + const char **args = xmalloc((4 + argc) * sizeof(char*)); args[0] = "name-rev"; args[1] = "--name-only"; args[2] = "--tags"; - memcpy(args + 3, argv + i, (argc - i) * sizeof(char*)); - args[3 + argc - i] = NULL; - return cmd_name_rev(3 + argc - i, args, prefix); + memcpy(args + 3, argv, argc * sizeof(char*)); + args[3 + argc] = NULL; + return cmd_name_rev(3 + argc, args, prefix); } - if (argc <= i) + if (argc == 0) { describe("HEAD", 1); - else - while (i < argc) { - describe(argv[i], (i == argc - 1)); - i++; + } else { + while (argc-- > 0) { + describe(*argv++, argc == 0); } - + } return 0; } From f81037947c518703cefee2ada5a5d7f724191bc6 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sun, 7 Oct 2007 23:02:29 +0200 Subject: [PATCH 24/74] Make builtin-revert.c use parse_options. Signed-off-by: Pierre Habouzit Signed-off-by: Shawn O. Pearce --- builtin-revert.c | 65 +++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/builtin-revert.c b/builtin-revert.c index a655c8ee2a..2ea766b5bf 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -7,6 +7,7 @@ #include "run-command.h" #include "exec_cmd.h" #include "utf8.h" +#include "parse-options.h" /* * This implements the builtins revert and cherry-pick. @@ -19,51 +20,42 @@ * Copyright (c) 2005 Junio C Hamano */ -static const char *revert_usage = "git-revert [--edit | --no-edit] [-n] "; +static const char * const revert_usage[] = { + "git-revert [options] ", + NULL +}; -static const char *cherry_pick_usage = "git-cherry-pick [--edit] [-n] [-r] [-x] "; +static const char * const cherry_pick_usage[] = { + "git-cherry-pick [options] ", + NULL +}; -static int edit; -static int replay; +static int edit, no_replay, no_commit, needed_deref; static enum { REVERT, CHERRY_PICK } action; -static int no_commit; static struct commit *commit; -static int needed_deref; static const char *me; #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" -static void parse_options(int argc, const char **argv) +static void parse_args(int argc, const char **argv) { - const char *usage_str = action == REVERT ? - revert_usage : cherry_pick_usage; + const char * const * usage_str = + action == REVERT ? revert_usage : cherry_pick_usage; unsigned char sha1[20]; const char *arg; - int i; + int noop; + struct option options[] = { + OPT_BOOLEAN('n', "no-commit", &no_commit, "don't automatically commit"), + OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"), + OPT_BOOLEAN('x', NULL, &no_replay, "append commit name when cherry-picking"), + OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"), + OPT_END(), + }; - if (argc < 2) - usage(usage_str); - - for (i = 1; i < argc; i++) { - arg = argv[i]; - if (arg[0] != '-') - break; - if (!strcmp(arg, "-n") || !strcmp(arg, "--no-commit")) - no_commit = 1; - else if (!strcmp(arg, "-e") || !strcmp(arg, "--edit")) - edit = 1; - else if (!strcmp(arg, "--no-edit")) - edit = 0; - else if (!strcmp(arg, "-x") || !strcmp(arg, "--i-really-want-" - "to-expose-my-private-commit-object-name")) - replay = 0; - else if (strcmp(arg, "-r")) - usage(usage_str); - } - if (i != argc - 1) - usage(usage_str); - arg = argv[argc - 1]; + if (parse_options(argc, argv, options, usage_str, 0) != 1) + usage_with_options(usage_str, options); + arg = argv[0]; if (get_sha1(arg, sha1)) die ("Cannot find '%s'", arg); commit = (struct commit *)parse_object(sha1); @@ -243,10 +235,10 @@ static int revert_or_cherry_pick(int argc, const char **argv) git_config(git_default_config); me = action == REVERT ? "revert" : "cherry-pick"; setenv(GIT_REFLOG_ACTION, me, 0); - parse_options(argc, argv); + parse_args(argc, argv); /* this is copied from the shell script, but it's never triggered... */ - if (action == REVERT && replay) + if (action == REVERT && !no_replay) die("revert is incompatible with replay"); if (no_commit) { @@ -310,7 +302,7 @@ static int revert_or_cherry_pick(int argc, const char **argv) next = commit; set_author_ident_env(message); add_message_to_msg(message); - if (!replay) { + if (no_replay) { add_to_msg("(cherry picked from commit "); add_to_msg(sha1_to_hex(commit->object.sha1)); add_to_msg(")\n"); @@ -388,13 +380,14 @@ int cmd_revert(int argc, const char **argv, const char *prefix) { if (isatty(0)) edit = 1; + no_replay = 1; action = REVERT; return revert_or_cherry_pick(argc, argv); } int cmd_cherry_pick(int argc, const char **argv, const char *prefix) { - replay = 1; + no_replay = 0; action = CHERRY_PICK; return revert_or_cherry_pick(argc, argv); } From 89942be6a13f69a34e5e824977b45204c36cf9e8 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sun, 7 Oct 2007 23:14:43 +0200 Subject: [PATCH 25/74] Make builtin-update-ref.c use parse_options Signed-off-by: Pierre Habouzit Signed-off-by: Shawn O. Pearce --- builtin-update-ref.c | 65 +++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 40 deletions(-) diff --git a/builtin-update-ref.c b/builtin-update-ref.c index fe1f74c9f3..e90737c350 100644 --- a/builtin-update-ref.c +++ b/builtin-update-ref.c @@ -1,59 +1,44 @@ #include "cache.h" #include "refs.h" #include "builtin.h" +#include "parse-options.h" -static const char git_update_ref_usage[] = -"git-update-ref [-m ] (-d | [--no-deref] [])"; +static const char * const git_update_ref_usage[] = { + "git-update-ref [options] -d ", + "git-update-ref [options] []", + NULL +}; int cmd_update_ref(int argc, const char **argv, const char *prefix) { - const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL; + const char *refname, *value, *oldval, *msg=NULL; unsigned char sha1[20], oldsha1[20]; - int i, delete, ref_flags; + int delete = 0, no_deref = 0; + struct option options[] = { + OPT_STRING( 'm', NULL, &msg, "reason", "reason of the update"), + OPT_BOOLEAN('d', NULL, &delete, "deletes the reference"), + OPT_BOOLEAN( 0 , "no-deref", &no_deref, + "update not the one it points to"), + OPT_END(), + }; - delete = 0; - ref_flags = 0; git_config(git_default_config); + argc = parse_options(argc, argv, options, git_update_ref_usage, 0); + if (msg && !*msg) + die("Refusing to perform update with empty message."); - for (i = 1; i < argc; i++) { - if (!strcmp("-m", argv[i])) { - if (i+1 >= argc) - usage(git_update_ref_usage); - msg = argv[++i]; - if (!*msg) - die("Refusing to perform update with empty message."); - continue; - } - if (!strcmp("-d", argv[i])) { - delete = 1; - continue; - } - if (!strcmp("--no-deref", argv[i])) { - ref_flags |= REF_NODEREF; - continue; - } - if (!refname) { - refname = argv[i]; - continue; - } - if (!value) { - value = argv[i]; - continue; - } - if (!oldval) { - oldval = argv[i]; - continue; - } - } - if (!refname || !value) - usage(git_update_ref_usage); + if (argc < 2 || argc > 3) + usage_with_options(git_update_ref_usage, options); + refname = argv[0]; + value = argv[1]; + oldval = argv[2]; if (get_sha1(value, sha1)) die("%s: not a valid SHA1", value); if (delete) { if (oldval) - usage(git_update_ref_usage); + usage_with_options(git_update_ref_usage, options); return delete_ref(refname, sha1); } @@ -62,5 +47,5 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) die("%s: not a valid old SHA1", oldval); return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL, - ref_flags, DIE_ON_ERR); + no_deref ? REF_NODEREF : 0, DIE_ON_ERR); } From 785586142a5ed083b97b7be472c08982ae88fd8f Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sun, 7 Oct 2007 23:18:23 +0200 Subject: [PATCH 26/74] Make builtin-symbolic-ref.c use parse_options. Signed-off-by: Pierre Habouzit Signed-off-by: Shawn O. Pearce --- builtin-symbolic-ref.c | 52 +++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 34 deletions(-) diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c index 9eb95e50da..d33982b967 100644 --- a/builtin-symbolic-ref.c +++ b/builtin-symbolic-ref.c @@ -1,9 +1,12 @@ #include "builtin.h" #include "cache.h" #include "refs.h" +#include "parse-options.h" -static const char git_symbolic_ref_usage[] = -"git-symbolic-ref [-q] [-m ] name [ref]"; +static const char * const git_symbolic_ref_usage[] = { + "git-symbolic-ref [options] name [ref]", + NULL +}; static void check_symref(const char *HEAD, int quiet) { @@ -26,44 +29,25 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) { int quiet = 0; const char *msg = NULL; + struct option options[] = { + OPT__QUIET(&quiet), + OPT_STRING('m', NULL, &msg, "reason", "reason of the update"), + OPT_END(), + }; git_config(git_default_config); - - while (1 < argc) { - const char *arg = argv[1]; - if (arg[0] != '-') - break; - else if (!strcmp("-q", arg)) - quiet = 1; - else if (!strcmp("-m", arg)) { - argc--; - argv++; - if (argc <= 1) - break; - msg = argv[1]; - if (!*msg) - die("Refusing to perform update with empty message"); - } - else if (!strcmp("--", arg)) { - argc--; - argv++; - break; - } - else - die("unknown option %s", arg); - argc--; - argv++; - } - + argc = parse_options(argc, argv, options, git_symbolic_ref_usage, 0); + if (msg &&!*msg) + die("Refusing to perform update with empty message"); switch (argc) { - case 2: - check_symref(argv[1], quiet); + case 1: + check_symref(argv[0], quiet); break; - case 3: - create_symref(argv[1], argv[2], msg); + case 2: + create_symref(argv[0], argv[1], msg); break; default: - usage(git_symbolic_ref_usage); + usage_with_options(git_symbolic_ref_usage, options); } return 0; } From c3428da87f625d22b7f0ea1e1fb7028264943da3 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sat, 13 Oct 2007 20:40:46 +0200 Subject: [PATCH 27/74] Make builtin-for-each-ref.c use parse-opts. Signed-off-by: Pierre Habouzit Signed-off-by: Shawn O. Pearce --- builtin-for-each-ref.c | 134 ++++++++++++++++++---------------------- t/t6300-for-each-ref.sh | 0 2 files changed, 59 insertions(+), 75 deletions(-) mode change 100644 => 100755 t/t6300-for-each-ref.sh diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index c74ef2800c..da8c7948e6 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -7,6 +7,7 @@ #include "tree.h" #include "blob.h" #include "quote.h" +#include "parse-options.h" /* Quoting styles */ #define QUOTE_NONE 0 @@ -158,17 +159,18 @@ static const char *find_next(const char *cp) * Make sure the format string is well formed, and parse out * the used atoms. */ -static void verify_format(const char *format) +static int verify_format(const char *format) { const char *cp, *sp; for (cp = format; *cp && (sp = find_next(cp)); ) { const char *ep = strchr(sp, ')'); if (!ep) - die("malformatted format string %s", sp); + return error("malformatted format string %s", sp); /* sp points at "%(" and ep points at the closing ")" */ parse_atom(sp + 2, ep); cp = ep + 1; } + return 0; } /* @@ -800,94 +802,76 @@ static struct ref_sort *default_sort(void) return sort; } -int cmd_for_each_ref(int ac, const char **av, const char *prefix) +int opt_parse_sort(const struct option *opt, const char *arg, int unset) +{ + struct ref_sort **sort_tail = opt->value; + struct ref_sort *s; + int len; + + if (!arg) /* should --no-sort void the list ? */ + return -1; + + *sort_tail = s = xcalloc(1, sizeof(*s)); + sort_tail = &s->next; + + if (*arg == '-') { + s->reverse = 1; + arg++; + } + len = strlen(arg); + s->atom = parse_atom(arg, arg+len); + return 0; +} + +static char const * const for_each_ref_usage[] = { + "git-for-each-ref [options] []", + NULL +}; + +int cmd_for_each_ref(int argc, const char **argv, const char *prefix) { int i, num_refs; - const char *format = NULL; + const char *format = "%(objectname) %(objecttype)\t%(refname)"; struct ref_sort *sort = NULL, **sort_tail = &sort; - int maxcount = 0; - int quote_style = -1; /* unspecified yet */ + int maxcount = 0, quote_style; + int quote_shell = 0, quote_perl = 0, quote_python = 0, quote_tcl = 0; struct refinfo **refs; struct grab_ref_cbdata cbdata; - for (i = 1; i < ac; i++) { - const char *arg = av[i]; - if (arg[0] != '-') - break; - if (!strcmp(arg, "--")) { - i++; - break; - } - if (!prefixcmp(arg, "--format=")) { - if (format) - die("more than one --format?"); - format = arg + 9; - continue; - } - if (!strcmp(arg, "-s") || !strcmp(arg, "--shell") ) { - if (0 <= quote_style) - die("more than one quoting style?"); - quote_style = QUOTE_SHELL; - continue; - } - if (!strcmp(arg, "-p") || !strcmp(arg, "--perl") ) { - if (0 <= quote_style) - die("more than one quoting style?"); - quote_style = QUOTE_PERL; - continue; - } - if (!strcmp(arg, "--python") ) { - if (0 <= quote_style) - die("more than one quoting style?"); - quote_style = QUOTE_PYTHON; - continue; - } - if (!strcmp(arg, "--tcl") ) { - if (0 <= quote_style) - die("more than one quoting style?"); - quote_style = QUOTE_TCL; - continue; - } - if (!prefixcmp(arg, "--count=")) { - if (maxcount) - die("more than one --count?"); - maxcount = atoi(arg + 8); - if (maxcount <= 0) - die("The number %s did not parse", arg); - continue; - } - if (!prefixcmp(arg, "--sort=")) { - struct ref_sort *s = xcalloc(1, sizeof(*s)); - int len; + struct option opts[] = { + OPT_BOOLEAN('s', "shell", "e_shell, "quote placeholders suitably for shells"), + OPT_BOOLEAN('p', "perl", "e_perl, "quote placeholders suitably for perl"), + OPT_BOOLEAN( 0 , "python", "e_python, "quote placeholders suitably for python"), + OPT_BOOLEAN( 0 , "tcl", "e_tcl, "quote placeholders suitably for tcl"), - s->next = NULL; - *sort_tail = s; - sort_tail = &s->next; + OPT_GROUP(""), + OPT_INTEGER( 0 , "count", &maxcount, "show only matched refs"), + OPT_STRING( 0 , "format", &format, "format", "format to use for the output"), + OPT_CALLBACK(0 , "sort", &sort_tail, "key", + "field name to sort on", &opt_parse_sort), + OPT_END(), + }; - arg += 7; - if (*arg == '-') { - s->reverse = 1; - arg++; - } - len = strlen(arg); - sort->atom = parse_atom(arg, arg+len); - continue; - } - break; + parse_options(argc, argv, opts, for_each_ref_usage, 0); + if (maxcount < 0) { + error("invalid --count argument: `%d'", maxcount); + usage_with_options(for_each_ref_usage, opts); } - if (quote_style < 0) - quote_style = QUOTE_NONE; + if (quote_shell + quote_perl + quote_python + quote_tcl > 1) { + error("more than one quoting style ?"); + usage_with_options(for_each_ref_usage, opts); + } + if (verify_format(format)) + usage_with_options(for_each_ref_usage, opts); + quote_style = QUOTE_SHELL * quote_shell + QUOTE_PERL * quote_perl + + QUOTE_PYTHON * quote_python + QUOTE_TCL * quote_tcl; if (!sort) sort = default_sort(); sort_atom_limit = used_atom_cnt; - if (!format) - format = "%(objectname) %(objecttype)\t%(refname)"; - - verify_format(format); memset(&cbdata, 0, sizeof(cbdata)); - cbdata.grab_pattern = av + i; + cbdata.grab_pattern = argv; for_each_ref(grab_single_ref, &cbdata); refs = cbdata.grab_array; num_refs = cbdata.grab_cnt; diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh old mode 100644 new mode 100755 From e0e6c0961140c0329a9cfccc1d227f3f4bcd4777 Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Sun, 14 Oct 2007 16:10:42 +0200 Subject: [PATCH 28/74] Update manpages to reflect new short and long option aliases Signed-off-by: Jonas Fonseca Signed-off-by: Shawn O. Pearce --- Documentation/git-add.txt | 4 ++-- Documentation/git-branch.txt | 2 +- Documentation/git-mv.txt | 2 +- Documentation/git-rm.txt | 4 ++-- Documentation/git-symbolic-ref.txt | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 2fe7355555..963e1ab1e2 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -50,10 +50,10 @@ OPTIONS and `dir/file2`) can be given to add all files in the directory, recursively. --n:: +-n, \--dry-run:: Don't actually add the file(s), just show if they exist. --v:: +-v, \--verbose:: Be verbose. -f:: diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index b7285bcdbc..5e81aa4ee1 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -85,7 +85,7 @@ OPTIONS -a:: List both remote-tracking branches and local branches. --v:: +-v, --verbose:: Show sha1 and commit subject line for each head. --abbrev=:: diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt index 2c9cf743c7..3b8ca76dff 100644 --- a/Documentation/git-mv.txt +++ b/Documentation/git-mv.txt @@ -34,7 +34,7 @@ OPTIONS condition. An error happens when a source is neither existing nor controlled by GIT, or when it would overwrite an existing file unless '-f' is given. --n:: +-n, \--dry-run:: Do nothing; only show what would happen diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt index be61a82164..48c1d97f93 100644 --- a/Documentation/git-rm.txt +++ b/Documentation/git-rm.txt @@ -30,7 +30,7 @@ OPTIONS -f:: Override the up-to-date check. --n:: +-n, \--dry-run:: Don't actually remove the file(s), just show if they exist in the index. @@ -51,7 +51,7 @@ OPTIONS \--ignore-unmatch:: Exit with a zero status even if no files matched. -\--quiet:: +-q, \--quiet:: git-rm normally outputs one line (in the form of an "rm" command) for each file removed. This option suppresses that output. diff --git a/Documentation/git-symbolic-ref.txt b/Documentation/git-symbolic-ref.txt index a88f722860..694cabab24 100644 --- a/Documentation/git-symbolic-ref.txt +++ b/Documentation/git-symbolic-ref.txt @@ -26,7 +26,7 @@ a regular file whose contents is `ref: refs/heads/master`. OPTIONS ------- --q:: +-q, --quiet:: Do not issue an error message if the is not a symbolic ref but a detached HEAD; instead exit with non-zero status silently. From 5ac0a2063e8f824f6e8ffb4d18de74c55aae7131 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 15 Oct 2007 22:34:05 +0200 Subject: [PATCH 29/74] Make builtin-fsck.c use parse_options. Signed-off-by: Pierre Habouzit Signed-off-by: Shawn O. Pearce --- builtin-fsck.c | 80 ++++++++++++++++---------------------------------- 1 file changed, 26 insertions(+), 54 deletions(-) diff --git a/builtin-fsck.c b/builtin-fsck.c index 8d12287f03..64da3bd363 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -8,6 +8,7 @@ #include "pack.h" #include "cache-tree.h" #include "tree-walk.h" +#include "parse-options.h" #define REACHABLE 0x0001 #define SEEN 0x0002 @@ -666,9 +667,24 @@ static int fsck_cache_tree(struct cache_tree *it) return err; } -static const char fsck_usage[] = -"git-fsck [--tags] [--root] [[--unreachable] [--cache] [--full] " -"[--strict] [--verbose] *]"; +static char const * const fsck_usage[] = { + "git-fsck [options] [...]", + NULL +}; + +static struct option fsck_opts[] = { + OPT__VERBOSE(&verbose), + OPT_BOOLEAN(0, "unreachable", &show_unreachable, "show unreachable objects"), + OPT_BOOLEAN(0, "tags", &show_tags, "report tags"), + OPT_BOOLEAN(0, "root", &show_root, "report root nodes"), + OPT_BOOLEAN(0, "cache", &keep_cache_objects, "make index objects head nodes"), + OPT_BOOLEAN(0, "reflogs", &include_reflogs, "make reflogs head nodes (default)"), + OPT_BOOLEAN(0, "full", &check_full, "also consider alternate objects"), + OPT_BOOLEAN(0, "struct", &check_strict, "enable more strict checking"), + OPT_BOOLEAN(0, "lost-found", &write_lost_and_found, + "write dangling objects in .git/lost-found"), + OPT_END(), +}; int cmd_fsck(int argc, const char **argv, const char *prefix) { @@ -677,49 +693,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) track_object_refs = 1; errors_found = 0; - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (!strcmp(arg, "--unreachable")) { - show_unreachable = 1; - continue; - } - if (!strcmp(arg, "--tags")) { - show_tags = 1; - continue; - } - if (!strcmp(arg, "--root")) { - show_root = 1; - continue; - } - if (!strcmp(arg, "--cache")) { - keep_cache_objects = 1; - continue; - } - if (!strcmp(arg, "--no-reflogs")) { - include_reflogs = 0; - continue; - } - if (!strcmp(arg, "--full")) { - check_full = 1; - continue; - } - if (!strcmp(arg, "--strict")) { - check_strict = 1; - continue; - } - if (!strcmp(arg, "--verbose")) { - verbose = 1; - continue; - } - if (!strcmp(arg, "--lost-found")) { - check_full = 1; - include_reflogs = 0; - write_lost_and_found = 1; - continue; - } - if (*arg == '-') - usage(fsck_usage); + argc = parse_options(argc, argv, fsck_opts, fsck_usage, 0); + if (write_lost_and_found) { + check_full = 1; + include_reflogs = 0; } fsck_head_link(); @@ -741,22 +718,18 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) verify_pack(p, 0); for (p = packed_git; p; p = p->next) { - uint32_t i, num; + uint32_t j, num; if (open_pack_index(p)) continue; num = p->num_objects; - for (i = 0; i < num; i++) - fsck_sha1(nth_packed_object_sha1(p, i)); + for (j = 0; j < num; j++) + fsck_sha1(nth_packed_object_sha1(p, j)); } } heads = 0; for (i = 1; i < argc; i++) { const char *arg = argv[i]; - - if (*arg == '-') - continue; - if (!get_sha1(arg, head_sha1)) { struct object *obj = lookup_object(head_sha1); @@ -783,7 +756,6 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) } if (keep_cache_objects) { - int i; read_cache(); for (i = 0; i < active_nr; i++) { unsigned int mode; From 833f3abd821434fd9b12d6cea66f04f232479c22 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 15 Oct 2007 22:38:51 +0200 Subject: [PATCH 30/74] Make builtin-count-objects.c use parse_options. Signed-off-by: Pierre Habouzit Signed-off-by: Shawn O. Pearce --- builtin-count-objects.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/builtin-count-objects.c b/builtin-count-objects.c index 4274ec1950..f00306fb67 100644 --- a/builtin-count-objects.c +++ b/builtin-count-objects.c @@ -6,8 +6,7 @@ #include "cache.h" #include "builtin.h" - -static const char count_objects_usage[] = "git-count-objects [-v]"; +#include "parse-options.h" static void count_objects(DIR *d, char *path, int len, int verbose, unsigned long *loose, @@ -67,29 +66,28 @@ static void count_objects(DIR *d, char *path, int len, int verbose, } } -int cmd_count_objects(int ac, const char **av, const char *prefix) +static char const * const count_objects_usage[] = { + "git-count-objects [-v]", + NULL +}; + +int cmd_count_objects(int argc, const char **argv, const char *prefix) { - int i; - int verbose = 0; + int i, verbose = 0; const char *objdir = get_object_directory(); int len = strlen(objdir); char *path = xmalloc(len + 50); unsigned long loose = 0, packed = 0, packed_loose = 0, garbage = 0; unsigned long loose_size = 0; + struct option opts[] = { + OPT__VERBOSE(&verbose), + OPT_END(), + }; - for (i = 1; i < ac; i++) { - const char *arg = av[i]; - if (*arg != '-') - break; - else if (!strcmp(arg, "-v")) - verbose = 1; - else - usage(count_objects_usage); - } - + argc = parse_options(argc, argv, opts, count_objects_usage, 0); /* we do not take arguments other than flags for now */ - if (i < ac) - usage(count_objects_usage); + if (argc) + usage_with_options(count_objects_usage, opts); memcpy(path, objdir, len); if (len && objdir[len-1] != '/') path[len++] = '/'; From edefb1a23755505cc9b4e750c292cd236b1e8655 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 15 Oct 2007 22:57:59 +0200 Subject: [PATCH 31/74] Make builtin-name-rev.c use parse_options. Signed-off-by: Pierre Habouzit Signed-off-by: Shawn O. Pearce --- builtin-name-rev.c | 64 ++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 39 deletions(-) diff --git a/builtin-name-rev.c b/builtin-name-rev.c index 03083e9477..a0c89a827b 100644 --- a/builtin-name-rev.c +++ b/builtin-name-rev.c @@ -3,12 +3,10 @@ #include "commit.h" #include "tag.h" #include "refs.h" +#include "parse-options.h" #define CUTOFF_DATE_SLOP 86400 /* one day */ -static const char name_rev_usage[] = - "git-name-rev [--tags | --refs=] ( --all | --stdin | committish [committish...] )\n"; - typedef struct rev_name { const char *tip_name; int generation; @@ -153,51 +151,41 @@ static const char* get_rev_name(struct object *o) } } +static char const * const name_rev_usage[] = { + "git-name-rev [options] ( --all | --stdin | ... )", + NULL +}; + int cmd_name_rev(int argc, const char **argv, const char *prefix) { struct object_array revs = { 0, 0, NULL }; - int as_is = 0, all = 0, transform_stdin = 0; + int all = 0, transform_stdin = 0; struct name_ref_data data = { 0, 0, NULL }; + struct option opts[] = { + OPT_BOOLEAN(0, "name-only", &data.name_only, "print only names (no SHA-1)"), + OPT_BOOLEAN(0, "tags", &data.tags_only, "only use tags to name the commits"), + OPT_STRING(0, "refs", &data.ref_filter, "pattern", + "only use refs matching "), + OPT_GROUP(""), + OPT_BOOLEAN(0, "all", &all, "list all commits reachable from all refs"), + OPT_BOOLEAN(0, "stdin", &transform_stdin, "read from stdin"), + OPT_END(), + }; git_config(git_default_config); + 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!"); + usage_with_options(name_rev_usage, opts); + } + if (all || transform_stdin) + cutoff = 0; - if (argc < 2) - usage(name_rev_usage); - - for (--argc, ++argv; argc; --argc, ++argv) { + for (; argc; argc--, argv++) { unsigned char sha1[20]; struct object *o; struct commit *commit; - if (!as_is && (*argv)[0] == '-') { - if (!strcmp(*argv, "--")) { - as_is = 1; - continue; - } else if (!strcmp(*argv, "--name-only")) { - data.name_only = 1; - continue; - } else if (!strcmp(*argv, "--tags")) { - data.tags_only = 1; - continue; - } else if (!prefixcmp(*argv, "--refs=")) { - data.ref_filter = *argv + 7; - continue; - } else if (!strcmp(*argv, "--all")) { - if (argc > 1) - die("Specify either a list, or --all, not both!"); - all = 1; - cutoff = 0; - continue; - } else if (!strcmp(*argv, "--stdin")) { - if (argc > 1) - die("Specify either a list, or --stdin, not both!"); - transform_stdin = 1; - cutoff = 0; - continue; - } - usage(name_rev_usage); - } - if (get_sha1(*argv, sha1)) { fprintf(stderr, "Could not get sha1 for %s. Skipping.\n", *argv); @@ -212,10 +200,8 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) } commit = (struct commit *)o; - if (cutoff > commit->date) cutoff = commit->date; - add_object_array((struct object *)commit, *argv, &revs); } From b2565ae57376116b9d78369b6eac07e55c6f7851 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 15 Oct 2007 23:06:02 +0200 Subject: [PATCH 32/74] Make builtin-pack-refs.c use parse_options. Signed-off-by: Pierre Habouzit Signed-off-by: Shawn O. Pearce --- builtin-pack-refs.c | 45 +++++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index 09df4e11a8..a62f06bb89 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -3,9 +3,7 @@ #include "refs.h" #include "object.h" #include "tag.h" - -static const char builtin_pack_refs_usage[] = -"git-pack-refs [--all] [--prune | --no-prune]"; +#include "parse-options.h" struct ref_to_prune { struct ref_to_prune *next; @@ -117,31 +115,26 @@ static int pack_refs(unsigned int flags) return 0; } +static char const * const pack_refs_usage[] = { + "git-pack-refs [options]", + NULL +}; + int cmd_pack_refs(int argc, const char **argv, const char *prefix) { - int i; - unsigned int flags; - - flags = PACK_REFS_PRUNE; - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (!strcmp(arg, "--prune")) { - flags |= PACK_REFS_PRUNE; /* now the default */ - continue; - } - if (!strcmp(arg, "--no-prune")) { - flags &= ~PACK_REFS_PRUNE; - continue; - } - if (!strcmp(arg, "--all")) { - flags |= PACK_REFS_ALL; - continue; - } - /* perhaps other parameters later... */ - break; - } - if (i != argc) - usage(builtin_pack_refs_usage); + int all = 0, prune = 1; + unsigned int flags = 0; + struct option opts[] = { + OPT_BOOLEAN(0, "all", &all, "pack everything"), + OPT_BOOLEAN(0, "prune", &prune, "prune loose refs (default)"), + OPT_END(), + }; + if (parse_options(argc, argv, opts, pack_refs_usage, 0)) + usage_with_options(pack_refs_usage, opts); + if (prune) + flags |= PACK_REFS_PRUNE; + if (all) + flags |= PACK_REFS_ALL; return pack_refs(flags); } From 0e549137966feb016927a827fb6e359aec8264a3 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 30 Oct 2007 14:57:31 -0400 Subject: [PATCH 33/74] prune-packed: don't call display_progress() for every file The progress count is per fanout directory, so it is useless to call it for every file as the count doesn't change that often. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-prune-packed.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c index 015c8bb7cc..907e36828f 100644 --- a/builtin-prune-packed.c +++ b/builtin-prune-packed.c @@ -15,6 +15,9 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts) struct dirent *de; char hex[40]; + if (opts == VERBOSE) + display_progress(&progress, i + 1); + sprintf(hex, "%02x", i); while ((de = readdir(dir)) != NULL) { unsigned char sha1[20]; @@ -26,8 +29,6 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts) if (!has_sha1_pack(sha1, NULL)) continue; memcpy(pathname + len, de->d_name, 38); - if (opts == VERBOSE) - display_progress(&progress, i + 1); if (opts & DRY_RUN) printf("rm -f %s\n", pathname); else if (unlink(pathname) < 0) From dc6a0757c4f966dd124bd85be2adad5a0b7b2167 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 30 Oct 2007 14:57:32 -0400 Subject: [PATCH 34/74] make struct progress an opaque type This allows for better management of progress "object" existence, as well as making the progress display implementation more independent from its callers. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 16 ++++++++-------- builtin-prune-packed.c | 7 +++---- builtin-unpack-objects.c | 6 +++--- index-pack.c | 12 ++++++------ progress.c | 33 +++++++++++++++++++++++++++------ progress.h | 18 +++++------------- unpack-trees.c | 10 +++++----- 7 files changed, 57 insertions(+), 45 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 21ba977df3..3ca5cc7800 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -73,7 +73,7 @@ static int depth = 50; static int delta_search_threads = 1; static int pack_to_stdout; static int num_preferred_base; -static struct progress progress_state; +static struct progress *progress_state; static int pack_compression_level = Z_DEFAULT_COMPRESSION; static int pack_compression_seen; @@ -598,7 +598,7 @@ static void write_pack_file(void) uint32_t nr_remaining = nr_result; if (do_progress) - start_progress(&progress_state, "Writing objects", nr_result); + progress_state = start_progress("Writing objects", nr_result); written_list = xmalloc(nr_objects * sizeof(struct object_entry *)); do { @@ -630,7 +630,7 @@ static void write_pack_file(void) break; offset = offset_one; if (do_progress) - display_progress(&progress_state, written); + display_progress(progress_state, written); } /* @@ -854,7 +854,7 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type, object_ix[-1 - ix] = nr_objects; if (progress) - display_progress(&progress_state, nr_objects); + display_progress(progress_state, nr_objects); if (name && no_try_delta(name)) entry->no_try_delta = 1; @@ -1518,7 +1518,7 @@ static void find_deltas(struct object_entry **list, unsigned list_size, progress_lock(); (*processed)++; if (progress) - display_progress(&progress_state, *processed); + display_progress(progress_state, *processed); progress_unlock(); /* @@ -1718,8 +1718,8 @@ static void prepare_pack(int window, int depth) if (nr_deltas && n > 1) { unsigned nr_done = 0; if (progress) - start_progress(&progress_state, "Compressing objects", - nr_deltas); + progress_state = start_progress("Compressing objects", + nr_deltas); qsort(delta_list, n, sizeof(*delta_list), type_size_sort); ll_find_deltas(delta_list, n, window+1, depth, &nr_done); if (progress) @@ -2135,7 +2135,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) prepare_packed_git(); if (progress) - start_progress(&progress_state, "Counting objects", 0); + progress_state = start_progress("Counting objects", 0); if (!use_internal_rev_list) read_object_list_from_stdin(); else { diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c index 907e36828f..c66fb037f6 100644 --- a/builtin-prune-packed.c +++ b/builtin-prune-packed.c @@ -8,7 +8,7 @@ static const char prune_packed_usage[] = #define DRY_RUN 01 #define VERBOSE 02 -static struct progress progress; +static struct progress *progress; static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts) { @@ -16,7 +16,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts) char hex[40]; if (opts == VERBOSE) - display_progress(&progress, i + 1); + display_progress(progress, i + 1); sprintf(hex, "%02x", i); while ((de = readdir(dir)) != NULL) { @@ -46,8 +46,7 @@ void prune_packed_objects(int opts) int len = strlen(dir); if (opts == VERBOSE) - start_progress_delay(&progress, - "Removing duplicate objects", + progress = start_progress_delay("Removing duplicate objects", 256, 95, 2); if (len > PATH_MAX - 42) diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c index 2317b8f411..e0ecbc51a7 100644 --- a/builtin-unpack-objects.c +++ b/builtin-unpack-objects.c @@ -311,7 +311,7 @@ static void unpack_one(unsigned nr) static void unpack_all(void) { int i; - struct progress progress; + struct progress *progress = NULL; struct pack_header *hdr = fill(sizeof(struct pack_header)); unsigned nr_objects = ntohl(hdr->hdr_entries); @@ -322,12 +322,12 @@ static void unpack_all(void) use(sizeof(struct pack_header)); if (!quiet) - start_progress(&progress, "Unpacking objects", nr_objects); + progress = start_progress("Unpacking objects", nr_objects); obj_list = xmalloc(nr_objects * sizeof(*obj_list)); for (i = 0; i < nr_objects; i++) { unpack_one(i); if (!quiet) - display_progress(&progress, i + 1); + display_progress(progress, i + 1); } if (!quiet) stop_progress(&progress); diff --git a/index-pack.c b/index-pack.c index 2f149a40fd..b4543a4cc2 100644 --- a/index-pack.c +++ b/index-pack.c @@ -46,7 +46,7 @@ static int nr_resolved_deltas; static int from_stdin; static int verbose; -static struct progress progress; +static struct progress *progress; /* We always read in 4kB chunks. */ static unsigned char input_buffer[4096]; @@ -406,7 +406,7 @@ static void parse_pack_objects(unsigned char *sha1) * - remember base (SHA1 or offset) for all deltas. */ if (verbose) - start_progress(&progress, "Indexing objects", nr_objects); + progress = start_progress("Indexing objects", nr_objects); for (i = 0; i < nr_objects; i++) { struct object_entry *obj = &objects[i]; data = unpack_raw_entry(obj, &delta->base); @@ -419,7 +419,7 @@ static void parse_pack_objects(unsigned char *sha1) sha1_object(data, obj->size, obj->type, obj->idx.sha1); free(data); if (verbose) - display_progress(&progress, i+1); + display_progress(progress, i+1); } objects[i].idx.offset = consumed_bytes; if (verbose) @@ -455,7 +455,7 @@ static void parse_pack_objects(unsigned char *sha1) * for some more deltas. */ if (verbose) - start_progress(&progress, "Resolving deltas", nr_deltas); + progress = start_progress("Resolving deltas", nr_deltas); for (i = 0; i < nr_objects; i++) { struct object_entry *obj = &objects[i]; union delta_base base; @@ -487,7 +487,7 @@ static void parse_pack_objects(unsigned char *sha1) } free(data); if (verbose) - display_progress(&progress, nr_resolved_deltas); + display_progress(progress, nr_resolved_deltas); } } @@ -595,7 +595,7 @@ static void fix_unresolved_deltas(int nr_unresolved) append_obj_to_pack(d->base.sha1, data, size, type); free(data); if (verbose) - display_progress(&progress, nr_resolved_deltas); + display_progress(progress, nr_resolved_deltas); } free(sorted_by_pos); } diff --git a/progress.c b/progress.c index 7629e0572b..c342e39c5d 100644 --- a/progress.c +++ b/progress.c @@ -1,6 +1,15 @@ #include "git-compat-util.h" #include "progress.h" +struct progress { + const char *title; + int last_value; + unsigned total; + unsigned last_percent; + unsigned delay; + unsigned delayed_percent_treshold; +}; + static volatile sig_atomic_t progress_update; static void progress_interval(int signum) @@ -76,12 +85,18 @@ static int display(struct progress *progress, unsigned n, int done) int display_progress(struct progress *progress, unsigned n) { - return display(progress, n, 0); + return progress ? display(progress, n, 0) : 0; } -void start_progress_delay(struct progress *progress, const char *title, - unsigned total, unsigned percent_treshold, unsigned delay) +struct progress *start_progress_delay(const char *title, unsigned total, + unsigned percent_treshold, unsigned delay) { + struct progress *progress = malloc(sizeof(*progress)); + if (!progress) { + /* unlikely, but here's a good fallback */ + fprintf(stderr, "%s...\n", title); + return NULL; + } progress->title = title; progress->total = total; progress->last_value = -1; @@ -89,19 +104,25 @@ void start_progress_delay(struct progress *progress, const char *title, progress->delayed_percent_treshold = percent_treshold; progress->delay = delay; set_progress_signal(); + return progress; } -void start_progress(struct progress *progress, const char *title, unsigned total) +struct progress *start_progress(const char *title, unsigned total) { - start_progress_delay(progress, title, total, 0, 0); + return start_progress_delay(title, total, 0, 0); } -void stop_progress(struct progress *progress) +void stop_progress(struct progress **p_progress) { + struct progress *progress = *p_progress; + if (!progress) + return; + *p_progress = NULL; if (progress->last_value != -1) { /* Force the last update */ progress_update = 1; display(progress, progress->last_value, 1); } clear_progress_signal(); + free(progress); } diff --git a/progress.h b/progress.h index 07b56bdbb5..4c6d53524b 100644 --- a/progress.h +++ b/progress.h @@ -1,20 +1,12 @@ #ifndef PROGRESS_H #define PROGRESS_H -struct progress { - const char *title; - int last_value; - unsigned total; - unsigned last_percent; - unsigned delay; - unsigned delayed_percent_treshold; -}; +struct progress; int display_progress(struct progress *progress, unsigned n); -void start_progress(struct progress *progress, const char *title, - unsigned total); -void start_progress_delay(struct progress *progress, const char *title, - unsigned total, unsigned percent_treshold, unsigned delay); -void stop_progress(struct progress *progress); +struct progress *start_progress(const char *title, unsigned total); +struct progress *start_progress_delay(const char *title, unsigned total, + unsigned percent_treshold, unsigned delay); +void stop_progress(struct progress **progress); #endif diff --git a/unpack-trees.c b/unpack-trees.c index 32251018b2..6776c29cde 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -297,7 +297,7 @@ static void check_updates(struct cache_entry **src, int nr, { unsigned short mask = htons(CE_UPDATE); unsigned cnt = 0, total = 0; - struct progress progress; + struct progress *progress = NULL; char last_symlink[PATH_MAX]; if (o->update && o->verbose_update) { @@ -307,8 +307,8 @@ static void check_updates(struct cache_entry **src, int nr, total++; } - start_progress_delay(&progress, "Checking out files", - total, 50, 2); + progress = start_progress_delay("Checking out files", + total, 50, 2); cnt = 0; } @@ -318,7 +318,7 @@ static void check_updates(struct cache_entry **src, int nr, if (total) if (!ce->ce_mode || ce->ce_flags & mask) - display_progress(&progress, ++cnt); + display_progress(progress, ++cnt); if (!ce->ce_mode) { if (o->update) unlink_entry(ce->name, last_symlink); @@ -333,7 +333,7 @@ static void check_updates(struct cache_entry **src, int nr, } } if (total) - stop_progress(&progress);; + stop_progress(&progress); } int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o) From 4d4fcc5451d9d653bebcc8afa18543cb426abeed Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 30 Oct 2007 14:57:33 -0400 Subject: [PATCH 35/74] relax usage of the progress API Since it is now OK to pass a null pointer to display_progress() and stop_progress() resulting in a no-op, then we can simplify the code and remove a bunch of lines by not making those calls conditional all the time. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 18 ++++++------------ builtin-prune-packed.c | 6 ++---- builtin-unpack-objects.c | 6 ++---- index-pack.c | 20 +++++++------------- unpack-trees.c | 8 +++----- 5 files changed, 20 insertions(+), 38 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 3ca5cc7800..52a26a28f4 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -629,8 +629,7 @@ static void write_pack_file(void) if (!offset_one) break; offset = offset_one; - if (do_progress) - display_progress(progress_state, written); + display_progress(progress_state, written); } /* @@ -684,8 +683,7 @@ static void write_pack_file(void) } while (nr_remaining && i < nr_objects); free(written_list); - if (do_progress) - stop_progress(&progress_state); + stop_progress(&progress_state); if (written != nr_result) die("wrote %u objects while expecting %u", written, nr_result); /* @@ -853,8 +851,7 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type, else object_ix[-1 - ix] = nr_objects; - if (progress) - display_progress(progress_state, nr_objects); + display_progress(progress_state, nr_objects); if (name && no_try_delta(name)) entry->no_try_delta = 1; @@ -1517,8 +1514,7 @@ static void find_deltas(struct object_entry **list, unsigned list_size, progress_lock(); (*processed)++; - if (progress) - display_progress(progress_state, *processed); + display_progress(progress_state, *processed); progress_unlock(); /* @@ -1722,8 +1718,7 @@ static void prepare_pack(int window, int depth) nr_deltas); qsort(delta_list, n, sizeof(*delta_list), type_size_sort); ll_find_deltas(delta_list, n, window+1, depth, &nr_done); - if (progress) - stop_progress(&progress_state); + stop_progress(&progress_state); if (nr_done != nr_deltas) die("inconsistency with delta count"); } @@ -2142,8 +2137,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) rp_av[rp_ac] = NULL; get_object_list(rp_ac, rp_av); } - if (progress) - stop_progress(&progress_state); + stop_progress(&progress_state); if (non_empty && !nr_result) return 0; diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c index c66fb037f6..f4287dad10 100644 --- a/builtin-prune-packed.c +++ b/builtin-prune-packed.c @@ -15,8 +15,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts) struct dirent *de; char hex[40]; - if (opts == VERBOSE) - display_progress(progress, i + 1); + display_progress(progress, i + 1); sprintf(hex, "%02x", i); while ((de = readdir(dir)) != NULL) { @@ -64,8 +63,7 @@ void prune_packed_objects(int opts) prune_dir(i, d, pathname, len + 3, opts); closedir(d); } - if (opts == VERBOSE) - stop_progress(&progress); + stop_progress(&progress); } int cmd_prune_packed(int argc, const char **argv, const char *prefix) diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c index e0ecbc51a7..1e51865c52 100644 --- a/builtin-unpack-objects.c +++ b/builtin-unpack-objects.c @@ -326,11 +326,9 @@ static void unpack_all(void) obj_list = xmalloc(nr_objects * sizeof(*obj_list)); for (i = 0; i < nr_objects; i++) { unpack_one(i); - if (!quiet) - display_progress(progress, i + 1); + display_progress(progress, i + 1); } - if (!quiet) - stop_progress(&progress); + stop_progress(&progress); if (delta_list) die("unresolved deltas left after unpacking"); diff --git a/index-pack.c b/index-pack.c index b4543a4cc2..879ea15485 100644 --- a/index-pack.c +++ b/index-pack.c @@ -418,12 +418,10 @@ static void parse_pack_objects(unsigned char *sha1) } else sha1_object(data, obj->size, obj->type, obj->idx.sha1); free(data); - if (verbose) - display_progress(progress, i+1); + display_progress(progress, i+1); } objects[i].idx.offset = consumed_bytes; - if (verbose) - stop_progress(&progress); + stop_progress(&progress); /* Check pack integrity */ flush(); @@ -486,8 +484,7 @@ static void parse_pack_objects(unsigned char *sha1) obj->size, obj->type); } free(data); - if (verbose) - display_progress(progress, nr_resolved_deltas); + display_progress(progress, nr_resolved_deltas); } } @@ -594,8 +591,7 @@ static void fix_unresolved_deltas(int nr_unresolved) die("local object %s is corrupt", sha1_to_hex(d->base.sha1)); append_obj_to_pack(d->base.sha1, data, size, type); free(data); - if (verbose) - display_progress(progress, nr_resolved_deltas); + display_progress(progress, nr_resolved_deltas); } free(sorted_by_pos); } @@ -774,8 +770,7 @@ int main(int argc, char **argv) deltas = xmalloc(nr_objects * sizeof(struct delta_entry)); parse_pack_objects(sha1); if (nr_deltas == nr_resolved_deltas) { - if (verbose) - stop_progress(&progress); + stop_progress(&progress); /* Flush remaining pack final 20-byte SHA1. */ flush(); } else { @@ -788,11 +783,10 @@ int main(int argc, char **argv) (nr_objects + nr_unresolved + 1) * sizeof(*objects)); fix_unresolved_deltas(nr_unresolved); - if (verbose) { - stop_progress(&progress); + stop_progress(&progress); + if (verbose) fprintf(stderr, "%d objects were added to complete this thin pack.\n", nr_objects - nr_objects_initial); - } fixup_pack_header_footer(output_fd, sha1, curr_pack, nr_objects); } diff --git a/unpack-trees.c b/unpack-trees.c index 6776c29cde..c527d7d049 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -316,9 +316,8 @@ static void check_updates(struct cache_entry **src, int nr, while (nr--) { struct cache_entry *ce = *src++; - if (total) - if (!ce->ce_mode || ce->ce_flags & mask) - display_progress(progress, ++cnt); + if (!ce->ce_mode || ce->ce_flags & mask) + display_progress(progress, ++cnt); if (!ce->ce_mode) { if (o->update) unlink_entry(ce->name, last_symlink); @@ -332,8 +331,7 @@ static void check_updates(struct cache_entry **src, int nr, } } } - if (total) - stop_progress(&progress); + stop_progress(&progress); } int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o) From cf84d51c43fa05cce416bfa3f5db3ad70773abdf Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 30 Oct 2007 14:57:34 -0400 Subject: [PATCH 36/74] add throughput to progress display This adds the ability for the progress code to also display transfer throughput when that makes sense. The math was inspired by commit c548cf4ee0737a321ffe94f6a97c65baf87281be from Linus. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- progress.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++--- progress.h | 1 + 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/progress.c b/progress.c index c342e39c5d..15197fbe6c 100644 --- a/progress.c +++ b/progress.c @@ -1,6 +1,19 @@ #include "git-compat-util.h" #include "progress.h" +#define TP_IDX_MAX 8 + +struct throughput { + struct timeval prev_tv; + unsigned long count; + unsigned long avg_bytes; + unsigned long last_bytes[TP_IDX_MAX]; + unsigned int avg_misecs; + unsigned int last_misecs[TP_IDX_MAX]; + unsigned int idx; + char display[20]; +}; + struct progress { const char *title; int last_value; @@ -8,6 +21,7 @@ struct progress { unsigned last_percent; unsigned delay; unsigned delayed_percent_treshold; + struct throughput *throughput; }; static volatile sig_atomic_t progress_update; @@ -46,7 +60,7 @@ static void clear_progress_signal(void) static int display(struct progress *progress, unsigned n, int done) { - char *eol; + char *eol, *tp; if (progress->delay) { if (!progress_update || --progress->delay) @@ -64,18 +78,20 @@ static int display(struct progress *progress, unsigned n, int done) } progress->last_value = n; + tp = (progress->throughput) ? progress->throughput->display : ""; eol = done ? ", done. \n" : " \r"; if (progress->total) { unsigned percent = n * 100 / progress->total; if (percent != progress->last_percent || progress_update) { progress->last_percent = percent; - fprintf(stderr, "%s: %3u%% (%u/%u)%s", progress->title, - percent, n, progress->total, eol); + fprintf(stderr, "%s: %3u%% (%u/%u)%s%s", + progress->title, percent, n, + progress->total, tp, eol); progress_update = 0; return 1; } } else if (progress_update) { - fprintf(stderr, "%s: %u%s", progress->title, n, eol); + fprintf(stderr, "%s: %u%s%s", progress->title, n, tp, eol); progress_update = 0; return 1; } @@ -83,6 +99,60 @@ static int display(struct progress *progress, unsigned n, int done) return 0; } +void display_throughput(struct progress *progress, unsigned long n) +{ + struct throughput *tp; + struct timeval tv; + unsigned int misecs; + + if (!progress) + return; + tp = progress->throughput; + + gettimeofday(&tv, NULL); + + if (!tp) { + progress->throughput = tp = calloc(1, sizeof(*tp)); + if (tp) + tp->prev_tv = tv; + return; + } + + tp->count += n; + + /* + * We have x = bytes and y = microsecs. We want z = KiB/s: + * + * z = (x / 1024) / (y / 1000000) + * z = x / y * 1000000 / 1024 + * z = x / (y * 1024 / 1000000) + * z = x / y' + * + * To simplify things we'll keep track of misecs, or 1024th of a sec + * obtained with: + * + * y' = y * 1024 / 1000000 + * y' = y / (1000000 / 1024) + * y' = y / 977 + */ + misecs = (tv.tv_sec - tp->prev_tv.tv_sec) * 1024; + misecs += (int)(tv.tv_usec - tp->prev_tv.tv_usec) / 977; + + if (misecs > 512) { + tp->prev_tv = tv; + tp->avg_bytes += tp->count; + tp->avg_misecs += misecs; + snprintf(tp->display, sizeof(tp->display), + ", %lu KiB/s", tp->avg_bytes / tp->avg_misecs); + tp->avg_bytes -= tp->last_bytes[tp->idx]; + tp->avg_misecs -= tp->last_misecs[tp->idx]; + tp->last_bytes[tp->idx] = tp->count; + tp->last_misecs[tp->idx] = misecs; + tp->idx = (tp->idx + 1) % TP_IDX_MAX; + tp->count = 0; + } +} + int display_progress(struct progress *progress, unsigned n) { return progress ? display(progress, n, 0) : 0; @@ -103,6 +173,7 @@ struct progress *start_progress_delay(const char *title, unsigned total, progress->last_percent = -1; progress->delayed_percent_treshold = percent_treshold; progress->delay = delay; + progress->throughput = NULL; set_progress_signal(); return progress; } @@ -124,5 +195,6 @@ void stop_progress(struct progress **p_progress) display(progress, progress->last_value, 1); } clear_progress_signal(); + free(progress->throughput); free(progress); } diff --git a/progress.h b/progress.h index 4c6d53524b..61cb68dfa5 100644 --- a/progress.h +++ b/progress.h @@ -3,6 +3,7 @@ struct progress; +void display_throughput(struct progress *progress, unsigned long n); int display_progress(struct progress *progress, unsigned n); struct progress *start_progress(const char *title, unsigned total); struct progress *start_progress_delay(const char *title, unsigned total, From 29e63ed3f6962cd96f7153e9f492f36797d25c33 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 30 Oct 2007 14:57:35 -0400 Subject: [PATCH 37/74] add throughput display to index-pack ... and call it "Receiving objects" when over stdin to look clearer to end users. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- index-pack.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/index-pack.c b/index-pack.c index 879ea15485..61ea7621be 100644 --- a/index-pack.c +++ b/index-pack.c @@ -87,6 +87,8 @@ static void *fill(int min) die("early EOF"); die("read error on input: %s", strerror(errno)); } + if (from_stdin) + display_throughput(progress, ret); input_len += ret; } while (input_len < min); return input_buffer; @@ -406,7 +408,9 @@ static void parse_pack_objects(unsigned char *sha1) * - remember base (SHA1 or offset) for all deltas. */ if (verbose) - progress = start_progress("Indexing objects", nr_objects); + progress = start_progress( + from_stdin ? "Receiving objects" : "Indexing objects", + nr_objects); for (i = 0; i < nr_objects; i++) { struct object_entry *obj = &objects[i]; data = unpack_raw_entry(obj, &delta->base); From 74b6792f7b2d4b410459cf96977a2007ddea8675 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 30 Oct 2007 15:41:13 -0400 Subject: [PATCH 38/74] add some copyright notice to the progress display code Some self patting on the back to keep my ego alive. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- progress.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/progress.c b/progress.c index 15197fbe6c..34a59611fb 100644 --- a/progress.c +++ b/progress.c @@ -1,3 +1,13 @@ +/* + * Simple text-based progress display module for GIT + * + * Copyright (c) 2007 by Nicolas Pitre + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + #include "git-compat-util.h" #include "progress.h" From 2a128d63dc140e8daeaf68659d9263b7389b7d1b Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 30 Oct 2007 17:06:21 -0400 Subject: [PATCH 39/74] add throughput display to git-push This one triggers only when git-pack-objects is called with --all-progress and --stdout which is the combination used by git-push. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 2 +- csum-file.c | 8 ++++++++ csum-file.h | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 52a26a28f4..25ec65d0f0 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -606,7 +606,7 @@ static void write_pack_file(void) char *pack_tmp_name = NULL; if (pack_to_stdout) { - f = sha1fd(1, ""); + f = sha1fd_throughput(1, "", progress_state); } else { char tmpname[PATH_MAX]; int fd; diff --git a/csum-file.c b/csum-file.c index 9929991dea..3729e73e19 100644 --- a/csum-file.c +++ b/csum-file.c @@ -8,6 +8,7 @@ * able to verify hasn't been messed with afterwards. */ #include "cache.h" +#include "progress.h" #include "csum-file.h" static void sha1flush(struct sha1file *f, unsigned int count) @@ -17,6 +18,7 @@ static void sha1flush(struct sha1file *f, unsigned int count) for (;;) { int ret = xwrite(f->fd, buf, count); if (ret > 0) { + display_throughput(f->tp, ret); buf = (char *) buf + ret; count -= ret; if (count) @@ -79,6 +81,11 @@ int sha1write(struct sha1file *f, void *buf, unsigned int count) } struct sha1file *sha1fd(int fd, const char *name) +{ + return sha1fd_throughput(fd, name, NULL); +} + +struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp) { struct sha1file *f; unsigned len; @@ -94,6 +101,7 @@ struct sha1file *sha1fd(int fd, const char *name) f->fd = fd; f->error = 0; f->offset = 0; + f->tp = tp; f->do_crc = 0; SHA1_Init(&f->ctx); return f; diff --git a/csum-file.h b/csum-file.h index c3c792f1b5..4d1b231292 100644 --- a/csum-file.h +++ b/csum-file.h @@ -1,11 +1,14 @@ #ifndef CSUM_FILE_H #define CSUM_FILE_H +struct progress; + /* A SHA1-protected file */ struct sha1file { int fd, error; unsigned int offset, namelen; SHA_CTX ctx; + struct progress *tp; char name[PATH_MAX]; int do_crc; uint32_t crc32; @@ -13,6 +16,7 @@ struct sha1file { }; 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 sha1write(struct sha1file *, void *, unsigned int); extern void crc32_begin(struct sha1file *); From dd46b9b95b853328e7ff1c11b7f5cae68751c79e Mon Sep 17 00:00:00 2001 From: Emil Medve Date: Tue, 30 Oct 2007 14:15:21 -0500 Subject: [PATCH 40/74] Fixed a command line option type for builtin-fsck.c The typo was introduced by 5ac0a2063e8f824f6e8ffb4d18de74c55aae7131 (Make builtin-fsck.c use parse_options.) Signed-off-by: Emil Medve Acked-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- builtin-fsck.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-fsck.c b/builtin-fsck.c index 64da3bd363..e4874f64a3 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -680,7 +680,7 @@ static struct option fsck_opts[] = { OPT_BOOLEAN(0, "cache", &keep_cache_objects, "make index objects head nodes"), OPT_BOOLEAN(0, "reflogs", &include_reflogs, "make reflogs head nodes (default)"), OPT_BOOLEAN(0, "full", &check_full, "also consider alternate objects"), - OPT_BOOLEAN(0, "struct", &check_strict, "enable more strict checking"), + OPT_BOOLEAN(0, "strict", &check_strict, "enable more strict checking"), OPT_BOOLEAN(0, "lost-found", &write_lost_and_found, "write dangling objects in .git/lost-found"), OPT_END(), From 93ff3f6a5311ebb6347b817d1c57c94dbbe4de73 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 1 Nov 2007 16:59:55 -0400 Subject: [PATCH 41/74] return the prune-packed progress display to the inner loop This reverts commit 0e549137966feb016927a827fb6e359aec8264a3 so to return to the same state as commit b5d72f0a4cd3cce945ca0d37e4fa0ebbfcdcdb52. On Wed, 31 Oct 2007, Shawn O. Pearce wrote: > During my testing with a 40,000 loose object case (yea, I fully > unpacked a git.git clone I had laying around) my system stalled > hard in the first object directory. A *lot* longer than 1 second. > So I got no progress meter for a long time, and then a progress > meter appeared on the second directory. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-prune-packed.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c index f4287dad10..23faf3129f 100644 --- a/builtin-prune-packed.c +++ b/builtin-prune-packed.c @@ -15,8 +15,6 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts) struct dirent *de; char hex[40]; - display_progress(progress, i + 1); - sprintf(hex, "%02x", i); while ((de = readdir(dir)) != NULL) { unsigned char sha1[20]; @@ -32,6 +30,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts) printf("rm -f %s\n", pathname); else if (unlink(pathname) < 0) error("unable to unlink %s", pathname); + display_progress(progress, i + 1); } pathname[len] = 0; rmdir(pathname); From 3e935d19822db08cc0dedd8764135771ffd6ec7b Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 1 Nov 2007 16:59:56 -0400 Subject: [PATCH 42/74] make sure throughput display gets updated even if progress doesn't move Currently the progress/throughput display update happens only through display_progress(). If the progress based on object count remains unchanged because a large object is being received, the latest throughput won't be displayed. The display update should occur through display_throughput() as well. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- progress.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/progress.c b/progress.c index 34a59611fb..39d5d2c9f2 100644 --- a/progress.c +++ b/progress.c @@ -160,6 +160,9 @@ void display_throughput(struct progress *progress, unsigned long n) tp->last_misecs[tp->idx] = misecs; tp->idx = (tp->idx + 1) % TP_IDX_MAX; tp->count = 0; + + if (progress->last_value != -1 && progress_update) + display(progress, progress->last_value, 0); } } From 81f6654a47075a345ba63a394921f77fc87b6500 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 1 Nov 2007 16:59:57 -0400 Subject: [PATCH 43/74] Show total transferred as part of throughput progress Right now it is infeasible to offer to the user a reasonable concept of when a clone will be complete as we aren't able to come up with the final pack size until after we have actually transferred the entire thing to the client. However in many cases users can work with a rough rule-of-thumb; for example it is somewhat well known that git.git is about 16 MiB today and that linux-2.6.git is over 120 MiB. We now show the total amount of data we have transferred over the network as part of the throughput meter, organizing it in "human friendly" terms like `ls -h` would do. Users can glance at this, see that the total transferred size is about 3 MiB, see the throughput of X KiB/sec, and determine a reasonable figure of about when the clone will be complete, assuming they know the rough size of the source repository or are able to obtain it. This is also a helpful indicator that there is progress being made even if we stall on a very large object. The thoughput meter may remain relatively constant and the percentage complete and object count won't be changing, but the total transferred will be increasing as additional data is received for this object. [from an initial proposal from Shawn O. Pearce] Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- progress.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/progress.c b/progress.c index 39d5d2c9f2..3f6a602a53 100644 --- a/progress.c +++ b/progress.c @@ -15,13 +15,14 @@ struct throughput { struct timeval prev_tv; + off_t total; unsigned long count; unsigned long avg_bytes; unsigned long last_bytes[TP_IDX_MAX]; unsigned int avg_misecs; unsigned int last_misecs[TP_IDX_MAX]; unsigned int idx; - char display[20]; + char display[32]; }; struct progress { @@ -128,6 +129,7 @@ void display_throughput(struct progress *progress, unsigned long n) return; } + tp->total += n; tp->count += n; /* @@ -149,11 +151,32 @@ void display_throughput(struct progress *progress, unsigned long n) misecs += (int)(tv.tv_usec - tp->prev_tv.tv_usec) / 977; if (misecs > 512) { + int l = sizeof(tp->display); tp->prev_tv = tv; tp->avg_bytes += tp->count; tp->avg_misecs += misecs; - snprintf(tp->display, sizeof(tp->display), - ", %lu KiB/s", tp->avg_bytes / tp->avg_misecs); + + if (tp->total > 1 << 30) { + l -= snprintf(tp->display, l, ", %u.%2.2u GiB", + (int)(tp->total >> 30), + (int)(tp->total & ((1 << 30) - 1)) / 10737419); + } else if (tp->total > 1 << 20) { + l -= snprintf(tp->display, l, ", %u.%2.2u MiB", + (int)(tp->total >> 20), + ((int)(tp->total & ((1 << 20) - 1)) + * 100) >> 20); + } else if (tp->total > 1 << 10) { + l -= snprintf(tp->display, l, ", %u.%2.2u KiB", + (int)(tp->total >> 10), + ((int)(tp->total & ((1 << 10) - 1)) + * 100) >> 10); + } else { + l -= snprintf(tp->display, l, ", %u bytes", + (int)tp->total); + } + snprintf(tp->display + sizeof(tp->display) - l, l, + " | %lu KiB/s", tp->avg_bytes / tp->avg_misecs); + tp->avg_bytes -= tp->last_bytes[tp->idx]; tp->avg_misecs -= tp->last_misecs[tp->idx]; tp->last_bytes[tp->idx] = tp->count; From 9e6c723087def70d001740db48cf042b77a1d9cd Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Wed, 31 Oct 2007 13:59:16 +0000 Subject: [PATCH 44/74] git-diff.txt: add section "output format" describing the diff formats git-diff.txt includes diff-options.txt which for the -p option refers to a section "generating patches.." which is missing from the git-diff documentation. This patch adapts diff-format.txt to additionally mention the git-diff program, and includes diff-format.txt into git-diff.txt. Tino Keitel noticed this problem. Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- Documentation/diff-format.txt | 19 ++++++++++--------- Documentation/git-diff.txt | 3 +++ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt index 001503205b..9709c35c98 100644 --- a/Documentation/diff-format.txt +++ b/Documentation/diff-format.txt @@ -1,5 +1,5 @@ -The output format from "git-diff-index", "git-diff-tree" and -"git-diff-files" are very similar. +The output format from "git-diff-index", "git-diff-tree", +"git-diff-files" and "git diff --raw" are very similar. These commands all compare two sets of things; what is compared differs: @@ -62,7 +62,8 @@ respectively. diff format for merges ---------------------- -"git-diff-tree" and "git-diff-files" can take '-c' or '--cc' option +"git-diff-tree", "git-diff-files" and "git-diff --raw" +can take '-c' or '--cc' option to generate diff output also for merge commits. The output differs from the format described above in the following way: @@ -86,10 +87,10 @@ Generating patches with -p -------------------------- When "git-diff-index", "git-diff-tree", or "git-diff-files" are run -with a '-p' option, they do not produce the output described above; -instead they produce a patch file. You can customize the creation -of such patches via the GIT_EXTERNAL_DIFF and the GIT_DIFF_OPTS -environment variables. +with a '-p' option, or "git diff" without the '--raw' option, they +do not produce the output described above; instead they produce a +patch file. You can customize the creation of such patches via the +GIT_EXTERNAL_DIFF and the GIT_DIFF_OPTS environment variables. What the -p option produces is slightly different from the traditional diff format. @@ -137,8 +138,8 @@ file made it into the new one. combined diff format -------------------- -git-diff-tree and git-diff-files can take '-c' or '--cc' option -to produce 'combined diff', which looks like this: +"git-diff-tree", "git-diff-files" and "git-diff" can take '-c' or +'--cc' option to produce 'combined diff', which looks like this: ------------ diff --combined describe.c diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt index db2eb46a19..201d5daf1a 100644 --- a/Documentation/git-diff.txt +++ b/Documentation/git-diff.txt @@ -82,6 +82,9 @@ include::diff-options.txt[] the diff to the named paths (you can give directory names and get diff for all files under them). +Output format +------------- +include::diff-format.txt[] EXAMPLES -------- From 4593fb84051d39f65cec81958e91056986e4682f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 31 Oct 2007 14:55:17 -0700 Subject: [PATCH 45/74] format-patch -s: add MIME encoding header if signer's name requires so When the body of the commit log message contains a non-ASCII character, format-patch correctly emitted the encoding header to mark the resulting message as such. However, if the original message was fully ASCII, the command line switch "-s" was given to add a new sign-off, and the signer's name was not ASCII only, the resulting message would have contained non-ASCII character but was not marked as such. Signed-off-by: Junio C Hamano --- builtin-branch.c | 2 +- builtin-log.c | 2 +- builtin-rev-list.c | 3 ++- builtin-show-branch.c | 2 +- commit.c | 5 ++--- commit.h | 4 +++- log-tree.c | 15 ++++++++++++++- 7 files changed, 24 insertions(+), 9 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index 3da8b55b8f..3e020ccf17 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -276,7 +276,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, commit = lookup_commit(item->sha1); if (commit && !parse_commit(commit)) { pretty_print_commit(CMIT_FMT_ONELINE, commit, - &subject, 0, NULL, NULL, 0); + &subject, 0, NULL, NULL, 0, 0); sub = subject.buf; } printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color), diff --git a/builtin-log.c b/builtin-log.c index e8b982db7c..8b2bf632c5 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -787,7 +787,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) struct strbuf buf; strbuf_init(&buf, 0); pretty_print_commit(CMIT_FMT_ONELINE, commit, - &buf, 0, NULL, NULL, 0); + &buf, 0, NULL, NULL, 0, 0); printf("%c %s %s\n", sign, sha1_to_hex(commit->object.sha1), buf.buf); strbuf_release(&buf); diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 44393320e8..697046723f 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -86,7 +86,8 @@ static void show_commit(struct commit *commit) struct strbuf buf; strbuf_init(&buf, 0); pretty_print_commit(revs.commit_format, commit, - &buf, revs.abbrev, NULL, NULL, revs.date_mode); + &buf, revs.abbrev, NULL, NULL, + revs.date_mode, 0); if (buf.len) printf("%s%c", buf.buf, hdr_termination); strbuf_release(&buf); diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 07a0c2316b..6dc835d30a 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -266,7 +266,7 @@ static void show_one_commit(struct commit *commit, int no_name) strbuf_init(&pretty, 0); if (commit->object.parsed) { pretty_print_commit(CMIT_FMT_ONELINE, commit, - &pretty, 0, NULL, NULL, 0); + &pretty, 0, NULL, NULL, 0, 0); pretty_str = pretty.buf; } if (!prefixcmp(pretty_str, "[PATCH] ")) diff --git a/commit.c b/commit.c index ac24266e93..8262f6ac58 100644 --- a/commit.c +++ b/commit.c @@ -479,7 +479,7 @@ static int get_one_line(const char *msg) } /* High bit set, or ISO-2022-INT */ -static int non_ascii(int ch) +int non_ascii(int ch) { ch = (ch & 0xff); return ((ch & 0x80) || (ch == 0x1b)); @@ -1046,12 +1046,11 @@ static void pp_remainder(enum cmit_fmt fmt, void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, struct strbuf *sb, int abbrev, const char *subject, const char *after_subject, - enum date_mode dmode) + enum date_mode dmode, int plain_non_ascii) { unsigned long beginning_of_body; int indent = 4; const char *msg = commit->buffer; - int plain_non_ascii = 0; char *reencoded; const char *encoding; diff --git a/commit.h b/commit.h index b661503972..13b537293d 100644 --- a/commit.h +++ b/commit.h @@ -61,13 +61,15 @@ enum cmit_fmt { CMIT_FMT_UNSPECIFIED, }; +extern int non_ascii(int); extern enum cmit_fmt get_commit_format(const char *arg); extern void format_commit_message(const struct commit *commit, const void *format, struct strbuf *sb); extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*, struct strbuf *, int abbrev, const char *subject, - const char *after_subject, enum date_mode); + const char *after_subject, enum date_mode, + int non_ascii_present); /** Removes the first commit from a list sorted by date, and adds all * of its parents. diff --git a/log-tree.c b/log-tree.c index 3763ce94fc..a34beb0b02 100644 --- a/log-tree.c +++ b/log-tree.c @@ -125,6 +125,18 @@ static unsigned int digits_in_number(unsigned int number) return result; } +static int has_non_ascii(const char *s) +{ + int ch; + if (!s) + return 0; + while ((ch = *s++) != '\0') { + if (non_ascii(ch)) + return 1; + } + return 0; +} + void show_log(struct rev_info *opt, const char *sep) { struct strbuf msgbuf; @@ -273,7 +285,8 @@ void show_log(struct rev_info *opt, const char *sep) */ strbuf_init(&msgbuf, 0); pretty_print_commit(opt->commit_format, commit, &msgbuf, - abbrev, subject, extra_headers, opt->date_mode); + abbrev, subject, extra_headers, opt->date_mode, + has_non_ascii(opt->add_signoff)); if (opt->add_signoff) append_signoff(&msgbuf, opt->add_signoff); From 9d3014566302ad0d3d378b0a1653959b3118066d Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 1 Nov 2007 12:38:08 +0100 Subject: [PATCH 46/74] gitweb: Always set 'from_file' and 'to_file' in parse_difftree_raw_line Always set 'from_file' and 'to_file' keys when parsing raw diff output format line, even if filename didn't change (file was not renamed). This allows for simpler code. Previously, you would have written: $diffinfo->{'from_file'} || $diffinfo->{'file'} but now you can just use $diffinfo->{'from_file'} as 'from_file' is always defined. While at it, replace (for merge commits) $diffinfo->{'from_file'}[$i] || $diffinfo->{'to_file'} by defined $diffinfo->{'from_file'}[$i] ? $diffinfo->{'from_file'}[$i] : $diffinfo->{'to_file'}; to have no problems with file named '0'. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 2e00756276..b22f4be152 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1995,7 +1995,7 @@ sub parse_difftree_raw_line { if ($res{'status'} eq 'R' || $res{'status'} eq 'C') { # renamed or copied ($res{'from_file'}, $res{'to_file'}) = map { unquote($_) } split("\t", $7); } else { - $res{'file'} = unquote($7); + $res{'from_file'} = $res{'to_file'} = $res{'file'} = unquote($7); } } # '::100755 100755 100755 60e79ca1b01bc8b057abe17ddab484699a7f5fdb 94067cc5f73388f33722d52ae02f44692bc07490 94067cc5f73388f33722d52ae02f44692bc07490 MR git-gui/git-gui.sh' @@ -2062,7 +2062,10 @@ sub parse_from_to_diffinfo { fill_from_file_info($diffinfo, @parents) unless exists $diffinfo->{'from_file'}; for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) { - $from->{'file'}[$i] = $diffinfo->{'from_file'}[$i] || $diffinfo->{'to_file'}; + $from->{'file'}[$i] = + defined $diffinfo->{'from_file'}[$i] ? + $diffinfo->{'from_file'}[$i] : + $diffinfo->{'to_file'}; if ($diffinfo->{'status'}[$i] ne "A") { # not new (added) file $from->{'href'}[$i] = href(action=>"blob", hash_base=>$parents[$i], @@ -2074,7 +2077,7 @@ sub parse_from_to_diffinfo { } } else { # ordinary (not combined) diff - $from->{'file'} = $diffinfo->{'from_file'} || $diffinfo->{'file'}; + $from->{'file'} = $diffinfo->{'from_file'}; if ($diffinfo->{'status'} ne "A") { # not new (added) file $from->{'href'} = href(action=>"blob", hash_base=>$hash_parent, hash=>$diffinfo->{'from_id'}, @@ -2084,7 +2087,7 @@ sub parse_from_to_diffinfo { } } - $to->{'file'} = $diffinfo->{'to_file'} || $diffinfo->{'file'}; + $to->{'file'} = $diffinfo->{'to_file'}; if (!is_deleted($diffinfo)) { # file exists in result $to->{'href'} = href(action=>"blob", hash_base=>$hash, hash=>$diffinfo->{'to_id'}, @@ -2829,7 +2832,7 @@ sub is_patch_split { my ($diffinfo, $patchinfo) = @_; return defined $diffinfo && defined $patchinfo - && ($diffinfo->{'to_file'} || $diffinfo->{'file'}) eq $patchinfo->{'to_file'}; + && $diffinfo->{'to_file'} eq $patchinfo->{'to_file'}; } @@ -4667,8 +4670,8 @@ sub git_blobdiff { } %diffinfo = parse_difftree_raw_line($difftree[0]); - $file_parent ||= $diffinfo{'from_file'} || $file_name || $diffinfo{'file'}; - $file_name ||= $diffinfo{'to_file'} || $diffinfo{'file'}; + $file_parent ||= $diffinfo{'from_file'} || $file_name; + $file_name ||= $diffinfo{'to_file'}; $hash_parent ||= $diffinfo{'from_id'}; $hash ||= $diffinfo{'to_id'}; From 6aa6f92fda47cc4ee5f599895e8a5a327fb6f9ab Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 1 Nov 2007 12:38:09 +0100 Subject: [PATCH 47/74] gitweb: Add 'status_str' to parse_difftree_raw_line output Add 'status_str' to diffinfo output, which stores status (also for merge commit) as a string. This allows for easy checking if there is given status among all for merge commit, e.g. $diffinfo->{'status_str'} =~ /D/; Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index b22f4be152..abb5a7d39f 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1990,7 +1990,7 @@ sub parse_difftree_raw_line { $res{'to_mode'} = $2; $res{'from_id'} = $3; $res{'to_id'} = $4; - $res{'status'} = $5; + $res{'status'} = $res{'status_str'} = $5; $res{'similarity'} = $6; if ($res{'status'} eq 'R' || $res{'status'} eq 'C') { # renamed or copied ($res{'from_file'}, $res{'to_file'}) = map { unquote($_) } split("\t", $7); @@ -2006,6 +2006,7 @@ sub parse_difftree_raw_line { $res{'to_mode'} = pop @{$res{'from_mode'}}; $res{'from_id'} = [ split(' ', $3) ]; $res{'to_id'} = pop @{$res{'from_id'}}; + $res{'status_str'} = $4; $res{'status'} = [ split('', $4) ]; $res{'to_file'} = unquote($5); } @@ -2821,7 +2822,7 @@ sub fill_from_file_info { sub is_deleted { my $diffinfo = shift; - return $diffinfo->{'to_id'} eq ('0' x 40); + return $diffinfo->{'status_str'} =~ /D/; } # does patch correspond to [previous] difftree raw line From fa9aff463da42feea68228ca51685cd9f4403e92 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 1 Nov 2007 14:23:16 +0100 Subject: [PATCH 48/74] gitweb: Remove CGI::Carp::set_programname() call from t9500 gitweb test It does appear to do nothing; gitweb is run as standalone program and not as CGI script in this test. This call caused problems later. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- t/t9500-gitweb-standalone-no-errors.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index f7bad5bb2f..1bf0988d9a 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -31,7 +31,6 @@ our \$projects_list = ""; our \$export_ok = ""; our \$strict_export = ""; -CGI::Carp::set_programname("gitweb/gitweb.cgi"); EOF cat >.git/description < Date: Thu, 1 Nov 2007 13:06:27 +0100 Subject: [PATCH 49/74] gitweb: Easier adding/changing parameters to current URL Add boolean option '-replay' to href() subroutine, which is used to generate links in gitweb. This option "replays" current URL, overriding it with provided parameters. It means that current value of each CGI parameter is used unless otherwise provided. This change is meant to make it easier to generate links which differ from current page URL only by one parameter, for example the same view but sorted by different column: href(-replay=>1, order=>"age") or view which differs by some option, e.g. in log views href(-replay=>1, extra_options=>"--no-merges") or alternate view of the same object, e.g. in the 'blob' view href(-replay=>1, action=>"blob_plain") Actual use of this functionality is left for later. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index abb5a7d39f..c93c546fbf 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -611,6 +611,15 @@ sub href(%) { ); my %mapping = @mapping; + if ($params{-replay}) { + while (my ($name, $symbol) = each %mapping) { + if (!exists $params{$name}) { + # to allow for multivalued params we use arrayref form + $params{$name} = [ $cgi->param($symbol) ]; + } + } + } + $params{'project'} = $project unless exists $params{'project'}; my ($use_pathinfo) = gitweb_check_feature('pathinfo'); From 7afd77bfc1b6881fd9e476274d3f08b793c292ed Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 1 Nov 2007 13:06:28 +0100 Subject: [PATCH 50/74] gitweb: Use href(-replay=>1, page=>...) to generate pagination links Use href(-replay=>1, page=>$page-1) and href(-replay=>1, page=>$page+1) to generate previous page and next page links. Generate next page link only once. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 48 ++++++++++++++++------------------------------ 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index c93c546fbf..d2adae31c8 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2518,7 +2518,7 @@ sub format_paging_nav { if ($page > 0) { $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>$action, hash=>$hash, page=>$page-1), + $cgi->a({-href => href(-replay=>1, page=>$page-1), -accesskey => "p", -title => "Alt-p"}, "prev"); } else { $paging_nav .= " ⋅ prev"; @@ -2526,7 +2526,7 @@ sub format_paging_nav { if ($nrevs >= (100 * ($page+1)-1)) { $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>$action, hash=>$hash, page=>$page+1), + $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); } else { $paging_nav .= " ⋅ next"; @@ -4448,7 +4448,7 @@ sub git_log { } if ($#commitlist >= 100) { print "
\n"; - print $cgi->a({-href => href(action=>"log", hash=>$hash, page=>$page+1), + print $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); print "
\n"; } @@ -5015,27 +5015,20 @@ sub git_history { file_name=>$file_name)}, "first"); $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, - file_name=>$file_name, page=>$page-1), + $cgi->a({-href => href(-replay=>1, page=>$page-1), -accesskey => "p", -title => "Alt-p"}, "prev"); } else { $paging_nav .= "first"; $paging_nav .= " ⋅ prev"; } - if ($#commitlist >= 100) { - $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, - file_name=>$file_name, page=>$page+1), - -accesskey => "n", -title => "Alt-n"}, "next"); - } else { - $paging_nav .= " ⋅ next"; - } my $next_link = ''; if ($#commitlist >= 100) { $next_link = - $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, - file_name=>$file_name, page=>$page+1), + $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); + $paging_nav .= " ⋅ $next_link"; + } else { + $paging_nav .= " ⋅ next"; } git_header_html(); @@ -5105,30 +5098,23 @@ sub git_search { searchtext=>$searchtext, searchtype=>$searchtype)}, "first"); $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>"search", hash=>$hash, - searchtext=>$searchtext, searchtype=>$searchtype, - page=>$page-1), + $cgi->a({-href => href(-replay=>1, page=>$page-1), -accesskey => "p", -title => "Alt-p"}, "prev"); } else { $paging_nav .= "first"; $paging_nav .= " ⋅ prev"; } - if ($#commitlist >= 100) { - $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>"search", hash=>$hash, - searchtext=>$searchtext, searchtype=>$searchtype, - page=>$page+1), - -accesskey => "n", -title => "Alt-n"}, "next"); - } else { - $paging_nav .= " ⋅ next"; - } my $next_link = ''; if ($#commitlist >= 100) { $next_link = - $cgi->a({-href => href(action=>"search", hash=>$hash, - searchtext=>$searchtext, searchtype=>$searchtype, - page=>$page+1), + $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); + $paging_nav .= " ⋅ $next_link"; + } else { + $paging_nav .= " ⋅ next"; + } + + if ($#commitlist >= 100) { } git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav); @@ -5327,7 +5313,7 @@ sub git_shortlog { my $next_link = ''; if ($#commitlist >= 100) { $next_link = - $cgi->a({-href => href(action=>"shortlog", hash=>$hash, page=>$page+1), + $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); } From a3823e5ad75498774a32850f44e8254b0e1c0901 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 1 Nov 2007 13:06:29 +0100 Subject: [PATCH 51/74] gitweb: Use href(-replay=>1, action=>...) to generate alternate views Use href(action=>..., -replay=>1) to generate links to alternate views of current page in the $formats_nav (bottom) part of page_nav navigation bar. This form is used only when all parameters are repeated, and when the replay form is shorter. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index d2adae31c8..ecb0113a79 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3911,11 +3911,11 @@ sub git_blame2 { or die_error(undef, "Open git-blame failed"); git_header_html(); my $formats_nav = - $cgi->a({-href => href(action=>"blob", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"blob", -replay=>1)}, "blob") . " | " . - $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, - "history") . + $cgi->a({-href => href(action=>"history", -replay=>1)}, + "history") . " | " . $cgi->a({-href => href(action=>"blame", file_name=>$file_name)}, "HEAD"); @@ -4191,18 +4191,15 @@ sub git_blob { if (defined $file_name) { if ($have_blame) { $formats_nav .= - $cgi->a({-href => href(action=>"blame", hash_base=>$hash_base, - hash=>$hash, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"blame", -replay=>1)}, "blame") . " | "; } $formats_nav .= - $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, - hash=>$hash, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"history", -replay=>1)}, "history") . " | " . - $cgi->a({-href => href(action=>"blob_plain", - hash=>$hash, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"blob_plain", -replay=>1)}, "raw") . " | " . $cgi->a({-href => href(action=>"blob", @@ -4210,7 +4207,8 @@ sub git_blob { "HEAD"); } else { $formats_nav .= - $cgi->a({-href => href(action=>"blob_plain", hash=>$hash)}, "raw"); + $cgi->a({-href => href(action=>"blob_plain", -replay=>1)}, + "raw"); } git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_header_div('commit', esc_html($co{'title'}), $hash_base); @@ -4273,8 +4271,7 @@ sub git_tree { my @views_nav = (); if (defined $file_name) { push @views_nav, - $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, - hash=>$hash, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"history", -replay=>1)}, "history"), $cgi->a({-href => href(action=>"tree", hash_base=>"HEAD", file_name=>$file_name)}, @@ -4742,10 +4739,7 @@ sub git_blobdiff { # header if ($format eq 'html') { my $formats_nav = - $cgi->a({-href => href(action=>"blobdiff_plain", - hash=>$hash, hash_parent=>$hash_parent, - hash_base=>$hash_base, hash_parent_base=>$hash_parent_base, - file_name=>$file_name, file_parent=>$file_parent)}, + $cgi->a({-href => href(action=>"blobdiff_plain", -replay=>1)}, "raw"); git_header_html(undef, $expires); if (defined $hash_base && (my %co = parse_commit($hash_base))) { @@ -4819,8 +4813,7 @@ sub git_commitdiff { my $formats_nav; if ($format eq 'html') { $formats_nav = - $cgi->a({-href => href(action=>"commitdiff_plain", - hash=>$hash, hash_parent=>$hash_parent)}, + $cgi->a({-href => href(action=>"commitdiff_plain", -replay=>1)}, "raw"); if (defined $hash_parent && From 44c637c8021e44253f0f8cb17391092e08b39e73 Mon Sep 17 00:00:00 2001 From: James Bowes Date: Thu, 1 Nov 2007 21:02:27 -0400 Subject: [PATCH 52/74] gc: use parse_options Signed-off-by: James Bowes Signed-off-by: Junio C Hamano --- builtin-gc.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/builtin-gc.c b/builtin-gc.c index 3a2ca4f901..c5bce893a6 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -12,11 +12,15 @@ #include "builtin.h" #include "cache.h" +#include "parse-options.h" #include "run-command.h" #define FAILED_RUN "failed to run %s" -static const char builtin_gc_usage[] = "git-gc [--prune] [--aggressive]"; +static const char * const builtin_gc_usage[] = { + "git-gc [options]", + NULL +}; static int pack_refs = 1; static int aggressive_window = -1; @@ -165,38 +169,34 @@ static int need_to_gc(void) int cmd_gc(int argc, const char **argv, const char *prefix) { - int i; int prune = 0; + int aggressive = 0; int auto_gc = 0; char buf[80]; + struct option builtin_gc_options[] = { + OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced loose objects"), + OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"), + OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"), + OPT_END() + }; + git_config(gc_config); if (pack_refs < 0) pack_refs = !is_bare_repository(); - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (!strcmp(arg, "--prune")) { - prune = 1; - continue; + argc = parse_options(argc, argv, builtin_gc_options, builtin_gc_usage, 0); + if (argc > 0) + usage_with_options(builtin_gc_usage, builtin_gc_options); + + if (aggressive) { + append_option(argv_repack, "-f", MAX_ADD); + if (aggressive_window > 0) { + sprintf(buf, "--window=%d", aggressive_window); + append_option(argv_repack, buf, MAX_ADD); } - if (!strcmp(arg, "--aggressive")) { - append_option(argv_repack, "-f", MAX_ADD); - if (aggressive_window > 0) { - sprintf(buf, "--window=%d", aggressive_window); - append_option(argv_repack, buf, MAX_ADD); - } - continue; - } - if (!strcmp(arg, "--auto")) { - auto_gc = 1; - continue; - } - break; } - if (i != argc) - usage(builtin_gc_usage); if (auto_gc) { /* From 4d00bda2aa9dd8cd6ec4015832b80eb1273d46d7 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 1 Nov 2007 23:26:04 -0400 Subject: [PATCH 53/74] make the pack index version configurable It is a good idea to use pack index version 2 all the time since it has proper protection against propagation of certain pack corruptions when repacking which is not possible with index version 1, as demonstrated in test t5302. Hence this config option. The default is still pack index version 1. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- Documentation/config.txt | 9 +++++++++ builtin-pack-objects.c | 6 ++++++ index-pack.c | 13 +++++++++++++ 3 files changed, 28 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index edf50cd211..0df004ea26 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -661,6 +661,15 @@ pack.threads:: machines. The required amount of memory for the delta search window is however multiplied by the number of threads. +pack.indexVersion:: + Specify the default pack index version. Valid values are 1 for + legacy pack index used by Git versions prior to 1.5.2, and 2 for + the new pack index with capabilities for packs larger than 4 GB + as well as proper protection against the repacking of corrupted + packs. Version 2 is selected and this config option ignored + whenever the corresponding pack is larger than 2 GB. Otherwise + the default is 1. + pull.octopus:: The default merge strategy to use when pulling multiple branches at once. diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 0be539ed7f..f4b90c1e4d 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1773,6 +1773,12 @@ static int git_pack_config(const char *k, const char *v) #endif return 0; } + if (!strcmp(k, "pack.indexversion")) { + pack_idx_default_version = git_config_int(k, v); + if (pack_idx_default_version > 2) + die("bad pack.indexversion=%d", pack_idx_default_version); + return 0; + } return git_default_config(k, v); } diff --git a/index-pack.c b/index-pack.c index db58e05041..c0bb78a8ed 100644 --- a/index-pack.c +++ b/index-pack.c @@ -683,6 +683,17 @@ 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) +{ + if (!strcmp(k, "pack.indexversion")) { + pack_idx_default_version = git_config_int(k, v); + if (pack_idx_default_version > 2) + die("bad pack.indexversion=%d", pack_idx_default_version); + return 0; + } + return git_default_config(k, v); +} + int main(int argc, char **argv) { int i, fix_thin_pack = 0; @@ -693,6 +704,8 @@ int main(int argc, char **argv) struct pack_idx_entry **idx_objects; unsigned char sha1[20]; + git_config(git_index_pack_config); + for (i = 1; i < argc; i++) { const char *arg = argv[i]; From 79814f425c00129dbdbdc3c99d04af52ccc58254 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 1 Nov 2007 23:43:24 -0400 Subject: [PATCH 54/74] pack-objects: get rid of an ugly cast ... when calling write_idx_file(). Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index f4b90c1e4d..d0ca165c9b 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -57,7 +57,7 @@ struct object_entry { * nice "minimum seek" order. */ static struct object_entry *objects; -static struct object_entry **written_list; +static struct pack_idx_entry **written_list; static uint32_t nr_objects, nr_alloc, nr_result, nr_written; static int non_empty; @@ -579,7 +579,7 @@ static off_t write_one(struct sha1file *f, e->idx.offset = 0; return 0; } - written_list[nr_written++] = e; + written_list[nr_written++] = &e->idx; /* make sure off_t is sufficiently large not to wrap */ if (offset > offset + size) @@ -607,7 +607,7 @@ static void write_pack_file(void) if (do_progress) start_progress(&progress_state, "Writing %u objects...", "", nr_result); - written_list = xmalloc(nr_objects * sizeof(struct object_entry *)); + written_list = xmalloc(nr_objects * sizeof(*written_list)); do { unsigned char sha1[20]; @@ -654,8 +654,8 @@ static void write_pack_file(void) umask(mode); mode = 0444 & ~mode; - idx_tmp_name = write_idx_file(NULL, - (struct pack_idx_entry **) written_list, nr_written, sha1); + idx_tmp_name = write_idx_file(NULL, written_list, + nr_written, sha1); snprintf(tmpname, sizeof(tmpname), "%s-%s.pack", base_name, sha1_to_hex(sha1)); if (adjust_perm(pack_tmp_name, mode)) @@ -677,7 +677,7 @@ static void write_pack_file(void) /* mark written objects as written to previous pack */ for (j = 0; j < nr_written; j++) { - written_list[j]->idx.offset = (off_t)-1; + written_list[j]->offset = (off_t)-1; } nr_remaining -= nr_written; } while (nr_remaining && i < nr_objects); From f88a545a94cb474d370ef97dd3694d09b6ac90c1 Mon Sep 17 00:00:00 2001 From: Simon Sasburg Date: Thu, 1 Nov 2007 23:57:45 +0100 Subject: [PATCH 55/74] Make mailsplit and mailinfo strip whitespace from the start of the input Signed-off-by: Simon Sasburg Signed-off-by: Junio C Hamano --- builtin-mailinfo.c | 6 ++++++ builtin-mailsplit.c | 6 ++++++ t/t5100/sample.mbox | 3 +++ 3 files changed, 15 insertions(+) diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index fb12248f82..2600847974 100644 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@ -915,6 +915,7 @@ static void handle_info(void) static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const char *msg, const char *patch) { + int peek; keep_subject = ks; metainfo_charset = encoding; fin = in; @@ -935,6 +936,11 @@ static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *)); s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *)); + do { + peek = fgetc(in); + } while (isspace(peek)); + ungetc(peek, in); + /* process the email header */ while (read_one_header_line(line, sizeof(line), fin)) check_header(line, sizeof(line), p_hdr_data, 1); diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c index 43fc373a15..74b04706f3 100644 --- a/builtin-mailsplit.c +++ b/builtin-mailsplit.c @@ -164,6 +164,7 @@ static int split_mbox(const char *file, const char *dir, int allow_bare, { char name[PATH_MAX]; int ret = -1; + int peek; FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r"); int file_done = 0; @@ -173,6 +174,11 @@ static int split_mbox(const char *file, const char *dir, int allow_bare, goto out; } + do { + peek = fgetc(f); + } while (isspace(peek)); + ungetc(peek, f); + if (fgets(buf, sizeof(buf), f) == NULL) { /* empty stdin is OK */ if (f != stdin) { diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox index b80c981c16..070c1661b9 100644 --- a/t/t5100/sample.mbox +++ b/t/t5100/sample.mbox @@ -1,3 +1,6 @@ + + + From nobody Mon Sep 17 00:00:00 2001 From: A U Thor Date: Fri, 9 Jun 2006 00:44:16 -0700 From 47ec79430dc6a60336bebdc0a45fb9f15129d0c7 Mon Sep 17 00:00:00 2001 From: Blake Ramsdell Date: Thu, 1 Nov 2007 19:38:22 -0700 Subject: [PATCH 56/74] transport.c: squelch a gcc 4.0.1 complaint about an uninitialized variable The variable is always set if it is going to be used; gcc just does not notice it. Signed-off-by: Blake Ramsdell Signed-off-by: Junio C Hamano --- transport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport.c b/transport.c index 5132d289da..d44fe7cee7 100644 --- a/transport.c +++ b/transport.c @@ -107,7 +107,7 @@ static void insert_packed_refs(const char *packed_refs, struct ref **list) return; for (;;) { - int cmp, len; + int cmp = cmp, len; if (!fgets(buffer, sizeof(buffer), f)) { fclose(f); From 7b55eee77e11da162fb197a79b72af375ed0f1d0 Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Fri, 2 Nov 2007 10:10:11 +0100 Subject: [PATCH 57/74] Remove escaping of '|' in manpage option sections The escaped were ending up verbatim in the generated documentation. Signed-off-by: Jonas Fonseca Signed-off-by: Junio C Hamano --- Documentation/git-filter-branch.txt | 2 +- Documentation/git-stripspace.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index 385ecc900f..895d750310 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -152,7 +152,7 @@ definition impossible to preserve signatures at any rate.) does this in the '.git-rewrite/' directory but you can override that choice by this parameter. --f\|--force:: +-f|--force:: `git filter-branch` refuses to start with an existing temporary directory or when there are already refs starting with 'refs/original/', unless forced. diff --git a/Documentation/git-stripspace.txt b/Documentation/git-stripspace.txt index 5212358306..f80526ba7e 100644 --- a/Documentation/git-stripspace.txt +++ b/Documentation/git-stripspace.txt @@ -16,7 +16,7 @@ Remove multiple empty lines, and empty lines at beginning and end. OPTIONS ------- --s\|--strip-comments:: +-s|--strip-comments:: In addition to empty lines, also strip lines starting with '#'. :: From d336fc096b450c01502e99fa17583d5bebf9aa24 Mon Sep 17 00:00:00 2001 From: Sergei Organov Date: Fri, 2 Nov 2007 20:12:57 +0300 Subject: [PATCH 58/74] Documentation: quote commit messages consistently. Documentation quotes commit messages 14 times with double-quotes, and 7 times with single-quotes. The patch turns everything to double-quotes. A nice side effect is that documentation becomes more Windoze-friendly as AFAIK single quotes won't work there. Signed-off-by: Sergei Organov Signed-off-by: Junio C Hamano --- Documentation/core-tutorial.txt | 8 ++++---- Documentation/everyday.txt | 4 ++-- Documentation/git-reset.txt | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt index df147b5e76..67064dd31a 100644 --- a/Documentation/core-tutorial.txt +++ b/Documentation/core-tutorial.txt @@ -833,7 +833,7 @@ that branch, and do some work there. ------------------------------------------------ $ git checkout mybranch $ echo "Work, work, work" >>hello -$ git commit -m 'Some work.' -i hello +$ git commit -m "Some work." -i hello ------------------------------------------------ Here, we just added another line to `hello`, and we used a shorthand for @@ -858,7 +858,7 @@ hasn't happened in the `master` branch at all. Then do ------------ $ echo "Play, play, play" >>hello $ echo "Lots of fun" >>example -$ git commit -m 'Some fun.' -i hello example +$ git commit -m "Some fun." -i hello example ------------ since the master branch is obviously in a much better mood. @@ -1613,8 +1613,8 @@ in both of them. You could merge in 'diff-fix' first and then 'commit-fix' next, like this: ------------ -$ git merge -m 'Merge fix in diff-fix' diff-fix -$ git merge -m 'Merge fix in commit-fix' commit-fix +$ git merge -m "Merge fix in diff-fix" diff-fix +$ git merge -m "Merge fix in commit-fix" commit-fix ------------ Which would result in: diff --git a/Documentation/everyday.txt b/Documentation/everyday.txt index 08c61b1f1a..ce7c170d69 100644 --- a/Documentation/everyday.txt +++ b/Documentation/everyday.txt @@ -109,7 +109,7 @@ $ tar zxf frotz.tar.gz $ cd frotz $ git-init $ git add . <1> -$ git commit -m 'import of frotz source tree.' +$ git commit -m "import of frotz source tree." $ git tag v2.43 <2> ------------ + @@ -300,7 +300,7 @@ $ git merge topic/one topic/two && git merge hold/linus <8> $ git checkout maint $ git cherry-pick master~4 <9> $ compile/test -$ git tag -s -m 'GIT 0.99.9x' v0.99.9x <10> +$ git tag -s -m "GIT 0.99.9x" v0.99.9x <10> $ git fetch ko && git show-branch master maint 'tags/ko-*' <11> $ git push ko <12> $ git push ko v0.99.9x <13> diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index 15e3aca9a1..87afa6f8da 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -157,7 +157,7 @@ need to get to the other branch for a quick bugfix. ------------ $ git checkout feature ;# you were working in "feature" branch and $ work work work ;# got interrupted -$ git commit -a -m 'snapshot WIP' <1> +$ git commit -a -m "snapshot WIP" <1> $ git checkout master $ fix fix fix $ git commit ;# commit with real log From 9e54dc6c12b14c12094c3a0f0b1e437148bbc0bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Fri, 2 Nov 2007 11:33:07 -0400 Subject: [PATCH 59/74] Remove unecessary hard-coding of EDITOR=':' VISUAL=':' in some test suites. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They are already set and exoprted by sourcing ./test-lib.sh in all test scripts. Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano --- t/t3501-revert-cherry-pick.sh | 4 ++-- t/t3901-i18n-patch.sh | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh index 552af1c4d2..2dbe04fb20 100755 --- a/t/t3501-revert-cherry-pick.sh +++ b/t/t3501-revert-cherry-pick.sh @@ -44,7 +44,7 @@ test_expect_success setup ' test_expect_success 'cherry-pick after renaming branch' ' git checkout rename2 && - EDITOR=: VISUAL=: git cherry-pick added && + git cherry-pick added && test -f opos && grep "Add extra line at the end" opos @@ -53,7 +53,7 @@ test_expect_success 'cherry-pick after renaming branch' ' test_expect_success 'revert after renaming branch' ' git checkout rename1 && - EDITOR=: VISUAL=: git revert added && + git revert added && test -f spoo && ! grep "Add extra line at the end" spoo diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh index 28e9e372f3..235f372832 100755 --- a/t/t3901-i18n-patch.sh +++ b/t/t3901-i18n-patch.sh @@ -154,7 +154,7 @@ test_expect_success 'cherry-pick(U/U)' ' git reset --hard master && git cherry-pick side^ && git cherry-pick side && - EDITOR=: VISUAL=: git revert HEAD && + git revert HEAD && check_encoding 3 ' @@ -169,7 +169,7 @@ test_expect_success 'cherry-pick(L/L)' ' git reset --hard master && git cherry-pick side^ && git cherry-pick side && - EDITOR=: VISUAL=: git revert HEAD && + git revert HEAD && check_encoding 3 8859 ' @@ -184,7 +184,7 @@ test_expect_success 'cherry-pick(U/L)' ' git reset --hard master && git cherry-pick side^ && git cherry-pick side && - EDITOR=: VISUAL=: git revert HEAD && + git revert HEAD && check_encoding 3 ' @@ -200,7 +200,7 @@ test_expect_success 'cherry-pick(L/U)' ' git reset --hard master && git cherry-pick side^ && git cherry-pick side && - EDITOR=: VISUAL=: git revert HEAD && + git revert HEAD && check_encoding 3 8859 ' From f45e867b1a60a2023c18719888bb52965ade7192 Mon Sep 17 00:00:00 2001 From: Jonathan del Strother Date: Wed, 17 Oct 2007 10:31:35 +0100 Subject: [PATCH 60/74] Fixing path quoting in git-rebase git-rebase used to fail when run from a path containing a space. Signed-off-by: Jonathan del Strother Signed-off-by: Shawn O. Pearce --- git-rebase.sh | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/git-rebase.sh b/git-rebase.sh index 058fcacb7e..b0c8ac1c8d 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -59,7 +59,7 @@ continue_merge () { die "$RESOLVEMSG" fi - cmt=`cat $dotest/current` + cmt=`cat "$dotest/current"` if ! git diff-index --quiet HEAD then if ! git-commit -C "$cmt" @@ -84,14 +84,14 @@ continue_merge () { } call_merge () { - cmt="$(cat $dotest/cmt.$1)" + cmt="$(cat "$dotest/cmt.$1")" echo "$cmt" > "$dotest/current" hd=$(git rev-parse --verify HEAD) cmt_name=$(git symbolic-ref HEAD) - msgnum=$(cat $dotest/msgnum) - end=$(cat $dotest/end) + msgnum=$(cat "$dotest/msgnum") + end=$(cat "$dotest/end") eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"' - eval GITHEAD_$hd='"$(cat $dotest/onto_name)"' + eval GITHEAD_$hd='$(cat "$dotest/onto_name")' export GITHEAD_$cmt GITHEAD_$hd git-merge-$strategy "$cmt^" -- "$hd" "$cmt" rv=$? @@ -140,10 +140,10 @@ do } if test -d "$dotest" then - prev_head="`cat $dotest/prev_head`" - end="`cat $dotest/end`" - msgnum="`cat $dotest/msgnum`" - onto="`cat $dotest/onto`" + prev_head=$(cat "$dotest/prev_head") + end=$(cat "$dotest/end") + msgnum=$(cat "$dotest/msgnum") + onto=$(cat "$dotest/onto") continue_merge while test "$msgnum" -le "$end" do @@ -160,11 +160,11 @@ do if test -d "$dotest" then git rerere clear - prev_head="`cat $dotest/prev_head`" - end="`cat $dotest/end`" - msgnum="`cat $dotest/msgnum`" + prev_head=$(cat "$dotest/prev_head") + end=$(cat "$dotest/end") + msgnum=$(cat "$dotest/msgnum") msgnum=$(($msgnum + 1)) - onto="`cat $dotest/onto`" + onto=$(cat "$dotest/onto") while test "$msgnum" -le "$end" do call_merge "$msgnum" From 6232b3438d127def8cc0612e45422347578c6102 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 2 Nov 2007 17:25:24 -0700 Subject: [PATCH 61/74] cherry-pick/revert -m: add tests This adds a new test to check cherry-pick/revert of a merge commit. Signed-off-by: Junio C Hamano --- t/t3502-cherry-pick-merge.sh | 123 +++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100755 t/t3502-cherry-pick-merge.sh diff --git a/t/t3502-cherry-pick-merge.sh b/t/t3502-cherry-pick-merge.sh new file mode 100755 index 0000000000..3274c6141b --- /dev/null +++ b/t/t3502-cherry-pick-merge.sh @@ -0,0 +1,123 @@ +#!/bin/sh + +test_description='cherry picking and reverting a merge + + b---c + / / + initial---a + +' + +. ./test-lib.sh + +test_expect_success setup ' + + >A && + >B && + git add A B && + git commit -m "Initial" && + git tag initial && + git branch side && + echo new line >A && + git commit -m "add line to A" A && + git tag a && + git checkout side && + echo new line >B && + git commit -m "add line to B" B && + git tag b && + git checkout master && + git merge side && + git tag c + +' + +test_expect_success 'cherry-pick a non-merge with -m should fail' ' + + git reset --hard && + git checkout a^0 && + ! git cherry-pick -m 1 b && + git diff --exit-code a + +' + +test_expect_success 'cherry pick a merge without -m should fail' ' + + git reset --hard && + git checkout a^0 && + ! git cherry-pick c && + git diff --exit-code a + +' + +test_expect_success 'cherry pick a merge (1)' ' + + git reset --hard && + git checkout a^0 && + git cherry-pick -m 1 c && + git diff --exit-code c + +' + +test_expect_success 'cherry pick a merge (2)' ' + + git reset --hard && + git checkout b^0 && + git cherry-pick -m 2 c && + git diff --exit-code c + +' + +test_expect_success 'cherry pick a merge relative to nonexistent parent should fail' ' + + git reset --hard && + git checkout b^0 && + ! git cherry-pick -m 3 c + +' + +test_expect_success 'revert a non-merge with -m should fail' ' + + git reset --hard && + git checkout c^0 && + ! git revert -m 1 b && + git diff --exit-code c + +' + +test_expect_success 'revert a merge without -m should fail' ' + + git reset --hard && + git checkout c^0 && + ! git revert c && + git diff --exit-code c + +' + +test_expect_success 'revert a merge (1)' ' + + git reset --hard && + git checkout c^0 && + git revert -m 1 c && + git diff --exit-code a + +' + +test_expect_success 'revert a merge (2)' ' + + git reset --hard && + git checkout c^0 && + git revert -m 2 c && + git diff --exit-code b + +' + +test_expect_success 'revert a merge relative to nonexistent parent should fail' ' + + git reset --hard && + git checkout c^0 && + ! git revert -m 3 c && + git diff --exit-code c + +' + +test_done From aacb8f10a70c07dfe1461684f098313f0edb371f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 2 Nov 2007 17:55:31 -0700 Subject: [PATCH 62/74] test format-patch -s: make sure MIME content type is shown as needed Signed-off-by: Junio C Hamano --- t/t4021-format-patch-signer-mime.sh | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100755 t/t4021-format-patch-signer-mime.sh diff --git a/t/t4021-format-patch-signer-mime.sh b/t/t4021-format-patch-signer-mime.sh new file mode 100755 index 0000000000..67a70fadab --- /dev/null +++ b/t/t4021-format-patch-signer-mime.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +test_description='format-patch -s should force MIME encoding as needed' + +. ./test-lib.sh + +test_expect_success setup ' + + >F && + git add F && + git commit -m initial && + echo new line >F && + + test_tick && + git commit -m "This adds some lines to F" F + +' + +test_expect_success 'format normally' ' + + git format-patch --stdout -1 >output && + ! grep Content-Type output + +' + +test_expect_success 'format with signoff without funny signer name' ' + + git format-patch -s --stdout -1 >output && + ! grep Content-Type output + +' + +test_expect_success 'format with non ASCII signer name' ' + + GIT_COMMITTER_NAME="$B$O$^$N(B $B$U$K$*$&(B" \ + git format-patch -s --stdout -1 >output && + grep Content-Type output + +' + +test_done + From e9c34c233f2e838c334f6e8a807ebc6a988e9509 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 3 Nov 2007 00:41:18 +0100 Subject: [PATCH 63/74] gitweb: Add tests for overriding gitweb config with repo config Make blame view and snapshot support overridable by repository config. Test tree view with both features disabled, and with both features enabled. Test with features enabled also tests multiple formats snapshot support (in tree view). Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- t/t9500-gitweb-standalone-no-errors.sh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index 1bf0988d9a..35fff3ddba 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -557,4 +557,27 @@ test_expect_success \ 'gitweb_run "p=.git;a=tree;opt=--no-merges"' test_debug 'cat gitweb.log' +# ---------------------------------------------------------------------- +# gitweb config and repo config + +cat >>gitweb_config.perl < Date: Sat, 3 Nov 2007 00:41:19 +0100 Subject: [PATCH 64/74] gitweb: Read repo config using 'git config -z -l' Change git_get_project_config to run git-config only once per repository, without changing its signature (its calling convention). This means for example that it returns 'true' or 'false' when called with second argument '--bool', and not true or false value. Instead of calling 'git config [] --get gitweb.' once for each config variable, call 'git config -z -l' only once, parsing and saving its output to %config variable. This makes possible to add new per repository configuration without paying cost of forking once per variable checked. We can now allow repository description and repository URLs to be stored in config file without badly affecting gitweb performance. For now only configuration variables for 'gitweb' section are stored. Multiple values for single configuration variable are stored as anonymous array reference; configuration variable with no value is stored as undef. Converting configuration variable values to boolean or integer value are done in Perl. Results differ from git-config in the fact that no conversion error is ever raised. For boolean values no value, 'true' (any case) and 'false' (any case) are considered true, numbers are true if not zero; all other values (even invalid for bool) are considered false. For integer values value suffix of 'k', 'm', or 'g' following decimal number will cause the value to be multiplied by 1024, 1048576, or 1073741824; other values are returned as-is, only whitespace stripped. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 115 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 108 insertions(+), 7 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index ecb0113a79..e94ff96337 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1432,20 +1432,121 @@ sub git_get_type { return $type; } +# repository configuration +our $config_file = ''; +our %config; + +# store multiple values for single key as anonymous array reference +# single values stored directly in the hash, not as [ ] +sub hash_set_multi { + my ($hash, $key, $value) = @_; + + if (!exists $hash->{$key}) { + $hash->{$key} = $value; + } elsif (!ref $hash->{$key}) { + $hash->{$key} = [ $hash->{$key}, $value ]; + } else { + push @{$hash->{$key}}, $value; + } +} + +# return hash of git project configuration +# optionally limited to some section, e.g. 'gitweb' +sub git_parse_project_config { + my $section_regexp = shift; + my %config; + + local $/ = "\0"; + + open my $fh, "-|", git_cmd(), "config", '-z', '-l', + or return; + + while (my $keyval = <$fh>) { + chomp $keyval; + my ($key, $value) = split(/\n/, $keyval, 2); + + hash_set_multi(\%config, $key, $value) + if (!defined $section_regexp || $key =~ /^(?:$section_regexp)\./o); + } + close $fh; + + return %config; +} + +# convert config value to boolean, 'true' or 'false' +# no value, number > 0, 'true' and 'yes' values are true +# rest of values are treated as false (never as error) +sub config_to_bool { + my $val = shift; + + # strip leading and trailing whitespace + $val =~ s/^\s+//; + $val =~ s/\s+$//; + + return (!defined $val || # section.key + ($val =~ /^\d+$/ && $val) || # section.key = 1 + ($val =~ /^(?:true|yes)$/i)); # section.key = true +} + +# convert config value to simple decimal number +# an optional value suffix of 'k', 'm', or 'g' will cause the value +# to be multiplied by 1024, 1048576, or 1073741824 +sub config_to_int { + my $val = shift; + + # strip leading and trailing whitespace + $val =~ s/^\s+//; + $val =~ s/\s+$//; + + if (my ($num, $unit) = ($val =~ /^([0-9]*)([kmg])$/i)) { + $unit = lc($unit); + # unknown unit is treated as 1 + return $num * ($unit eq 'g' ? 1073741824 : + $unit eq 'm' ? 1048576 : + $unit eq 'k' ? 1024 : 1); + } + return $val; +} + +# convert config value to array reference, if needed +sub config_to_multi { + my $val = shift; + + return ref($val) ? $val : [ $val ]; +} + sub git_get_project_config { my ($key, $type) = @_; + # key sanity check return unless ($key); $key =~ s/^gitweb\.//; return if ($key =~ m/\W/); - my @x = (git_cmd(), 'config'); - if (defined $type) { push @x, $type; } - push @x, "--get"; - push @x, "gitweb.$key"; - my $val = qx(@x); - chomp $val; - return ($val); + # type sanity check + if (defined $type) { + $type =~ s/^--//; + $type = undef + unless ($type eq 'bool' || $type eq 'int'); + } + + # get config + if (!defined $config_file || + $config_file ne "$git_dir/config") { + %config = git_parse_project_config('gitweb'); + $config_file = "$git_dir/config"; + } + + # ensure given type + if (!defined $type) { + return $config{"gitweb.$key"}; + } elsif ($type eq 'bool') { + # backward compatibility: 'git config --bool' returns true/false + return config_to_bool($config{"gitweb.$key"}) ? 'true' : 'false'; + } elsif ($type eq 'int') { + return config_to_int($config{"gitweb.$key"}); + } + return $config{"gitweb.$key"}; } # get hash of given path at given ref From 0e121a2cd42d28bc4034feedf8a13c5a91f85bd3 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 3 Nov 2007 00:41:20 +0100 Subject: [PATCH 65/74] gitweb: Use config file for repository description and URLs Allow to use configuration variable gitweb.description for repository description if there is no $GIT_DIR/description file, and multivalued configuration variable gitweb.url for URLs of a project (to clone or fetch from) if there is no $GIT_DIR/cloneurl file. While repository description is shown in the projects list page, so it is better to use file and not config variable for performance, it is I think better to use gitweb.url for URLs (as it is shown only on project summary page). Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index e94ff96337..759dff1cce 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1606,7 +1606,9 @@ sub git_get_path_by_hash { sub git_get_project_description { my $path = shift; - open my $fd, "$projectroot/$path/description" or return undef; + $git_dir = "$projectroot/$path"; + open my $fd, "$projectroot/$path/description" + or return git_get_project_config('description'); my $descr = <$fd>; close $fd; if (defined $descr) { @@ -1618,7 +1620,11 @@ sub git_get_project_description { sub git_get_project_url_list { my $path = shift; - open my $fd, "$projectroot/$path/cloneurl" or return; + $git_dir = "$projectroot/$path"; + open my $fd, "$projectroot/$path/cloneurl" + or return wantarray ? + @{ config_to_multi(git_get_project_config('url')) } : + config_to_multi(git_get_project_config('url')); my @git_project_url_list = map { chomp; $_ } <$fd>; close $fd; From 165f390250874b32ed8158150fe49d574297c9f9 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Sat, 3 Nov 2007 01:32:48 -0400 Subject: [PATCH 66/74] git-fetch: more terse fetch output This makes the fetch output much more terse and prettier on a 80 column display, based on a consensus reached on the mailing list. Here's an example output: Receiving objects: 100% (5439/5439), 1.60 MiB | 636 KiB/s, done. Resolving deltas: 100% (4604/4604), done. From git://git.kernel.org/pub/scm/git/git ! [rejected] html -> origin/html (non fast forward) 136e631..f45e867 maint -> origin/maint (fast forward) 9850e2e..44dd7e0 man -> origin/man (fast forward) 3e4bb08..e3d6d56 master -> origin/master (fast forward) fa3665c..536f64a next -> origin/next (fast forward) + 4f6d9d6...768326f pu -> origin/pu (forced update) * [new branch] todo -> origin/todo Some portions of this patch have been extracted from earlier proposals by Jeff King and Shawn Pearce. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-fetch.c | 114 +++++++++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 54 deletions(-) diff --git a/builtin-fetch.c b/builtin-fetch.c index 003ed76d16..960b1dae89 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -131,12 +131,6 @@ static struct ref *get_ref_map(struct transport *transport, return ref_map; } -static void show_new(enum object_type type, unsigned char *sha1_new) -{ - fprintf(stderr, " %s: %s\n", typename(type), - find_unique_abbrev(sha1_new, DEFAULT_ABBREV)); -} - static int s_update_ref(const char *action, struct ref *ref, int check_old) @@ -157,34 +151,38 @@ static int s_update_ref(const char *action, return 0; } +#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3) + static int update_local_ref(struct ref *ref, - const char *note, - int verbose) + const char *remote, + int verbose, + char *display) { - char oldh[41], newh[41]; struct commit *current = NULL, *updated; enum object_type type; struct branch *current_branch = branch_get(NULL); + const char *pretty_ref = ref->name + ( + !prefixcmp(ref->name, "refs/heads/") ? 11 : + !prefixcmp(ref->name, "refs/tags/") ? 10 : + !prefixcmp(ref->name, "refs/remotes/") ? 13 : + 0); + *display = 0; type = sha1_object_info(ref->new_sha1, NULL); if (type < 0) die("object %s not found", sha1_to_hex(ref->new_sha1)); if (!*ref->name) { /* Not storing */ - if (verbose) { - fprintf(stderr, "* fetched %s\n", note); - show_new(type, ref->new_sha1); - } + if (verbose) + sprintf(display, "* branch %s -> FETCH_HEAD", remote); return 0; } if (!hashcmp(ref->old_sha1, ref->new_sha1)) { - if (verbose) { - fprintf(stderr, "* %s: same as %s\n", - ref->name, note); - show_new(type, ref->new_sha1); - } + if (verbose) + sprintf(display, "= %-*s %s -> %s", SUMMARY_WIDTH, + "[up to date]", remote, pretty_ref); return 0; } @@ -196,63 +194,65 @@ static int update_local_ref(struct ref *ref, * If this is the head, and it's not okay to update * the head, and the old value of the head isn't empty... */ - fprintf(stderr, - " * %s: Cannot fetch into the current branch.\n", - ref->name); + sprintf(display, "! %-*s %s -> %s (can't fetch in current branch)", + SUMMARY_WIDTH, "[rejected]", remote, pretty_ref); return 1; } if (!is_null_sha1(ref->old_sha1) && !prefixcmp(ref->name, "refs/tags/")) { - fprintf(stderr, "* %s: updating with %s\n", - ref->name, note); - show_new(type, ref->new_sha1); + sprintf(display, "- %-*s %s -> %s", + SUMMARY_WIDTH, "[tag update]", remote, pretty_ref); return s_update_ref("updating tag", ref, 0); } current = lookup_commit_reference_gently(ref->old_sha1, 1); updated = lookup_commit_reference_gently(ref->new_sha1, 1); if (!current || !updated) { - char *msg; - if (!strncmp(ref->name, "refs/tags/", 10)) + const char *msg; + const char *what; + if (!strncmp(ref->name, "refs/tags/", 10)) { msg = "storing tag"; - else + what = "[new tag]"; + } + else { msg = "storing head"; - fprintf(stderr, "* %s: storing %s\n", - ref->name, note); - show_new(type, ref->new_sha1); + what = "[new branch]"; + } + + sprintf(display, "* %-*s %s -> %s", + SUMMARY_WIDTH, what, remote, pretty_ref); return s_update_ref(msg, ref, 0); } - strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); - strcpy(newh, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); - if (in_merge_bases(current, &updated, 1)) { - fprintf(stderr, "* %s: fast forward to %s\n", - ref->name, note); - fprintf(stderr, " old..new: %s..%s\n", oldh, newh); + char quickref[83]; + strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); + strcat(quickref, ".."); + strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); + sprintf(display, " %-*s %s -> %s (fast forward)", + SUMMARY_WIDTH, quickref, remote, pretty_ref); return s_update_ref("fast forward", ref, 1); - } - if (!force && !ref->force) { - fprintf(stderr, - "* %s: not updating to non-fast forward %s\n", - ref->name, note); - fprintf(stderr, - " old...new: %s...%s\n", oldh, newh); + } else if (force || ref->force) { + char quickref[84]; + strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); + strcat(quickref, "..."); + strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); + sprintf(display, "+ %-*s %s -> %s (forced update)", + SUMMARY_WIDTH, quickref, remote, pretty_ref); + return s_update_ref("forced-update", ref, 1); + } else { + sprintf(display, "! %-*s %s -> %s (non fast forward)", + SUMMARY_WIDTH, "[rejected]", remote, pretty_ref); return 1; } - fprintf(stderr, - "* %s: forcing update to non-fast forward %s\n", - ref->name, note); - fprintf(stderr, " old...new: %s...%s\n", oldh, newh); - return s_update_ref("forced-update", ref, 1); } static void store_updated_refs(const char *url, struct ref *ref_map) { FILE *fp; struct commit *commit; - int url_len, i, note_len; + int url_len, i, note_len, shown_url = 0; char note[1024]; const char *what, *kind; struct ref *rm; @@ -315,8 +315,17 @@ static void store_updated_refs(const char *url, struct ref *ref_map) rm->merge ? "" : "not-for-merge", note); - if (ref) - update_local_ref(ref, note, verbose); + if (ref) { + update_local_ref(ref, what, verbose, note); + if (*note) { + if (!shown_url) { + fprintf(stderr, "From %.*s\n", + url_len, url); + shown_url = 1; + } + fprintf(stderr, " %s\n", note); + } + } } fclose(fp); } @@ -376,9 +385,6 @@ static struct ref *find_non_local_tags(struct transport *transport, if (!path_list_has_path(&existing_refs, ref_name) && !path_list_has_path(&new_refs, ref_name) && lookup_object(ref->old_sha1)) { - fprintf(stderr, "Auto-following %s\n", - ref_name); - path_list_insert(ref_name, &new_refs); rm = alloc_ref(strlen(ref_name) + 1); From 00ae82895e7f30e52ff12edc1076409c7c53d99e Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 2 Nov 2007 22:39:44 -0400 Subject: [PATCH 67/74] errors: "strict subset" -> "ancestor" The term "ancestor" is a bit more intuitive (and more consistent with the documentation) than the term "strict subset". Also, remove superfluous "ref", capitalize, and add some carriage returns, changing: error: remote 'refs/heads/master' is not a strict subset of local ref 'refs/heads/master'. maybe you are not up-to-date and need to pull first? error: failed to push to 'ssh://linux-nfs.org/~bfields/exports/git.git' to: error: remote 'refs/heads/master' is not an ancestor of local 'refs/heads/master'. Maybe you are not up-to-date and need to pull first? error: failed to push to 'ssh://linux-nfs.org/~bfields/exports/git.git' Signed-off-by: J. Bruce Fields Signed-off-by: Junio C Hamano --- builtin-branch.c | 2 +- http-push.c | 15 ++++++++++----- send-pack.c | 6 +++--- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index d6d5cff6b8..fbd90e42d1 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -148,7 +148,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) if (!force && !in_merge_bases(rev, &head_rev, 1)) { - error("The branch '%s' is not a strict subset of " + error("The branch '%s' is not an ancestor of " "your current HEAD.\n" "If you are sure you want to delete it, " "run 'git branch -D %s'.", argv[i], argv[i]); diff --git a/http-push.c b/http-push.c index c02a3af634..9314621a11 100644 --- a/http-push.c +++ b/http-push.c @@ -2241,7 +2241,11 @@ static int delete_remote_branch(char *pattern, int force) /* Remote branch must be an ancestor of remote HEAD */ if (!verify_merge_base(head_sha1, remote_ref->old_sha1)) { - return error("The branch '%s' is not a strict subset of your current HEAD.\nIf you are sure you want to delete it, run:\n\t'git http-push -D %s %s'", remote_ref->name, remote->url, pattern); + return error("The branch '%s' is not an ancestor " + "of your current HEAD.\n" + "If you are sure you want to delete it," + " run:\n\t'git http-push -D %s %s'", + remote_ref->name, remote->url, pattern); } } @@ -2417,16 +2421,17 @@ int main(int argc, char **argv) if (!has_sha1_file(ref->old_sha1) || !ref_newer(ref->peer_ref->new_sha1, ref->old_sha1)) { - /* We do not have the remote ref, or + /* + * We do not have the remote ref, or * we know that the remote ref is not * an ancestor of what we are trying to * push. Either way this can be losing * commits at the remote end and likely * we were not up to date to begin with. */ - error("remote '%s' is not a strict " - "subset of local ref '%s'. " - "maybe you are not up-to-date and " + error("remote '%s' is not an ancestor of\n" + "local '%s'.\n" + "Maybe you are not up-to-date and " "need to pull first?", ref->name, ref->peer_ref->name); diff --git a/send-pack.c b/send-pack.c index 5e127a1b7b..b74fd454f2 100644 --- a/send-pack.c +++ b/send-pack.c @@ -297,9 +297,9 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha * commits at the remote end and likely * we were not up to date to begin with. */ - error("remote '%s' is not a strict " - "subset of local ref '%s'. " - "maybe you are not up-to-date and " + error("remote '%s' is not an ancestor of\n" + " local '%s'.\n" + " Maybe you are not up-to-date and " "need to pull first?", ref->name, ref->peer_ref->name); From a0554224a285b73b87fe1c95dbdff469561b182f Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Sat, 3 Nov 2007 11:55:02 +0000 Subject: [PATCH 68/74] git-cvsimport: really convert underscores in branch names to dots with -u The documentation states for the -u option that underscores in tag and branch names are converted to dots, but this was actually implemented for the tag names only. Kurt Roeckx reported this through http://bugs.debian.org/446495 Signed-off-by: Gerrit Pape 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 2954fb846e..e4bc2b54f6 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -818,6 +818,7 @@ while () { $state = 4; } elsif ($state == 4 and s/^Branch:\s+//) { s/\s+$//; + tr/_/\./ if ( $opt_u ); s/[\/]/$opt_s/g; $branch = $_; $state = 5; From b92565dc5c505c3d01f3219fb7f61ebc70630a2c Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Sat, 3 Nov 2007 13:22:53 +0100 Subject: [PATCH 69/74] Delay pager setup in git blame This avoids to launch the pager when git blame fails for any reason. Signed-off-by: Mike Hommey Signed-off-by: Junio C Hamano --- builtin-blame.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin-blame.c b/builtin-blame.c index dc88a953a5..e17b03d876 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -2231,9 +2231,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix) argv[unk++] = arg; } - if (!incremental) - setup_pager(); - if (!blame_move_score) blame_move_score = BLAME_DEFAULT_MOVE_SCORE; if (!blame_copy_score) @@ -2427,6 +2424,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix) read_mailmap(&mailmap, ".mailmap", NULL); + if (!incremental) + setup_pager(); + assign_blame(&sb, &revs, opt); if (incremental) From 4b7bbdd14c6b926fbb0dbd203ce02e150b308694 Mon Sep 17 00:00:00 2001 From: Steven Grimm Date: Sat, 3 Nov 2007 19:26:54 -0700 Subject: [PATCH 70/74] builtin-fetch: Add "-q" as a synonym for "--quiet" "-q" is the very first option described in the git-fetch manpage, and it isn't supported. Signed-off-by: Steven Grimm Signed-off-by: Junio C Hamano --- builtin-fetch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-fetch.c b/builtin-fetch.c index 003ed76d16..8e790552cd 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -517,7 +517,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) depth = argv[i]; continue; } - if (!strcmp(arg, "--quiet")) { + if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) { quiet = 1; continue; } From aa807bc2663d69d6adb1d823231ba4b6d30d4860 Mon Sep 17 00:00:00 2001 From: Benoit Sigoure Date: Sat, 3 Nov 2007 19:53:34 +0100 Subject: [PATCH 71/74] git-svn: sort the options in the --help message. "git svn --help" gave options in the order they were found in a Perl hash, which meant "randomly" to humans. Signed-off-by: Benoit Sigoure Acked-by: Eric Wong 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 22bb47b34d..4900f57f18 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -252,7 +252,7 @@ Usage: $0 [options] [arguments]\n next if $cmd && $cmd ne $_; next if /^multi-/; # don't show deprecated commands print $fd ' ',pack('A17',$_),$cmd{$_}->[1],"\n"; - foreach (keys %{$cmd{$_}->[2]}) { + foreach (sort keys %{$cmd{$_}->[2]}) { # mixed-case options are for .git/config only next if /[A-Z]/ && /^[a-z]+$/i; # prints out arguments as they should be passed: From ee787400de25ed419f40e70698ba35db475b2d61 Mon Sep 17 00:00:00 2001 From: David D Kilzer Date: Sat, 3 Nov 2007 07:04:52 -0700 Subject: [PATCH 72/74] RelNotes-1.5.3.5: fix typo Signed-off-by: David D Kilzer Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.3.5.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/RelNotes-1.5.3.5.txt b/Documentation/RelNotes-1.5.3.5.txt index 4e46d2c2a2..f99a2cd650 100644 --- a/Documentation/RelNotes-1.5.3.5.txt +++ b/Documentation/RelNotes-1.5.3.5.txt @@ -63,8 +63,8 @@ Fixes since v1.5.3.4 * Git segfaulted when reading an invalid .gitattributes file. Fixed. - * post-receive-email example hook fixed was fixed for - non-fast-forward updates. + * post-receive-email example hook was fixed for non-fast-forward + updates. * Documentation updates for supported (but previously undocumented) options of "git-archive" and "git-reflog". From 19391c371cec06da6cca45dbe3c212bd479cc48c Mon Sep 17 00:00:00 2001 From: Heikki Orsila Date: Thu, 1 Nov 2007 16:21:39 +0200 Subject: [PATCH 73/74] git-clone: honor "--" to end argument parsing Signed-off-by: Heikki Orsila --- Documentation/git-clone.txt | 2 +- git-clone.sh | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index cca14d6b5d..14e58f3866 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -12,7 +12,7 @@ SYNOPSIS 'git-clone' [--template=] [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [-o ] [-u ] [--reference ] - [--depth ] [] + [--depth ] [--] [] DESCRIPTION ----------- diff --git a/git-clone.sh b/git-clone.sh index 0ea3c24f59..3f00693608 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -14,7 +14,7 @@ die() { } usage() { - die "Usage: $0 [--template=] [--reference ] [--bare] [-l [-s]] [-q] [-u ] [--origin ] [--depth ] [-n] []" + die "Usage: $0 [--template=] [--reference ] [--bare] [-l [-s]] [-q] [-u ] [--origin ] [--depth ] [-n] [--] []" } get_repo_base() { @@ -160,6 +160,9 @@ while *,--depth) shift depth="--depth=$1";; + *,--) + shift + break ;; *,-*) usage ;; *) break ;; esac From 2e7a9785c27f00a8f7a06edc1d4c9b2f3fa2eeb9 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 3 Nov 2007 13:12:17 +0000 Subject: [PATCH 74/74] git-reset: do not be confused if there is nothing to reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The purpose of the function update_index_from_diff() (which is the callback function we give do_diff_cache()) is to update those index entries which differ from the given commit. Since do_diff_cache() plays games with the in-memory index, this function discarded the cache and reread it. Then, back in the function read_from_tree() we wrote the index. Of course, this broke down when there were no changes and update_index_from_diff() was not called, and therefore the mangled index was not discarded. The solution is to move the index writing into the function update_index_from_diff(). Noticed by Björn Steinbrink. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-reset.c | 24 ++++++++++++++++++------ t/t7102-reset.sh | 7 +++++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/builtin-reset.c b/builtin-reset.c index e1dc31e0eb..5467e36c73 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -113,10 +113,17 @@ static int update_index_refresh(void) return run_command_v_opt(argv_update_index, RUN_GIT_CMD); } +struct update_cb_data { + int index_fd; + struct lock_file *lock; + int exit_code; +}; + static void update_index_from_diff(struct diff_queue_struct *q, struct diff_options *opt, void *data) { int i; + struct update_cb_data *cb = data; /* do_diff_cache() mangled the index */ discard_cache(); @@ -133,29 +140,34 @@ static void update_index_from_diff(struct diff_queue_struct *q, } else remove_file_from_cache(one->path); } + + cb->exit_code = write_cache(cb->index_fd, active_cache, active_nr) || + close(cb->index_fd) || + commit_locked_index(cb->lock); } static int read_from_tree(const char *prefix, const char **argv, unsigned char *tree_sha1) { - struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); - int index_fd; struct diff_options opt; + struct update_cb_data cb; memset(&opt, 0, sizeof(opt)); diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt); opt.output_format = DIFF_FORMAT_CALLBACK; opt.format_callback = update_index_from_diff; + opt.format_callback_data = &cb; - index_fd = hold_locked_index(lock, 1); + cb.lock = xcalloc(1, sizeof(struct lock_file)); + cb.index_fd = hold_locked_index(cb.lock, 1); + cb.exit_code = 0; read_cache(); if (do_diff_cache(tree_sha1, &opt)) return 1; diffcore_std(&opt); diff_flush(&opt); - return write_cache(index_fd, active_cache, active_nr) || - close(index_fd) || - commit_locked_index(lock); + + return cb.exit_code; } static void prepend_reflog_action(const char *action, char *buf, size_t size) diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index f64b1cbf75..cea9afb764 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -402,4 +402,11 @@ test_expect_success 'test resetting the index at give paths' ' ' +test_expect_success 'resetting an unmodified path is a no-op' ' + git reset --hard && + git reset -- file1 && + git diff-files --exit-code && + git diff-index --cached --exit-code HEAD +' + test_done