#include "send-pack.h"
static const char send_pack_usage[] =
-"git-send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
+"git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
" --all and explicit <ref> specification are mutually exclusive.";
static struct send_pack_args args = {
/* .receivepack = */ "git-receive-pack",
};
+static int feed_object(const unsigned char *sha1, int fd, int negative)
+{
+ char buf[42];
+
+ if (negative && !has_sha1_file(sha1))
+ return 1;
+
+ memcpy(buf + negative, sha1_to_hex(sha1), 40);
+ if (negative)
+ buf[0] = '^';
+ buf[40 + negative] = '\n';
+ return write_or_whine(fd, buf, 41 + negative, "send-pack: send refs");
+}
+
/*
* Make a pack stream and spit it out into file descriptor fd
*/
-static int pack_objects(int fd, struct ref *refs)
+static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra)
{
/*
* The child becomes pack-objects --revs; we feed
NULL,
};
struct child_process po;
+ int i;
if (args.use_thin_pack)
argv[4] = "--thin";
po.out = fd;
po.git_cmd = 1;
if (start_command(&po))
- die("git-pack-objects failed (%s)", strerror(errno));
+ die("git pack-objects failed (%s)", strerror(errno));
/*
* We feed the pack-objects we just spawned with revision
* parameters by writing to the pipe.
*/
- while (refs) {
- char buf[42];
+ for (i = 0; i < extra->nr; i++)
+ if (!feed_object(extra->array[i], po.in, 1))
+ break;
+ while (refs) {
if (!is_null_sha1(refs->old_sha1) &&
- has_sha1_file(refs->old_sha1)) {
- memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);
- buf[0] = '^';
- buf[41] = '\n';
- if (!write_or_whine(po.in, buf, 42,
- "send-pack: send refs"))
- break;
- }
- if (!is_null_sha1(refs->new_sha1)) {
- memcpy(buf, sha1_to_hex(refs->new_sha1), 40);
- buf[40] = '\n';
- if (!write_or_whine(po.in, buf, 41,
- "send-pack: send refs"))
- break;
- }
+ !feed_object(refs->old_sha1, po.in, 1))
+ break;
+ if (!is_null_sha1(refs->new_sha1) &&
+ !feed_object(refs->new_sha1, po.in, 0))
+ break;
refs = refs->next;
}
static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
struct ref *ref;
- int len = strlen(refname) + 1;
+ int len;
+
+ /* we already know it starts with refs/ to get here */
+ if (check_ref_format(refname + 5))
+ return 0;
+
+ len = strlen(refname) + 1;
ref = xcalloc(1, sizeof(*ref) + len);
hashcpy(ref->new_sha1, sha1);
memcpy(ref->name, refname, len);
{
struct refspec rs;
- if (ref->status != REF_STATUS_OK)
+ if (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE)
return;
rs.src = ref->name;
if (args.verbose)
fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
if (ref->deletion) {
- delete_ref(rs.dst, NULL);
+ delete_ref(rs.dst, NULL, 0);
} else
update_ref("update by push", rs.dst,
ref->new_sha1, NULL, 0, 0);
int expect_status_report = 0;
int flags = MATCH_REFS_NONE;
int ret;
+ struct extra_have_objects extra_have;
+ memset(&extra_have, 0, sizeof(extra_have));
if (args.send_all)
flags |= MATCH_REFS_ALL;
if (args.send_mirror)
flags |= MATCH_REFS_MIRROR;
/* No funny business with the matcher */
- remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL);
+ remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL,
+ &extra_have);
get_local_heads();
/* Does the other end support the reporting? */
*/
new_refs = 0;
for (ref = remote_refs; ref; ref = ref->next) {
- const unsigned char *new_sha1;
-
- if (!ref->peer_ref) {
- if (!args.send_mirror)
- continue;
- new_sha1 = null_sha1;
- }
- else
- new_sha1 = ref->peer_ref->new_sha1;
+ if (ref->peer_ref)
+ hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
+ else if (!args.send_mirror)
+ continue;
- ref->deletion = is_null_sha1(new_sha1);
+ ref->deletion = is_null_sha1(ref->new_sha1);
if (ref->deletion && !allow_deleting_refs) {
ref->status = REF_STATUS_REJECT_NODELETE;
continue;
}
if (!ref->deletion &&
- !hashcmp(ref->old_sha1, new_sha1)) {
+ !hashcmp(ref->old_sha1, ref->new_sha1)) {
ref->status = REF_STATUS_UPTODATE;
continue;
}
!ref->deletion &&
!is_null_sha1(ref->old_sha1) &&
(!has_sha1_file(ref->old_sha1)
- || !ref_newer(new_sha1, ref->old_sha1));
+ || !ref_newer(ref->new_sha1, ref->old_sha1));
if (ref->nonfastforward && !ref->force && !args.force_update) {
ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
continue;
}
- hashcpy(ref->new_sha1, new_sha1);
if (!ref->deletion)
new_refs++;
packet_flush(out);
if (new_refs && !args.dry_run) {
- if (pack_objects(out, remote_refs) < 0)
+ if (pack_objects(out, remote_refs, &extra_have) < 0)
return -1;
}
else