summaryrefslogtreecommitdiff
path: root/remote.c
diff options
context:
space:
mode:
Diffstat (limited to 'remote.c')
-rw-r--r--remote.c124
1 files changed, 48 insertions, 76 deletions
diff --git a/remote.c b/remote.c
index 4b1153f02c..174e48eb74 100644
--- a/remote.c
+++ b/remote.c
@@ -15,6 +15,7 @@ static struct refspec s_tag_refspec = {
0,
1,
0,
+ 0,
"refs/tags/*",
"refs/tags/*"
};
@@ -538,7 +539,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
/*
* Before going on, special case ":" (or "+:") as a refspec
- * for matching refs.
+ * for pushing matching refs.
*/
if (!fetch && rhs == lhs && rhs[1] == '\0') {
rs[i].matching = 1;
@@ -565,26 +566,25 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
if (fetch) {
- /*
- * LHS
- * - empty is allowed; it means HEAD.
- * - otherwise it must be a valid looking ref.
- */
+ unsigned char unused[40];
+
+ /* LHS */
if (!*rs[i].src)
- ; /* empty is ok */
- else if (check_refname_format(rs[i].src, flags))
+ ; /* empty is ok; it means "HEAD" */
+ else if (llen == 40 && !get_sha1_hex(rs[i].src, unused))
+ rs[i].exact_sha1 = 1; /* ok */
+ else if (!check_refname_format(rs[i].src, flags))
+ ; /* valid looking ref is ok */
+ else
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.
- */
+ /* RHS */
if (!rs[i].dst)
- ; /* ok */
+ ; /* missing is ok; it is the same as empty */
else if (!*rs[i].dst)
- ; /* ok */
- else if (check_refname_format(rs[i].dst, flags))
+ ; /* empty is ok; it means "do not store" */
+ else if (!check_refname_format(rs[i].dst, flags))
+ ; /* valid looking ref is ok */
+ else
goto invalid;
} else {
/*
@@ -1279,26 +1279,6 @@ int match_push_refs(struct ref *src, struct ref **dst,
return 0;
}
-static inline int is_forwardable(struct ref* ref)
-{
- struct object *o;
-
- if (!prefixcmp(ref->name, "refs/tags/"))
- return 0;
-
- /* old object must be a commit */
- o = parse_object(ref->old_sha1);
- if (!o || o->type != OBJ_COMMIT)
- return 0;
-
- /* new object must be commit-ish */
- o = deref_tag(parse_object(ref->new_sha1), NULL, 0);
- if (!o || o->type != OBJ_COMMIT)
- return 0;
-
- return 1;
-}
-
void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
int force_update)
{
@@ -1320,54 +1300,40 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
}
/*
- * The below logic determines whether an individual
- * refspec A:B can be pushed. The push will succeed
- * if any of the following are true:
+ * Decide whether an individual refspec A:B can be
+ * pushed. The push will succeed if any of the
+ * following are true:
*
* (1) the remote reference B does not exist
*
* (2) the remote reference B is being removed (i.e.,
* pushing :B where no source is specified)
*
- * (3) the update meets all fast-forwarding criteria:
- *
- * (a) the destination is not under refs/tags/
- * (b) the old is a commit
- * (c) the new is a descendant of the old
- *
- * NOTE: We must actually have the old object in
- * order to overwrite it in the remote reference,
- * and the new object must be commit-ish. These are
- * implied by (b) and (c) respectively.
+ * (3) the destination is not under refs/tags/, and
+ * if the old and new value is a commit, the new
+ * is a descendant of the old.
*
* (4) it is forced using the +A:B notation, or by
* passing the --force argument
*/
- ref->not_forwardable = !is_forwardable(ref);
-
- ref->update =
- !ref->deletion &&
- !is_null_sha1(ref->old_sha1);
-
- if (ref->update) {
- ref->nonfastforward =
- !has_sha1_file(ref->old_sha1)
- || !ref_newer(ref->new_sha1, ref->old_sha1);
-
- if (ref->not_forwardable) {
- ref->requires_force = 1;
- if (!force_ref_update) {
- ref->status = REF_STATUS_REJECT_ALREADY_EXISTS;
- continue;
- }
- } else if (ref->nonfastforward) {
- ref->requires_force = 1;
- if (!force_ref_update) {
- ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
- continue;
- }
- }
+ if (!ref->deletion && !is_null_sha1(ref->old_sha1)) {
+ int why = 0; /* why would this push require --force? */
+
+ if (!prefixcmp(ref->name, "refs/tags/"))
+ why = REF_STATUS_REJECT_ALREADY_EXISTS;
+ else if (!has_sha1_file(ref->old_sha1))
+ why = REF_STATUS_REJECT_FETCH_FIRST;
+ else if (!lookup_commit_reference_gently(ref->old_sha1, 1) ||
+ !lookup_commit_reference_gently(ref->new_sha1, 1))
+ why = REF_STATUS_REJECT_NEEDS_FORCE;
+ else if (!ref_newer(ref->new_sha1, ref->old_sha1))
+ why = REF_STATUS_REJECT_NONFASTFORWARD;
+
+ if (!force_ref_update)
+ ref->status = why;
+ else if (why)
+ ref->forced_update = 1;
}
}
}
@@ -1500,7 +1466,12 @@ int get_fetch_map(const struct ref *remote_refs,
} else {
const char *name = refspec->src[0] ? refspec->src : "HEAD";
- ref_map = get_remote_ref(remote_refs, name);
+ if (refspec->exact_sha1) {
+ ref_map = alloc_ref(name);
+ get_sha1_hex(name, ref_map->old_sha1);
+ } else {
+ ref_map = get_remote_ref(remote_refs, name);
+ }
if (!missing_ok && !ref_map)
die("Couldn't find remote ref %s", name);
if (ref_map) {
@@ -1561,7 +1532,8 @@ int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
struct commit_list *list, *used;
int found = 0;
- /* Both new and old must be commit-ish and new is descendant of
+ /*
+ * Both new and old must be commit-ish and new is descendant of
* old. Otherwise we require --force.
*/
o = deref_tag(parse_object(old_sha1), NULL, 0);