From 4577370e9bfeca8652880b99b8499f76d18865ba Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Mon, 29 Oct 2007 21:05:40 -0400 Subject: [PATCH 01/71] Miscellaneous const changes and utilities The list of remote refs in struct transport should be const, because builtin-fetch will get confused if it changes. The url in git_connect should be const (and work on a copy) instead of requiring the caller to copy it. match_refs doesn't modify the refspecs it gets. get_fetch_map and get_remote_ref don't change the list they get. Allow transport get_refs_list methods to modify the struct transport. Add a function to copy a list of refs, when a function needs a mutable copy of a const list. Add a function to check the type of a ref, as per the code in connect.c Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- builtin-fetch.c | 10 +++++----- cache.h | 2 +- connect.c | 10 +++++++++- http-push.c | 2 +- remote.c | 32 ++++++++++++++++++++++---------- remote.h | 10 +++++++--- send-pack.c | 8 ++++---- transport.c | 10 +++++----- transport.h | 6 +++--- 9 files changed, 57 insertions(+), 33 deletions(-) diff --git a/builtin-fetch.c b/builtin-fetch.c index 003ed76d16..fa0af170dd 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -29,7 +29,7 @@ static void unlock_pack_on_signal(int signo) } static void add_merge_config(struct ref **head, - struct ref *remote_refs, + const struct ref *remote_refs, struct branch *branch, struct ref ***tail) { @@ -77,7 +77,7 @@ static struct ref *get_ref_map(struct transport *transport, struct ref *ref_map = NULL; struct ref **tail = &ref_map; - struct ref *remote_refs = transport_get_remote_refs(transport); + const struct ref *remote_refs = transport_get_remote_refs(transport); if (ref_count || tags) { for (i = 0; i < ref_count; i++) { @@ -345,12 +345,12 @@ static struct ref *find_non_local_tags(struct transport *transport, struct path_list new_refs = { NULL, 0, 0, 1 }; char *ref_name; int ref_name_len; - unsigned char *ref_sha1; - struct ref *tag_ref; + const unsigned char *ref_sha1; + const struct ref *tag_ref; struct ref *rm = NULL; struct ref *ref_map = NULL; struct ref **tail = &ref_map; - struct ref *ref; + const struct ref *ref; for_each_ref(add_existing, &existing_refs); for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) { diff --git a/cache.h b/cache.h index bfffa05dff..119566bf6f 100644 --- a/cache.h +++ b/cache.h @@ -503,7 +503,7 @@ struct ref { #define REF_TAGS (1u << 2) #define CONNECT_VERBOSE (1u << 0) -extern struct child_process *git_connect(int fd[2], char *url, const char *prog, int flags); +extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags); extern int finish_connect(struct child_process *conn); extern int path_match(const char *path, int nr, char **match); extern int get_ack(int fd, unsigned char *result_sha1); diff --git a/connect.c b/connect.c index 44e423dafd..3aefd4ace5 100644 --- a/connect.c +++ b/connect.c @@ -36,6 +36,11 @@ static int check_ref(const char *name, int len, unsigned int flags) return !(flags & ~REF_NORMAL); } +int check_ref_type(const struct ref *ref, int flags) +{ + return check_ref(ref->name, strlen(ref->name), flags); +} + /* * Read all the refs from the other end */ @@ -476,9 +481,10 @@ char *get_port(char *host) * * If it returns, the connect is successful; it just dies on errors. */ -struct child_process *git_connect(int fd[2], char *url, +struct child_process *git_connect(int fd[2], const char *url_orig, const char *prog, int flags) { + char *url = xstrdup(url_orig); char *host, *path = url; char *end; int c; @@ -568,6 +574,7 @@ struct child_process *git_connect(int fd[2], char *url, prog, path, 0, target_host, 0); free(target_host); + free(url); if (free_path) free(path); return NULL; @@ -619,6 +626,7 @@ struct child_process *git_connect(int fd[2], char *url, fd[0] = conn->out; /* read from child's stdout */ fd[1] = conn->in; /* write to child's stdin */ strbuf_release(&cmd); + free(url); if (free_path) free(path); return conn; diff --git a/http-push.c b/http-push.c index c02a3af634..f461bb3248 100644 --- a/http-push.c +++ b/http-push.c @@ -2389,7 +2389,7 @@ int main(int argc, char **argv) if (!remote_tail) remote_tail = &remote_refs; if (match_refs(local_refs, remote_refs, &remote_tail, - nr_refspec, refspec, push_all)) + nr_refspec, (const char **) refspec, push_all)) return -1; if (!remote_refs) { fprintf(stderr, "No refs in common and none specified; doing nothing.\n"); diff --git a/remote.c b/remote.c index bec2ba1adb..59defdbf14 100644 --- a/remote.c +++ b/remote.c @@ -485,7 +485,7 @@ struct ref *alloc_ref(unsigned namelen) return ret; } -static struct ref *copy_ref(struct ref *ref) +static struct ref *copy_ref(const struct ref *ref) { struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1); memcpy(ret, ref, sizeof(struct ref) + strlen(ref->name) + 1); @@ -493,6 +493,18 @@ static struct ref *copy_ref(struct ref *ref) return ret; } +struct ref *copy_ref_list(const struct ref *ref) +{ + struct ref *ret = NULL; + struct ref **tail = &ret; + while (ref) { + *tail = copy_ref(ref); + ref = ref->next; + tail = &((*tail)->next); + } + return ret; +} + void free_refs(struct ref *ref) { struct ref *next; @@ -710,7 +722,7 @@ static const struct refspec *check_pattern_match(const struct refspec *rs, * without thinking. */ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, - int nr_refspec, char **refspec, int all) + int nr_refspec, const char **refspec, int all) { struct refspec *rs = parse_ref_spec(nr_refspec, (const char **) refspec); @@ -810,10 +822,10 @@ int branch_merge_matches(struct branch *branch, return ref_matches_abbrev(branch->merge[i]->src, refname); } -static struct ref *get_expanded_map(struct ref *remote_refs, +static struct ref *get_expanded_map(const struct ref *remote_refs, const struct refspec *refspec) { - struct ref *ref; + const struct ref *ref; struct ref *ret = NULL; struct ref **tail = &ret; @@ -824,7 +836,7 @@ static struct ref *get_expanded_map(struct ref *remote_refs, if (strchr(ref->name, '^')) continue; /* a dereference item */ if (!prefixcmp(ref->name, refspec->src)) { - char *match; + const char *match; struct ref *cpy = copy_ref(ref); match = ref->name + remote_prefix_len; @@ -842,9 +854,9 @@ static struct ref *get_expanded_map(struct ref *remote_refs, return ret; } -static struct ref *find_ref_by_name_abbrev(struct ref *refs, const char *name) +static const struct ref *find_ref_by_name_abbrev(const struct ref *refs, const char *name) { - struct ref *ref; + const struct ref *ref; for (ref = refs; ref; ref = ref->next) { if (ref_matches_abbrev(name, ref->name)) return ref; @@ -852,9 +864,9 @@ static struct ref *find_ref_by_name_abbrev(struct ref *refs, const char *name) return NULL; } -struct ref *get_remote_ref(struct ref *remote_refs, const char *name) +struct ref *get_remote_ref(const struct ref *remote_refs, const char *name) { - struct ref *ref = find_ref_by_name_abbrev(remote_refs, name); + const struct ref *ref = find_ref_by_name_abbrev(remote_refs, name); if (!ref) return NULL; @@ -887,7 +899,7 @@ static struct ref *get_local_ref(const char *name) return ret; } -int get_fetch_map(struct ref *remote_refs, +int get_fetch_map(const struct ref *remote_refs, const struct refspec *refspec, struct ref ***tail, int missing_ok) diff --git a/remote.h b/remote.h index 878b4ecc32..6a4c7a0f37 100644 --- a/remote.h +++ b/remote.h @@ -44,6 +44,10 @@ struct refspec { struct ref *alloc_ref(unsigned namelen); +struct ref *copy_ref_list(const struct ref *ref); + +int check_ref_type(const struct ref *ref, int flags); + /* * Frees the entire list and peers of elements. */ @@ -57,7 +61,7 @@ void ref_remove_duplicates(struct ref *ref_map); struct refspec *parse_ref_spec(int nr_refspec, const char **refspec); int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, - int nr_refspec, char **refspec, int all); + int nr_refspec, const char **refspec, int all); /* * Given a list of the remote refs and the specification of things to @@ -71,10 +75,10 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, * missing_ok is usually false, but when we are adding branch.$name.merge * it is Ok if the branch is not at the remote anymore. */ -int get_fetch_map(struct ref *remote_refs, const struct refspec *refspec, +int get_fetch_map(const struct ref *remote_refs, const struct refspec *refspec, struct ref ***tail, int missing_ok); -struct ref *get_remote_ref(struct ref *remote_refs, const char *name); +struct ref *get_remote_ref(const struct ref *remote_refs, const char *name); /* * For the given remote, reads the refspec's src and sets the other fields. diff --git a/send-pack.c b/send-pack.c index 5e127a1b7b..456fe4f125 100644 --- a/send-pack.c +++ b/send-pack.c @@ -207,7 +207,7 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref) } } -static int send_pack(int in, int out, struct remote *remote, int nr_refspec, char **refspec) +static int send_pack(int in, int out, struct remote *remote, int nr_refspec, const char **refspec) { struct ref *ref; int new_refs; @@ -357,7 +357,7 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha return ret; } -static void verify_remote_names(int nr_heads, char **heads) +static void verify_remote_names(int nr_heads, const char **heads) { int i; @@ -382,7 +382,7 @@ int main(int argc, char **argv) { int i, nr_heads = 0; char *dest = NULL; - char **heads = NULL; + const char **heads = NULL; int fd[2], ret; struct child_process *conn; char *remote_name = NULL; @@ -434,7 +434,7 @@ int main(int argc, char **argv) dest = arg; continue; } - heads = argv; + heads = (const char **) argv; nr_heads = argc - i; break; } diff --git a/transport.c b/transport.c index d44fe7cee7..ab79f0cbb0 100644 --- a/transport.c +++ b/transport.c @@ -141,7 +141,7 @@ static void insert_packed_refs(const char *packed_refs, struct ref **list) } } -static struct ref *get_refs_via_rsync(const struct transport *transport) +static struct ref *get_refs_via_rsync(struct transport *transport) { struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT; struct ref dummy, *tail = &dummy; @@ -427,7 +427,7 @@ static int missing__target(int code, int result) #define missing_target(a) missing__target((a)->http_code, (a)->curl_result) -static struct ref *get_refs_via_curl(const struct transport *transport) +static struct ref *get_refs_via_curl(struct transport *transport) { struct buffer buffer; char *data, *start, *mid; @@ -524,7 +524,7 @@ struct bundle_transport_data { struct bundle_header header; }; -static struct ref *get_refs_from_bundle(const struct transport *transport) +static struct ref *get_refs_from_bundle(struct transport *transport) { struct bundle_transport_data *data = transport->data; struct ref *result = NULL; @@ -596,7 +596,7 @@ static int set_git_option(struct transport *connection, return 1; } -static struct ref *get_refs_via_connect(const struct transport *transport) +static struct ref *get_refs_via_connect(struct transport *transport) { struct git_transport_data *data = transport->data; struct ref *refs; @@ -781,7 +781,7 @@ int transport_push(struct transport *transport, return transport->push(transport, refspec_nr, refspec, flags); } -struct ref *transport_get_remote_refs(struct transport *transport) +const struct ref *transport_get_remote_refs(struct transport *transport) { if (!transport->remote_refs) transport->remote_refs = transport->get_refs_list(transport); diff --git a/transport.h b/transport.h index df12ea7424..d27f5629d2 100644 --- a/transport.h +++ b/transport.h @@ -8,7 +8,7 @@ struct transport { struct remote *remote; const char *url; void *data; - struct ref *remote_refs; + const struct ref *remote_refs; /** * Returns 0 if successful, positive if the option is not @@ -18,7 +18,7 @@ struct transport { int (*set_option)(struct transport *connection, const char *name, const char *value); - struct ref *(*get_refs_list)(const struct transport *transport); + struct ref *(*get_refs_list)(struct transport *transport); int (*fetch)(struct transport *transport, int refs_nr, struct ref **refs); int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags); @@ -61,7 +61,7 @@ int transport_set_option(struct transport *transport, const char *name, int transport_push(struct transport *connection, int refspec_nr, const char **refspec, int flags); -struct ref *transport_get_remote_refs(struct transport *transport); +const struct ref *transport_get_remote_refs(struct transport *transport); int transport_fetch_refs(struct transport *transport, struct ref *refs); void transport_unlock_pack(struct transport *transport); From 18f7c51cf921f7db021e012499456120f3f095a9 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Mon, 29 Oct 2007 21:05:43 -0400 Subject: [PATCH 02/71] Build-in peek-remote, using transport infrastructure. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- Makefile | 3 +- peek-remote.c => builtin-peek-remote.c | 55 ++++++++++++-------------- builtin.h | 1 + git.c | 1 + 4 files changed, 30 insertions(+), 30 deletions(-) rename peek-remote.c => builtin-peek-remote.c (59%) diff --git a/Makefile b/Makefile index 042f79ef8f..14f25ff8ec 100644 --- a/Makefile +++ b/Makefile @@ -239,7 +239,7 @@ PROGRAMS = \ git-fast-import$X \ git-daemon$X \ git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \ - git-peek-remote$X git-receive-pack$X \ + git-receive-pack$X \ git-send-pack$X git-shell$X \ git-show-index$X \ git-unpack-file$X \ @@ -352,6 +352,7 @@ BUILTIN_OBJS = \ builtin-mv.o \ builtin-name-rev.o \ builtin-pack-objects.o \ + builtin-peek-remote.o \ builtin-prune.o \ builtin-prune-packed.o \ builtin-push.o \ diff --git a/peek-remote.c b/builtin-peek-remote.c similarity index 59% rename from peek-remote.c rename to builtin-peek-remote.c index 8d20f7c9c6..b4106f510a 100644 --- a/peek-remote.c +++ b/builtin-peek-remote.c @@ -1,38 +1,26 @@ +#include "builtin.h" #include "cache.h" -#include "refs.h" -#include "pkt-line.h" +#include "transport.h" +#include "remote.h" static const char peek_remote_usage[] = "git-peek-remote [--upload-pack=] [:]"; -static const char *uploadpack = "git-upload-pack"; -static int peek_remote(int fd[2], unsigned flags) +int cmd_peek_remote(int argc, const char **argv, const char *prefix) { - struct ref *ref; - - get_remote_heads(fd[0], &ref, 0, NULL, flags); - packet_flush(fd[1]); - - while (ref) { - printf("%s %s\n", sha1_to_hex(ref->old_sha1), ref->name); - ref = ref->next; - } - return 0; -} - -int main(int argc, char **argv) -{ - int i, ret; - char *dest = NULL; - int fd[2]; - struct child_process *conn; + int i; + const char *dest = NULL; int nongit = 0; unsigned flags = 0; + const char *uploadpack = NULL; + + struct transport *transport; + const struct ref *ref; setup_git_directory_gently(&nongit); for (i = 1; i < argc; i++) { - char *arg = argv[i]; + const char *arg = argv[i]; if (*arg == '-') { if (!prefixcmp(arg, "--upload-pack=")) { @@ -64,10 +52,19 @@ int main(int argc, char **argv) if (!dest || i != argc - 1) usage(peek_remote_usage); - conn = git_connect(fd, dest, uploadpack, 0); - ret = peek_remote(fd, flags); - close(fd[0]); - close(fd[1]); - ret |= finish_connect(conn); - return !!ret; + transport = transport_get(NULL, dest); + if (uploadpack != NULL) + transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack); + + ref = transport_get_remote_refs(transport); + + if (!ref) + return 1; + + while (ref) { + if (check_ref_type(ref, flags)) + printf("%s %s\n", sha1_to_hex(ref->old_sha1), ref->name); + ref = ref->next; + } + return 0; } diff --git a/builtin.h b/builtin.h index 9a6213af12..62ee5373dc 100644 --- a/builtin.h +++ b/builtin.h @@ -55,6 +55,7 @@ extern int cmd_merge_file(int argc, const char **argv, const char *prefix); extern int cmd_mv(int argc, const char **argv, const char *prefix); extern int cmd_name_rev(int argc, const char **argv, const char *prefix); extern int cmd_pack_objects(int argc, const char **argv, const char *prefix); +extern int cmd_peek_remote(int argc, const char **argv, const char *prefix); extern int cmd_pickaxe(int argc, const char **argv, const char *prefix); extern int cmd_prune(int argc, const char **argv, const char *prefix); extern int cmd_prune_packed(int argc, const char **argv, const char *prefix); diff --git a/git.c b/git.c index 4e10581101..c55a13de17 100644 --- a/git.c +++ b/git.c @@ -333,6 +333,7 @@ static void handle_internal_command(int argc, const char **argv) { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE }, { "name-rev", cmd_name_rev, RUN_SETUP }, { "pack-objects", cmd_pack_objects, RUN_SETUP }, + { "peek-remote", cmd_peek_remote }, { "pickaxe", cmd_blame, RUN_SETUP }, { "prune", cmd_prune, RUN_SETUP }, { "prune-packed", cmd_prune_packed, RUN_SETUP }, From 40cb4fab720807f8525fb125e6c4ddc802ee8ed1 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Mon, 29 Oct 2007 22:03:42 -0400 Subject: [PATCH 03/71] Use built-in send-pack. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- transport.c | 51 ++++++++++----------------------------------------- 1 file changed, 10 insertions(+), 41 deletions(-) diff --git a/transport.c b/transport.c index ab79f0cbb0..f4577b7fc6 100644 --- a/transport.c +++ b/transport.c @@ -6,6 +6,7 @@ #endif #include "pkt-line.h" #include "fetch-pack.h" +#include "send-pack.h" #include "walker.h" #include "bundle.h" #include "dir.h" @@ -648,48 +649,16 @@ static int fetch_refs_via_pack(struct transport *transport, static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) { struct git_transport_data *data = transport->data; - const char **argv; - char *rem; - int argc; - int err; + struct send_pack_args args; - argv = xmalloc((refspec_nr + 11) * sizeof(char *)); - argv[0] = "send-pack"; - argc = 1; - if (flags & TRANSPORT_PUSH_ALL) - argv[argc++] = "--all"; - if (flags & TRANSPORT_PUSH_FORCE) - argv[argc++] = "--force"; - if (flags & TRANSPORT_PUSH_DRY_RUN) - argv[argc++] = "--dry-run"; - if (data->receivepack) { - char *rp = xmalloc(strlen(data->receivepack) + 16); - sprintf(rp, "--receive-pack=%s", data->receivepack); - argv[argc++] = rp; - } - if (data->thin) - argv[argc++] = "--thin"; - rem = xmalloc(strlen(transport->remote->name) + 10); - sprintf(rem, "--remote=%s", transport->remote->name); - argv[argc++] = rem; - argv[argc++] = transport->url; - while (refspec_nr--) - argv[argc++] = *refspec++; - argv[argc] = NULL; - err = run_command_v_opt(argv, RUN_GIT_CMD); - switch (err) { - case -ERR_RUN_COMMAND_FORK: - error("unable to fork for %s", argv[0]); - case -ERR_RUN_COMMAND_EXEC: - error("unable to exec %s", argv[0]); - break; - case -ERR_RUN_COMMAND_WAITPID: - case -ERR_RUN_COMMAND_WAITPID_WRONG_PID: - case -ERR_RUN_COMMAND_WAITPID_SIGNAL: - case -ERR_RUN_COMMAND_WAITPID_NOEXIT: - error("%s died with strange error", argv[0]); - } - return !!err; + args.receivepack = data->receivepack; + args.send_all = !!(flags & TRANSPORT_PUSH_ALL); + args.force_update = !!(flags & TRANSPORT_PUSH_FORCE); + args.use_thin_pack = data->thin; + args.verbose = transport->verbose; + args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN); + + return send_pack(&args, transport->url, transport->remote, refspec_nr, refspec); } static int disconnect_git(struct transport *transport) From 96249c04c07f00485a3ed1052ff10b27ab147fc5 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Mon, 29 Oct 2007 22:03:39 -0400 Subject: [PATCH 04/71] Build-in send-pack, with an API for other programs to call. Also marks some more things as const, as needed. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- Makefile | 1 + send-pack.c => builtin-send-pack.c | 81 ++++++++++++++++-------------- builtin.h | 1 + git.c | 1 + send-pack.h | 17 +++++++ 5 files changed, 64 insertions(+), 37 deletions(-) rename send-pack.c => builtin-send-pack.c (88%) create mode 100644 send-pack.h diff --git a/Makefile b/Makefile index 14f25ff8ec..3ec1876cbb 100644 --- a/Makefile +++ b/Makefile @@ -358,6 +358,7 @@ BUILTIN_OBJS = \ builtin-push.o \ builtin-read-tree.o \ builtin-reflog.o \ + builtin-send-pack.o \ builtin-config.o \ builtin-rerere.o \ builtin-reset.o \ diff --git a/send-pack.c b/builtin-send-pack.c similarity index 88% rename from send-pack.c rename to builtin-send-pack.c index 456fe4f125..aedef09ef0 100644 --- a/send-pack.c +++ b/builtin-send-pack.c @@ -5,16 +5,15 @@ #include "pkt-line.h" #include "run-command.h" #include "remote.h" +#include "send-pack.h" static const char send_pack_usage[] = "git-send-pack [--all] [--dry-run] [--force] [--receive-pack=] [--verbose] [--thin] [:] [...]\n" " --all and explicit specification are mutually exclusive."; -static const char *receivepack = "git-receive-pack"; -static int verbose; -static int send_all; -static int force_update; -static int use_thin_pack; -static int dry_run; + +static struct send_pack_args args = { + /* .receivepack = */ "git-receive-pack", +}; /* * Make a pack stream and spit it out into file descriptor fd @@ -26,7 +25,7 @@ static int pack_objects(int fd, struct ref *refs) * the revision parameters to it via its stdin and * let its stdout go back to the other end. */ - const char *args[] = { + const char *argv[] = { "pack-objects", "--all-progress", "--revs", @@ -36,10 +35,10 @@ static int pack_objects(int fd, struct ref *refs) }; struct child_process po; - if (use_thin_pack) - args[4] = "--thin"; + if (args.use_thin_pack) + argv[4] = "--thin"; memset(&po, 0, sizeof(po)); - po.argv = args; + po.argv = argv; po.in = -1; po.out = fd; po.git_cmd = 1; @@ -207,7 +206,7 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref) } } -static int send_pack(int in, int out, struct remote *remote, int nr_refspec, const char **refspec) +static int do_send_pack(int in, int out, struct remote *remote, int nr_refspec, const char **refspec) { struct ref *ref; int new_refs; @@ -230,7 +229,7 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, con if (!remote_tail) remote_tail = &remote_refs; if (match_refs(local_refs, remote_refs, &remote_tail, - nr_refspec, refspec, send_all)) + nr_refspec, refspec, args.send_all)) return -1; if (!remote_refs) { @@ -259,7 +258,7 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, con } if (!will_delete_ref && !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) { - if (verbose) + if (args.verbose) fprintf(stderr, "'%s': up-to-date\n", ref->name); continue; } @@ -283,7 +282,7 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, con * always allowed. */ - if (!force_update && + if (!args.force_update && !will_delete_ref && !is_null_sha1(ref->old_sha1) && !ref->force) { @@ -313,7 +312,7 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, con strcpy(old_hex, sha1_to_hex(ref->old_sha1)); new_hex = sha1_to_hex(ref->new_sha1); - if (!dry_run) { + if (!args.dry_run) { if (ask_for_status_report) { packet_write(out, "%s %s %s%c%s", old_hex, new_hex, ref->name, 0, @@ -338,7 +337,7 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, con } packet_flush(out); - if (new_refs && !dry_run) + if (new_refs && !args.dry_run) ret = pack_objects(out, remote_refs); close(out); @@ -347,7 +346,7 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, con ret = -4; } - if (!dry_run && remote && ret == 0) { + if (!args.dry_run && remote && ret == 0) { for (ref = remote_refs; ref; ref = ref->next) update_tracking_ref(remote, ref); } @@ -378,30 +377,25 @@ static void verify_remote_names(int nr_heads, const char **heads) } } -int main(int argc, char **argv) +int cmd_send_pack(int argc, const char **argv, const char *prefix) { int i, nr_heads = 0; - char *dest = NULL; const char **heads = NULL; - int fd[2], ret; - struct child_process *conn; - char *remote_name = NULL; + const char *remote_name = NULL; struct remote *remote = NULL; - - setup_git_directory(); - git_config(git_default_config); + const char *dest = NULL; argv++; for (i = 1; i < argc; i++, argv++) { - char *arg = *argv; + const char *arg = *argv; if (*arg == '-') { if (!prefixcmp(arg, "--receive-pack=")) { - receivepack = arg + 15; + args.receivepack = arg + 15; continue; } if (!prefixcmp(arg, "--exec=")) { - receivepack = arg + 7; + args.receivepack = arg + 7; continue; } if (!prefixcmp(arg, "--remote=")) { @@ -409,23 +403,23 @@ int main(int argc, char **argv) continue; } if (!strcmp(arg, "--all")) { - send_all = 1; + args.send_all = 1; continue; } if (!strcmp(arg, "--dry-run")) { - dry_run = 1; + args.dry_run = 1; continue; } if (!strcmp(arg, "--force")) { - force_update = 1; + args.force_update = 1; continue; } if (!strcmp(arg, "--verbose")) { - verbose = 1; + args.verbose = 1; continue; } if (!strcmp(arg, "--thin")) { - use_thin_pack = 1; + args.use_thin_pack = 1; continue; } usage(send_pack_usage); @@ -440,9 +434,8 @@ int main(int argc, char **argv) } if (!dest) usage(send_pack_usage); - if (heads && send_all) + if (heads && args.send_all) usage(send_pack_usage); - verify_remote_names(nr_heads, heads); if (remote_name) { remote = remote_get(remote_name); @@ -452,8 +445,22 @@ int main(int argc, char **argv) } } - conn = git_connect(fd, dest, receivepack, verbose ? CONNECT_VERBOSE : 0); - ret = send_pack(fd[0], fd[1], remote, nr_heads, heads); + return send_pack(&args, dest, remote, nr_heads, heads); +} + +int send_pack(struct send_pack_args *my_args, + const char *dest, struct remote *remote, + int nr_heads, const char **heads) +{ + int fd[2], ret; + struct child_process *conn; + + memcpy(&args, my_args, sizeof(args)); + + verify_remote_names(nr_heads, heads); + + conn = git_connect(fd, dest, args.receivepack, args.verbose ? CONNECT_VERBOSE : 0); + ret = do_send_pack(fd[0], fd[1], remote, nr_heads, heads); close(fd[0]); close(fd[1]); ret |= finish_connect(conn); diff --git a/builtin.h b/builtin.h index 62ee5373dc..2335c01b02 100644 --- a/builtin.h +++ b/builtin.h @@ -70,6 +70,7 @@ extern int cmd_rev_parse(int argc, const char **argv, const char *prefix); extern int cmd_revert(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_send_pack(int argc, const char **argv, const char *prefix); extern int cmd_shortlog(int argc, const char **argv, const char *prefix); extern int cmd_show(int argc, const char **argv, const char *prefix); extern int cmd_show_branch(int argc, const char **argv, const char *prefix); diff --git a/git.c b/git.c index c55a13de17..19a2172a10 100644 --- a/git.c +++ b/git.c @@ -348,6 +348,7 @@ static void handle_internal_command(int argc, const char **argv) { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE }, { "rm", cmd_rm, RUN_SETUP | NEED_WORK_TREE }, { "runstatus", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE }, + { "send-pack", cmd_send_pack, RUN_SETUP }, { "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER }, { "show-branch", cmd_show_branch, RUN_SETUP }, { "show", cmd_show, RUN_SETUP | USE_PAGER }, diff --git a/send-pack.h b/send-pack.h new file mode 100644 index 0000000000..7a24f71c77 --- /dev/null +++ b/send-pack.h @@ -0,0 +1,17 @@ +#ifndef SEND_PACK_H +#define SEND_PACK_H + +struct send_pack_args { + const char *receivepack; + unsigned verbose:1, + send_all:1, + force_update:1, + use_thin_pack:1, + dry_run:1; +}; + +int send_pack(struct send_pack_args *args, + const char *dest, struct remote *remote, + int nr_heads, const char **heads); + +#endif From 8951d7c1f1ae38f34617b6c2490bf65e73e371f7 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Sun, 4 Nov 2007 15:51:17 -0500 Subject: [PATCH 05/71] Build in ls-remote This actually replaces peek-remote with ls-remote, since peek-remote now handles everything. peek-remote remains an a second name for ls-remote, although its help message now gives the "ls-remote" name. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- Makefile | 3 +-- builtin-peek-remote.c => builtin-ls-remote.c | 10 +++++----- builtin.h | 2 +- git-ls-remote.sh => contrib/examples/git-ls-remote.sh | 0 git.c | 3 ++- 5 files changed, 9 insertions(+), 9 deletions(-) rename builtin-peek-remote.c => builtin-ls-remote.c (83%) rename git-ls-remote.sh => contrib/examples/git-ls-remote.sh (100%) diff --git a/Makefile b/Makefile index 3ec1876cbb..470e54a60d 100644 --- a/Makefile +++ b/Makefile @@ -210,7 +210,6 @@ BASIC_LDFLAGS = SCRIPT_SH = \ git-bisect.sh git-checkout.sh \ git-clean.sh git-clone.sh git-commit.sh \ - git-ls-remote.sh \ git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \ git-pull.sh git-rebase.sh git-rebase--interactive.sh \ git-repack.sh git-request-pull.sh \ @@ -345,6 +344,7 @@ BUILTIN_OBJS = \ builtin-log.o \ builtin-ls-files.o \ builtin-ls-tree.o \ + builtin-ls-remote.o \ builtin-mailinfo.o \ builtin-mailsplit.o \ builtin-merge-base.o \ @@ -352,7 +352,6 @@ BUILTIN_OBJS = \ builtin-mv.o \ builtin-name-rev.o \ builtin-pack-objects.o \ - builtin-peek-remote.o \ builtin-prune.o \ builtin-prune-packed.o \ builtin-push.o \ diff --git a/builtin-peek-remote.c b/builtin-ls-remote.c similarity index 83% rename from builtin-peek-remote.c rename to builtin-ls-remote.c index b4106f510a..003580c26a 100644 --- a/builtin-peek-remote.c +++ b/builtin-ls-remote.c @@ -3,10 +3,10 @@ #include "transport.h" #include "remote.h" -static const char peek_remote_usage[] = -"git-peek-remote [--upload-pack=] [:]"; +static const char ls_remote_usage[] = +"git-ls-remote [--upload-pack=] [:]"; -int cmd_peek_remote(int argc, const char **argv, const char *prefix) +int cmd_ls_remote(int argc, const char **argv, const char *prefix) { int i; const char *dest = NULL; @@ -43,14 +43,14 @@ int cmd_peek_remote(int argc, const char **argv, const char *prefix) flags |= REF_NORMAL; continue; } - usage(peek_remote_usage); + usage(ls_remote_usage); } dest = arg; break; } if (!dest || i != argc - 1) - usage(peek_remote_usage); + usage(ls_remote_usage); transport = transport_get(NULL, dest); if (uploadpack != NULL) diff --git a/builtin.h b/builtin.h index 2335c01b02..525107f385 100644 --- a/builtin.h +++ b/builtin.h @@ -48,6 +48,7 @@ extern int cmd_log(int argc, const char **argv, const char *prefix); extern int cmd_log_reflog(int argc, const char **argv, const char *prefix); extern int cmd_ls_files(int argc, const char **argv, const char *prefix); extern int cmd_ls_tree(int argc, const char **argv, const char *prefix); +extern int cmd_ls_remote(int argc, const char **argv, const char *prefix); extern int cmd_mailinfo(int argc, const char **argv, const char *prefix); extern int cmd_mailsplit(int argc, const char **argv, const char *prefix); extern int cmd_merge_base(int argc, const char **argv, const char *prefix); @@ -55,7 +56,6 @@ extern int cmd_merge_file(int argc, const char **argv, const char *prefix); extern int cmd_mv(int argc, const char **argv, const char *prefix); extern int cmd_name_rev(int argc, const char **argv, const char *prefix); extern int cmd_pack_objects(int argc, const char **argv, const char *prefix); -extern int cmd_peek_remote(int argc, const char **argv, const char *prefix); extern int cmd_pickaxe(int argc, const char **argv, const char *prefix); extern int cmd_prune(int argc, const char **argv, const char *prefix); extern int cmd_prune_packed(int argc, const char **argv, const char *prefix); diff --git a/git-ls-remote.sh b/contrib/examples/git-ls-remote.sh similarity index 100% rename from git-ls-remote.sh rename to contrib/examples/git-ls-remote.sh diff --git a/git.c b/git.c index 19a2172a10..b173f227f0 100644 --- a/git.c +++ b/git.c @@ -326,6 +326,7 @@ static void handle_internal_command(int argc, const char **argv) { "log", cmd_log, RUN_SETUP | USE_PAGER }, { "ls-files", cmd_ls_files, RUN_SETUP }, { "ls-tree", cmd_ls_tree, RUN_SETUP }, + { "ls-remote", cmd_ls_remote }, { "mailinfo", cmd_mailinfo }, { "mailsplit", cmd_mailsplit }, { "merge-base", cmd_merge_base, RUN_SETUP }, @@ -333,7 +334,7 @@ static void handle_internal_command(int argc, const char **argv) { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE }, { "name-rev", cmd_name_rev, RUN_SETUP }, { "pack-objects", cmd_pack_objects, RUN_SETUP }, - { "peek-remote", cmd_peek_remote }, + { "peek-remote", cmd_ls_remote }, { "pickaxe", cmd_blame, RUN_SETUP }, { "prune", cmd_prune, RUN_SETUP }, { "prune-packed", cmd_prune_packed, RUN_SETUP }, From f76734902bba47afff622068524a0c38f642d769 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 5 Nov 2007 00:11:15 -0500 Subject: [PATCH 06/71] more terse push output This changes the output of send-pack to match the new, more terse fetch output. It looks like this: To git://host.tld/path/to/repo + f3325dc...3b91d1c hasforce -> mirror/hasforce (forced update) f3325dc..bb022dc master -> mirror/master ! [rejected] needsforce -> mirror/needsforce (non-fast forward) * [new branch] newbranch -> mirror/newbranch * [new tag] v1.0 -> v1.0 instead of: updating 'refs/heads/mirror/hasforce' using 'refs/heads/hasforce' from f3325dca9c4a34d74012c0e159254f454930cec7 to 3b91d1c310ca9d7b547b85466dd876e143498304 updating 'refs/heads/mirror/master' using 'refs/heads/master' from f3325dca9c4a34d74012c0e159254f454930cec7 to bb022dc363d5c2aa9aa3026beb9706d44fbe1328 error: remote 'refs/heads/mirror/needsforce' is not an ancestor of local 'refs/heads/needsforce'. Maybe you are not up-to-date and need to pull first? updating 'refs/heads/mirror/newbranch' using 'refs/heads/newbranch' from 0000000000000000000000000000000000000000 to 3b91d1c310ca9d7b547b85466dd876e143498304 updating 'refs/tags/v1.0' from 0000000000000000000000000000000000000000 to bb022dc363d5c2aa9aa3026beb9706d44fbe1328 Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-send-pack.c | 81 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 17 deletions(-) diff --git a/builtin-send-pack.c b/builtin-send-pack.c index aedef09ef0..d74cc3c4a6 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -206,7 +206,18 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref) } } -static int do_send_pack(int in, int out, struct remote *remote, int nr_refspec, const char **refspec) +static const char *prettify_ref(const char *name) +{ + return name + ( + !prefixcmp(name, "refs/heads/") ? 11 : + !prefixcmp(name, "refs/tags/") ? 10 : + !prefixcmp(name, "refs/remotes/") ? 13 : + 0); +} + +#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3) + +static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec) { struct ref *ref; int new_refs; @@ -214,6 +225,7 @@ static int do_send_pack(int in, int out, struct remote *remote, int nr_refspec, int ask_for_status_report = 0; int allow_deleting_refs = 0; int expect_status_report = 0; + int shown_dest = 0; /* No funny business with the matcher */ remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL); @@ -245,21 +257,33 @@ static int do_send_pack(int in, int out, struct remote *remote, int nr_refspec, for (ref = remote_refs; ref; ref = ref->next) { char old_hex[60], *new_hex; int will_delete_ref; + const char *pretty_ref; + const char *pretty_peer; if (!ref->peer_ref) continue; + if (!shown_dest) { + fprintf(stderr, "To %s\n", dest); + shown_dest = 1; + } + + pretty_ref = prettify_ref(ref->name); + pretty_peer = prettify_ref(ref->peer_ref->name); will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1); if (will_delete_ref && !allow_deleting_refs) { - error("remote does not support deleting refs"); + fprintf(stderr, " ! %-*s %s (remote does not support deleting refs)\n", + SUMMARY_WIDTH, "[rejected]", pretty_ref); ret = -2; continue; } if (!will_delete_ref && !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) { if (args.verbose) - fprintf(stderr, "'%s': up-to-date\n", ref->name); + fprintf(stderr, " = %-*s %s -> %s\n", + SUMMARY_WIDTH, "[up to date]", + pretty_peer, pretty_ref); continue; } @@ -296,12 +320,9 @@ static int do_send_pack(int in, int out, struct remote *remote, int nr_refspec, * commits at the remote end and likely * we were not up to date to begin with. */ - error("remote '%s' is not a strict " - "subset of local ref '%s'. " - "maybe you are not up-to-date and " - "need to pull first?", - ref->name, - ref->peer_ref->name); + fprintf(stderr, " ! %-*s %s -> %s (non-fast forward)\n", + SUMMARY_WIDTH, "[rejected]", + pretty_peer, pretty_ref); ret = -2; continue; } @@ -325,14 +346,40 @@ static int do_send_pack(int in, int out, struct remote *remote, int nr_refspec, old_hex, new_hex, ref->name); } if (will_delete_ref) - fprintf(stderr, "deleting '%s'\n", ref->name); + fprintf(stderr, " - %-*s %s\n", + SUMMARY_WIDTH, "[deleting]", + pretty_ref); + else if (is_null_sha1(ref->old_sha1)) { + const char *msg; + + if (!prefixcmp(ref->name, "refs/tags/")) + msg = "[new tag]"; + else + msg = "[new branch]"; + fprintf(stderr, " * %-*s %s -> %s\n", + SUMMARY_WIDTH, msg, + pretty_peer, pretty_ref); + } else { - fprintf(stderr, "updating '%s'", ref->name); - if (strcmp(ref->name, ref->peer_ref->name)) - fprintf(stderr, " using '%s'", - ref->peer_ref->name); - fprintf(stderr, "\n from %s\n to %s\n", - old_hex, new_hex); + char quickref[83]; + char type = ' '; + const char *msg = ""; + + strcpy(quickref, find_unique_abbrev(ref->old_sha1, DEFAULT_ABBREV)); + if (ref_newer(ref->peer_ref->new_sha1, ref->old_sha1)) + strcat(quickref, ".."); + else { + strcat(quickref, "..."); + type = '+'; + msg = " (forced update)"; + } + strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); + + fprintf(stderr, " %c %-*s %s -> %s%s\n", + type, + SUMMARY_WIDTH, quickref, + pretty_peer, pretty_ref, + msg); } } @@ -460,7 +507,7 @@ int send_pack(struct send_pack_args *my_args, verify_remote_names(nr_heads, heads); conn = git_connect(fd, dest, args.receivepack, args.verbose ? CONNECT_VERBOSE : 0); - ret = do_send_pack(fd[0], fd[1], remote, nr_heads, heads); + ret = do_send_pack(fd[0], fd[1], remote, dest, nr_heads, heads); close(fd[0]); close(fd[1]); ret |= finish_connect(conn); From 3b70da2b17dc5b7df644701a96a141d8f7c5ea15 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 5 Nov 2007 00:11:41 -0500 Subject: [PATCH 07/71] receive-pack: don't mention successful updates The proposed updates are already shown to the user by send-pack, so there's no point. We continue to show errors, since they are unexpected. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- receive-pack.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/receive-pack.c b/receive-pack.c index 38e35c06b9..ed44b897f6 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -204,8 +204,6 @@ static const char *update(struct command *cmd) error("failed to delete %s", name); return "failed to delete"; } - fprintf(stderr, "%s: %s -> deleted\n", name, - sha1_to_hex(old_sha1)); return NULL; /* good */ } else { @@ -217,8 +215,6 @@ static const char *update(struct command *cmd) if (write_ref_sha1(lock, new_sha1, "push")) { return "failed to write"; /* error() already called */ } - fprintf(stderr, "%s: %s -> %s\n", name, - sha1_to_hex(old_sha1), sha1_to_hex(new_sha1)); return NULL; /* good */ } } From b50fa2bd061c3bb21f2918849ece43ac9ca2c504 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 5 Nov 2007 00:12:18 -0500 Subject: [PATCH 08/71] send-pack: require --verbose to show update of tracking refs This is really an uninteresting detail, and it just takes attention away from the actual push updates and posssible errors. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-send-pack.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builtin-send-pack.c b/builtin-send-pack.c index d74cc3c4a6..c1fd3f5fbb 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -195,7 +195,8 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref) return; if (!remote_find_tracking(remote, &rs)) { - fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst); + if (args.verbose) + fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst); if (is_null_sha1(ref->peer_ref->new_sha1)) { if (delete_ref(rs.dst, NULL)) error("Failed to delete"); From 7c2c6ee7e0259d591acb3d9841cf5417e6b7a8eb Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 6 Nov 2007 20:29:20 -0500 Subject: [PATCH 09/71] Reteach builtin-ls-remote to understand remotes Prior to being made a builtin git-ls-remote understood that when it was given a remote name we wanted it to resolve that to the pre-configured URL and connect to that location. That changed when it was converted to a builtin and many of my automation tools broke. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-ls-remote.c | 6 ++++- t/t5512-ls-remote.sh | 52 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100755 t/t5512-ls-remote.sh diff --git a/builtin-ls-remote.c b/builtin-ls-remote.c index 003580c26a..56f3f88023 100644 --- a/builtin-ls-remote.c +++ b/builtin-ls-remote.c @@ -14,6 +14,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) unsigned flags = 0; const char *uploadpack = NULL; + struct remote *remote; struct transport *transport; const struct ref *ref; @@ -52,7 +53,10 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) if (!dest || i != argc - 1) usage(ls_remote_usage); - transport = transport_get(NULL, dest); + remote = nongit ? NULL : remote_get(dest); + if (remote && !remote->url_nr) + die("remote %s has no configured URL", dest); + transport = transport_get(remote, remote ? remote->url[0] : dest); if (uploadpack != NULL) transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack); diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh new file mode 100755 index 0000000000..6ec5f7c48b --- /dev/null +++ b/t/t5512-ls-remote.sh @@ -0,0 +1,52 @@ +#!/bin/sh + +test_description='git ls-remote' + +. ./test-lib.sh + +test_expect_success setup ' + + >file && + git add file && + test_tick && + git commit -m initial && + git tag mark && + git show-ref --tags -d | sed -e "s/ / /" >expected.tag && + ( + echo "$(git rev-parse HEAD) HEAD" + git show-ref -d | sed -e "s/ / /" + ) >expected.all && + + git remote add self $(pwd)/.git + +' + +test_expect_success 'ls-remote --tags .git' ' + + git ls-remote --tags .git >actual && + diff -u expected.tag actual + +' + +test_expect_success 'ls-remote .git' ' + + git ls-remote .git >actual && + diff -u expected.all actual + +' + +test_expect_success 'ls-remote --tags self' ' + + git ls-remote --tags self >actual && + diff -u expected.tag actual + +' + +test_expect_success 'ls-remote self' ' + + git ls-remote self >actual && + diff -u expected.all actual + +' + +test_done From acef41c9db33f2261e46e17cca098df3403dd745 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 31 Oct 2007 14:55:17 -0700 Subject: [PATCH 10/71] format-patch -s: add MIME encoding header if signer's name requires so When the body of the commit log message contains a non-ASCII character, format-patch correctly emitted the encoding header to mark the resulting message as such. However, if the original message was fully ASCII, the command line switch "-s" was given to add a new sign-off, and the signer's name was not ASCII only, the resulting message would have contained non-ASCII character but was not marked as such. This was cherry-picked from the fix in 'master' Signed-off-by: Junio C Hamano --- builtin-branch.c | 2 +- builtin-log.c | 2 +- builtin-rev-list.c | 2 +- builtin-show-branch.c | 2 +- commit.c | 6 +++--- commit.h | 3 ++- log-tree.c | 15 ++++++++++++++- 7 files changed, 23 insertions(+), 9 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index 5f5c1823cb..c1e9a61ea5 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -276,7 +276,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, if (commit && !parse_commit(commit)) { pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, &subject, &subject_len, 0, - NULL, NULL, 0); + NULL, NULL, 0, 0); sub = subject; } printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color), diff --git a/builtin-log.c b/builtin-log.c index c6cc3aef52..070886ae57 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -795,7 +795,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) char *buf = NULL; unsigned long buflen = 0; pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, - &buf, &buflen, 0, NULL, NULL, 0); + &buf, &buflen, 0, NULL, NULL, 0, 0); printf("%c %s %s\n", sign, sha1_to_hex(commit->object.sha1), buf); free(buf); diff --git a/builtin-rev-list.c b/builtin-rev-list.c index ac551d59f3..9dbfae416c 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -84,7 +84,7 @@ static void show_commit(struct commit *commit) unsigned long buflen = 0; pretty_print_commit(revs.commit_format, commit, ~0, &buf, &buflen, - revs.abbrev, NULL, NULL, revs.date_mode); + revs.abbrev, NULL, NULL, revs.date_mode, 0); printf("%s%c", buf, hdr_termination); free(buf); } diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 4fa87f6081..b9cf1b379f 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -267,7 +267,7 @@ static void show_one_commit(struct commit *commit, int no_name) if (commit->object.parsed) { pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, &pretty, &pretty_len, - 0, NULL, NULL, 0); + 0, NULL, NULL, 0, 0); pretty_str = pretty; } if (!prefixcmp(pretty_str, "[PATCH] ")) diff --git a/commit.c b/commit.c index 1fbdd2d51b..10f7b14e76 100644 --- a/commit.c +++ b/commit.c @@ -479,7 +479,7 @@ static int get_one_line(const char *msg, unsigned long len) } /* High bit set, or ISO-2022-INT */ -static int non_ascii(int ch) +int non_ascii(int ch) { ch = (ch & 0xff); return ((ch & 0x80) || (ch == 0x1b)); @@ -1158,13 +1158,13 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, char **buf_p, unsigned long *space_p, int abbrev, const char *subject, const char *after_subject, - enum date_mode dmode) + enum date_mode dmode, + int plain_non_ascii) { unsigned long offset = 0; unsigned long beginning_of_body; int indent = 4; const char *msg = commit->buffer; - int plain_non_ascii = 0; char *reencoded; const char *encoding; char *buf; diff --git a/commit.h b/commit.h index 467872eeca..b897b5730d 100644 --- a/commit.h +++ b/commit.h @@ -60,8 +60,9 @@ enum cmit_fmt { CMIT_FMT_UNSPECIFIED, }; +extern int non_ascii(int); extern enum cmit_fmt get_commit_format(const char *arg); -extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char **buf_p, unsigned long *space_p, int abbrev, const char *subject, const char *after_subject, enum date_mode dmode); +extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char **buf_p, unsigned long *space_p, int abbrev, const char *subject, const char *after_subject, enum date_mode dmode, int non_ascii_present); /** Removes the first commit from a list sorted by date, and adds all * of its parents. diff --git a/log-tree.c b/log-tree.c index b509c0c7ec..9ebc24b687 100644 --- a/log-tree.c +++ b/log-tree.c @@ -140,6 +140,18 @@ static unsigned int digits_in_number(unsigned int number) return result; } +static int has_non_ascii(const char *s) +{ + int ch; + if (!s) + return 0; + while ((ch = *s++) != '\0') { + if (non_ascii(ch)) + return 1; + } + return 0; +} + void show_log(struct rev_info *opt, const char *sep) { char *msgbuf = NULL; @@ -290,7 +302,8 @@ void show_log(struct rev_info *opt, const char *sep) */ len = pretty_print_commit(opt->commit_format, commit, ~0u, &msgbuf, &msgbuf_len, abbrev, subject, - extra_headers, opt->date_mode); + extra_headers, opt->date_mode, + has_non_ascii(opt->add_signoff)); if (opt->add_signoff) len = append_signoff(&msgbuf, &msgbuf_len, len, From 6b945b9beee4cd01a58d875c0f83d018b4830ca0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 2 Nov 2007 17:55:31 -0700 Subject: [PATCH 11/71] test format-patch -s: make sure MIME content type is shown as needed Signed-off-by: Junio C Hamano --- t/t4021-format-patch-signer-mime.sh | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100755 t/t4021-format-patch-signer-mime.sh diff --git a/t/t4021-format-patch-signer-mime.sh b/t/t4021-format-patch-signer-mime.sh new file mode 100755 index 0000000000..67a70fadab --- /dev/null +++ b/t/t4021-format-patch-signer-mime.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +test_description='format-patch -s should force MIME encoding as needed' + +. ./test-lib.sh + +test_expect_success setup ' + + >F && + git add F && + git commit -m initial && + echo new line >F && + + test_tick && + git commit -m "This adds some lines to F" F + +' + +test_expect_success 'format normally' ' + + git format-patch --stdout -1 >output && + ! grep Content-Type output + +' + +test_expect_success 'format with signoff without funny signer name' ' + + git format-patch -s --stdout -1 >output && + ! grep Content-Type output + +' + +test_expect_success 'format with non ASCII signer name' ' + + GIT_COMMITTER_NAME="$B$O$^$N(B $B$U$K$*$&(B" \ + git format-patch -s --stdout -1 >output && + grep Content-Type output + +' + +test_done + From 6738c8194286fa0017f72cb57628dcae9ec07b27 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 8 Nov 2007 01:38:12 -0800 Subject: [PATCH 12/71] send-pack: segfault fix on forced push When pushing to overwrite a ref that points at a commit we do not even have, the recent "terse push" patch tried to get a unique abbreviation for the non-existent (from our point of view) object, which resulted in strcpy(buf, NULL) and segfaulted. Signed-off-by: Junio C Hamano --- builtin-send-pack.c | 5 +++-- t/t5405-send-pack-rewind.sh | 42 +++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100755 t/t5405-send-pack-rewind.sh diff --git a/builtin-send-pack.c b/builtin-send-pack.c index c1fd3f5fbb..5a0f5c681c 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -365,8 +365,9 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest char quickref[83]; char type = ' '; const char *msg = ""; - - strcpy(quickref, find_unique_abbrev(ref->old_sha1, DEFAULT_ABBREV)); + const char *old_abb; + old_abb = find_unique_abbrev(ref->old_sha1, DEFAULT_ABBREV); + strcpy(quickref, old_abb ? old_abb : old_hex); if (ref_newer(ref->peer_ref->new_sha1, ref->old_sha1)) strcat(quickref, ".."); else { diff --git a/t/t5405-send-pack-rewind.sh b/t/t5405-send-pack-rewind.sh new file mode 100755 index 0000000000..86abc62271 --- /dev/null +++ b/t/t5405-send-pack-rewind.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +test_description='forced push to replace commit we do not have' + +. ./test-lib.sh + +test_expect_success setup ' + + >file1 && git add file1 && test_tick && + git commit -m Initial && + + mkdir another && ( + cd another && + git init && + git fetch .. master:master + ) && + + >file2 && git add file2 && test_tick && + git commit -m Second + +' + +test_expect_success 'non forced push should die not segfault' ' + + ( + cd another && + git push .. master:master + test $? = 1 + ) + +' + +test_expect_success 'forced push should succeed' ' + + ( + cd another && + git push .. +master:master + ) + +' + +test_done From 28b9d6e548322755bbdb24c28a493862f61b1eba Mon Sep 17 00:00:00 2001 From: Andy Whitcroft Date: Fri, 9 Nov 2007 23:32:10 +0000 Subject: [PATCH 13/71] Teach send-pack a mirror mode Existing "git push --all" is almost perfect for backing up to another repository, except that "--all" only means "all branches" in modern git, and it does not delete old branches and tags that exist at the back-up repository that you have removed from your local repository. This teaches "git-send-pack" a new "--mirror" option. The difference from the "--all" option are that (1) it sends all refs, not just branches, and (2) it deletes old refs you no longer have on the local side from the remote side. Original patch by Junio C Hamano. Signed-off-by: Andy Whitcroft Signed-off-by: Junio C Hamano --- builtin-send-pack.c | 48 +++++++++++++++++++++++++++++++++------------ http-push.c | 4 ++-- remote.c | 15 +++++++++----- remote.h | 7 +++++++ send-pack.h | 1 + 5 files changed, 55 insertions(+), 20 deletions(-) diff --git a/builtin-send-pack.c b/builtin-send-pack.c index 5a0f5c681c..d42164ec08 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -8,7 +8,7 @@ #include "send-pack.h" static const char send_pack_usage[] = -"git-send-pack [--all] [--dry-run] [--force] [--receive-pack=] [--verbose] [--thin] [:] [...]\n" +"git-send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=] [--verbose] [--thin] [:] [...]\n" " --all and explicit specification are mutually exclusive."; static struct send_pack_args args = { @@ -227,6 +227,12 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest int allow_deleting_refs = 0; int expect_status_report = 0; int shown_dest = 0; + int flags = MATCH_REFS_NONE; + + if (args.send_all) + flags |= MATCH_REFS_ALL; + if (args.send_mirror) + flags |= MATCH_REFS_MIRROR; /* No funny business with the matcher */ remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL); @@ -242,7 +248,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest if (!remote_tail) remote_tail = &remote_refs; if (match_refs(local_refs, remote_refs, &remote_tail, - nr_refspec, refspec, args.send_all)) + nr_refspec, refspec, flags)) return -1; if (!remote_refs) { @@ -259,20 +265,28 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest char old_hex[60], *new_hex; int will_delete_ref; const char *pretty_ref; - const char *pretty_peer; + const char *pretty_peer = NULL; /* only used when not deleting */ + const unsigned char *new_sha1; - if (!ref->peer_ref) - continue; + if (!ref->peer_ref) { + if (!args.send_mirror) + continue; + new_sha1 = null_sha1; + } + else + new_sha1 = ref->peer_ref->new_sha1; if (!shown_dest) { fprintf(stderr, "To %s\n", dest); shown_dest = 1; } - pretty_ref = prettify_ref(ref->name); - pretty_peer = prettify_ref(ref->peer_ref->name); + will_delete_ref = is_null_sha1(new_sha1); + + pretty_ref = prettify_ref(ref->name); + if (!will_delete_ref) + pretty_peer = prettify_ref(ref->peer_ref->name); - will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1); if (will_delete_ref && !allow_deleting_refs) { fprintf(stderr, " ! %-*s %s (remote does not support deleting refs)\n", SUMMARY_WIDTH, "[rejected]", pretty_ref); @@ -280,7 +294,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest continue; } if (!will_delete_ref && - !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) { + !hashcmp(ref->old_sha1, new_sha1)) { if (args.verbose) fprintf(stderr, " = %-*s %s -> %s\n", SUMMARY_WIDTH, "[up to date]", @@ -312,8 +326,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest !is_null_sha1(ref->old_sha1) && !ref->force) { if (!has_sha1_file(ref->old_sha1) || - !ref_newer(ref->peer_ref->new_sha1, - ref->old_sha1)) { + !ref_newer(new_sha1, ref->old_sha1)) { /* We do not have the remote ref, or * we know that the remote ref is not * an ancestor of what we are trying to @@ -328,7 +341,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest continue; } } - hashcpy(ref->new_sha1, ref->peer_ref->new_sha1); + hashcpy(ref->new_sha1, new_sha1); if (!will_delete_ref) new_refs++; strcpy(old_hex, sha1_to_hex(ref->old_sha1)); @@ -459,6 +472,10 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) args.dry_run = 1; continue; } + if (!strcmp(arg, "--mirror")) { + args.send_mirror = 1; + continue; + } if (!strcmp(arg, "--force")) { args.force_update = 1; continue; @@ -483,7 +500,12 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) } if (!dest) usage(send_pack_usage); - if (heads && args.send_all) + /* + * --all and --mirror are incompatible; neither makes sense + * with any refspecs. + */ + if ((heads && (args.send_all || args.send_mirror)) || + (args.send_all && args.send_mirror)) usage(send_pack_usage); if (remote_name) { diff --git a/http-push.c b/http-push.c index 99328f5909..66b81f1726 100644 --- a/http-push.c +++ b/http-push.c @@ -78,7 +78,7 @@ static struct curl_slist *no_pragma_header; static struct curl_slist *default_headers; static int push_verbosely; -static int push_all; +static int push_all = MATCH_REFS_NONE; static int force_all; static int dry_run; @@ -2300,7 +2300,7 @@ int main(int argc, char **argv) if (*arg == '-') { if (!strcmp(arg, "--all")) { - push_all = 1; + push_all = MATCH_REFS_ALL; continue; } if (!strcmp(arg, "--force")) { diff --git a/remote.c b/remote.c index 59defdbf14..09b7aad525 100644 --- a/remote.c +++ b/remote.c @@ -722,10 +722,12 @@ static const struct refspec *check_pattern_match(const struct refspec *rs, * without thinking. */ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, - int nr_refspec, const char **refspec, int all) + int nr_refspec, const char **refspec, int flags) { struct refspec *rs = parse_ref_spec(nr_refspec, (const char **) refspec); + int send_all = flags & MATCH_REFS_ALL; + int send_mirror = flags & MATCH_REFS_MIRROR; if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec)) return -1; @@ -742,7 +744,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, if (!pat) continue; } - else if (prefixcmp(src->name, "refs/heads/")) + else if (!send_mirror && prefixcmp(src->name, "refs/heads/")) /* * "matching refs"; traditionally we pushed everything * including refs outside refs/heads/ hierarchy, but @@ -763,10 +765,13 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, if (dst_peer && dst_peer->peer_ref) /* We're already sending something to this ref. */ goto free_name; - if (!dst_peer && !nr_refspec && !all) - /* Remote doesn't have it, and we have no + + if (!dst_peer && !nr_refspec && !(send_all || send_mirror)) + /* + * Remote doesn't have it, and we have no * explicit pattern, and we don't have - * --all. */ + * --all nor --mirror. + */ goto free_name; if (!dst_peer) { /* Create a new one and link it */ diff --git a/remote.h b/remote.h index 6a4c7a0f37..b10036cae6 100644 --- a/remote.h +++ b/remote.h @@ -102,4 +102,11 @@ struct branch *branch_get(const char *name); int branch_has_merge_config(struct branch *branch); int branch_merge_matches(struct branch *, int n, const char *); +/* Flags to match_refs. */ +enum match_refs_flags { + MATCH_REFS_NONE = 0, + MATCH_REFS_ALL = (1 << 0), + MATCH_REFS_MIRROR = (1 << 1), +}; + #endif diff --git a/send-pack.h b/send-pack.h index 7a24f71c77..8ff1dc3539 100644 --- a/send-pack.h +++ b/send-pack.h @@ -5,6 +5,7 @@ struct send_pack_args { const char *receivepack; unsigned verbose:1, send_all:1, + send_mirror:1, force_update:1, use_thin_pack:1, dry_run:1; From 94c89ba662e964c544fdb171dc8dd33f95b97942 Mon Sep 17 00:00:00 2001 From: Andy Whitcroft Date: Fri, 9 Nov 2007 23:32:25 +0000 Subject: [PATCH 14/71] git-push: plumb in --mirror mode Plumb in the --mirror mode for git-push. Signed-off-by: Andy Whitcroft Signed-off-by: Junio C Hamano --- builtin-push.c | 14 ++++++++++++-- transport.c | 7 +++++++ transport.h | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/builtin-push.c b/builtin-push.c index 2c561953fc..d49157c926 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -10,7 +10,7 @@ #include "parse-options.h" static const char * const push_usage[] = { - "git-push [--all] [--dry-run] [--tags] [--receive-pack=] [--repo=all] [-f | --force] [-v] [ ...]", + "git-push [--all | --mirror] [--dry-run] [--tags] [--receive-pack=] [--repo=all] [-f | --force] [-v] [ ...]", NULL, }; @@ -91,6 +91,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) { int flags = 0; int all = 0; + int mirror = 0; int dry_run = 0; int force = 0; int tags = 0; @@ -100,6 +101,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) OPT__VERBOSE(&verbose), OPT_STRING( 0 , "repo", &repo, "repository", "repository"), OPT_BOOLEAN( 0 , "all", &all, "push all refs"), + OPT_BOOLEAN( 0 , "mirror", &mirror, "mirror all refs"), OPT_BOOLEAN( 0 , "tags", &tags, "push tags"), OPT_BOOLEAN( 0 , "dry-run", &dry_run, "dry run"), OPT_BOOLEAN('f', "force", &force, "force updates"), @@ -119,13 +121,21 @@ int cmd_push(int argc, const char **argv, const char *prefix) add_refspec("refs/tags/*"); if (all) flags |= TRANSPORT_PUSH_ALL; + if (mirror) + flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE); if (argc > 0) { repo = argv[0]; set_refspecs(argv + 1, argc - 1); } - if ((flags & TRANSPORT_PUSH_ALL) && refspec) + if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) && refspec) usage_with_options(push_usage, options); + if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) == + (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) { + error("--all and --mirror are incompatible"); + usage_with_options(push_usage, options); + } + return do_push(repo, flags); } diff --git a/transport.c b/transport.c index 83677fca40..fad97d7cb2 100644 --- a/transport.c +++ b/transport.c @@ -284,6 +284,9 @@ static int rsync_transport_push(struct transport *transport, struct child_process rsync; const char *args[10]; + if (flags & TRANSPORT_PUSH_MIRROR) + return error("rsync transport does not support mirror mode"); + /* first push the objects */ strbuf_addstr(&buf, transport->url); @@ -387,6 +390,9 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons int argc; int err; + if (flags & TRANSPORT_PUSH_MIRROR) + return error("http transport does not support mirror mode"); + argv = xmalloc((refspec_nr + 11) * sizeof(char *)); argv[0] = "http-push"; argc = 1; @@ -655,6 +661,7 @@ static int git_transport_push(struct transport *transport, int refspec_nr, const args.receivepack = data->receivepack; args.send_all = !!(flags & TRANSPORT_PUSH_ALL); + args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR); args.force_update = !!(flags & TRANSPORT_PUSH_FORCE); args.use_thin_pack = data->thin; args.verbose = transport->verbose; diff --git a/transport.h b/transport.h index d27f5629d2..7f337d2f0f 100644 --- a/transport.h +++ b/transport.h @@ -30,6 +30,7 @@ struct transport { #define TRANSPORT_PUSH_ALL 1 #define TRANSPORT_PUSH_FORCE 2 #define TRANSPORT_PUSH_DRY_RUN 4 +#define TRANSPORT_PUSH_MIRROR 8 /* Returns a transport suitable for the url */ struct transport *transport_get(struct remote *, const char *); From fb6e4e1f3f048898677f3cf177bfcaf60123bd5c Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 8 Nov 2007 08:03:06 +0100 Subject: [PATCH 15/71] Do git reset --hard HEAD when using git rebase --skip When you have a merge conflict and want to bypass the commit causing it, you don't want to care about the dirty state of the working tree. Also, don't git reset --hard HEAD in the rebase-skip test, so that the lack of support for this is detected. Signed-off-by: Mike Hommey Signed-off-by: Junio C Hamano --- git-rebase.sh | 1 + t/t3403-rebase-skip.sh | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/git-rebase.sh b/git-rebase.sh index 224cca98ee..9af9da992f 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -157,6 +157,7 @@ do exit ;; --skip) + git reset --hard HEAD || exit $? if test -d "$dotest" then git rerere clear diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh index eab053c3e0..becabfc33c 100755 --- a/t/t3403-rebase-skip.sh +++ b/t/t3403-rebase-skip.sh @@ -36,7 +36,6 @@ test_expect_failure 'rebase with git am -3 (default)' ' ' test_expect_success 'rebase --skip with am -3' ' - git reset --hard HEAD && git rebase --skip ' test_expect_success 'checkout skip-merge' 'git checkout -f skip-merge' @@ -44,7 +43,6 @@ test_expect_success 'checkout skip-merge' 'git checkout -f skip-merge' test_expect_failure 'rebase with --merge' 'git rebase --merge master' test_expect_success 'rebase --skip with --merge' ' - git reset --hard HEAD && git rebase --skip ' From 6fa92bf3cdbfdd8c9c56ef40a5170a859442b166 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Mon, 12 Nov 2007 22:38:23 +0100 Subject: [PATCH 16/71] Add a test checking if send-pack updated local tracking branches correctly Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- t/t5404-tracking-branches.sh | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100755 t/t5404-tracking-branches.sh diff --git a/t/t5404-tracking-branches.sh b/t/t5404-tracking-branches.sh new file mode 100755 index 0000000000..20718d4679 --- /dev/null +++ b/t/t5404-tracking-branches.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +test_description='tracking branch update checks for git push' + +. ./test-lib.sh + +test_expect_success 'setup' ' + echo 1 >file && + git add file && + git commit -m 1 && + git branch b1 && + git branch b2 && + git clone . aa && + git checkout b1 && + echo b1 >>file && + git commit -a -m b1 && + git checkout b2 && + echo b2 >>file && + git commit -a -m b2 +' + +test_expect_success 'check tracking branches updated correctly after push' ' + cd aa && + b1=$(git rev-parse origin/b1) && + b2=$(git rev-parse origin/b2) && + git checkout -b b1 origin/b1 && + echo aa-b1 >>file && + git commit -a -m aa-b1 && + git checkout -b b2 origin/b2 && + echo aa-b2 >>file && + git commit -a -m aa-b2 && + git checkout master && + echo aa-master >>file && + git commit -a -m aa-master && + git push && + test "$(git rev-parse origin/b1)" = "$b1" && + test "$(git rev-parse origin/b2)" = "$b2" +' + +test_done From ed31df312a30d91c288a4b6e3d031de15284405a Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Mon, 12 Nov 2007 22:39:38 +0100 Subject: [PATCH 17/71] Update the tracking references only if they were succesfully updated on remote It fixes the bug where local tracking branches were filled with zeroed SHA-1 if the remote branch was not updated because, for instance, it was not an ancestor of the local (i.e. had other changes). Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- send-pack.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/send-pack.c b/send-pack.c index b74fd454f2..d56d980af7 100644 --- a/send-pack.c +++ b/send-pack.c @@ -349,7 +349,8 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha if (!dry_run && remote && ret == 0) { for (ref = remote_refs; ref; ref = ref->next) - update_tracking_ref(remote, ref); + if (!is_null_sha1(ref->new_sha1)) + update_tracking_ref(remote, ref); } if (!new_refs && ret == 0) From 8e806adb65dc4a0df91a0ebd54b76bb24ff1263e Mon Sep 17 00:00:00 2001 From: Andy Whitcroft Date: Fri, 9 Nov 2007 23:32:41 +0000 Subject: [PATCH 18/71] Add tests for git push'es mirror mode Add some tests for git push --mirror mode. Signed-off-by: Andy Whitcroft Signed-off-by: Junio C Hamano --- t/t5517-push-mirror.sh | 228 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100755 t/t5517-push-mirror.sh diff --git a/t/t5517-push-mirror.sh b/t/t5517-push-mirror.sh new file mode 100755 index 0000000000..ed3fec192a --- /dev/null +++ b/t/t5517-push-mirror.sh @@ -0,0 +1,228 @@ +#!/bin/sh + +test_description='pushing to a mirror repository' + +. ./test-lib.sh + +D=`pwd` + +invert () { + if "$@"; then + return 1 + else + return 0 + fi +} + +mk_repo_pair () { + rm -rf master mirror && + mkdir mirror && + ( + cd mirror && + git init + ) && + mkdir master && + ( + cd master && + git init && + git config remote.up.url ../mirror + ) +} + + +# BRANCH tests +test_expect_success 'push mirror creates new branches' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git push --mirror up + ) && + master_master=$(cd master && git show-ref -s --verify refs/heads/master) && + mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) && + test "$master_master" = "$mirror_master" + +' + +test_expect_success 'push mirror updates existing branches' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git push --mirror up && + echo two >foo && git add foo && git commit -m two && + git push --mirror up + ) && + master_master=$(cd master && git show-ref -s --verify refs/heads/master) && + mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) && + test "$master_master" = "$mirror_master" + +' + +test_expect_success 'push mirror force updates existing branches' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git push --mirror up && + echo two >foo && git add foo && git commit -m two && + git push --mirror up && + git reset --hard HEAD^ + git push --mirror up + ) && + master_master=$(cd master && git show-ref -s --verify refs/heads/master) && + mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) && + test "$master_master" = "$mirror_master" + +' + +test_expect_success 'push mirror removes branches' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git branch remove master && + git push --mirror up && + git branch -D remove + git push --mirror up + ) && + ( + cd mirror && + invert git show-ref -s --verify refs/heads/remove + ) + +' + +test_expect_success 'push mirror adds, updates and removes branches together' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git branch remove master && + git push --mirror up && + git branch -D remove && + git branch add master && + echo two >foo && git add foo && git commit -m two && + git push --mirror up + ) && + master_master=$(cd master && git show-ref -s --verify refs/heads/master) && + master_add=$(cd master && git show-ref -s --verify refs/heads/add) && + mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) && + mirror_add=$(cd mirror && git show-ref -s --verify refs/heads/add) && + test "$master_master" = "$mirror_master" && + test "$master_add" = "$mirror_add" && + ( + cd mirror && + invert git show-ref -s --verify refs/heads/remove + ) + +' + + +# TAG tests +test_expect_success 'push mirror creates new tags' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git tag -f tmaster master && + git push --mirror up + ) && + master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) && + mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) && + test "$master_master" = "$mirror_master" + +' + +test_expect_success 'push mirror updates existing tags' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git tag -f tmaster master && + git push --mirror up && + echo two >foo && git add foo && git commit -m two && + git tag -f tmaster master && + git push --mirror up + ) && + master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) && + mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) && + test "$master_master" = "$mirror_master" + +' + +test_expect_success 'push mirror force updates existing tags' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git tag -f tmaster master && + git push --mirror up && + echo two >foo && git add foo && git commit -m two && + git tag -f tmaster master && + git push --mirror up && + git reset --hard HEAD^ + git tag -f tmaster master && + git push --mirror up + ) && + master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) && + mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) && + test "$master_master" = "$mirror_master" + +' + +test_expect_success 'push mirror removes tags' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git tag -f tremove master && + git push --mirror up && + git tag -d tremove + git push --mirror up + ) && + ( + cd mirror && + invert git show-ref -s --verify refs/tags/tremove + ) + +' + +test_expect_success 'push mirror adds, updates and removes tags together' ' + + mk_repo_pair && + ( + cd master && + echo one >foo && git add foo && git commit -m one && + git tag -f tmaster master && + git tag -f tremove master && + git push --mirror up && + git tag -d tremove && + git tag tadd master && + echo two >foo && git add foo && git commit -m two && + git tag -f tmaster master && + git push --mirror up + ) && + master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) && + master_add=$(cd master && git show-ref -s --verify refs/tags/tadd) && + mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) && + mirror_add=$(cd mirror && git show-ref -s --verify refs/tags/tadd) && + test "$master_master" = "$mirror_master" && + test "$master_add" = "$mirror_add" && + ( + cd mirror && + invert git show-ref -s --verify refs/tags/tremove + ) + +' + +test_done From ff206748158aa54196bde1462ceaf550a5c2440e Mon Sep 17 00:00:00 2001 From: Andy Whitcroft Date: Fri, 9 Nov 2007 23:32:57 +0000 Subject: [PATCH 19/71] git-push: add documentation for the newly added --mirror mode Add some basic documentation on the --mirror mode for git-push. Signed-off-by: Andy Whitcroft Signed-off-by: Junio C Hamano --- Documentation/git-push.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index e5dd4c1066..3fa5992313 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -63,6 +63,14 @@ the remote repository. Instead of naming each ref to push, specifies that all refs under `$GIT_DIR/refs/heads/` be pushed. +\--mirror:: + Instead of naming each ref to push, specifies that all + refs under `$GIT_DIR/refs/heads/` and `$GIT_DIR/refs/tags/` + be mirrored to the remote repository. Newly created local + refs will be pushed to the remote end, locally updated refs + will be force updated on the remote end, and deleted refs + will be removed from the remote end. + \--dry-run:: Do everything except actually send the updates. From 947a604b01a8e81b3d0341d38fbf891289f3c0bb Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 15 Nov 2007 08:18:07 +0100 Subject: [PATCH 20/71] Bisect reset: remove bisect refs that may have been packed. If refs were ever packed in the middle of bisection, the bisect refs were not removed from the "packed-refs" file. This patch fixes this problem by using "git update-ref -d $ref $hash" in "bisect_clean_state". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- git-bisect.sh | 8 +++++++- t/t6030-bisect-porcelain.sh | 12 ++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/git-bisect.sh b/git-bisect.sh index 1ed44e56ad..46a7b8d54a 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -351,7 +351,13 @@ bisect_reset() { bisect_clean_state() { rm -fr "$GIT_DIR/refs/bisect" - rm -f "$GIT_DIR/refs/heads/bisect" + + # There may be some refs packed during bisection. + git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* refs/heads/bisect | + while read ref hash + do + git update-ref -d $ref $hash + done rm -f "$GIT_DIR/BISECT_LOG" rm -f "$GIT_DIR/BISECT_NAMES" rm -f "$GIT_DIR/BISECT_RUN" diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 53956c08e2..f09db6244e 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -71,6 +71,18 @@ test_expect_success 'bisect start with one bad and good' ' git bisect next ' +test_expect_success 'bisect reset removes packed refs' ' + git bisect reset && + git bisect start && + git bisect good $HASH1 && + git bisect bad $HASH3 && + git pack-refs --all --prune && + git bisect next && + git bisect reset && + test -z "$(git for-each-ref "refs/bisect/*")" && + test -z "$(git for-each-ref "refs/heads/bisect")" +' + # $HASH1 is good, $HASH4 is bad, we skip $HASH3 # but $HASH2 is bad, # so we should find $HASH2 as the first bad commit From e23cb8c0cc0fb9f18e7e7106e2eec692a7b10044 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 15 Nov 2007 00:39:57 -0800 Subject: [PATCH 21/71] git-bisect: war on "sed" We do not need to pipe "echo" to "sed" only to strip refs/heads/ from the beginning. We are assuming not-so-ancient shells these days. Also there is no need to avoid assuming \012 is the LF; we do not run on EBCDIC, sorry. Other parts of the script already uses tr to convert separator to LF that way. Signed-off-by: Junio C Hamano --- git-bisect.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 46a7b8d54a..3a210335e7 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -71,7 +71,7 @@ bisect_start() { ;; refs/heads/*) [ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree" - echo "$head" | sed 's#^refs/heads/##' >"$GIT_DIR/head-name" + echo "${head#refs/heads/}" >"$GIT_DIR/head-name" ;; *) die "Bad HEAD - strange symbolic ref" @@ -275,8 +275,7 @@ exit_if_skipped_commits () { if expr "$_tried" : ".*[|].*" > /dev/null ; then echo "There are only 'skip'ped commit left to test." echo "The first bad commit could be any of:" - echo "$_tried" | sed -e 's/[|]/\ -/g' + echo "$_tried" | tr '[|]' '[\012]' echo "We cannot bisect more!" exit 2 fi From 3d7cd64cb493e3e30e4398c7b6cd66ee38cb4418 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 15 Nov 2007 00:42:04 -0800 Subject: [PATCH 22/71] git-bisect: use update-ref to mark good/bad commits This removes the last instance of making a ref by hand with "echo SHA1 >.git/refs/$refname" from the script and replaces it with update-ref. Signed-off-by: Junio C Hamano --- git-bisect.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-bisect.sh b/git-bisect.sh index 3a210335e7..4b74a7bb42 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -130,7 +130,7 @@ bisect_write() { good|skip) tag="$state"-"$rev" ;; *) die "Bad bisect_write argument: $state" ;; esac - echo "$rev" >"$GIT_DIR/refs/bisect/$tag" + git update-ref "refs/bisect/$tag" "$rev" echo "# $state: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG" test -z "$nolog" && echo "git-bisect $state $rev" >>"$GIT_DIR/BISECT_LOG" } From 0bee49c6abf18082a01f5d1a2106608456fb7d5a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 15 Nov 2007 00:47:53 -0800 Subject: [PATCH 23/71] git-bisect: modernize branch shuffling hack When switching to a new rev, we first made "new-bisect" branch to point at the chosen commit, attempt to switch to it, and then finally renamed the new-bisect branch to bisect by hand when successful. This is so that we can catch checkout failure (your local modification may interfere with switching to the chosen version) without losing information on which commit the next attempt should be made. Rewrite it using a more modern form but without breaking the safety. Signed-off-by: Junio C Hamano --- git-bisect.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 4b74a7bb42..dae8a8e980 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -316,10 +316,9 @@ bisect_next() { exit_if_skipped_commits "$bisect_rev" echo "Bisecting: $bisect_nr revisions left to test after this" - echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect" + git branch -f new-bisect "$bisect_rev" git checkout -q new-bisect || exit - mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" && - GIT_DIR="$GIT_DIR" git symbolic-ref HEAD refs/heads/bisect + git branch -M new-bisect bisect git show-branch "$bisect_rev" } From 481f0ee60eef2c34b891e5d04b7e6e5a955eedf4 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 11 Nov 2007 23:35:23 +0000 Subject: [PATCH 24/71] Fix rev-list when showing objects involving submodules The function mark_tree_uninteresting() assumed that the tree entries are blob when they are not trees. This is not so. Since we do not traverse into submodules (yet), the gitlinks should be ignored. In general, we should try to start moving away from using the "S_ISLNK()" like things for internal git state. It was a mistake to just assume the numbers all were same across all systems in the first place. This implementation converts to the "object_type", and then uses a case statement. Noticed by Ilari on IRC. Test script taken from an earlier version by Dscho. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 2 +- revision.c | 11 +++++++-- t/t6008-rev-list-submodule.sh | 42 +++++++++++++++++++++++++++++++++++ tree-walk.h | 7 ++++++ 4 files changed, 59 insertions(+), 3 deletions(-) create mode 100755 t/t6008-rev-list-submodule.sh diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 12509faa77..228040486e 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -989,7 +989,7 @@ static void add_pbase_object(struct tree_desc *tree, return; if (name[cmplen] != '/') { add_object_entry(entry.sha1, - S_ISDIR(entry.mode) ? OBJ_TREE : OBJ_BLOB, + object_type(entry.mode), fullname, 1); return; } diff --git a/revision.c b/revision.c index 48756b5d44..0ba0729b08 100644 --- a/revision.c +++ b/revision.c @@ -65,10 +65,17 @@ void mark_tree_uninteresting(struct tree *tree) init_tree_desc(&desc, tree->buffer, tree->size); while (tree_entry(&desc, &entry)) { - if (S_ISDIR(entry.mode)) + switch (object_type(entry.mode)) { + case OBJ_TREE: mark_tree_uninteresting(lookup_tree(entry.sha1)); - else + break; + case OBJ_BLOB: mark_blob_uninteresting(lookup_blob(entry.sha1)); + break; + default: + /* Subproject commit - not in this repository */ + break; + } } /* diff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh new file mode 100755 index 0000000000..88e96fb91b --- /dev/null +++ b/t/t6008-rev-list-submodule.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# +# Copyright (c) 2007 Johannes E. Schindelin +# + +test_description='git rev-list involving submodules that this repo has' + +. ./test-lib.sh + +test_expect_success 'setup' ' + : > file && + git add file && + test_tick && + git commit -m initial && + echo 1 > file && + test_tick && + git commit -m second file && + echo 2 > file && + test_tick && + git commit -m third file && + + rm .git/index && + + : > super-file && + git add super-file && + git submodule add . sub && + git symbolic-ref HEAD refs/heads/super && + test_tick && + git commit -m super-initial && + echo 1 > super-file && + test_tick && + git commit -m super-first super-file && + echo 2 > super-file && + test_tick && + git commit -m super-second super-file +' + +test_expect_success "Ilari's test" ' + git rev-list --objects super master ^super^ +' + +test_done diff --git a/tree-walk.h b/tree-walk.h index db0fbdc701..903a7b0f48 100644 --- a/tree-walk.h +++ b/tree-walk.h @@ -7,6 +7,13 @@ struct name_entry { unsigned int mode; }; +static inline enum object_type object_type(unsigned int mode) +{ + return S_ISDIR(mode) ? OBJ_TREE : + S_ISGITLINK(mode) ? OBJ_COMMIT : + OBJ_BLOB; +} + struct tree_desc { const void *buffer; struct name_entry entry; From e3f062bfd412adafb7ed6a8f24a3ec89d39211fc Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 17 Nov 2007 14:35:25 +0100 Subject: [PATCH 25/71] Bisect visualize: use "for-each-ref" to list all good refs. In bisect_visualize, "cd $GIT_DIR/refs && echo bisect/good-*" was still used instead of "git for-each-ref". This patch fix it. We now pass "refs/bisect/bad" and "--not refs/bisect/good-" instead of "bisect/bad" and "--not bisect/good-" to gitk, but it seems to work. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- git-bisect.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index dae8a8e980..2b20037a11 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -324,8 +324,8 @@ bisect_next() { bisect_visualize() { bisect_next_check fail - not=`cd "$GIT_DIR/refs" && echo bisect/good-*` - eval gitk bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES") + not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*") + eval gitk refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES") } bisect_reset() { From 8736a8489080509516f5f4cc1cc74de33150f397 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 17 Nov 2007 07:54:27 -0500 Subject: [PATCH 26/71] send-pack: track errors for each ref Instead of keeping the 'ret' variable, we instead have a status flag for each ref that tracks what happened to it. We then print the ref status after all of the refs have been examined. This paves the way for three improvements: - updating tracking refs only for non-error refs - incorporating remote rejection into the printed status - printing errors in a different order than we processed (e.g., consolidating non-ff errors near the end with a special message) Signed-off-by: Jeff King Acked-by: Alex Riesen Acked-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- builtin-send-pack.c | 224 +++++++++++++++++++++-------------- cache.h | 13 +- t/t5404-tracking-branches.sh | 14 ++- 3 files changed, 156 insertions(+), 95 deletions(-) diff --git a/builtin-send-pack.c b/builtin-send-pack.c index 418925eb01..dafc02bcb0 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -207,8 +207,9 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref) } } -static const char *prettify_ref(const char *name) +static const char *prettify_ref(const struct ref *ref) { + const char *name = ref->name; return name + ( !prefixcmp(name, "refs/heads/") ? 11 : !prefixcmp(name, "refs/tags/") ? 10 : @@ -218,15 +219,104 @@ static const char *prettify_ref(const char *name) #define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3) +static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg) +{ + fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary); + if (from) + fprintf(stderr, "%s -> %s", prettify_ref(from), prettify_ref(to)); + else + fputs(prettify_ref(to), stderr); + if (msg) { + fputs(" (", stderr); + fputs(msg, stderr); + fputc(')', stderr); + } + fputc('\n', stderr); +} + +static const char *status_abbrev(unsigned char sha1[20]) +{ + const char *abbrev; + abbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV); + return abbrev ? abbrev : sha1_to_hex(sha1); +} + +static void print_ok_ref_status(struct ref *ref) +{ + if (ref->deletion) + print_ref_status('-', "[deleted]", ref, NULL, NULL); + else if (is_null_sha1(ref->old_sha1)) + print_ref_status('*', + (!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" : + "[new branch]"), + ref, ref->peer_ref, NULL); + else { + char quickref[84]; + char type; + const char *msg; + + strcpy(quickref, status_abbrev(ref->old_sha1)); + if (ref->nonfastforward) { + strcat(quickref, "..."); + type = '+'; + msg = "forced update"; + } else { + strcat(quickref, ".."); + type = ' '; + msg = NULL; + } + strcat(quickref, status_abbrev(ref->new_sha1)); + + print_ref_status(type, quickref, ref, ref->peer_ref, msg); + } +} + +static void print_push_status(const char *dest, struct ref *refs) +{ + struct ref *ref; + int shown_dest = 0; + + for (ref = refs; ref; ref = ref->next) { + if (!ref->status) + continue; + if (ref->status == REF_STATUS_UPTODATE && !args.verbose) + continue; + + if (!shown_dest) { + fprintf(stderr, "To %s\n", dest); + shown_dest = 1; + } + + switch(ref->status) { + case REF_STATUS_NONE: + print_ref_status('X', "[no match]", ref, NULL, NULL); + break; + case REF_STATUS_REJECT_NODELETE: + print_ref_status('!', "[rejected]", ref, NULL, + "remote does not support deleting refs"); + break; + case REF_STATUS_UPTODATE: + print_ref_status('=', "[up to date]", ref, + ref->peer_ref, NULL); + break; + case REF_STATUS_REJECT_NONFASTFORWARD: + print_ref_status('!', "[rejected]", ref, ref->peer_ref, + "non-fast forward"); + break; + case REF_STATUS_OK: + print_ok_ref_status(ref); + break; + } + } +} + static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec) { struct ref *ref; int new_refs; - int ret = 0; int ask_for_status_report = 0; int allow_deleting_refs = 0; int expect_status_report = 0; - int shown_dest = 0; int flags = MATCH_REFS_NONE; if (args.send_all) @@ -262,10 +352,6 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest */ new_refs = 0; for (ref = remote_refs; ref; ref = ref->next) { - char old_hex[60], *new_hex; - int will_delete_ref; - const char *pretty_ref; - const char *pretty_peer = NULL; /* only used when not deleting */ const unsigned char *new_sha1; if (!ref->peer_ref) { @@ -276,29 +362,15 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest else new_sha1 = ref->peer_ref->new_sha1; - if (!shown_dest) { - fprintf(stderr, "To %s\n", dest); - shown_dest = 1; - } - will_delete_ref = is_null_sha1(new_sha1); - - pretty_ref = prettify_ref(ref->name); - if (!will_delete_ref) - pretty_peer = prettify_ref(ref->peer_ref->name); - - if (will_delete_ref && !allow_deleting_refs) { - fprintf(stderr, " ! %-*s %s (remote does not support deleting refs)\n", - SUMMARY_WIDTH, "[rejected]", pretty_ref); - ret = -2; + ref->deletion = is_null_sha1(new_sha1); + if (ref->deletion && !allow_deleting_refs) { + ref->status = REF_STATUS_REJECT_NODELETE; continue; } - if (!will_delete_ref && + if (!ref->deletion && !hashcmp(ref->old_sha1, new_sha1)) { - if (args.verbose) - fprintf(stderr, " = %-*s %s -> %s\n", - SUMMARY_WIDTH, "[up to date]", - pretty_peer, pretty_ref); + ref->status = REF_STATUS_UPTODATE; continue; } @@ -321,33 +393,26 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest * always allowed. */ - if (!args.force_update && - !will_delete_ref && + ref->nonfastforward = + !ref->deletion && !is_null_sha1(ref->old_sha1) && - !ref->force) { - if (!has_sha1_file(ref->old_sha1) || - !ref_newer(new_sha1, ref->old_sha1)) { - /* We do not have the remote ref, or - * we know that the remote ref is not - * an ancestor of what we are trying to - * push. Either way this can be losing - * commits at the remote end and likely - * we were not up to date to begin with. - */ - fprintf(stderr, " ! %-*s %s -> %s (non-fast forward)\n", - SUMMARY_WIDTH, "[rejected]", - pretty_peer, pretty_ref); - ret = -2; - continue; - } + (!has_sha1_file(ref->old_sha1) + || !ref_newer(new_sha1, ref->old_sha1)); + + if (ref->nonfastforward && !ref->force && !args.force_update) { + ref->status = REF_STATUS_REJECT_NONFASTFORWARD; + continue; } + hashcpy(ref->new_sha1, new_sha1); - if (!will_delete_ref) + if (!ref->deletion) new_refs++; - strcpy(old_hex, sha1_to_hex(ref->old_sha1)); - new_hex = sha1_to_hex(ref->new_sha1); + ref->status = REF_STATUS_OK; if (!args.dry_run) { + char *old_hex = sha1_to_hex(ref->old_sha1); + char *new_hex = sha1_to_hex(ref->new_sha1); + if (ask_for_status_report) { packet_write(out, "%s %s %s%c%s", old_hex, new_hex, ref->name, 0, @@ -359,64 +424,43 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest packet_write(out, "%s %s %s", old_hex, new_hex, ref->name); } - if (will_delete_ref) - fprintf(stderr, " - %-*s %s\n", - SUMMARY_WIDTH, "[deleting]", - pretty_ref); - else if (is_null_sha1(ref->old_sha1)) { - const char *msg; - - if (!prefixcmp(ref->name, "refs/tags/")) - msg = "[new tag]"; - else - msg = "[new branch]"; - fprintf(stderr, " * %-*s %s -> %s\n", - SUMMARY_WIDTH, msg, - pretty_peer, pretty_ref); - } - else { - char quickref[83]; - char type = ' '; - const char *msg = ""; - const char *old_abb; - old_abb = find_unique_abbrev(ref->old_sha1, DEFAULT_ABBREV); - strcpy(quickref, old_abb ? old_abb : old_hex); - if (ref_newer(ref->peer_ref->new_sha1, ref->old_sha1)) - strcat(quickref, ".."); - else { - strcat(quickref, "..."); - type = '+'; - msg = " (forced update)"; - } - strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); - - fprintf(stderr, " %c %-*s %s -> %s%s\n", - type, - SUMMARY_WIDTH, quickref, - pretty_peer, pretty_ref, - msg); - } } packet_flush(out); - if (new_refs && !args.dry_run) - ret = pack_objects(out, remote_refs); + if (new_refs && !args.dry_run) { + if (pack_objects(out, remote_refs) < 0) { + close(out); + return -1; + } + } close(out); + print_push_status(dest, remote_refs); + if (expect_status_report) { if (receive_status(in)) - ret = -4; + return -1; } - if (!args.dry_run && remote && ret == 0) { + if (!args.dry_run && remote) { for (ref = remote_refs; ref; ref = ref->next) if (!is_null_sha1(ref->new_sha1)) update_tracking_ref(remote, ref); } - if (!new_refs && ret == 0) + if (!new_refs) fprintf(stderr, "Everything up-to-date\n"); - return ret; + for (ref = remote_refs; ref; ref = ref->next) { + switch (ref->status) { + case REF_STATUS_NONE: + case REF_STATUS_UPTODATE: + case REF_STATUS_OK: + break; + default: + return -1; + } + } + return 0; } static void verify_remote_names(int nr_heads, const char **heads) diff --git a/cache.h b/cache.h index e5ea637c29..6663ec52d1 100644 --- a/cache.h +++ b/cache.h @@ -493,8 +493,17 @@ struct ref { struct ref *next; unsigned char old_sha1[20]; unsigned char new_sha1[20]; - unsigned char force; - unsigned char merge; + unsigned char force : 1; + unsigned char merge : 1; + unsigned char nonfastforward : 1; + unsigned char deletion : 1; + enum { + REF_STATUS_NONE = 0, + REF_STATUS_OK, + REF_STATUS_REJECT_NONFASTFORWARD, + REF_STATUS_REJECT_NODELETE, + REF_STATUS_UPTODATE, + } status; struct ref *peer_ref; /* when renaming */ char name[FLEX_ARRAY]; /* more */ }; diff --git a/t/t5404-tracking-branches.sh b/t/t5404-tracking-branches.sh index 20718d4679..799e47e5ba 100755 --- a/t/t5404-tracking-branches.sh +++ b/t/t5404-tracking-branches.sh @@ -19,7 +19,7 @@ test_expect_success 'setup' ' git commit -a -m b2 ' -test_expect_success 'check tracking branches updated correctly after push' ' +test_expect_success 'prepare pushable branches' ' cd aa && b1=$(git rev-parse origin/b1) && b2=$(git rev-parse origin/b2) && @@ -31,8 +31,16 @@ test_expect_success 'check tracking branches updated correctly after push' ' git commit -a -m aa-b2 && git checkout master && echo aa-master >>file && - git commit -a -m aa-master && - git push && + git commit -a -m aa-master +' + +test_expect_success 'mixed-success push returns error' '! git push' + +test_expect_success 'check tracking branches updated correctly after push' ' + test "$(git rev-parse origin/master)" = "$(git rev-parse master)" +' + +test_expect_success 'check tracking branches not updated for failed refs' ' test "$(git rev-parse origin/b1)" = "$b1" && test "$(git rev-parse origin/b2)" = "$b2" ' From 1f0e2a1a65477c2b8eb8812e5bf0ad07bf03738e Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 17 Nov 2007 07:55:15 -0500 Subject: [PATCH 27/71] send-pack: check ref->status before updating tracking refs Previously, we manually checked the 'NONE' and 'UPTODATE' conditions. Now that we have ref->status, we can easily say "only update if we pushed successfully". This adds a test for and fixes a regression introduced in ed31df31 where deleted refs did not have their tracking branches removed. This was due to a bogus per-ref error test that is superseded by the more accurate ref->status flag. Signed-off-by: Jeff King Completely-Acked-By: Alex "Sleepy" Riesen Acked-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- builtin-send-pack.c | 18 +++++------------- t/t5404-tracking-branches.sh | 5 +++++ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/builtin-send-pack.c b/builtin-send-pack.c index dafc02bcb0..3f86acb315 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -180,24 +180,17 @@ static int receive_status(int in) static void update_tracking_ref(struct remote *remote, struct ref *ref) { struct refspec rs; - int will_delete_ref; + + if (ref->status != REF_STATUS_OK) + return; rs.src = ref->name; rs.dst = NULL; - if (!ref->peer_ref) - return; - - will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1); - - if (!will_delete_ref && - !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) - return; - if (!remote_find_tracking(remote, &rs)) { if (args.verbose) fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst); - if (is_null_sha1(ref->peer_ref->new_sha1)) { + if (ref->deletion) { if (delete_ref(rs.dst, NULL)) error("Failed to delete"); } else @@ -444,8 +437,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest if (!args.dry_run && remote) { for (ref = remote_refs; ref; ref = ref->next) - if (!is_null_sha1(ref->new_sha1)) - update_tracking_ref(remote, ref); + update_tracking_ref(remote, ref); } if (!new_refs) diff --git a/t/t5404-tracking-branches.sh b/t/t5404-tracking-branches.sh index 799e47e5ba..1493a92c06 100755 --- a/t/t5404-tracking-branches.sh +++ b/t/t5404-tracking-branches.sh @@ -45,4 +45,9 @@ test_expect_success 'check tracking branches not updated for failed refs' ' test "$(git rev-parse origin/b2)" = "$b2" ' +test_expect_success 'deleted branches have their tracking branches removed' ' + git push origin :b1 && + test "$(git rev-parse origin/b1)" = "origin/b1" +' + test_done From ca74c458a3908314cf29b96f1c43fe2b2597de76 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 17 Nov 2007 07:56:03 -0500 Subject: [PATCH 28/71] send-pack: assign remote errors to each ref This lets us show remote errors (e.g., a denied hook) along with the usual push output. There is a slightly clever optimization in receive_status that bears explanation. We need to correlate the returned status and our ref objects, which naively could be an O(m*n) operation. However, since the current implementation of receive-pack returns the errors to us in the same order that we sent them, we optimistically look for the next ref to be looked up to come after the last one we have found. So it should be an O(m+n) merge if the receive-pack behavior holds, but we fall back to a correct but slower behavior if it should change. Signed-off-by: Jeff King Acked-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- builtin-send-pack.c | 51 ++++++++++++++++++++++++++++++++++----- cache.h | 2 ++ t/t5406-remote-rejects.sh | 24 ++++++++++++++++++ 3 files changed, 71 insertions(+), 6 deletions(-) create mode 100755 t/t5406-remote-rejects.sh diff --git a/builtin-send-pack.c b/builtin-send-pack.c index 3f86acb315..5fadd0bc95 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -146,19 +146,43 @@ static void get_local_heads(void) for_each_ref(one_local_ref, NULL); } -static int receive_status(int in) +static struct ref *set_ref_error(struct ref *refs, const char *line) { + struct ref *ref; + + for (ref = refs; ref; ref = ref->next) { + const char *msg; + if (prefixcmp(line, ref->name)) + continue; + msg = line + strlen(ref->name); + if (*msg++ != ' ') + continue; + ref->status = REF_STATUS_REMOTE_REJECT; + ref->error = xstrdup(msg); + ref->error[strlen(ref->error)-1] = '\0'; + return ref; + } + return NULL; +} + +/* a return value of -1 indicates that an error occurred, + * but we were able to set individual ref errors. A return + * value of -2 means we couldn't even get that far. */ +static int receive_status(int in, struct ref *refs) +{ + struct ref *hint; char line[1000]; int ret = 0; int len = packet_read_line(in, line, sizeof(line)); if (len < 10 || memcmp(line, "unpack ", 7)) { fprintf(stderr, "did not receive status back\n"); - return -1; + return -2; } if (memcmp(line, "unpack ok\n", 10)) { fputs(line, stderr); ret = -1; } + hint = NULL; while (1) { len = packet_read_line(in, line, sizeof(line)); if (!len) @@ -171,7 +195,10 @@ static int receive_status(int in) } if (!memcmp(line, "ok", 2)) continue; - fputs(line, stderr); + if (hint) + hint = set_ref_error(hint, line + 3); + if (!hint) + hint = set_ref_error(refs, line + 3); ret = -1; } return ret; @@ -296,6 +323,12 @@ static void print_push_status(const char *dest, struct ref *refs) print_ref_status('!', "[rejected]", ref, ref->peer_ref, "non-fast forward"); break; + case REF_STATUS_REMOTE_REJECT: + if (ref->deletion) + print_ref_status('!', "[remote rejected]", ref, NULL, ref->error); + else + print_ref_status('!', "[remote rejected]", ref, ref->peer_ref, ref->error); + break; case REF_STATUS_OK: print_ok_ref_status(ref); break; @@ -311,6 +344,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest int allow_deleting_refs = 0; int expect_status_report = 0; int flags = MATCH_REFS_NONE; + int ret; if (args.send_all) flags |= MATCH_REFS_ALL; @@ -428,12 +462,15 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest } close(out); - print_push_status(dest, remote_refs); - if (expect_status_report) { - if (receive_status(in)) + ret = receive_status(in, remote_refs); + if (ret == -2) return -1; } + else + ret = 0; + + print_push_status(dest, remote_refs); if (!args.dry_run && remote) { for (ref = remote_refs; ref; ref = ref->next) @@ -442,6 +479,8 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest if (!new_refs) fprintf(stderr, "Everything up-to-date\n"); + if (ret < 0) + return ret; for (ref = remote_refs; ref; ref = ref->next) { switch (ref->status) { case REF_STATUS_NONE: diff --git a/cache.h b/cache.h index 6663ec52d1..81e8c88e46 100644 --- a/cache.h +++ b/cache.h @@ -503,7 +503,9 @@ struct ref { REF_STATUS_REJECT_NONFASTFORWARD, REF_STATUS_REJECT_NODELETE, REF_STATUS_UPTODATE, + REF_STATUS_REMOTE_REJECT, } status; + char *error; struct ref *peer_ref; /* when renaming */ char name[FLEX_ARRAY]; /* more */ }; diff --git a/t/t5406-remote-rejects.sh b/t/t5406-remote-rejects.sh new file mode 100755 index 0000000000..46b2cb4e46 --- /dev/null +++ b/t/t5406-remote-rejects.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +test_description='remote push rejects are reported by client' + +. ./test-lib.sh + +test_expect_success 'setup' ' + mkdir .git/hooks && + (echo "#!/bin/sh" ; echo "exit 1") >.git/hooks/update && + chmod +x .git/hooks/update && + echo 1 >file && + git add file && + git commit -m 1 && + git clone . child && + cd child && + echo 2 >file && + git commit -a -m 2 +' + +test_expect_success 'push reports error' '! git push 2>stderr' + +test_expect_success 'individual ref reports error' 'grep rejected stderr' + +test_done From 9f8a15c73437abc634f2c43501105b108c51eae8 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 18 Nov 2007 04:31:37 -0500 Subject: [PATCH 29/71] Fix warning about bitfield in struct ref cache.h:503: warning: type of bit-field 'force' is a GCC extension cache.h:504: warning: type of bit-field 'merge' is a GCC extension cache.h:505: warning: type of bit-field 'nonfastforward' is a GCC extension cache.h:506: warning: type of bit-field 'deletion' is a GCC extension So we change it to an 'unsigned int' which is not a GCC extension. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- cache.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cache.h b/cache.h index 81e8c88e46..6ccb764229 100644 --- a/cache.h +++ b/cache.h @@ -493,10 +493,10 @@ struct ref { struct ref *next; unsigned char old_sha1[20]; unsigned char new_sha1[20]; - unsigned char force : 1; - unsigned char merge : 1; - unsigned char nonfastforward : 1; - unsigned char deletion : 1; + unsigned int force:1, + merge:1, + nonfastforward:1, + deletion:1; enum { REF_STATUS_NONE = 0, REF_STATUS_OK, From cda69f481db510e2a3f0ca8a0f4b54123c799416 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 18 Nov 2007 02:13:10 -0500 Subject: [PATCH 30/71] make "find_ref_by_name" a public function This was a static in remote.c, but is generally useful. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- cache.h | 2 ++ refs.c | 8 ++++++++ remote.c | 8 -------- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/cache.h b/cache.h index 6ccb764229..8d601dd6f7 100644 --- a/cache.h +++ b/cache.h @@ -514,6 +514,8 @@ struct ref { #define REF_HEADS (1u << 1) #define REF_TAGS (1u << 2) +extern struct ref *find_ref_by_name(struct ref *list, const char *name); + #define CONNECT_VERBOSE (1u << 0) extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags); extern int finish_connect(struct child_process *conn); diff --git a/refs.c b/refs.c index aff02cd09d..387c588c74 100644 --- a/refs.c +++ b/refs.c @@ -1445,3 +1445,11 @@ int update_ref(const char *action, const char *refname, } return 0; } + +struct ref *find_ref_by_name(struct ref *list, const char *name) +{ + for ( ; list; list = list->next) + if (!strcmp(list->name, name)) + return list; + return NULL; +} diff --git a/remote.c b/remote.c index 09b7aad525..bb01059083 100644 --- a/remote.c +++ b/remote.c @@ -696,14 +696,6 @@ static int match_explicit_refs(struct ref *src, struct ref *dst, return -errs; } -static struct ref *find_ref_by_name(struct ref *list, const char *name) -{ - for ( ; list; list = list->next) - if (!strcmp(list->name, name)) - return list; - return NULL; -} - static const struct refspec *check_pattern_match(const struct refspec *rs, int rs_nr, const struct ref *src) From 2a0fe89a976331cb2163d4f299e38e2cb5010632 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 18 Nov 2007 02:16:52 -0500 Subject: [PATCH 31/71] send-pack: tighten remote error reporting Previously, we set all ref pushes to 'OK', and then marked them as errors if the remote reported so. This has the problem that if the remote dies or fails to report a ref, we just assume it was OK. Instead, we use a new non-OK state to indicate that we are expecting status (if the remote doesn't support the report-status feature, we fall back on the old behavior). Thus we can flag refs for which we expected a status, but got none (conversely, we now also print a warning for refs for which we get a status, but weren't expecting one). This also allows us to simplify the receive_status exit code, since each ref is individually marked with failure until we get a success response. We can just print the usual status table, so the user still gets a sense of what we were trying to do when the failure happened. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-send-pack.c | 94 +++++++++++++++++++++++++-------------------- cache.h | 3 +- 2 files changed, 54 insertions(+), 43 deletions(-) diff --git a/builtin-send-pack.c b/builtin-send-pack.c index 5fadd0bc95..14447bb7cd 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -146,60 +146,67 @@ static void get_local_heads(void) for_each_ref(one_local_ref, NULL); } -static struct ref *set_ref_error(struct ref *refs, const char *line) -{ - struct ref *ref; - - for (ref = refs; ref; ref = ref->next) { - const char *msg; - if (prefixcmp(line, ref->name)) - continue; - msg = line + strlen(ref->name); - if (*msg++ != ' ') - continue; - ref->status = REF_STATUS_REMOTE_REJECT; - ref->error = xstrdup(msg); - ref->error[strlen(ref->error)-1] = '\0'; - return ref; - } - return NULL; -} - -/* a return value of -1 indicates that an error occurred, - * but we were able to set individual ref errors. A return - * value of -2 means we couldn't even get that far. */ static int receive_status(int in, struct ref *refs) { struct ref *hint; char line[1000]; int ret = 0; int len = packet_read_line(in, line, sizeof(line)); - if (len < 10 || memcmp(line, "unpack ", 7)) { - fprintf(stderr, "did not receive status back\n"); - return -2; - } + if (len < 10 || memcmp(line, "unpack ", 7)) + return error("did not receive remote status"); if (memcmp(line, "unpack ok\n", 10)) { - fputs(line, stderr); + char *p = line + strlen(line) - 1; + if (*p == '\n') + *p = '\0'; + error("unpack failed: %s", line + 7); ret = -1; } hint = NULL; while (1) { + char *refname; + char *msg; len = packet_read_line(in, line, sizeof(line)); if (!len) break; if (len < 3 || - (memcmp(line, "ok", 2) && memcmp(line, "ng", 2))) { + (memcmp(line, "ok ", 3) && memcmp(line, "ng ", 3))) { fprintf(stderr, "protocol error: %s\n", line); ret = -1; break; } - if (!memcmp(line, "ok", 2)) - continue; + + line[strlen(line)-1] = '\0'; + refname = line + 3; + msg = strchr(refname, ' '); + if (msg) + *msg++ = '\0'; + + /* first try searching at our hint, falling back to all refs */ if (hint) - hint = set_ref_error(hint, line + 3); + hint = find_ref_by_name(hint, refname); if (!hint) - hint = set_ref_error(refs, line + 3); - ret = -1; + hint = find_ref_by_name(refs, refname); + if (!hint) { + warning("remote reported status on unknown ref: %s", + refname); + continue; + } + if (hint->status != REF_STATUS_EXPECTING_REPORT) { + warning("remote reported status on unexpected ref: %s", + refname); + continue; + } + + if (line[0] == 'o' && line[1] == 'k') + hint->status = REF_STATUS_OK; + else { + hint->status = REF_STATUS_REMOTE_REJECT; + ret = -1; + } + if (msg) + hint->remote_status = xstrdup(msg); + /* start our next search from the next ref */ + hint = hint->next; } return ret; } @@ -324,10 +331,14 @@ static void print_push_status(const char *dest, struct ref *refs) "non-fast forward"); break; case REF_STATUS_REMOTE_REJECT: - if (ref->deletion) - print_ref_status('!', "[remote rejected]", ref, NULL, ref->error); - else - print_ref_status('!', "[remote rejected]", ref, ref->peer_ref, ref->error); + print_ref_status('!', "[remote rejected]", ref, + ref->deletion ? NULL : ref->peer_ref, + ref->remote_status); + break; + case REF_STATUS_EXPECTING_REPORT: + print_ref_status('!', "[remote failure]", ref, + ref->deletion ? NULL : ref->peer_ref, + "remote failed to report status"); break; case REF_STATUS_OK: print_ok_ref_status(ref); @@ -434,7 +445,6 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest hashcpy(ref->new_sha1, new_sha1); if (!ref->deletion) new_refs++; - ref->status = REF_STATUS_OK; if (!args.dry_run) { char *old_hex = sha1_to_hex(ref->old_sha1); @@ -451,6 +461,9 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest packet_write(out, "%s %s %s", old_hex, new_hex, ref->name); } + ref->status = expect_status_report ? + REF_STATUS_EXPECTING_REPORT : + REF_STATUS_OK; } packet_flush(out); @@ -462,11 +475,8 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest } close(out); - if (expect_status_report) { + if (expect_status_report) ret = receive_status(in, remote_refs); - if (ret == -2) - return -1; - } else ret = 0; diff --git a/cache.h b/cache.h index 8d601dd6f7..e0c1cc3fe8 100644 --- a/cache.h +++ b/cache.h @@ -504,8 +504,9 @@ struct ref { REF_STATUS_REJECT_NODELETE, REF_STATUS_UPTODATE, REF_STATUS_REMOTE_REJECT, + REF_STATUS_EXPECTING_REPORT, } status; - char *error; + char *remote_status; struct ref *peer_ref; /* when renaming */ char name[FLEX_ARRAY]; /* more */ }; From 73fa0b44ac1ed3b0b34ea6bcdca7d9b76886b903 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 18 Nov 2007 03:08:22 -0500 Subject: [PATCH 32/71] send-pack: fix "everything up-to-date" message This has always been slightly inaccurate, since it used the new_refs counter, which really meant "did we send any objects," so deletions were not counted. It has gotten even worse with recent patches, since we no longer look at the 'ret' value, meaning we would say "up to date" if non-ff pushes were rejected. Instead, we now claim up to date iff every ref is either unmatched or up to date. Any other case should already have generated a status line. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-send-pack.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/builtin-send-pack.c b/builtin-send-pack.c index 14447bb7cd..3aab89ca1b 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -347,6 +347,20 @@ static void print_push_status(const char *dest, struct ref *refs) } } +static int refs_pushed(struct ref *ref) +{ + for (; ref; ref = ref->next) { + switch(ref->status) { + case REF_STATUS_NONE: + case REF_STATUS_UPTODATE: + break; + default: + return 1; + } + } + return 0; +} + static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec) { struct ref *ref; @@ -487,7 +501,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest update_tracking_ref(remote, ref); } - if (!new_refs) + if (!refs_pushed(remote_refs)) fprintf(stderr, "Everything up-to-date\n"); if (ret < 0) return ret; From 6459c7c6786aa9bda0c7a095c9db66c36da0e5f0 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 18 Nov 2007 16:34:03 +0100 Subject: [PATCH 33/71] Bisect: use "$GIT_DIR/BISECT_NAMES" to check if we are bisecting. Previously we tested if the "$GIT_DIR/refs/bisect" directory existed, to check if we were bisecting. Now with packed refs, it is simpler to check if the file "$GIT_DIR/BISECT_NAMES" exists, as it is already created when starting bisection and removed when reseting bisection. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- git-bisect.sh | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 2b20037a11..414f813be7 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -36,7 +36,7 @@ sq() { } bisect_autostart() { - test -d "$GIT_DIR/refs/bisect" || { + test -f "$GIT_DIR/BISECT_NAMES" || { echo >&2 'You need to start by "git bisect start"' if test -t 0 then @@ -82,7 +82,6 @@ bisect_start() { # Get rid of any old bisect state # bisect_clean_state - mkdir "$GIT_DIR/refs/bisect" # # Check for one bad and then some good revisions. @@ -191,7 +190,7 @@ bisect_next_check() { ;; *) THEN='' - test -d "$GIT_DIR/refs/bisect" || { + test -f "$GIT_DIR/BISECT_NAMES" || { echo >&2 'You need to start by "git bisect start".' THEN='then ' } @@ -348,8 +347,6 @@ bisect_reset() { } bisect_clean_state() { - rm -fr "$GIT_DIR/refs/bisect" - # There may be some refs packed during bisection. git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* refs/heads/bisect | while read ref hash From 113f10f22f4b3b599e44e192e241e0bace9cc39e Mon Sep 17 00:00:00 2001 From: Shawn Bohrer Date: Sun, 11 Nov 2007 19:48:47 -0600 Subject: [PATCH 34/71] Make git-clean a builtin This replaces git-clean.sh with builtin-clean.c, and moves git-clean.sh to the examples. This also introduces a change in behavior when removing directories explicitly specified as a path. For example currently: 1. When dir has only untracked files, these two behave differently: $ git clean -n dir $ git clean -n dir/ the former says "Would not remove dir/", while the latter would say "Would remove dir/untracked" for all paths under it, but not the directory itself. With -d, the former would stop refusing, however since the user explicitly asked to remove the directory the -d is no longer required. 2. When there are more parameters: $ git clean -n dir foo $ git clean -n dir/ foo both cases refuse to remove dir/ unless -d is specified. Once again since both cases requested to remove dir the -d is no longer required. Thanks to Johannes Schindelin for the conversion to using the parse-options API. Signed-off-by: Shawn Bohrer Signed-off-by: Junio C Hamano --- Makefile | 3 +- builtin-clean.c | 154 ++++++++++++++++++ builtin.h | 1 + git-clean.sh => contrib/examples/git-clean.sh | 0 git.c | 1 + 5 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 builtin-clean.c rename git-clean.sh => contrib/examples/git-clean.sh (100%) diff --git a/Makefile b/Makefile index cabde8177e..87da99f432 100644 --- a/Makefile +++ b/Makefile @@ -213,7 +213,7 @@ BASIC_LDFLAGS = SCRIPT_SH = \ git-bisect.sh git-checkout.sh \ - git-clean.sh git-clone.sh git-commit.sh \ + git-clone.sh git-commit.sh \ git-ls-remote.sh \ git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \ git-pull.sh git-rebase.sh git-rebase--interactive.sh \ @@ -330,6 +330,7 @@ BUILTIN_OBJS = \ builtin-check-attr.o \ builtin-checkout-index.o \ builtin-check-ref-format.o \ + builtin-clean.o \ builtin-commit-tree.o \ builtin-count-objects.o \ builtin-describe.o \ diff --git a/builtin-clean.c b/builtin-clean.c new file mode 100644 index 0000000000..55658e742f --- /dev/null +++ b/builtin-clean.c @@ -0,0 +1,154 @@ +/* + * "git clean" builtin command + * + * Copyright (C) 2007 Shawn Bohrer + * + * Based on git-clean.sh by Pavel Roskin + */ + +#include "builtin.h" +#include "cache.h" +#include "dir.h" +#include "parse-options.h" + +static int force; + +static const char *const builtin_clean_usage[] = { + "git-clean [-d] [-f] [-n] [-q] [-x | -X] [--] ...", + NULL +}; + +static int git_clean_config(const char *var, const char *value) +{ + if (!strcmp(var, "clean.requireforce")) + force = !git_config_bool(var, value); + return 0; +} + +int cmd_clean(int argc, const char **argv, const char *prefix) +{ + int j; + int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0; + int ignored_only = 0, baselen = 0; + struct strbuf directory; + struct dir_struct dir; + const char *path, *base; + static const char **pathspec; + struct option options[] = { + OPT__QUIET(&quiet), + OPT__DRY_RUN(&show_only), + OPT_BOOLEAN('f', NULL, &force, "force"), + OPT_BOOLEAN('d', NULL, &remove_directories, + "remove whole directories"), + OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"), + OPT_BOOLEAN('X', NULL, &ignored_only, + "remove only ignored files"), + OPT_END() + }; + + git_config(git_clean_config); + argc = parse_options(argc, argv, options, builtin_clean_usage, 0); + + memset(&dir, 0, sizeof(dir)); + if (ignored_only) { + dir.show_ignored =1; + dir.exclude_per_dir = ".gitignore"; + } + + if (ignored && ignored_only) + die("-x and -X cannot be used together"); + + if (!show_only && !force) + die("clean.requireForce set and -n or -f not given; refusing to clean"); + + dir.show_other_directories = 1; + + if (!ignored) { + dir.exclude_per_dir = ".gitignore"; + if (!access(git_path("info/exclude"), F_OK)) { + char *exclude_path = git_path("info/exclude"); + add_excludes_from_file(&dir, exclude_path); + } + } + + pathspec = get_pathspec(prefix, argv); + read_cache(); + + /* + * Calculate common prefix for the pathspec, and + * use that to optimize the directory walk + */ + baselen = common_prefix(pathspec); + path = "."; + base = ""; + if (baselen) + path = base = xmemdupz(*pathspec, baselen); + read_directory(&dir, path, base, baselen, pathspec); + strbuf_init(&directory, 0); + + for (j = 0; j < dir.nr; ++j) { + struct dir_entry *ent = dir.entries[j]; + int len, pos, specs; + struct cache_entry *ce; + struct stat st; + char *seen; + + /* + * Remove the '/' at the end that directory + * walking adds for directory entries. + */ + len = ent->len; + if (len && ent->name[len-1] == '/') + len--; + pos = cache_name_pos(ent->name, len); + if (0 <= pos) + continue; /* exact match */ + pos = -pos - 1; + if (pos < active_nr) { + ce = active_cache[pos]; + if (ce_namelen(ce) == len && + !memcmp(ce->name, ent->name, len)) + continue; /* Yup, this one exists unmerged */ + } + + if (!lstat(ent->name, &st) && (S_ISDIR(st.st_mode))) { + int matched_path = 0; + strbuf_addstr(&directory, ent->name); + if (pathspec) { + for (specs =0; pathspec[specs]; ++specs) + /* nothing */; + seen = xcalloc(specs, 1); + /* Check if directory was explictly passed as + * pathspec. If so we want to remove it */ + if (match_pathspec(pathspec, ent->name, ent->len, + baselen, seen)) + matched_path = 1; + free(seen); + } + if (show_only && (remove_directories || matched_path)) { + printf("Would remove %s\n", directory.buf); + } else if (quiet && (remove_directories || matched_path)) { + remove_dir_recursively(&directory, 0); + } else if (remove_directories || matched_path) { + printf("Removing %s\n", directory.buf); + remove_dir_recursively(&directory, 0); + } else if (show_only) { + printf("Would not remove %s\n", directory.buf); + } else { + printf("Not removing %s\n", directory.buf); + } + strbuf_reset(&directory); + } else { + if (show_only) { + printf("Would remove %s\n", ent->name); + continue; + } else if (!quiet) { + printf("Removing %s\n", ent->name); + } + unlink(ent->name); + } + } + + strbuf_release(&directory); + return 0; +} diff --git a/builtin.h b/builtin.h index 9a6213af12..8248f293e6 100644 --- a/builtin.h +++ b/builtin.h @@ -24,6 +24,7 @@ extern int cmd_check_attr(int argc, const char **argv, const char *prefix); extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix); extern int cmd_cherry(int argc, const char **argv, const char *prefix); extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix); +extern int cmd_clean(int argc, const char **argv, const char *prefix); extern int cmd_commit_tree(int argc, const char **argv, const char *prefix); extern int cmd_count_objects(int argc, const char **argv, const char *prefix); extern int cmd_describe(int argc, const char **argv, const char *prefix); diff --git a/git-clean.sh b/contrib/examples/git-clean.sh similarity index 100% rename from git-clean.sh rename to contrib/examples/git-clean.sh diff --git a/git.c b/git.c index 7604319b5a..0c0483f815 100644 --- a/git.c +++ b/git.c @@ -293,6 +293,7 @@ static void handle_internal_command(int argc, const char **argv) { "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE }, { "cherry", cmd_cherry, RUN_SETUP }, { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE }, + { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE }, { "commit-tree", cmd_commit_tree, RUN_SETUP }, { "config", cmd_config }, { "count-objects", cmd_count_objects, RUN_SETUP }, From 625db1b7530b621f32b0ef1ef502a09a05872f5d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 12 Nov 2007 21:13:05 -0800 Subject: [PATCH 35/71] git-clean: Fix error message if clean.requireForce is not set. It was distracting to see this error message: clean.requireForce set and -n or -f not given; refusing to clean even though clean.requireForce was not set at all. This patch distinguishes the cases and gives a different message depending on whether the configuration variable is not set or set to true. Signed-off-by: Junio C Hamano --- builtin-clean.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/builtin-clean.c b/builtin-clean.c index 55658e742f..8da6f3c633 100644 --- a/builtin-clean.c +++ b/builtin-clean.c @@ -11,7 +11,7 @@ #include "dir.h" #include "parse-options.h" -static int force; +static int force = -1; /* unset */ static const char *const builtin_clean_usage[] = { "git-clean [-d] [-f] [-n] [-q] [-x | -X] [--] ...", @@ -29,7 +29,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) { int j; int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0; - int ignored_only = 0, baselen = 0; + int ignored_only = 0, baselen = 0, config_set = 0; struct strbuf directory; struct dir_struct dir; const char *path, *base; @@ -47,6 +47,11 @@ int cmd_clean(int argc, const char **argv, const char *prefix) }; git_config(git_clean_config); + if (force < 0) + force = 0; + else + config_set = 1; + argc = parse_options(argc, argv, options, builtin_clean_usage, 0); memset(&dir, 0, sizeof(dir)); @@ -59,7 +64,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix) die("-x and -X cannot be used together"); if (!show_only && !force) - die("clean.requireForce set and -n or -f not given; refusing to clean"); + die("clean.requireForce%s set and -n or -f not given; " + "refusing to clean", config_set ? "" : " not"); dir.show_other_directories = 1; From 1617adc7a074415413b2f48da72f43d5c6cd74c4 Mon Sep 17 00:00:00 2001 From: Shawn Bohrer Date: Wed, 14 Nov 2007 23:00:54 -0600 Subject: [PATCH 36/71] Teach git clean to use setup_standard_excludes() Signed-off-by: Shawn Bohrer Signed-off-by: Junio C Hamano --- builtin-clean.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/builtin-clean.c b/builtin-clean.c index 8da6f3c633..56ae4eb9bb 100644 --- a/builtin-clean.c +++ b/builtin-clean.c @@ -22,7 +22,7 @@ static int git_clean_config(const char *var, const char *value) { if (!strcmp(var, "clean.requireforce")) force = !git_config_bool(var, value); - return 0; + return git_default_config(var, value); } int cmd_clean(int argc, const char **argv, const char *prefix) @@ -55,10 +55,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, options, builtin_clean_usage, 0); memset(&dir, 0, sizeof(dir)); - if (ignored_only) { - dir.show_ignored =1; - dir.exclude_per_dir = ".gitignore"; - } + if (ignored_only) + dir.show_ignored = 1; if (ignored && ignored_only) die("-x and -X cannot be used together"); @@ -69,13 +67,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix) dir.show_other_directories = 1; - if (!ignored) { - dir.exclude_per_dir = ".gitignore"; - if (!access(git_path("info/exclude"), F_OK)) { - char *exclude_path = git_path("info/exclude"); - add_excludes_from_file(&dir, exclude_path); - } - } + if (!ignored) + setup_standard_excludes(&dir); pathspec = get_pathspec(prefix, argv); read_cache(); From fce0499fad13815d936c1068b7a064030f543b3d Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 20 Nov 2007 06:39:53 +0100 Subject: [PATCH 37/71] Bisect reset: do nothing when not bisecting. Before this patch, using "git bisect reset" when not bisecting did a "git checkout master" for no good reason. This also happened using "git bisect replay" when not bisecting because "bisect_replay" starts by calling "bisect_reset". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- git-bisect.sh | 4 ++++ t/t6030-bisect-porcelain.sh | 27 ++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/git-bisect.sh b/git-bisect.sh index 414f813be7..6f20a297a5 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -328,6 +328,10 @@ bisect_visualize() { } bisect_reset() { + test -f "$GIT_DIR/BISECT_NAMES" || { + echo "We are not bisecting." + return + } case "$#" in 0) if [ -s "$GIT_DIR/head-name" ]; then branch=`cat "$GIT_DIR/head-name"` diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index f09db6244e..2ba4b00e52 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -71,6 +71,31 @@ test_expect_success 'bisect start with one bad and good' ' git bisect next ' +test_expect_success 'bisect reset: back in the master branch' ' + git bisect reset && + echo "* master" > branch.expect && + git branch > branch.output && + cmp branch.expect branch.output +' + +test_expect_success 'bisect reset: back in another branch' ' + git checkout -b other && + git bisect start && + git bisect good $HASH1 && + git bisect bad $HASH3 && + git bisect reset && + echo " master" > branch.expect && + echo "* other" >> branch.expect && + git branch > branch.output && + cmp branch.expect branch.output +' + +test_expect_success 'bisect reset when not bisecting' ' + git bisect reset && + git branch > branch.output && + cmp branch.expect branch.output +' + test_expect_success 'bisect reset removes packed refs' ' git bisect reset && git bisect start && @@ -179,7 +204,7 @@ test_expect_success 'bisect skip: add line and then a new test' ' git bisect skip && git bisect good > my_bisect_log.txt && grep "$HASH5 is first bad commit" my_bisect_log.txt && - git bisect log > log_to_replay.txt + git bisect log > log_to_replay.txt && git bisect reset ' From 07f507155d01af0fdc8aa179b6a779301168b1eb Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 20 Nov 2007 06:18:01 -0500 Subject: [PATCH 38/71] send-pack: cluster ref status reporting Instead of intermingling success and failure, we now print: 1. all uptodate refs (if args.verbose is enabled) 2. successfully pushed refs 3. failed refs with the assumption that the user is most likely to see the ones at the end, and therefore we order them from "least interesting" to "most interesting." Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin-send-pack.c | 95 ++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 41 deletions(-) diff --git a/builtin-send-pack.c b/builtin-send-pack.c index 3aab89ca1b..25ae1fe860 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -298,52 +298,65 @@ static void print_ok_ref_status(struct ref *ref) } } +static int print_one_push_status(struct ref *ref, const char *dest, int count) +{ + if (!count) + fprintf(stderr, "To %s\n", dest); + + switch(ref->status) { + case REF_STATUS_NONE: + print_ref_status('X', "[no match]", ref, NULL, NULL); + break; + case REF_STATUS_REJECT_NODELETE: + print_ref_status('!', "[rejected]", ref, NULL, + "remote does not support deleting refs"); + break; + case REF_STATUS_UPTODATE: + print_ref_status('=', "[up to date]", ref, + ref->peer_ref, NULL); + break; + case REF_STATUS_REJECT_NONFASTFORWARD: + print_ref_status('!', "[rejected]", ref, ref->peer_ref, + "non-fast forward"); + break; + case REF_STATUS_REMOTE_REJECT: + print_ref_status('!', "[remote rejected]", ref, + ref->deletion ? NULL : ref->peer_ref, + ref->remote_status); + break; + case REF_STATUS_EXPECTING_REPORT: + print_ref_status('!', "[remote failure]", ref, + ref->deletion ? NULL : ref->peer_ref, + "remote failed to report status"); + break; + case REF_STATUS_OK: + print_ok_ref_status(ref); + break; + } + + return 1; +} + static void print_push_status(const char *dest, struct ref *refs) { struct ref *ref; - int shown_dest = 0; + int n = 0; + + if (args.verbose) { + for (ref = refs; ref; ref = ref->next) + if (ref->status == REF_STATUS_UPTODATE) + n += print_one_push_status(ref, dest, n); + } + + for (ref = refs; ref; ref = ref->next) + if (ref->status == REF_STATUS_OK) + n += print_one_push_status(ref, dest, n); for (ref = refs; ref; ref = ref->next) { - if (!ref->status) - continue; - if (ref->status == REF_STATUS_UPTODATE && !args.verbose) - continue; - - if (!shown_dest) { - fprintf(stderr, "To %s\n", dest); - shown_dest = 1; - } - - switch(ref->status) { - case REF_STATUS_NONE: - print_ref_status('X', "[no match]", ref, NULL, NULL); - break; - case REF_STATUS_REJECT_NODELETE: - print_ref_status('!', "[rejected]", ref, NULL, - "remote does not support deleting refs"); - break; - case REF_STATUS_UPTODATE: - print_ref_status('=', "[up to date]", ref, - ref->peer_ref, NULL); - break; - case REF_STATUS_REJECT_NONFASTFORWARD: - print_ref_status('!', "[rejected]", ref, ref->peer_ref, - "non-fast forward"); - break; - case REF_STATUS_REMOTE_REJECT: - print_ref_status('!', "[remote rejected]", ref, - ref->deletion ? NULL : ref->peer_ref, - ref->remote_status); - break; - case REF_STATUS_EXPECTING_REPORT: - print_ref_status('!', "[remote failure]", ref, - ref->deletion ? NULL : ref->peer_ref, - "remote failed to report status"); - break; - case REF_STATUS_OK: - print_ok_ref_status(ref); - break; - } + if (ref->status != REF_STATUS_NONE && + ref->status != REF_STATUS_UPTODATE && + ref->status != REF_STATUS_OK) + n += print_one_push_status(ref, dest, n); } } From 29cc0ef1ab946bc593dbe3a1e71d7b3efa42e0c4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 23 Nov 2007 20:13:41 -0800 Subject: [PATCH 39/71] t4119: correct overeager war-on-whitespace Earlier a6080a0a44d5ead84db3dabbbc80e82df838533d (War on whitespace) dropped a necessary trailing whitespace from the test vector. --- t/t4119-apply-config.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t4119-apply-config.sh b/t/t4119-apply-config.sh index 65571e0549..b540f7295a 100755 --- a/t/t4119-apply-config.sh +++ b/t/t4119-apply-config.sh @@ -24,7 +24,7 @@ cat >gpatch.file <<\EOF && +++ file1+ 2007-02-21 01:07:44.000000000 -0800 @@ -1 +1 @@ -A -+B ++B EOF sed -e 's|file1|sub/&|' gpatch.file >gpatch-sub.file && From 87194d26d9b4aae6bf6d224cdc1a19c9f4de9acc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 24 Nov 2007 17:41:18 -0800 Subject: [PATCH 40/71] Deprecate peek-remote Signed-off-by: Junio C Hamano --- Documentation/cmd-list.perl | 2 +- Documentation/git-peek-remote.txt | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl index 57a790df63..b709551726 100755 --- a/Documentation/cmd-list.perl +++ b/Documentation/cmd-list.perl @@ -151,7 +151,7 @@ git-pack-redundant plumbinginterrogators git-pack-refs ancillarymanipulators git-parse-remote synchelpers git-patch-id purehelpers -git-peek-remote purehelpers +git-peek-remote purehelpers deprecated git-prune ancillarymanipulators git-prune-packed plumbingmanipulators git-pull mainporcelain diff --git a/Documentation/git-peek-remote.txt b/Documentation/git-peek-remote.txt index abc171266a..38a5325af7 100644 --- a/Documentation/git-peek-remote.txt +++ b/Documentation/git-peek-remote.txt @@ -12,8 +12,7 @@ SYNOPSIS DESCRIPTION ----------- -Lists the references the remote repository has, and optionally -stores them in the local repository under the same name. +This command is deprecated; use `git-ls-remote` instead. OPTIONS ------- From 92b7ba16b7605f70cac845d4a8d0162392ac0223 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 24 Nov 2007 17:42:36 -0800 Subject: [PATCH 41/71] Update draft release notes for 1.5.4 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.4.txt | 38 ++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/Documentation/RelNotes-1.5.4.txt b/Documentation/RelNotes-1.5.4.txt index a4a2a7f429..ff4d3d82b0 100644 --- a/Documentation/RelNotes-1.5.4.txt +++ b/Documentation/RelNotes-1.5.4.txt @@ -6,6 +6,14 @@ Updates since v1.5.3 * Comes with much improved gitk. + * Comes with git-gui 0.9.0 with i18n. + + * git-lost-found was deprecated in favor of git-fsck's --lost-found + option. + + * git-peek-remote is deprecated, as git-ls-remote was written in C and + works for all transports. + * "progress display" from many commands are a lot nicer to the eye. Transfer commands show throughput data. @@ -15,6 +23,11 @@ Updates since v1.5.3 * git-rebase learned --whitespace option. + * In git-rebase, when you decide not to replay a particular change + after the command stopped with a conflict, you can say "git-rebase + --skip" without first running "git reset --hard", as the command now + run it for you. + * git-remote knows --mirror mode. * git-merge can call the "post-merge" hook. @@ -37,11 +50,20 @@ Updates since v1.5.3 variable used to mean "do not require", but we now use the safer default). + * git-clean has been rewritten in C. + * git-push has been rewritten in C. * git-push learned --dry-run option to show what would happen if a push is run. + * git-push does not update a tracking ref on the pushing side when the + remote refused to update the corresponding ref. + + * git-push learned --mirror option. This is to push the local refs + one-to-one to the remote, and deletes refs from the remote that do + not exist anymore in the repository on the pushing side. + * git-remote learned "rm" subcommand. * git-rebase --interactive mode can now work on detached HEAD. @@ -54,9 +76,6 @@ Updates since v1.5.3 * Various Perforce importer updates. - * git-lost-found was deprecated in favor of git-fsck's --lost-found - option. - * "git log" learned --early-output option to help interactive GUI implementations. @@ -86,6 +105,17 @@ Updates since v1.5.3 to allow checking out a path outside the current directory without cd'ing up. + * "git send-email --dry-run" shows full headers for easier + diagnosis. + + * "git merge-ours" is built-in. + + * "git svn" learned "info" subcommand. + + * "git status" from a subdirectory now shows relative paths + which makes copy-and-pasting for git-checkout/git-add/git-rm + easier. + * Output processing for '--pretty=format:' has been optimized. @@ -119,6 +149,6 @@ this release, unless otherwise noted. -- exec >/var/tmp/1 -O=v1.5.3.6-727-g5d3d1ca +O=v1.5.3.6-950-gda03a58 echo O=`git describe refs/heads/master` git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint From f0c4881facabc0aa76f4b51871bd70c45effb508 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 25 Nov 2007 10:34:27 -0800 Subject: [PATCH 42/71] git-checkout: describe detached head correctly When you have a file called HEAD in the work tree, the code to report where the HEAD is at when "git checkout $commit^0" is done triggered unnecessary ambiguity checking. Explicitly mark the command line with "--" and make it clear that we are talking about a revision. Signed-off-by: Junio C Hamano --- git-checkout.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-checkout.sh b/git-checkout.sh index 17f43927aa..5ca71242e7 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -169,7 +169,7 @@ detach_warn= describe_detached_head () { test -n "$quiet" || { printf >&2 "$1 " - GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2" + GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2" -- } } From 0c4a33b54f3dbb9fa8cd2f5cf0e2a6363849d899 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 25 Nov 2007 13:53:37 -0500 Subject: [PATCH 43/71] user-manual: define "branch" and "working tree" at start Some explanation here might help. Signed-off-by: J. Bruce Fields --- Documentation/user-manual.txt | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index c027353337..8355cce294 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -56,11 +56,12 @@ $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git The initial clone may be time-consuming for a large project, but you will only need to clone once. -The clone command creates a new directory named after the project -("git" or "linux-2.6" in the examples above). After you cd into this +The clone command creates a new directory named after the project ("git" +or "linux-2.6" in the examples above). After you cd into this directory, you will see that it contains a copy of the project files, -together with a special top-level directory named ".git", which -contains all the information about the history of the project. +called the <>, together with a special +top-level directory named ".git", which contains all the information +about the history of the project. [[how-to-check-out]] How to check out a different version of a project @@ -71,8 +72,13 @@ of files. It stores the history as a compressed collection of interrelated snapshots of the project's contents. In git each such version is called a <>. -A single git repository may contain multiple branches. It keeps track -of them by keeping a list of <> which reference the +Those snapshots aren't necessarily all arranged in a single line from +oldest to newest; instead, work may simultaneously proceed along +parallel lines of development, called >, which may +merge and diverge. + +A single git repository can track development on multiple branches. It +does this by keeping a list of <> which reference the latest commit on each branch; the gitlink:git-branch[1] command shows you the list of branch heads: From 81eb417ad423ef7e8d088d517f89d3bda92f9c06 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 25 Nov 2007 17:54:19 -0500 Subject: [PATCH 44/71] user-manual: failed push to public repository More details on the case of a failed push to a public (non-shared) repository. Signed-off-by: J. Bruce Fields --- Documentation/user-manual.txt | 58 +++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 8355cce294..547c9364b9 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1929,15 +1929,9 @@ or just $ git push ssh://yourserver.com/~you/proj.git master ------------------------------------------------- -As with git-fetch, git-push will complain if this does not result in -a <>. Normally this is a sign of -something wrong. However, if you are sure you know what you're -doing, you may force git-push to perform the update anyway by -proceeding the branch name by a plus sign: - -------------------------------------------------- -$ git push ssh://yourserver.com/~you/proj.git +master -------------------------------------------------- +As with git-fetch, git-push will complain if this does not result in a +<>; see the following section for details on +handling this case. Note that the target of a "push" is normally a <> repository. You can also push to a @@ -1965,6 +1959,52 @@ See the explanations of the remote..url, branch..remote, and remote..push options in gitlink:git-config[1] for details. +[[forcing-push]] +What to do when a push fails +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If a push would not result in a <> of the +remote branch, then it will fail with an error like: + +------------------------------------------------- +error: remote 'refs/heads/master' is not an ancestor of + local 'refs/heads/master'. + Maybe you are not up-to-date and need to pull first? +error: failed to push to 'ssh://yourserver.com/~you/proj.git' +------------------------------------------------- + +This can happen, for example, if you: + + - use `git reset --hard` to remove already-published commits, or + - use `git commit --amend` to replace already-published commits + (as in <>), or + - use `git rebase` to rebase any already-published commits (as + in <>). + +You may force git-push to perform the update anyway by preceding the +branch name with a plus sign: + +------------------------------------------------- +$ git push ssh://yourserver.com/~you/proj.git +master +------------------------------------------------- + +Normally whenever a branch head in a public repository is modified, it +is modified to point to a descendent of the commit that it pointed to +before. By forcing a push in this situation, you break that convention. +(See <>.) + +Nevertheless, this is a common practice for people that need a simple +way to publish a work-in-progress patch series, and it is an acceptable +compromise as long as you warn other developers that this is how you +intend to manage the branch. + +It's also possible for a push to fail in this way when other people have +the right to push to the same repository. In that case, the correct +solution is to retry the push after first updating your work by either a +pull or a fetch followed by a rebase; see the +<> and +link:cvs-migration.html[git for CVS users] for more. + [[setting-up-a-shared-repository]] Setting up a shared repository ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 757f58ed380a17225e8de9f461083a21d0a2c97c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 25 Nov 2007 15:15:48 -0800 Subject: [PATCH 45/71] revert/cherry-pick: do not mention the original ref When you cherry-pick or revert a commit, naming it with an annotated tag, we added a comment, attempting to repeat what we got from the end user, to the message. But this was inconsistent. When we got "cherry-pick branch", we recorded the object name (40-letter SHA-1) without saying anything like "original was 'branch'". There was no need to. Also recent rewrite to use parse-options made it impossible to parrot the original command line without "unparsing". This removes the code that implements the misguided "we dereferenced the tag so record that in the commit message" behaviour. Signed-off-by: Junio C Hamano --- builtin-revert.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/builtin-revert.c b/builtin-revert.c index 365b330f9e..a0586f9753 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -30,7 +30,7 @@ static const char * const cherry_pick_usage[] = { NULL }; -static int edit, no_replay, no_commit, needed_deref, mainline; +static int edit, no_replay, no_commit, mainline; static enum { REVERT, CHERRY_PICK } action; static struct commit *commit; @@ -66,7 +66,6 @@ static void parse_args(int argc, const char **argv) if (commit->object.type == OBJ_TAG) { commit = (struct commit *) deref_tag((struct object *)commit, arg, strlen(arg)); - needed_deref = 1; } if (commit->object.type != OBJ_COMMIT) die ("'%s' does not point to a commit", arg); @@ -333,17 +332,6 @@ static int revert_or_cherry_pick(int argc, const char **argv) add_to_msg(")\n"); } } - if (needed_deref) { - add_to_msg("(original 'git "); - add_to_msg(me); - add_to_msg("' arguments: "); - for (i = 0; i < argc; i++) { - if (i) - add_to_msg(" "); - add_to_msg(argv[i]); - } - add_to_msg(")\n"); - } if (merge_recursive(sha1_to_hex(base->object.sha1), sha1_to_hex(head), "HEAD", From 7cb192eab0251911e2ca77d4ecceb621dd2d34f5 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 25 Nov 2007 19:01:57 -0500 Subject: [PATCH 46/71] user-manual: clarify language about "modifying" old commits It's important to remember that git doesn't really allowing "editing" or "modifying" commits, only replacing them by new commits. Redo some of the language to make this clearer. Signed-off-by: J. Bruce Fields --- Documentation/user-manual.txt | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 547c9364b9..7fd3791b5e 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1416,8 +1416,8 @@ with the changes to be reverted, then you will be asked to fix conflicts manually, just as in the case of <>. -[[fixing-a-mistake-by-editing-history]] -Fixing a mistake by editing history +[[fixing-a-mistake-by-rewriting-history]] +Fixing a mistake by rewriting history ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If the problematic commit is the most recent commit, and you have not @@ -1440,7 +1440,7 @@ Again, you should never do this to a commit that may already have been merged into another branch; use gitlink:git-revert[1] instead in that case. -It is also possible to edit commits further back in the history, but +It is also possible to replace commits further back in the history, but this is an advanced topic to be left for <>. @@ -1977,7 +1977,7 @@ This can happen, for example, if you: - use `git reset --hard` to remove already-published commits, or - use `git commit --amend` to replace already-published commits - (as in <>), or + (as in <>), or - use `git rebase` to rebase any already-published commits (as in <>). @@ -2472,11 +2472,11 @@ return mywork to the state it had before you started the rebase: $ git rebase --abort ------------------------------------------------- -[[modifying-one-commit]] -Modifying a single commit +[[rewriting-one-commit]] +Rewriting a single commit ------------------------- -We saw in <> that you can replace the +We saw in <> that you can replace the most recent commit using ------------------------------------------------- @@ -2486,8 +2486,10 @@ $ git commit --amend which will replace the old commit by a new commit incorporating your changes, giving you a chance to edit the old commit message first. -You can also use a combination of this and gitlink:git-rebase[1] to edit -commits further back in your history. First, tag the problematic commit with +You can also use a combination of this and gitlink:git-rebase[1] to +replace a commit further back in your history and recreate the +intervening changes on top of it. First, tag the problematic commit +with ------------------------------------------------- $ git tag bad mywork~5 From 84ef033832af9e0be886214c70b2006b08630072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Goddard=20Rosa?= Date: Wed, 21 Nov 2007 18:59:14 -0200 Subject: [PATCH 47/71] Print the real filename that we failed to open. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we fail to open a temporary file to be renamed to something else, we reported the final filename, not the temporary file we failed to open. Signed-off-by: André Goddard Rosa Signed-off-by: Junio C Hamano --- http-push.c | 4 ++-- http-walker.c | 4 ++-- server-info.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/http-push.c b/http-push.c index 66b81f1726..78283b4de3 100644 --- a/http-push.c +++ b/http-push.c @@ -433,7 +433,7 @@ static void start_fetch_packed(struct transfer_request *request) packfile = fopen(request->tmpfile, "a"); if (!packfile) { fprintf(stderr, "Unable to open local file %s for pack", - filename); + request->tmpfile); remote->can_update_info_refs = 0; free(url); return; @@ -941,7 +941,7 @@ static int fetch_index(unsigned char *sha1) indexfile = fopen(tmpfile, "a"); if (!indexfile) return error("Unable to open local file %s for pack index", - filename); + tmpfile); slot = get_active_slot(); slot->results = &results; diff --git a/http-walker.c b/http-walker.c index 444aebf526..a3fb596542 100644 --- a/http-walker.c +++ b/http-walker.c @@ -405,7 +405,7 @@ static int fetch_index(struct walker *walker, struct alt_base *repo, unsigned ch indexfile = fopen(tmpfile, "a"); if (!indexfile) return error("Unable to open local file %s for pack index", - filename); + tmpfile); slot = get_active_slot(); slot->results = &results; @@ -770,7 +770,7 @@ static int fetch_pack(struct walker *walker, struct alt_base *repo, unsigned cha packfile = fopen(tmpfile, "a"); if (!packfile) return error("Unable to open local file %s for pack", - filename); + tmpfile); slot = get_active_slot(); slot->results = &results; diff --git a/server-info.c b/server-info.c index 0d1312ca56..a051e49a9e 100644 --- a/server-info.c +++ b/server-info.c @@ -35,7 +35,7 @@ static int update_info_refs(int force) safe_create_leading_directories(path0); info_ref_fp = fopen(path1, "w"); if (!info_ref_fp) - return error("unable to update %s", path0); + return error("unable to update %s", path1); for_each_ref(add_info_ref, NULL); fclose(info_ref_fp); adjust_shared_perm(path1); From 1cdade2c4cb27f648a98d326ef3db523b6afafa7 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sat, 3 Mar 2007 22:53:37 -0500 Subject: [PATCH 48/71] user-manual: recovering from corruption Some instructions on dealing with corruption of the object database. Most of this text is from an example by Linus, identified by Nicolas Pitre with a little further editing by me. Signed-off-by: "J. Bruce Fields" --- Documentation/user-manual.txt | 131 +++++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 1 deletion(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 7fd3791b5e..b0a873bd7f 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1560,6 +1560,11 @@ This may be time-consuming. Unlike most other git operations (including git-gc when run without any options), it is not safe to prune while other git operations are in progress in the same repository. +If gitlink:git-fsck[1] complains about sha1 mismatches or missing +objects, you may have a much more serious problem; your best option is +probably restoring from backups. See +<> for a detailed discussion. + [[recovering-lost-changes]] Recovering lost changes ~~~~~~~~~~~~~~~~~~~~~~~ @@ -3220,6 +3225,127 @@ confusing and scary messages, but it won't actually do anything bad. In contrast, running "git prune" while somebody is actively changing the repository is a *BAD* idea). +[[recovering-from-repository-corruption]] +Recovering from repository corruption +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By design, git treats data trusted to it with caution. However, even in +the absence of bugs in git itself, it is still possible that hardware or +operating system errors could corrupt data. + +The first defense against such problems is backups. You can back up a +git directory using clone, or just using cp, tar, or any other backup +mechanism. + +As a last resort, you can search for the corrupted objects and attempt +to replace them by hand. Back up your repository before attempting this +in case you corrupt things even more in the process. + +We'll assume that the problem is a single missing or corrupted blob, +which is sometimes a solveable problem. (Recovering missing trees and +especially commits is *much* harder). + +Before starting, verify that there is corruption, and figure out where +it is with gitlink:git-fsck[1]; this may be time-consuming. + +Assume the output looks like this: + +------------------------------------------------ +$ git-fsck --full +broken link from tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8 + to blob 4b9458b3786228369c63936db65827de3cc06200 +missing blob 4b9458b3786228369c63936db65827de3cc06200 +------------------------------------------------ + +(Typically there will be some "dangling object" messages too, but they +aren't interesting.) + +Now you know that blob 4b9458b3 is missing, and that the tree 2d9263c6 +points to it. If you could find just one copy of that missing blob +object, possibly in some other repository, you could move it into +.git/objects/4b/9458b3... and be done. Suppose you can't. You can +still examine the tree that pointed to it with gitlink:git-ls-tree[1], +which might output something like: + +------------------------------------------------ +$ git ls-tree 2d9263c6d23595e7cb2a21e5ebbb53655278dff8 +100644 blob 8d14531846b95bfa3564b58ccfb7913a034323b8 .gitignore +100644 blob ebf9bf84da0aab5ed944264a5db2a65fe3a3e883 .mailmap +100644 blob ca442d313d86dc67e0a2e5d584b465bd382cbf5c COPYING +... +100644 blob 4b9458b3786228369c63936db65827de3cc06200 myfile +... +------------------------------------------------ + +So now you know that the missing blob was the data for a file named +"myfile". And chances are you can also identify the directory--let's +say it's in "somedirectory". If you're lucky the missing copy might be +the same as the copy you have checked out in your working tree at +"somedirectory/myfile"; you can test whether that's right with +gitlink:git-hash-object[1]: + +------------------------------------------------ +$ git hash-object -w somedirectory/myfile +------------------------------------------------ + +which will create and store a blob object with the contents of +somedirectory/myfile, and output the sha1 of that object. if you're +extremely lucky it might be 4b9458b3786228369c63936db65827de3cc06200, in +which case you've guessed right, and the corruption is fixed! + +Otherwise, you need more information. How do you tell which version of +the file has been lost? + +The easiest way to do this is with: + +------------------------------------------------ +$ git log --raw --all --full-history -- somedirectory/myfile +------------------------------------------------ + +Because you're asking for raw output, you'll now get something like + +------------------------------------------------ +commit abc +Author: +Date: +... +:100644 100644 4b9458b... newsha... M somedirectory/myfile + + +commit xyz +Author: +Date: + +... +:100644 100644 oldsha... 4b9458b... M somedirectory/myfile +------------------------------------------------ + +This tells you that the immediately preceding version of the file was +"newsha", and that the immediately following version was "oldsha". +You also know the commit messages that went with the change from oldsha +to 4b9458b and with the change from 4b9458b to newsha. + +If you've been committing small enough changes, you may now have a good +shot at reconstructing the contents of the in-between state 4b9458b. + +If you can do that, you can now recreate the missing object with + +------------------------------------------------ +$ git hash-object -w +------------------------------------------------ + +and your repository is good again! + +(Btw, you could have ignored the fsck, and started with doing a + +------------------------------------------------ +$ git log --raw --all +------------------------------------------------ + +and just looked for the sha of the missing object (4b9458b..) in that +whole thing. It's up to you - git does *have* a lot of information, it is +just missing one particular blob version. + [[the-index]] The index ----------- @@ -4429,4 +4555,7 @@ Write a chapter on using plumbing and writing scripts. Alternates, clone -reference, etc. -git unpack-objects -r for recovery +More on recovery from repository corruption. See: + http://marc.theaimsgroup.com/?l=git&m=117263864820799&w=2 + http://marc.theaimsgroup.com/?l=git&m=117147855503798&w=2 + http://marc.theaimsgroup.com/?l=git&m=117147855503798&w=2 From e0fda6abd11c567b72f29ec0ee06c541404a9cb7 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Mon, 26 Nov 2007 09:34:48 +0100 Subject: [PATCH 49/71] Fix typo in draft 1.5.4 release notes Signed-off-by: Wincent Colaiuta Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.4.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/RelNotes-1.5.4.txt b/Documentation/RelNotes-1.5.4.txt index ff4d3d82b0..c9c537649f 100644 --- a/Documentation/RelNotes-1.5.4.txt +++ b/Documentation/RelNotes-1.5.4.txt @@ -26,7 +26,7 @@ Updates since v1.5.3 * In git-rebase, when you decide not to replay a particular change after the command stopped with a conflict, you can say "git-rebase --skip" without first running "git reset --hard", as the command now - run it for you. + runs it for you. * git-remote knows --mirror mode. From ecf4831d89ba6986249e1bf232b2e2f5f7536223 Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Sun, 25 Nov 2007 23:29:03 +0100 Subject: [PATCH 50/71] Use is_absolute_path() in diff-lib.c, lockfile.c, setup.c, trace.c Using the helper function to test for absolute paths makes porting easier. Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano --- diff-lib.c | 2 +- lockfile.c | 2 +- setup.c | 2 +- trace.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/diff-lib.c b/diff-lib.c index f8e936ae10..d85d8f34ba 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -231,7 +231,7 @@ static int handle_diff_files_args(struct rev_info *revs, static int is_outside_repo(const char *path, int nongit, const char *prefix) { int i; - if (nongit || !strcmp(path, "-") || path[0] == '/') + if (nongit || !strcmp(path, "-") || is_absolute_path(path)) return 1; if (prefixcmp(path, "../")) return 0; diff --git a/lockfile.c b/lockfile.c index 258fb3f5ef..f45d3ed544 100644 --- a/lockfile.c +++ b/lockfile.c @@ -94,7 +94,7 @@ static char *resolve_symlink(char *p, size_t s) return p; } - if (link[0] == '/') { + if (is_absolute_path(link)) { /* absolute path simply replaces p */ if (link_len < s) strcpy(p, link); diff --git a/setup.c b/setup.c index 43cd3f94ea..2c7b5cb200 100644 --- a/setup.c +++ b/setup.c @@ -59,7 +59,7 @@ const char *prefix_path(const char *prefix, int len, const char *path) const char *prefix_filename(const char *pfx, int pfx_len, const char *arg) { static char path[PATH_MAX]; - if (!pfx || !*pfx || arg[0] == '/') + if (!pfx || !*pfx || is_absolute_path(arg)) return arg; memcpy(path, pfx, pfx_len); strcpy(path + pfx_len, arg); diff --git a/trace.c b/trace.c index 0d89dbe779..d3d1b6d55e 100644 --- a/trace.c +++ b/trace.c @@ -37,7 +37,7 @@ static int get_trace_fd(int *need_close) return STDERR_FILENO; if (strlen(trace) == 1 && isdigit(*trace)) return atoi(trace); - if (*trace == '/') { + if (is_absolute_path(trace)) { int fd = open(trace, O_WRONLY | O_APPEND | O_CREAT, 0666); if (fd == -1) { fprintf(stderr, From aa4f31d5a3923939bf2cb670ecaa4f68025f4bf3 Mon Sep 17 00:00:00 2001 From: Pascal Obry Date: Mon, 26 Nov 2007 23:04:28 +0100 Subject: [PATCH 51/71] git-stash: do not get fooled with "color.diff = true" When colors are set to "true" on the repository, the git log output will contain control characters to set/reset the colors, even when the output is to a pipe. This makes list_stash() fail as the downstream sed does not see what it is expecting. Signed-off-by: Junio C Hamano --- git-stash.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-stash.sh b/git-stash.sh index 534eb168ab..dd155568d9 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -37,7 +37,7 @@ create_stash () { # state of the base commit if b_commit=$(git rev-parse --verify HEAD) then - head=$(git log --abbrev-commit --pretty=oneline -n 1 HEAD) + head=$(git log --no-color --abbrev-commit --pretty=oneline -n 1 HEAD --) else die "You do not have the initial commit yet" fi @@ -108,7 +108,7 @@ have_stash () { list_stash () { have_stash || return 0 - git log --pretty=oneline -g "$@" $ref_stash | + git log --no-color --pretty=oneline -g "$@" $ref_stash -- | sed -n -e 's/^[.0-9a-f]* refs\///p' } From 795c7c0b08fefc7d0f5d0903d8341491350996f0 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 28 Nov 2007 13:55:46 -0500 Subject: [PATCH 52/71] Add basic cvsimport tests We weren't even testing basic things before, so let's at least try importing and updating a trivial repository, which will catch total breakage. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t9600-cvsimport.sh | 99 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100755 t/t9600-cvsimport.sh diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh new file mode 100755 index 0000000000..1ee06bb386 --- /dev/null +++ b/t/t9600-cvsimport.sh @@ -0,0 +1,99 @@ +#!/bin/sh + +test_description='git-cvsimport basic tests' +. ./test-lib.sh + +if ! ( type cvs && type cvsps ) >/dev/null 2>&1 +then + test_expect_success 'skipping cvsimport tests, cvs/cvsps not found' '' + test_done + exit +fi + +CVSROOT=$(pwd)/cvsroot +export CVSROOT +# for clean cvsps cache +HOME=$(pwd) +export HOME + +test_expect_success 'setup cvsroot' 'cvs init' + +test_expect_success 'setup a cvs module' ' + + mkdir $CVSROOT/module && + cvs co -d module-cvs module && + cd module-cvs && + cat <o_fortuna && +O Fortuna +velut luna +statu variabilis, + +semper crescis +aut decrescis; +vita detestabilis + +nunc obdurat +et tunc curat +ludo mentis aciem, + +egestatem, +potestatem +dissolvit ut glaciem. +EOF + cvs add o_fortuna && + cat <message && +add "O Fortuna" lyrics + +These public domain lyrics make an excellent sample text. +EOF + cvs commit -F message && + cd .. +' + +test_expect_success 'import a trivial module' ' + + git cvsimport -a -z 0 -C module-git module && + git diff module-cvs/o_fortuna module-git/o_fortuna + +' + +test_expect_success 'update cvs module' ' + + cd module-cvs && + cat <o_fortuna && +O Fortune, +like the moon +you are changeable, + +ever waxing +and waning; +hateful life + +first oppresses +and then soothes +as fancy takes it; + +poverty +and power +it melts them like ice. +EOF + cat <message && +translate to English + +My Latin is terrible. +EOF + cvs commit -F message && + cd .. +' + +test_expect_success 'update git module' ' + + cd module-git && + git cvsimport -a -z 0 module && + git merge origin && + cd .. && + git diff module-cvs/o_fortuna module-git/o_fortuna + +' + +test_done From 9da0dabcd9ff71083b6bbc9878c22b12002400dd Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 28 Nov 2007 13:56:11 -0500 Subject: [PATCH 53/71] cvsimport: use rev-parse to support packed refs Previously, if refs were packed, git-cvsimport would assume that particular refs did not exist. This could lead to, for example, overwriting previous 'origin' commits that were packed. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- git-cvsimport.perl | 25 ++++++++++--------------- t/t9600-cvsimport.sh | 2 ++ 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index e4bc2b54f6..a7a7d8f44d 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -526,18 +526,12 @@ sub is_sha1 { return $s =~ /^[a-f0-9]{40}$/; } -sub get_headref ($$) { - my $name = shift; - my $git_dir = shift; - - my $f = "$git_dir/$remote/$name"; - if (open(my $fh, $f)) { - chomp(my $r = <$fh>); - is_sha1($r) or die "Cannot get head id for $name ($r): $!"; - return $r; - } - die "unable to open $f: $!" unless $! == POSIX::ENOENT; - return undef; +sub get_headref ($) { + my $name = shift; + my $r = `git rev-parse --verify '$name' 2>/dev/null`; + return undef unless $? == 0; + chomp $r; + return $r; } -d $git_tree @@ -697,7 +691,8 @@ my (@old,@new,@skipped,%ignorebranch); $ignorebranch{'#CVSPS_NO_BRANCH'} = 1; sub commit { - if ($branch eq $opt_o && !$index{branch} && !get_headref($branch, $git_dir)) { + if ($branch eq $opt_o && !$index{branch} && + !get_headref("$remote/$branch")) { # looks like an initial commit # use the index primed by git-init $ENV{GIT_INDEX_FILE} = "$git_dir/index"; @@ -721,7 +716,7 @@ sub commit { update_index(@old, @new); @old = @new = (); my $tree = write_tree(); - my $parent = get_headref($last_branch, $git_dir); + my $parent = get_headref("$remote/$last_branch"); print "Parent ID " . ($parent ? $parent : "(empty)") . "\n" if $opt_v; my @commit_args; @@ -732,7 +727,7 @@ sub commit { foreach my $rx (@mergerx) { next unless $logmsg =~ $rx && $1; my $mparent = $1 eq 'HEAD' ? $opt_o : $1; - if (my $sha1 = get_headref($mparent, $git_dir)) { + if (my $sha1 = get_headref("$remote/$mparent")) { push @commit_args, '-p', $mparent; print "Merge parent branch: $mparent\n" if $opt_v; } diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh index 1ee06bb386..3338d447c5 100755 --- a/t/t9600-cvsimport.sh +++ b/t/t9600-cvsimport.sh @@ -57,6 +57,8 @@ test_expect_success 'import a trivial module' ' ' +test_expect_success 'pack refs' 'cd module-git && git gc && cd ..' + test_expect_success 'update cvs module' ' cd module-cvs && From 0750d751157addde94f28a7933ed4f914a526eb7 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 28 Nov 2007 13:56:28 -0500 Subject: [PATCH 54/71] cvsimport: miscellaneous packed-ref fixes These were found with a grep for '$git_dir'; they all replace a direct access of "$git_dir/refs/..." with a call to git-rev-parse or git-update-ref. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- git-cvsimport.perl | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index a7a7d8f44d..d29b547d26 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -864,29 +864,27 @@ while () { print STDERR "Branch $branch erroneously stems from itself -- changed ancestor to $opt_o\n"; $ancestor = $opt_o; } - if (-f "$git_dir/$remote/$branch") { + if (defined get_headref("$remote/$branch")) { print STDERR "Branch $branch already exists!\n"; $state=11; next; } - unless (open(H,"$git_dir/$remote/$ancestor")) { + my $id = get_headref("$remote/$ancestor"); + if (!$id) { print STDERR "Branch $ancestor does not exist!\n"; $ignorebranch{$branch} = 1; $state=11; next; } - chomp(my $id = ); - close(H); - unless (open(H,"> $git_dir/$remote/$branch")) { - print STDERR "Could not create branch $branch: $!\n"; + + system(qw(git update-ref -m cvsimport), + "$remote/$branch", $id); + if($? != 0) { + print STDERR "Could not create branch $branch\n"; $ignorebranch{$branch} = 1; $state=11; next; } - print H "$id\n" - or die "Could not write branch $branch: $!"; - close(H) - or die "Could not write branch $branch: $!"; } $last_branch = $branch if $branch ne $last_branch; $state = 9; @@ -998,7 +996,7 @@ if ($orig_branch) { $orig_branch = "master"; print "DONE; creating $orig_branch branch\n" if $opt_v; system("git-update-ref", "refs/heads/master", "$remote/$opt_o") - unless -f "$git_dir/refs/heads/master"; + unless defined get_headref('refs/heads/master'); system("git-symbolic-ref", "$remote/HEAD", "$remote/$opt_o") if ($opt_r && $opt_o ne 'HEAD'); system('git-update-ref', 'HEAD', "$orig_branch"); From 16ed34ad35d9a991a72b2db420ff95bc260990cc Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 28 Nov 2007 15:50:38 +0000 Subject: [PATCH 55/71] filter-branch: fix dirty way to provide the helpers to commit filters The helper functions 'map' and 'skip_commit' were provided to commit filters by sourcing filter-branch itself. This was done with a certain environment variable set to indicate that only the functions should be defined, and the script should return then. This was really hacky, and it did not work all that well, since the full path to git-filter-branch was not known at all times. Avoid that by putting the functions into a variable, and eval'ing that variable. The commit filter gets these functions by prepending the variable to the specified commands. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-filter-branch.sh | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/git-filter-branch.sh b/git-filter-branch.sh index c9f515d0ee..19cab5a9a8 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -8,6 +8,9 @@ # a new branch. You can specify a number of filters to modify the commits, # files and trees. +# The following functions will also be available in the commit filter: + +functions=$(cat << \EOF warn () { echo "$*" >&2 } @@ -46,6 +49,10 @@ die() echo "$*" >&2 exit 1 } +EOF +) + +eval "$functions" # When piped a commit, output a script to set the ident of either # "author" or "committer @@ -80,11 +87,6 @@ set_ident () { echo "[ -n \"\$GIT_${uid}_NAME\" ] || export GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\"" } -# This script can be sourced by the commit filter to get the functions -test "a$SOURCE_FUNCTIONS" = a1 && return -this_script="$(cd "$(dirname "$0")"; pwd)"/$(basename "$0") -export this_script - USAGE="[--env-filter ] [--tree-filter ] \ [--index-filter ] [--parent-filter ] \ [--msg-filter ] [--commit-filter ] \ @@ -156,7 +158,7 @@ do filter_msg="$OPTARG" ;; --commit-filter) - filter_commit='SOURCE_FUNCTIONS=1 . "$this_script";'" $OPTARG" + filter_commit="$functions; $OPTARG" ;; --tag-name-filter) filter_tag_name="$OPTARG" From 1e72a40de99be24809eba5abdab4c0b00846b9b5 Mon Sep 17 00:00:00 2001 From: Jan Hudec Date: Sat, 17 Nov 2007 20:51:44 +0100 Subject: [PATCH 56/71] Improve description of git-branch -d and -D in man page. Some users expect that deleting a remote-tracking branch would prevent fetch from creating it again, so be explcit about that it's not the case. Also be a little more explicit about what fully merged means. Signed-off-by: Jan Hudec Signed-off-by: Junio C Hamano --- Documentation/git-branch.txt | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 37cb8b83b2..d64ad25028 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -45,17 +45,22 @@ to happen. With a `-d` or `-D` option, `` will be deleted. You may specify more than one branch for deletion. If the branch currently -has a reflog then the reflog will also be deleted. Use -r together with -d -to delete remote-tracking branches. +has a reflog then the reflog will also be deleted. + +Use -r together with -d to delete remote-tracking branches. Note, that it +only makes sense to delete remote-tracking branches if they no longer exist +in remote repository or if gitlink:git-fetch[1] was configured not to fetch +them again. See also 'prune' subcommand of gitlink:git-remote[1] for way to +clean up all obsolete remote-tracking branches. OPTIONS ------- -d:: - Delete a branch. The branch must be fully merged. + Delete a branch. The branch must be fully merged in HEAD. -D:: - Delete a branch irrespective of its index status. + Delete a branch irrespective of its merged status. -l:: Create the branch's reflog. This activates recording of @@ -153,9 +158,11 @@ $ git branch -d -r origin/todo origin/html origin/man <1> $ git branch -D test <2> ------------ + -<1> Delete remote-tracking branches "todo", "html", "man" -<2> Delete "test" branch even if the "master" branch does not have all -commits from test branch. +<1> Delete remote-tracking branches "todo", "html", "man". Next 'fetch' or +'pull' will create them again unless you configure them not to. See +gitlink:git-fetch[1]. +<2> Delete "test" branch even if the "master" branch (or whichever branch is +currently checked out) does not have all commits from test branch. Notes From 0673c96db92429d1c6dee528c71b067470b1e8f9 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 21 Nov 2007 02:19:34 -0500 Subject: [PATCH 57/71] Revert "t5516: test update of local refs on push" This reverts commit 09fba7a59d38d1cafaf33eadaf1d409c4113b30c. These tests are superseded by the ones in t5404 (added in 6fa92bf3 and 8736a848), which are more extensive and better organized. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t5516-fetch-push.sh | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 86f9b5346a..4fbd5b1f47 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -254,32 +254,4 @@ test_expect_success 'push with dry-run' ' check_push_result $old_commit heads/master ' -test_expect_success 'push updates local refs' ' - - rm -rf parent child && - mkdir parent && cd parent && git init && - echo one >foo && git add foo && git commit -m one && - cd .. && - git clone parent child && cd child && - echo two >foo && git commit -a -m two && - git push && - test $(git rev-parse master) = $(git rev-parse remotes/origin/master) - -' - -test_expect_success 'push does not update local refs on failure' ' - - rm -rf parent child && - mkdir parent && cd parent && git init && - echo one >foo && git add foo && git commit -m one && - echo exit 1 >.git/hooks/pre-receive && - chmod +x .git/hooks/pre-receive && - cd .. && - git clone parent child && cd child && - echo two >foo && git commit -a -m two || exit 1 - git push && exit 1 - test $(git rev-parse master) != $(git rev-parse remotes/origin/master) - -' - test_done From 9e42d6a1c53dadd409fab11cc76e0eba9ec15365 Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Wed, 21 Nov 2007 21:27:19 +0100 Subject: [PATCH 58/71] sha1_file.c: Fix size_t related printf format warnings The old way of fixing warnings did not succeed on MinGW. MinGW does not support C99 printf format strings for size_t [1]. But gcc on MinGW issues warnings if C99 printf format is not used. Hence, the old stragegy to avoid warnings fails. [1] http://www.mingw.org/MinGWiki/index.php/C99 This commits passes arguments of type size_t through a tiny helper functions that casts to the type expected by the format string. Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano --- sha1_file.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sha1_file.c b/sha1_file.c index 560c0e0d08..b0c24356ae 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -25,8 +25,10 @@ #ifdef NO_C99_FORMAT #define SZ_FMT "lu" +static unsigned long sz_fmt(size_t s) { return (unsigned long)s; } #else #define SZ_FMT "zu" +static size_t sz_fmt(size_t s) { return s; } #endif const unsigned char null_sha1[20]; @@ -423,9 +425,9 @@ void pack_report(void) "pack_report: getpagesize() = %10" SZ_FMT "\n" "pack_report: core.packedGitWindowSize = %10" SZ_FMT "\n" "pack_report: core.packedGitLimit = %10" SZ_FMT "\n", - (size_t) getpagesize(), - packed_git_window_size, - packed_git_limit); + sz_fmt(getpagesize()), + sz_fmt(packed_git_window_size), + sz_fmt(packed_git_limit)); fprintf(stderr, "pack_report: pack_used_ctr = %10u\n" "pack_report: pack_mmap_calls = %10u\n" @@ -435,7 +437,7 @@ void pack_report(void) pack_used_ctr, pack_mmap_calls, pack_open_windows, peak_pack_open_windows, - pack_mapped, peak_pack_mapped); + sz_fmt(pack_mapped), sz_fmt(peak_pack_mapped)); } static int check_packed_git_idx(const char *path, struct packed_git *p) From c67bbc55f0310de79647457b1fa2a5d3b1746488 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 23 Nov 2007 00:20:35 +0000 Subject: [PATCH 59/71] git checkout's reflog: even when detaching the HEAD, say from where When checking out another ref, the reflogs already record from which branch you switched. Do that also when switching to a detached HEAD. While at it, record also when coming _from_ a detached HEAD. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-checkout.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/git-checkout.sh b/git-checkout.sh index f80939da5a..f6d58ac044 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -266,7 +266,7 @@ if [ "$?" -eq 0 ]; then if test -n "$branch" then old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'` - GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from $old_branch_name to $branch" HEAD "refs/heads/$branch" + GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from ${old_branch_name:-$old} to $branch" HEAD "refs/heads/$branch" if test -n "$quiet" then true # nothing @@ -278,7 +278,8 @@ if [ "$?" -eq 0 ]; then fi elif test -n "$detached" then - git update-ref --no-deref -m "checkout: moving to $arg" HEAD "$detached" || + old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'` + git update-ref --no-deref -m "checkout: moving from ${old_branch_name:-$old} to $arg" HEAD "$detached" || die "Cannot detach HEAD" if test -n "$detach_warn" then From b3a4f8586b4ffa6c896cf0afb2ea49d64faf81ad Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 23 Nov 2007 01:11:35 +0000 Subject: [PATCH 60/71] bash completion: add diff options I use "git diff" (the porcelain) really often, and am almost as often annoyed that the completions do not know how to complete something simple as --cached. Now they do. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 599b2fc571..58e0e53cd6 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -551,6 +551,20 @@ _git_describe () _git_diff () { + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--cached --stat --numstat --shortstat --summary + --patch-with-stat --name-only --name-status --color + --no-color --color-words --no-renames --check + --full-index --binary --abbrev --diff-filter + --find-copies-harder --pickaxe-all --pickaxe-regex + --text --ignore-space-at-eol --ignore-space-change + --ignore-all-space --exit-code --quiet --ext-diff + --no-ext-diff" + return + ;; + esac __git_complete_file } From 38762c47d6442dc0ce0f45533f9151877c485337 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 28 Nov 2007 16:15:04 -0800 Subject: [PATCH 61/71] scripts: do not get confused with HEAD in work tree When you have a file called HEAD in your work tree, many commands that our scripts feed "HEAD" to would complain about the rev vs path ambiguity. A solution is to form command line more carefully by appending -- to them, which makes it clear that we mean HEAD rev not HEAD file. This patch would apply to maint. Signed-off-by: Junio C Hamano --- git-am.sh | 8 ++++---- git-filter-branch.sh | 2 +- git-merge-ours.sh | 2 +- git-rebase--interactive.sh | 4 ++-- git-rebase.sh | 4 ++-- git-stash.sh | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/git-am.sh b/git-am.sh index 32c46d7ed4..5792631d84 100755 --- a/git-am.sh +++ b/git-am.sh @@ -214,7 +214,7 @@ fi case "$resolved" in '') - files=$(git diff-index --cached --name-only HEAD) || exit + files=$(git diff-index --cached --name-only HEAD --) || exit if [ "$files" ]; then echo "Dirty index: cannot apply patches (dirty: $files)" >&2 exit 1 @@ -348,7 +348,7 @@ do case "$resolved$interactive" in tt) # This is used only for interactive view option. - git diff-index -p --cached HEAD >"$dotest/patch" + git diff-index -p --cached HEAD -- >"$dotest/patch" ;; esac esac @@ -409,7 +409,7 @@ do # trust what the user has in the index file and the # working tree. resolved= - git diff-index --quiet --cached HEAD && { + git diff-index --quiet --cached HEAD -- && { echo "No changes - did you forget to use 'git add'?" stop_here_user_resolve $this } @@ -431,7 +431,7 @@ do then # Applying the patch to an earlier tree and merging the # result may have produced the same tree as ours. - git diff-index --quiet --cached HEAD && { + git diff-index --quiet --cached HEAD -- && { echo No changes -- Patch already applied. go_next continue diff --git a/git-filter-branch.sh b/git-filter-branch.sh index ffcc408ee5..dbab1a9a4a 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -95,7 +95,7 @@ USAGE="[--env-filter ] [--tree-filter ] \ . git-sh-setup git diff-files --quiet && - git diff-index --cached --quiet HEAD || + git diff-index --cached --quiet HEAD -- || die "Cannot rewrite branch(es) with a dirty working directory." tempdir=.git-rewrite diff --git a/git-merge-ours.sh b/git-merge-ours.sh index c81a790aa6..29dba4ba3a 100755 --- a/git-merge-ours.sh +++ b/git-merge-ours.sh @@ -9,6 +9,6 @@ # because the current index is what we will be committing as the # merge result. -git diff-index --quiet --cached HEAD || exit 2 +git diff-index --quiet --cached HEAD -- || exit 2 exit 0 diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index d65df2cb80..ff38a22edf 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -52,7 +52,7 @@ require_clean_work_tree () { git rev-parse --verify HEAD > /dev/null && git update-index --refresh && git diff-files --quiet && - git diff-index --cached --quiet HEAD || + git diff-index --cached --quiet HEAD -- || die "Working tree is dirty" } @@ -331,7 +331,7 @@ do git rev-parse --verify HEAD > /dev/null && git update-index --refresh && git diff-files --quiet && - ! git diff-index --cached --quiet HEAD && + ! git diff-index --cached --quiet HEAD -- && . "$DOTEST"/author-script && export GIT_AUTHOR_NAME GIT_AUTHOR_NAME GIT_AUTHOR_DATE && git commit -F "$DOTEST"/message -e diff --git a/git-rebase.sh b/git-rebase.sh index b0c8ac1c8d..c9b284c751 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -60,7 +60,7 @@ continue_merge () { fi cmt=`cat "$dotest/current"` - if ! git diff-index --quiet HEAD + if ! git diff-index --quiet HEAD -- then if ! git-commit -C "$cmt" then @@ -253,7 +253,7 @@ fi # The tree must be really really clean. git update-index --refresh || exit -diff=$(git diff-index --cached --name-status -r HEAD) +diff=$(git diff-index --cached --name-status -r HEAD --) case "$diff" in ?*) echo "cannot rebase: your index is not up-to-date" echo "$diff" diff --git a/git-stash.sh b/git-stash.sh index 5bbda47b7b..77c94210b7 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -14,7 +14,7 @@ trap 'rm -f "$TMP-*"' 0 ref_stash=refs/stash no_changes () { - git diff-index --quiet --cached HEAD && + git diff-index --quiet --cached HEAD -- && git diff-files --quiet } From 686a4a06b679a202430ca81267ad6505e92d839a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 29 Nov 2007 01:11:46 -0800 Subject: [PATCH 62/71] dir.c: minor clean-up Replace handcrafted reallocation with ALLOC_GROW(). Reindent "file_exists()" helper function. Signed-off-by: Junio C Hamano --- dir.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/dir.c b/dir.c index 225fdfb52c..7c97483761 100644 --- a/dir.c +++ b/dir.c @@ -144,11 +144,7 @@ void add_exclude(const char *string, const char *base, x->flags |= EXC_FLAG_NOWILDCARD; if (*string == '*' && no_wildcard(string+1)) x->flags |= EXC_FLAG_ENDSWITH; - if (which->nr == which->alloc) { - which->alloc = alloc_nr(which->alloc); - which->excludes = xrealloc(which->excludes, - which->alloc * sizeof(x)); - } + ALLOC_GROW(which->excludes, which->nr + 1, which->alloc); which->excludes[which->nr++] = x; } @@ -690,11 +686,10 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i return dir->nr; } -int -file_exists(const char *f) +int file_exists(const char *f) { - struct stat sb; - return stat(f, &sb) == 0; + struct stat sb; + return stat(f, &sb) == 0; } /* From 63d285c8494d03a08600bb4453fcce077ecdd517 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 29 Nov 2007 02:17:44 -0800 Subject: [PATCH 63/71] per-directory-exclude: lazily read .gitignore files Operations that walk directories or trees, which potentially need to consult the .gitignore files, used to always try to open the .gitignore file every time they entered a new directory, even when they ended up not needing to call excluded() function to see if a path in the directory is ignored. This was done by push/pop exclude_per_directory() functions that managed the data in a stack. This changes the directory walking API to remove the need to call these two functions. Instead, the directory walk data structure caches the data used by excluded() function the last time, and lazily reuses it as much as possible. Among the data the last check used, the ones from deeper directories that the path we are checking is outside are discarded, data from the common leading directories are reused, and then the directories between the common directory and the directory the path being checked is in are checked for .gitignore file. This is very similar to the way gitattributes are handled. This API change also fixes "ls-files -c -i", which called excluded() without setting up the gitignore data via the old push/pop functions. Signed-off-by: Junio C Hamano --- dir.c | 101 +++++++++++++++++++++++++------------------------ dir.h | 32 +++++++++------- unpack-trees.c | 6 --- 3 files changed, 71 insertions(+), 68 deletions(-) diff --git a/dir.c b/dir.c index 7c97483761..d448902909 100644 --- a/dir.c +++ b/dir.c @@ -151,6 +151,7 @@ void add_exclude(const char *string, const char *base, static int add_excludes_from_file_1(const char *fname, const char *base, int baselen, + char **buf_p, struct exclude_list *which) { struct stat st; @@ -171,6 +172,8 @@ static int add_excludes_from_file_1(const char *fname, goto err; close(fd); + if (buf_p) + *buf_p = buf; buf[size++] = '\n'; entry = buf; for (i = 0; i < size; i++) { @@ -192,31 +195,63 @@ static int add_excludes_from_file_1(const char *fname, void add_excludes_from_file(struct dir_struct *dir, const char *fname) { - if (add_excludes_from_file_1(fname, "", 0, + if (add_excludes_from_file_1(fname, "", 0, NULL, &dir->exclude_list[EXC_FILE]) < 0) die("cannot use %s as an exclude file", fname); } -int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen) +static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) { - char exclude_file[PATH_MAX]; - struct exclude_list *el = &dir->exclude_list[EXC_DIRS]; - int current_nr = el->nr; + struct exclude_list *el; + struct exclude_stack *stk = NULL; + int current; - if (dir->exclude_per_dir) { - memcpy(exclude_file, base, baselen); - strcpy(exclude_file + baselen, dir->exclude_per_dir); - add_excludes_from_file_1(exclude_file, base, baselen, el); + if ((!dir->exclude_per_dir) || + (baselen + strlen(dir->exclude_per_dir) >= PATH_MAX)) + return; /* too long a path -- ignore */ + + /* Pop the ones that are not the prefix of the path being checked. */ + el = &dir->exclude_list[EXC_DIRS]; + while ((stk = dir->exclude_stack) != NULL) { + if (stk->baselen <= baselen && + !strncmp(dir->basebuf, base, stk->baselen)) + break; + dir->exclude_stack = stk->prev; + while (stk->exclude_ix < el->nr) + free(el->excludes[--el->nr]); + free(stk->filebuf); + free(stk); } - return current_nr; -} -void pop_exclude_per_directory(struct dir_struct *dir, int stk) -{ - struct exclude_list *el = &dir->exclude_list[EXC_DIRS]; + /* Read from the parent directories and push them down. */ + current = stk ? stk->baselen : -1; + while (current < baselen) { + struct exclude_stack *stk = xcalloc(1, sizeof(*stk)); + const char *cp; - while (stk < el->nr) - free(el->excludes[--el->nr]); + if (current < 0) { + cp = base; + current = 0; + } + else { + cp = strchr(base + current + 1, '/'); + if (!cp) + die("oops in prep_exclude"); + cp++; + } + stk->prev = dir->exclude_stack; + stk->baselen = cp - base; + stk->exclude_ix = el->nr; + memcpy(dir->basebuf + current, base + current, + stk->baselen - current); + strcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir); + add_excludes_from_file_1(dir->basebuf, + dir->basebuf, stk->baselen, + &stk->filebuf, el); + dir->exclude_stack = stk; + current = stk->baselen; + } + dir->basebuf[baselen] = '\0'; } /* Scan the list and let the last match determines the fate. @@ -283,6 +318,7 @@ int excluded(struct dir_struct *dir, const char *pathname) const char *basename = strrchr(pathname, '/'); basename = (basename) ? basename+1 : pathname; + prep_exclude(dir, pathname, basename-pathname); for (st = EXC_CMDL; st <= EXC_FILE; st++) { switch (excluded_1(pathname, pathlen, basename, &dir->exclude_list[st])) { case 0: @@ -500,13 +536,10 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co int contents = 0; if (fdir) { - int exclude_stk; struct dirent *de; char fullname[PATH_MAX + 1]; memcpy(fullname, base, baselen); - exclude_stk = push_exclude_per_directory(dir, base, baselen); - while ((de = readdir(fdir)) != NULL) { int len, dtype; int exclude; @@ -580,8 +613,6 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co } exit_early: closedir(fdir); - - pop_exclude_per_directory(dir, exclude_stk); } return contents; @@ -650,37 +681,9 @@ static void free_simplify(struct path_simplify *simplify) int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec) { struct path_simplify *simplify = create_simplify(pathspec); - char *pp = NULL; - - /* - * Make sure to do the per-directory exclude for all the - * directories leading up to our base. - */ - if (baselen) { - if (dir->exclude_per_dir) { - char *p; - pp = xmalloc(baselen+1); - memcpy(pp, base, baselen+1); - p = pp; - while (1) { - char save = *p; - *p = 0; - push_exclude_per_directory(dir, pp, p-pp); - *p++ = save; - if (!save) - break; - p = strchr(p, '/'); - if (p) - p++; - else - p = pp + baselen; - } - } - } read_directory_recursive(dir, path, base, baselen, 0, simplify); free_simplify(simplify); - free(pp); qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name); qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name); return dir->nr; diff --git a/dir.h b/dir.h index 82009dc13e..d8814dccb2 100644 --- a/dir.h +++ b/dir.h @@ -1,17 +1,6 @@ #ifndef DIR_H #define DIR_H -/* - * We maintain three exclude pattern lists: - * EXC_CMDL lists patterns explicitly given on the command line. - * EXC_DIRS lists patterns obtained from per-directory ignore files. - * EXC_FILE lists patterns from fallback ignore files. - */ -#define EXC_CMDL 0 -#define EXC_DIRS 1 -#define EXC_FILE 2 - - struct dir_entry { unsigned int len; char name[FLEX_ARRAY]; /* more */ @@ -34,6 +23,13 @@ struct exclude_list { } **excludes; }; +struct exclude_stack { + struct exclude_stack *prev; + char *filebuf; + int baselen; + int exclude_ix; +}; + struct dir_struct { int nr, alloc; int ignored_nr, ignored_alloc; @@ -48,6 +44,18 @@ struct dir_struct { /* Exclude info */ const char *exclude_per_dir; struct exclude_list exclude_list[3]; + /* + * We maintain three exclude pattern lists: + * EXC_CMDL lists patterns explicitly given on the command line. + * EXC_DIRS lists patterns obtained from per-directory ignore files. + * EXC_FILE lists patterns from fallback ignore files. + */ +#define EXC_CMDL 0 +#define EXC_DIRS 1 +#define EXC_FILE 2 + + struct exclude_stack *exclude_stack; + char basebuf[PATH_MAX]; }; extern int common_prefix(const char **pathspec); @@ -58,8 +66,6 @@ extern int common_prefix(const char **pathspec); extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen); extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen, const char **pathspec); -extern int push_exclude_per_directory(struct dir_struct *, const char *, int); -extern void pop_exclude_per_directory(struct dir_struct *, int); extern int excluded(struct dir_struct *, const char *); extern void add_excludes_from_file(struct dir_struct *, const char *fname); diff --git a/unpack-trees.c b/unpack-trees.c index aea16adde8..e9eb795d64 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -71,12 +71,8 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, int remove; int baselen = strlen(base); int src_size = len + 1; - int i_stk = i_stk; int retval = 0; - if (o->dir) - i_stk = push_exclude_per_directory(o->dir, base, strlen(base)); - do { int i; const char *first; @@ -255,8 +251,6 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, } while (1); leave_directory: - if (o->dir) - pop_exclude_per_directory(o->dir, i_stk); return retval; } From d6617c7cdebc20fe007e983f70b44a223dd52c28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Goddard=20Rosa?= Date: Thu, 22 Nov 2007 20:22:23 -0200 Subject: [PATCH 64/71] Error out when user doesn't have access permission to the repository MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Goddard Rosa --- builtin-fetch--tool.c | 12 ++++++++++-- builtin-fetch.c | 19 ++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c index ed60847d9f..7460ab7fce 100644 --- a/builtin-fetch--tool.c +++ b/builtin-fetch--tool.c @@ -511,10 +511,14 @@ int cmd_fetch__tool(int argc, const char **argv, const char *prefix) if (!strcmp("append-fetch-head", argv[1])) { int result; FILE *fp; + char *filename; if (argc != 8) return error("append-fetch-head takes 6 args"); - fp = fopen(git_path("FETCH_HEAD"), "a"); + filename = git_path("FETCH_HEAD"); + fp = fopen(filename, "a"); + if (!fp) + return error("cannot open %s: %s\n", filename, strerror(errno)); result = append_fetch_head(fp, argv[2], argv[3], argv[4], argv[5], argv[6], !!argv[7][0], @@ -525,10 +529,14 @@ int cmd_fetch__tool(int argc, const char **argv, const char *prefix) if (!strcmp("native-store", argv[1])) { int result; FILE *fp; + char *filename; if (argc != 5) return error("fetch-native-store takes 3 args"); - fp = fopen(git_path("FETCH_HEAD"), "a"); + filename = git_path("FETCH_HEAD"); + fp = fopen(filename, "a"); + if (!fp) + return error("cannot open %s: %s\n", filename, strerror(errno)); result = fetch_native_store(fp, argv[2], argv[3], argv[4], verbose, force); fclose(fp); diff --git a/builtin-fetch.c b/builtin-fetch.c index 31e138eab8..de9947e7ac 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -255,7 +255,7 @@ static int update_local_ref(struct ref *ref, } } -static void store_updated_refs(const char *url, struct ref *ref_map) +static int store_updated_refs(const char *url, struct ref *ref_map) { FILE *fp; struct commit *commit; @@ -263,8 +263,11 @@ static void store_updated_refs(const char *url, struct ref *ref_map) char note[1024]; const char *what, *kind; struct ref *rm; + char *filename = git_path("FETCH_HEAD"); - fp = fopen(git_path("FETCH_HEAD"), "a"); + fp = fopen(filename, "a"); + if (!fp) + return error("cannot open %s: %s\n", filename, strerror(errno)); for (rm = ref_map; rm; rm = rm->next) { struct ref *ref = NULL; @@ -335,6 +338,7 @@ static void store_updated_refs(const char *url, struct ref *ref_map) } } fclose(fp); + return 0; } /* @@ -404,7 +408,7 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map) if (ret) ret = transport_fetch_refs(transport, ref_map); if (!ret) - store_updated_refs(transport->url, ref_map); + ret |= store_updated_refs(transport->url, ref_map); transport_unlock_pack(transport); return ret; } @@ -487,8 +491,13 @@ static int do_fetch(struct transport *transport, die("Don't know how to fetch from %s", transport->url); /* if not appending, truncate FETCH_HEAD */ - if (!append) - fclose(fopen(git_path("FETCH_HEAD"), "w")); + if (!append) { + char *filename = git_path("FETCH_HEAD"); + FILE *fp = fopen(filename, "w"); + if (!fp) + return error("cannot open %s: %s\n", filename, strerror(errno)); + fclose(fp); + } ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags); From 346d203bc3931c33f2e40093ee9325ab0334b9fe Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 23 Nov 2007 19:04:52 +0100 Subject: [PATCH 65/71] Add config_int() method to the Git perl module Integer variables can have optional 'k', 'm' or 'g' suffix. config_int() method will return simple decimal number, taking care of those suffixes. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- perl/Git.pm | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/perl/Git.pm b/perl/Git.pm index dca92c8adb..7468460f9a 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -549,6 +549,37 @@ sub config_bool { }; } +=item config_int ( VARIABLE ) + +Retrieve the integer configuration C. The return value +is simple decimal number. An optional value suffix of 'k', 'm', +or 'g' in the config file will cause the value to be multiplied +by 1024, 1048576 (1024^2), or 1073741824 (1024^3) prior to output. +It would return C if configuration variable is not defined, + +Must be called on a repository instance. + +This currently wraps command('config') so it is not so fast. + +=cut + +sub config_int { + my ($self, $var) = @_; + $self->repo_path() + or throw Error::Simple("not a repository"); + + try { + return $self->command_oneline('config', '--int', '--get', $var); + } catch Git::Error::Command with { + my $E = shift; + if ($E->value() == 1) { + # Key not found. + return undef; + } else { + throw $E; + } + }; +} =item ident ( TYPE | IDENTSTR ) From fdd7d48d6a09c593a68c983c450443c1e55705e6 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 27 Nov 2007 13:10:19 +0100 Subject: [PATCH 66/71] t7003-filter-branch: Fix test of a failing --msg-filter. The test passed for the wrong reason: If the script given to --msg-filter fails, it is expected that git-filter-branch aborts. But the test forgot to tell the branch name to rewrite, and so git-filter-branch failed due to incorrect usage. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- t/t7003-filter-branch.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index 2089351f7d..5f60b22d87 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -114,7 +114,7 @@ test_expect_success 'use index-filter to move into a subdirectory' ' test_expect_success 'stops when msg filter fails' ' old=$(git rev-parse HEAD) && - ! git-filter-branch -f --msg-filter false && + ! git-filter-branch -f --msg-filter false HEAD && test $old = $(git rev-parse HEAD) && rm -rf .git-rewrite ' From 28391a80a94d2b59d1d21f8264fe5dab91d77249 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 29 Nov 2007 01:02:53 +0000 Subject: [PATCH 67/71] receive-pack: allow deletion of corrupt refs Occasionally, in some setups (*cough* forks on repo.or.cz *cough*) some refs go stale, e.g. when the forkee rebased and lost some objects needed by the fork. The quick & dirty way to deal with those refs is to delete them and push them again. However, git-push first would first fetch the current commit name for the ref, would receive a null sha1 since the ref does not point to a valid object, then tell receive-pack that it should delete the ref with this commit name. delete_ref() would be subsequently be called, and check that resolve_ref() (which does _not_ check for validity of the object) returns the same commit name. Which would fail. The proper fix is to avoid corrupting repositories, but in the meantime this is a good fix in any case. Incidentally, some instances of "cd .." in the test cases were fixed, so that subsequent test cases run in t/trash/ irrespective of the outcome of the previous test cases. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- receive-pack.c | 4 ++++ t/t5516-fetch-push.sh | 45 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/receive-pack.c b/receive-pack.c index ed44b897f6..fba4cf8235 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -200,6 +200,10 @@ static const char *update(struct command *cmd) } if (is_null_sha1(new_sha1)) { + if (!parse_object(old_sha1)) { + warning ("Allowing deletion of corrupt ref."); + old_sha1 = NULL; + } if (delete_ref(name, old_sha1)) { error("failed to delete %s", name); return "failed to delete"; diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 4fbd5b1f47..987c9d21ca 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -247,11 +247,50 @@ test_expect_success 'push with colon-less refspec (4)' ' test_expect_success 'push with dry-run' ' mk_test heads/master && - cd testrepo && - old_commit=$(git show-ref -s --verify refs/heads/master) && - cd .. && + (cd testrepo && + old_commit=$(git show-ref -s --verify refs/heads/master)) && git push --dry-run testrepo && check_push_result $old_commit heads/master ' +test_expect_success 'push updates local refs' ' + + rm -rf parent child && + mkdir parent && + (cd parent && git init && + echo one >foo && git add foo && git commit -m one) && + git clone parent child && + (cd child && + echo two >foo && git commit -a -m two && + git push && + test $(git rev-parse master) = $(git rev-parse remotes/origin/master)) + +' + +test_expect_success 'push does not update local refs on failure' ' + + rm -rf parent child && + mkdir parent && + (cd parent && git init && + echo one >foo && git add foo && git commit -m one && + echo exit 1 >.git/hooks/pre-receive && + chmod +x .git/hooks/pre-receive) && + git clone parent child && + (cd child && + echo two >foo && git commit -a -m two && + ! git push && + test $(git rev-parse master) != \ + $(git rev-parse remotes/origin/master)) + +' + +test_expect_success 'allow deleting an invalid remote ref' ' + + pwd && + rm -f testrepo/.git/objects/??/* && + git push testrepo :refs/heads/master && + (cd testrepo && ! git rev-parse --verify refs/heads/master) + +' + test_done From 67d232426b8858b31e54a9b6a5a90916690d1153 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 30 Nov 2007 17:22:12 -0500 Subject: [PATCH 68/71] cvsimport: fix usage of cvsimport.module There were two problems: 1. We only look at the config variable if there is no module given on the command line. We checked this by comparing @ARGV == 0. However, at the time of the comparison, we have not yet parsed the dashed options, meaning that "git cvsimport" would read the variable but "git cvsimport -a" would not. This is fixed by simply moving the check after the call to getopt. 2. If the config variable did not exist, we were adding an empty string to @ARGV. The rest of the script, rather than barfing for insufficient input, would then try to import the module '', leading to rather confusing error messages. Based on patch from Emanuele Giaquinta. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- git-cvsimport.perl | 8 ++++---- t/t9600-cvsimport.sh | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index d29b547d26..d565091c35 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -108,10 +108,6 @@ sub read_repo_config { } } } - if (@ARGV == 0) { - chomp(my $module = `git-repo-config --get cvsimport.module`); - push(@ARGV, $module); - } } my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:"; @@ -119,6 +115,10 @@ read_repo_config($opts); getopts($opts) or usage(); usage if $opt_h; +if (@ARGV == 0) { + chomp(my $module = `git-repo-config --get cvsimport.module`); + push(@ARGV, $module) if $? == 0; +} @ARGV <= 1 or usage("You can't specify more than one CVS module"); if ($opt_d) { diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh index 3338d447c5..29fee2dd13 100755 --- a/t/t9600-cvsimport.sh +++ b/t/t9600-cvsimport.sh @@ -98,4 +98,25 @@ test_expect_success 'update git module' ' ' +test_expect_success 'update cvs module' ' + + cd module-cvs && + echo 1 >tick && + cvs add tick && + cvs commit -m 1 + cd .. + +' + +test_expect_success 'cvsimport.module config works' ' + + cd module-git && + git config cvsimport.module module && + git cvsimport -a -z0 && + git merge origin && + cd .. && + git diff module-cvs/tick module-git/tick + +' + test_done From 10455d2a955a29db1809be139177e4e298771eb0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 30 Nov 2007 11:35:23 +0000 Subject: [PATCH 69/71] Replace the word 'update-cache' by 'update-index' everywhere Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Documentation/user-manual.txt | 2 +- Makefile | 2 +- configure.ac | 2 +- t/t4000-diff-format.sh | 2 +- t/t4001-diff-rename.sh | 2 +- t/t4100/t-apply-1.patch | 2 +- t/t4100/t-apply-2.patch | 2 +- t/t4100/t-apply-5.patch | 2 +- t/t4100/t-apply-6.patch | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index b0a873bd7f..159de30e19 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -3706,7 +3706,7 @@ should use the `--remove` and `--add` flags respectively. NOTE! A `--remove` flag does 'not' mean that subsequent filenames will necessarily be removed: if the files still exist in your directory structure, the index will be updated with their new status, not -removed. The only thing `--remove` means is that update-cache will be +removed. The only thing `--remove` means is that update-index will be considering a removed file to be a valid thing, and if the file really does not exist any more, it will update the index accordingly. diff --git a/Makefile b/Makefile index e0cfff35b6..c72d40ada3 100644 --- a/Makefile +++ b/Makefile @@ -107,7 +107,7 @@ all:: # times (my ext3 doesn't). # # Define USE_STDEV below if you want git to care about the underlying device -# change being considered an inode change from the update-cache perspective. +# change being considered an inode change from the update-index perspective. # # Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8 # diff --git a/configure.ac b/configure.ac index ab516db379..8dfe9a0e12 100644 --- a/configure.ac +++ b/configure.ac @@ -397,7 +397,7 @@ GIT_PARSE_WITH(iconv)) # times (my ext3 doesn't). # # Define USE_STDEV below if you want git to care about the underlying device -# change being considered an inode change from the update-cache perspective. +# change being considered an inode change from the update-index perspective. ## Output files diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh index 7d92ae3e99..c44b27aeb2 100755 --- a/t/t4000-diff-format.sh +++ b/t/t4000-diff-format.sh @@ -16,7 +16,7 @@ cat path0 >path1 chmod +x path1 test_expect_success \ - 'update-cache --add two files with and without +x.' \ + 'update-index --add two files with and without +x.' \ 'git update-index --add path0 path1' mv path0 path0- diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh index 063e79257a..2fe50bc7ce 100755 --- a/t/t4001-diff-rename.sh +++ b/t/t4001-diff-rename.sh @@ -27,7 +27,7 @@ Line 15 ' test_expect_success \ - 'update-cache --add a file.' \ + 'update-index --add a file.' \ 'git update-index --add path0' test_expect_success \ diff --git a/t/t4100/t-apply-1.patch b/t/t4100/t-apply-1.patch index de587517f4..90ab54f0f5 100644 --- a/t/t4100/t-apply-1.patch +++ b/t/t4100/t-apply-1.patch @@ -90,7 +90,7 @@ diff --git a/Documentation/git.txt b/Documentation/git.txt diff --git a/Makefile b/Makefile --- a/Makefile +++ b/Makefile -@@ -30,7 +30,7 @@ PROG= git-update-cache git-diff-files +@@ -30,7 +30,7 @@ PROG= git-update-index git-diff-files git-checkout-cache git-diff-tree git-rev-tree git-ls-files \ git-check-files git-ls-tree git-merge-base git-merge-cache \ git-unpack-file git-export git-diff-cache git-convert-cache \ diff --git a/t/t4100/t-apply-2.patch b/t/t4100/t-apply-2.patch index cfdc80885b..f5c7d601fc 100644 --- a/t/t4100/t-apply-2.patch +++ b/t/t4100/t-apply-2.patch @@ -9,7 +9,7 @@ diff --git a/Makefile b/Makefile - git-deltafy-script + git-deltafy-script git-fetch-script - PROG= git-update-cache git-diff-files git-init-db git-write-tree \ + PROG= git-update-index git-diff-files git-init-db git-write-tree \ git-read-tree git-commit-tree git-cat-file git-fsck-cache \ diff --git a/git-pull-script b/git-fetch-script similarity index 87% diff --git a/t/t4100/t-apply-5.patch b/t/t4100/t-apply-5.patch index de11623d1b..5f6ddc1059 100644 --- a/t/t4100/t-apply-5.patch +++ b/t/t4100/t-apply-5.patch @@ -200,7 +200,7 @@ diff a/Documentation/git.txt b/Documentation/git.txt diff a/Makefile b/Makefile --- a/Makefile +++ b/Makefile -@@ -30,7 +30,7 @@ PROG= git-update-cache git-diff-files +@@ -30,7 +30,7 @@ PROG= git-update-index git-diff-files git-checkout-cache git-diff-tree git-rev-tree git-ls-files \ git-check-files git-ls-tree git-merge-base git-merge-cache \ git-unpack-file git-export git-diff-cache git-convert-cache \ diff --git a/t/t4100/t-apply-6.patch b/t/t4100/t-apply-6.patch index d9753637fc..a72729a712 100644 --- a/t/t4100/t-apply-6.patch +++ b/t/t4100/t-apply-6.patch @@ -8,7 +8,7 @@ diff a/Makefile b/Makefile - git-deltafy-script + git-deltafy-script git-fetch-script - PROG= git-update-cache git-diff-files git-init-db git-write-tree \ + PROG= git-update-index git-diff-files git-init-db git-write-tree \ git-read-tree git-commit-tree git-cat-file git-fsck-cache \ diff a/git-fetch-script b/git-fetch-script --- /dev/null From 32d75d29f99cca8e0874b1bdf94ded48b576c906 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 29 Nov 2007 13:30:13 -0800 Subject: [PATCH 70/71] Fix a pathological case in git detecting proper renames Kumar Gala had a case in the u-boot archive with multiple renames of files with identical contents, and git would turn those into multiple "copy" operations of one of the sources, and just deleting the other sources. This patch makes the git exact rename detection prefer to spread out the renames over the multiple sources, rather than do multiple copies of one source. NOTE! The changes are a bit larger than required, because I also renamed the variables named "one" and "two" to "target" and "source" respectively. That makes the logic easier to follow, especially as the "one" was illogically the target and not the soruce, for purely historical reasons (this piece of code used to traverse over sources and targets in the wrong order, and when we fixed that, we didn't fix the names back then. So I fixed them now). The important part of this change is just the trivial score calculations for when files have identical contents: /* Give higher scores to sources that haven't been used already */ score = !source->rename_used; score += basename_same(source, target); and when we have multiple choices we'll now pick the choice that gets the best rename score, rather than only looking at whether the basename matched. It's worth noting a few gotchas: - this scoring is currently only done for the "exact match" case. In particular, in Kumar's example, even after this patch, the inexact match case is still done as a copy+delete rather than as two renames: delete mode 100644 board/cds/mpc8555cds/u-boot.lds copy board/{cds => freescale}/mpc8541cds/u-boot.lds (97%) rename board/{cds/mpc8541cds => freescale/mpc8555cds}/u-boot.lds (97%) because apparently the "cds/mpc8541cds/u-boot.lds" copy looked a bit more similar to both end results. That said, I *suspect* we just have the exact same issue there - the similarity analysis just gave identical (or at least very _close_ to identical) similarity points, and we do not have any logic to prefer multiple renames over a copy/delete there. That is a separate patch. - When you have identical contents and identical basenames, the actual entry that is chosen is still picked fairly "at random" for the first one (but the subsequent ones will prefer entries that haven't already been used). It's not actually really random, in that it actually depends on the relative alphabetical order of the files (which in turn will have impacted the order that the entries got hashed!), so it gives consistent results that can be explained. But I wanted to point it out as an issue for when anybody actually does cross-renames. In Kumar's case the choice is the right one (and for a single normal directory rename it should always be, since the relative alphabetical sorting of the files will be identical), and we now get: rename board/{cds => freescale}/mpc8541cds/init.S (100%) rename board/{cds => freescale}/mpc8548cds/init.S (100%) which is the "expected" answer. However, it might still be better to change the pedantic "exact same basename" on/off choice into a more graduated "how similar are the pathnames" scoring situation, in order to be more likely to get the exact rename choice that people *expect* to see, rather than other alternatives that may *technically* be equally good, but are surprising to a human. It's also unclear whether we should consider "basenames are equal" or "have already used this as a source" to be more important. This gives them equal weight, but I suspect we might want to just multiple the "basenames are equal" weight by two, or something, to prefer equal basenames even if that causes a copy/delete pair. I dunno. Anyway, what I'm just saying in a really long-winded manner is that I think this is right as-is, but it's not the complete solution, and it may want some further tweaking in the future. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diffcore-rename.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/diffcore-rename.c b/diffcore-rename.c index f9ebea5640..f64294e3cf 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -244,28 +244,35 @@ static int find_identical_files(struct file_similarity *src, * Walk over all the destinations ... */ do { - struct diff_filespec *one = dst->filespec; + struct diff_filespec *target = dst->filespec; struct file_similarity *p, *best; - int i = 100; + int i = 100, best_score = -1; /* * .. to find the best source match */ best = NULL; for (p = src; p; p = p->next) { - struct diff_filespec *two = p->filespec; + int score; + struct diff_filespec *source = p->filespec; /* False hash collission? */ - if (hashcmp(one->sha1, two->sha1)) + if (hashcmp(source->sha1, target->sha1)) continue; /* Non-regular files? If so, the modes must match! */ - if (!S_ISREG(one->mode) || !S_ISREG(two->mode)) { - if (one->mode != two->mode) + if (!S_ISREG(source->mode) || !S_ISREG(target->mode)) { + if (source->mode != target->mode) continue; } - best = p; - if (basename_same(one, two)) - break; + /* Give higher scores to sources that haven't been used already */ + score = !source->rename_used; + score += basename_same(source, target); + if (score > best_score) { + best = p; + best_score = score; + if (score == 2) + break; + } /* Too many identical alternatives? Pick one */ if (!--i) From 9ae8fcb36ac9fde8e048a304cc3717f2c7914e78 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 29 Nov 2007 16:41:09 -0800 Subject: [PATCH 71/71] Fix a pathological case in git detecting proper renames On Thu, 29 Nov 2007, Jeff King wrote: > > I think it will get worse, because you are simultaneously calculating > all of the similarity scores bit by bit rather than doing a loop. Though > perhaps you mean at the end you will end up with a list of src/dst pairs > sorted by score, and you can loop over that. Well, after thinking about this a bit, I think there's a solution that may work well with the current thing too: instead of looping just *once* over the list of rename pairs, loop twice - and simply refuse to do copies on the first loop. This trivial patch does that, and turns Kumar's test-case into a perfect rename list. It's not pretty, it's not smart, but it seems to work. There's something to be said for keeping it simple and stupid. And it should not be nearly as expensive as it may _look_. Yes, the loop is "(i = 0; i < num_create * num_src; i++)", but the important part is that the whole array is sorted by rename score, and we have a if (mx[i].score < minimum_score) break; in it, so uthe loop actually would tend to terminate rather quickly. Anyway, Kumar, the thing to take away from this is: - git really doesn't even *care* about the whole "rename detection" internally, and any commits you have done with renames are totally independent of the heuristics we then use to *show* the renames. - the rename detection really is for just two reasons: (a) keep humans happy, and keep the diffs small and (b) help automatic merging across renames. So getting renames right is certainly good, but it's more of a "politeness" issue than a "correctness" issue, although the merge portion of it does matter a lot sometimes. - the important thing here is that you can commit your changes and not worry about them being somehow "corrupted" by lack of rename detection, even if you commit them with a version of git that doesn't do rename detection the way you expected it. The rename detection is an "after-the-fact" thing, not something that actually gets saved in the repository, which is why we can change the heuristics _after_ seeing examples, and the examples magically correct themselves! - try out the two patches I've posted, and see if they work for you. They pass the test-suite, and the output for your example commit looks sane, but hey, if you have other test-cases, try them out. Here's Kumar's pretty diffstat with both my patches: Makefile | 6 +++--- board/{cds => freescale}/common/cadmus.c | 0 board/{cds => freescale}/common/cadmus.h | 0 board/{cds => freescale}/common/eeprom.c | 0 board/{cds => freescale}/common/eeprom.h | 0 board/{cds => freescale}/common/ft_board.c | 0 board/{cds => freescale}/common/via.c | 0 board/{cds => freescale}/common/via.h | 0 board/{cds => freescale}/mpc8541cds/Makefile | 0 board/{cds => freescale}/mpc8541cds/config.mk | 0 board/{cds => freescale}/mpc8541cds/init.S | 0 board/{cds => freescale}/mpc8541cds/mpc8541cds.c | 0 board/{cds => freescale}/mpc8541cds/u-boot.lds | 4 ++-- board/{cds => freescale}/mpc8548cds/Makefile | 0 board/{cds => freescale}/mpc8548cds/config.mk | 0 board/{cds => freescale}/mpc8548cds/init.S | 0 board/{cds => freescale}/mpc8548cds/mpc8548cds.c | 0 board/{cds => freescale}/mpc8548cds/u-boot.lds | 4 ++-- board/{cds => freescale}/mpc8555cds/Makefile | 0 board/{cds => freescale}/mpc8555cds/config.mk | 0 board/{cds => freescale}/mpc8555cds/init.S | 0 board/{cds => freescale}/mpc8555cds/mpc8555cds.c | 0 board/{cds => freescale}/mpc8555cds/u-boot.lds | 4 ++-- 23 files changed, 9 insertions(+), 9 deletions(-) and here it is before: Makefile | 6 +- board/cds/mpc8548cds/Makefile | 60 ----- board/cds/mpc8555cds/Makefile | 60 ----- board/cds/mpc8555cds/init.S | 255 -------------------- board/cds/mpc8555cds/u-boot.lds | 150 ------------ board/{cds => freescale}/common/cadmus.c | 0 board/{cds => freescale}/common/cadmus.h | 0 board/{cds => freescale}/common/eeprom.c | 0 board/{cds => freescale}/common/eeprom.h | 0 board/{cds => freescale}/common/ft_board.c | 0 board/{cds => freescale}/common/via.c | 0 board/{cds => freescale}/common/via.h | 0 board/{cds => freescale}/mpc8541cds/Makefile | 0 board/{cds => freescale}/mpc8541cds/config.mk | 0 board/{cds => freescale}/mpc8541cds/init.S | 0 board/{cds => freescale}/mpc8541cds/mpc8541cds.c | 0 board/{cds => freescale}/mpc8541cds/u-boot.lds | 4 +- .../mpc8541cds => freescale/mpc8548cds}/Makefile | 0 board/{cds => freescale}/mpc8548cds/config.mk | 0 board/{cds => freescale}/mpc8548cds/init.S | 0 board/{cds => freescale}/mpc8548cds/mpc8548cds.c | 0 board/{cds => freescale}/mpc8548cds/u-boot.lds | 4 +- .../mpc8541cds => freescale/mpc8555cds}/Makefile | 0 board/{cds => freescale}/mpc8555cds/config.mk | 0 .../mpc8541cds => freescale/mpc8555cds}/init.S | 0 board/{cds => freescale}/mpc8555cds/mpc8555cds.c | 0 .../mpc8541cds => freescale/mpc8555cds}/u-boot.lds | 4 +- 27 files changed, 9 insertions(+), 534 deletions(-) so it certainly makes the diffs prettier. Linus Signed-off-by: Junio C Hamano --- diffcore-rename.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/diffcore-rename.c b/diffcore-rename.c index f64294e3cf..3d377251be 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -495,6 +495,19 @@ void diffcore_rename(struct diff_options *options) } /* cost matrix sorted by most to least similar pair */ qsort(mx, num_create * num_src, sizeof(*mx), score_compare); + for (i = 0; i < num_create * num_src; i++) { + struct diff_rename_dst *dst = &rename_dst[mx[i].dst]; + struct diff_filespec *src; + if (dst->pair) + continue; /* already done, either exact or fuzzy. */ + if (mx[i].score < minimum_score) + break; /* there is no more usable pair. */ + src = rename_src[mx[i].src].one; + if (src->rename_used) + continue; + record_rename_pair(mx[i].dst, mx[i].src, mx[i].score); + rename_count++; + } for (i = 0; i < num_create * num_src; i++) { struct diff_rename_dst *dst = &rename_dst[mx[i].dst]; if (dst->pair)