From c04c4e5708c9ad3dc534ff50ad8f28de5a7cfbff Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 5 Jul 2006 18:12:12 -0700 Subject: upload-pack: minor clean-up in multi-ack logic No changes to what it does, but separating the codepath clearly with if ... else if ... chain makes it easier to follow. Signed-off-by: Junio C Hamano diff --git a/upload-pack.c b/upload-pack.c index bbd6bd6..35c7ecb 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -351,7 +351,8 @@ static int got_sha1(char *hex, unsigned char *sha1) static int get_common_commits(void) { static char line[1000]; - unsigned char sha1[20], last_sha1[20]; + unsigned char sha1[20]; + char hex[41], last_hex[41]; int len; track_object_refs = 0; @@ -368,21 +369,22 @@ static int get_common_commits(void) } len = strip(line, len); if (!strncmp(line, "have ", 5)) { - if (got_sha1(line+5, sha1) && - (multi_ack || have_obj.nr == 1)) { - packet_write(1, "ACK %s%s\n", - sha1_to_hex(sha1), - multi_ack ? " continue" : ""); - if (multi_ack) - memcpy(last_sha1, sha1, 20); + if (got_sha1(line+5, sha1)) { + memcpy(hex, sha1_to_hex(sha1), 41); + if (multi_ack) { + const char *msg = "ACK %s continue\n"; + packet_write(1, msg, hex); + memcpy(last_hex, hex, 41); + } + else if (have_obj.nr == 1) + packet_write(1, "ACK %s\n", hex); } continue; } if (!strcmp(line, "done")) { if (have_obj.nr > 0) { if (multi_ack) - packet_write(1, "ACK %s\n", - sha1_to_hex(last_sha1)); + packet_write(1, "ACK %s\n", last_hex); return 0; } packet_write(1, "NAK\n"); -- cgit v0.10.2-6-g49f6 From c64ed70d2557101f2a2c3f76315049d027fe645b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 4 Sep 2006 21:50:12 -0700 Subject: Separate object listing routines out of rev-list Create a separate file, list-objects.c, and move object listing routines from rev-list to it. The next round will use it in pack-objects directly. Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index 7b3114f..18cd79e 100644 --- a/Makefile +++ b/Makefile @@ -233,7 +233,7 @@ XDIFF_LIB=xdiff/lib.a LIB_H = \ blob.h cache.h commit.h csum-file.h delta.h \ - diff.h object.h pack.h pkt-line.h quote.h refs.h \ + diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.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 @@ -250,7 +250,7 @@ LIB_OBJS = \ server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \ tag.o tree.o usage.o config.o environment.o ctype.o copy.o \ fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \ - write_or_die.o trace.o \ + write_or_die.o trace.o list-objects.o \ alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) BUILTIN_OBJS = \ diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 8fe8afb..0900737 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -7,6 +7,7 @@ #include "tree-walk.h" #include "diff.h" #include "revision.h" +#include "list-objects.h" #include "builtin.h" /* bits #0-15 in revision.h */ @@ -98,104 +99,19 @@ static void show_commit(struct commit *commit) commit->buffer = NULL; } -static void process_blob(struct blob *blob, - struct object_array *p, - struct name_path *path, - const char *name) +static void show_object(struct object_array_entry *p) { - struct object *obj = &blob->object; - - if (!revs.blob_objects) - return; - if (obj->flags & (UNINTERESTING | SEEN)) - return; - obj->flags |= SEEN; - name = xstrdup(name); - add_object(obj, p, path, name); -} - -static void process_tree(struct tree *tree, - struct object_array *p, - struct name_path *path, - const char *name) -{ - struct object *obj = &tree->object; - struct tree_desc desc; - struct name_entry entry; - struct name_path me; - - if (!revs.tree_objects) - return; - if (obj->flags & (UNINTERESTING | SEEN)) - return; - if (parse_tree(tree) < 0) - die("bad tree object %s", sha1_to_hex(obj->sha1)); - obj->flags |= SEEN; - name = xstrdup(name); - add_object(obj, p, path, name); - me.up = path; - me.elem = name; - me.elem_len = strlen(name); - - desc.buf = tree->buffer; - desc.size = tree->size; - - while (tree_entry(&desc, &entry)) { - if (S_ISDIR(entry.mode)) - process_tree(lookup_tree(entry.sha1), p, &me, entry.path); - else - process_blob(lookup_blob(entry.sha1), p, &me, entry.path); - } - free(tree->buffer); - tree->buffer = NULL; -} - -static void show_commit_list(struct rev_info *revs) -{ - int i; - struct commit *commit; - struct object_array objects = { 0, 0, NULL }; - - while ((commit = get_revision(revs)) != NULL) { - process_tree(commit->tree, &objects, NULL, ""); - show_commit(commit); - } - for (i = 0; i < revs->pending.nr; i++) { - struct object_array_entry *pending = revs->pending.objects + i; - struct object *obj = pending->item; - const char *name = pending->name; - if (obj->flags & (UNINTERESTING | SEEN)) - continue; - if (obj->type == OBJ_TAG) { - obj->flags |= SEEN; - add_object_array(obj, name, &objects); - continue; - } - if (obj->type == OBJ_TREE) { - process_tree((struct tree *)obj, &objects, NULL, name); - continue; - } - if (obj->type == OBJ_BLOB) { - process_blob((struct blob *)obj, &objects, NULL, name); - continue; - } - die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name); - } - for (i = 0; i < objects.nr; i++) { - struct object_array_entry *p = objects.objects + i; - - /* An object with name "foo\n0000000..." can be used to - * confuse downstream git-pack-objects very badly. - */ - const char *ep = strchr(p->name, '\n'); - if (ep) { - printf("%s %.*s\n", sha1_to_hex(p->item->sha1), - (int) (ep - p->name), - p->name); - } - else - printf("%s %s\n", sha1_to_hex(p->item->sha1), p->name); + /* An object with name "foo\n0000000..." can be used to + * confuse downstream git-pack-objects very badly. + */ + const char *ep = strchr(p->name, '\n'); + if (ep) { + printf("%s %.*s\n", sha1_to_hex(p->item->sha1), + (int) (ep - p->name), + p->name); } + else + printf("%s %s\n", sha1_to_hex(p->item->sha1), p->name); } /* @@ -389,7 +305,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (bisect_list) revs.commits = find_bisection(revs.commits); - show_commit_list(&revs); + traverse_commit_list(&revs, show_commit, show_object); return 0; } diff --git a/list-objects.c b/list-objects.c new file mode 100644 index 0000000..adaf997 --- /dev/null +++ b/list-objects.c @@ -0,0 +1,107 @@ +#include "cache.h" +#include "tag.h" +#include "commit.h" +#include "tree.h" +#include "blob.h" +#include "diff.h" +#include "tree-walk.h" +#include "revision.h" +#include "list-objects.h" + +static void process_blob(struct rev_info *revs, + struct blob *blob, + struct object_array *p, + struct name_path *path, + const char *name) +{ + struct object *obj = &blob->object; + + if (!revs->blob_objects) + return; + if (obj->flags & (UNINTERESTING | SEEN)) + return; + obj->flags |= SEEN; + name = xstrdup(name); + add_object(obj, p, path, name); +} + +static void process_tree(struct rev_info *revs, + struct tree *tree, + struct object_array *p, + struct name_path *path, + const char *name) +{ + struct object *obj = &tree->object; + struct tree_desc desc; + struct name_entry entry; + struct name_path me; + + if (!revs->tree_objects) + return; + if (obj->flags & (UNINTERESTING | SEEN)) + return; + if (parse_tree(tree) < 0) + die("bad tree object %s", sha1_to_hex(obj->sha1)); + obj->flags |= SEEN; + name = xstrdup(name); + add_object(obj, p, path, name); + me.up = path; + me.elem = name; + me.elem_len = strlen(name); + + desc.buf = tree->buffer; + desc.size = tree->size; + + while (tree_entry(&desc, &entry)) { + if (S_ISDIR(entry.mode)) + process_tree(revs, + lookup_tree(entry.sha1), + p, &me, entry.path); + else + process_blob(revs, + lookup_blob(entry.sha1), + p, &me, entry.path); + } + free(tree->buffer); + tree->buffer = NULL; +} + +void traverse_commit_list(struct rev_info *revs, + void (*show_commit)(struct commit *), + void (*show_object)(struct object_array_entry *)) +{ + int i; + struct commit *commit; + struct object_array objects = { 0, 0, NULL }; + + while ((commit = get_revision(revs)) != NULL) { + process_tree(revs, commit->tree, &objects, NULL, ""); + show_commit(commit); + } + for (i = 0; i < revs->pending.nr; i++) { + struct object_array_entry *pending = revs->pending.objects + i; + struct object *obj = pending->item; + const char *name = pending->name; + if (obj->flags & (UNINTERESTING | SEEN)) + continue; + if (obj->type == OBJ_TAG) { + obj->flags |= SEEN; + add_object_array(obj, name, &objects); + continue; + } + if (obj->type == OBJ_TREE) { + process_tree(revs, (struct tree *)obj, &objects, + NULL, name); + continue; + } + if (obj->type == OBJ_BLOB) { + process_blob(revs, (struct blob *)obj, &objects, + NULL, name); + continue; + } + die("unknown pending object %s (%s)", + sha1_to_hex(obj->sha1), name); + } + for (i = 0; i < objects.nr; i++) + show_object(&objects.objects[i]); +} diff --git a/list-objects.h b/list-objects.h new file mode 100644 index 0000000..8a5fae6 --- /dev/null +++ b/list-objects.h @@ -0,0 +1,8 @@ +#ifndef LIST_OBJECTS_H +#define LIST_OBJECTS_H + +void traverse_commit_list(struct rev_info *revs, + void (*show_commit)(struct commit *), + void (*show_object)(struct object_array_entry *)); + +#endif -- cgit v0.10.2-6-g49f6 From b5d97e6b0a044b11b409250189c61d40209065f2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 4 Sep 2006 23:47:39 -0700 Subject: pack-objects: run rev-list equivalent internally. Instead of piping the rev-list output from its standard input, you can say: pack-objects --all --unpacked --revs pack and feed the rev parameters you would otherwise give the rev-list on its command line from the standard input. In other words: echo 'master..next' | pack-objects --revs pack and rev-list --objects master..next | pack-objects pack are equivalent. Signed-off-by: Junio C Hamano diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 149fa28..b6e5960 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -9,10 +9,13 @@ #include "pack.h" #include "csum-file.h" #include "tree-walk.h" +#include "diff.h" +#include "revision.h" +#include "list-objects.h" #include #include -static const char pack_usage[] = "git-pack-objects [-q] [--no-reuse-delta] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list"; +static const char pack_usage[] = "git-pack-objects [-q] [--no-reuse-delta] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} [--revs [--unpacked | --all]* parents; parents; parents = parents->next) { + struct commit *parent = parents->item; + if (!(parent->object.flags & UNINTERESTING)) + continue; + mark_tree_uninteresting(parent->tree); + } +} + +static void mark_edges_uninteresting(struct commit_list *list) +{ + for ( ; list; list = list->next) { + struct commit *commit = list->item; + + if (commit->object.flags & UNINTERESTING) { + mark_tree_uninteresting(commit->tree); + continue; + } + mark_edge_parents_uninteresting(commit); + } +} + +static void show_commit(struct commit *commit) +{ + unsigned hash = name_hash(""); + add_object_entry(commit->object.sha1, hash, 0); +} + +static void show_object(struct object_array_entry *p) +{ + unsigned hash = name_hash(p->name); + add_object_entry(p->item->sha1, hash, 0); +} + +static void get_object_list(int unpacked, int all) +{ + struct rev_info revs; + char line[1000]; + const char *av[6]; + int ac; + int flags = 0; + + av[0] = "pack-objects"; + av[1] = "--objects"; + ac = 2; + if (unpacked) + av[ac++] = "--unpacked"; + if (all) + av[ac++] = "--all"; + av[ac++] = "--stdin"; + av[ac] = NULL; + + init_revisions(&revs, NULL); + save_commit_buffer = 0; + track_object_refs = 0; + setup_revisions(ac, av, &revs, NULL); + + /* make sure we did not get pathspecs */ + if (revs.prune_data) + die("pathspec given"); + + while (fgets(line, sizeof(line), stdin) != NULL) { + int len = strlen(line); + if (line[len - 1] == '\n') + line[--len] = 0; + if (!len) + break; + if (*line == '-') { + if (!strcmp(line, "--not")) { + flags ^= UNINTERESTING; + continue; + } + die("not a rev '%s'", line); + } + if (handle_revision_arg(line, &revs, flags, 1)) + die("bad revision '%s'", line); + } + + prepare_revision_walk(&revs); + mark_edges_uninteresting(revs.commits); + + traverse_commit_list(&revs, show_commit, show_object); +} + +int cmd_pack_objects(int argc, const char **argv, const char *prefix) +{ + SHA_CTX ctx; + int depth = 10; + struct object_entry **list; + int use_internal_rev_list = 0; + int unpacked = 0; + int all = 0; + int i; + + git_config(git_pack_config); + + progress = isatty(2); + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + if (*arg != '-') + break; + + if (!strcmp("--non-empty", arg)) { + non_empty = 1; + continue; + } + if (!strcmp("--local", arg)) { + local = 1; + continue; + } + if (!strcmp("--progress", arg)) { + progress = 1; + continue; + } + if (!strcmp("--incremental", arg)) { + incremental = 1; + continue; + } + if (!strncmp("--window=", arg, 9)) { + char *end; + window = strtoul(arg+9, &end, 0); + if (!arg[9] || *end) + usage(pack_usage); + continue; + } + if (!strncmp("--depth=", arg, 8)) { + char *end; + depth = strtoul(arg+8, &end, 0); + if (!arg[8] || *end) + usage(pack_usage); + continue; + } + if (!strcmp("--progress", arg)) { + progress = 1; + continue; + } + if (!strcmp("-q", arg)) { + progress = 0; + continue; + } + if (!strcmp("--no-reuse-delta", arg)) { + no_reuse_delta = 1; + continue; + } + if (!strcmp("--stdout", arg)) { + pack_to_stdout = 1; + continue; + } + if (!strcmp("--revs", arg)) { + use_internal_rev_list = 1; + continue; + } + if (!strcmp("--unpacked", arg)) { + unpacked = 1; + continue; + } + if (!strcmp("--all", arg)) { + all = 1; + continue; + } + usage(pack_usage); + } + + /* Traditionally "pack-objects [options] base extra" failed; + * we would however want to take refs parameter that would + * have been given to upstream rev-list ourselves, which means + * we somehow want to say what the base name is. So the + * syntax would be: + * + * pack-objects [options] base + * + * in other words, we would treat the first non-option as the + * base_name and send everything else to the internal revision + * walker. + */ + + if (!pack_to_stdout) + base_name = argv[i++]; + + if (pack_to_stdout != !base_name) + usage(pack_usage); + + /* --unpacked and --all makes sense only with --revs */ + if (!use_internal_rev_list && (unpacked || all)) + usage(pack_usage); + + prepare_packed_git(); + + if (progress) { + fprintf(stderr, "Generating pack...\n"); + setup_progress_signal(); + } + + if (!use_internal_rev_list) + read_object_list_from_stdin(); + else + get_object_list(unpacked, all); + if (progress) fprintf(stderr, "Done counting %d objects.\n", nr_objects); sorted_by_sha = create_final_object_list(); -- cgit v0.10.2-6-g49f6 From 8d1d8f83b5d918c6071b606e321de9c31fed9e68 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 6 Sep 2006 01:42:23 -0700 Subject: pack-objects: further work on internal rev-list logic. This teaches the internal rev-list logic to understand options that are needed for pack handling: --all, --unpacked, and --thin. It also moves two functions from builtin-rev-list to list-objects so that the two programs can share more code. Signed-off-by: Junio C Hamano diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index b6e5960..753dd9a 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -69,6 +69,7 @@ static int progress = 1; static volatile sig_atomic_t progress_update; static int window = 10; static int pack_to_stdout; +static int num_preferred_base; /* * The object names in objects array are hashed with this hashtable, @@ -841,7 +842,7 @@ static int check_pbase_path(unsigned hash) return 0; } -static void add_preferred_base_object(char *name, unsigned hash) +static void add_preferred_base_object(const char *name, unsigned hash) { struct pbase_tree *it; int cmplen = name_cmp_len(name); @@ -870,6 +871,9 @@ static void add_preferred_base(unsigned char *sha1) unsigned long size; unsigned char tree_sha1[20]; + if (window <= num_preferred_base++) + return; + data = read_object_with_reference(sha1, tree_type, &size, tree_sha1); if (!data) return; @@ -1331,7 +1335,6 @@ static int git_pack_config(const char *k, const char *v) static void read_object_list_from_stdin(void) { - int num_preferred_base = 0; char line[40 + 1 + PATH_MAX + 2]; unsigned char sha1[20]; unsigned hash; @@ -1351,8 +1354,7 @@ static void read_object_list_from_stdin(void) if (get_sha1_hex(line+1, sha1)) die("expected edge sha1, got garbage:\n %s", line); - if (num_preferred_base++ < window) - add_preferred_base(sha1); + add_preferred_base(sha1); continue; } if (get_sha1_hex(line, sha1)) @@ -1364,71 +1366,36 @@ static void read_object_list_from_stdin(void) } } -/* copied from rev-list but needs to do things slightly differently */ -static void mark_edge_parents_uninteresting(struct commit *commit) -{ - struct commit_list *parents; - - for (parents = commit->parents; parents; parents = parents->next) { - struct commit *parent = parents->item; - if (!(parent->object.flags & UNINTERESTING)) - continue; - mark_tree_uninteresting(parent->tree); - } -} - -static void mark_edges_uninteresting(struct commit_list *list) -{ - for ( ; list; list = list->next) { - struct commit *commit = list->item; - - if (commit->object.flags & UNINTERESTING) { - mark_tree_uninteresting(commit->tree); - continue; - } - mark_edge_parents_uninteresting(commit); - } -} - static void show_commit(struct commit *commit) { unsigned hash = name_hash(""); + add_preferred_base_object("", hash); add_object_entry(commit->object.sha1, hash, 0); } static void show_object(struct object_array_entry *p) { unsigned hash = name_hash(p->name); + add_preferred_base_object(p->name, hash); add_object_entry(p->item->sha1, hash, 0); } -static void get_object_list(int unpacked, int all) +static void show_edge(struct commit *commit) +{ + add_preferred_base(commit->object.sha1); +} + +static void get_object_list(int ac, const char **av) { struct rev_info revs; char line[1000]; - const char *av[6]; - int ac; int flags = 0; - av[0] = "pack-objects"; - av[1] = "--objects"; - ac = 2; - if (unpacked) - av[ac++] = "--unpacked"; - if (all) - av[ac++] = "--all"; - av[ac++] = "--stdin"; - av[ac] = NULL; - init_revisions(&revs, NULL); save_commit_buffer = 0; track_object_refs = 0; setup_revisions(ac, av, &revs, NULL); - /* make sure we did not get pathspecs */ - if (revs.prune_data) - die("pathspec given"); - while (fgets(line, sizeof(line), stdin) != NULL) { int len = strlen(line); if (line[len - 1] == '\n') @@ -1447,8 +1414,7 @@ static void get_object_list(int unpacked, int all) } prepare_revision_walk(&revs); - mark_edges_uninteresting(revs.commits); - + mark_edges_uninteresting(revs.commits, &revs, show_edge); traverse_commit_list(&revs, show_commit, show_object); } @@ -1458,9 +1424,14 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) int depth = 10; struct object_entry **list; int use_internal_rev_list = 0; - int unpacked = 0; - int all = 0; + int thin = 0; int i; + const char *rp_av[64]; + int rp_ac; + + rp_av[0] = "pack-objects"; + rp_av[1] = "--objects"; /* --thin will make it --objects-edge */ + rp_ac = 2; git_config(git_pack_config); @@ -1521,12 +1492,19 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) use_internal_rev_list = 1; continue; } - if (!strcmp("--unpacked", arg)) { - unpacked = 1; + if (!strcmp("--unpacked", arg) || + !strncmp("--unpacked=", arg, 11) || + !strcmp("--all", arg)) { + use_internal_rev_list = 1; + if (ARRAY_SIZE(rp_av) - 1 <= rp_ac) + die("too many internal rev-list options"); + rp_av[rp_ac++] = arg; continue; } - if (!strcmp("--all", arg)) { - all = 1; + if (!strcmp("--thin", arg)) { + use_internal_rev_list = 1; + thin = 1; + rp_av[1] = "--objects-edge"; continue; } usage(pack_usage); @@ -1551,9 +1529,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (pack_to_stdout != !base_name) usage(pack_usage); - /* --unpacked and --all makes sense only with --revs */ - if (!use_internal_rev_list && (unpacked || all)) - usage(pack_usage); + if (!pack_to_stdout && thin) + die("--thin cannot be used to build an indexable pack."); prepare_packed_git(); @@ -1564,8 +1541,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (!use_internal_rev_list) read_object_list_from_stdin(); - else - get_object_list(unpacked, all); + else { + rp_av[rp_ac] = NULL; + get_object_list(rp_ac, rp_av); + } if (progress) fprintf(stderr, "Done counting %d objects.\n", nr_objects); diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 0900737..1f3333d 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -114,6 +114,11 @@ static void show_object(struct object_array_entry *p) printf("%s %s\n", sha1_to_hex(p->item->sha1), p->name); } +static void show_edge(struct commit *commit) +{ + printf("-%s\n", sha1_to_hex(commit->object.sha1)); +} + /* * This is a truly stupid algorithm, but it's only * used for bisection, and we just don't care enough. @@ -192,35 +197,6 @@ static struct commit_list *find_bisection(struct commit_list *list) return best; } -static void mark_edge_parents_uninteresting(struct commit *commit) -{ - struct commit_list *parents; - - for (parents = commit->parents; parents; parents = parents->next) { - struct commit *parent = parents->item; - if (!(parent->object.flags & UNINTERESTING)) - continue; - mark_tree_uninteresting(parent->tree); - if (revs.edge_hint && !(parent->object.flags & SHOWN)) { - parent->object.flags |= SHOWN; - printf("-%s\n", sha1_to_hex(parent->object.sha1)); - } - } -} - -static void mark_edges_uninteresting(struct commit_list *list) -{ - for ( ; list; list = list->next) { - struct commit *commit = list->item; - - if (commit->object.flags & UNINTERESTING) { - mark_tree_uninteresting(commit->tree); - continue; - } - mark_edge_parents_uninteresting(commit); - } -} - static void read_revisions_from_stdin(struct rev_info *revs) { char line[1000]; @@ -300,7 +276,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) prepare_revision_walk(&revs); if (revs.tree_objects) - mark_edges_uninteresting(revs.commits); + mark_edges_uninteresting(revs.commits, &revs, show_edge); if (bisect_list) revs.commits = find_bisection(revs.commits); diff --git a/list-objects.c b/list-objects.c index adaf997..f1fa21c 100644 --- a/list-objects.c +++ b/list-objects.c @@ -66,6 +66,39 @@ static void process_tree(struct rev_info *revs, tree->buffer = NULL; } +static void mark_edge_parents_uninteresting(struct commit *commit, + struct rev_info *revs, + show_edge_fn show_edge) +{ + struct commit_list *parents; + + for (parents = commit->parents; parents; parents = parents->next) { + struct commit *parent = parents->item; + if (!(parent->object.flags & UNINTERESTING)) + continue; + mark_tree_uninteresting(parent->tree); + if (revs->edge_hint && !(parent->object.flags & SHOWN)) { + parent->object.flags |= SHOWN; + show_edge(parent); + } + } +} + +void mark_edges_uninteresting(struct commit_list *list, + struct rev_info *revs, + show_edge_fn show_edge) +{ + for ( ; list; list = list->next) { + struct commit *commit = list->item; + + if (commit->object.flags & UNINTERESTING) { + mark_tree_uninteresting(commit->tree); + continue; + } + mark_edge_parents_uninteresting(commit, revs, show_edge); + } +} + void traverse_commit_list(struct rev_info *revs, void (*show_commit)(struct commit *), void (*show_object)(struct object_array_entry *)) diff --git a/list-objects.h b/list-objects.h index 8a5fae6..0f41391 100644 --- a/list-objects.h +++ b/list-objects.h @@ -1,8 +1,12 @@ #ifndef LIST_OBJECTS_H #define LIST_OBJECTS_H -void traverse_commit_list(struct rev_info *revs, - void (*show_commit)(struct commit *), - void (*show_object)(struct object_array_entry *)); +typedef void (*show_commit_fn)(struct commit *); +typedef void (*show_object_fn)(struct object_array_entry *); +typedef void (*show_edge_fn)(struct commit *); + +void traverse_commit_list(struct rev_info *revs, show_commit_fn, show_object_fn); + +void mark_edges_uninteresting(struct commit_list *, struct rev_info *, show_edge_fn); #endif -- cgit v0.10.2-6-g49f6 From 106d710bc13f34aec1a15c4cff80f062f384edf6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 6 Sep 2006 02:12:09 -0700 Subject: pack-objects --unpacked= option. Incremental repack without -a essentially boils down to: rev-list --objects --unpacked --all | pack-objects $new_pack which picks up all loose objects that are still live and creates a new pack. This implements --unpacked= option to tell the revision walking machinery to pretend as if objects in such a pack are unpacked for the purpose of object listing. With this, we could say: rev-list --objects --unpacked=$active_pack --all | pack-objects $new_pack instead, to mean "all live loose objects but pretend as if objects that are in this pack are also unpacked". The newly created pack would be perfect for updating $active_pack by replacing it. Since pack-objects now knows how to do the rev-list's work itself internally, you can also write the above example by: pack-objects --unpacked=$active_pack --all $new_pack diff --git a/builtin-count-objects.c b/builtin-count-objects.c index 1d3729a..73c5982 100644 --- a/builtin-count-objects.c +++ b/builtin-count-objects.c @@ -62,7 +62,7 @@ static void count_objects(DIR *d, char *path, int len, int verbose, hex[40] = 0; if (get_sha1_hex(hex, sha1)) die("internal error"); - if (has_sha1_pack(sha1)) + if (has_sha1_pack(sha1, NULL)) (*packed_loose)++; } } diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c index d3dd94d..960db49 100644 --- a/builtin-prune-packed.c +++ b/builtin-prune-packed.c @@ -19,7 +19,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len) memcpy(hex+2, de->d_name, 38); if (get_sha1_hex(hex, sha1)) continue; - if (!has_sha1_pack(sha1)) + if (!has_sha1_pack(sha1, NULL)) continue; memcpy(pathname + len, de->d_name, 38); if (dryrun) diff --git a/cache.h b/cache.h index a53204f..ac51ed1 100644 --- a/cache.h +++ b/cache.h @@ -259,7 +259,7 @@ extern int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, extern int write_sha1_to_fd(int fd, const unsigned char *sha1); extern int move_temp_to_file(const char *tmpfile, const char *filename); -extern int has_sha1_pack(const unsigned char *sha1); +extern int has_sha1_pack(const unsigned char *sha1, const char **ignore); extern int has_sha1_file(const unsigned char *sha1); extern void *map_sha1_file(const unsigned char *sha1, unsigned long *); extern int legacy_loose_object(unsigned char *); diff --git a/revision.c b/revision.c index db01682..6a2539b 100644 --- a/revision.c +++ b/revision.c @@ -416,7 +416,8 @@ static void limit_list(struct rev_info *revs) if (revs->max_age != -1 && (commit->date < revs->max_age)) obj->flags |= UNINTERESTING; - if (revs->unpacked && has_sha1_pack(obj->sha1)) + if (revs->unpacked && + has_sha1_pack(obj->sha1, revs->ignore_packed)) obj->flags |= UNINTERESTING; add_parents_to_list(revs, commit, &list); if (obj->flags & UNINTERESTING) { @@ -671,6 +672,16 @@ int handle_revision_arg(const char *arg, struct rev_info *revs, return 0; } +static void add_ignore_packed(struct rev_info *revs, const char *name) +{ + int num = ++revs->num_ignore_packed; + + revs->ignore_packed = xrealloc(revs->ignore_packed, + sizeof(const char **) * (num + 1)); + revs->ignore_packed[num-1] = name; + revs->ignore_packed[num] = NULL; +} + /* * Parse revision information, filling in the "rev_info" structure, * and removing the used arguments from the argument list. @@ -811,6 +822,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch } if (!strcmp(arg, "--unpacked")) { revs->unpacked = 1; + free(revs->ignore_packed); + revs->ignore_packed = NULL; + revs->num_ignore_packed = 0; + continue; + } + if (!strncmp(arg, "--unpacked=", 11)) { + revs->unpacked = 1; + add_ignore_packed(revs, arg+11); continue; } if (!strcmp(arg, "-r")) { @@ -1057,7 +1076,8 @@ struct commit *get_revision(struct rev_info *revs) */ if (!revs->limited) { if ((revs->unpacked && - has_sha1_pack(commit->object.sha1)) || + has_sha1_pack(commit->object.sha1, + revs->ignore_packed)) || (revs->max_age != -1 && (commit->date < revs->max_age))) continue; diff --git a/revision.h b/revision.h index c1f71af..a5c35d0 100644 --- a/revision.h +++ b/revision.h @@ -38,7 +38,7 @@ struct rev_info { blob_objects:1, edge_hint:1, limited:1, - unpacked:1, + unpacked:1, /* see also ignore_packed below */ boundary:1, parents:1; @@ -57,6 +57,10 @@ struct rev_info { unsigned int shown_one:1, abbrev_commit:1, relative_date:1; + + const char **ignore_packed; /* pretend objects in these are unpacked */ + int num_ignore_packed; + unsigned int abbrev; enum cmit_fmt commit_format; struct log_info *loginfo; diff --git a/sha1_file.c b/sha1_file.c index 428d791..d5d048a 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1189,12 +1189,20 @@ int find_pack_entry_one(const unsigned char *sha1, return 0; } -static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e) +static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed) { struct packed_git *p; prepare_packed_git(); for (p = packed_git; p; p = p->next) { + if (ignore_packed) { + const char **ig; + for (ig = ignore_packed; *ig; ig++) + if (!strcmp(p->pack_name, *ig)) + break; + if (*ig) + continue; + } if (find_pack_entry_one(sha1, e, p)) return 1; } @@ -1227,10 +1235,10 @@ int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep if (!map) { struct pack_entry e; - if (find_pack_entry(sha1, &e)) + if (find_pack_entry(sha1, &e, NULL)) return packed_object_info(&e, type, sizep); reprepare_packed_git(); - if (find_pack_entry(sha1, &e)) + if (find_pack_entry(sha1, &e, NULL)) return packed_object_info(&e, type, sizep); return error("unable to find %s", sha1_to_hex(sha1)); } @@ -1253,7 +1261,7 @@ static void *read_packed_sha1(const unsigned char *sha1, char *type, unsigned lo { struct pack_entry e; - if (!find_pack_entry(sha1, &e)) { + if (!find_pack_entry(sha1, &e, NULL)) { error("cannot read sha1_file for %s", sha1_to_hex(sha1)); return NULL; } @@ -1266,7 +1274,7 @@ void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size void *map, *buf; struct pack_entry e; - if (find_pack_entry(sha1, &e)) + if (find_pack_entry(sha1, &e, NULL)) return read_packed_sha1(sha1, type, size); map = map_sha1_file(sha1, &mapsize); if (map) { @@ -1275,7 +1283,7 @@ void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size return buf; } reprepare_packed_git(); - if (find_pack_entry(sha1, &e)) + if (find_pack_entry(sha1, &e, NULL)) return read_packed_sha1(sha1, type, size); return NULL; } @@ -1707,10 +1715,10 @@ int has_pack_file(const unsigned char *sha1) return 1; } -int has_sha1_pack(const unsigned char *sha1) +int has_sha1_pack(const unsigned char *sha1, const char **ignore_packed) { struct pack_entry e; - return find_pack_entry(sha1, &e); + return find_pack_entry(sha1, &e, ignore_packed); } int has_sha1_file(const unsigned char *sha1) @@ -1718,7 +1726,7 @@ int has_sha1_file(const unsigned char *sha1) struct stat st; struct pack_entry e; - if (find_pack_entry(sha1, &e)) + if (find_pack_entry(sha1, &e, NULL)) return 1; return find_sha1_file(sha1, &st) ? 1 : 0; } -- cgit v0.10.2-6-g49f6 From 0424558190fb85e6702e7c64cd2b11bc31d7be85 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 7 Sep 2006 02:35:42 -0400 Subject: diff: support custom callbacks for output Users can request the DIFF_FORMAT_CALLBACK output format to get a callback consisting of the whole diff_queue_struct. Signed-off-by: Junio C Hamano diff --git a/diff.c b/diff.c index 9dcbda3..a3ebc62 100644 --- a/diff.c +++ b/diff.c @@ -2587,6 +2587,9 @@ void diff_flush(struct diff_options *options) } } + if (output_format & DIFF_FORMAT_CALLBACK) + options->format_callback(q, options, options->format_callback_data); + for (i = 0; i < q->nr; i++) diff_free_filepair(q->queue[i]); free_queue: diff --git a/diff.h b/diff.h index b007240..b60a02e 100644 --- a/diff.h +++ b/diff.h @@ -8,6 +8,7 @@ struct rev_info; struct diff_options; +struct diff_queue_struct; typedef void (*change_fn_t)(struct diff_options *options, unsigned old_mode, unsigned new_mode, @@ -20,6 +21,9 @@ typedef void (*add_remove_fn_t)(struct diff_options *options, const unsigned char *sha1, const char *base, const char *path); +typedef void (*diff_format_fn_t)(struct diff_queue_struct *q, + struct diff_options *options, void *data); + #define DIFF_FORMAT_RAW 0x0001 #define DIFF_FORMAT_DIFFSTAT 0x0002 #define DIFF_FORMAT_SUMMARY 0x0004 @@ -35,6 +39,8 @@ typedef void (*add_remove_fn_t)(struct diff_options *options, */ #define DIFF_FORMAT_NO_OUTPUT 0x0080 +#define DIFF_FORMAT_CALLBACK 0x0100 + struct diff_options { const char *filter; const char *orderfile; @@ -68,6 +74,8 @@ struct diff_options { int *pathlens; change_fn_t change; add_remove_fn_t add_remove; + diff_format_fn_t format_callback; + void *format_callback_data; }; enum color_diff { -- cgit v0.10.2-6-g49f6 From 7c92fe0eaa4fb89e27fa3617b9ae52f20b511573 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 8 Sep 2006 04:03:18 -0400 Subject: Move color option parsing out of diff.c and into color.[ch] The intent is to lib-ify colorizing code so it can be reused. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index 7b3114f..78748cb 100644 --- a/Makefile +++ b/Makefile @@ -251,7 +251,8 @@ LIB_OBJS = \ tag.o tree.o usage.o config.o environment.o ctype.o copy.o \ fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \ write_or_die.o trace.o \ - alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) + alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \ + color.o BUILTIN_OBJS = \ builtin-add.o \ diff --git a/color.c b/color.c new file mode 100644 index 0000000..d8c8399 --- /dev/null +++ b/color.c @@ -0,0 +1,176 @@ +#include "color.h" +#include "cache.h" +#include "git-compat-util.h" + +#include + +#define COLOR_RESET "\033[m" + +static int parse_color(const char *name, int len) +{ + static const char * const color_names[] = { + "normal", "black", "red", "green", "yellow", + "blue", "magenta", "cyan", "white" + }; + char *end; + int i; + for (i = 0; i < ARRAY_SIZE(color_names); i++) { + const char *str = color_names[i]; + if (!strncasecmp(name, str, len) && !str[len]) + return i - 1; + } + i = strtol(name, &end, 10); + if (*name && !*end && i >= -1 && i <= 255) + return i; + return -2; +} + +static int parse_attr(const char *name, int len) +{ + static const int attr_values[] = { 1, 2, 4, 5, 7 }; + static const char * const attr_names[] = { + "bold", "dim", "ul", "blink", "reverse" + }; + int i; + for (i = 0; i < ARRAY_SIZE(attr_names); i++) { + const char *str = attr_names[i]; + if (!strncasecmp(name, str, len) && !str[len]) + return attr_values[i]; + } + return -1; +} + +void color_parse(const char *value, const char *var, char *dst) +{ + const char *ptr = value; + int attr = -1; + int fg = -2; + int bg = -2; + + if (!strcasecmp(value, "reset")) { + strcpy(dst, "\033[m"); + return; + } + + /* [fg [bg]] [attr] */ + while (*ptr) { + const char *word = ptr; + int val, len = 0; + + while (word[len] && !isspace(word[len])) + len++; + + ptr = word + len; + while (*ptr && isspace(*ptr)) + ptr++; + + val = parse_color(word, len); + if (val >= -1) { + if (fg == -2) { + fg = val; + continue; + } + if (bg == -2) { + bg = val; + continue; + } + goto bad; + } + val = parse_attr(word, len); + if (val < 0 || attr != -1) + goto bad; + attr = val; + } + + if (attr >= 0 || fg >= 0 || bg >= 0) { + int sep = 0; + + *dst++ = '\033'; + *dst++ = '['; + if (attr >= 0) { + *dst++ = '0' + attr; + sep++; + } + if (fg >= 0) { + if (sep++) + *dst++ = ';'; + if (fg < 8) { + *dst++ = '3'; + *dst++ = '0' + fg; + } else { + dst += sprintf(dst, "38;5;%d", fg); + } + } + if (bg >= 0) { + if (sep++) + *dst++ = ';'; + if (bg < 8) { + *dst++ = '4'; + *dst++ = '0' + bg; + } else { + dst += sprintf(dst, "48;5;%d", bg); + } + } + *dst++ = 'm'; + } + *dst = 0; + return; +bad: + die("bad config value '%s' for variable '%s'", value, var); +} + +int git_config_colorbool(const char *var, const char *value) +{ + if (!value) + return 1; + if (!strcasecmp(value, "auto")) { + if (isatty(1) || (pager_in_use && pager_use_color)) { + char *term = getenv("TERM"); + if (term && strcmp(term, "dumb")) + return 1; + } + return 0; + } + if (!strcasecmp(value, "never")) + return 0; + if (!strcasecmp(value, "always")) + return 1; + return git_config_bool(var, value); +} + +static int color_vprintf(const char *color, const char *fmt, + va_list args, const char *trail) +{ + int r = 0; + + if (*color) + r += printf("%s", color); + r += vprintf(fmt, args); + if (*color) + r += printf("%s", COLOR_RESET); + if (trail) + r += printf("%s", trail); + return r; +} + + + +int color_printf(const char *color, const char *fmt, ...) +{ + va_list args; + int r; + va_start(args, fmt); + r = color_vprintf(color, fmt, args, NULL); + va_end(args); + return r; +} + +int color_printf_ln(const char *color, const char *fmt, ...) +{ + va_list args; + int r; + va_start(args, fmt); + r = color_vprintf(color, fmt, args, "\n"); + va_end(args); + return r; +} diff --git a/color.h b/color.h new file mode 100644 index 0000000..88bb8ff --- /dev/null +++ b/color.h @@ -0,0 +1,12 @@ +#ifndef COLOR_H +#define COLOR_H + +/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ +#define COLOR_MAXLEN 24 + +int git_config_colorbool(const char *var, const char *value); +void color_parse(const char *var, const char *value, char *dst); +int color_printf(const char *color, const char *fmt, ...); +int color_printf_ln(const char *color, const char *fmt, ...); + +#endif /* COLOR_H */ diff --git a/diff.c b/diff.c index a3ebc62..8178c11 100644 --- a/diff.c +++ b/diff.c @@ -10,6 +10,7 @@ #include "diffcore.h" #include "delta.h" #include "xdiff-interface.h" +#include "color.h" static int use_size_cache; @@ -17,8 +18,7 @@ static int diff_detect_rename_default; static int diff_rename_limit_default = -1; static int diff_use_color_default; -/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ -static char diff_colors[][24] = { +static char diff_colors[][COLOR_MAXLEN] = { "\033[m", /* reset */ "", /* normal */ "\033[1m", /* bold */ @@ -45,119 +45,6 @@ static int parse_diff_color_slot(const char *var, int ofs) die("bad config variable '%s'", var); } -static int parse_color(const char *name, int len) -{ - static const char * const color_names[] = { - "normal", "black", "red", "green", "yellow", - "blue", "magenta", "cyan", "white" - }; - char *end; - int i; - for (i = 0; i < ARRAY_SIZE(color_names); i++) { - const char *str = color_names[i]; - if (!strncasecmp(name, str, len) && !str[len]) - return i - 1; - } - i = strtol(name, &end, 10); - if (*name && !*end && i >= -1 && i <= 255) - return i; - return -2; -} - -static int parse_attr(const char *name, int len) -{ - static const int attr_values[] = { 1, 2, 4, 5, 7 }; - static const char * const attr_names[] = { - "bold", "dim", "ul", "blink", "reverse" - }; - int i; - for (i = 0; i < ARRAY_SIZE(attr_names); i++) { - const char *str = attr_names[i]; - if (!strncasecmp(name, str, len) && !str[len]) - return attr_values[i]; - } - return -1; -} - -static void parse_diff_color_value(const char *value, const char *var, char *dst) -{ - const char *ptr = value; - int attr = -1; - int fg = -2; - int bg = -2; - - if (!strcasecmp(value, "reset")) { - strcpy(dst, "\033[m"); - return; - } - - /* [fg [bg]] [attr] */ - while (*ptr) { - const char *word = ptr; - int val, len = 0; - - while (word[len] && !isspace(word[len])) - len++; - - ptr = word + len; - while (*ptr && isspace(*ptr)) - ptr++; - - val = parse_color(word, len); - if (val >= -1) { - if (fg == -2) { - fg = val; - continue; - } - if (bg == -2) { - bg = val; - continue; - } - goto bad; - } - val = parse_attr(word, len); - if (val < 0 || attr != -1) - goto bad; - attr = val; - } - - if (attr >= 0 || fg >= 0 || bg >= 0) { - int sep = 0; - - *dst++ = '\033'; - *dst++ = '['; - if (attr >= 0) { - *dst++ = '0' + attr; - sep++; - } - if (fg >= 0) { - if (sep++) - *dst++ = ';'; - if (fg < 8) { - *dst++ = '3'; - *dst++ = '0' + fg; - } else { - dst += sprintf(dst, "38;5;%d", fg); - } - } - if (bg >= 0) { - if (sep++) - *dst++ = ';'; - if (bg < 8) { - *dst++ = '4'; - *dst++ = '0' + bg; - } else { - dst += sprintf(dst, "48;5;%d", bg); - } - } - *dst++ = 'm'; - } - *dst = 0; - return; -bad: - die("bad config value '%s' for variable '%s'", value, var); -} - /* * These are to give UI layer defaults. * The core-level commands such as git-diff-files should @@ -171,22 +58,7 @@ int git_diff_ui_config(const char *var, const char *value) return 0; } if (!strcmp(var, "diff.color")) { - if (!value) - diff_use_color_default = 1; /* bool */ - else if (!strcasecmp(value, "auto")) { - diff_use_color_default = 0; - if (isatty(1) || (pager_in_use && pager_use_color)) { - char *term = getenv("TERM"); - if (term && strcmp(term, "dumb")) - diff_use_color_default = 1; - } - } - else if (!strcasecmp(value, "never")) - diff_use_color_default = 0; - else if (!strcasecmp(value, "always")) - diff_use_color_default = 1; - else - diff_use_color_default = git_config_bool(var, value); + diff_use_color_default = git_config_colorbool(var, value); return 0; } if (!strcmp(var, "diff.renames")) { @@ -201,7 +73,7 @@ int git_diff_ui_config(const char *var, const char *value) } if (!strncmp(var, "diff.color.", 11)) { int slot = parse_diff_color_slot(var, 11); - parse_diff_color_value(value, var, diff_colors[slot]); + color_parse(value, var, diff_colors[slot]); return 0; } return git_default_config(var, value); -- cgit v0.10.2-6-g49f6 From c91f0d92efb36d7b349f586cafafaf0e6ac3f5b2 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 8 Sep 2006 04:05:34 -0400 Subject: git-commit.sh: convert run_status to a C builtin This creates a new git-runstatus which should do roughly the same thing as the run_status function from git-commit.sh. Except for color support, the main focus has been to keep the output identical, so that it can be verified as correct and then used as a C platform for other improvements to the status printing code. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano diff --git a/.gitignore b/.gitignore index 0d608fe..97592ab 100644 --- a/.gitignore +++ b/.gitignore @@ -94,6 +94,7 @@ git-rev-list git-rev-parse git-revert git-rm +git-runstatus git-send-email git-send-pack git-sh-setup diff --git a/Makefile b/Makefile index 78748cb..a9314ac 100644 --- a/Makefile +++ b/Makefile @@ -252,7 +252,7 @@ LIB_OBJS = \ fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \ write_or_die.o trace.o \ alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \ - color.o + color.o wt-status.o BUILTIN_OBJS = \ builtin-add.o \ @@ -286,6 +286,7 @@ BUILTIN_OBJS = \ builtin-rev-list.o \ builtin-rev-parse.o \ builtin-rm.o \ + builtin-runstatus.o \ builtin-show-branch.o \ builtin-stripspace.o \ builtin-symbolic-ref.o \ diff --git a/builtin-runstatus.c b/builtin-runstatus.c new file mode 100644 index 0000000..7979d61 --- /dev/null +++ b/builtin-runstatus.c @@ -0,0 +1,34 @@ +#include "wt-status.h" +#include "cache.h" + +extern int wt_status_use_color; + +static const char runstatus_usage[] = +"git-runstatus [--color|--nocolor] [--amend] [--verbose]"; + +int cmd_runstatus(int argc, const char **argv, const char *prefix) +{ + struct wt_status s; + int i; + + git_config(git_status_config); + wt_status_prepare(&s); + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "--color")) + wt_status_use_color = 1; + else if (!strcmp(argv[i], "--nocolor")) + wt_status_use_color = 0; + else if (!strcmp(argv[i], "--amend")) { + s.amend = 1; + s.reference = "HEAD^1"; + } + else if (!strcmp(argv[i], "--verbose")) + s.verbose = 1; + else + usage(runstatus_usage); + } + + wt_status_print(&s); + return s.commitable ? 0 : 1; +} diff --git a/builtin.h b/builtin.h index 25431d7..53a896c 100644 --- a/builtin.h +++ b/builtin.h @@ -47,6 +47,7 @@ extern int cmd_repo_config(int argc, const char **argv, const char *prefix); extern int cmd_rev_list(int argc, const char **argv, const char *prefix); extern int cmd_rev_parse(int argc, const char **argv, const char *prefix); extern int cmd_rm(int argc, const char **argv, const char *prefix); +extern int cmd_runstatus(int argc, const char **argv, const char *prefix); extern int cmd_show_branch(int argc, const char **argv, const char *prefix); extern int cmd_show(int argc, const char **argv, const char *prefix); extern int cmd_stripspace(int argc, const char **argv, const char *prefix); diff --git a/dir.c b/dir.c index 5a40d8f..e2f472b 100644 --- a/dir.c +++ b/dir.c @@ -397,3 +397,10 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name); return dir->nr; } + +int +file_exists(const char *f) +{ + struct stat sb; + return stat(f, &sb) == 0; +} diff --git a/dir.h b/dir.h index 56a1b7f..313f8ab 100644 --- a/dir.h +++ b/dir.h @@ -47,5 +47,6 @@ extern int excluded(struct dir_struct *, const char *); extern void add_excludes_from_file(struct dir_struct *, const char *fname); extern void add_exclude(const char *string, const char *base, int baselen, struct exclude_list *which); +extern int file_exists(const char *); #endif diff --git a/git-commit.sh b/git-commit.sh index 4cf3fab..10c269a 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -60,26 +60,6 @@ report () { } run_status () { - ( - # We always show status for the whole tree. - cd "$TOP" - - IS_INITIAL="$initial_commit" - REFERENCE=HEAD - case "$amend" in - t) - # If we are amending the initial commit, there - # is no HEAD^1. - if git-rev-parse --verify "HEAD^1" >/dev/null 2>&1 - then - REFERENCE="HEAD^1" - IS_INITIAL= - else - IS_INITIAL=t - fi - ;; - esac - # If TMP_INDEX is defined, that means we are doing # "--only" partial commit, and that index file is used # to build the tree for the commit. Otherwise, if @@ -96,85 +76,13 @@ run_status () { export GIT_INDEX_FILE fi - case "$branch" in - refs/heads/master) ;; - *) echo "# On branch $branch" ;; - esac - - if test -z "$IS_INITIAL" - then - git-diff-index -M --cached --name-status \ - --diff-filter=MDTCRA $REFERENCE | - sed -e ' - s/\\/\\\\/g - s/ /\\ /g - ' | - report "Updated but not checked in" "will commit" - committable="$?" - else - echo '# -# Initial commit -#' - git-ls-files | - sed -e ' - s/\\/\\\\/g - s/ /\\ /g - s/^/A / - ' | - report "Updated but not checked in" "will commit" - - committable="$?" - fi - - git-diff-files --name-status | - sed -e ' - s/\\/\\\\/g - s/ /\\ /g - ' | - report "Changed but not updated" \ - "use git-update-index to mark for commit" - - option="" - if test -z "$untracked_files"; then - option="--directory --no-empty-directory" - fi - hdr_shown= - if test -f "$GIT_DIR/info/exclude" - then - git-ls-files --others $option \ - --exclude-from="$GIT_DIR/info/exclude" \ - --exclude-per-directory=.gitignore - else - git-ls-files --others $option \ - --exclude-per-directory=.gitignore - fi | - while read line; do - if [ -z "$hdr_shown" ]; then - echo '#' - echo '# Untracked files:' - echo '# (use "git add" to add to commit)' - echo '#' - hdr_shown=1 - fi - echo "# $line" - done - - if test -n "$verbose" -a -z "$IS_INITIAL" - then - git-diff-index --cached -M -p --diff-filter=MDTCRA $REFERENCE - fi - case "$committable" in - 0) - case "$amend" in - t) - echo "# No changes" ;; - *) - echo "nothing to commit" ;; - esac - exit 1 ;; - esac - exit 0 - ) + case "$status_only" in + t) color= ;; + *) color=--nocolor ;; + esac + git-runstatus ${color} \ + ${verbose:+--verbose} \ + ${amend:+--amend} } trap ' diff --git a/git.c b/git.c index 335f405..495b39a 100644 --- a/git.c +++ b/git.c @@ -252,6 +252,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "rev-list", cmd_rev_list, RUN_SETUP }, { "rev-parse", cmd_rev_parse, RUN_SETUP }, { "rm", cmd_rm, RUN_SETUP }, + { "runstatus", cmd_runstatus, RUN_SETUP }, { "show-branch", cmd_show_branch, RUN_SETUP }, { "show", cmd_show, RUN_SETUP | USE_PAGER }, { "stripspace", cmd_stripspace }, diff --git a/wt-status.c b/wt-status.c new file mode 100644 index 0000000..ec2c728 --- /dev/null +++ b/wt-status.c @@ -0,0 +1,271 @@ +#include "wt-status.h" +#include "color.h" +#include "cache.h" +#include "object.h" +#include "dir.h" +#include "commit.h" +#include "diff.h" +#include "revision.h" +#include "diffcore.h" + +int wt_status_use_color = 0; +static char wt_status_colors[][COLOR_MAXLEN] = { + "", /* WT_STATUS_HEADER: normal */ + "\033[32m", /* WT_STATUS_UPDATED: green */ + "\033[31m", /* WT_STATUS_CHANGED: red */ + "\033[31m", /* WT_STATUS_UNTRACKED: red */ +}; + +static int parse_status_slot(const char *var, int offset) +{ + if (!strcasecmp(var+offset, "header")) + return WT_STATUS_HEADER; + if (!strcasecmp(var+offset, "updated")) + return WT_STATUS_UPDATED; + if (!strcasecmp(var+offset, "changed")) + return WT_STATUS_CHANGED; + if (!strcasecmp(var+offset, "untracked")) + return WT_STATUS_UNTRACKED; + die("bad config variable '%s'", var); +} + +static const char* color(int slot) +{ + return wt_status_use_color ? wt_status_colors[slot] : ""; +} + +void wt_status_prepare(struct wt_status *s) +{ + unsigned char sha1[20]; + const char *head; + + s->is_initial = get_sha1("HEAD", sha1) ? 1 : 0; + + head = resolve_ref(git_path("HEAD"), sha1, 0); + s->branch = head ? + strdup(head + strlen(get_git_dir()) + 1) : + NULL; + + s->reference = "HEAD"; + s->amend = 0; + s->verbose = 0; + s->commitable = 0; +} + +static void wt_status_print_header(const char *main, const char *sub) +{ + const char *c = color(WT_STATUS_HEADER); + color_printf_ln(c, "# %s:", main); + color_printf_ln(c, "# (%s)", sub); + color_printf_ln(c, "#"); +} + +static void wt_status_print_trailer(void) +{ + color_printf_ln(color(WT_STATUS_HEADER), "#"); +} + +static void wt_status_print_filepair(int t, struct diff_filepair *p) +{ + const char *c = color(t); + color_printf(color(WT_STATUS_HEADER), "#\t"); + switch (p->status) { + case DIFF_STATUS_ADDED: + color_printf(c, "new file: %s", p->one->path); break; + case DIFF_STATUS_COPIED: + color_printf(c, "copied: %s -> %s", + p->one->path, p->two->path); + break; + case DIFF_STATUS_DELETED: + color_printf_ln(c, "deleted: %s", p->one->path); break; + case DIFF_STATUS_MODIFIED: + color_printf(c, "modified: %s", p->one->path); break; + case DIFF_STATUS_RENAMED: + color_printf(c, "renamed: %s -> %s", + p->one->path, p->two->path); + break; + case DIFF_STATUS_TYPE_CHANGED: + color_printf(c, "typechange: %s", p->one->path); break; + case DIFF_STATUS_UNKNOWN: + color_printf(c, "unknown: %s", p->one->path); break; + case DIFF_STATUS_UNMERGED: + color_printf(c, "unmerged: %s", p->one->path); break; + default: + die("bug: unhandled diff status %c", p->status); + } + printf("\n"); +} + +static void wt_status_print_updated_cb(struct diff_queue_struct *q, + struct diff_options *options, + void *data) +{ + struct wt_status *s = data; + int shown_header = 0; + int i; + if (q->nr) { + } + for (i = 0; i < q->nr; i++) { + if (q->queue[i]->status == 'U') + continue; + if (!shown_header) { + wt_status_print_header("Updated but not checked in", + "will commit"); + s->commitable = 1; + shown_header = 1; + } + wt_status_print_filepair(WT_STATUS_UPDATED, q->queue[i]); + } + if (shown_header) + wt_status_print_trailer(); +} + +static void wt_status_print_changed_cb(struct diff_queue_struct *q, + struct diff_options *options, + void *data) +{ + int i; + if (q->nr) + wt_status_print_header("Changed but not updated", + "use git-update-index to mark for commit"); + for (i = 0; i < q->nr; i++) + wt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]); + if (q->nr) + wt_status_print_trailer(); +} + +void wt_status_print_initial(struct wt_status *s) +{ + int i; + read_cache(); + if (active_nr) { + s->commitable = 1; + wt_status_print_header("Updated but not checked in", + "will commit"); + } + for (i = 0; i < active_nr; i++) { + color_printf(color(WT_STATUS_HEADER), "#\t"); + color_printf_ln(color(WT_STATUS_UPDATED), "new file: %s", + active_cache[i]->name); + } + if (active_nr) + wt_status_print_trailer(); +} + +static void wt_status_print_updated(struct wt_status *s) +{ + struct rev_info rev; + const char *argv[] = { NULL, NULL, NULL }; + argv[1] = s->reference; + init_revisions(&rev, NULL); + setup_revisions(2, argv, &rev, NULL); + rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; + rev.diffopt.format_callback = wt_status_print_updated_cb; + rev.diffopt.format_callback_data = s; + rev.diffopt.detect_rename = 1; + run_diff_index(&rev, 1); +} + +static void wt_status_print_changed(struct wt_status *s) +{ + struct rev_info rev; + const char *argv[] = { NULL, NULL }; + init_revisions(&rev, ""); + setup_revisions(1, argv, &rev, NULL); + rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; + rev.diffopt.format_callback = wt_status_print_changed_cb; + rev.diffopt.format_callback_data = s; + run_diff_files(&rev, 0); +} + +static void wt_status_print_untracked(const struct wt_status *s) +{ + struct dir_struct dir; + const char *x; + int i; + int shown_header = 0; + + memset(&dir, 0, sizeof(dir)); + + dir.exclude_per_dir = ".gitignore"; + x = git_path("info/exclude"); + if (file_exists(x)) + add_excludes_from_file(&dir, x); + + read_directory(&dir, ".", "", 0); + for(i = 0; i < dir.nr; i++) { + /* check for matching entry, which is unmerged; lifted from + * builtin-ls-files:show_other_files */ + struct dir_entry *ent = dir.entries[i]; + int pos = cache_name_pos(ent->name, ent->len); + struct cache_entry *ce; + if (0 <= pos) + die("bug in wt_status_print_untracked"); + pos = -pos - 1; + if (pos < active_nr) { + ce = active_cache[pos]; + if (ce_namelen(ce) == ent->len && + !memcmp(ce->name, ent->name, ent->len)) + continue; + } + if (!shown_header) { + wt_status_print_header("Untracked files", + "use \"git add\" to add to commit"); + shown_header = 1; + } + color_printf(color(WT_STATUS_HEADER), "#\t"); + color_printf_ln(color(WT_STATUS_UNTRACKED), "%.*s", + ent->len, ent->name); + } +} + +static void wt_status_print_verbose(struct wt_status *s) +{ + struct rev_info rev; + const char *argv[] = { NULL, NULL, NULL }; + argv[1] = s->reference; + init_revisions(&rev, NULL); + setup_revisions(2, argv, &rev, NULL); + rev.diffopt.output_format |= DIFF_FORMAT_PATCH; + rev.diffopt.detect_rename = 1; + run_diff_index(&rev, 1); +} + +void wt_status_print(struct wt_status *s) +{ + if (s->branch && strcmp(s->branch, "refs/heads/master")) + color_printf_ln(color(WT_STATUS_HEADER), + "# On branch %s", s->branch); + + if (s->is_initial) { + color_printf_ln(color(WT_STATUS_HEADER), "#"); + color_printf_ln(color(WT_STATUS_HEADER), "# Initial commit"); + color_printf_ln(color(WT_STATUS_HEADER), "#"); + wt_status_print_initial(s); + } + else { + wt_status_print_updated(s); + discard_cache(); + } + + wt_status_print_changed(s); + wt_status_print_untracked(s); + + if (s->verbose && !s->is_initial) + wt_status_print_verbose(s); + if (!s->commitable) + printf("%s\n", s->amend ? "# No changes" : "nothing to commit"); +} + +int git_status_config(const char *k, const char *v) +{ + if (!strcmp(k, "status.color")) { + wt_status_use_color = git_config_colorbool(k, v); + return 0; + } + if (!strncmp(k, "status.color.", 13)) { + int slot = parse_status_slot(k, 13); + color_parse(v, k, wt_status_colors[slot]); + } + return git_default_config(k, v); +} diff --git a/wt-status.h b/wt-status.h new file mode 100644 index 0000000..75d3cfe --- /dev/null +++ b/wt-status.h @@ -0,0 +1,24 @@ +#ifndef STATUS_H +#define STATUS_H + +enum color_wt_status { + WT_STATUS_HEADER, + WT_STATUS_UPDATED, + WT_STATUS_CHANGED, + WT_STATUS_UNTRACKED, +}; + +struct wt_status { + int is_initial; + char *branch; + const char *reference; + int commitable; + int verbose; + int amend; +}; + +int git_status_config(const char *var, const char *value); +void wt_status_prepare(struct wt_status *s); +void wt_status_print(struct wt_status *s); + +#endif /* STATUS_H */ -- cgit v0.10.2-6-g49f6 From 4df096a5ca24f2f39042c51cf51b8a2bec66a2b5 Mon Sep 17 00:00:00 2001 From: Franck Bui-Huu Date: Thu, 7 Sep 2006 15:12:02 +0200 Subject: Add git-archive git-archive is a command to make TAR and ZIP archives of a git tree. It helps prevent a proliferation of git-{format}-tree commands. Instead of directly calling git-{tar,zip}-tree command, it defines a very simple API, that archiver should implement and register in "git-archive.c". This API is made up by 2 functions whose prototype is defined in "archive.h" file. - The first one is used to parse 'extra' parameters which have signification only for the specific archiver. That would allow different archive backends to have different kind of options. - The second one is used to ask to an archive backend to build the archive given some already resolved parameters. The main reason for making this API is to avoid using git-{tar,zip}-tree commands, hence making them useless. Maybe it's time for them to die ? It also implements remote operations by defining a very simple protocol: it first sends the name of the specific uploader followed the repository name (git-upload-tar git://example.org/repo.git). Then it sends options. It's done by sending a sequence of one argument per packet, with prefix "argument ", followed by a flush. The remote protocol is implemented in "git-archive.c" for client side and is triggered by "--remote=" option. For example, to fetch a TAR archive in a remote repo, you can issue: $ git archive --format=tar --remote=git://xxx/yyy/zzz.git HEAD We choose to not make a new command "git-fetch-archive" for example, avoind one more GIT command which should be nice for users (less commands to remember, keeps existing --remote option). Signed-off-by: Franck Bui-Huu Acked-by: Rene Scharfe Signed-off-by: Junio C Hamano diff --git a/.gitignore b/.gitignore index 0d608fe..a3f33d4 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ git-apply git-applymbox git-applypatch git-archimport +git-archive git-bisect git-branch git-cat-file diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt new file mode 100644 index 0000000..913528d --- /dev/null +++ b/Documentation/git-archive.txt @@ -0,0 +1,100 @@ +git-archive(1) +============== + +NAME +---- +git-archive - Creates a archive of the files in the named tree + + +SYNOPSIS +-------- +'git-archive' --format= [--list] [--prefix=/] [] + [--remote=] [path...] + +DESCRIPTION +----------- +Creates an archive of the specified format containing the tree +structure for the named tree. If is specified it is +prepended to the filenames in the archive. + +'git-archive' behaves differently when given a tree ID versus when +given a commit ID or tag ID. In the first case the current time is +used as modification time of each file in the archive. In the latter +case the commit time as recorded in the referenced commit object is +used instead. Additionally the commit ID is stored in a global +extended pax header if the tar format is used; it can be extracted +using 'git-get-tar-commit-id'. In ZIP files it is stored as a file +comment. + +OPTIONS +------- + +--format=:: + Format of the resulting archive: 'tar', 'zip'... + +--list:: + Show all available formats. + +--prefix=/:: + Prepend / to each filename in the archive. + +:: + This can be any options that the archiver backend understand. + +--remote=:: + Instead of making a tar archive from local repository, + retrieve a tar archive from a remote repository. + +:: + The tree or commit to produce an archive for. + +path:: + If one or more paths are specified, include only these in the + archive, otherwise include all files and subdirectories. + +CONFIGURATION +------------- +By default, file and directories modes are set to 0666 or 0777 in tar +archives. It is possible to change this by setting the "umask" variable +in the repository configuration as follows : + +[tar] + umask = 002 ;# group friendly + +The special umask value "user" indicates that the user's current umask +will be used instead. The default value remains 0, which means world +readable/writable files and directories. + +EXAMPLES +-------- +git archive --format=tar --prefix=junk/ HEAD | (cd /var/tmp/ && tar xf -):: + + Create a tar archive that contains the contents of the + latest commit on the current branch, and extracts it in + `/var/tmp/junk` directory. + +git archive --format=tar --prefix=git-1.4.0/ v1.4.0 | gzip >git-1.4.0.tar.gz:: + + Create a compressed tarball for v1.4.0 release. + +git archive --format=tar --prefix=git-1.4.0/ v1.4.0{caret}\{tree\} | gzip >git-1.4.0.tar.gz:: + + Create a compressed tarball for v1.4.0 release, but without a + global extended pax header. + +git archive --format=zip --prefix=git-docs/ HEAD:Documentation/ > git-1.4.0-docs.zip:: + + Put everything in the current head's Documentation/ directory + into 'git-1.4.0-docs.zip', with the prefix 'git-docs/'. + +Author +------ +Written by Franck Bui-Huu and Rene Scharfe. + +Documentation +-------------- +Documentation by David Greaves, Junio C Hamano and the git-list . + +GIT +--- +Part of the gitlink:git[7] suite diff --git a/Makefile b/Makefile index 7b3114f..8f62cef 100644 --- a/Makefile +++ b/Makefile @@ -232,7 +232,7 @@ LIB_FILE=libgit.a XDIFF_LIB=xdiff/lib.a LIB_H = \ - blob.h cache.h commit.h csum-file.h delta.h \ + archive.h blob.h cache.h commit.h csum-file.h delta.h \ diff.h object.h pack.h pkt-line.h quote.h refs.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 @@ -256,6 +256,7 @@ LIB_OBJS = \ BUILTIN_OBJS = \ builtin-add.o \ builtin-apply.o \ + builtin-archive.o \ builtin-cat-file.o \ builtin-checkout-index.o \ builtin-check-ref-format.o \ diff --git a/archive.h b/archive.h new file mode 100644 index 0000000..24b016f --- /dev/null +++ b/archive.h @@ -0,0 +1,41 @@ +#ifndef ARCHIVE_H +#define ARCHIVE_H + +#define MAX_EXTRA_ARGS 32 +#define MAX_ARGS (MAX_EXTRA_ARGS + 32) + +struct archiver_args { + const char *base; + struct tree *tree; + const unsigned char *commit_sha1; + time_t time; + const char **pathspec; + void *extra; +}; + +typedef int (*write_archive_fn_t)(struct archiver_args *); + +typedef void *(*parse_extra_args_fn_t)(int argc, const char **argv); + +struct archiver { + const char *name; + const char *remote; + struct archiver_args args; + write_archive_fn_t write_archive; + parse_extra_args_fn_t parse_extra; +}; + +extern struct archiver archivers[]; + +extern int parse_archive_args(int argc, + const char **argv, + struct archiver *ar); + +extern void parse_treeish_arg(const char **treeish, + struct archiver_args *ar_args, + const char *prefix); + +extern void parse_pathspec_arg(const char **pathspec, + struct archiver_args *args); + +#endif /* ARCHIVE_H */ diff --git a/builtin-archive.c b/builtin-archive.c new file mode 100644 index 0000000..f6bc269 --- /dev/null +++ b/builtin-archive.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2006 Franck Bui-Huu + * Copyright (c) 2006 Rene Scharfe + */ +#include +#include "cache.h" +#include "builtin.h" +#include "archive.h" +#include "commit.h" +#include "tree-walk.h" +#include "exec_cmd.h" +#include "pkt-line.h" + +static const char archive_usage[] = \ +"git-archive --format= [--prefix=/] [] [path...]"; + +struct archiver archivers[] = { + { "" /* dummy */ }, +}; + +static int run_remote_archiver(struct archiver *ar, int argc, + const char **argv) +{ + char *url, buf[1024]; + int fd[2], i, len, rv; + pid_t pid; + + sprintf(buf, "git-upload-archive"); + + url = xstrdup(ar->remote); + pid = git_connect(fd, url, buf); + if (pid < 0) + return pid; + + for (i = 1; i < argc; i++) { + if (!strncmp(argv[i], "--remote=", 9)) + continue; + packet_write(fd[1], "argument %s\n", argv[i]); + } + packet_flush(fd[1]); + + len = packet_read_line(fd[0], buf, sizeof(buf)); + if (!len) + die("git-archive: expected ACK/NAK, got EOF"); + if (buf[len-1] == '\n') + buf[--len] = 0; + if (strcmp(buf, "ACK")) { + if (len > 5 && !strncmp(buf, "NACK ", 5)) + die("git-archive: NACK %s", buf + 5); + die("git-archive: protocol error"); + } + + len = packet_read_line(fd[0], buf, sizeof(buf)); + if (len) + die("git-archive: expected a flush"); + + /* Now, start reading from fd[0] and spit it out to stdout */ + rv = copy_fd(fd[0], 1); + + close(fd[0]); + rv |= finish_connect(pid); + + return !!rv; +} + +static int init_archiver(const char *name, struct archiver *ar) +{ + int rv = -1, i; + + for (i = 0; i < ARRAY_SIZE(archivers); i++) { + if (!strcmp(name, archivers[i].name)) { + memcpy(ar, &archivers[i], sizeof(struct archiver)); + rv = 0; + break; + } + } + return rv; +} + +void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args) +{ + ar_args->pathspec = get_pathspec(ar_args->base, pathspec); +} + +void parse_treeish_arg(const char **argv, struct archiver_args *ar_args, + const char *prefix) +{ + const char *name = argv[0]; + const unsigned char *commit_sha1; + time_t archive_time; + struct tree *tree; + struct commit *commit; + unsigned char sha1[20]; + + if (get_sha1(name, sha1)) + die("Not a valid object name"); + + commit = lookup_commit_reference_gently(sha1, 1); + if (commit) { + commit_sha1 = commit->object.sha1; + archive_time = commit->date; + } else { + commit_sha1 = NULL; + archive_time = time(NULL); + } + + tree = parse_tree_indirect(sha1); + if (tree == NULL) + die("not a tree object"); + + if (prefix) { + unsigned char tree_sha1[20]; + unsigned int mode; + int err; + + err = get_tree_entry(tree->object.sha1, prefix, + tree_sha1, &mode); + if (err || !S_ISDIR(mode)) + die("current working directory is untracked"); + + free(tree); + tree = parse_tree_indirect(tree_sha1); + } + ar_args->tree = tree; + ar_args->commit_sha1 = commit_sha1; + ar_args->time = archive_time; +} + +static const char *default_parse_extra(struct archiver *ar, + const char **argv) +{ + static char msg[64]; + + snprintf(msg, sizeof(msg) - 4, "'%s' format does not handle %s", + ar->name, *argv); + + return strcat(msg, "..."); +} + +int parse_archive_args(int argc, const char **argv, struct archiver *ar) +{ + const char *extra_argv[MAX_EXTRA_ARGS]; + int extra_argc = 0; + const char *format = NULL; /* might want to default to "tar" */ + const char *remote = NULL; + const char *base = ""; + int list = 0; + int i; + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) { + list = 1; + continue; + } + if (!strncmp(arg, "--format=", 9)) { + format = arg + 9; + continue; + } + if (!strncmp(arg, "--prefix=", 9)) { + base = arg + 9; + continue; + } + if (!strncmp(arg, "--remote=", 9)) { + remote = arg + 9; + continue; + } + if (!strcmp(arg, "--")) { + i++; + break; + } + if (arg[0] == '-') { + if (extra_argc > MAX_EXTRA_ARGS - 1) + die("Too many extra options"); + extra_argv[extra_argc++] = arg; + continue; + } + break; + } + + if (list) { + if (!remote) { + for (i = 0; i < ARRAY_SIZE(archivers); i++) + printf("%s\n", archivers[i].name); + exit(0); + } + die("--list and --remote are mutually exclusive"); + } + + if (argc - i < 1) + usage(archive_usage); + if (!format) + die("You must specify an archive format"); + if (init_archiver(format, ar) < 0) + die("Unknown archive format '%s'", format); + + if (extra_argc && !remote) { + if (!ar->parse_extra) { + die("%s", default_parse_extra(ar, extra_argv)); + } + ar->args.extra = ar->parse_extra(extra_argc, extra_argv); + } + ar->remote = remote; + ar->args.base = base; + + return i; +} + +int cmd_archive(int argc, const char **argv, const char *prefix) +{ + struct archiver ar; + int tree_idx; + + tree_idx = parse_archive_args(argc, argv, &ar); + + if (ar.remote) + return run_remote_archiver(&ar, argc, argv); + + if (prefix == NULL) + prefix = setup_git_directory(); + + argv += tree_idx; + parse_treeish_arg(argv, &ar.args, prefix); + parse_pathspec_arg(argv + 1, &ar.args); + + return ar.write_archive(&ar.args); +} diff --git a/builtin.h b/builtin.h index 25431d7..50852cd 100644 --- a/builtin.h +++ b/builtin.h @@ -15,6 +15,7 @@ extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix); extern int cmd_add(int argc, const char **argv, const char *prefix); extern int cmd_apply(int argc, const char **argv, const char *prefix); +extern int cmd_archive(int argc, const char **argv, const char *prefix); extern int cmd_cat_file(int argc, const char **argv, const char *prefix); extern int cmd_checkout_index(int argc, const char **argv, const char *prefix); extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix); diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh index ec1eda2..5450918 100755 --- a/generate-cmdlist.sh +++ b/generate-cmdlist.sh @@ -12,6 +12,7 @@ struct cmdname_help common_cmds[] = {" sort <<\EOF | add apply +archive bisect branch checkout diff --git a/git.c b/git.c index 335f405..8c64b27 100644 --- a/git.c +++ b/git.c @@ -220,6 +220,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) } commands[] = { { "add", cmd_add, RUN_SETUP }, { "apply", cmd_apply }, + { "archive", cmd_archive }, { "cat-file", cmd_cat_file, RUN_SETUP }, { "checkout-index", cmd_checkout_index, RUN_SETUP }, { "check-ref-format", cmd_check_ref_format }, -- cgit v0.10.2-6-g49f6 From efd8696cd7ccfa5042ef710e89fe0d10efdcd085 Mon Sep 17 00:00:00 2001 From: Franck Bui-Huu Date: Thu, 7 Sep 2006 15:12:03 +0200 Subject: git-archive: wire up TAR format. This is based on Rene Scharfe's earlier patch, but uses the archiver support introduced by the previous patch. Signed-off-by: Franck Bui-Huu Acked-by: Rene Scharfe Signed-off-by: Junio C Hamano diff --git a/archive.h b/archive.h index 24b016f..5c3f29b 100644 --- a/archive.h +++ b/archive.h @@ -37,5 +37,9 @@ extern void parse_treeish_arg(const char **treeish, extern void parse_pathspec_arg(const char **pathspec, struct archiver_args *args); +/* + * Archive-format specific backends. + */ +extern int write_tar_archive(struct archiver_args *); #endif /* ARCHIVE_H */ diff --git a/builtin-archive.c b/builtin-archive.c index f6bc269..c6423b9 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -15,7 +15,7 @@ static const char archive_usage[] = \ "git-archive --format= [--prefix=/] [] [path...]"; struct archiver archivers[] = { - { "" /* dummy */ }, + { .name = "tar", .write_archive = write_tar_archive }, }; static int run_remote_archiver(struct archiver *ar, int argc, diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c index fa666f7..c20eb0e 100644 --- a/builtin-tar-tree.c +++ b/builtin-tar-tree.c @@ -9,6 +9,7 @@ #include "tar.h" #include "builtin.h" #include "pkt-line.h" +#include "archive.h" #define RECORDSIZE (512) #define BLOCKSIZE (RECORDSIZE * 20) @@ -338,6 +339,72 @@ static int generate_tar(int argc, const char **argv, const char *prefix) return 0; } +static int write_tar_entry(const unsigned char *sha1, + const char *base, int baselen, + const char *filename, unsigned mode, int stage) +{ + static struct strbuf path; + int filenamelen = strlen(filename); + void *buffer; + char type[20]; + unsigned long size; + + if (!path.alloc) { + path.buf = xmalloc(PATH_MAX); + path.alloc = PATH_MAX; + path.len = path.eof = 0; + } + if (path.alloc < baselen + filenamelen) { + free(path.buf); + path.buf = xmalloc(baselen + filenamelen); + path.alloc = baselen + filenamelen; + } + memcpy(path.buf, base, baselen); + memcpy(path.buf + baselen, filename, filenamelen); + path.len = baselen + filenamelen; + if (S_ISDIR(mode)) { + strbuf_append_string(&path, "/"); + buffer = NULL; + size = 0; + } else { + buffer = read_sha1_file(sha1, type, &size); + if (!buffer) + die("cannot read %s", sha1_to_hex(sha1)); + } + + write_entry(sha1, &path, mode, buffer, size); + free(buffer); + + return READ_TREE_RECURSIVE; +} + +int write_tar_archive(struct archiver_args *args) +{ + int plen = strlen(args->base); + + git_config(git_tar_config); + + archive_time = args->time; + + if (args->commit_sha1) + write_global_extended_header(args->commit_sha1); + + if (args->base && plen > 0 && args->base[plen - 1] == '/') { + char *base = strdup(args->base); + int baselen = strlen(base); + + while (baselen > 0 && base[baselen - 1] == '/') + base[--baselen] = '\0'; + write_tar_entry(args->tree->object.sha1, "", 0, base, 040777, 0); + free(base); + } + read_tree_recursive(args->tree, args->base, plen, 0, + args->pathspec, write_tar_entry); + write_trailer(); + + return 0; +} + static const char *exec = "git-upload-tar"; static int remote_tar(int argc, const char **argv) -- cgit v0.10.2-6-g49f6 From ec06bff5e6f76b46c22a3b2c97452568f088fa3c Mon Sep 17 00:00:00 2001 From: Franck Bui-Huu Date: Thu, 7 Sep 2006 15:12:04 +0200 Subject: git-archive: wire up ZIP format. Again, this is based on Rene Scharfe's earlier patch, but uses the archiver support introduced by the previous patch. Signed-off-by: Franck Bui-Huu Acked-by: Rene Scharfe Signed-off-by: Junio C Hamano diff --git a/archive.h b/archive.h index 5c3f29b..f3d344b 100644 --- a/archive.h +++ b/archive.h @@ -41,5 +41,6 @@ extern void parse_pathspec_arg(const char **pathspec, * Archive-format specific backends. */ extern int write_tar_archive(struct archiver_args *); +extern int write_zip_archive(struct archiver_args *); #endif /* ARCHIVE_H */ diff --git a/builtin-archive.c b/builtin-archive.c index c6423b9..651d1bf 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -16,6 +16,7 @@ static const char archive_usage[] = \ struct archiver archivers[] = { { .name = "tar", .write_archive = write_tar_archive }, + { .name = "zip", .write_archive = write_zip_archive }, }; static int run_remote_archiver(struct archiver *ar, int argc, diff --git a/builtin-zip-tree.c b/builtin-zip-tree.c index 1c1f683..3afb7bd 100644 --- a/builtin-zip-tree.c +++ b/builtin-zip-tree.c @@ -8,6 +8,7 @@ #include "tree.h" #include "quote.h" #include "builtin.h" +#include "archive.h" static const char zip_tree_usage[] = "git-zip-tree [-0|...|-9] [ ]"; @@ -351,3 +352,30 @@ int cmd_zip_tree(int argc, const char **argv, const char *prefix) return 0; } + +int write_zip_archive(struct archiver_args *args) +{ + int plen = strlen(args->base); + + dos_time(&args->time, &zip_date, &zip_time); + + zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE); + zip_dir_size = ZIP_DIRECTORY_MIN_SIZE; + + if (args->base && plen > 0 && args->base[plen - 1] == '/') { + char *base = strdup(args->base); + int baselen = strlen(base); + + while (baselen > 0 && base[baselen - 1] == '/') + base[--baselen] = '\0'; + write_zip_entry(args->tree->object.sha1, "", 0, base, 040777, 0); + free(base); + } + read_tree_recursive(args->tree, args->base, plen, 0, + args->pathspec, write_zip_entry); + write_zip_trailer(args->commit_sha1); + + free(zip_dir); + + return 0; +} -- cgit v0.10.2-6-g49f6 From 39345a216ff37bda9fb7cec85f6de44069f5205d Mon Sep 17 00:00:00 2001 From: Franck Bui-Huu Date: Thu, 7 Sep 2006 15:12:05 +0200 Subject: Add git-upload-archive This command implements the git archive protocol on the server side. This command is not intended to be used by the end user. Underlying git-archive command line options are sent over the protocol from "git-archive --remote=...", just like upload-tar currently does with "git-tar-tree=...". As for "git-archive" command implementation, this new command does not execute any existing "git-{tar,zip}-tree" but rely on the archive API defined by "git-archive" patch. Hence we get 2 good points: - "git-archive" and "git-upload-archive" share all option parsing code. - All kind of git-upload-{tar,zip} can be deprecated. Signed-off-by: Franck Bui-Huu Signed-off-by: Junio C Hamano diff --git a/.gitignore b/.gitignore index a3f33d4..90d6d7c 100644 --- a/.gitignore +++ b/.gitignore @@ -119,6 +119,7 @@ git-unpack-objects git-update-index git-update-ref git-update-server-info +git-upload-archive git-upload-pack git-upload-tar git-var diff --git a/Documentation/git-upload-archive.txt b/Documentation/git-upload-archive.txt new file mode 100644 index 0000000..388bb53 --- /dev/null +++ b/Documentation/git-upload-archive.txt @@ -0,0 +1,37 @@ +git-upload-archive(1) +==================== + +NAME +---- +git-upload-archive - Send archive + + +SYNOPSIS +-------- +'git-upload-archive' + +DESCRIPTION +----------- +Invoked by 'git-archive --remote' and sends a generated archive to the +other end over the git protocol. + +This command is usually not invoked directly by the end user. The UI +for the protocol is on the 'git-archive' side, and the program pair +is meant to be used to get an archive from a remote repository. + +OPTIONS +------- +:: + The repository to get a tar archive from. + +Author +------ +Written by Franck Bui-Huu. + +Documentation +-------------- +Documentation by Junio C Hamano and the git-list . + +GIT +--- +Part of the gitlink:git[7] suite diff --git a/Makefile b/Makefile index 8f62cef..4ac85fd 100644 --- a/Makefile +++ b/Makefile @@ -293,6 +293,7 @@ BUILTIN_OBJS = \ builtin-unpack-objects.o \ builtin-update-index.o \ builtin-update-ref.o \ + builtin-upload-archive.o \ builtin-upload-tar.o \ builtin-verify-pack.o \ builtin-write-tree.o \ diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c new file mode 100644 index 0000000..3bdb607 --- /dev/null +++ b/builtin-upload-archive.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2006 Franck Bui-Huu + */ +#include +#include "cache.h" +#include "builtin.h" +#include "archive.h" +#include "pkt-line.h" + +static const char upload_archive_usage[] = + "git-upload-archive "; + + +int cmd_upload_archive(int argc, const char **argv, const char *prefix) +{ + struct archiver ar; + const char *sent_argv[MAX_ARGS]; + const char *arg_cmd = "argument "; + char *p, buf[4096]; + int treeish_idx; + int sent_argc; + int len; + + if (argc != 2) + usage(upload_archive_usage); + + if (strlen(argv[1]) > sizeof(buf)) + die("insanely long repository name"); + + strcpy(buf, argv[1]); /* enter-repo smudges its argument */ + + if (!enter_repo(buf, 0)) + die("not a git archive"); + + /* put received options in sent_argv[] */ + sent_argc = 1; + sent_argv[0] = "git-upload-archive"; + for (p = buf;;) { + /* This will die if not enough free space in buf */ + len = packet_read_line(0, p, (buf + sizeof buf) - p); + if (len == 0) + break; /* got a flush */ + if (sent_argc > MAX_ARGS - 2) + die("Too many options (>29)"); + + if (p[len-1] == '\n') { + p[--len] = 0; + } + if (len < strlen(arg_cmd) || + strncmp(arg_cmd, p, strlen(arg_cmd))) + die("'argument' token or flush expected"); + + len -= strlen(arg_cmd); + memmove(p, p + strlen(arg_cmd), len); + sent_argv[sent_argc++] = p; + p += len; + *p++ = 0; + } + sent_argv[sent_argc] = NULL; + + /* parse all options sent by the client */ + treeish_idx = parse_archive_args(sent_argc, sent_argv, &ar); + + parse_treeish_arg(sent_argv + treeish_idx, &ar.args, prefix); + parse_pathspec_arg(sent_argv + treeish_idx + 1, &ar.args); + + packet_write(1, "ACK\n"); + packet_flush(1); + + return ar.write_archive(&ar.args); +} + diff --git a/builtin.h b/builtin.h index 50852cd..6bfd2e7 100644 --- a/builtin.h +++ b/builtin.h @@ -57,6 +57,7 @@ extern int cmd_zip_tree(int argc, const char **argv, const char *prefix); extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix); extern int cmd_update_index(int argc, const char **argv, const char *prefix); extern int cmd_update_ref(int argc, const char **argv, const char *prefix); +extern int cmd_upload_archive(int argc, const char **argv, const char *prefix); extern int cmd_upload_tar(int argc, const char **argv, const char *prefix); extern int cmd_version(int argc, const char **argv, const char *prefix); extern int cmd_whatchanged(int argc, const char **argv, const char *prefix); diff --git a/daemon.c b/daemon.c index b14d808..a2954a0 100644 --- a/daemon.c +++ b/daemon.c @@ -325,7 +325,14 @@ static int upload_pack(void) return -1; } +static int upload_archive(void) +{ + execl_git_cmd("upload-archive", ".", NULL); + return -1; +} + static struct daemon_service daemon_service[] = { + { "upload-archive", "uploadarch", upload_archive, 0, 1 }, { "upload-pack", "uploadpack", upload_pack, 1, 1 }, }; diff --git a/git.c b/git.c index 8c64b27..bcf3fc8 100644 --- a/git.c +++ b/git.c @@ -262,6 +262,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "unpack-objects", cmd_unpack_objects, RUN_SETUP }, { "update-index", cmd_update_index, RUN_SETUP }, { "update-ref", cmd_update_ref, RUN_SETUP }, + { "upload-archive", cmd_upload_archive }, { "upload-tar", cmd_upload_tar }, { "version", cmd_version }, { "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER }, -- cgit v0.10.2-6-g49f6 From 854c4168e77a692dc198311f04bf31939355f2a3 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Sat, 9 Sep 2006 17:02:38 +0200 Subject: git-archive: make compression level of ZIP archives configurable Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano diff --git a/archive.h b/archive.h index f3d344b..d8cca73 100644 --- a/archive.h +++ b/archive.h @@ -42,5 +42,6 @@ extern void parse_pathspec_arg(const char **pathspec, */ extern int write_tar_archive(struct archiver_args *); extern int write_zip_archive(struct archiver_args *); +extern void *parse_extra_zip_args(int argc, const char **argv); #endif /* ARCHIVE_H */ diff --git a/builtin-archive.c b/builtin-archive.c index 651d1bf..b944737 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -15,8 +15,15 @@ static const char archive_usage[] = \ "git-archive --format= [--prefix=/] [] [path...]"; struct archiver archivers[] = { - { .name = "tar", .write_archive = write_tar_archive }, - { .name = "zip", .write_archive = write_zip_archive }, + { + .name = "tar", + .write_archive = write_tar_archive, + }, + { + .name = "zip", + .write_archive = write_zip_archive, + .parse_extra = parse_extra_zip_args, + }, }; static int run_remote_archiver(struct archiver *ar, int argc, diff --git a/builtin-zip-tree.c b/builtin-zip-tree.c index 3afb7bd..4e79633 100644 --- a/builtin-zip-tree.c +++ b/builtin-zip-tree.c @@ -379,3 +379,16 @@ int write_zip_archive(struct archiver_args *args) return 0; } + +void *parse_extra_zip_args(int argc, const char **argv) +{ + for (; argc > 0; argc--, argv++) { + const char *arg = argv[0]; + + if (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\0') + zlib_compression_level = arg[1] - '0'; + else + die("Unknown argument for zip format: %s", arg); + } + return NULL; +} -- cgit v0.10.2-6-g49f6 From 37f944363d5b5fb5bcbf2d184865534739713c01 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 9 Sep 2006 23:48:03 -0700 Subject: archive: allow remote to have more formats than we understand. This fixes git-archive --remote not to parse archiver arguments; otherwise if the remote end implements formats other than the one known locally we will not be able to access that format. Signed-off-by: Junio C Hamano diff --git a/archive.h b/archive.h index d8cca73..e0782b9 100644 --- a/archive.h +++ b/archive.h @@ -19,7 +19,6 @@ typedef void *(*parse_extra_args_fn_t)(int argc, const char **argv); struct archiver { const char *name; - const char *remote; struct archiver_args args; write_archive_fn_t write_archive; parse_extra_args_fn_t parse_extra; diff --git a/builtin-archive.c b/builtin-archive.c index b944737..c70488c 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -26,7 +26,7 @@ struct archiver archivers[] = { }, }; -static int run_remote_archiver(struct archiver *ar, int argc, +static int run_remote_archiver(const char *remote, int argc, const char **argv) { char *url, buf[1024]; @@ -35,16 +35,13 @@ static int run_remote_archiver(struct archiver *ar, int argc, sprintf(buf, "git-upload-archive"); - url = xstrdup(ar->remote); + url = xstrdup(remote); pid = git_connect(fd, url, buf); if (pid < 0) return pid; - for (i = 1; i < argc; i++) { - if (!strncmp(argv[i], "--remote=", 9)) - continue; + for (i = 1; i < argc; i++) packet_write(fd[1], "argument %s\n", argv[i]); - } packet_flush(fd[1]); len = packet_read_line(fd[0], buf, sizeof(buf)); @@ -150,17 +147,16 @@ int parse_archive_args(int argc, const char **argv, struct archiver *ar) const char *extra_argv[MAX_EXTRA_ARGS]; int extra_argc = 0; const char *format = NULL; /* might want to default to "tar" */ - const char *remote = NULL; const char *base = ""; - int list = 0; int i; for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) { - list = 1; - continue; + for (i = 0; i < ARRAY_SIZE(archivers); i++) + printf("%s\n", archivers[i].name); + exit(0); } if (!strncmp(arg, "--format=", 9)) { format = arg + 9; @@ -170,10 +166,6 @@ int parse_archive_args(int argc, const char **argv, struct archiver *ar) base = arg + 9; continue; } - if (!strncmp(arg, "--remote=", 9)) { - remote = arg + 9; - continue; - } if (!strcmp(arg, "--")) { i++; break; @@ -187,44 +179,67 @@ int parse_archive_args(int argc, const char **argv, struct archiver *ar) break; } - if (list) { - if (!remote) { - for (i = 0; i < ARRAY_SIZE(archivers); i++) - printf("%s\n", archivers[i].name); - exit(0); - } - die("--list and --remote are mutually exclusive"); - } - - if (argc - i < 1) + /* We need at least one parameter -- tree-ish */ + if (argc - 1 < i) usage(archive_usage); if (!format) die("You must specify an archive format"); if (init_archiver(format, ar) < 0) die("Unknown archive format '%s'", format); - if (extra_argc && !remote) { - if (!ar->parse_extra) { + if (extra_argc) { + if (!ar->parse_extra) die("%s", default_parse_extra(ar, extra_argv)); - } ar->args.extra = ar->parse_extra(extra_argc, extra_argv); } - ar->remote = remote; ar->args.base = base; return i; } +static const char *remote_request(int *ac, const char **av) +{ + int ix, iy, cnt = *ac; + int no_more_options = 0; + const char *remote = NULL; + + for (ix = iy = 1; ix < cnt; ix++) { + const char *arg = av[ix]; + if (!strcmp(arg, "--")) + no_more_options = 1; + if (!no_more_options) { + if (!strncmp(arg, "--remote=", 9)) { + if (remote) + die("Multiple --remote specified"); + remote = arg + 9; + continue; + } + if (arg[0] != '-') + no_more_options = 1; + } + if (ix != iy) + av[iy] = arg; + iy++; + } + if (remote) { + av[--cnt] = NULL; + *ac = cnt; + } + return remote; +} + int cmd_archive(int argc, const char **argv, const char *prefix) { struct archiver ar; int tree_idx; + const char *remote = NULL; - tree_idx = parse_archive_args(argc, argv, &ar); - - if (ar.remote) - return run_remote_archiver(&ar, argc, argv); + remote = remote_request(&argc, argv); + if (remote) + return run_remote_archiver(remote, argc, argv); + memset(&ar, 0, sizeof(ar)); + tree_idx = parse_archive_args(argc, argv, &ar); if (prefix == NULL) prefix = setup_git_directory(); -- cgit v0.10.2-6-g49f6 From 49a52b1d1fb120f52de3a67f1e4e5ae81512ab81 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 Sep 2006 01:06:33 -0700 Subject: Move sideband client side support into reusable form. This moves the receiver side of the sideband support from fetch-clone.c to sideband.c and its header file, so that archiver protocol can use it. Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index 7b3114f..a46cd52 100644 --- a/Makefile +++ b/Makefile @@ -233,7 +233,7 @@ XDIFF_LIB=xdiff/lib.a LIB_H = \ blob.h cache.h commit.h csum-file.h delta.h \ - diff.h object.h pack.h pkt-line.h quote.h refs.h \ + diff.h object.h pack.h pkt-line.h quote.h refs.h sideband.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 @@ -245,7 +245,7 @@ DIFF_OBJS = \ LIB_OBJS = \ blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \ date.o diff-delta.o entry.o exec_cmd.o ident.o lockfile.o \ - object.o pack-check.o patch-delta.o path.o pkt-line.o \ + object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \ quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \ server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \ tag.o tree.o usage.o config.o environment.o ctype.o copy.o \ diff --git a/fetch-clone.c b/fetch-clone.c index c5cf477..b62feac 100644 --- a/fetch-clone.c +++ b/fetch-clone.c @@ -1,6 +1,7 @@ #include "cache.h" #include "exec_cmd.h" #include "pkt-line.h" +#include "sideband.h" #include #include @@ -114,36 +115,13 @@ static pid_t setup_sideband(int sideband, const char *me, int fd[2], int xd[2]) die("%s: unable to fork off sideband demultiplexer", me); if (!side_pid) { /* subprocess */ + char buf[DEFAULT_PACKET_MAX]; + close(fd[0]); if (xd[0] != xd[1]) close(xd[1]); - while (1) { - char buf[1024]; - int len = packet_read_line(xd[0], buf, sizeof(buf)); - if (len == 0) - break; - if (len < 1) - die("%s: protocol error: no band designator", - me); - len--; - switch (buf[0] & 0xFF) { - case 3: - safe_write(2, "remote: ", 8); - safe_write(2, buf+1, len); - safe_write(2, "\n", 1); - exit(1); - case 2: - safe_write(2, "remote: ", 8); - safe_write(2, buf+1, len); - continue; - case 1: - safe_write(fd[1], buf+1, len); - continue; - default: - die("%s: protocol error: bad band #%d", - me, (buf[0] & 0xFF)); - } - } + if (recv_sideband(me, xd[0], fd[1], 2, buf, sizeof(buf))) + exit(1); exit(0); } close(xd[0]); diff --git a/sideband.c b/sideband.c new file mode 100644 index 0000000..861f621 --- /dev/null +++ b/sideband.c @@ -0,0 +1,48 @@ +#include "pkt-line.h" +#include "sideband.h" + +/* + * Receive multiplexed output stream over git native protocol. + * in_stream is the input stream from the remote, which carries data + * in pkt_line format with band designator. Demultiplex it into out + * and err and return error appropriately. Band #1 carries the + * primary payload. Things coming over band #2 is not necessarily + * error; they are usually informative message on the standard error + * stream, aka "verbose"). A message over band #3 is a signal that + * the remote died unexpectedly. A flush() concludes the stream. + */ +int recv_sideband(const char *me, int in_stream, int out, int err, char *buf, int bufsz) +{ + while (1) { + int len = packet_read_line(in_stream, buf, bufsz); + if (len == 0) + break; + if (len < 1) { + len = sprintf(buf, "%s: protocol error: no band designator\n", me); + safe_write(err, buf, len); + return SIDEBAND_PROTOCOL_ERROR; + } + len--; + switch (buf[0] & 0xFF) { + case 3: + safe_write(err, "remote: ", 8); + safe_write(err, buf+1, len); + safe_write(err, "\n", 1); + return SIDEBAND_REMOTE_ERROR; + case 2: + safe_write(err, "remote: ", 8); + safe_write(err, buf+1, len); + continue; + case 1: + safe_write(out, buf+1, len); + continue; + default: + len = sprintf(buf + 1, + "%s: protocol error: bad band #%d\n", + me, buf[0] & 0xFF); + safe_write(err, buf+1, len); + return SIDEBAND_PROTOCOL_ERROR; + } + } + return 0; +} diff --git a/sideband.h b/sideband.h new file mode 100644 index 0000000..90b3855 --- /dev/null +++ b/sideband.h @@ -0,0 +1,11 @@ +#ifndef SIDEBAND_H +#define SIDEBAND_H + +#define SIDEBAND_PROTOCOL_ERROR -2 +#define SIDEBAND_REMOTE_ERROR -1 + +#define DEFAULT_PACKET_MAX 1000 + +int recv_sideband(const char *me, int in_stream, int out, int err, char *, int); + +#endif -- cgit v0.10.2-6-g49f6 From 958c24b1b8f463bca857f45c41a2f8198e345c2f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 Sep 2006 03:20:24 -0700 Subject: Move sideband server side support into reusable form. The server side support; this is just the very low level, and the caller needs to know which band it wants to send things out. Signed-off-by: Junio C Hamano (cherry picked from b786552b67878c7780c50def4c069d46dc54efbe commit) diff --git a/sideband.c b/sideband.c index 861f621..1b14ff8 100644 --- a/sideband.c +++ b/sideband.c @@ -46,3 +46,29 @@ int recv_sideband(const char *me, int in_stream, int out, int err, char *buf, in } return 0; } + +/* + * fd is connected to the remote side; send the sideband data + * over multiplexed packet stream. + */ +ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max) +{ + ssize_t ssz = sz; + const char *p = data; + + while (sz) { + unsigned n; + char hdr[5]; + + n = sz; + if (packet_max - 5 < n) + n = packet_max - 5; + sprintf(hdr, "%04x", n + 5); + hdr[4] = band; + safe_write(fd, hdr, 5); + safe_write(fd, p, n); + p += n; + sz -= n; + } + return ssz; +} diff --git a/sideband.h b/sideband.h index 90b3855..c645cf2 100644 --- a/sideband.h +++ b/sideband.h @@ -7,5 +7,6 @@ #define DEFAULT_PACKET_MAX 1000 int recv_sideband(const char *me, int in_stream, int out, int err, char *, int); +ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max); #endif diff --git a/upload-pack.c b/upload-pack.c index 51ce936..1f2f7f7 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -4,6 +4,7 @@ #include "cache.h" #include "refs.h" #include "pkt-line.h" +#include "sideband.h" #include "tag.h" #include "object.h" #include "commit.h" @@ -33,45 +34,19 @@ static int strip(char *line, int len) return len; } -#define PACKET_MAX 1000 static ssize_t send_client_data(int fd, const char *data, ssize_t sz) { - ssize_t ssz; - const char *p; - - if (!data) { - if (!use_sideband) - return 0; - packet_flush(1); - } - - if (!use_sideband) { - if (fd == 3) - /* emergency quit */ - fd = 2; - if (fd == 2) { - xwrite(fd, data, sz); - return sz; - } - return safe_write(fd, data, sz); - } - p = data; - ssz = sz; - while (sz) { - unsigned n; - char hdr[5]; - - n = sz; - if (PACKET_MAX - 5 < n) - n = PACKET_MAX - 5; - sprintf(hdr, "%04x", n + 5); - hdr[4] = fd; - safe_write(1, hdr, 5); - safe_write(1, p, n); - p += n; - sz -= n; + if (use_sideband) + return send_sideband(1, fd, data, sz, DEFAULT_PACKET_MAX); + + if (fd == 3) + /* emergency quit */ + fd = 2; + if (fd == 2) { + xwrite(fd, data, sz); + return sz; } - return ssz; + return safe_write(fd, data, sz); } static void create_pack_file(void) @@ -308,7 +283,8 @@ static void create_pack_file(void) goto fail; fprintf(stderr, "flushed.\n"); } - send_client_data(1, NULL, 0); + if (use_sideband) + packet_flush(1); return; } fail: -- cgit v0.10.2-6-g49f6 From 326711c16879792da8d7159bf29d080569c3a1e0 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Sun, 10 Sep 2006 18:10:01 +0200 Subject: Use xstrdup instead of strdup in builtin-{tar,zip}-tree.c Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano (cherry picked from 5d2aea4cb383a43e40d47ab69d8ad7a495df6ea2 commit) diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c index c20eb0e..e8e492f 100644 --- a/builtin-tar-tree.c +++ b/builtin-tar-tree.c @@ -390,7 +390,7 @@ int write_tar_archive(struct archiver_args *args) write_global_extended_header(args->commit_sha1); if (args->base && plen > 0 && args->base[plen - 1] == '/') { - char *base = strdup(args->base); + char *base = xstrdup(args->base); int baselen = strlen(base); while (baselen > 0 && base[baselen - 1] == '/') diff --git a/builtin-zip-tree.c b/builtin-zip-tree.c index 4e79633..fdac2bd 100644 --- a/builtin-zip-tree.c +++ b/builtin-zip-tree.c @@ -363,7 +363,7 @@ int write_zip_archive(struct archiver_args *args) zip_dir_size = ZIP_DIRECTORY_MIN_SIZE; if (args->base && plen > 0 && args->base[plen - 1] == '/') { - char *base = strdup(args->base); + char *base = xstrdup(args->base); int baselen = strlen(base); while (baselen > 0 && base[baselen - 1] == '/') -- cgit v0.10.2-6-g49f6 From 8142f603b9955648228549d2e83ace7fbe834114 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 Sep 2006 04:16:39 -0700 Subject: archive: force line buffered output to stderr Otherwise the remote notification that comes with -v option can get clumped together. Signed-off-by: Junio C Hamano (cherry picked from a675cda60ead41f439b04bc69e0f19ace04e59d3 commit) diff --git a/builtin-archive.c b/builtin-archive.c index c70488c..3a8be57 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -238,6 +238,8 @@ int cmd_archive(int argc, const char **argv, const char *prefix) if (remote) return run_remote_archiver(remote, argc, argv); + setlinebuf(stderr); + memset(&ar, 0, sizeof(ar)); tree_idx = parse_archive_args(argc, argv, &ar); if (prefix == NULL) -- cgit v0.10.2-6-g49f6 From e0ffb24877d4530208905512f7c91dd8d71e2c95 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 9 Sep 2006 22:42:02 -0700 Subject: Add --verbose to git-archive And teach backends about it. Signed-off-by: Junio C Hamano (cherry picked from 9e2c44a2893ae90944a0b7c9f40a9d22b759b5c0 commit) diff --git a/archive.h b/archive.h index e0782b9..16dcdb8 100644 --- a/archive.h +++ b/archive.h @@ -10,6 +10,7 @@ struct archiver_args { const unsigned char *commit_sha1; time_t time; const char **pathspec; + unsigned int verbose : 1; void *extra; }; diff --git a/builtin-archive.c b/builtin-archive.c index 3a8be57..7544ad3 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -12,7 +12,7 @@ #include "pkt-line.h" static const char archive_usage[] = \ -"git-archive --format= [--prefix=/] [] [path...]"; +"git-archive --format= [--prefix=/] [--verbose] [] [path...]"; struct archiver archivers[] = { { @@ -148,6 +148,7 @@ int parse_archive_args(int argc, const char **argv, struct archiver *ar) int extra_argc = 0; const char *format = NULL; /* might want to default to "tar" */ const char *base = ""; + int verbose = 0; int i; for (i = 1; i < argc; i++) { @@ -158,6 +159,10 @@ int parse_archive_args(int argc, const char **argv, struct archiver *ar) printf("%s\n", archivers[i].name); exit(0); } + if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) { + verbose = 1; + continue; + } if (!strncmp(arg, "--format=", 9)) { format = arg + 9; continue; @@ -192,6 +197,7 @@ int parse_archive_args(int argc, const char **argv, struct archiver *ar) die("%s", default_parse_extra(ar, extra_argv)); ar->args.extra = ar->parse_extra(extra_argc, extra_argv); } + ar->args.verbose = verbose; ar->args.base = base; return i; diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c index e8e492f..f2679a8 100644 --- a/builtin-tar-tree.c +++ b/builtin-tar-tree.c @@ -22,6 +22,7 @@ static unsigned long offset; static time_t archive_time; static int tar_umask; +static int verbose; /* writes out the whole block, but only if it is full */ static void write_if_needed(void) @@ -169,6 +170,8 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, mode = 0100666; sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1)); } else { + if (verbose) + fprintf(stderr, "%.*s\n", path->len, path->buf); if (S_ISDIR(mode)) { *header.typeflag = TYPEFLAG_DIR; mode = (mode | 0777) & ~tar_umask; @@ -385,6 +388,7 @@ int write_tar_archive(struct archiver_args *args) git_config(git_tar_config); archive_time = args->time; + verbose = args->verbose; if (args->commit_sha1) write_global_extended_header(args->commit_sha1); diff --git a/builtin-zip-tree.c b/builtin-zip-tree.c index fdac2bd..52d4b7a 100644 --- a/builtin-zip-tree.c +++ b/builtin-zip-tree.c @@ -13,6 +13,7 @@ static const char zip_tree_usage[] = "git-zip-tree [-0|...|-9] [ ]"; +static int verbose; static int zip_date; static int zip_time; @@ -164,6 +165,8 @@ static int write_zip_entry(const unsigned char *sha1, crc = crc32(0, Z_NULL, 0); path = construct_path(base, baselen, filename, S_ISDIR(mode), &pathlen); + if (verbose) + fprintf(stderr, "%s\n", path); if (pathlen > 0xffff) { error("path too long (%d chars, SHA1: %s): %s", pathlen, sha1_to_hex(sha1), path); @@ -361,6 +364,7 @@ int write_zip_archive(struct archiver_args *args) zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE); zip_dir_size = ZIP_DIRECTORY_MIN_SIZE; + verbose = args->verbose; if (args->base && plen > 0 && args->base[plen - 1] == '/') { char *base = xstrdup(args->base); -- cgit v0.10.2-6-g49f6 From fe5ab763f848cfcda22001f9280625f06c4c3760 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 Sep 2006 04:02:57 -0700 Subject: Teach --exec to git-archive --remote Some people needed --exec to specify the location of the upload-pack executable, because their default SSH log-in does not include the directory they have their own private copy of git on the $PATH. These people need to be able to say --exec to git-archive --remote for the same reason. Signed-off-by: Junio C Hamano diff --git a/builtin-archive.c b/builtin-archive.c index 7544ad3..dd7ffc0 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -32,16 +32,30 @@ static int run_remote_archiver(const char *remote, int argc, char *url, buf[1024]; int fd[2], i, len, rv; pid_t pid; + const char *exec = "git-upload-archive"; + int exec_at = 0; - sprintf(buf, "git-upload-archive"); + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (!strncmp("--exec=", arg, 7)) { + if (exec_at) + die("multiple --exec specified"); + exec = arg + 7; + exec_at = i; + break; + } + } url = xstrdup(remote); - pid = git_connect(fd, url, buf); + pid = git_connect(fd, url, exec); if (pid < 0) return pid; - for (i = 1; i < argc; i++) + for (i = 1; i < argc; i++) { + if (i == exec_at) + continue; packet_write(fd[1], "argument %s\n", argv[i]); + } packet_flush(fd[1]); len = packet_read_line(fd[0], buf, sizeof(buf)); -- cgit v0.10.2-6-g49f6 From d47f3db75c58139cdcbca5cc63b17bf5db293b6a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 Sep 2006 16:27:08 -0700 Subject: Prepare larger packet buffer for upload-pack protocol. The original side-band support added to the upload-pack protocol used the default 1000-byte packet length. The pkt-line format allows up to 64k, so prepare the receiver for the maximum size, and have the uploader and downloader negotiate if larger packet length is allowed. Signed-off-by: Junio C Hamano diff --git a/fetch-clone.c b/fetch-clone.c index b62feac..b632ca0 100644 --- a/fetch-clone.c +++ b/fetch-clone.c @@ -115,7 +115,7 @@ static pid_t setup_sideband(int sideband, const char *me, int fd[2], int xd[2]) die("%s: unable to fork off sideband demultiplexer", me); if (!side_pid) { /* subprocess */ - char buf[DEFAULT_PACKET_MAX]; + char buf[LARGE_PACKET_MAX]; close(fd[0]); if (xd[0] != xd[1]) diff --git a/fetch-pack.c b/fetch-pack.c index 377fede..1b2d6ee 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -166,10 +166,11 @@ static int find_common(int fd[2], unsigned char *result_sha1, } if (!fetching) - packet_write(fd[1], "want %s%s%s%s\n", + packet_write(fd[1], "want %s%s%s%s%s\n", sha1_to_hex(remote), (multi_ack ? " multi_ack" : ""), - (use_sideband ? " side-band" : ""), + (use_sideband == 2 ? " side-band-64k" : ""), + (use_sideband == 1 ? " side-band" : ""), (use_thin_pack ? " thin-pack" : "")); else packet_write(fd[1], "want %s\n", sha1_to_hex(remote)); @@ -426,7 +427,12 @@ static int fetch_pack(int fd[2], int nr_match, char **match) fprintf(stderr, "Server supports multi_ack\n"); multi_ack = 1; } - if (server_supports("side-band")) { + if (server_supports("side-band-64k")) { + if (verbose) + fprintf(stderr, "Server supports side-band-64k\n"); + use_sideband = 2; + } + else if (server_supports("side-band")) { if (verbose) fprintf(stderr, "Server supports side-band\n"); use_sideband = 1; diff --git a/sideband.h b/sideband.h index c645cf2..4872106 100644 --- a/sideband.h +++ b/sideband.h @@ -5,6 +5,7 @@ #define SIDEBAND_REMOTE_ERROR -1 #define DEFAULT_PACKET_MAX 1000 +#define LARGE_PACKET_MAX 65520 int recv_sideband(const char *me, int in_stream, int out, int err, char *, int); ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max); diff --git a/upload-pack.c b/upload-pack.c index 1f2f7f7..b673d8c 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -20,6 +20,9 @@ static int use_thin_pack; static struct object_array have_obj; static struct object_array want_obj; static unsigned int timeout; +/* 0 for no sideband, + * otherwise maximum packet size (up to 65520 bytes). + */ static int use_sideband; static void reset_timeout(void) @@ -37,8 +40,7 @@ static int strip(char *line, int len) static ssize_t send_client_data(int fd, const char *data, ssize_t sz) { if (use_sideband) - return send_sideband(1, fd, data, sz, DEFAULT_PACKET_MAX); - + return send_sideband(1, fd, data, sz, use_sideband); if (fd == 3) /* emergency quit */ fd = 2; @@ -389,8 +391,10 @@ static void receive_needs(void) multi_ack = 1; if (strstr(line+45, "thin-pack")) use_thin_pack = 1; - if (strstr(line+45, "side-band")) - use_sideband = 1; + if (strstr(line+45, "side-band-64k")) + use_sideband = LARGE_PACKET_MAX; + else if (strstr(line+45, "side-band")) + use_sideband = DEFAULT_PACKET_MAX; /* We have sent all our refs already, and the other end * should have chosen out of them; otherwise they are @@ -412,7 +416,7 @@ static void receive_needs(void) static int send_ref(const char *refname, const unsigned char *sha1) { - static const char *capabilities = "multi_ack thin-pack side-band"; + static const char *capabilities = "multi_ack thin-pack side-band side-band-64k"; struct object *o = parse_object(sha1); if (!o) -- cgit v0.10.2-6-g49f6 From 23d6d112c004d4242f9dbd8161f79ccdeb47bde8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 Sep 2006 03:33:34 -0700 Subject: Add sideband status report to git-archive protocol Using the refactored sideband code from existing upload-pack protocol, this lets the error condition and status output sent from the remote process to be shown locally. Signed-off-by: Junio C Hamano diff --git a/builtin-archive.c b/builtin-archive.c index dd7ffc0..cb883df 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -10,6 +10,7 @@ #include "tree-walk.h" #include "exec_cmd.h" #include "pkt-line.h" +#include "sideband.h" static const char archive_usage[] = \ "git-archive --format= [--prefix=/] [--verbose] [] [path...]"; @@ -29,7 +30,7 @@ struct archiver archivers[] = { static int run_remote_archiver(const char *remote, int argc, const char **argv) { - char *url, buf[1024]; + char *url, buf[LARGE_PACKET_MAX]; int fd[2], i, len, rv; pid_t pid; const char *exec = "git-upload-archive"; @@ -74,8 +75,7 @@ static int run_remote_archiver(const char *remote, int argc, die("git-archive: expected a flush"); /* Now, start reading from fd[0] and spit it out to stdout */ - rv = copy_fd(fd[0], 1); - + rv = recv_sideband("archive", fd[0], 1, 2, buf, sizeof(buf)); close(fd[0]); rv |= finish_connect(pid); diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c index 3bdb607..42cb9f8 100644 --- a/builtin-upload-archive.c +++ b/builtin-upload-archive.c @@ -6,12 +6,18 @@ #include "builtin.h" #include "archive.h" #include "pkt-line.h" +#include "sideband.h" +#include +#include static const char upload_archive_usage[] = "git-upload-archive "; +static const char deadchild[] = +"git-upload-archive: archiver died with error"; -int cmd_upload_archive(int argc, const char **argv, const char *prefix) + +static int run_upload_archive(int argc, const char **argv, const char *prefix) { struct archiver ar; const char *sent_argv[MAX_ARGS]; @@ -64,9 +70,89 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix) parse_treeish_arg(sent_argv + treeish_idx, &ar.args, prefix); parse_pathspec_arg(sent_argv + treeish_idx + 1, &ar.args); + return ar.write_archive(&ar.args); +} + +int cmd_upload_archive(int argc, const char **argv, const char *prefix) +{ + pid_t writer; + int fd1[2], fd2[2]; + /* + * Set up sideband subprocess. + * + * We (parent) monitor and read from child, sending its fd#1 and fd#2 + * multiplexed out to our fd#1. If the child dies, we tell the other + * end over channel #3. + */ + if (pipe(fd1) < 0 || pipe(fd2) < 0) { + int err = errno; + packet_write(1, "NACK pipe failed on the remote side\n"); + die("upload-archive: %s", strerror(err)); + } + writer = fork(); + if (writer < 0) { + int err = errno; + packet_write(1, "NACK fork failed on the remote side\n"); + die("upload-archive: %s", strerror(err)); + } + if (!writer) { + /* child - connect fd#1 and fd#2 to the pipe */ + dup2(fd1[1], 1); + dup2(fd2[1], 2); + close(fd1[1]); close(fd2[1]); + close(fd1[0]); close(fd2[0]); /* we do not read from pipe */ + + exit(run_upload_archive(argc, argv, prefix)); + } + + /* parent - read from child, multiplex and send out to fd#1 */ + close(fd1[1]); close(fd2[1]); /* we do not write to pipe */ packet_write(1, "ACK\n"); packet_flush(1); - return ar.write_archive(&ar.args); -} + while (1) { + struct pollfd pfd[2]; + char buf[16384]; + ssize_t sz; + pid_t pid; + int status; + + pfd[0].fd = fd1[0]; + pfd[0].events = POLLIN; + pfd[1].fd = fd2[0]; + pfd[1].events = POLLIN; + if (poll(pfd, 2, -1) < 0) { + if (errno != EINTR) { + error("poll failed resuming: %s", + strerror(errno)); + sleep(1); + } + continue; + } + if (pfd[0].revents & (POLLIN|POLLHUP)) { + /* Data stream ready */ + sz = read(pfd[0].fd, buf, sizeof(buf)); + send_sideband(1, 1, buf, sz, LARGE_PACKET_MAX); + } + if (pfd[1].revents & (POLLIN|POLLHUP)) { + /* Status stream ready */ + sz = read(pfd[1].fd, buf, sizeof(buf)); + send_sideband(1, 2, buf, sz, LARGE_PACKET_MAX); + } + if (((pfd[0].revents | pfd[1].revents) & POLLHUP) == 0) + continue; + /* did it die? */ + pid = waitpid(writer, &status, WNOHANG); + if (!pid) { + fprintf(stderr, "Hmph, HUP?\n"); + continue; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) + send_sideband(1, 3, deadchild, strlen(deadchild), + LARGE_PACKET_MAX); + packet_flush(1); + break; + } + return 0; +} -- cgit v0.10.2-6-g49f6 From d3788e19e20cd14aeac99d1f294d9a368437284f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 12 Sep 2006 00:26:57 -0700 Subject: upload-archive: monitor child communication more carefully. Franck noticed that the code around polling and relaying messages from the child process was quite bogus. Here is an attempt to clean it up a bit, based on his patch: - When POLLHUP is set, it goes ahead and reads the file descriptor. Worse yet, it does not check the return value of read() for errors when it does. - When we processed one POLLIN, we should just go back and see if any more data is available. We can check if the child is still there when poll gave control back at us but without any actual input. [jc: with simplification suggested by Franck. ] Signed-off-by: Junio C Hamano diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c index 42cb9f8..115a12d 100644 --- a/builtin-upload-archive.c +++ b/builtin-upload-archive.c @@ -16,6 +16,9 @@ static const char upload_archive_usage[] = static const char deadchild[] = "git-upload-archive: archiver died with error"; +static const char lostchild[] = +"git-upload-archive: archiver process was lost"; + static int run_upload_archive(int argc, const char **argv, const char *prefix) { @@ -73,6 +76,31 @@ static int run_upload_archive(int argc, const char **argv, const char *prefix) return ar.write_archive(&ar.args); } +static void error_clnt(const char *fmt, ...) +{ + char buf[1024]; + va_list params; + int len; + + va_start(params, fmt); + len = vsprintf(buf, fmt, params); + va_end(params); + send_sideband(1, 3, buf, len, LARGE_PACKET_MAX); + die("sent error to the client: %s", buf); +} + +static void process_input(int child_fd, int band) +{ + char buf[16384]; + ssize_t sz = read(child_fd, buf, sizeof(buf)); + if (sz < 0) { + if (errno != EINTR) + error_clnt("read error: %s\n", strerror(errno)); + return; + } + send_sideband(1, band, buf, sz, LARGE_PACKET_MAX); +} + int cmd_upload_archive(int argc, const char **argv, const char *prefix) { pid_t writer; @@ -112,9 +140,6 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix) while (1) { struct pollfd pfd[2]; - char buf[16384]; - ssize_t sz; - pid_t pid; int status; pfd[0].fd = fd1[0]; @@ -129,28 +154,19 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix) } continue; } - if (pfd[0].revents & (POLLIN|POLLHUP)) { + if (pfd[0].revents & POLLIN) /* Data stream ready */ - sz = read(pfd[0].fd, buf, sizeof(buf)); - send_sideband(1, 1, buf, sz, LARGE_PACKET_MAX); - } - if (pfd[1].revents & (POLLIN|POLLHUP)) { + process_input(pfd[0].fd, 1); + if (pfd[1].revents & POLLIN) /* Status stream ready */ - sz = read(pfd[1].fd, buf, sizeof(buf)); - send_sideband(1, 2, buf, sz, LARGE_PACKET_MAX); - } - - if (((pfd[0].revents | pfd[1].revents) & POLLHUP) == 0) - continue; - /* did it die? */ - pid = waitpid(writer, &status, WNOHANG); - if (!pid) { - fprintf(stderr, "Hmph, HUP?\n"); + process_input(pfd[1].fd, 2); + if ((pfd[0].revents | pfd[1].revents) == POLLIN) continue; - } - if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) - send_sideband(1, 3, deadchild, strlen(deadchild), - LARGE_PACKET_MAX); + + if (waitpid(writer, &status, 0) < 0) + error_clnt("%s", lostchild); + else if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) + error_clnt("%s", deadchild); packet_flush(1); break; } -- cgit v0.10.2-6-g49f6 From d751864cf7f5253da1ce749cf71215466590037f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 12 Sep 2006 22:42:31 -0700 Subject: builtin-archive.c: rename remote_request() to extract_remote_arg() Suggested by Franck, and I think it makes sense. Signed-off-by: Junio C Hamano diff --git a/builtin-archive.c b/builtin-archive.c index cb883df..da3f714 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -217,7 +217,7 @@ int parse_archive_args(int argc, const char **argv, struct archiver *ar) return i; } -static const char *remote_request(int *ac, const char **av) +static const char *extract_remote_arg(int *ac, const char **av) { int ix, iy, cnt = *ac; int no_more_options = 0; @@ -254,7 +254,7 @@ int cmd_archive(int argc, const char **argv, const char *prefix) int tree_idx; const char *remote = NULL; - remote = remote_request(&argc, argv); + remote = extract_remote_arg(&argc, argv); if (remote) return run_remote_archiver(remote, argc, argv); -- cgit v0.10.2-6-g49f6 From 4321134cd85eabc5d4d07a7ce00d4cf6a02c38fb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 12 Sep 2006 22:59:15 -0700 Subject: pack-objects: document --revs, --unpacked and --all. Signed-off-by: Junio C Hamano diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt index 4991f88..d4661dd 100644 --- a/Documentation/git-pack-objects.txt +++ b/Documentation/git-pack-objects.txt @@ -11,7 +11,7 @@ SYNOPSIS [verse] 'git-pack-objects' [-q] [--no-reuse-delta] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] - {--stdout | base-name} < object-list + [--revs [--unpacked | --all]*] [--stdout | base-name] < object-list DESCRIPTION @@ -56,6 +56,24 @@ base-name:: Write the pack contents (what would have been written to .pack file) out to the standard output. +--revs:: + Read the revision arguments from the standard input, instead of + individual object names. The revision arguments are processed + the same way as gitlink:git-rev-list[1] with `--objects` flag + uses its `commit` arguments to build the list of objects it + outputs. The objects on the resulting list are packed. + +--unpacked:: + This implies `--revs`. When processing the list of + revision arguments read from the standard input, limit + the objects packed to those that are not already packed. + +--all:: + This implies `--revs`. In addition to the list of + revision arguments read from the standard input, pretend + as if all refs under `$GIT_DIR/refs` are specifed to be + included. + --window and --depth:: These two options affects how the objects contained in the pack are stored using delta compression. The @@ -103,6 +121,7 @@ Documentation by Junio C Hamano See Also -------- +gitlink:git-rev-list[1] gitlink:git-repack[1] gitlink:git-prune-packed[1] diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 753dd9a..8d7a120 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -15,7 +15,7 @@ #include #include -static const char pack_usage[] = "git-pack-objects [-q] [--no-reuse-delta] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} [--revs [--unpacked | --all]* Date: Tue, 12 Sep 2006 22:45:12 +0200 Subject: Teach runstatus about --untracked Actually, teach runstatus what to do if it is not passed; it should not list the contents of completely untracked directories, but only the name of that directory (plus a trailing '/'). [jc: with comments by Jeff King to match hide-empty-directories behaviour of the original.] Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/builtin-runstatus.c b/builtin-runstatus.c index 7979d61..303c556 100644 --- a/builtin-runstatus.c +++ b/builtin-runstatus.c @@ -25,6 +25,8 @@ int cmd_runstatus(int argc, const char **argv, const char *prefix) } else if (!strcmp(argv[i], "--verbose")) s.verbose = 1; + else if (!strcmp(argv[i], "--untracked")) + s.untracked = 1; else usage(runstatus_usage); } diff --git a/git-commit.sh b/git-commit.sh index 10c269a..5a4c659 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -82,7 +82,8 @@ run_status () { esac git-runstatus ${color} \ ${verbose:+--verbose} \ - ${amend:+--amend} + ${amend:+--amend} \ + ${untracked_files:+--untracked} } trap ' diff --git a/wt-status.c b/wt-status.c index ec2c728..c644331 100644 --- a/wt-status.c +++ b/wt-status.c @@ -50,6 +50,7 @@ void wt_status_prepare(struct wt_status *s) s->amend = 0; s->verbose = 0; s->commitable = 0; + s->untracked = 0; } static void wt_status_print_header(const char *main, const char *sub) @@ -188,6 +189,10 @@ static void wt_status_print_untracked(const struct wt_status *s) memset(&dir, 0, sizeof(dir)); dir.exclude_per_dir = ".gitignore"; + if (!s->untracked) { + dir.show_other_directories = 1; + dir.hide_empty_directories = 1; + } x = git_path("info/exclude"); if (file_exists(x)) add_excludes_from_file(&dir, x); diff --git a/wt-status.h b/wt-status.h index 75d3cfe..0a5a5b7 100644 --- a/wt-status.h +++ b/wt-status.h @@ -15,6 +15,7 @@ struct wt_status { int commitable; int verbose; int amend; + int untracked; }; int git_status_config(const char *var, const char *value); -- cgit v0.10.2-6-g49f6 From b982592d6677aebff646a0e68b1075804ca3955f Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 11 Sep 2006 19:21:17 -0400 Subject: git-status: document colorization config options Signed-off-by: Jeff King Signed-off-by: Junio C Hamano diff --git a/Documentation/config.txt b/Documentation/config.txt index ce722a2..844cae4 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -225,6 +225,20 @@ showbranch.default:: The default set of branches for gitlink:git-show-branch[1]. See gitlink:git-show-branch[1]. +status.color:: + A boolean to enable/disable color in the output of + gitlink:git-status[1]. May be set to `true` (or `always`), + `false` (or `never`) or `auto`, in which case colors are used + only when the output is to a terminal. Defaults to false. + +status.color.:: + Use customized color for status colorization. `` is + one of `header` (the header text of the status message), + `updated` (files which are updated but not committed), + `changed` (files which are changed but not updated in the index), + or `untracked` (files which are not tracked by git). The values of + these variables may be specified as in diff.color.. + tar.umask:: By default, gitlink:git-tar-tree[1] sets file and directories modes to 0666 or 0777. While this is both useful and acceptable for projects -- cgit v0.10.2-6-g49f6 From 2232c0c69ff114a23011a0698b119aeea0272e8b Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Wed, 13 Sep 2006 22:55:04 +0200 Subject: git-archive: inline default_parse_extra() Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano diff --git a/builtin-archive.c b/builtin-archive.c index da3f714..6dabdee 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -145,17 +145,6 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args, ar_args->time = archive_time; } -static const char *default_parse_extra(struct archiver *ar, - const char **argv) -{ - static char msg[64]; - - snprintf(msg, sizeof(msg) - 4, "'%s' format does not handle %s", - ar->name, *argv); - - return strcat(msg, "..."); -} - int parse_archive_args(int argc, const char **argv, struct archiver *ar) { const char *extra_argv[MAX_EXTRA_ARGS]; @@ -208,7 +197,8 @@ int parse_archive_args(int argc, const char **argv, struct archiver *ar) if (extra_argc) { if (!ar->parse_extra) - die("%s", default_parse_extra(ar, extra_argv)); + die("'%s' format does not handle %s", + ar->name, extra_argv[0]); ar->args.extra = ar->parse_extra(extra_argc, extra_argv); } ar->args.verbose = verbose; -- cgit v0.10.2-6-g49f6 From db830b4f233bd45e28592fbf59395bc98d79dec5 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 13 Sep 2006 18:37:14 -0400 Subject: wt-status: remove extraneous newline from 'deleted:' output This was accidentally introduced during the fixes to avoid putting newlines inside of colorized output. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano diff --git a/wt-status.c b/wt-status.c index c644331..4b74e68 100644 --- a/wt-status.c +++ b/wt-status.c @@ -78,7 +78,7 @@ static void wt_status_print_filepair(int t, struct diff_filepair *p) p->one->path, p->two->path); break; case DIFF_STATUS_DELETED: - color_printf_ln(c, "deleted: %s", p->one->path); break; + color_printf(c, "deleted: %s", p->one->path); break; case DIFF_STATUS_MODIFIED: color_printf(c, "modified: %s", p->one->path); break; case DIFF_STATUS_RENAMED: -- cgit v0.10.2-6-g49f6 From c95b138985186992b222321f332cf92edbbd4141 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 15 Sep 2006 23:19:02 -0700 Subject: Fix git-am safety checks An earlier commit cbd64af added a check that prevents "git-am" to run without its standard input connected to a terminal while resuming operation. This was to catch a user error to try feeding a new patch from its standard input while recovery. The assumption of the check was that it is an indication that a new patch is being fed if the standard input is not connected to a terminal. It is however not quite correct (the standard input can be /dev/null if the user knows the operation does not need any input, for example). This broke t3403 when the test was run with its standard input connected to /dev/null. When git-am is given an explicit command such as --skip, there is no reason to insist that the standard input is a terminal; we are not going to read a new patch anyway. Credit goes to Gerrit Pape for noticing and reporting the problem with t3403-rebase-skip test. Signed-off-by: Junio C Hamano diff --git a/git-am.sh b/git-am.sh index d0af786..afe322b 100755 --- a/git-am.sh +++ b/git-am.sh @@ -166,10 +166,25 @@ fi if test -d "$dotest" then - if test ",$#," != ",0," || ! tty -s - then - die "previous dotest directory $dotest still exists but mbox given." - fi + case "$#,$skip$resolved" in + 0,*t*) + # Explicit resume command and we do not have file, so + # we are happy. + : ;; + 0,) + # No file input but without resume parameters; catch + # user error to feed us a patch from standard input + # when there is already .dotest. This is somewhat + # unreliable -- stdin could be /dev/null for example + # and the caller did not intend to feed us a patch but + # wanted to continue unattended. + tty -s + ;; + *) + false + ;; + esac || + die "previous dotest directory $dotest still exists but mbox given." resume=yes else # Make sure we are not given --skip nor --resolved -- cgit v0.10.2-6-g49f6 From be4a015b0fed2bd96e509f45b80b63db056b9ca9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 16 Sep 2006 10:58:20 -0700 Subject: http-fetch.c: consolidate code to detect missing fetch target At a handful places we check two error codes from curl library to see if the file we asked was missing from the remote (e.g. we asked for a loose object when it is in a pack) to decide what to do next. This consolidates the check into a single function. NOTE: the original did not check for HTTP_RETURNED_ERROR when error code is 404, but this version does to make sure 404 is from HTTP and not some other protcol. Signed-off-by: Junio C Hamano diff --git a/http-fetch.c b/http-fetch.c index a113bb8..98cf52d 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -144,6 +144,17 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb, return size; } +static int missing__target(int code, int result) +{ + return /* file:// URL -- do we ever use one??? */ + (result == CURLE_FILE_COULDNT_READ_FILE) || + /* http:// and https:// URL */ + (code == 404 && result == CURLE_HTTP_RETURNED_ERROR) + ; +} + +#define missing_target(a) missing__target((a)->http_code, (a)->curl_result) + static void fetch_alternates(const char *base); static void process_object_response(void *callback_data); @@ -323,8 +334,7 @@ static void process_object_response(void *callback_data) obj_req->state = COMPLETE; /* Use alternates if necessary */ - if (obj_req->http_code == 404 || - obj_req->curl_result == CURLE_FILE_COULDNT_READ_FILE) { + if (missing_target(obj_req)) { fetch_alternates(alt->base); if (obj_req->repo->next != NULL) { obj_req->repo = @@ -537,8 +547,7 @@ static void process_alternates_response(void *callback_data) return; } } else if (slot->curl_result != CURLE_OK) { - if (slot->http_code != 404 && - slot->curl_result != CURLE_FILE_COULDNT_READ_FILE) { + if (!missing_target(slot)) { got_alternates = -1; return; } @@ -941,8 +950,7 @@ static int fetch_indices(struct alt_base *repo) if (start_active_slot(slot)) { run_active_slot(slot); if (results.curl_result != CURLE_OK) { - if (results.http_code == 404 || - results.curl_result == CURLE_FILE_COULDNT_READ_FILE) { + if (missing_target(&results)) { repo->got_indices = 1; free(buffer.buffer); return 0; @@ -1123,8 +1131,7 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1) ret = error("Request for %s aborted", hex); } else if (obj_req->curl_result != CURLE_OK && obj_req->http_code != 416) { - if (obj_req->http_code == 404 || - obj_req->curl_result == CURLE_FILE_COULDNT_READ_FILE) + if (missing_target(obj_req)) ret = -1; /* Be silent, it is probably in a pack. */ else ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)", -- cgit v0.10.2-6-g49f6 From 4adffc7b54e8c5d8fc7510007dd9b8235a67430f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 16 Sep 2006 11:06:02 -0700 Subject: Add ftp:// protocol support for git-http-fetch Based on Sasha Khapyorsky's patch but adjusted to the refactored "missing target" detection code. It might have been better if the program were called git-url-fetch but it is too late now ;-). Signed-off-by: Junio C Hamano diff --git a/http-fetch.c b/http-fetch.c index 98cf52d..bc74f30 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -149,7 +149,9 @@ static int missing__target(int code, int result) return /* file:// URL -- do we ever use one??? */ (result == CURLE_FILE_COULDNT_READ_FILE) || /* http:// and https:// URL */ - (code == 404 && result == CURLE_HTTP_RETURNED_ERROR) + (code == 404 && result == CURLE_HTTP_RETURNED_ERROR) || + /* ftp:// URL */ + (code == 550 && result == CURLE_FTP_COULDNT_RETR_FILE) ; } -- cgit v0.10.2-6-g49f6 From 8aac4b45f39a7c845848a75ac971717a1933d99f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 16 Sep 2006 13:41:14 -0700 Subject: t1400: make test debuggable. I had a hard time figuring out why this test was failing with the packed-refs update without running it under "sh -x". This makes output from "sh t1400-update-ref.sh -v" more descriptive. Updating other tests would be a good janitorial task. Signed-off-by: Junio C Hamano diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index ddc80bb..b3b920e 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -19,51 +19,51 @@ n=$n_dir/fixes test_expect_success \ "create $m" \ - 'git-update-ref $m $A && - test $A = $(cat .git/$m)' + "git-update-ref $m $A && + test $A"' = $(cat .git/'"$m"')' test_expect_success \ "create $m" \ - 'git-update-ref $m $B $A && - test $B = $(cat .git/$m)' + "git-update-ref $m $B $A && + test $B"' = $(cat .git/'"$m"')' rm -f .git/$m test_expect_success \ "fail to create $n" \ - 'touch .git/$n_dir + "touch .git/$n_dir git-update-ref $n $A >out 2>err - test $? = 1 && + test "'$? = 1 && test "" = "$(cat out)" && grep "error: unable to resolve reference" err && - grep $n err' + grep '"$n err" rm -f .git/$n_dir out err test_expect_success \ "create $m (by HEAD)" \ - 'git-update-ref HEAD $A && - test $A = $(cat .git/$m)' + "git-update-ref HEAD $A && + test $A"' = $(cat .git/'"$m"')' test_expect_success \ "create $m (by HEAD)" \ - 'git-update-ref HEAD $B $A && - test $B = $(cat .git/$m)' + "git-update-ref HEAD $B $A && + test $B"' = $(cat .git/'"$m"')' rm -f .git/$m test_expect_failure \ '(not) create HEAD with old sha1' \ - 'git-update-ref HEAD $A $B' + "git-update-ref HEAD $A $B" test_expect_failure \ "(not) prior created .git/$m" \ - 'test -f .git/$m' + "test -f .git/$m" rm -f .git/$m test_expect_success \ "create HEAD" \ - 'git-update-ref HEAD $A' + "git-update-ref HEAD $A" test_expect_failure \ '(not) change HEAD with wrong SHA1' \ - 'git-update-ref HEAD $B $Z' + "git-update-ref HEAD $B $Z" test_expect_failure \ "(not) changed .git/$m" \ - 'test $B = $(cat .git/$m)' + "test $B"' = $(cat .git/'"$m"')' rm -f .git/$m mkdir -p .git/logs/refs/heads @@ -71,18 +71,18 @@ touch .git/logs/refs/heads/master test_expect_success \ "create $m (logged by touch)" \ 'GIT_COMMITTER_DATE="2005-05-26 23:30" \ - git-update-ref HEAD $A -m "Initial Creation" && - test $A = $(cat .git/$m)' + git-update-ref HEAD '"$A"' -m "Initial Creation" && + test '"$A"' = $(cat .git/'"$m"')' test_expect_success \ "update $m (logged by touch)" \ 'GIT_COMMITTER_DATE="2005-05-26 23:31" \ - git-update-ref HEAD $B $A -m "Switch" && - test $B = $(cat .git/$m)' + git-update-ref HEAD'" $B $A "'-m "Switch" && + test '"$B"' = $(cat .git/'"$m"')' test_expect_success \ "set $m (logged by touch)" \ 'GIT_COMMITTER_DATE="2005-05-26 23:41" \ - git-update-ref HEAD $A && - test $A = $(cat .git/$m)' + git-update-ref HEAD'" $A && + test $A"' = $(cat .git/'"$m"')' cat >expect < 1117150200 +0000 Initial Creation @@ -91,7 +91,7 @@ $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000 EOF test_expect_success \ "verifying $m's log" \ - 'diff expect .git/logs/$m' + "diff expect .git/logs/$m" rm -rf .git/$m .git/logs expect test_expect_success \ @@ -102,18 +102,18 @@ test_expect_success \ test_expect_success \ "create $m (logged by config)" \ 'GIT_COMMITTER_DATE="2005-05-26 23:32" \ - git-update-ref HEAD $A -m "Initial Creation" && - test $A = $(cat .git/$m)' + git-update-ref HEAD'" $A "'-m "Initial Creation" && + test '"$A"' = $(cat .git/'"$m"')' test_expect_success \ "update $m (logged by config)" \ 'GIT_COMMITTER_DATE="2005-05-26 23:33" \ - git-update-ref HEAD $B $A -m "Switch" && - test $B = $(cat .git/$m)' + git-update-ref HEAD'" $B $A "'-m "Switch" && + test '"$B"' = $(cat .git/'"$m"')' test_expect_success \ "set $m (logged by config)" \ 'GIT_COMMITTER_DATE="2005-05-26 23:43" \ - git-update-ref HEAD $A && - test $A = $(cat .git/$m)' + git-update-ref HEAD '"$A && + test $A"' = $(cat .git/'"$m"')' cat >expect < 1117150320 +0000 Initial Creation @@ -140,50 +140,50 @@ test_expect_success \ 'Query "master@{May 25 2005}" (before history)' \ 'rm -f o e git-rev-parse --verify "master@{May 25 2005}" >o 2>e && - test $C = $(cat o) && - test "warning: Log .git/logs/$m only goes back to $ed." = "$(cat e)"' + test '"$C"' = $(cat o) && + test "warning: Log .git/logs/'"$m only goes back to $ed"'." = "$(cat e)"' test_expect_success \ "Query master@{2005-05-25} (before history)" \ 'rm -f o e git-rev-parse --verify master@{2005-05-25} >o 2>e && - test $C = $(cat o) && - echo test "warning: Log .git/logs/$m only goes back to $ed." = "$(cat e)"' + test '"$C"' = $(cat o) && + echo test "warning: Log .git/logs/'"$m only goes back to $ed"'." = "$(cat e)"' test_expect_success \ 'Query "master@{May 26 2005 23:31:59}" (1 second before history)' \ 'rm -f o e git-rev-parse --verify "master@{May 26 2005 23:31:59}" >o 2>e && - test $C = $(cat o) && - test "warning: Log .git/logs/$m only goes back to $ed." = "$(cat e)"' + test '"$C"' = $(cat o) && + test "warning: Log .git/logs/'"$m only goes back to $ed"'." = "$(cat e)"' test_expect_success \ 'Query "master@{May 26 2005 23:32:00}" (exactly history start)' \ 'rm -f o e git-rev-parse --verify "master@{May 26 2005 23:32:00}" >o 2>e && - test $A = $(cat o) && + test '"$A"' = $(cat o) && test "" = "$(cat e)"' test_expect_success \ 'Query "master@{2005-05-26 23:33:01}" (middle of history with gap)' \ 'rm -f o e git-rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e && - test $B = $(cat o) && - test "warning: Log .git/logs/$m has gap after $gd." = "$(cat e)"' + test '"$B"' = $(cat o) && + test "warning: Log .git/logs/'"$m has gap after $gd"'." = "$(cat e)"' test_expect_success \ 'Query "master@{2005-05-26 23:38:00}" (middle of history)' \ 'rm -f o e git-rev-parse --verify "master@{2005-05-26 23:38:00}" >o 2>e && - test $Z = $(cat o) && + test '"$Z"' = $(cat o) && test "" = "$(cat e)"' test_expect_success \ 'Query "master@{2005-05-26 23:43:00}" (exact end of history)' \ 'rm -f o e git-rev-parse --verify "master@{2005-05-26 23:43:00}" >o 2>e && - test $E = $(cat o) && + test '"$E"' = $(cat o) && test "" = "$(cat e)"' test_expect_success \ 'Query "master@{2005-05-28}" (past end of history)' \ 'rm -f o e git-rev-parse --verify "master@{2005-05-28}" >o 2>e && - test $D = $(cat o) && - test "warning: Log .git/logs/$m unexpectedly ended on $ld." = "$(cat e)"' + test '"$D"' = $(cat o) && + test "warning: Log .git/logs/'"$m unexpectedly ended on $ld"'." = "$(cat e)"' rm -f .git/$m .git/logs/$m expect @@ -221,7 +221,7 @@ $h_FIXED $h_MERGED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151100 +0000 c EOF test_expect_success \ 'git-commit logged updates' \ - 'diff expect .git/logs/$m' + "diff expect .git/logs/$m" unset h_TEST h_OTHER h_FIXED h_MERGED test_expect_success \ -- cgit v0.10.2-6-g49f6 From 4be609625e48e908f2b76d35bfeb61a8ba3a83a0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 17 Sep 2006 01:04:24 -0700 Subject: apply --unidiff-zero: loosen sanity checks for --unidiff=0 patches In "git-apply", we have a few sanity checks and heuristics that expects that the patch fed to us is a unified diff with at least one line of context. * When there is no leading context line in a hunk, the hunk must apply at the beginning of the preimage. Similarly, no trailing context means that the hunk is anchored at the end. * We learn a patch deletes the file from a hunk that has no resulting line (i.e. all lines are prefixed with '-') if it has not otherwise been known if the patch deletes the file. Similarly, no old line means the file is being created. And we declare an error condition when the file created by a creation patch already exists, and/or when a deletion patch still leaves content in the file. These sanity checks are good safety measures, but breaks down when people feed a diff generated with --unified=0. This was recently noticed first by Matthew Wilcox and Gerrit Pape. This adds a new flag, --unified-zero, to allow bypassing these checks. If you are in control of the patch generation process, you should not use --unified=0 patch and fix it up with this flag; rather you should try work with a patch with context. But if all you have to work with is a patch without context, this flag may come handy as the last resort. Signed-off-by: Junio C Hamano diff --git a/builtin-apply.c b/builtin-apply.c index 6e0864c..25e90d8 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -27,6 +27,7 @@ static const char *prefix; static int prefix_length = -1; static int newfd = -1; +static int unidiff_zero; static int p_value = 1; static int check_index; static int write_index; @@ -854,11 +855,10 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc } /* - * Parse a unified diff. Note that this really needs - * to parse each fragment separately, since the only - * way to know the difference between a "---" that is - * part of a patch, and a "---" that starts the next - * patch is to look at the line counts.. + * Parse a unified diff. Note that this really needs to parse each + * fragment separately, since the only way to know the difference + * between a "---" that is part of a patch, and a "---" that starts + * the next patch is to look at the line counts.. */ static int parse_fragment(char *line, unsigned long size, struct patch *patch, struct fragment *fragment) { @@ -875,31 +875,14 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s leading = 0; trailing = 0; - if (patch->is_new < 0) { - patch->is_new = !oldlines; - if (!oldlines) - patch->old_name = NULL; - } - if (patch->is_delete < 0) { - patch->is_delete = !newlines; - if (!newlines) - patch->new_name = NULL; - } - - if (patch->is_new && oldlines) - return error("new file depends on old contents"); - if (patch->is_delete != !newlines) { - if (newlines) - return error("deleted file still has contents"); - fprintf(stderr, "** warning: file %s becomes empty but is not deleted\n", patch->new_name); - } - /* Parse the thing.. */ line += len; size -= len; linenr++; added = deleted = 0; - for (offset = len; size > 0; offset += len, size -= len, line += len, linenr++) { + for (offset = len; + 0 < size; + offset += len, size -= len, line += len, linenr++) { if (!oldlines && !newlines) break; len = linelen(line, size); @@ -972,12 +955,18 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s patch->lines_added += added; patch->lines_deleted += deleted; + + if (0 < patch->is_new && oldlines) + return error("new file depends on old contents"); + if (0 < patch->is_delete && newlines) + return error("deleted file still has contents"); return offset; } static int parse_single_patch(char *line, unsigned long size, struct patch *patch) { unsigned long offset = 0; + unsigned long oldlines = 0, newlines = 0, context = 0; struct fragment **fragp = &patch->fragments; while (size > 4 && !memcmp(line, "@@ -", 4)) { @@ -988,9 +977,11 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc len = parse_fragment(line, size, patch, fragment); if (len <= 0) die("corrupt patch at line %d", linenr); - fragment->patch = line; fragment->size = len; + oldlines += fragment->oldlines; + newlines += fragment->newlines; + context += fragment->leading + fragment->trailing; *fragp = fragment; fragp = &fragment->next; @@ -999,6 +990,46 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc line += len; size -= len; } + + /* + * If something was removed (i.e. we have old-lines) it cannot + * be creation, and if something was added it cannot be + * deletion. However, the reverse is not true; --unified=0 + * patches that only add are not necessarily creation even + * though they do not have any old lines, and ones that only + * delete are not necessarily deletion. + * + * Unfortunately, a real creation/deletion patch do _not_ have + * any context line by definition, so we cannot safely tell it + * apart with --unified=0 insanity. At least if the patch has + * more than one hunk it is not creation or deletion. + */ + if (patch->is_new < 0 && + (oldlines || (patch->fragments && patch->fragments->next))) + patch->is_new = 0; + if (patch->is_delete < 0 && + (newlines || (patch->fragments && patch->fragments->next))) + patch->is_delete = 0; + if (!unidiff_zero || context) { + /* If the user says the patch is not generated with + * --unified=0, or if we have seen context lines, + * then not having oldlines means the patch is creation, + * and not having newlines means the patch is deletion. + */ + if (patch->is_new < 0 && !oldlines) + patch->is_new = 1; + if (patch->is_delete < 0 && !newlines) + patch->is_delete = 1; + } + + if (0 < patch->is_new && oldlines) + die("new file %s depends on old contents", patch->new_name); + if (0 < patch->is_delete && newlines) + die("deleted file %s still has contents", patch->old_name); + if (!patch->is_delete && !newlines && context) + fprintf(stderr, "** warning: file %s becomes empty but " + "is not deleted\n", patch->new_name); + return offset; } @@ -1556,9 +1587,19 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i /* * If we don't have any leading/trailing data in the patch, * we want it to match at the beginning/end of the file. + * + * But that would break if the patch is generated with + * --unified=0; sane people wouldn't do that to cause us + * trouble, but we try to please not so sane ones as well. */ - match_beginning = !leading && (frag->oldpos == 1); - match_end = !trailing; + if (unidiff_zero) { + match_beginning = (!leading && !frag->oldpos); + match_end = 0; + } + else { + match_beginning = !leading && (frag->oldpos == 1); + match_end = !trailing; + } lines = 0; pos = frag->newpos; @@ -1804,7 +1845,7 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry * patch->result = desc.buffer; patch->resultsize = desc.size; - if (patch->is_delete && patch->resultsize) + if (0 < patch->is_delete && patch->resultsize) return error("removal patch leaves file contents"); return 0; @@ -1876,7 +1917,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch) old_name, st_mode, patch->old_mode); } - if (new_name && prev_patch && prev_patch->is_delete && + if (new_name && prev_patch && 0 < prev_patch->is_delete && !strcmp(prev_patch->old_name, new_name)) /* A type-change diff is always split into a patch to * delete old, immediately followed by a patch to @@ -1889,7 +1930,8 @@ static int check_patch(struct patch *patch, struct patch *prev_patch) else ok_if_exists = 0; - if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) { + if (new_name && + ((0 < patch->is_new) | (0 < patch->is_rename) | patch->is_copy)) { if (check_index && cache_name_pos(new_name, strlen(new_name)) >= 0 && !ok_if_exists) @@ -1906,7 +1948,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch) return error("%s: %s", new_name, strerror(errno)); } if (!patch->new_mode) { - if (patch->is_new) + if (0 < patch->is_new) patch->new_mode = S_IFREG | 0644; else patch->new_mode = patch->old_mode; @@ -1957,7 +1999,7 @@ static void show_index_list(struct patch *list) const char *name; name = patch->old_name ? patch->old_name : patch->new_name; - if (patch->is_new) + if (0 < patch->is_new) sha1_ptr = null_sha1; else if (get_sha1(patch->old_sha1_prefix, sha1)) die("sha1 information is lacking or useless (%s).", @@ -2543,6 +2585,10 @@ int cmd_apply(int argc, const char **argv, const char *prefix) apply_in_reverse = 1; continue; } + if (!strcmp(arg, "--unidiff-zero")) { + unidiff_zero = 1; + continue; + } if (!strcmp(arg, "--reject")) { apply = apply_with_reject = apply_verbosely = 1; continue; diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh index 8ab63c5..bb25315 100755 --- a/t/t3403-rebase-skip.sh +++ b/t/t3403-rebase-skip.sh @@ -37,7 +37,9 @@ test_expect_success setup ' git branch skip-merge skip-reference ' -test_expect_failure 'rebase with git am -3 (default)' 'git rebase master' +test_expect_failure 'rebase with git am -3 (default)' ' + git rebase master +' test_expect_success 'rebase --skip with am -3' ' git reset --hard HEAD && diff --git a/t/t4104-apply-boundary.sh b/t/t4104-apply-boundary.sh new file mode 100755 index 0000000..2ff800c --- /dev/null +++ b/t/t4104-apply-boundary.sh @@ -0,0 +1,115 @@ +#!/bin/sh +# +# Copyright (c) 2005 Junio C Hamano +# + +test_description='git-apply boundary tests + +' +. ./test-lib.sh + +L="c d e f g h i j k l m n o p q r s t u v w x" + +test_expect_success setup ' + for i in b '"$L"' y + do + echo $i + done >victim && + cat victim >original && + git update-index --add victim && + + : add to the head + for i in a b '"$L"' y + do + echo $i + done >victim && + cat victim >add-a-expect && + git diff victim >add-a-patch.with && + git diff --unified=0 >add-a-patch.without && + + : modify at the head + for i in a '"$L"' y + do + echo $i + done >victim && + cat victim >mod-a-expect && + git diff victim >mod-a-patch.with && + git diff --unified=0 >mod-a-patch.without && + + : remove from the head + for i in '"$L"' y + do + echo $i + done >victim && + cat victim >del-a-expect && + git diff victim >del-a-patch.with + git diff --unified=0 >del-a-patch.without && + + : add to the tail + for i in b '"$L"' y z + do + echo $i + done >victim && + cat victim >add-z-expect && + git diff victim >add-z-patch.with && + git diff --unified=0 >add-z-patch.without && + + : modify at the tail + for i in a '"$L"' y + do + echo $i + done >victim && + cat victim >mod-z-expect && + git diff victim >mod-z-patch.with && + git diff --unified=0 >mod-z-patch.without && + + : remove from the tail + for i in b '"$L"' + do + echo $i + done >victim && + cat victim >del-z-expect && + git diff victim >del-z-patch.with + git diff --unified=0 >del-z-patch.without && + + : done +' + +for with in with without +do + case "$with" in + with) u= ;; + without) u='--unidiff-zero ' ;; + esac + for kind in add-a add-z mod-a mod-z del-a del-z + do + test_expect_success "apply $kind-patch $with context" ' + cat original >victim && + git update-index victim && + git apply --index '"$u$kind-patch.$with"' || { + cat '"$kind-patch.$with"' + (exit 1) + } && + diff -u '"$kind"'-expect victim + ' + done +done + +for kind in add-a add-z mod-a mod-z del-a del-z +do + rm -f $kind-ng.without + sed -e "s/^diff --git /diff /" \ + -e '/^index /d' \ + <$kind-patch.without >$kind-ng.without + test_expect_success "apply non-git $kind-patch without context" ' + cat original >victim && + git update-index victim && + git apply --unidiff-zero --index '"$kind-ng.without"' || { + cat '"$kind-ng.without"' + (exit 1) + } && + diff -u '"$kind"'-expect victim + ' +done + +test_done -- cgit v0.10.2-6-g49f6 From dd70235f5a81e52725365365a554cf7c8cfa37e6 Mon Sep 17 00:00:00 2001 From: Martin Waitz Date: Sat, 16 Sep 2006 23:08:32 +0200 Subject: gitweb: more support for PATH_INFO based URLs Now three types of path based URLs are supported: gitweb.cgi/project.git gitweb.cgi/project.git/branch gitweb.cgi/project.git/branch/filename The first one (show project summary) was already supported for a long time now. The other two are new: they show the shortlog of a branch or the plain file contents of some file contained in the repository. This is especially useful to support project web pages for small projects: just create an html branch and then use an URL like gitweb.cgi/project.git/html/index.html. Signed-off-by: Martin Waitz Acked-by: Jakub Narebski Signed-off-by: Junio C Hamano diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index a81c8d4..645ae79 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -196,12 +196,7 @@ if (defined $action) { } } -our $project = ($cgi->param('p') || $ENV{'PATH_INFO'}); -if (defined $project) { - $project =~ s|^/||; - $project =~ s|/$||; - $project = undef unless $project; -} +our $project = $cgi->param('p'); if (defined $project) { if (!validate_input($project)) { die_error(undef, "Invalid project parameter"); @@ -212,7 +207,6 @@ if (defined $project) { if (!(-e "$projectroot/$project/HEAD")) { die_error(undef, "No such project"); } - $git_dir = "$projectroot/$project"; } our $file_name = $cgi->param('f'); @@ -272,6 +266,32 @@ if (defined $searchtext) { $searchtext = quotemeta $searchtext; } +# now read PATH_INFO and use it as alternative to parameters +our $path_info = $ENV{"PATH_INFO"}; +$path_info =~ s|^/||; +$path_info =~ s|/$||; +if (validate_input($path_info) && !defined $project) { + $project = $path_info; + while ($project && !-e "$projectroot/$project/HEAD") { + $project =~ s,/*[^/]*$,,; + } + if (defined $project) { + $project = undef unless $project; + } + if ($path_info =~ m,^$project/([^/]+)/(.+)$,) { + # we got "project.git/branch/filename" + $action ||= "blob_plain"; + $hash_base ||= $1; + $file_name ||= $2; + } elsif ($path_info =~ m,^$project/([^/]+)$,) { + # we got "project.git/branch" + $action ||= "shortlog"; + $hash ||= $1; + } +} + +$git_dir = "$projectroot/$project"; + # dispatch my %actions = ( "blame" => \&git_blame2, -- cgit v0.10.2-6-g49f6 From 800764cf335cb4de289269c919e865c536635885 Mon Sep 17 00:00:00 2001 From: Martin Waitz Date: Sat, 16 Sep 2006 23:09:02 +0200 Subject: gitweb: fix uninitialized variable warning. Perl spit out a varning when "blob" or "blob_plain" actions were used without a $hash parameter. Signed-off-by: Martin Waitz Signed-off-by: Junio C Hamano diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 645ae79..2501385 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2547,11 +2547,7 @@ sub git_heads { } sub git_blob_plain { - # blobs defined by non-textual hash id's can be cached my $expires; - if ($hash =~ m/^[0-9a-fA-F]{40}$/) { - $expires = "+1d"; - } if (!defined $hash) { if (defined $file_name) { @@ -2561,7 +2557,11 @@ sub git_blob_plain { } else { die_error(undef, "No file name defined"); } + } elsif ($hash =~ m/^[0-9a-fA-F]{40}$/) { + # blobs defined by non-textual hash id's can be cached + $expires = "+1d"; } + my $type = shift; open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash or die_error(undef, "Couldn't cat $file_name, $hash"); @@ -2589,11 +2589,7 @@ sub git_blob_plain { } sub git_blob { - # blobs defined by non-textual hash id's can be cached my $expires; - if ($hash =~ m/^[0-9a-fA-F]{40}$/) { - $expires = "+1d"; - } if (!defined $hash) { if (defined $file_name) { @@ -2603,7 +2599,11 @@ sub git_blob { } else { die_error(undef, "No file name defined"); } + } elsif ($hash =~ m/^[0-9a-fA-F]{40}$/) { + # blobs defined by non-textual hash id's can be cached + $expires = "+1d"; } + my ($have_blame) = gitweb_check_feature('blame'); open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash or die_error(undef, "Couldn't cat $file_name, $hash"); -- cgit v0.10.2-6-g49f6 From 87af29f09f119c4a4fa7fdf308483ae1abb74b49 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Sat, 16 Sep 2006 21:20:36 +0200 Subject: git-tar-tree: devolve git-tar-tree into a wrapper for git-archive This patch removes the custom tree walker tree_traverse(), and makes generate_tar() use write_tar_archive() and the infrastructure provided by git-archive instead. As a kind of side effect, make write_tar_archive() able to handle NULL as base directory, as this is what the new and simple generate_tar() uses to indicate the absence of a base directory. This was simpler and cleaner than playing tricks with empty strings. The behaviour of git-tar-tree should be unchanged (quick tests didn't indicate otherwise) except for the text of some error messages. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c index f2679a8..437eb72 100644 --- a/builtin-tar-tree.c +++ b/builtin-tar-tree.c @@ -3,7 +3,6 @@ */ #include #include "cache.h" -#include "tree-walk.h" #include "commit.h" #include "strbuf.h" #include "tar.h" @@ -248,37 +247,6 @@ static void write_global_extended_header(const unsigned char *sha1) free(ext_header.buf); } -static void traverse_tree(struct tree_desc *tree, struct strbuf *path) -{ - int pathlen = path->len; - struct name_entry entry; - - while (tree_entry(tree, &entry)) { - void *eltbuf; - char elttype[20]; - unsigned long eltsize; - - eltbuf = read_sha1_file(entry.sha1, elttype, &eltsize); - if (!eltbuf) - die("cannot read %s", sha1_to_hex(entry.sha1)); - - path->len = pathlen; - strbuf_append_string(path, entry.path); - if (S_ISDIR(entry.mode)) - strbuf_append_string(path, "/"); - - write_entry(entry.sha1, path, entry.mode, eltbuf, eltsize); - - if (S_ISDIR(entry.mode)) { - struct tree_desc subtree; - subtree.buf = eltbuf; - subtree.size = eltsize; - traverse_tree(&subtree, path); - } - free(eltbuf); - } -} - static int git_tar_config(const char *var, const char *value) { if (!strcmp(var, "tar.umask")) { @@ -295,51 +263,29 @@ static int git_tar_config(const char *var, const char *value) static int generate_tar(int argc, const char **argv, const char *prefix) { - unsigned char sha1[20], tree_sha1[20]; - struct commit *commit; - struct tree_desc tree; - struct strbuf current_path; - void *buffer; - - current_path.buf = xmalloc(PATH_MAX); - current_path.alloc = PATH_MAX; - current_path.len = current_path.eof = 0; + struct archiver_args args; + int result; + char *base = NULL; git_config(git_tar_config); - switch (argc) { - case 3: - strbuf_append_string(¤t_path, argv[2]); - strbuf_append_string(¤t_path, "/"); - /* FALLTHROUGH */ - case 2: - if (get_sha1(argv[1], sha1)) - die("Not a valid object name %s", argv[1]); - break; - default: + memset(&args, 0, sizeof(args)); + if (argc != 2 && argc != 3) usage(tar_tree_usage); + if (argc == 3) { + int baselen = strlen(argv[2]); + base = xmalloc(baselen + 2); + memcpy(base, argv[2], baselen); + base[baselen] = '/'; + base[baselen + 1] = '\0'; } + args.base = base; + parse_treeish_arg(argv + 1, &args, NULL); - commit = lookup_commit_reference_gently(sha1, 1); - if (commit) { - write_global_extended_header(commit->object.sha1); - archive_time = commit->date; - } else - archive_time = time(NULL); - - tree.buf = buffer = read_object_with_reference(sha1, tree_type, - &tree.size, tree_sha1); - if (!tree.buf) - die("not a reference to a tag, commit or tree object: %s", - sha1_to_hex(sha1)); - - if (current_path.len > 0) - write_entry(tree_sha1, ¤t_path, 040777, NULL, 0); - traverse_tree(&tree, ¤t_path); - write_trailer(); - free(buffer); - free(current_path.buf); - return 0; + result = write_tar_archive(&args); + free(base); + + return result; } static int write_tar_entry(const unsigned char *sha1, @@ -383,7 +329,7 @@ static int write_tar_entry(const unsigned char *sha1, int write_tar_archive(struct archiver_args *args) { - int plen = strlen(args->base); + int plen = args->base ? strlen(args->base) : 0; git_config(git_tar_config); -- cgit v0.10.2-6-g49f6 From 7939fe44b8abedb4d8bb5090aec7a92eb1aa7a4b Mon Sep 17 00:00:00 2001 From: Matthias Lederhofer Date: Sun, 17 Sep 2006 00:30:27 +0200 Subject: gitweb: do not use 'No such directory' error message undef $project; to prevent a file named description to be read. Signed-off-by: Junio C Hamano diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 2501385..fa65757 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -198,13 +198,10 @@ if (defined $action) { our $project = $cgi->param('p'); if (defined $project) { - if (!validate_input($project)) { - die_error(undef, "Invalid project parameter"); - } - if (!(-d "$projectroot/$project")) { - die_error(undef, "No such directory"); - } - if (!(-e "$projectroot/$project/HEAD")) { + if (!validate_input($project) || + !(-d "$projectroot/$project") || + !(-e "$projectroot/$project/HEAD")) { + undef $project; die_error(undef, "No such project"); } } -- cgit v0.10.2-6-g49f6 From 32f4aaccaa45fcd79bacd424c7d69051156bb766 Mon Sep 17 00:00:00 2001 From: Matthias Lederhofer Date: Sun, 17 Sep 2006 00:31:01 +0200 Subject: gitweb: export options $export_ok: If this variable evaluates to true it is checked if a file with this name exists in the repository. If it does not exist the repository cannot be viewed from gitweb. (Similar to git-daemon-export-ok for git-daemon). $strict_export: If this variable evaluates to true only repositories listed on the project-list-page of gitweb can be accessed. Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index 7b3114f..b9938ac 100644 --- a/Makefile +++ b/Makefile @@ -126,6 +126,8 @@ GITWEB_CONFIG = gitweb_config.perl GITWEB_HOME_LINK_STR = projects GITWEB_SITENAME = GITWEB_PROJECTROOT = /pub/git +GITWEB_EXPORT_OK = +GITWEB_STRICT_EXPORT = GITWEB_BASE_URL = GITWEB_LIST = GITWEB_HOMETEXT = indextext.html @@ -631,6 +633,8 @@ gitweb/gitweb.cgi: gitweb/gitweb.perl -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \ -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \ -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \ + -e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \ + -e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \ -e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \ -e 's|++GITWEB_LIST++|$(GITWEB_LIST)|g' \ -e 's|++GITWEB_HOMETEXT++|$(GITWEB_HOMETEXT)|g' \ diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index fa65757..497129a 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -54,6 +54,13 @@ our $favicon = "++GITWEB_FAVICON++"; # source of projects list our $projects_list = "++GITWEB_LIST++"; +# show repository only if this file exists +# (only effective if this variable evaluates to true) +our $export_ok = "++GITWEB_EXPORT_OK++"; + +# only allow viewing of repositories also shown on the overview page +our $strict_export = "++GITWEB_STRICT_EXPORT++"; + # list of git base URLs used for URL to where fetch project from, # i.e. full URL is "$git_base_url/$project" our @git_base_url_list = ("++GITWEB_BASE_URL++"); @@ -200,7 +207,9 @@ our $project = $cgi->param('p'); if (defined $project) { if (!validate_input($project) || !(-d "$projectroot/$project") || - !(-e "$projectroot/$project/HEAD")) { + !(-e "$projectroot/$project/HEAD") || + ($export_ok && !(-e "$projectroot/$project/$export_ok")) || + ($strict_export && !project_in_list($project))) { undef $project; die_error(undef, "No such project"); } @@ -422,6 +431,12 @@ sub untabify { return $line; } +sub project_in_list { + my $project = shift; + my @list = git_get_projects_list(); + return @list && scalar(grep { $_->{'path'} eq $project } @list); +} + ## ---------------------------------------------------------------------- ## HTML aware string manipulation @@ -734,7 +749,8 @@ sub git_get_projects_list { my $subdir = substr($File::Find::name, $pfxlen + 1); # we check related file in $projectroot - if (-e "$projectroot/$subdir/HEAD") { + if (-e "$projectroot/$subdir/HEAD" && (!$export_ok || + -e "$projectroot/$subdir/$export_ok")) { push @list, { path => $subdir }; $File::Find::prune = 1; } @@ -755,7 +771,8 @@ sub git_get_projects_list { if (!defined $path) { next; } - if (-e "$projectroot/$path/HEAD") { + if (-e "$projectroot/$path/HEAD" && (!$export_ok || + -e "$projectroot/$path/$export_ok")) { my $pr = { path => $path, owner => decode("utf8", $owner, Encode::FB_DEFAULT), -- cgit v0.10.2-6-g49f6 From 9c95fbf94f9a801d04dbe38898909def7a99793f Mon Sep 17 00:00:00 2001 From: Franck Bui-Huu Date: Sun, 17 Sep 2006 16:09:18 +0200 Subject: upload-archive: monitor child communication even more carefully. The current code works like this: if others flags than POLLIN is raised we assume that (a) something bad happened and the child died or (b) the child has closed the pipe because it had no more data to send. For the latter case, we assume wrongly that one call to process_input() will empty the pipe. Indeed it reads only 16Ko of data by call and the the pipe capacity can be larger than that (on current Linux kernel, it is 65536 bytes). Therefore the child can write 32ko of data, for example, and close the pipe. After that poll will return POLLIN _and_ POLLHUP and the parent will read only 16ko of data. This patch forces the parent to empty the pipe as soon as POLLIN is raised and even if POLLHUP or something else is raised too. Moreover, some implementations of poll might return POLLRDNORM flag even if it is non standard. Signed-off-by: Franck Bui-Huu Signed-off-by: Junio C Hamano diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c index 115a12d..0596865 100644 --- a/builtin-upload-archive.c +++ b/builtin-upload-archive.c @@ -160,7 +160,8 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix) if (pfd[1].revents & POLLIN) /* Status stream ready */ process_input(pfd[1].fd, 2); - if ((pfd[0].revents | pfd[1].revents) == POLLIN) + /* Always finish to read data when available */ + if ((pfd[0].revents | pfd[1].revents) & POLLIN) continue; if (waitpid(writer, &status, 0) < 0) -- cgit v0.10.2-6-g49f6 From 645927cec82253a3592ca738c4a405427a71eb31 Mon Sep 17 00:00:00 2001 From: Matthias Lederhofer Date: Sun, 17 Sep 2006 15:29:48 +0200 Subject: gitweb: fix warnings in PATH_INFO code and add export_ok/strict_export Signed-off-by: Junio C Hamano diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 497129a..0fb8638 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -189,9 +189,6 @@ do $GITWEB_CONFIG if -e $GITWEB_CONFIG; # version of the core git binary our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown"; -# path to the current git repository -our $git_dir; - $projects_list ||= $projectroot; # ====================================================================== @@ -273,30 +270,41 @@ if (defined $searchtext) { } # now read PATH_INFO and use it as alternative to parameters -our $path_info = $ENV{"PATH_INFO"}; -$path_info =~ s|^/||; -$path_info =~ s|/$||; -if (validate_input($path_info) && !defined $project) { +sub evaluate_path_info { + return if defined $project; + my $path_info = $ENV{"PATH_INFO"}; + return if !$path_info; + $path_info =~ s,(^/|/$),,gs; + $path_info = validate_input($path_info); + return if !$path_info; $project = $path_info; while ($project && !-e "$projectroot/$project/HEAD") { $project =~ s,/*[^/]*$,,; } - if (defined $project) { - $project = undef unless $project; + if (!$project || + ($export_ok && !-e "$projectroot/$project/$export_ok") || + ($strict_export && !project_in_list($project))) { + undef $project; + return; } + # do not change any parameters if an action is given using the query string + return if $action; if ($path_info =~ m,^$project/([^/]+)/(.+)$,) { # we got "project.git/branch/filename" $action ||= "blob_plain"; - $hash_base ||= $1; - $file_name ||= $2; + $hash_base ||= validate_input($1); + $file_name ||= validate_input($2); } elsif ($path_info =~ m,^$project/([^/]+)$,) { # we got "project.git/branch" $action ||= "shortlog"; - $hash ||= $1; + $hash ||= validate_input($1); } } +evaluate_path_info(); -$git_dir = "$projectroot/$project"; +# path to the current git repository +our $git_dir; +$git_dir = "$projectroot/$project" if $project; # dispatch my %actions = ( -- cgit v0.10.2-6-g49f6 From f58bb6fb41a24723ef59febe344f36956a9a8d41 Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Sun, 17 Sep 2006 12:55:38 +0200 Subject: git-apply(1): document --unidiff-zero Signed-off-by: Jonas Fonseca Signed-off-by: Junio C Hamano diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt index 0a6f7b3..d9137c7 100644 --- a/Documentation/git-apply.txt +++ b/Documentation/git-apply.txt @@ -95,6 +95,16 @@ OPTIONS context exist they all must match. By default no context is ever ignored. +--unidiff-zero:: + By default, gitlink:git-apply[1] expects that the patch being + applied is a unified diff with at least one line of context. + This provides good safety measures, but breaks down when + applying a diff generated with --unified=0. To bypass these + checks use '--unidiff-zero'. ++ +Note, for the reasons stated above usage of context-free patches are +discouraged. + --apply:: If you use any of the options marked "Turns off 'apply'" above, gitlink:git-apply[1] reads and outputs the -- cgit v0.10.2-6-g49f6 From 02ac04fc9feacc0174fcbddba6dc8c098c315b13 Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Sun, 17 Sep 2006 13:02:59 +0200 Subject: git-repack(1): document --window and --depth Copy and pasted from git-pack-objects(1). Signed-off-by: Jonas Fonseca Signed-off-by: Junio C Hamano diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index 9516227..49f7e0a 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -9,7 +9,7 @@ objects into pack files. SYNOPSIS -------- -'git-repack' [-a] [-d] [-f] [-l] [-n] [-q] +'git-repack' [-a] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N] DESCRIPTION ----------- @@ -56,6 +56,16 @@ OPTIONS Do not update the server information with `git update-server-info`. +--window=[N], --depth=[N]:: + These two options affects how the objects contained in the pack are + stored using delta compression. The objects are first internally + sorted by type, size and optionally names and compared against the + other objects within `--window` to see if using delta compression saves + space. `--depth` limits the maximum delta depth; making it too deep + affects the performance on the unpacker side, because delta data needs + to be applied that many times to get to the necessary object. + + Author ------ Written by Linus Torvalds -- cgit v0.10.2-6-g49f6 From ac8e3f2bb8a790ca7810df18c3282d07e84ae345 Mon Sep 17 00:00:00 2001 From: Matthias Lederhofer Date: Sun, 17 Sep 2006 13:52:45 +0200 Subject: gitweb fix validating pg (page) parameter Currently it is possible to give any string ending with a number as page. -1 for example is quite bad (error log shows probably 100 warnings). Signed-off-by: Junio C Hamano diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 0fb8638..c77270c 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -256,7 +256,7 @@ if (defined $hash_parent_base) { our $page = $cgi->param('pg'); if (defined $page) { - if ($page =~ m/[^0-9]$/) { + if ($page =~ m/[^0-9]/) { die_error(undef, "Invalid page parameter"); } } -- cgit v0.10.2-6-g49f6 From e1e22e37f47e3f4d741d28920e1d27e3775c31ad Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 11 Sep 2006 16:37:32 -0700 Subject: Start handling references internally as a sorted in-memory list This also adds some very rudimentary support for the notion of packed refs. HOWEVER! At this point it isn't used to actually look up a ref yet, only for listing them (ie "for_each_ref()" and friends see the packed refs, but none of the other single-ref lookup routines). Note how we keep two separate lists: one for the loose refs, and one for the packed refs we read. That's so that we can easily keep the two apart, and read only one set or the other (and still always make sure that the loose refs take precedence). [ From this, it's not actually obvious why we'd keep the two separate lists, but it's important to have the packed refs on their own list later on, when I add support for looking up a single loose one. For that case, we will want to read _just_ the packed refs in case the single-ref lookup fails, yet we may end up needing the other list at some point in the future, so keeping them separated is important ] Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index 8467447..cdbb566 100644 --- a/Makefile +++ b/Makefile @@ -301,7 +301,8 @@ BUILTIN_OBJS = \ builtin-upload-tar.o \ builtin-verify-pack.o \ builtin-write-tree.o \ - builtin-zip-tree.o + builtin-zip-tree.o \ + builtin-pack-refs.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c new file mode 100644 index 0000000..0f5d827 --- /dev/null +++ b/builtin-pack-refs.c @@ -0,0 +1,41 @@ +#include "cache.h" +#include "refs.h" + +static FILE *refs_file; +static const char *result_path, *lock_path; + +static void remove_lock_file(void) +{ + if (lock_path) + unlink(lock_path); +} + +static int handle_one_ref(const char *path, const unsigned char *sha1) +{ + fprintf(refs_file, "%s %s\n", sha1_to_hex(sha1), path); + return 0; +} + +int cmd_pack_refs(int argc, const char **argv, const char *prefix) +{ + int fd; + + result_path = xstrdup(git_path("packed-refs")); + lock_path = xstrdup(mkpath("%s.lock", result_path)); + + fd = open(lock_path, O_CREAT | O_EXCL | O_WRONLY, 0666); + if (fd < 0) + die("unable to create new ref-pack file (%s)", strerror(errno)); + atexit(remove_lock_file); + + refs_file = fdopen(fd, "w"); + if (!refs_file) + die("unable to create ref-pack file structure (%s)", strerror(errno)); + for_each_ref(handle_one_ref); + fsync(fd); + fclose(refs_file); + if (rename(lock_path, result_path) < 0) + die("unable to overwrite old ref-pack file (%s)", strerror(errno)); + lock_path = NULL; + return 0; +} diff --git a/builtin.h b/builtin.h index ccade94..4b11f52 100644 --- a/builtin.h +++ b/builtin.h @@ -63,5 +63,6 @@ extern int cmd_version(int argc, const char **argv, const char *prefix); extern int cmd_whatchanged(int argc, const char **argv, const char *prefix); extern int cmd_write_tree(int argc, const char **argv, const char *prefix); extern int cmd_verify_pack(int argc, const char **argv, const char *prefix); +extern int cmd_pack_refs(int argc, const char **argv, const char *prefix); #endif diff --git a/git.c b/git.c index 44ab0de..de2a06b 100644 --- a/git.c +++ b/git.c @@ -269,6 +269,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER }, { "write-tree", cmd_write_tree, RUN_SETUP }, { "verify-pack", cmd_verify_pack }, + { "pack-refs", cmd_pack_refs, RUN_SETUP }, }; int i; diff --git a/refs.c b/refs.c index 5e65314..5f80a68 100644 --- a/refs.c +++ b/refs.c @@ -3,6 +3,145 @@ #include +struct ref_list { + struct ref_list *next; + unsigned char sha1[20]; + char name[FLEX_ARRAY]; +}; + +static const char *parse_ref_line(char *line, unsigned char *sha1) +{ + /* + * 42: the answer to everything. + * + * In this case, it happens to be the answer to + * 40 (length of sha1 hex representation) + * +1 (space in between hex and name) + * +1 (newline at the end of the line) + */ + int len = strlen(line) - 42; + + if (len <= 0) + return NULL; + if (get_sha1_hex(line, sha1) < 0) + return NULL; + if (!isspace(line[40])) + return NULL; + line += 41; + if (line[len] != '\n') + return NULL; + line[len] = 0; + return line; +} + +static struct ref_list *add_ref(const char *name, const unsigned char *sha1, struct ref_list *list) +{ + int len; + struct ref_list **p = &list, *entry; + + /* Find the place to insert the ref into.. */ + while ((entry = *p) != NULL) { + int cmp = strcmp(entry->name, name); + if (cmp > 0) + break; + + /* Same as existing entry? */ + if (!cmp) + return list; + p = &entry->next; + } + + /* Allocate it and add it in.. */ + len = strlen(name) + 1; + entry = xmalloc(sizeof(struct ref_list) + len); + hashcpy(entry->sha1, sha1); + memcpy(entry->name, name, len); + entry->next = *p; + *p = entry; + return list; +} + +static struct ref_list *get_packed_refs(void) +{ + static int did_refs = 0; + static struct ref_list *refs = NULL; + + if (!did_refs) { + FILE *f = fopen(git_path("packed-refs"), "r"); + if (f) { + struct ref_list *list = NULL; + char refline[PATH_MAX]; + while (fgets(refline, sizeof(refline), f)) { + unsigned char sha1[20]; + const char *name = parse_ref_line(refline, sha1); + if (!name) + continue; + list = add_ref(name, sha1, list); + } + fclose(f); + refs = list; + } + did_refs = 1; + } + return refs; +} + +static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) +{ + DIR *dir = opendir(git_path("%s", base)); + + if (dir) { + struct dirent *de; + int baselen = strlen(base); + char *path = xmalloc(baselen + 257); + + memcpy(path, base, baselen); + if (baselen && base[baselen-1] != '/') + path[baselen++] = '/'; + + while ((de = readdir(dir)) != NULL) { + unsigned char sha1[20]; + struct stat st; + int namelen; + + if (de->d_name[0] == '.') + continue; + namelen = strlen(de->d_name); + if (namelen > 255) + continue; + if (has_extension(de->d_name, ".lock")) + continue; + memcpy(path + baselen, de->d_name, namelen+1); + if (stat(git_path("%s", path), &st) < 0) + continue; + if (S_ISDIR(st.st_mode)) { + list = get_ref_dir(path, list); + continue; + } + if (read_ref(git_path("%s", path), sha1) < 0) { + error("%s points nowhere!", path); + continue; + } + list = add_ref(path, sha1, list); + } + free(path); + closedir(dir); + } + return list; +} + +static struct ref_list *get_loose_refs(void) +{ + static int did_refs = 0; + static struct ref_list *refs = NULL; + + if (!did_refs) { + refs = get_ref_dir("refs", NULL); + did_refs = 1; + } + return refs; +} + /* We allow "recursive" symbolic refs. Only within reason, though */ #define MAXDEPTH 5 @@ -121,60 +260,41 @@ int read_ref(const char *filename, unsigned char *sha1) static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1), int trim) { - int retval = 0; - DIR *dir = opendir(git_path("%s", base)); - - if (dir) { - struct dirent *de; - int baselen = strlen(base); - char *path = xmalloc(baselen + 257); - - if (!strncmp(base, "./", 2)) { - base += 2; - baselen -= 2; + int retval; + struct ref_list *packed = get_packed_refs(); + struct ref_list *loose = get_loose_refs(); + + while (packed && loose) { + struct ref_list *entry; + int cmp = strcmp(packed->name, loose->name); + if (!cmp) { + packed = packed->next; + continue; } - memcpy(path, base, baselen); - if (baselen && base[baselen-1] != '/') - path[baselen++] = '/'; - - while ((de = readdir(dir)) != NULL) { - unsigned char sha1[20]; - struct stat st; - int namelen; + if (cmp > 0) { + entry = loose; + loose = loose->next; + } else { + entry = packed; + packed = packed->next; + } + if (strncmp(base, entry->name, trim)) + continue; + retval = fn(entry->name + trim, entry->sha1); + if (retval) + return retval; + } - if (de->d_name[0] == '.') - continue; - namelen = strlen(de->d_name); - if (namelen > 255) - continue; - if (has_extension(de->d_name, ".lock")) - continue; - memcpy(path + baselen, de->d_name, namelen+1); - if (stat(git_path("%s", path), &st) < 0) - continue; - if (S_ISDIR(st.st_mode)) { - retval = do_for_each_ref(path, fn, trim); - if (retval) - break; - continue; - } - if (read_ref(git_path("%s", path), sha1) < 0) { - error("%s points nowhere!", path); - continue; - } - if (!has_sha1_file(sha1)) { - error("%s does not point to a valid " - "commit object!", path); - continue; - } - retval = fn(path + trim, sha1); + packed = packed ? packed : loose; + while (packed) { + if (!strncmp(base, packed->name, trim)) { + retval = fn(packed->name + trim, packed->sha1); if (retval) - break; + return retval; } - free(path); - closedir(dir); + packed = packed->next; } - return retval; + return 0; } int head_ref(int (*fn)(const char *path, const unsigned char *sha1)) @@ -187,22 +307,22 @@ int head_ref(int (*fn)(const char *path, const unsigned char *sha1)) int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1)) { - return do_for_each_ref("refs", fn, 0); + return do_for_each_ref("refs/", fn, 0); } int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1)) { - return do_for_each_ref("refs/tags", fn, 10); + return do_for_each_ref("refs/tags/", fn, 10); } int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1)) { - return do_for_each_ref("refs/heads", fn, 11); + return do_for_each_ref("refs/heads/", fn, 11); } int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1)) { - return do_for_each_ref("refs/remotes", fn, 13); + return do_for_each_ref("refs/remotes/", fn, 13); } int get_ref_sha1(const char *ref, unsigned char *sha1) -- cgit v0.10.2-6-g49f6 From b37a562a1097af7403c649a5f903a93acaf279e8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 11 Sep 2006 20:10:15 -0700 Subject: Add support for negative refs You can remove a ref that is packed two different ways: either simply repack all the refs without that one, or create a loose ref that has the magic all-zero SHA1. This also adds back the test that a ref actually has the object it points to. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index 5f80a68..72e2283 100644 --- a/refs.c +++ b/refs.c @@ -280,6 +280,12 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u } if (strncmp(base, entry->name, trim)) continue; + if (is_null_sha1(entry->sha1)) + continue; + if (!has_sha1_file(entry->sha1)) { + error("%s does not point to a valid object!", entry->name); + continue; + } retval = fn(entry->name + trim, entry->sha1); if (retval) return retval; -- cgit v0.10.2-6-g49f6 From ed378ec7e85fd2c5cfcc7bd64b454236357fdd97 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 11 Sep 2006 20:17:35 -0700 Subject: Make ref resolution saner The old code used to totally mix up the notion of a ref-name and the path that that ref was associated with. That was not only horribly ugly (a number of users got the path, and then wanted to try to turn it back into a ref-name again), but it fundamnetally doesn't work at all once we do any setup where a ref doesn't have a 1:1 relationship with a particular pathname. This fixes things up so that we use the ref-name throughout, and only turn it into a pathname once we actually look it up in the filesystem. That makes a lot of things much clearer and more straightforward. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index c407c03..b93c17c 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -249,7 +249,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) FILE *in = stdin; const char *sep = ""; unsigned char head_sha1[20]; - const char *head, *current_branch; + const char *current_branch; git_config(fmt_merge_msg_config); @@ -277,10 +277,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) usage(fmt_merge_msg_usage); /* get current branch */ - head = xstrdup(git_path("HEAD")); - current_branch = resolve_ref(head, head_sha1, 1); - current_branch += strlen(head) - 4; - free((char *)head); + current_branch = resolve_ref("HEAD", head_sha1, 1); if (!strncmp(current_branch, "refs/heads/", 11)) current_branch += 11; diff --git a/builtin-init-db.c b/builtin-init-db.c index 5085018..23b7714 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -218,8 +218,8 @@ static void create_default_files(const char *git_dir, const char *template_path) * branch, if it does not exist yet. */ strcpy(path + len, "HEAD"); - if (read_ref(path, sha1) < 0) { - if (create_symref(path, "refs/heads/master") < 0) + if (read_ref("HEAD", sha1) < 0) { + if (create_symref("HEAD", "refs/heads/master") < 0) exit(1); } diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 578c9fa..4d8db0c 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -437,21 +437,13 @@ static void snarf_refs(int head, int tag) } } -static int rev_is_head(char *head_path, int headlen, char *name, +static int rev_is_head(char *head, int headlen, char *name, unsigned char *head_sha1, unsigned char *sha1) { - int namelen; - if ((!head_path[0]) || + if ((!head[0]) || (head_sha1 && sha1 && hashcmp(head_sha1, sha1))) return 0; - namelen = strlen(name); - if ((headlen < namelen) || - memcmp(head_path + headlen - namelen, name, namelen)) - return 0; - if (headlen == namelen || - head_path[headlen - namelen - 1] == '/') - return 1; - return 0; + return !strcmp(head, name); } static int show_merge_base(struct commit_list *seen, int num_rev) @@ -559,9 +551,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) int all_heads = 0, all_tags = 0; int all_mask, all_revs; int lifo = 1; - char head_path[128]; - const char *head_path_p; - int head_path_len; + char head[128]; + const char *head_p; + int head_len; unsigned char head_sha1[20]; int merge_base = 0; int independent = 0; @@ -638,31 +630,31 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) ac--; av++; } - head_path_p = resolve_ref(git_path("HEAD"), head_sha1, 1); - if (head_path_p) { - head_path_len = strlen(head_path_p); - memcpy(head_path, head_path_p, head_path_len + 1); + head_p = resolve_ref("HEAD", head_sha1, 1); + if (head_p) { + head_len = strlen(head_p); + memcpy(head, head_p, head_len + 1); } else { - head_path_len = 0; - head_path[0] = 0; + head_len = 0; + head[0] = 0; } - if (with_current_branch && head_path_p) { + if (with_current_branch && head_p) { int has_head = 0; for (i = 0; !has_head && i < ref_name_cnt; i++) { /* We are only interested in adding the branch * HEAD points at. */ - if (rev_is_head(head_path, - head_path_len, + if (rev_is_head(head, + head_len, ref_name[i], head_sha1, NULL)) has_head++; } if (!has_head) { - int pfxlen = strlen(git_path("refs/heads/")); - append_one_rev(head_path + pfxlen); + int pfxlen = strlen("refs/heads/"); + append_one_rev(head + pfxlen); } } @@ -713,8 +705,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) if (1 < num_rev || extra < 0) { for (i = 0; i < num_rev; i++) { int j; - int is_head = rev_is_head(head_path, - head_path_len, + int is_head = rev_is_head(head, + head_len, ref_name[i], head_sha1, rev[i]->object.sha1); diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c index 1d3a5e2..6f18db8 100644 --- a/builtin-symbolic-ref.c +++ b/builtin-symbolic-ref.c @@ -7,15 +7,11 @@ static const char git_symbolic_ref_usage[] = static void check_symref(const char *HEAD) { unsigned char sha1[20]; - const char *git_HEAD = xstrdup(git_path("%s", HEAD)); - const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 0); - if (git_refs_heads_master) { - /* we want to strip the .git/ part */ - int pfxlen = strlen(git_HEAD) - strlen(HEAD); - puts(git_refs_heads_master + pfxlen); - } - else + const char *refs_heads_master = resolve_ref("HEAD", sha1, 0); + + if (!refs_heads_master) die("No such ref: %s", HEAD); + puts(refs_heads_master); } int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) @@ -26,7 +22,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) check_symref(argv[1]); break; case 3: - create_symref(xstrdup(git_path("%s", argv[1])), argv[2]); + create_symref(argv[1], argv[2]); break; default: usage(git_symbolic_ref_usage); diff --git a/cache.h b/cache.h index 57db7c9..282eed6 100644 --- a/cache.h +++ b/cache.h @@ -287,8 +287,8 @@ extern int get_sha1_hex(const char *hex, unsigned char *sha1); extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */ extern int read_ref(const char *filename, unsigned char *sha1); extern const char *resolve_ref(const char *path, unsigned char *sha1, int); -extern int create_symref(const char *git_HEAD, const char *refs_heads_master); -extern int validate_symref(const char *git_HEAD); +extern int create_symref(const char *ref, const char *refs_heads_master); +extern int validate_symref(const char *ref); extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2); extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2); diff --git a/refs.c b/refs.c index 72e2283..50c25d3 100644 --- a/refs.c +++ b/refs.c @@ -93,11 +93,11 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) if (dir) { struct dirent *de; int baselen = strlen(base); - char *path = xmalloc(baselen + 257); + char *ref = xmalloc(baselen + 257); - memcpy(path, base, baselen); + memcpy(ref, base, baselen); if (baselen && base[baselen-1] != '/') - path[baselen++] = '/'; + ref[baselen++] = '/'; while ((de = readdir(dir)) != NULL) { unsigned char sha1[20]; @@ -111,20 +111,20 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) continue; if (has_extension(de->d_name, ".lock")) continue; - memcpy(path + baselen, de->d_name, namelen+1); - if (stat(git_path("%s", path), &st) < 0) + memcpy(ref + baselen, de->d_name, namelen+1); + if (stat(git_path("%s", ref), &st) < 0) continue; if (S_ISDIR(st.st_mode)) { - list = get_ref_dir(path, list); + list = get_ref_dir(ref, list); continue; } - if (read_ref(git_path("%s", path), sha1) < 0) { - error("%s points nowhere!", path); + if (read_ref(ref, sha1) < 0) { + error("%s points nowhere!", ref); continue; } - list = add_ref(path, sha1, list); + list = add_ref(ref, sha1, list); } - free(path); + free(ref); closedir(dir); } return list; @@ -145,12 +145,14 @@ static struct ref_list *get_loose_refs(void) /* We allow "recursive" symbolic refs. Only within reason, though */ #define MAXDEPTH 5 -const char *resolve_ref(const char *path, unsigned char *sha1, int reading) +const char *resolve_ref(const char *ref, unsigned char *sha1, int reading) { int depth = MAXDEPTH, len; char buffer[256]; + static char ref_buffer[256]; for (;;) { + const char *path = git_path("%s", ref); struct stat st; char *buf; int fd; @@ -169,14 +171,16 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading) if (reading || errno != ENOENT) return NULL; hashclr(sha1); - return path; + return ref; } /* Follow "normalized" - ie "refs/.." symlinks by hand */ if (S_ISLNK(st.st_mode)) { len = readlink(path, buffer, sizeof(buffer)-1); if (len >= 5 && !memcmp("refs/", buffer, 5)) { - path = git_path("%.*s", len, buffer); + buffer[len] = 0; + strcpy(ref_buffer, buffer); + ref = ref_buffer; continue; } } @@ -201,19 +205,22 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading) while (len && isspace(*buf)) buf++, len--; while (len && isspace(buf[len-1])) - buf[--len] = 0; - path = git_path("%.*s", len, buf); + len--; + buf[len] = 0; + memcpy(ref_buffer, buf, len + 1); + ref = ref_buffer; } if (len < 40 || get_sha1_hex(buffer, sha1)) return NULL; - return path; + return ref; } -int create_symref(const char *git_HEAD, const char *refs_heads_master) +int create_symref(const char *ref_target, const char *refs_heads_master) { const char *lockpath; char ref[1000]; int fd, len, written; + const char *git_HEAD = git_path("%s", ref_target); #ifndef NO_SYMLINK_HEAD if (prefer_symlink_refs) { @@ -251,9 +258,9 @@ int create_symref(const char *git_HEAD, const char *refs_heads_master) return 0; } -int read_ref(const char *filename, unsigned char *sha1) +int read_ref(const char *ref, unsigned char *sha1) { - if (resolve_ref(filename, sha1, 1)) + if (resolve_ref(ref, sha1, 1)) return 0; return -1; } @@ -306,7 +313,7 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u int head_ref(int (*fn)(const char *path, const unsigned char *sha1)) { unsigned char sha1[20]; - if (!read_ref(git_path("HEAD"), sha1)) + if (!read_ref("HEAD", sha1)) return fn("HEAD", sha1); return 0; } @@ -335,7 +342,7 @@ int get_ref_sha1(const char *ref, unsigned char *sha1) { if (check_ref_format(ref)) return -1; - return read_ref(git_path("refs/%s", ref), sha1); + return read_ref(mkpath("refs/%s", ref), sha1); } /* @@ -416,31 +423,30 @@ static struct ref_lock *verify_lock(struct ref_lock *lock, return lock; } -static struct ref_lock *lock_ref_sha1_basic(const char *path, +static struct ref_lock *lock_ref_sha1_basic(const char *ref, int plen, const unsigned char *old_sha1, int mustexist) { - const char *orig_path = path; + const char *orig_ref = ref; struct ref_lock *lock; struct stat st; lock = xcalloc(1, sizeof(struct ref_lock)); lock->lock_fd = -1; - plen = strlen(path) - plen; - path = resolve_ref(path, lock->old_sha1, mustexist); - if (!path) { + ref = resolve_ref(ref, lock->old_sha1, mustexist); + if (!ref) { int last_errno = errno; error("unable to resolve reference %s: %s", - orig_path, strerror(errno)); + orig_ref, strerror(errno)); unlock_ref(lock); errno = last_errno; return NULL; } lock->lk = xcalloc(1, sizeof(struct lock_file)); - lock->ref_file = xstrdup(path); - lock->log_file = xstrdup(git_path("logs/%s", lock->ref_file + plen)); + lock->ref_file = xstrdup(git_path("%s", ref)); + lock->log_file = xstrdup(git_path("logs/%s", ref)); lock->force_write = lstat(lock->ref_file, &st) && errno == ENOENT; if (safe_create_leading_directories(lock->ref_file)) @@ -455,15 +461,14 @@ struct ref_lock *lock_ref_sha1(const char *ref, { if (check_ref_format(ref)) return NULL; - return lock_ref_sha1_basic(git_path("refs/%s", ref), + return lock_ref_sha1_basic(mkpath("refs/%s", ref), 5 + strlen(ref), old_sha1, mustexist); } struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int mustexist) { - return lock_ref_sha1_basic(git_path("%s", ref), - strlen(ref), old_sha1, mustexist); + return lock_ref_sha1_basic(ref, strlen(ref), old_sha1, mustexist); } void unlock_ref(struct ref_lock *lock) diff --git a/sha1_name.c b/sha1_name.c index 1fbc443..b497528 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -247,8 +247,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) NULL }; static const char *warning = "warning: refname '%.*s' is ambiguous.\n"; - const char **p, *pathname; - char *real_path = NULL; + const char **p, *ref; + char *real_ref = NULL; int refs_found = 0, am; unsigned long at_time = (unsigned long)-1; unsigned char *this_result; @@ -276,10 +276,10 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) for (p = fmt; *p; p++) { this_result = refs_found ? sha1_from_ref : sha1; - pathname = resolve_ref(git_path(*p, len, str), this_result, 1); - if (pathname) { + ref = resolve_ref(mkpath(*p, len, str), this_result, 1); + if (ref) { if (!refs_found++) - real_path = xstrdup(pathname); + real_ref = xstrdup(ref); if (!warn_ambiguous_refs) break; } @@ -293,12 +293,12 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) if (at_time != (unsigned long)-1) { read_ref_at( - real_path + strlen(git_path(".")) - 1, + real_ref, at_time, sha1); } - free(real_path); + free(real_ref); return 0; } -- cgit v0.10.2-6-g49f6 From 434cd0cd30d17bc703397a76480fdd129cecc064 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 14 Sep 2006 10:14:47 -0700 Subject: Enable the packed refs file format This actually "turns on" the packed ref file format, now that the infrastructure to do so sanely exists (ie notably the change to make the reference reading logic take refnames rather than pathnames to the loose objects that no longer necessarily even exist). In particular, when the ref lookup hits a refname that has no loose file associated with it, it falls back on the packed-ref information. Also, the ref-locking code, while still using a loose file for the locking itself (and _creating_ a loose file for the new ref) no longer requires that the old ref be in such an unpacked state. Finally, this does a minimal hack to git-checkout.sh to rather than check the ref-file directly, do a "git-rev-parse" on the "heads/$refname". That's not really wonderful - we should rather really have a special routine to verify the names as proper branch head names, but it is a workable solution for now. With this, I can literally do something like git pack-refs find .git/refs -type f -print0 | xargs -0 rm -f -- and the end result is a largely working repository (ie I've done two commits - which creates _one_ unpacked ref file - done things like run "gitk" and "git log" etc, and it all looks ok). There are probably things missing, but I'm hoping that the missing things are now of the "small and obvious" kind, and that somebody else might want to start looking at this too. Hint hint ;) Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/git-checkout.sh b/git-checkout.sh index 580a9e8..c60e029 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -22,7 +22,7 @@ while [ "$#" != "0" ]; do shift [ -z "$newbranch" ] && die "git checkout: -b needs a branch name" - [ -e "$GIT_DIR/refs/heads/$newbranch" ] && + git-rev-parse --symbolic "heads/$newbranch" >&/dev/null && die "git checkout: branch $newbranch already exists" git-check-ref-format "heads/$newbranch" || die "git checkout: we do not like '$newbranch' as a branch name." @@ -51,7 +51,7 @@ while [ "$#" != "0" ]; do fi new="$rev" new_name="$arg^0" - if [ -f "$GIT_DIR/refs/heads/$arg" ]; then + if git-rev-parse "heads/$arg^0" >&/dev/null; then branch="$arg" fi elif rev=$(git-rev-parse --verify "$arg^{tree}" 2>/dev/null) diff --git a/refs.c b/refs.c index 50c25d3..134c0fc 100644 --- a/refs.c +++ b/refs.c @@ -28,6 +28,8 @@ static const char *parse_ref_line(char *line, unsigned char *sha1) if (!isspace(line[40])) return NULL; line += 41; + if (isspace(*line)) + return NULL; if (line[len] != '\n') return NULL; line[len] = 0; @@ -168,6 +170,14 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading) * reading. */ if (lstat(path, &st) < 0) { + struct ref_list *list = get_packed_refs(); + while (list) { + if (!strcmp(ref, list->name)) { + hashcpy(sha1, list->sha1); + return ref; + } + list = list->next; + } if (reading || errno != ENOENT) return NULL; hashclr(sha1); @@ -400,22 +410,13 @@ int check_ref_format(const char *ref) static struct ref_lock *verify_lock(struct ref_lock *lock, const unsigned char *old_sha1, int mustexist) { - char buf[40]; - int nr, fd = open(lock->ref_file, O_RDONLY); - if (fd < 0 && (mustexist || errno != ENOENT)) { - error("Can't verify ref %s", lock->ref_file); - unlock_ref(lock); - return NULL; - } - nr = read(fd, buf, 40); - close(fd); - if (nr != 40 || get_sha1_hex(buf, lock->old_sha1) < 0) { - error("Can't verify ref %s", lock->ref_file); + if (!resolve_ref(lock->ref_name, lock->old_sha1, mustexist)) { + error("Can't verify ref %s", lock->ref_name); unlock_ref(lock); return NULL; } if (hashcmp(lock->old_sha1, old_sha1)) { - error("Ref %s is at %s but expected %s", lock->ref_file, + error("Ref %s is at %s but expected %s", lock->ref_name, sha1_to_hex(lock->old_sha1), sha1_to_hex(old_sha1)); unlock_ref(lock); return NULL; @@ -427,6 +428,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, int plen, const unsigned char *old_sha1, int mustexist) { + char *ref_file; const char *orig_ref = ref; struct ref_lock *lock; struct stat st; @@ -445,13 +447,14 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, } lock->lk = xcalloc(1, sizeof(struct lock_file)); - lock->ref_file = xstrdup(git_path("%s", ref)); + lock->ref_name = xstrdup(ref); lock->log_file = xstrdup(git_path("logs/%s", ref)); - lock->force_write = lstat(lock->ref_file, &st) && errno == ENOENT; + ref_file = git_path(ref); + lock->force_write = lstat(ref_file, &st) && errno == ENOENT; - if (safe_create_leading_directories(lock->ref_file)) - die("unable to create directory for %s", lock->ref_file); - lock->lock_fd = hold_lock_file_for_update(lock->lk, lock->ref_file, 1); + if (safe_create_leading_directories(ref_file)) + die("unable to create directory for %s", ref_file); + lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1); return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock; } @@ -479,7 +482,7 @@ void unlock_ref(struct ref_lock *lock) if (lock->lk) rollback_lock_file(lock->lk); } - free(lock->ref_file); + free(lock->ref_name); free(lock->log_file); free(lock); } @@ -556,7 +559,7 @@ int write_ref_sha1(struct ref_lock *lock, return -1; } if (commit_lock_file(lock->lk)) { - error("Couldn't set %s", lock->ref_file); + error("Couldn't set %s", lock->ref_name); unlock_ref(lock); return -1; } diff --git a/refs.h b/refs.h index 553155c..af347e6 100644 --- a/refs.h +++ b/refs.h @@ -2,7 +2,7 @@ #define REFS_H struct ref_lock { - char *ref_file; + char *ref_name; char *log_file; struct lock_file *lk; unsigned char old_sha1[20]; -- cgit v0.10.2-6-g49f6 From ef176ad06afde424b2167da290a0fdd7fb8ca3d4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 16 Sep 2006 13:46:00 -0700 Subject: Fix t1400-update-ref test minimally It depended on specific error messages to detect failure but the implementation changed and broke the test. This fixes the breakage minimally. Signed-off-by: Junio C Hamano diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index b3b920e..6a917f2 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -30,11 +30,8 @@ rm -f .git/$m test_expect_success \ "fail to create $n" \ "touch .git/$n_dir - git-update-ref $n $A >out 2>err - test "'$? = 1 && - test "" = "$(cat out)" && - grep "error: unable to resolve reference" err && - grep '"$n err" + git-update-ref $n $A >out 2>err"' + test $? != 0' rm -f .git/$n_dir out err test_expect_success \ -- cgit v0.10.2-6-g49f6 From f62363fb02865967e5efd17de1a09de6e02f3e3f Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 17 Sep 2006 19:13:56 -0700 Subject: wt-status: use simplified resolve_ref to find current branch Signed-off-by: Junio C Hamano diff --git a/wt-status.c b/wt-status.c index 4b74e68..050922d 100644 --- a/wt-status.c +++ b/wt-status.c @@ -41,10 +41,8 @@ void wt_status_prepare(struct wt_status *s) s->is_initial = get_sha1("HEAD", sha1) ? 1 : 0; - head = resolve_ref(git_path("HEAD"), sha1, 0); - s->branch = head ? - strdup(head + strlen(get_git_dir()) + 1) : - NULL; + head = resolve_ref("HEAD", sha1, 0); + s->branch = head ? xstrdup(head) : NULL; s->reference = "HEAD"; s->amend = 0; -- cgit v0.10.2-6-g49f6 From b3dc864c6d5ffb96513328f976c076c3a90331b0 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 18 Sep 2006 00:34:38 -0700 Subject: gitignore: git-pack-refs is a generated file. Signed-off-by: Junio C Hamano diff --git a/.gitignore b/.gitignore index a3d9c7a..1ff28c7 100644 --- a/.gitignore +++ b/.gitignore @@ -73,6 +73,7 @@ git-name-rev git-mv git-pack-redundant git-pack-objects +git-pack-refs git-parse-remote git-patch-id git-peek-remote -- cgit v0.10.2-6-g49f6 From 5b10b091139ea5ef87376998c89f3a20dd7c7793 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 18 Sep 2006 01:08:00 -0700 Subject: fsck-objects: adjust to resolve_ref() clean-up. Signed-off-by: Junio C Hamano diff --git a/fsck-objects.c b/fsck-objects.c index 4d994f3..456c17e 100644 --- a/fsck-objects.c +++ b/fsck-objects.c @@ -458,15 +458,13 @@ static void fsck_object_dir(const char *path) static int fsck_head_link(void) { unsigned char sha1[20]; - const char *git_HEAD = xstrdup(git_path("HEAD")); - const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 1); - int pfxlen = strlen(git_HEAD) - 4; /* strip .../.git/ part */ + const char *head_points_at = resolve_ref("HEAD", sha1, 1); - if (!git_refs_heads_master) + if (!head_points_at) return error("HEAD is not a symbolic ref"); - if (strncmp(git_refs_heads_master + pfxlen, "refs/heads/", 11)) + if (strncmp(head_points_at, "refs/heads/", 11)) return error("HEAD points to something strange (%s)", - git_refs_heads_master + pfxlen); + head_points_at); if (is_null_sha1(sha1)) return error("HEAD: not a valid git pointer"); return 0; -- cgit v0.10.2-6-g49f6 From 53cce84c05d3cba237ab45540b1e882be9c97215 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Tue, 19 Sep 2006 22:58:23 +0200 Subject: Fix broken sha1 locking Current git#next is totally broken wrt. cloning over HTTP, generating refs at random directories. Of course it's caused by the static get_pathname() buffer. lock_ref_sha1() stores return value of mkpath()'s get_pathname() call, then calls lock_ref_sha1_basic() which calls git_path(ref) which calls get_pathname() at that point returning pointer to the same buffer. So now you are sprintf()ing a format string into itself, wow! The resulting pathnames are really cute. (If you've been paying attention, yes, the mere fact that a format string _could_ write over itself is very wrong and probably exploitable here. See the other mail I've just sent.) I've never liked how we use return values of those functions so liberally, the "allow some random number of get_pathname() return values to work concurrently" is absolutely horrible pit and we've already fallen in this before IIRC. I consider it an awful coding practice, you add a call somewhere and at some other point some distant caller of that breaks since it reuses the same return values. Not to mention this takes quite some time to debug. My gut feeling tells me that there might be more of this. I don't have time to review the rest of the users of the refs.c functions though. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index 134c0fc..7bd36e4 100644 --- a/refs.c +++ b/refs.c @@ -462,10 +462,12 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1, int mustexist) { + char refpath[PATH_MAX]; if (check_ref_format(ref)) return NULL; - return lock_ref_sha1_basic(mkpath("refs/%s", ref), - 5 + strlen(ref), old_sha1, mustexist); + strcpy(refpath, mkpath("refs/%s", ref)); + return lock_ref_sha1_basic(refpath, strlen(refpath), + old_sha1, mustexist); } struct ref_lock *lock_any_ref_for_update(const char *ref, -- cgit v0.10.2-6-g49f6 From cc4c4f0ce25cc3803197bc19c275fd6f59a2a511 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 20 Sep 2006 21:10:17 -0700 Subject: symbolit-ref: fix resolve_ref conversion. An earlier conversion accidentally hardcoded "HEAD" to be passed to resolve_ref(), thereby causing git-symbolic-ref command to always report where the HEAD points at, ignoring the command line parameter. Signed-off-by: Junio C Hamano diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c index 6f18db8..13163ba 100644 --- a/builtin-symbolic-ref.c +++ b/builtin-symbolic-ref.c @@ -7,7 +7,7 @@ static const char git_symbolic_ref_usage[] = static void check_symref(const char *HEAD) { unsigned char sha1[20]; - const char *refs_heads_master = resolve_ref("HEAD", sha1, 0); + const char *refs_heads_master = resolve_ref(HEAD, sha1, 0); if (!refs_heads_master) die("No such ref: %s", HEAD); -- cgit v0.10.2-6-g49f6 From cb5d709ff8a4bae19d57a470ba2b137c25938a44 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 20 Sep 2006 21:47:42 -0700 Subject: Add callback data to for_each_ref() family. This is a long overdue fix to the API for for_each_ref() family of functions. It allows the callers to specify a callback data pointer, so that the caller does not have to use static variables to communicate with the callback funciton. The updated for_each_ref() family takes a function of type int (*fn)(const char *, const unsigned char *, void *) and a void pointer as parameters, and calls the function with the name of the ref and its SHA-1 with the caller-supplied void pointer as parameters. The commit updates two callers, builtin-name-rev.c and builtin-pack-refs.c as an example. Signed-off-by: Junio C Hamano diff --git a/builtin-name-rev.c b/builtin-name-rev.c index 52886b6..9e3e537 100644 --- a/builtin-name-rev.c +++ b/builtin-name-rev.c @@ -75,11 +75,10 @@ copy_data: } } -static int tags_only; - -static int name_ref(const char *path, const unsigned char *sha1) +static int name_ref(const char *path, const unsigned char *sha1, void *cb_data) { struct object *o = parse_object(sha1); + int tags_only = *(int*)cb_data; int deref = 0; if (tags_only && strncmp(path, "refs/tags/", 10)) @@ -131,6 +130,7 @@ 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 tags_only = 0; git_config(git_default_config); @@ -186,7 +186,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) add_object_array((struct object *)commit, *argv, &revs); } - for_each_ref(name_ref); + for_each_ref(name_ref, &tags_only); if (transform_stdin) { char buffer[2048]; diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index 0f5d827..b3d5470 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -1,7 +1,6 @@ #include "cache.h" #include "refs.h" -static FILE *refs_file; static const char *result_path, *lock_path; static void remove_lock_file(void) @@ -10,8 +9,10 @@ static void remove_lock_file(void) unlink(lock_path); } -static int handle_one_ref(const char *path, const unsigned char *sha1) +static int handle_one_ref(const char *path, const unsigned char *sha1, void *cb_data) { + FILE *refs_file = cb_data; + fprintf(refs_file, "%s %s\n", sha1_to_hex(sha1), path); return 0; } @@ -19,6 +20,7 @@ static int handle_one_ref(const char *path, const unsigned char *sha1) int cmd_pack_refs(int argc, const char **argv, const char *prefix) { int fd; + FILE *refs_file; result_path = xstrdup(git_path("packed-refs")); lock_path = xstrdup(mkpath("%s.lock", result_path)); @@ -31,7 +33,7 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix) refs_file = fdopen(fd, "w"); if (!refs_file) die("unable to create ref-pack file structure (%s)", strerror(errno)); - for_each_ref(handle_one_ref); + for_each_ref(handle_one_ref, refs_file); fsync(fd); fclose(refs_file); if (rename(lock_path, result_path) < 0) diff --git a/builtin-prune.c b/builtin-prune.c index 6228c79..e21c29b 100644 --- a/builtin-prune.c +++ b/builtin-prune.c @@ -174,7 +174,7 @@ static void walk_commit_list(struct rev_info *revs) } } -static int add_one_ref(const char *path, const unsigned char *sha1) +static int add_one_ref(const char *path, const unsigned char *sha1, void *cb_data) { struct object *object = parse_object(sha1); if (!object) @@ -240,7 +240,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix) revs.tree_objects = 1; /* Add all external refs */ - for_each_ref(add_one_ref); + for_each_ref(add_one_ref, NULL); /* Add all refs from the index file */ add_cache_refs(); diff --git a/builtin-push.c b/builtin-push.c index c43f256..88fc8e2 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -27,7 +27,7 @@ static void add_refspec(const char *ref) refspec_nr = nr; } -static int expand_one_ref(const char *ref, const unsigned char *sha1) +static int expand_one_ref(const char *ref, const unsigned char *sha1, void *cb_data) { /* Ignore the "refs/" at the beginning of the refname */ ref += 5; @@ -51,7 +51,7 @@ static void expand_refspecs(void) } if (!tags) return; - for_each_ref(expand_one_ref); + for_each_ref(expand_one_ref, NULL); } static void set_refspecs(const char **refs, int nr) diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index fd3ccc8..c771274 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -137,7 +137,7 @@ static void show_default(void) } } -static int show_reference(const char *refname, const unsigned char *sha1) +static int show_reference(const char *refname, const unsigned char *sha1, void *cb_data) { show_rev(NORMAL, sha1, refname); return 0; @@ -299,19 +299,19 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--all")) { - for_each_ref(show_reference); + for_each_ref(show_reference, NULL); continue; } if (!strcmp(arg, "--branches")) { - for_each_branch_ref(show_reference); + for_each_branch_ref(show_reference, NULL); continue; } if (!strcmp(arg, "--tags")) { - for_each_tag_ref(show_reference); + for_each_tag_ref(show_reference, NULL); continue; } if (!strcmp(arg, "--remotes")) { - for_each_remote_ref(show_reference); + for_each_remote_ref(show_reference, NULL); continue; } if (!strcmp(arg, "--show-prefix")) { diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 4d8db0c..b3548ae 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -346,7 +346,7 @@ static void sort_ref_range(int bottom, int top) compare_ref_name); } -static int append_ref(const char *refname, const unsigned char *sha1) +static int append_ref(const char *refname, const unsigned char *sha1, void *cb_data) { struct commit *commit = lookup_commit_reference_gently(sha1, 1); int i; @@ -369,7 +369,7 @@ static int append_ref(const char *refname, const unsigned char *sha1) return 0; } -static int append_head_ref(const char *refname, const unsigned char *sha1) +static int append_head_ref(const char *refname, const unsigned char *sha1, void *cb_data) { unsigned char tmp[20]; int ofs = 11; @@ -380,14 +380,14 @@ static int append_head_ref(const char *refname, const unsigned char *sha1) */ if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1)) ofs = 5; - return append_ref(refname + ofs, sha1); + return append_ref(refname + ofs, sha1, cb_data); } -static int append_tag_ref(const char *refname, const unsigned char *sha1) +static int append_tag_ref(const char *refname, const unsigned char *sha1, void *cb_data) { if (strncmp(refname, "refs/tags/", 10)) return 0; - return append_ref(refname + 5, sha1); + return append_ref(refname + 5, sha1, cb_data); } static const char *match_ref_pattern = NULL; @@ -401,7 +401,7 @@ static int count_slash(const char *s) return cnt; } -static int append_matching_ref(const char *refname, const unsigned char *sha1) +static int append_matching_ref(const char *refname, const unsigned char *sha1, void *cb_data) { /* we want to allow pattern hold/ to show all * branches under refs/heads/hold/, and v0.99.9? to show @@ -417,22 +417,22 @@ static int append_matching_ref(const char *refname, const unsigned char *sha1) if (fnmatch(match_ref_pattern, tail, 0)) return 0; if (!strncmp("refs/heads/", refname, 11)) - return append_head_ref(refname, sha1); + return append_head_ref(refname, sha1, cb_data); if (!strncmp("refs/tags/", refname, 10)) - return append_tag_ref(refname, sha1); - return append_ref(refname, sha1); + return append_tag_ref(refname, sha1, cb_data); + return append_ref(refname, sha1, cb_data); } static void snarf_refs(int head, int tag) { if (head) { int orig_cnt = ref_name_cnt; - for_each_ref(append_head_ref); + for_each_ref(append_head_ref, NULL); sort_ref_range(orig_cnt, ref_name_cnt); } if (tag) { int orig_cnt = ref_name_cnt; - for_each_ref(append_tag_ref); + for_each_ref(append_tag_ref, NULL); sort_ref_range(orig_cnt, ref_name_cnt); } } @@ -487,7 +487,7 @@ static void append_one_rev(const char *av) { unsigned char revkey[20]; if (!get_sha1(av, revkey)) { - append_ref(av, revkey); + append_ref(av, revkey, NULL); return; } if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) { @@ -495,7 +495,7 @@ static void append_one_rev(const char *av) int saved_matches = ref_name_cnt; match_ref_pattern = av; match_ref_slash = count_slash(av); - for_each_ref(append_matching_ref); + for_each_ref(append_matching_ref, NULL); if (saved_matches == ref_name_cnt && ref_name_cnt < MAX_REVS) error("no matching refs with %s", av); diff --git a/describe.c b/describe.c index ab192f8..ea0f2ce 100644 --- a/describe.c +++ b/describe.c @@ -53,7 +53,7 @@ static void add_to_known_names(const char *path, names = ++idx; } -static int get_name(const char *path, const unsigned char *sha1) +static int get_name(const char *path, const unsigned char *sha1, void *cb_data) { struct commit *commit = lookup_commit_reference_gently(sha1, 1); struct object *object; @@ -113,7 +113,7 @@ static void describe(const char *arg, int last_one) if (!initialized) { initialized = 1; - for_each_ref(get_name); + for_each_ref(get_name, NULL); qsort(name_array, names, sizeof(*name_array), compare_names); } diff --git a/fetch-pack.c b/fetch-pack.c index e8708aa..6264ea1 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -42,7 +42,7 @@ static void rev_list_push(struct commit *commit, int mark) } } -static int rev_list_insert_ref(const char *path, const unsigned char *sha1) +static int rev_list_insert_ref(const char *path, const unsigned char *sha1, void *cb_data) { struct object *o = deref_tag(parse_object(sha1), path, 0); @@ -143,7 +143,7 @@ static int find_common(int fd[2], unsigned char *result_sha1, unsigned in_vain = 0; int got_continue = 0; - for_each_ref(rev_list_insert_ref); + for_each_ref(rev_list_insert_ref, NULL); fetching = 0; for ( ; refs ; refs = refs->next) { @@ -253,7 +253,7 @@ done: static struct commit_list *complete; -static int mark_complete(const char *path, const unsigned char *sha1) +static int mark_complete(const char *path, const unsigned char *sha1, void *cb_data) { struct object *o = parse_object(sha1); @@ -365,7 +365,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match) } } - for_each_ref(mark_complete); + for_each_ref(mark_complete, NULL); if (cutoff) mark_recent_complete_commits(cutoff); diff --git a/fetch.c b/fetch.c index 34df8d3..36d1e76 100644 --- a/fetch.c +++ b/fetch.c @@ -201,7 +201,7 @@ static int interpret_target(char *target, unsigned char *sha1) return -1; } -static int mark_complete(const char *path, const unsigned char *sha1) +static int mark_complete(const char *path, const unsigned char *sha1, void *cb_data) { struct commit *commit = lookup_commit_reference_gently(sha1, 1); if (commit) { @@ -274,7 +274,7 @@ int pull(int targets, char **target, const char **write_ref, } if (!get_recover) - for_each_ref(mark_complete); + for_each_ref(mark_complete, NULL); for (i = 0; i < targets; i++) { if (interpret_target(target[i], &sha1[20 * i])) { diff --git a/fsck-objects.c b/fsck-objects.c index 456c17e..bb0c94e 100644 --- a/fsck-objects.c +++ b/fsck-objects.c @@ -402,7 +402,7 @@ static void fsck_dir(int i, char *path) static int default_refs; -static int fsck_handle_ref(const char *refname, const unsigned char *sha1) +static int fsck_handle_ref(const char *refname, const unsigned char *sha1, void *cb_data) { struct object *obj; @@ -424,7 +424,7 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1) static void get_default_heads(void) { - for_each_ref(fsck_handle_ref); + for_each_ref(fsck_handle_ref, NULL); /* * Not having any default heads isn't really fatal, but diff --git a/http-push.c b/http-push.c index 670ff00..460c9be 100644 --- a/http-push.c +++ b/http-push.c @@ -1864,7 +1864,7 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock) static struct ref *local_refs, **local_tail; static struct ref *remote_refs, **remote_tail; -static int one_local_ref(const char *refname, const unsigned char *sha1) +static int one_local_ref(const char *refname, const unsigned char *sha1, void *cb_data) { struct ref *ref; int len = strlen(refname) + 1; @@ -1913,7 +1913,7 @@ static void one_remote_ref(char *refname) static void get_local_heads(void) { local_tail = &local_refs; - for_each_ref(one_local_ref); + for_each_ref(one_local_ref, NULL); } static void get_dav_remote_heads(void) diff --git a/receive-pack.c b/receive-pack.c index 78f75da..7abc921 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -12,7 +12,7 @@ static int report_status; static char capabilities[] = "report-status"; static int capabilities_sent; -static int show_ref(const char *path, const unsigned char *sha1) +static int show_ref(const char *path, const unsigned char *sha1, void *cb_data) { if (capabilities_sent) packet_write(1, "%s %s\n", sha1_to_hex(sha1), path); @@ -25,9 +25,9 @@ static int show_ref(const char *path, const unsigned char *sha1) static void write_head_info(void) { - for_each_ref(show_ref); + for_each_ref(show_ref, NULL); if (!capabilities_sent) - show_ref("capabilities^{}", null_sha1); + show_ref("capabilities^{}", null_sha1, NULL); } diff --git a/refs.c b/refs.c index 7bd36e4..85564f0 100644 --- a/refs.c +++ b/refs.c @@ -275,7 +275,7 @@ int read_ref(const char *ref, unsigned char *sha1) return -1; } -static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1), int trim) +static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, void *cb_data) { int retval; struct ref_list *packed = get_packed_refs(); @@ -303,7 +303,7 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u error("%s does not point to a valid object!", entry->name); continue; } - retval = fn(entry->name + trim, entry->sha1); + retval = fn(entry->name + trim, entry->sha1, cb_data); if (retval) return retval; } @@ -311,7 +311,7 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u packed = packed ? packed : loose; while (packed) { if (!strncmp(base, packed->name, trim)) { - retval = fn(packed->name + trim, packed->sha1); + retval = fn(packed->name + trim, packed->sha1, cb_data); if (retval) return retval; } @@ -320,34 +320,39 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u return 0; } -int head_ref(int (*fn)(const char *path, const unsigned char *sha1)) +int head_ref(each_ref_fn fn, void *cb_data) { unsigned char sha1[20]; if (!read_ref("HEAD", sha1)) - return fn("HEAD", sha1); + return fn("HEAD", sha1, cb_data); return 0; } -int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1)) +int for_each_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref("refs/", fn, 0); + return do_for_each_ref("refs/", fn, 0, cb_data); } -int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1)) +int for_each_tag_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref("refs/tags/", fn, 10); + return do_for_each_ref("refs/tags/", fn, 10, cb_data); } -int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1)) +int for_each_branch_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref("refs/heads/", fn, 11); + return do_for_each_ref("refs/heads/", fn, 11, cb_data); } -int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1)) +int for_each_remote_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref("refs/remotes/", fn, 13); + return do_for_each_ref("refs/remotes/", fn, 13, cb_data); } +/* NEEDSWORK: This is only used by ssh-upload and it should go; the + * caller should do resolve_ref or read_ref like everybody else. Or + * maybe everybody else should use get_ref_sha1() instead of doing + * read_ref(). + */ int get_ref_sha1(const char *ref, unsigned char *sha1) { if (check_ref_format(ref)) diff --git a/refs.h b/refs.h index af347e6..886c857 100644 --- a/refs.h +++ b/refs.h @@ -14,11 +14,12 @@ struct ref_lock { * Calls the specified function for each ref file until it returns nonzero, * and returns the value */ -extern int head_ref(int (*fn)(const char *path, const unsigned char *sha1)); -extern int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1)); -extern int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1)); -extern int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1)); -extern int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1)); +typedef int each_ref_fn(const char *refname, const unsigned char *sha1, void *cb_data); +extern int head_ref(each_ref_fn, void *); +extern int for_each_ref(each_ref_fn, void *); +extern int for_each_tag_ref(each_ref_fn, void *); +extern int for_each_branch_ref(each_ref_fn, void *); +extern int for_each_remote_ref(each_ref_fn, void *); /** Reads the refs file specified into sha1 **/ extern int get_ref_sha1(const char *ref, unsigned char *sha1); diff --git a/revision.c b/revision.c index 6a2539b..0e84b8a 100644 --- a/revision.c +++ b/revision.c @@ -466,7 +466,7 @@ static void limit_list(struct rev_info *revs) static int all_flags; static struct rev_info *all_revs; -static int handle_one_ref(const char *path, const unsigned char *sha1) +static int handle_one_ref(const char *path, const unsigned char *sha1, void *cb_data) { struct object *object = get_reference(all_revs, path, sha1, all_flags); add_pending_object(all_revs, object, ""); @@ -477,7 +477,7 @@ static void handle_all(struct rev_info *revs, unsigned flags) { all_revs = revs; all_flags = flags; - for_each_ref(handle_one_ref); + for_each_ref(handle_one_ref, NULL); } static int add_parents_only(struct rev_info *revs, const char *arg, int flags) diff --git a/send-pack.c b/send-pack.c index 5bb123a..ee13093 100644 --- a/send-pack.c +++ b/send-pack.c @@ -215,7 +215,7 @@ static int ref_newer(const unsigned char *new_sha1, static struct ref *local_refs, **local_tail; static struct ref *remote_refs, **remote_tail; -static int one_local_ref(const char *refname, const unsigned char *sha1) +static int one_local_ref(const char *refname, const unsigned char *sha1, void *cb_data) { struct ref *ref; int len = strlen(refname) + 1; @@ -230,7 +230,7 @@ static int one_local_ref(const char *refname, const unsigned char *sha1) static void get_local_heads(void) { local_tail = &local_refs; - for_each_ref(one_local_ref); + for_each_ref(one_local_ref, NULL); } static int receive_status(int in) diff --git a/server-info.c b/server-info.c index 2fb8f57..7667b41 100644 --- a/server-info.c +++ b/server-info.c @@ -7,7 +7,7 @@ /* refs */ static FILE *info_ref_fp; -static int add_info_ref(const char *path, const unsigned char *sha1) +static int add_info_ref(const char *path, const unsigned char *sha1, void *cb_data) { struct object *o = parse_object(sha1); @@ -34,7 +34,7 @@ static int update_info_refs(int force) info_ref_fp = fopen(path1, "w"); if (!info_ref_fp) return error("unable to update %s", path0); - for_each_ref(add_info_ref); + for_each_ref(add_info_ref, NULL); fclose(info_ref_fp); rename(path1, path0); free(path0); diff --git a/upload-pack.c b/upload-pack.c index 189b239..10237eb 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -416,7 +416,7 @@ static void receive_needs(void) } } -static int send_ref(const char *refname, const unsigned char *sha1) +static int send_ref(const char *refname, const unsigned char *sha1, void *cb_data) { static const char *capabilities = "multi_ack thin-pack side-band side-band-64k"; struct object *o = parse_object(sha1); @@ -444,8 +444,8 @@ static int send_ref(const char *refname, const unsigned char *sha1) static void upload_pack(void) { reset_timeout(); - head_ref(send_ref); - for_each_ref(send_ref); + head_ref(send_ref, NULL); + for_each_ref(send_ref, NULL); packet_flush(1); receive_needs(); if (want_obj.nr) { -- cgit v0.10.2-6-g49f6 From 8da197755450d4f16018bd4b5486dc8ed88b0f2a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 20 Sep 2006 22:02:01 -0700 Subject: Tell between packed, unpacked and symbolic refs. This adds a "int *flag" parameter to resolve_ref() and makes for_each_ref() family to call callback function with an extra "int flag" parameter. They are used to give two bits of information (REF_ISSYMREF and REF_ISPACKED) about the ref. Signed-off-by: Junio C Hamano diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index b93c17c..3d3097d 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -277,7 +277,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) usage(fmt_merge_msg_usage); /* get current branch */ - current_branch = resolve_ref("HEAD", head_sha1, 1); + current_branch = resolve_ref("HEAD", head_sha1, 1, NULL); if (!strncmp(current_branch, "refs/heads/", 11)) current_branch += 11; diff --git a/builtin-name-rev.c b/builtin-name-rev.c index 9e3e537..618aa31 100644 --- a/builtin-name-rev.c +++ b/builtin-name-rev.c @@ -75,7 +75,7 @@ copy_data: } } -static int name_ref(const char *path, const unsigned char *sha1, void *cb_data) +static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data) { struct object *o = parse_object(sha1); int tags_only = *(int*)cb_data; diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index b3d5470..9871089 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -9,7 +9,8 @@ static void remove_lock_file(void) unlink(lock_path); } -static int handle_one_ref(const char *path, const unsigned char *sha1, void *cb_data) +static int handle_one_ref(const char *path, const unsigned char *sha1, + int flags, void *cb_data) { FILE *refs_file = cb_data; diff --git a/builtin-prune.c b/builtin-prune.c index e21c29b..e79b515 100644 --- a/builtin-prune.c +++ b/builtin-prune.c @@ -174,7 +174,7 @@ static void walk_commit_list(struct rev_info *revs) } } -static int add_one_ref(const char *path, const unsigned char *sha1, void *cb_data) +static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data) { struct object *object = parse_object(sha1); if (!object) diff --git a/builtin-push.c b/builtin-push.c index 88fc8e2..581c44b 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -27,7 +27,7 @@ static void add_refspec(const char *ref) refspec_nr = nr; } -static int expand_one_ref(const char *ref, const unsigned char *sha1, void *cb_data) +static int expand_one_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data) { /* Ignore the "refs/" at the beginning of the refname */ ref += 5; diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index c771274..3b716fb 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -137,7 +137,7 @@ static void show_default(void) } } -static int show_reference(const char *refname, const unsigned char *sha1, void *cb_data) +static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { show_rev(NORMAL, sha1, refname); return 0; diff --git a/builtin-show-branch.c b/builtin-show-branch.c index b3548ae..5d6ce56 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -346,7 +346,7 @@ static void sort_ref_range(int bottom, int top) compare_ref_name); } -static int append_ref(const char *refname, const unsigned char *sha1, void *cb_data) +static int append_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { struct commit *commit = lookup_commit_reference_gently(sha1, 1); int i; @@ -369,7 +369,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, void *cb_d return 0; } -static int append_head_ref(const char *refname, const unsigned char *sha1, void *cb_data) +static int append_head_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { unsigned char tmp[20]; int ofs = 11; @@ -380,14 +380,14 @@ static int append_head_ref(const char *refname, const unsigned char *sha1, void */ if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1)) ofs = 5; - return append_ref(refname + ofs, sha1, cb_data); + return append_ref(refname + ofs, sha1, flag, cb_data); } -static int append_tag_ref(const char *refname, const unsigned char *sha1, void *cb_data) +static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { if (strncmp(refname, "refs/tags/", 10)) return 0; - return append_ref(refname + 5, sha1, cb_data); + return append_ref(refname + 5, sha1, flag, cb_data); } static const char *match_ref_pattern = NULL; @@ -401,7 +401,7 @@ static int count_slash(const char *s) return cnt; } -static int append_matching_ref(const char *refname, const unsigned char *sha1, void *cb_data) +static int append_matching_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { /* we want to allow pattern hold/ to show all * branches under refs/heads/hold/, and v0.99.9? to show @@ -417,10 +417,10 @@ static int append_matching_ref(const char *refname, const unsigned char *sha1, v if (fnmatch(match_ref_pattern, tail, 0)) return 0; if (!strncmp("refs/heads/", refname, 11)) - return append_head_ref(refname, sha1, cb_data); + return append_head_ref(refname, sha1, flag, cb_data); if (!strncmp("refs/tags/", refname, 10)) - return append_tag_ref(refname, sha1, cb_data); - return append_ref(refname, sha1, cb_data); + return append_tag_ref(refname, sha1, flag, cb_data); + return append_ref(refname, sha1, flag, cb_data); } static void snarf_refs(int head, int tag) @@ -487,7 +487,7 @@ static void append_one_rev(const char *av) { unsigned char revkey[20]; if (!get_sha1(av, revkey)) { - append_ref(av, revkey, NULL); + append_ref(av, revkey, 0, NULL); return; } if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) { @@ -630,7 +630,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) ac--; av++; } - head_p = resolve_ref("HEAD", head_sha1, 1); + head_p = resolve_ref("HEAD", head_sha1, 1, NULL); if (head_p) { head_len = strlen(head_p); memcpy(head, head_p, head_len + 1); diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c index 13163ba..d8be052 100644 --- a/builtin-symbolic-ref.c +++ b/builtin-symbolic-ref.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "refs.h" static const char git_symbolic_ref_usage[] = "git-symbolic-ref name [ref]"; @@ -7,10 +8,13 @@ static const char git_symbolic_ref_usage[] = static void check_symref(const char *HEAD) { unsigned char sha1[20]; - const char *refs_heads_master = resolve_ref(HEAD, sha1, 0); + int flag; + const char *refs_heads_master = resolve_ref(HEAD, sha1, 0, &flag); if (!refs_heads_master) die("No such ref: %s", HEAD); + else if (!(flag & REF_ISSYMREF)) + die("ref %s is not a symbolic ref", HEAD); puts(refs_heads_master); } diff --git a/cache.h b/cache.h index 282eed6..6def155 100644 --- a/cache.h +++ b/cache.h @@ -286,7 +286,7 @@ extern int get_sha1(const char *str, unsigned char *sha1); extern int get_sha1_hex(const char *hex, unsigned char *sha1); extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */ extern int read_ref(const char *filename, unsigned char *sha1); -extern const char *resolve_ref(const char *path, unsigned char *sha1, int); +extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *); extern int create_symref(const char *ref, const char *refs_heads_master); extern int validate_symref(const char *ref); diff --git a/describe.c b/describe.c index ea0f2ce..f4029ee 100644 --- a/describe.c +++ b/describe.c @@ -53,7 +53,7 @@ static void add_to_known_names(const char *path, names = ++idx; } -static int get_name(const char *path, const unsigned char *sha1, void *cb_data) +static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data) { struct commit *commit = lookup_commit_reference_gently(sha1, 1); struct object *object; diff --git a/fetch-pack.c b/fetch-pack.c index 6264ea1..99ac08b 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -42,7 +42,7 @@ static void rev_list_push(struct commit *commit, int mark) } } -static int rev_list_insert_ref(const char *path, const unsigned char *sha1, void *cb_data) +static int rev_list_insert_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data) { struct object *o = deref_tag(parse_object(sha1), path, 0); @@ -253,7 +253,7 @@ done: static struct commit_list *complete; -static int mark_complete(const char *path, const unsigned char *sha1, void *cb_data) +static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data) { struct object *o = parse_object(sha1); diff --git a/fetch.c b/fetch.c index 36d1e76..a2cbdfb 100644 --- a/fetch.c +++ b/fetch.c @@ -201,7 +201,7 @@ static int interpret_target(char *target, unsigned char *sha1) return -1; } -static int mark_complete(const char *path, const unsigned char *sha1, void *cb_data) +static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data) { struct commit *commit = lookup_commit_reference_gently(sha1, 1); if (commit) { diff --git a/fsck-objects.c b/fsck-objects.c index bb0c94e..46b628c 100644 --- a/fsck-objects.c +++ b/fsck-objects.c @@ -402,7 +402,7 @@ static void fsck_dir(int i, char *path) static int default_refs; -static int fsck_handle_ref(const char *refname, const unsigned char *sha1, void *cb_data) +static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { struct object *obj; @@ -458,9 +458,10 @@ static void fsck_object_dir(const char *path) static int fsck_head_link(void) { unsigned char sha1[20]; - const char *head_points_at = resolve_ref("HEAD", sha1, 1); + int flag; + const char *head_points_at = resolve_ref("HEAD", sha1, 1, &flag); - if (!head_points_at) + if (!head_points_at || !(flag & REF_ISSYMREF)) return error("HEAD is not a symbolic ref"); if (strncmp(head_points_at, "refs/heads/", 11)) return error("HEAD points to something strange (%s)", diff --git a/http-push.c b/http-push.c index 460c9be..ecefdfd 100644 --- a/http-push.c +++ b/http-push.c @@ -1864,7 +1864,7 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock) static struct ref *local_refs, **local_tail; static struct ref *remote_refs, **remote_tail; -static int one_local_ref(const char *refname, const unsigned char *sha1, void *cb_data) +static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { struct ref *ref; int len = strlen(refname) + 1; diff --git a/receive-pack.c b/receive-pack.c index 7abc921..abbcb6a 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -12,7 +12,7 @@ static int report_status; static char capabilities[] = "report-status"; static int capabilities_sent; -static int show_ref(const char *path, const unsigned char *sha1, void *cb_data) +static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data) { if (capabilities_sent) packet_write(1, "%s %s\n", sha1_to_hex(sha1), path); @@ -27,7 +27,7 @@ static void write_head_info(void) { for_each_ref(show_ref, NULL); if (!capabilities_sent) - show_ref("capabilities^{}", null_sha1, NULL); + show_ref("capabilities^{}", null_sha1, 0, NULL); } diff --git a/refs.c b/refs.c index 85564f0..40f16af 100644 --- a/refs.c +++ b/refs.c @@ -5,6 +5,7 @@ struct ref_list { struct ref_list *next; + unsigned char flag; /* ISSYMREF? ISPACKED? */ unsigned char sha1[20]; char name[FLEX_ARRAY]; }; @@ -36,7 +37,8 @@ static const char *parse_ref_line(char *line, unsigned char *sha1) return line; } -static struct ref_list *add_ref(const char *name, const unsigned char *sha1, struct ref_list *list) +static struct ref_list *add_ref(const char *name, const unsigned char *sha1, + int flag, struct ref_list *list) { int len; struct ref_list **p = &list, *entry; @@ -58,6 +60,7 @@ static struct ref_list *add_ref(const char *name, const unsigned char *sha1, str entry = xmalloc(sizeof(struct ref_list) + len); hashcpy(entry->sha1, sha1); memcpy(entry->name, name, len); + entry->flag = flag; entry->next = *p; *p = entry; return list; @@ -78,7 +81,7 @@ static struct ref_list *get_packed_refs(void) const char *name = parse_ref_line(refline, sha1); if (!name) continue; - list = add_ref(name, sha1, list); + list = add_ref(name, sha1, REF_ISPACKED, list); } fclose(f); refs = list; @@ -104,6 +107,7 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) while ((de = readdir(dir)) != NULL) { unsigned char sha1[20]; struct stat st; + int flag; int namelen; if (de->d_name[0] == '.') @@ -120,11 +124,11 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) list = get_ref_dir(ref, list); continue; } - if (read_ref(ref, sha1) < 0) { + if (!resolve_ref(ref, sha1, 1, &flag)) { error("%s points nowhere!", ref); continue; } - list = add_ref(ref, sha1, list); + list = add_ref(ref, sha1, flag, list); } free(ref); closedir(dir); @@ -147,12 +151,15 @@ static struct ref_list *get_loose_refs(void) /* We allow "recursive" symbolic refs. Only within reason, though */ #define MAXDEPTH 5 -const char *resolve_ref(const char *ref, unsigned char *sha1, int reading) +const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag) { int depth = MAXDEPTH, len; char buffer[256]; static char ref_buffer[256]; + if (flag) + *flag = 0; + for (;;) { const char *path = git_path("%s", ref); struct stat st; @@ -174,6 +181,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading) while (list) { if (!strcmp(ref, list->name)) { hashcpy(sha1, list->sha1); + if (flag) + *flag |= REF_ISPACKED; return ref; } list = list->next; @@ -191,6 +200,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading) buffer[len] = 0; strcpy(ref_buffer, buffer); ref = ref_buffer; + if (flag) + *flag |= REF_ISSYMREF; continue; } } @@ -219,6 +230,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading) buf[len] = 0; memcpy(ref_buffer, buf, len + 1); ref = ref_buffer; + if (flag) + *flag |= REF_ISSYMREF; } if (len < 40 || get_sha1_hex(buffer, sha1)) return NULL; @@ -270,12 +283,13 @@ int create_symref(const char *ref_target, const char *refs_heads_master) int read_ref(const char *ref, unsigned char *sha1) { - if (resolve_ref(ref, sha1, 1)) + if (resolve_ref(ref, sha1, 1, NULL)) return 0; return -1; } -static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, void *cb_data) +static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, + void *cb_data) { int retval; struct ref_list *packed = get_packed_refs(); @@ -303,7 +317,8 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, void *cb_ error("%s does not point to a valid object!", entry->name); continue; } - retval = fn(entry->name + trim, entry->sha1, cb_data); + retval = fn(entry->name + trim, entry->sha1, + entry->flag, cb_data); if (retval) return retval; } @@ -311,7 +326,8 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, void *cb_ packed = packed ? packed : loose; while (packed) { if (!strncmp(base, packed->name, trim)) { - retval = fn(packed->name + trim, packed->sha1, cb_data); + retval = fn(packed->name + trim, packed->sha1, + packed->flag, cb_data); if (retval) return retval; } @@ -323,8 +339,10 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, void *cb_ int head_ref(each_ref_fn fn, void *cb_data) { unsigned char sha1[20]; - if (!read_ref("HEAD", sha1)) - return fn("HEAD", sha1, cb_data); + int flag; + + if (resolve_ref("HEAD", sha1, 1, &flag)) + return fn("HEAD", sha1, flag, cb_data); return 0; } @@ -415,7 +433,7 @@ int check_ref_format(const char *ref) static struct ref_lock *verify_lock(struct ref_lock *lock, const unsigned char *old_sha1, int mustexist) { - if (!resolve_ref(lock->ref_name, lock->old_sha1, mustexist)) { + if (!resolve_ref(lock->ref_name, lock->old_sha1, mustexist, NULL)) { error("Can't verify ref %s", lock->ref_name); unlock_ref(lock); return NULL; @@ -441,7 +459,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, lock = xcalloc(1, sizeof(struct ref_lock)); lock->lock_fd = -1; - ref = resolve_ref(ref, lock->old_sha1, mustexist); + ref = resolve_ref(ref, lock->old_sha1, mustexist, NULL); if (!ref) { int last_errno = errno; error("unable to resolve reference %s: %s", diff --git a/refs.h b/refs.h index 886c857..305d408 100644 --- a/refs.h +++ b/refs.h @@ -14,7 +14,9 @@ struct ref_lock { * Calls the specified function for each ref file until it returns nonzero, * and returns the value */ -typedef int each_ref_fn(const char *refname, const unsigned char *sha1, void *cb_data); +#define REF_ISSYMREF 01 +#define REF_ISPACKED 02 +typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data); extern int head_ref(each_ref_fn, void *); extern int for_each_ref(each_ref_fn, void *); extern int for_each_tag_ref(each_ref_fn, void *); diff --git a/revision.c b/revision.c index 0e84b8a..cb13b90 100644 --- a/revision.c +++ b/revision.c @@ -466,7 +466,7 @@ static void limit_list(struct rev_info *revs) static int all_flags; static struct rev_info *all_revs; -static int handle_one_ref(const char *path, const unsigned char *sha1, void *cb_data) +static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data) { struct object *object = get_reference(all_revs, path, sha1, all_flags); add_pending_object(all_revs, object, ""); diff --git a/send-pack.c b/send-pack.c index ee13093..fbd792c 100644 --- a/send-pack.c +++ b/send-pack.c @@ -215,7 +215,7 @@ static int ref_newer(const unsigned char *new_sha1, static struct ref *local_refs, **local_tail; static struct ref *remote_refs, **remote_tail; -static int one_local_ref(const char *refname, const unsigned char *sha1, void *cb_data) +static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { struct ref *ref; int len = strlen(refname) + 1; diff --git a/server-info.c b/server-info.c index 7667b41..6cd38be 100644 --- a/server-info.c +++ b/server-info.c @@ -7,7 +7,7 @@ /* refs */ static FILE *info_ref_fp; -static int add_info_ref(const char *path, const unsigned char *sha1, void *cb_data) +static int add_info_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data) { struct object *o = parse_object(sha1); diff --git a/sha1_name.c b/sha1_name.c index b497528..84d24c6 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -276,7 +276,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) for (p = fmt; *p; p++) { this_result = refs_found ? sha1_from_ref : sha1; - ref = resolve_ref(mkpath(*p, len, str), this_result, 1); + ref = resolve_ref(mkpath(*p, len, str), this_result, 1, NULL); if (ref) { if (!refs_found++) real_ref = xstrdup(ref); diff --git a/upload-pack.c b/upload-pack.c index 10237eb..9412a9b 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -416,7 +416,7 @@ static void receive_needs(void) } } -static int send_ref(const char *refname, const unsigned char *sha1, void *cb_data) +static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { static const char *capabilities = "multi_ack thin-pack side-band side-band-64k"; struct object *o = parse_object(sha1); diff --git a/wt-status.c b/wt-status.c index 050922d..d8e284c 100644 --- a/wt-status.c +++ b/wt-status.c @@ -41,7 +41,7 @@ void wt_status_prepare(struct wt_status *s) s->is_initial = get_sha1("HEAD", sha1) ? 1 : 0; - head = resolve_ref("HEAD", sha1, 0); + head = resolve_ref("HEAD", sha1, 0, NULL); s->branch = head ? xstrdup(head) : NULL; s->reference = "HEAD"; -- cgit v0.10.2-6-g49f6 From 13e4aa90acad5738f54385c8a336f89fb6aacdd0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 21 Sep 2006 00:06:05 -0700 Subject: pack-refs: do not pack symbolic refs. Now we can tell which one is symbolic and which one is not, it is easy to do so. Signed-off-by: Junio C Hamano diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index 9871089..0fc8a55 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -14,7 +14,9 @@ static int handle_one_ref(const char *path, const unsigned char *sha1, { FILE *refs_file = cb_data; - fprintf(refs_file, "%s %s\n", sha1_to_hex(sha1), path); + /* Do not pack the symbolic refs */ + if (!(flags & REF_ISSYMREF)) + fprintf(refs_file, "%s %s\n", sha1_to_hex(sha1), path); return 0; } -- cgit v0.10.2-6-g49f6 From 968846015229fe0fec28e3c85db722e131c15f93 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 21 Sep 2006 00:06:06 -0700 Subject: git-pack-refs --prune "git pack-refs --prune", after successfully packing the existing refs, removes the loose ref files. It tries to protect against race by doing the usual lock_ref_sha1() which makes sure the contents of the ref has not changed since we last looked at. Also we do not bother trying to prune what was already packed, and we do not try pruning symbolic refs. Signed-off-by: Junio C Hamano diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index 0fc8a55..246dd63 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -2,6 +2,20 @@ #include "refs.h" static const char *result_path, *lock_path; +static const char builtin_pack_refs_usage[] = +"git-pack-refs [--prune]"; + +struct ref_to_prune { + struct ref_to_prune *next; + unsigned char sha1[20]; + char name[FLEX_ARRAY]; +}; + +struct pack_refs_cb_data { + int prune; + struct ref_to_prune *ref_to_prune; + FILE *refs_file; +}; static void remove_lock_file(void) { @@ -9,21 +23,70 @@ static void remove_lock_file(void) unlink(lock_path); } +static int do_not_prune(int flags) +{ + /* If it is already packed or if it is a symref, + * do not prune it. + */ + return (flags & (REF_ISSYMREF|REF_ISPACKED)); +} + static int handle_one_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data) { - FILE *refs_file = cb_data; + struct pack_refs_cb_data *cb = cb_data; /* Do not pack the symbolic refs */ if (!(flags & REF_ISSYMREF)) - fprintf(refs_file, "%s %s\n", sha1_to_hex(sha1), path); + fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path); + if (cb->prune && !do_not_prune(flags)) { + int namelen = strlen(path) + 1; + struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen); + hashcpy(n->sha1, sha1); + strcpy(n->name, path); + n->next = cb->ref_to_prune; + cb->ref_to_prune = n; + } return 0; } +/* make sure nobody touched the ref, and unlink */ +static void prune_ref(struct ref_to_prune *r) +{ + struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1, 1); + + if (lock) { + unlink(git_path(r->name)); + unlock_ref(lock); + } +} + +static void prune_refs(struct ref_to_prune *r) +{ + while (r) { + prune_ref(r); + r = r->next; + } +} + int cmd_pack_refs(int argc, const char **argv, const char *prefix) { - int fd; - FILE *refs_file; + int fd, i; + struct pack_refs_cb_data cbdata; + + memset(&cbdata, 0, sizeof(cbdata)); + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (!strcmp(arg, "--prune")) { + cbdata.prune = 1; + continue; + } + /* perhaps other parameters later... */ + break; + } + if (i != argc) + usage(builtin_pack_refs_usage); result_path = xstrdup(git_path("packed-refs")); lock_path = xstrdup(mkpath("%s.lock", result_path)); @@ -33,14 +96,17 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix) die("unable to create new ref-pack file (%s)", strerror(errno)); atexit(remove_lock_file); - refs_file = fdopen(fd, "w"); - if (!refs_file) - die("unable to create ref-pack file structure (%s)", strerror(errno)); - for_each_ref(handle_one_ref, refs_file); + cbdata.refs_file = fdopen(fd, "w"); + if (!cbdata.refs_file) + die("unable to create ref-pack file structure (%s)", + strerror(errno)); + for_each_ref(handle_one_ref, &cbdata); fsync(fd); - fclose(refs_file); + fclose(cbdata.refs_file); if (rename(lock_path, result_path) < 0) die("unable to overwrite old ref-pack file (%s)", strerror(errno)); lock_path = NULL; + if (cbdata.prune) + prune_refs(cbdata.ref_to_prune); return 0; } -- cgit v0.10.2-6-g49f6