Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
git-mirror
/
git
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
0
Pull requests
0
Actions
Projects
0
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Security
Insights
Files
c091b3d
Documentation
arm
compat
contrib
git-gui
gitk-git
gitweb
mozilla-sha1
perl
ppc
t
templates
xdiff
.gitignore
.mailmap
COPYING
GIT-VERSION-GEN
INSTALL
Makefile
README
RelNotes
alloc.c
archive-tar.c
archive-zip.c
archive.c
archive.h
attr.c
attr.h
base85.c
blob.c
blob.h
builtin-add.c
builtin-annotate.c
builtin-apply.c
builtin-archive.c
builtin-blame.c
builtin-branch.c
builtin-bundle.c
builtin-cat-file.c
builtin-check-attr.c
builtin-check-ref-format.c
builtin-checkout-index.c
builtin-clean.c
builtin-commit-tree.c
builtin-commit.c
builtin-config.c
builtin-count-objects.c
builtin-describe.c
builtin-diff-files.c
builtin-diff-index.c
builtin-diff-tree.c
builtin-diff.c
builtin-fast-export.c
builtin-fetch--tool.c
builtin-fetch-pack.c
builtin-fetch.c
builtin-fmt-merge-msg.c
builtin-for-each-ref.c
builtin-fsck.c
builtin-gc.c
builtin-grep.c
builtin-http-fetch.c
builtin-init-db.c
builtin-log.c
builtin-ls-files.c
builtin-ls-remote.c
builtin-ls-tree.c
builtin-mailinfo.c
builtin-mailsplit.c
builtin-merge-base.c
builtin-merge-file.c
builtin-merge-ours.c
builtin-mv.c
builtin-name-rev.c
builtin-pack-objects.c
builtin-pack-refs.c
builtin-prune-packed.c
builtin-prune.c
builtin-push.c
builtin-read-tree.c
builtin-reflog.c
builtin-rerere.c
builtin-reset.c
builtin-rev-list.c
builtin-rev-parse.c
builtin-revert.c
builtin-rm.c
builtin-send-pack.c
builtin-shortlog.c
builtin-show-branch.c
builtin-show-ref.c
builtin-stripspace.c
builtin-symbolic-ref.c
builtin-tag.c
builtin-tar-tree.c
builtin-unpack-objects.c
builtin-update-index.c
builtin-update-ref.c
builtin-upload-archive.c
builtin-verify-pack.c
builtin-verify-tag.c
builtin-write-tree.c
builtin.h
bundle.c
bundle.h
cache-tree.c
cache-tree.h
cache.h
check-builtins.sh
check-racy.c
color.c
color.h
combine-diff.c
command-list.txt
commit.c
commit.h
config.c
config.mak.in
configure.ac
connect.c
convert.c
copy.c
csum-file.c
csum-file.h
ctype.c
daemon.c
date.c
decorate.c
decorate.h
delta.h
diff-delta.c
diff-lib.c
diff.c
diff.h
diffcore-break.c
diffcore-delta.c
diffcore-order.c
diffcore-pickaxe.c
diffcore-rename.c
diffcore.h
dir.c
dir.h
dump-cache-tree.c
entry.c
environment.c
exec_cmd.c
exec_cmd.h
fast-import.c
fetch-pack.h
fixup-builtins
generate-cmdlist.sh
git-add--interactive.perl
git-am.sh
git-archimport.perl
git-bisect.sh
git-checkout.sh
git-clone.sh
git-compat-util.h
git-cvsexportcommit.perl
git-cvsimport.perl
git-cvsserver.perl
git-filter-branch.sh
git-help--browse.sh
git-instaweb.sh
git-lost-found.sh
git-merge-octopus.sh
git-merge-one-file.sh
git-merge-resolve.sh
git-merge-stupid.sh
git-merge.sh
git-mergetool.sh
git-parse-remote.sh
git-pull.sh
git-quiltimport.sh
git-rebase--interactive.sh
git-rebase.sh
git-relink.perl
git-remote.perl
git-repack.sh
git-request-pull.sh
git-send-email.perl
git-sh-setup.sh
git-stash.sh
git-submodule.sh
git-svn.perl
git.c
git.spec.in
grep.c
grep.h
hash-object.c
hash.c
hash.h
help.c
http-push.c
http-walker.c
http.c
http.h
ident.c
imap-send.c
index-pack.c
interpolate.c
interpolate.h
list-objects.c
list-objects.h
lockfile.c
log-tree.c
log-tree.h
mailmap.c
mailmap.h
match-trees.c
merge-file.c
merge-index.c
merge-recursive.c
merge-tree.c
mktag.c
mktree.c
object-refs.c
object.c
object.h
pack-check.c
pack-redundant.c
pack-write.c
pack.h
pager.c
parse-options.c
parse-options.h
patch-delta.c
patch-id.c
patch-ids.c
patch-ids.h
path-list.c
path-list.h
path.c
pkt-line.c
pkt-line.h
pretty.c
progress.c
progress.h
quote.c
quote.h
reachable.c
reachable.h
read-cache.c
receive-pack.c
reflog-walk.c
reflog-walk.h
refs.c
refs.h
remote.c
remote.h
revision.c
revision.h
run-command.c
run-command.h
send-pack.h
server-info.c
setup.c
sha1_file.c
sha1_name.c
shallow.c
shell.c
show-index.c
sideband.c
sideband.h
strbuf.c
strbuf.h
symlinks.c
tag.c
tag.h
tar.h
test-absolute-path.c
test-chmtime.c
test-date.c
test-delta.c
test-genrandom.c
test-match-trees.c
test-parse-options.c
test-sha1.c
test-sha1.sh
trace.c
transport.c
transport.h
tree-diff.c
tree-walk.c
tree-walk.h
tree.c
tree.h
unpack-file.c
unpack-trees.c
unpack-trees.h
update-server-info.c
upload-pack.c
usage.c
utf8.c
utf8.h
var.c
walker.c
walker.h
write_or_die.c
ws.c
wt-status.c
wt-status.h
xdiff-interface.c
xdiff-interface.h
Breadcrumbs
git
/
remote.c
Blame
Blame
Latest commit
History
History
1022 lines (927 loc) · 23.7 KB
Breadcrumbs
git
/
remote.c
Top
File metadata and controls
Code
Blame
1022 lines (927 loc) · 23.7 KB
Raw
#include "cache.h" #include "remote.h" #include "refs.h" static struct remote **remotes; static int allocated_remotes; static struct branch **branches; static int allocated_branches; static struct branch *current_branch; static const char *default_remote_name; #define BUF_SIZE (2048) static char buffer[BUF_SIZE]; static void add_push_refspec(struct remote *remote, const char *ref) { int nr = remote->push_refspec_nr + 1; remote->push_refspec = xrealloc(remote->push_refspec, nr * sizeof(char *)); remote->push_refspec[nr-1] = ref; remote->push_refspec_nr = nr; } static void add_fetch_refspec(struct remote *remote, const char *ref) { int nr = remote->fetch_refspec_nr + 1; remote->fetch_refspec = xrealloc(remote->fetch_refspec, nr * sizeof(char *)); remote->fetch_refspec[nr-1] = ref; remote->fetch_refspec_nr = nr; } static void add_url(struct remote *remote, const char *url) { int nr = remote->url_nr + 1; remote->url = xrealloc(remote->url, nr * sizeof(char *)); remote->url[nr-1] = url; remote->url_nr = nr; } static struct remote *make_remote(const char *name, int len) { int i, empty = -1; for (i = 0; i < allocated_remotes; i++) { if (!remotes[i]) { if (empty < 0) empty = i; } else { if (len ? (!strncmp(name, remotes[i]->name, len) && !remotes[i]->name[len]) : !strcmp(name, remotes[i]->name)) return remotes[i]; } } if (empty < 0) { empty = allocated_remotes; allocated_remotes += allocated_remotes ? allocated_remotes : 1; remotes = xrealloc(remotes, sizeof(*remotes) * allocated_remotes); memset(remotes + empty, 0, (allocated_remotes - empty) * sizeof(*remotes)); } remotes[empty] = xcalloc(1, sizeof(struct remote)); if (len) remotes[empty]->name = xstrndup(name, len); else remotes[empty]->name = xstrdup(name); return remotes[empty]; } static void add_merge(struct branch *branch, const char *name) { int nr = branch->merge_nr + 1; branch->merge_name = xrealloc(branch->merge_name, nr * sizeof(char *)); branch->merge_name[nr-1] = name; branch->merge_nr = nr; } static struct branch *make_branch(const char *name, int len) { int i, empty = -1; char *refname; for (i = 0; i < allocated_branches; i++) { if (!branches[i]) { if (empty < 0) empty = i; } else { if (len ? (!strncmp(name, branches[i]->name, len) && !branches[i]->name[len]) : !strcmp(name, branches[i]->name)) return branches[i]; } } if (empty < 0) { empty = allocated_branches; allocated_branches += allocated_branches ? allocated_branches : 1; branches = xrealloc(branches, sizeof(*branches) * allocated_branches); memset(branches + empty, 0, (allocated_branches - empty) * sizeof(*branches)); } branches[empty] = xcalloc(1, sizeof(struct branch)); if (len) branches[empty]->name = xstrndup(name, len); else branches[empty]->name = xstrdup(name); refname = malloc(strlen(name) + strlen("refs/heads/") + 1); strcpy(refname, "refs/heads/"); strcpy(refname + strlen("refs/heads/"), branches[empty]->name); branches[empty]->refname = refname; return branches[empty]; } static void read_remotes_file(struct remote *remote) { FILE *f = fopen(git_path("remotes/%s", remote->name), "r"); if (!f) return; while (fgets(buffer, BUF_SIZE, f)) { int value_list; char *s, *p; if (!prefixcmp(buffer, "URL:")) { value_list = 0; s = buffer + 4; } else if (!prefixcmp(buffer, "Push:")) { value_list = 1; s = buffer + 5; } else if (!prefixcmp(buffer, "Pull:")) { value_list = 2; s = buffer + 5; } else continue; while (isspace(*s)) s++; if (!*s) continue; p = s + strlen(s); while (isspace(p[-1])) *--p = 0; switch (value_list) { case 0: add_url(remote, xstrdup(s)); break; case 1: add_push_refspec(remote, xstrdup(s)); break; case 2: add_fetch_refspec(remote, xstrdup(s)); break; } } fclose(f); } static void read_branches_file(struct remote *remote) { const char *slash = strchr(remote->name, '/'); char *frag; char *branch; int n = slash ? slash - remote->name : 1000; FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r"); char *s, *p; int len; if (!f) return; s = fgets(buffer, BUF_SIZE, f); fclose(f); if (!s) return; while (isspace(*s)) s++; if (!*s) return; p = s + strlen(s); while (isspace(p[-1])) *--p = 0; len = p - s; if (slash) len += strlen(slash); p = xmalloc(len + 1); strcpy(p, s); if (slash) strcat(p, slash); frag = strchr(p, '#'); if (frag) { *(frag++) = '\0'; branch = xmalloc(strlen(frag) + 12); strcpy(branch, "refs/heads/"); strcat(branch, frag); } else { branch = "refs/heads/master"; } add_url(remote, p); add_fetch_refspec(remote, branch); remote->fetch_tags = 1; /* always auto-follow */ } static int handle_config(const char *key, const char *value) { const char *name; const char *subkey; struct remote *remote; struct branch *branch; if (!prefixcmp(key, "branch.")) { name = key + 7; subkey = strrchr(name, '.'); if (!subkey) return 0; branch = make_branch(name, subkey - name); if (!strcmp(subkey, ".remote")) { if (!value) return config_error_nonbool(key); branch->remote_name = xstrdup(value); if (branch == current_branch) default_remote_name = branch->remote_name; } else if (!strcmp(subkey, ".merge")) { if (!value) return config_error_nonbool(key); add_merge(branch, xstrdup(value)); } return 0; } if (prefixcmp(key, "remote.")) return 0; name = key + 7; subkey = strrchr(name, '.'); if (!subkey) return error("Config with no key for remote %s", name); if (*subkey == '/') { warning("Config remote shorthand cannot begin with '/': %s", name); return 0; } remote = make_remote(name, subkey - name); if (!value) { /* if we ever have a boolean variable, e.g. "remote.*.disabled" * [remote "frotz"] * disabled * is a valid way to set it to true; we get NULL in value so * we need to handle it here. * * if (!strcmp(subkey, ".disabled")) { * val = git_config_bool(key, value); * return 0; * } else * */ return 0; /* ignore unknown booleans */ } if (!strcmp(subkey, ".url")) { add_url(remote, xstrdup(value)); } else if (!strcmp(subkey, ".push")) { add_push_refspec(remote, xstrdup(value)); } else if (!strcmp(subkey, ".fetch")) { add_fetch_refspec(remote, xstrdup(value)); } else if (!strcmp(subkey, ".receivepack")) { if (!remote->receivepack) remote->receivepack = xstrdup(value); else error("more than one receivepack given, using the first"); } else if (!strcmp(subkey, ".uploadpack")) { if (!remote->uploadpack) remote->uploadpack = xstrdup(value); else error("more than one uploadpack given, using the first"); } 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; } static void read_config(void) { unsigned char sha1[20]; const char *head_ref; int flag; if (default_remote_name) // did this already return; default_remote_name = xstrdup("origin"); current_branch = NULL; head_ref = resolve_ref("HEAD", sha1, 0, &flag); if (head_ref && (flag & REF_ISSYMREF) && !prefixcmp(head_ref, "refs/heads/")) { current_branch = make_branch(head_ref + strlen("refs/heads/"), 0); } git_config(handle_config); } static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch) { int i; int st; struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec); for (i = 0; i < nr_refspec; i++) { size_t llen, rlen; int is_glob; const char *lhs, *rhs; llen = rlen = is_glob = 0; lhs = refspec[i]; if (*lhs == '+') { rs[i].force = 1; lhs++; } rhs = strrchr(lhs, ':'); if (rhs) { rhs++; rlen = strlen(rhs); is_glob = (2 <= rlen && !strcmp(rhs + rlen - 2, "/*")); if (is_glob) rlen -= 2; rs[i].dst = xstrndup(rhs, rlen); } llen = (rhs ? (rhs - lhs - 1) : strlen(lhs)); if (2 <= llen && !memcmp(lhs + llen - 2, "/*", 2)) { if ((rhs && !is_glob) || (!rhs && fetch)) goto invalid; is_glob = 1; llen -= 2; } else if (rhs && is_glob) { goto invalid; } rs[i].pattern = is_glob; rs[i].src = xstrndup(lhs, llen); if (fetch) { /* * LHS * - empty is allowed; it means HEAD. * - otherwise it must be a valid looking ref. */ if (!*rs[i].src) ; /* empty is ok */ else { st = check_ref_format(rs[i].src); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } /* * RHS * - missing is ok, and is same as empty. * - empty is ok; it means not to store. * - otherwise it must be a valid looking ref. */ if (!rs[i].dst) { ; /* ok */ } else if (!*rs[i].dst) { ; /* ok */ } else { st = check_ref_format(rs[i].dst); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } } else { /* * LHS * - empty is allowed; it means delete. * - when wildcarded, it must be a valid looking ref. * - otherwise, it must be an extended SHA-1, but * there is no existing way to validate this. */ if (!*rs[i].src) ; /* empty is ok */ else if (is_glob) { st = check_ref_format(rs[i].src); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } else ; /* anything goes, for now */ /* * RHS * - missing is allowed, but LHS then must be a * valid looking ref. * - empty is not allowed. * - otherwise it must be a valid looking ref. */ if (!rs[i].dst) { st = check_ref_format(rs[i].src); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } else if (!*rs[i].dst) { goto invalid; } else { st = check_ref_format(rs[i].dst); if (st && st != CHECK_REF_FORMAT_ONELEVEL) goto invalid; } } } return rs; invalid: die("Invalid refspec '%s'", refspec[i]); } struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec) { return parse_refspec_internal(nr_refspec, refspec, 1); } struct refspec *parse_push_refspec(int nr_refspec, const char **refspec) { return parse_refspec_internal(nr_refspec, refspec, 0); } static int valid_remote_nick(const char *name) { if (!name[0] || /* not empty */ (name[0] == '.' && /* not "." */ (!name[1] || /* not ".." */ (name[1] == '.' && !name[2])))) return 0; return !strchr(name, '/'); /* no slash */ } struct remote *remote_get(const char *name) { struct remote *ret; read_config(); if (!name) name = default_remote_name; ret = make_remote(name, 0); if (valid_remote_nick(name)) { if (!ret->url) read_remotes_file(ret); if (!ret->url) read_branches_file(ret); } if (!ret->url) add_url(ret, name); if (!ret->url) return NULL; ret->fetch = parse_fetch_refspec(ret->fetch_refspec_nr, ret->fetch_refspec); ret->push = parse_push_refspec(ret->push_refspec_nr, ret->push_refspec); return ret; } int for_each_remote(each_remote_fn fn, void *priv) { int i, result = 0; read_config(); for (i = 0; i < allocated_remotes && !result; i++) { struct remote *r = remotes[i]; if (!r) continue; if (!r->fetch) r->fetch = parse_fetch_refspec(r->fetch_refspec_nr, r->fetch_refspec); if (!r->push) r->push = parse_push_refspec(r->push_refspec_nr, r->push_refspec); result = fn(r, priv); } return result; } void ref_remove_duplicates(struct ref *ref_map) { struct ref **posn; struct ref *next; for (; ref_map; ref_map = ref_map->next) { if (!ref_map->peer_ref) continue; posn = &ref_map->next; while (*posn) { if ((*posn)->peer_ref && !strcmp((*posn)->peer_ref->name, ref_map->peer_ref->name)) { if (strcmp((*posn)->name, ref_map->name)) die("%s tracks both %s and %s", ref_map->peer_ref->name, (*posn)->name, ref_map->name); next = (*posn)->next; free((*posn)->peer_ref); free(*posn); *posn = next; } else { posn = &(*posn)->next; } } } } int remote_has_url(struct remote *remote, const char *url) { int i; for (i = 0; i < remote->url_nr; i++) { if (!strcmp(remote->url[i], url)) return 1; } return 0; } int remote_find_tracking(struct remote *remote, struct refspec *refspec) { int find_src = refspec->src == NULL; char *needle, **result; int i; if (find_src) { if (!refspec->dst) return error("find_tracking: need either src or dst"); needle = refspec->dst; result = &refspec->src; } else { needle = refspec->src; result = &refspec->dst; } for (i = 0; i < remote->fetch_refspec_nr; i++) { struct refspec *fetch = &remote->fetch[i]; const char *key = find_src ? fetch->dst : fetch->src; const char *value = find_src ? fetch->src : fetch->dst; if (!fetch->dst) continue; if (fetch->pattern) { if (!prefixcmp(needle, key) && needle[strlen(key)] == '/') { *result = xmalloc(strlen(value) + strlen(needle) - strlen(key) + 1); strcpy(*result, value); strcpy(*result + strlen(value), needle + strlen(key)); refspec->force = fetch->force; return 0; } } else if (!strcmp(needle, key)) { *result = xstrdup(value); refspec->force = fetch->force; return 0; } } return -1; } struct ref *alloc_ref(unsigned namelen) { struct ref *ret = xmalloc(sizeof(struct ref) + namelen); memset(ret, 0, sizeof(struct ref) + namelen); return ret; } 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); ret->next = NULL; 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; while (ref) { next = ref->next; if (ref->peer_ref) free(ref->peer_ref); free(ref); ref = next; } } static int count_refspec_match(const char *pattern, struct ref *refs, struct ref **matched_ref) { int patlen = strlen(pattern); struct ref *matched_weak = NULL; struct ref *matched = NULL; int weak_match = 0; int match = 0; for (weak_match = match = 0; refs; refs = refs->next) { char *name = refs->name; int namelen = strlen(name); if (!refname_match(pattern, name, ref_rev_parse_rules)) continue; /* A match is "weak" if it is with refs outside * heads or tags, and did not specify the pattern * in full (e.g. "refs/remotes/origin/master") or at * least from the toplevel (e.g. "remotes/origin/master"); * otherwise "git push $URL master" would result in * ambiguity between remotes/origin/master and heads/master * at the remote site. */ if (namelen != patlen && patlen != namelen - 5 && prefixcmp(name, "refs/heads/") && prefixcmp(name, "refs/tags/")) { /* We want to catch the case where only weak * matches are found and there are multiple * matches, and where more than one strong * matches are found, as ambiguous. One * strong match with zero or more weak matches * are acceptable as a unique match. */ matched_weak = refs; weak_match++; } else { matched = refs; match++; } } if (!matched) { *matched_ref = matched_weak; return weak_match; } else { *matched_ref = matched; return match; } } static void tail_link_ref(struct ref *ref, struct ref ***tail) { **tail = ref; while (ref->next) ref = ref->next; *tail = &ref->next; } static struct ref *try_explicit_object_name(const char *name) { unsigned char sha1[20]; struct ref *ref; int len; if (!*name) { ref = alloc_ref(20); strcpy(ref->name, "(delete)"); hashclr(ref->new_sha1); return ref; } if (get_sha1(name, sha1)) return NULL; len = strlen(name) + 1; ref = alloc_ref(len); memcpy(ref->name, name, len); hashcpy(ref->new_sha1, sha1); return ref; } static struct ref *make_linked_ref(const char *name, struct ref ***tail) { struct ref *ret; size_t len; len = strlen(name) + 1; ret = alloc_ref(len); memcpy(ret->name, name, len); tail_link_ref(ret, tail); return ret; } static int match_explicit(struct ref *src, struct ref *dst, struct ref ***dst_tail, struct refspec *rs, int errs) { struct ref *matched_src, *matched_dst; const char *dst_value = rs->dst; if (rs->pattern) return errs; matched_src = matched_dst = NULL; switch (count_refspec_match(rs->src, src, &matched_src)) { case 1: break; case 0: /* The source could be in the get_sha1() format * not a reference name. :refs/other is a * way to delete 'other' ref at the remote end. */ matched_src = try_explicit_object_name(rs->src); if (!matched_src) error("src refspec %s does not match any.", rs->src); break; default: matched_src = NULL; error("src refspec %s matches more than one.", rs->src); break; } if (!matched_src) errs = 1; if (!dst_value) { if (!matched_src) return errs; dst_value = matched_src->name; } switch (count_refspec_match(dst_value, dst, &matched_dst)) { case 1: break; case 0: if (!memcmp(dst_value, "refs/", 5)) matched_dst = make_linked_ref(dst_value, dst_tail); else error("dst refspec %s does not match any " "existing ref on the remote and does " "not start with refs/.", dst_value); break; default: matched_dst = NULL; error("dst refspec %s matches more than one.", dst_value); break; } if (errs || !matched_dst) return 1; if (matched_dst->peer_ref) { errs = 1; error("dst ref %s receives from more than one src.", matched_dst->name); } else { matched_dst->peer_ref = matched_src; matched_dst->force = rs->force; } return errs; } static int match_explicit_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, struct refspec *rs, int rs_nr) { int i, errs; for (i = errs = 0; i < rs_nr; i++) errs |= match_explicit(src, dst, dst_tail, &rs[i], errs); return -errs; } static const struct refspec *check_pattern_match(const struct refspec *rs, int rs_nr, const struct ref *src) { int i; for (i = 0; i < rs_nr; i++) { if (rs[i].pattern && !prefixcmp(src->name, rs[i].src) && src->name[strlen(rs[i].src)] == '/') return rs + i; } return NULL; } /* * Note. This is used only by "push"; refspec matching rules for * push and fetch are subtly different, so do not try to reuse it * without thinking. */ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, int nr_refspec, const char **refspec, int flags) { struct refspec *rs = parse_push_refspec(nr_refspec, (const char **) refspec); 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; /* pick the remainder */ for ( ; src; src = src->next) { struct ref *dst_peer; const struct refspec *pat = NULL; char *dst_name; if (src->peer_ref) continue; if (nr_refspec) { pat = check_pattern_match(rs, nr_refspec, src); if (!pat) continue; } else if (!send_mirror && prefixcmp(src->name, "refs/heads/")) /* * "matching refs"; traditionally we pushed everything * including refs outside refs/heads/ hierarchy, but * that does not make much sense these days. */ continue; if (pat) { const char *dst_side = pat->dst ? pat->dst : pat->src; dst_name = xmalloc(strlen(dst_side) + strlen(src->name) - strlen(pat->src) + 2); strcpy(dst_name, dst_side); strcat(dst_name, src->name + strlen(pat->src)); } else dst_name = xstrdup(src->name); dst_peer = find_ref_by_name(dst, dst_name); if (dst_peer && dst_peer->peer_ref) /* We're already sending something to this ref. */ goto free_name; if (!dst_peer && !nr_refspec && !(send_all || send_mirror)) /* * Remote doesn't have it, and we have no * explicit pattern, and we don't have * --all nor --mirror. */ goto free_name; if (!dst_peer) { /* Create a new one and link it */ dst_peer = make_linked_ref(dst_name, dst_tail); hashcpy(dst_peer->new_sha1, src->new_sha1); } dst_peer->peer_ref = src; if (pat) dst_peer->force = pat->force; free_name: free(dst_name); } return 0; } struct branch *branch_get(const char *name) { struct branch *ret; read_config(); if (!name || !*name || !strcmp(name, "HEAD")) ret = current_branch; else ret = make_branch(name, 0); if (ret && ret->remote_name) { ret->remote = remote_get(ret->remote_name); if (ret->merge_nr) { int i; ret->merge = xcalloc(sizeof(*ret->merge), ret->merge_nr); for (i = 0; i < ret->merge_nr; i++) { ret->merge[i] = xcalloc(1, sizeof(**ret->merge)); ret->merge[i]->src = xstrdup(ret->merge_name[i]); remote_find_tracking(ret->remote, ret->merge[i]); } } } return ret; } int branch_has_merge_config(struct branch *branch) { return branch && !!branch->merge; } int branch_merge_matches(struct branch *branch, int i, const char *refname) { if (!branch || i < 0 || i >= branch->merge_nr) return 0; return refname_match(branch->merge[i]->src, refname, ref_fetch_rules); } static struct ref *get_expanded_map(const struct ref *remote_refs, const struct refspec *refspec) { const struct ref *ref; struct ref *ret = NULL; struct ref **tail = &ret; int remote_prefix_len = strlen(refspec->src); int local_prefix_len = strlen(refspec->dst); for (ref = remote_refs; ref; ref = ref->next) { if (strchr(ref->name, '^')) continue; /* a dereference item */ if (!prefixcmp(ref->name, refspec->src)) { const char *match; struct ref *cpy = copy_ref(ref); match = ref->name + remote_prefix_len; cpy->peer_ref = alloc_ref(local_prefix_len + strlen(match) + 1); sprintf(cpy->peer_ref->name, "%s%s", refspec->dst, match); if (refspec->force) cpy->peer_ref->force = 1; *tail = cpy; tail = &cpy->next; } } return ret; } static const struct ref *find_ref_by_name_abbrev(const struct ref *refs, const char *name) { const struct ref *ref; for (ref = refs; ref; ref = ref->next) { if (refname_match(name, ref->name, ref_fetch_rules)) return ref; } return NULL; } struct ref *get_remote_ref(const struct ref *remote_refs, const char *name) { const struct ref *ref = find_ref_by_name_abbrev(remote_refs, name); if (!ref) return NULL; return copy_ref(ref); } static struct ref *get_local_ref(const char *name) { struct ref *ret; if (!name) return NULL; if (!prefixcmp(name, "refs/")) { ret = alloc_ref(strlen(name) + 1); strcpy(ret->name, name); return ret; } if (!prefixcmp(name, "heads/") || !prefixcmp(name, "tags/") || !prefixcmp(name, "remotes/")) { ret = alloc_ref(strlen(name) + 6); sprintf(ret->name, "refs/%s", name); return ret; } ret = alloc_ref(strlen(name) + 12); sprintf(ret->name, "refs/heads/%s", name); return ret; } int get_fetch_map(const struct ref *remote_refs, const struct refspec *refspec, struct ref ***tail, int missing_ok) { struct ref *ref_map, **rmp; if (refspec->pattern) { ref_map = get_expanded_map(remote_refs, refspec); } else { const char *name = refspec->src[0] ? refspec->src : "HEAD"; ref_map = get_remote_ref(remote_refs, name); if (!missing_ok && !ref_map) die("Couldn't find remote ref %s", name); if (ref_map) { ref_map->peer_ref = get_local_ref(refspec->dst); if (ref_map->peer_ref && refspec->force) ref_map->peer_ref->force = 1; } } for (rmp = &ref_map; *rmp; ) { if ((*rmp)->peer_ref) { int st = check_ref_format((*rmp)->peer_ref->name + 5); if (st && st != CHECK_REF_FORMAT_ONELEVEL) { struct ref *ignore = *rmp; error("* Ignoring funny ref '%s' locally", (*rmp)->peer_ref->name); *rmp = (*rmp)->next; free(ignore->peer_ref); free(ignore); continue; } } rmp = &((*rmp)->next); } if (ref_map) tail_link_ref(ref_map, tail); return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
You can’t perform that action at this time.