#include "cache.h"
#include "remote.h"
#include "refs.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
static struct refspec s_tag_refspec = {
0,
if (!longest)
return url;
- ret = malloc(rewrite[longest_i]->baselen +
+ ret = xmalloc(rewrite[longest_i]->baselen +
(strlen(url) - longest->len) + 1);
strcpy(ret, rewrite[longest_i]->base);
strcpy(ret + rewrite[longest_i]->baselen, url + longest->len);
ret->name = xstrndup(name, len);
else
ret->name = xstrdup(name);
- refname = malloc(strlen(name) + strlen("refs/heads/") + 1);
+ refname = xmalloc(strlen(name) + strlen("refs/heads/") + 1);
strcpy(refname, "refs/heads/");
strcpy(refname + strlen("refs/heads/"), ret->name);
ret->refname = refname;
{
const char *slash = strchr(remote->name, '/');
char *frag;
- struct strbuf branch;
+ struct strbuf branch = STRBUF_INIT;
int n = slash ? slash - remote->name : 1000;
FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r");
char *s, *p;
* #branch specified. The "master" (or specified) branch is
* fetched and stored in the local branch of the same name.
*/
- strbuf_init(&branch, 0);
frag = strchr(p, '#');
if (frag) {
*(frag++) = '\0';
if (prefixcmp(key, "remote."))
return 0;
name = key + 7;
+ if (*name == '/') {
+ warning("Config remote shorthand cannot begin with '/': %s",
+ name);
+ return 0;
+ }
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 (!strcmp(subkey, ".mirror"))
remote->mirror = git_config_bool(key, value);
return result;
}
+/*
+ * This function frees a refspec array.
+ * Warning: code paths should be checked to ensure that the src
+ * and dst pointers are always freeable pointers as well
+ * as the refspec pointer itself.
+ */
+static void free_refspecs(struct refspec *refspec, int nr_refspec)
+{
+ int i;
+
+ if (!refspec)
+ return;
+
+ for (i = 0; i < nr_refspec; i++) {
+ free(refspec[i].src);
+ free(refspec[i].dst);
+ }
+ free(refspec);
+}
+
static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
{
int i;
invalid:
if (verify) {
- free(rs);
+ /*
+ * nr_refspec must be greater than zero and i must be valid
+ * since it is only possible to reach this point from within
+ * the for loop above.
+ */
+ free_refspecs(rs, i+1);
return NULL;
}
die("Invalid refspec '%s'", refspec[i]);
struct refspec *refspec;
refspec = parse_refspec_internal(1, fetch_refspec, 1, 1);
- if (refspec)
- free(refspec);
+ free_refspecs(refspec, 1);
return !!refspec;
}
return parse_refspec_internal(nr_refspec, refspec, 1, 0);
}
-struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
+static struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
{
return parse_refspec_internal(nr_refspec, refspec, 0, 0);
}
struct ref *alloc_ref(unsigned namelen)
{
- struct ref *ret = xmalloc(sizeof(struct ref) + namelen);
- memset(ret, 0, sizeof(struct ref) + namelen);
+ struct ref *ret = xcalloc(1, sizeof(struct ref) + namelen);
return ret;
}
return ret;
}
-void free_ref(struct ref *ref)
+static void free_ref(struct ref *ref)
{
if (!ref)
return;
}
return 1;
}
+
+/*
+ * Return true if there is anything to report, otherwise false.
+ */
+int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
+{
+ unsigned char sha1[20];
+ struct commit *ours, *theirs;
+ char symmetric[84];
+ struct rev_info revs;
+ const char *rev_argv[10], *base;
+ int rev_argc;
+
+ /*
+ * Nothing to report unless we are marked to build on top of
+ * somebody else.
+ */
+ if (!branch ||
+ !branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
+ return 0;
+
+ /*
+ * If what we used to build on no longer exists, there is
+ * nothing to report.
+ */
+ base = branch->merge[0]->dst;
+ if (!resolve_ref(base, sha1, 1, NULL))
+ return 0;
+ theirs = lookup_commit(sha1);
+ if (!theirs)
+ return 0;
+
+ if (!resolve_ref(branch->refname, sha1, 1, NULL))
+ return 0;
+ ours = lookup_commit(sha1);
+ if (!ours)
+ return 0;
+
+ /* are we the same? */
+ if (theirs == ours)
+ return 0;
+
+ /* Run "rev-list --left-right ours...theirs" internally... */
+ rev_argc = 0;
+ rev_argv[rev_argc++] = NULL;
+ rev_argv[rev_argc++] = "--left-right";
+ rev_argv[rev_argc++] = symmetric;
+ rev_argv[rev_argc++] = "--";
+ rev_argv[rev_argc] = NULL;
+
+ strcpy(symmetric, sha1_to_hex(ours->object.sha1));
+ strcpy(symmetric + 40, "...");
+ strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1));
+
+ init_revisions(&revs, NULL);
+ setup_revisions(rev_argc, rev_argv, &revs, NULL);
+ prepare_revision_walk(&revs);
+
+ /* ... and count the commits on each side. */
+ *num_ours = 0;
+ *num_theirs = 0;
+ while (1) {
+ struct commit *c = get_revision(&revs);
+ if (!c)
+ break;
+ if (c->object.flags & SYMMETRIC_LEFT)
+ (*num_ours)++;
+ else
+ (*num_theirs)++;
+ }
+
+ /* clear object flags smudged by the above traversal */
+ clear_commit_marks(ours, ALL_REV_FLAGS);
+ clear_commit_marks(theirs, ALL_REV_FLAGS);
+ return 1;
+}
+
+/*
+ * Return true when there is anything to report, otherwise false.
+ */
+int format_tracking_info(struct branch *branch, struct strbuf *sb)
+{
+ int num_ours, num_theirs;
+ const char *base;
+
+ if (!stat_tracking_info(branch, &num_ours, &num_theirs))
+ return 0;
+
+ base = branch->merge[0]->dst;
+ if (!prefixcmp(base, "refs/remotes/")) {
+ base += strlen("refs/remotes/");
+ }
+ if (!num_theirs)
+ strbuf_addf(sb, "Your branch is ahead of '%s' "
+ "by %d commit%s.\n",
+ base, num_ours, (num_ours == 1) ? "" : "s");
+ else if (!num_ours)
+ strbuf_addf(sb, "Your branch is behind '%s' "
+ "by %d commit%s, "
+ "and can be fast-forwarded.\n",
+ base, num_theirs, (num_theirs == 1) ? "" : "s");
+ else
+ strbuf_addf(sb, "Your branch and '%s' have diverged,\n"
+ "and have %d and %d different commit(s) each, "
+ "respectively.\n",
+ base, num_ours, num_theirs);
+ return 1;
+}