struct refspec {
char *src;
char *dst;
+ char force;
};
+/*
+ * A:B means fast forward remote B with local A.
+ * +A:B means overwrite remote B with local A.
+ * +A is a shorthand for +A:A.
+ * A is a shorthand for A:A.
+ */
static struct refspec *parse_ref_spec(int nr_refspec, char **refspec)
{
int i;
- struct refspec *rs = xmalloc(sizeof(*rs) * (nr_refspec + 1));
+ struct refspec *rs = xcalloc(sizeof(*rs), (nr_refspec + 1));
for (i = 0; i < nr_refspec; i++) {
char *sp, *dp, *ep;
sp = refspec[i];
+ if (*sp == '+') {
+ rs[i].force = 1;
+ sp++;
+ }
ep = strchr(sp, ':');
if (ep) {
dp = ep + 1;
**tail = NULL;
}
+static struct ref *try_explicit_object_name(const char *name)
+{
+ unsigned char sha1[20];
+ struct ref *ref;
+ int len;
+ if (get_sha1(name, sha1))
+ return NULL;
+ len = strlen(name) + 1;
+ ref = xcalloc(1, sizeof(*ref) + len);
+ memcpy(ref->name, name, len);
+ memcpy(ref->new_sha1, sha1, 20);
+ return ref;
+}
+
static int match_explicit_refs(struct ref *src, struct ref *dst,
struct ref ***dst_tail, struct refspec *rs)
{
case 1:
break;
case 0:
+ /* The source could be in the get_sha1() format
+ * not a reference name.
+ */
+ matched_src = try_explicit_object_name(rs[i].src);
+ if (matched_src)
+ break;
errs = 1;
- error("src refspec %s does not match any.");
+ error("src refspec %s does not match any.",
+ rs[i].src);
break;
default:
errs = 1;
/* pushing "master:master" when
* remote does not have master yet.
*/
- int len = strlen(matched_src->name);
+ int len = strlen(matched_src->name) + 1;
matched_dst = xcalloc(1, sizeof(*dst) + len);
memcpy(matched_dst->name, matched_src->name,
len);
}
if (errs)
continue;
- if (matched_src->peer_ref) {
- errs = 1;
- error("src ref %s is sent to more than one dst.",
- matched_src->name);
- }
- else
- matched_src->peer_ref = matched_dst;
if (matched_dst->peer_ref) {
errs = 1;
error("dst ref %s receives from more than one src.",
matched_dst->name);
}
- else
+ else {
matched_dst->peer_ref = matched_src;
+ matched_dst->force = rs[i].force;
+ }
}
return -errs;
}
close(pipefd[0][1]);
close(pipefd[1][0]);
close(pipefd[1][1]);
- if (protocol == PROTO_SSH)
- execlp("ssh", "ssh", host, command, NULL);
+ if (protocol == PROTO_SSH) {
+ const char *ssh, *ssh_basename;
+ ssh = getenv("GIT_SSH");
+ if (!ssh) ssh = "ssh";
+ ssh_basename = strrchr(ssh, '/');
+ if (!ssh_basename)
+ ssh_basename = ssh;
+ else
+ ssh_basename++;
+ execlp(ssh, ssh_basename, host, command, NULL);
+ }
else
execlp("sh", "sh", "-c", command, NULL);
die("exec failed");