From: Junio C Hamano Date: Wed, 5 Dec 2007 01:07:10 +0000 (-0800) Subject: Merge branch 'sp/refspec-match' X-Git-Tag: v1.5.4-rc0~79 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/9bbe6db85f368fc4f75cc9f6c4bfaf2269bf0068?hp=-c Merge branch 'sp/refspec-match' * sp/refspec-match: refactor fetch's ref matching to use refname_match() push: use same rules as git-rev-parse to resolve refspecs add refname_match() push: support pushing HEAD to real branch name --- 9bbe6db85f368fc4f75cc9f6c4bfaf2269bf0068 diff --combined builtin-push.c index 41df717f84,54fba0e832..c8cb63e238 --- a/builtin-push.c +++ b/builtin-push.c @@@ -10,7 -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, }; @@@ -44,6 -44,15 +44,15 @@@ static void set_refspecs(const char **r strcat(tag, refs[i]); ref = tag; } + if (!strcmp("HEAD", ref)) { + unsigned char sha1_dummy[20]; + ref = resolve_ref(ref, sha1_dummy, 1, NULL); + if (!ref) + die("HEAD cannot be resolved."); + if (prefixcmp(ref, "refs/heads/")) + die("HEAD cannot be resolved to branch."); + ref = xstrdup(ref + 11); + } add_refspec(ref); } } @@@ -91,7 -100,6 +100,7 @@@ int cmd_push(int argc, const char **arg { int flags = 0; int all = 0; + int mirror = 0; int dry_run = 0; int force = 0; int tags = 0; @@@ -101,7 -109,6 +110,7 @@@ 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"), @@@ -123,21 -130,13 +132,21 @@@ 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 --combined cache.h index ed8be06299,cb8f3cabbb..ff2a1c0778 --- a/cache.h +++ b/cache.h @@@ -192,13 -192,6 +192,13 @@@ enum object_type OBJ_MAX, }; +static inline enum object_type object_type(unsigned int mode) +{ + return S_ISDIR(mode) ? OBJ_TREE : + S_ISGITLINK(mode) ? OBJ_COMMIT : + OBJ_BLOB; +} + #define GIT_DIR_ENVIRONMENT "GIT_DIR" #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE" #define DEFAULT_GIT_DIR_ENVIRONMENT ".git" @@@ -297,7 -290,6 +297,7 @@@ extern int refresh_index(struct index_s struct lock_file { struct lock_file *next; + int fd; pid_t owner; char on_list; char filename[PATH_MAX]; @@@ -423,6 -415,10 +423,10 @@@ extern const char *resolve_ref(const ch extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); + extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules); + extern const char *ref_rev_parse_rules[]; + extern const char *ref_fetch_rules[]; + extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg); extern int validate_headref(const char *ref); @@@ -507,20 -503,8 +511,20 @@@ struct ref struct ref *next; unsigned char old_sha1[20]; unsigned char new_sha1[20]; - unsigned char force; - unsigned char merge; + unsigned int force:1, + merge:1, + nonfastforward:1, + deletion:1; + enum { + REF_STATUS_NONE = 0, + REF_STATUS_OK, + REF_STATUS_REJECT_NONFASTFORWARD, + REF_STATUS_REJECT_NODELETE, + REF_STATUS_UPTODATE, + REF_STATUS_REMOTE_REJECT, + REF_STATUS_EXPECTING_REPORT, + } status; + char *remote_status; struct ref *peer_ref; /* when renaming */ char name[FLEX_ARRAY]; /* more */ }; @@@ -529,10 -513,8 +533,10 @@@ #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], 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); @@@ -578,7 -560,6 +582,7 @@@ extern int git_config_bool(const char * extern int git_config_set(const char *, const char *); extern int git_config_set_multivar(const char *, const char *, const char *, int); extern int git_config_rename_section(const char *, const char *); +extern const char *git_etc_gitconfig(void); extern int check_repository_format_version(const char *var, const char *value); #define MAX_GITNAME (1000) @@@ -620,7 -601,7 +624,7 @@@ extern void alloc_report(void) /* trace.c */ extern void trace_printf(const char *format, ...); -extern void trace_argv_printf(const char **argv, int count, const char *format, ...); +extern void trace_argv_printf(const char **argv, const char *format, ...); /* convert.c */ /* returns 1 if *dst was used */ diff --combined refs.c index 54ec98d153,6a04a667bb..3e6e98c5eb --- a/refs.c +++ b/refs.c @@@ -643,6 -643,37 +643,37 @@@ int check_ref_format(const char *ref } } + const char *ref_rev_parse_rules[] = { + "%.*s", + "refs/%.*s", + "refs/tags/%.*s", + "refs/heads/%.*s", + "refs/remotes/%.*s", + "refs/remotes/%.*s/HEAD", + NULL + }; + + const char *ref_fetch_rules[] = { + "%.*s", + "refs/%.*s", + "refs/heads/%.*s", + NULL + }; + + int refname_match(const char *abbrev_name, const char *full_name, const char **rules) + { + const char **p; + const int abbrev_name_len = strlen(abbrev_name); + + for (p = rules; *p; p++) { + if (!strcmp(full_name, mkpath(*p, abbrev_name_len, abbrev_name))) { + return 1; + } + } + + return 0; + } + static struct ref_lock *verify_lock(struct ref_lock *lock, const unsigned char *old_sha1, int mustexist) { @@@ -1433,11 -1464,3 +1464,11 @@@ int update_ref(const char *action, cons } 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 --combined remote.c index 46e5f04243,48812a713e..3fb0f99b29 --- a/remote.c +++ b/remote.c @@@ -278,8 -278,6 +278,8 @@@ static int handle_config(const char *ke } else if (!strcmp(subkey, ".tagopt")) { if (!strcmp(value, "--no-tags")) remote->fetch_tags = -1; + } else if (!strcmp(subkey, ".proxy")) { + remote->http_proxy = xstrdup(value); } return 0; } @@@ -419,25 -417,6 +419,6 @@@ int remote_has_url(struct remote *remot return 0; } - /* - * Returns true if, under the matching rules for fetching, name is the - * same as the given full name. - */ - static int ref_matches_abbrev(const char *name, const char *full) - { - if (!prefixcmp(name, "refs/") || !strcmp(name, "HEAD")) - return !strcmp(name, full); - if (prefixcmp(full, "refs/")) - return 0; - if (!prefixcmp(name, "heads/") || - !prefixcmp(name, "tags/") || - !prefixcmp(name, "remotes/")) - return !strcmp(name, full + 5); - if (prefixcmp(full + 5, "heads/")) - return 0; - return !strcmp(full + 11, name); - } - int remote_find_tracking(struct remote *remote, struct refspec *refspec) { int find_src = refspec->src == NULL; @@@ -487,7 -466,7 +468,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); @@@ -495,18 -474,6 +476,18 @@@ 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; @@@ -533,10 -500,7 +514,7 @@@ static int count_refspec_match(const ch char *name = refs->name; int namelen = strlen(name); - if (namelen < patlen || - memcmp(name + namelen - patlen, pattern, patlen)) - continue; - if (namelen != patlen && name[namelen - patlen - 1] != '/') + if (!refname_match(pattern, name, ref_rev_parse_rules)) continue; /* A match is "weak" if it is with refs outside @@@ -698,6 -662,14 +676,6 @@@ static int match_explicit_refs(struct r 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) @@@ -716,12 -688,10 +694,12 @@@ * 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 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; @@@ -738,7 -708,7 +716,7 @@@ 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 @@@ -759,13 -729,10 +737,13 @@@ 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 */ @@@ -818,13 -785,13 +796,13 @@@ int branch_merge_matches(struct branch { if (!branch || i < 0 || i >= branch->merge_nr) return 0; - return ref_matches_abbrev(branch->merge[i]->src, refname); + return refname_match(branch->merge[i]->src, refname, ref_fetch_rules); } -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; @@@ -835,7 -802,7 +813,7 @@@ 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; @@@ -853,19 -820,19 +831,19 @@@ 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)) + if (refname_match(name, ref->name, ref_fetch_rules)) return ref; } 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; @@@ -898,7 -865,7 +876,7 @@@ static struct ref *get_local_ref(const 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 --combined sha1_name.c index bd8fc05c4b,d364244dc4..13e11645e1 --- a/sha1_name.c +++ b/sha1_name.c @@@ -239,23 -239,13 +239,13 @@@ static int ambiguous_path(const char *p return slash; } - static const char *ref_fmt[] = { - "%.*s", - "refs/%.*s", - "refs/tags/%.*s", - "refs/heads/%.*s", - "refs/remotes/%.*s", - "refs/remotes/%.*s/HEAD", - NULL - }; - int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref) { const char **p, *r; int refs_found = 0; *ref = NULL; - for (p = ref_fmt; *p; p++) { + for (p = ref_rev_parse_rules; *p; p++) { unsigned char sha1_from_ref[20]; unsigned char *this_result; @@@ -277,7 -267,7 +267,7 @@@ int dwim_log(const char *str, int len, int logs_found = 0; *log = NULL; - for (p = ref_fmt; *p; p++) { + for (p = ref_rev_parse_rules; *p; p++) { struct stat st; unsigned char hash[20]; char path[PATH_MAX]; @@@ -610,35 -600,24 +600,35 @@@ static int get_sha1_oneline(const char { struct commit_list *list = NULL, *backup = NULL, *l; int retval = -1; + char *temp_commit_buffer = NULL; if (prefix[0] == '!') { if (prefix[1] != '!') die ("Invalid search pattern: %s", prefix); prefix++; } - if (!save_commit_buffer) - return error("Could not expand oneline-name."); for_each_ref(handle_one_ref, &list); for (l = list; l; l = l->next) commit_list_insert(l->item, &backup); while (list) { char *p; struct commit *commit; + enum object_type type; + unsigned long size; commit = pop_most_recent_commit(&list, ONELINE_SEEN); parse_object(commit->object.sha1); - if (!commit->buffer || !(p = strstr(commit->buffer, "\n\n"))) + if (temp_commit_buffer) + free(temp_commit_buffer); + if (commit->buffer) + p = commit->buffer; + else { + p = read_sha1_file(commit->object.sha1, &type, &size); + if (!p) + continue; + temp_commit_buffer = p; + } + if (!(p = strstr(p, "\n\n"))) continue; if (!prefixcmp(p + 2, prefix)) { hashcpy(sha1, commit->object.sha1); @@@ -646,8 -625,6 +636,8 @@@ break; } } + if (temp_commit_buffer) + free(temp_commit_buffer); free_commit_list(list); for (l = backup; l; l = l->next) clear_commit_marks(l->item, ONELINE_SEEN); diff --combined t/t5510-fetch.sh index 35889c0a12,20257428eb..46a9c4d95c --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@@ -95,6 -95,31 +95,31 @@@ test_expect_success 'fetch following ta ' + test_expect_failure 'fetch must not resolve short tag name' ' + + cd "$D" && + + mkdir five && + cd five && + git init && + + git fetch .. anno:five + + ' + + test_expect_failure 'fetch must not resolve short remote name' ' + + cd "$D" && + git-update-ref refs/remotes/six/HEAD HEAD + + mkdir six && + cd six && + git init && + + git fetch .. six:six + + ' + test_expect_success 'create bundle 1' ' cd "$D" && echo >file updated again by origin && @@@ -215,17 -240,4 +240,17 @@@ test_expect_success 'quoting of a stran grep "fatal: '\''a\\\\!'\''b'\''" result ' +test_expect_success 'bundle should record HEAD correctly' ' + + cd "$D" && + git bundle create bundle5 HEAD master && + git bundle list-heads bundle5 >actual && + for h in HEAD refs/heads/master + do + echo "$(git rev-parse --verify $h) $h" + done >expect && + diff -u expect actual + +' + test_done diff --combined t/t5516-fetch-push.sh index 987c9d21ca,fd5f284e9a..9d2dc33cbd --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@@ -145,11 -145,21 +145,21 @@@ test_expect_success 'push with no ambig test_expect_success 'push with no ambiguity (2)' ' mk_test remotes/origin/master && - git push testrepo master:master && + git push testrepo master:origin/master && check_push_result $the_commit remotes/origin/master ' + test_expect_success 'push with colon-less refspec, no ambiguity' ' + + mk_test heads/master heads/t/master && + git branch -f t/master master && + git push testrepo master && + check_push_result $the_commit heads/master && + check_push_result $the_first_commit heads/t/master + + ' + test_expect_success 'push with weak ambiguity (1)' ' mk_test heads/master remotes/origin/master && @@@ -244,11 -254,29 +254,28 @@@ test_expect_success 'push with colon-le ' + test_expect_success 'push with HEAD' ' + + mk_test heads/master && + git checkout master && + git push testrepo HEAD && + check_push_result $the_commit heads/master + + ' + + test_expect_success 'push with HEAD nonexisting at remote' ' + + mk_test heads/master && + git checkout -b local master && + git push testrepo HEAD && + check_push_result $the_commit heads/local + ' + 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 ' @@@ -256,40 -284,28 +283,40 @@@ 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 && + 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 $(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 && + 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) + 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) '