From: Junio C Hamano Date: Thu, 25 Oct 2007 04:59:50 +0000 (-0700) Subject: Merge branch 'db/fetch-pack' X-Git-Tag: v1.5.4-rc0~325 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/d90a7fda355c251b8ffdd79617fb083c18245ec2?ds=inline;hp=-c Merge branch 'db/fetch-pack' * db/fetch-pack: (60 commits) Define compat version of mkdtemp for systems lacking it Avoid scary errors about tagged trees/blobs during git-fetch fetch: if not fetching from default remote, ignore default merge Support 'push --dry-run' for http transport Support 'push --dry-run' for rsync transport Fix 'push --all branch...' error handling Fix compilation when NO_CURL is defined Added a test for fetching remote tags when there is not tags. Fix a crash in ls-remote when refspec expands into nothing Remove duplicate ref matches in fetch Restore default verbosity for http fetches. fetch/push: readd rsync support Introduce remove_dir_recursively() bundle transport: fix an alloc_ref() call Allow abbreviations in the first refspec to be merged Prevent send-pack from segfaulting when a branch doesn't match Cleanup unnecessary break in remote.c Cleanup style nit of 'x == NULL' in remote.c Fix memory leaks when disconnecting transport instances Ensure builtin-fetch honors {fetch,transfer}.unpackLimit ... --- d90a7fda355c251b8ffdd79617fb083c18245ec2 diff --combined Makefile index 9f81c73af7,6287418df3..b7289204eb --- a/Makefile +++ b/Makefile @@@ -38,6 -38,8 +38,8 @@@ all: # # Define NO_SETENV if you don't have setenv in the C library. # + # Define NO_MKDTEMP if you don't have mkdtemp in the C library. + # # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link. # Enable it on Windows. By default, symrefs are still used. # @@@ -165,7 -167,6 +167,7 @@@ GITWEB_CONFIG = gitweb_config.per GITWEB_HOME_LINK_STR = projects GITWEB_SITENAME = GITWEB_PROJECTROOT = /pub/git +GITWEB_PROJECT_MAXDEPTH = 2007 GITWEB_EXPORT_OK = GITWEB_STRICT_EXPORT = GITWEB_BASE_URL = @@@ -208,7 -209,6 +210,6 @@@ BASIC_LDFLAGS SCRIPT_SH = \ git-bisect.sh git-checkout.sh \ git-clean.sh git-clone.sh git-commit.sh \ - git-fetch.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 \ @@@ -235,14 -235,14 +236,14 @@@ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH # ... and all the rest that could be moved out of bindir to gitexecdir PROGRAMS = \ git-fetch-pack$X \ - git-hash-object$X git-index-pack$X git-local-fetch$X \ + git-hash-object$X git-index-pack$X \ 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-send-pack$X git-shell$X \ - git-show-index$X git-ssh-fetch$X \ - git-ssh-upload$X git-unpack-file$X \ + git-show-index$X \ + git-unpack-file$X \ git-update-server-info$X \ git-upload-pack$X \ git-pack-redundant$X git-var$X \ @@@ -270,9 -270,6 +271,6 @@@ ifndef NO_TCLT OTHER_PROGRAMS += gitk-wish endif - # Backward compatibility -- to be removed after 1.0 - PROGRAMS += git-ssh-pull$X git-ssh-push$X - # Set paths to tools early so that they can be used for version tests. ifndef SHELL_PATH SHELL_PATH = /bin/sh @@@ -292,7 -289,7 +290,7 @@@ LIB_H = run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \ tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \ utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \ - mailmap.h remote.h + mailmap.h remote.h transport.h DIFF_OBJS = \ diff.o diff-lib.o diffcore-break.o diffcore-order.o \ @@@ -314,7 -311,8 +312,8 @@@ LIB_OBJS = write_or_die.o trace.o list-objects.o grep.o match-trees.o \ alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \ color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \ - convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o + convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \ + transport.o bundle.o walker.o BUILTIN_OBJS = \ builtin-add.o \ @@@ -335,6 -333,8 +334,8 @@@ builtin-diff-files.o \ builtin-diff-index.o \ builtin-diff-tree.o \ + builtin-fetch.o \ + builtin-fetch-pack.o \ builtin-fetch--tool.o \ builtin-fmt-merge-msg.o \ builtin-for-each-ref.o \ @@@ -416,12 -416,14 +417,14 @@@ ifeq ($(uname_S),SunOS NEEDS_LIBICONV = YesPlease NO_UNSETENV = YesPlease NO_SETENV = YesPlease + NO_MKDTEMP = YesPlease NO_C99_FORMAT = YesPlease NO_STRTOUMAX = YesPlease endif ifeq ($(uname_R),5.9) NO_UNSETENV = YesPlease NO_SETENV = YesPlease + NO_MKDTEMP = YesPlease NO_C99_FORMAT = YesPlease NO_STRTOUMAX = YesPlease endif @@@ -518,7 -520,9 +521,9 @@@ els CC_LD_DYNPATH = -R endif - ifndef NO_CURL + ifdef NO_CURL + BASIC_CFLAGS += -DNO_CURL + else ifdef CURLDIR # Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case. BASIC_CFLAGS += -I$(CURLDIR)/include @@@ -526,7 -530,9 +531,9 @@@ else CURL_LIBCURL = -lcurl endif - PROGRAMS += git-http-fetch$X + BUILTIN_OBJS += builtin-http-fetch.o + EXTLIBS += $(CURL_LIBCURL) + LIB_OBJS += http.o http-walker.o curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p) ifeq "$(curl_check)" "070908" ifndef NO_EXPAT @@@ -608,6 -614,10 +615,10 @@@ ifdef NO_SETEN COMPAT_CFLAGS += -DNO_SETENV COMPAT_OBJS += compat/setenv.o endif + ifdef NO_MKDTEMP + COMPAT_CFLAGS += -DNO_MKDTEMP + COMPAT_OBJS += compat/mkdtemp.o + endif ifdef NO_UNSETENV COMPAT_CFLAGS += -DNO_UNSETENV COMPAT_OBJS += compat/unsetenv.o @@@ -832,7 -842,6 +843,7 @@@ gitweb/gitweb.cgi: gitweb/gitweb.per -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \ -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \ -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \ + -e 's|"++GITWEB_PROJECT_MAXDEPTH++"|$(GITWEB_PROJECT_MAXDEPTH)|g' \ -e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \ -e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \ -e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \ @@@ -889,33 -898,22 +900,22 @@@ http.o: http.c GIT-CFLAG $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $< ifdef NO_EXPAT - http-fetch.o: http-fetch.c http.h GIT-CFLAGS + http-walker.o: http-walker.c http.h GIT-CFLAGS $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_EXPAT $< endif git-%$X: %.o $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) - ssh-pull.o: ssh-fetch.c - ssh-push.o: ssh-upload.c - git-local-fetch$X: fetch.o - git-ssh-fetch$X: rsh.o fetch.o - git-ssh-upload$X: rsh.o - git-ssh-pull$X: rsh.o fetch.o - git-ssh-push$X: rsh.o - git-imap-send$X: imap-send.o $(LIB_FILE) - http.o http-fetch.o http-push.o: http.h - git-http-fetch$X: fetch.o http.o http-fetch.o $(GITLIBS) - $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ - $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) + http.o http-walker.o http-push.o: http.h git-http-push$X: revision.o http.o http-push.o $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) - $(LIB_OBJS) $(BUILTIN_OBJS) fetch.o: $(LIB_H) + $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) $(DIFF_OBJS): diffcore.h @@@ -1131,8 -1129,7 +1131,7 @@@ check-docs: git-merge-octopus | git-merge-ours | git-merge-recursive | \ git-merge-resolve | git-merge-stupid | \ git-add--interactive | git-fsck-objects | git-init-db | \ - git-repo-config | git-fetch--tool | \ - git-ssh-pull | git-ssh-push ) continue ;; \ + git-repo-config | git-fetch--tool ) continue ;; \ esac ; \ test -f "Documentation/$$v.txt" || \ echo "no doc: $$v"; \ diff --combined dir.c index f843c4dd20,b18257e65f..4c17d3643e --- a/dir.c +++ b/dir.c @@@ -443,24 -443,6 +443,24 @@@ static int in_pathspec(const char *path return 0; } +static int get_dtype(struct dirent *de, const char *path) +{ + int dtype = DTYPE(de); + struct stat st; + + if (dtype != DT_UNKNOWN) + return dtype; + if (lstat(path, &st)) + return dtype; + if (S_ISREG(st.st_mode)) + return DT_REG; + if (S_ISDIR(st.st_mode)) + return DT_DIR; + if (S_ISLNK(st.st_mode)) + return DT_LNK; + return dtype; +} + /* * Read a directory tree. We currently ignore anything but * directories, regular files and symlinks. That's because git @@@ -484,7 -466,7 +484,7 @@@ static int read_directory_recursive(str exclude_stk = push_exclude_per_directory(dir, base, baselen); while ((de = readdir(fdir)) != NULL) { - int len; + int len, dtype; int exclude; if ((de->d_name[0] == '.') && @@@ -504,30 -486,24 +504,30 @@@ if (exclude && dir->collect_ignored && in_pathspec(fullname, baselen + len, simplify)) dir_add_ignored(dir, fullname, baselen + len); - if (exclude != dir->show_ignored) { - if (!dir->show_ignored || DTYPE(de) != DT_DIR) { + + /* + * Excluded? If we don't explicitly want to show + * ignored files, ignore it + */ + if (exclude && !dir->show_ignored) + continue; + + dtype = get_dtype(de, fullname); + + /* + * Do we want to see just the ignored files? + * We still need to recurse into directories, + * even if we don't ignore them, since the + * directory may contain files that we do.. + */ + if (!exclude && dir->show_ignored) { + if (dtype != DT_DIR) continue; - } } - switch (DTYPE(de)) { - struct stat st; + switch (dtype) { default: continue; - case DT_UNKNOWN: - if (lstat(fullname, &st)) - continue; - if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) - break; - if (!S_ISDIR(st.st_mode)) - continue; - /* fallthrough */ case DT_DIR: memcpy(fullname + baselen + len, "/", 2); len++; @@@ -709,3 -685,44 +709,44 @@@ int is_inside_dir(const char *dir char buffer[PATH_MAX]; return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL; } + + int remove_dir_recursively(struct strbuf *path, int only_empty) + { + DIR *dir = opendir(path->buf); + struct dirent *e; + int ret = 0, original_len = path->len, len; + + if (!dir) + return -1; + if (path->buf[original_len - 1] != '/') + strbuf_addch(path, '/'); + + len = path->len; + while ((e = readdir(dir)) != NULL) { + struct stat st; + if ((e->d_name[0] == '.') && + ((e->d_name[1] == 0) || + ((e->d_name[1] == '.') && e->d_name[2] == 0))) + continue; /* "." and ".." */ + + strbuf_setlen(path, len); + strbuf_addstr(path, e->d_name); + if (lstat(path->buf, &st)) + ; /* fall thru */ + else if (S_ISDIR(st.st_mode)) { + if (!remove_dir_recursively(path, only_empty)) + continue; /* happy */ + } else if (!only_empty && !unlink(path->buf)) + continue; /* happy, too */ + + /* path too long, stat fails, or non-directory still exists */ + ret = -1; + break; + } + closedir(dir); + + strbuf_setlen(path, original_len); + if (!ret) + ret = rmdir(path->buf); + return ret; + } diff --combined git.c index 853e66cddb,d7c6bcaed6..23a430c369 --- a/git.c +++ b/git.c @@@ -328,6 -328,8 +328,8 @@@ static void handle_internal_command(in { "diff-files", cmd_diff_files }, { "diff-index", cmd_diff_index, RUN_SETUP }, { "diff-tree", cmd_diff_tree, RUN_SETUP }, + { "fetch", cmd_fetch, RUN_SETUP }, + { "fetch-pack", cmd_fetch_pack, RUN_SETUP }, { "fetch--tool", cmd_fetch__tool, RUN_SETUP }, { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP }, { "for-each-ref", cmd_for_each_ref, RUN_SETUP }, @@@ -338,6 -340,9 +340,9 @@@ { "get-tar-commit-id", cmd_get_tar_commit_id }, { "grep", cmd_grep, RUN_SETUP | USE_PAGER }, { "help", cmd_help }, + #ifndef NO_CURL + { "http-fetch", cmd_http_fetch, RUN_SETUP }, + #endif { "init", cmd_init_db }, { "init-db", cmd_init_db }, { "log", cmd_log, RUN_SETUP | USE_PAGER }, @@@ -409,14 -414,13 +414,14 @@@ int main(int argc, const char **argv /* * Take the basename of argv[0] as the command * name, and the dirname as the default exec_path - * if it's an absolute path and we don't have - * anything better. + * if we don't have anything better. */ if (slash) { *slash++ = 0; if (*cmd == '/') exec_path = cmd; + else + exec_path = xstrdup(make_absolute_path(cmd)); cmd = slash; } diff --combined receive-pack.c index 1521d0b2de,61e9929763..38e35c06b9 --- a/receive-pack.c +++ b/receive-pack.c @@@ -166,7 -166,7 +166,7 @@@ static const char *update(struct comman struct ref_lock *lock; if (!prefixcmp(name, "refs/") && check_ref_format(name + 5)) { - error("refusing to create funny ref '%s' locally", name); + error("refusing to create funny ref '%s' remotely", name); return "funny refname"; } @@@ -382,9 -382,8 +382,8 @@@ static const char *unpack(void } } else { const char *keeper[6]; - int s, len, status; + int s, status; char keep_arg[256]; - char packname[46]; struct child_process ip; s = sprintf(keep_arg, "--keep=receive-pack %i on ", getpid()); @@@ -403,26 -402,7 +402,7 @@@ ip.git_cmd = 1; if (start_command(&ip)) return "index-pack fork failed"; - - /* - * The first thing we expects from index-pack's output - * is "pack\t%40s\n" or "keep\t%40s\n" (46 bytes) where - * %40s is the newly created pack SHA1 name. In the "keep" - * case, we need it to remove the corresponding .keep file - * later on. If we don't get that then tough luck with it. - */ - for (len = 0; - len < 46 && (s = xread(ip.out, packname+len, 46-len)) > 0; - len += s); - if (len == 46 && packname[45] == '\n' && - memcmp(packname, "keep\t", 5) == 0) { - char path[PATH_MAX]; - packname[45] = 0; - snprintf(path, sizeof(path), "%s/pack/pack-%s.keep", - get_object_directory(), packname + 5); - pack_lockfile = xstrdup(path); - } - + pack_lockfile = index_pack_lockfile(ip.out); status = finish_command(&ip); if (!status) { reprepare_packed_git(); diff --combined remote.c index cdbbdcb00d,b20e2be433..170015aabf --- a/remote.c +++ b/remote.c @@@ -5,6 -5,12 +5,12 @@@ 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]; @@@ -26,13 -32,13 +32,13 @@@ static void add_fetch_refspec(struct re remote->fetch_refspec_nr = nr; } - static void add_uri(struct remote *remote, const char *uri) + static void add_url(struct remote *remote, const char *url) { - int nr = remote->uri_nr + 1; - remote->uri = - xrealloc(remote->uri, nr * sizeof(char *)); - remote->uri[nr-1] = uri; - remote->uri_nr = nr; + 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) @@@ -67,6 -73,54 +73,54 @@@ 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"); @@@ -100,7 -154,7 +154,7 @@@ switch (value_list) { case 0: - add_uri(remote, xstrdup(s)); + add_url(remote, xstrdup(s)); break; case 1: add_push_refspec(remote, xstrdup(s)); @@@ -116,6 -170,8 +170,8 @@@ 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; @@@ -141,23 -197,41 +197,41 @@@ strcpy(p, s); if (slash) strcat(p, slash); - add_uri(remote, p); + 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 char *default_remote_name = NULL; - static const char *current_branch = NULL; - static int current_branch_len = 0; - static int handle_config(const char *key, const char *value) { const char *name; const char *subkey; struct remote *remote; - if (!prefixcmp(key, "branch.") && current_branch && - !strncmp(key + 7, current_branch, current_branch_len) && - !strcmp(key + 7 + current_branch_len, ".remote")) { - free(default_remote_name); - default_remote_name = xstrdup(value); + struct branch *branch; + if (!prefixcmp(key, "branch.")) { + name = key + 7; + subkey = strrchr(name, '.'); + branch = make_branch(name, subkey - name); + if (!subkey) + return 0; + if (!value) + return 0; + if (!strcmp(subkey, ".remote")) { + branch->remote_name = xstrdup(value); + if (branch == current_branch) + default_remote_name = branch->remote_name; + } else if (!strcmp(subkey, ".merge")) + add_merge(branch, xstrdup(value)); + return 0; } if (prefixcmp(key, "remote.")) return 0; @@@ -186,7 -260,7 +260,7 @@@ return 0; /* ignore unknown booleans */ } if (!strcmp(subkey, ".url")) { - add_uri(remote, xstrdup(value)); + add_url(remote, xstrdup(value)); } else if (!strcmp(subkey, ".push")) { add_push_refspec(remote, xstrdup(value)); } else if (!strcmp(subkey, ".fetch")) { @@@ -196,6 -270,14 +270,14 @@@ 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; } return 0; } @@@ -212,13 -294,13 +294,13 @@@ static void read_config(void head_ref = resolve_ref("HEAD", sha1, 0, &flag); if (head_ref && (flag & REF_ISSYMREF) && !prefixcmp(head_ref, "refs/heads/")) { - current_branch = head_ref + strlen("refs/heads/"); - current_branch_len = strlen(current_branch); + current_branch = + make_branch(head_ref + strlen("refs/heads/"), 0); } git_config(handle_config); } - static struct refspec *parse_ref_spec(int nr_refspec, const char **refspec) + struct refspec *parse_ref_spec(int nr_refspec, const char **refspec) { int i; struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec); @@@ -265,14 -347,14 +347,14 @@@ struct remote *remote_get(const char *n name = default_remote_name; ret = make_remote(name, 0); if (name[0] != '/') { - if (!ret->uri) + if (!ret->url) read_remotes_file(ret); - if (!ret->uri) + if (!ret->url) read_branches_file(ret); } - if (!ret->uri) - add_uri(ret, name); - if (!ret->uri) + if (!ret->url) + add_url(ret, name); + if (!ret->url) return NULL; ret->fetch = parse_ref_spec(ret->fetch_refspec_nr, ret->fetch_refspec); ret->push = parse_ref_spec(ret->push_refspec_nr, ret->push_refspec); @@@ -298,16 -380,62 +380,62 @@@ int for_each_remote(each_remote_fn fn, return result; } - int remote_has_uri(struct remote *remote, const char *uri) + 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->uri_nr; i++) { - if (!strcmp(remote->uri[i], uri)) + for (i = 0; i < remote->url_nr; i++) { + if (!strcmp(remote->url[i], url)) return 1; } 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; @@@ -315,7 -443,7 +443,7 @@@ int i; if (find_src) { - if (refspec->dst == NULL) + if (!refspec->dst) return error("find_tracking: need either src or dst"); needle = refspec->dst; result = &refspec->src; @@@ -357,6 -485,14 +485,14 @@@ struct ref *alloc_ref(unsigned namelen return ret; } + static struct ref *copy_ref(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; + } + void free_refs(struct ref *ref) { struct ref *next; @@@ -489,23 -625,23 +625,23 @@@ static int match_explicit(struct ref *s * way to delete 'other' ref at the remote end. */ matched_src = try_explicit_object_name(rs->src); - if (matched_src) - break; - error("src refspec %s does not match any.", - 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); + error("src refspec %s matches more than one.", rs->src); break; } if (!matched_src) errs = 1; - if (dst_value == NULL) + if (!dst_value) { + if (!matched_src) + return errs; dst_value = matched_src->name; + } switch (count_refspec_match(dst_value, dst, &matched_dst)) { case 1: @@@ -524,7 -660,7 +660,7 @@@ dst_value); break; } - if (errs || matched_dst == NULL) + if (errs || !matched_dst) return 1; if (matched_dst->peer_ref) { errs = 1; @@@ -626,10 -762,155 +762,157 @@@ int match_refs(struct ref *src, struct 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 ref_matches_abbrev(branch->merge[i]->src, refname); + } + + static struct ref *get_expanded_map(struct ref *remote_refs, + const struct refspec *refspec) + { + 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)) { + 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 struct ref *find_ref_by_name_abbrev(struct ref *refs, const char *name) + { + struct ref *ref; + for (ref = refs; ref; ref = ref->next) { + if (ref_matches_abbrev(name, ref->name)) + return ref; + } + return NULL; + } + + struct ref *get_remote_ref(struct ref *remote_refs, const char *name) + { + struct ref *ref = find_ref_by_name_abbrev(remote_refs, name); + + if (!ref) + die("Couldn't find remote ref %s\n", name); + + 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(struct ref *remote_refs, + const struct refspec *refspec, + struct ref ***tail) + { + struct ref *ref_map, *rm; + + if (refspec->pattern) { + ref_map = get_expanded_map(remote_refs, refspec); + } else { + ref_map = get_remote_ref(remote_refs, + refspec->src[0] ? + refspec->src : "HEAD"); + + ref_map->peer_ref = get_local_ref(refspec->dst); + if (ref_map->peer_ref && refspec->force) + ref_map->peer_ref->force = 1; + } + + for (rm = ref_map; rm; rm = rm->next) { + if (rm->peer_ref && check_ref_format(rm->peer_ref->name + 5)) + die("* refusing to create funny ref '%s' locally", + rm->peer_ref->name); + } + + if (ref_map) + tail_link_ref(ref_map, tail); + + return 0; + } diff --combined send-pack.c index c1807f0794,7b776243e6..e9b9a39f41 --- a/send-pack.c +++ b/send-pack.c @@@ -205,8 -205,7 +205,8 @@@ static int send_pack(int in, int out, s return -1; if (!remote_refs) { - fprintf(stderr, "No refs in common and none specified; doing nothing.\n"); + fprintf(stderr, "No refs in common and none specified; doing nothing.\n" + "Perhaps you should specify a branch such as 'master'.\n"); return 0; } @@@ -428,7 -427,7 +428,7 @@@ int main(int argc, char **argv if (remote_name) { remote = remote_get(remote_name); - if (!remote_has_uri(remote, dest)) { + if (!remote_has_url(remote, dest)) { die("Destination %s is not a uri for %s", dest, remote_name); }