#
# 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.
#
GITWEB_HOME_LINK_STR = projects
GITWEB_SITENAME =
GITWEB_PROJECTROOT = /pub/git
+GITWEB_PROJECT_MAXDEPTH = 2007
GITWEB_EXPORT_OK =
GITWEB_STRICT_EXPORT =
GITWEB_BASE_URL =
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 \
# ... 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 \
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
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 \
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 \
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 \
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
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
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
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
-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' \
$(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
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"; \
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
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] == '.') &&
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++;
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;
+ }
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];
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)
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");
switch (value_list) {
case 0:
- add_uri(remote, xstrdup(s));
+ add_url(remote, xstrdup(s));
break;
case 1:
add_push_refspec(remote, xstrdup(s));
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;
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;
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")) {
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;
}
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);
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);
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;
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;
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;
* 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:
dst_value);
break;
}
- if (errs || matched_dst == NULL)
+ if (errs || !matched_dst)
return 1;
if (matched_dst->peer_ref) {
errs = 1;
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;
+ }