static int push_all = MATCH_REFS_NONE;
static int force_all;
static int dry_run;
+static int helper_status;
static struct object_list *objects;
struct remote_lock *lock;
struct curl_slist *headers;
struct buffer buffer;
- char filename[PATH_MAX];
- char tmpfile[PATH_MAX];
- int local_fileno;
enum transfer_state state;
CURLcode curl_result;
char errorstr[CURL_ERROR_SIZE];
long http_code;
- unsigned char real_sha1[20];
- git_SHA_CTX c;
- z_stream stream;
- int zret;
- int rename;
void *userData;
struct active_request_slot *slot;
struct transfer_request *next;
case '&':
strbuf_addstr(&buf, "&");
break;
+ case 0:
+ return strbuf_detach(&buf, NULL);
}
s++;
}
return dav_headers;
}
-static void append_remote_object_url(struct strbuf *buf, const char *url,
- const char *hex,
- int only_two_digit_prefix)
-{
- strbuf_addf(buf, "%sobjects/%.*s/", url, 2, hex);
- if (!only_two_digit_prefix)
- strbuf_addf(buf, "%s", hex+2);
-}
-
static void finish_request(struct transfer_request *request);
static void release_request(struct transfer_request *request);
#ifdef USE_CURL_MULTI
-static char *get_remote_object_url(const char *url, const char *hex,
- int only_two_digit_prefix)
-{
- struct strbuf buf = STRBUF_INIT;
- append_remote_object_url(&buf, url, hex, only_two_digit_prefix);
- return strbuf_detach(&buf, NULL);
-}
-
-static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
- void *data)
-{
- unsigned char expn[4096];
- size_t size = eltsize * nmemb;
- int posn = 0;
- struct transfer_request *request = (struct transfer_request *)data;
- do {
- ssize_t retval = xwrite(request->local_fileno,
- (char *) ptr + posn, size - posn);
- if (retval < 0)
- return posn;
- posn += retval;
- } while (posn < size);
-
- request->stream.avail_in = size;
- request->stream.next_in = ptr;
- do {
- request->stream.next_out = expn;
- request->stream.avail_out = sizeof(expn);
- request->zret = git_inflate(&request->stream, Z_SYNC_FLUSH);
- git_SHA1_Update(&request->c, expn,
- sizeof(expn) - request->stream.avail_out);
- } while (request->stream.avail_in && request->zret == Z_OK);
- data_received++;
- return size;
-}
-
static void start_fetch_loose(struct transfer_request *request)
{
- char *hex = sha1_to_hex(request->obj->sha1);
- char *filename;
- char prevfile[PATH_MAX];
- char *url;
- int prevlocal;
- unsigned char prev_buf[PREV_BUF_SIZE];
- ssize_t prev_read = 0;
- long prev_posn = 0;
- char range[RANGE_HEADER_SIZE];
- struct curl_slist *range_header = NULL;
struct active_request_slot *slot;
+ struct http_object_request *obj_req;
- filename = sha1_file_name(request->obj->sha1);
- snprintf(request->filename, sizeof(request->filename), "%s", filename);
- snprintf(request->tmpfile, sizeof(request->tmpfile),
- "%s.temp", filename);
-
- snprintf(prevfile, sizeof(prevfile), "%s.prev", request->filename);
- unlink_or_warn(prevfile);
- rename(request->tmpfile, prevfile);
- unlink_or_warn(request->tmpfile);
-
- if (request->local_fileno != -1)
- error("fd leakage in start: %d", request->local_fileno);
- request->local_fileno = open(request->tmpfile,
- O_WRONLY | O_CREAT | O_EXCL, 0666);
- /*
- * This could have failed due to the "lazy directory creation";
- * try to mkdir the last path component.
- */
- if (request->local_fileno < 0 && errno == ENOENT) {
- char *dir = strrchr(request->tmpfile, '/');
- if (dir) {
- *dir = 0;
- mkdir(request->tmpfile, 0777);
- *dir = '/';
- }
- request->local_fileno = open(request->tmpfile,
- O_WRONLY | O_CREAT | O_EXCL, 0666);
- }
-
- if (request->local_fileno < 0) {
+ obj_req = new_http_object_request(repo->url, request->obj->sha1);
+ if (obj_req == NULL) {
request->state = ABORTED;
- error("Couldn't create temporary file %s for %s: %s",
- request->tmpfile, request->filename, strerror(errno));
return;
}
- memset(&request->stream, 0, sizeof(request->stream));
-
- git_inflate_init(&request->stream);
-
- git_SHA1_Init(&request->c);
-
- url = get_remote_object_url(repo->url, hex, 0);
- request->url = xstrdup(url);
-
- /*
- * If a previous temp file is present, process what was already
- * fetched.
- */
- prevlocal = open(prevfile, O_RDONLY);
- if (prevlocal != -1) {
- do {
- prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
- if (prev_read>0) {
- if (fwrite_sha1_file(prev_buf,
- 1,
- prev_read,
- request) == prev_read)
- prev_posn += prev_read;
- else
- prev_read = -1;
- }
- } while (prev_read > 0);
- close(prevlocal);
- }
- unlink_or_warn(prevfile);
-
- /*
- * Reset inflate/SHA1 if there was an error reading the previous temp
- * file; also rewind to the beginning of the local file.
- */
- if (prev_read == -1) {
- memset(&request->stream, 0, sizeof(request->stream));
- git_inflate_init(&request->stream);
- git_SHA1_Init(&request->c);
- if (prev_posn>0) {
- prev_posn = 0;
- lseek(request->local_fileno, 0, SEEK_SET);
- ftruncate(request->local_fileno, 0);
- }
- }
-
- slot = get_active_slot();
+ slot = obj_req->slot;
slot->callback_func = process_response;
slot->callback_data = request;
request->slot = slot;
-
- curl_easy_setopt(slot->curl, CURLOPT_FILE, request);
- curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
- curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);
- curl_easy_setopt(slot->curl, CURLOPT_URL, url);
- curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
-
- /*
- * If we have successfully processed data from a previous fetch
- * attempt, only fetch the data we don't already have.
- */
- if (prev_posn>0) {
- if (push_verbosely)
- fprintf(stderr,
- "Resuming fetch of object %s at byte %ld\n",
- hex, prev_posn);
- sprintf(range, "Range: bytes=%ld-", prev_posn);
- range_header = curl_slist_append(range_header, range);
- curl_easy_setopt(slot->curl,
- CURLOPT_HTTPHEADER, range_header);
- }
+ request->userData = obj_req;
/* Try to get the request started, abort the request on error */
request->state = RUN_FETCH_LOOSE;
if (!start_active_slot(slot)) {
fprintf(stderr, "Unable to start GET request\n");
repo->can_update_info_refs = 0;
+ release_http_object_request(obj_req);
release_request(request);
}
}
curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, &request->buffer);
#endif
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
+ curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
curl_easy_setopt(slot->curl, CURLOPT_PUT, 1);
- curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
if (start_active_slot(slot)) {
entry->next = entry->next->next;
}
- if (request->local_fileno != -1)
- close(request->local_fileno);
free(request->url);
free(request);
}
static void finish_request(struct transfer_request *request)
{
- struct stat st;
struct http_pack_request *preq;
+ struct http_object_request *obj_req;
request->curl_result = request->slot->curl_result;
request->http_code = request->slot->http_code;
aborted = 1;
}
} else if (request->state == RUN_FETCH_LOOSE) {
- close(request->local_fileno);
- request->local_fileno = -1;
-
- if (request->curl_result != CURLE_OK &&
- request->http_code != 416) {
- if (stat(request->tmpfile, &st) == 0) {
- if (st.st_size == 0)
- unlink_or_warn(request->tmpfile);
- }
- } else {
- if (request->http_code == 416)
- warning("requested range invalid; we may already have all the data.");
-
- git_inflate_end(&request->stream);
- git_SHA1_Final(request->real_sha1, &request->c);
- if (request->zret != Z_STREAM_END) {
- unlink_or_warn(request->tmpfile);
- } else if (hashcmp(request->obj->sha1, request->real_sha1)) {
- unlink_or_warn(request->tmpfile);
- } else {
- request->rename =
- move_temp_to_file(
- request->tmpfile,
- request->filename);
- if (request->rename == 0)
- request->obj->flags |= (LOCAL | REMOTE);
- }
- }
+ obj_req = (struct http_object_request *)request->userData;
+
+ if (finish_http_object_request(obj_req) == 0)
+ if (obj_req->rename == 0)
+ request->obj->flags |= (LOCAL | REMOTE);
/* Try fetching packed if necessary */
- if (request->obj->flags & LOCAL)
+ if (request->obj->flags & LOCAL) {
+ release_http_object_request(obj_req);
release_request(request);
- else
+ } else
start_fetch_packed(request);
} else if (request->state == RUN_FETCH_PACKED) {
preq = (struct http_pack_request *)request->userData;
if (preq) {
- if (finish_http_pack_request(preq) > 0)
+ if (finish_http_pack_request(preq) == 0)
fail = 0;
release_http_pack_request(preq);
}
request->url = NULL;
request->lock = NULL;
request->headers = NULL;
- request->local_fileno = -1;
request->state = NEED_FETCH;
request->next = request_queue_head;
request_queue_head = request;
request->url = NULL;
request->lock = lock;
request->headers = NULL;
- request->local_fileno = -1;
request->state = NEED_PUSH;
request->next = request_queue_head;
request_queue_head = request;
fprintf(stderr, "Removing remote locks...\n");
while (lock) {
+ struct remote_lock *next = lock->next;
unlock_remote(lock);
- lock = lock->next;
+ lock = next;
}
}
return 1;
}
-static struct ref *remote_refs, **remote_tail;
+static struct ref *remote_refs;
static void one_remote_ref(char *refname)
{
}
}
- *remote_tail = ref;
- remote_tail = &ref->next;
+ ref->next = remote_refs;
+ remote_refs = ref;
}
static void get_dav_remote_heads(void)
{
- remote_tail = &remote_refs;
remote_ls("refs/", (PROCESS_FILES | PROCESS_DIRS | RECURSIVE), process_ls_ref, NULL);
}
-static int is_zero_sha1(const unsigned char *sha1)
-{
- int i;
-
- for (i = 0; i < 20; i++) {
- if (*sha1++)
- return 0;
- }
- return 1;
-}
-
static void add_remote_info_ref(struct remote_ls_ctx *ls)
{
struct strbuf *buf = (struct strbuf *)ls->userData;
/* Remote HEAD must resolve to a known object */
if (symref)
return error("Remote HEAD symrefs too deep");
- if (is_zero_sha1(head_sha1))
+ if (is_null_sha1(head_sha1))
return error("Unable to resolve remote HEAD");
if (!has_sha1_file(head_sha1))
return error("Remote HEAD resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", sha1_to_hex(head_sha1));
/* Remote branch must resolve to a known object */
- if (is_zero_sha1(remote_ref->old_sha1))
+ if (is_null_sha1(remote_ref->old_sha1))
return error("Unable to resolve remote branch %s",
remote_ref->name);
if (!has_sha1_file(remote_ref->old_sha1))
return 0;
}
-void run_request_queue(void)
+static void run_request_queue(void)
{
#ifdef USE_CURL_MULTI
is_running_queue = 1;
git_extract_argv0_path(argv[0]);
- setup_git_directory();
-
repo = xcalloc(sizeof(*repo), 1);
argv++;
dry_run = 1;
continue;
}
+ if (!strcmp(arg, "--helper-status")) {
+ helper_status = 1;
+ continue;
+ }
if (!strcmp(arg, "--verbose")) {
push_verbosely = 1;
http_is_verbose = 1;
force_delete = 1;
continue;
}
+ if (!strcmp(arg, "-h"))
+ usage(http_push_usage);
}
if (!repo->url) {
char *path = strstr(arg, "//");
if (delete_branch && nr_refspec != 1)
die("You must specify only one branch name when deleting a remote branch");
+ setup_git_directory();
+
memset(remote_dir_exists, -1, 256);
/*
/* Remove a remote branch if -d or -D was specified */
if (delete_branch) {
- if (delete_remote_branch(refspec[0], force_delete) == -1)
+ if (delete_remote_branch(refspec[0], force_delete) == -1) {
fprintf(stderr, "Unable to delete remote branch %s\n",
refspec[0]);
+ if (helper_status)
+ printf("error %s cannot remove\n", refspec[0]);
+ }
goto cleanup;
}
/* match them up */
- if (!remote_tail)
- remote_tail = &remote_refs;
- if (match_refs(local_refs, remote_refs, &remote_tail,
+ if (match_refs(local_refs, &remote_refs,
nr_refspec, (const char **) refspec, push_all)) {
rc = -1;
goto cleanup;
}
if (!remote_refs) {
fprintf(stderr, "No refs in common and none specified; doing nothing.\n");
+ if (helper_status)
+ printf("error null no match\n");
rc = 0;
goto cleanup;
}
new_refs = 0;
for (ref = remote_refs; ref; ref = ref->next) {
char old_hex[60], *new_hex;
- const char *commit_argv[4];
+ const char *commit_argv[5];
int commit_argc;
char *new_sha1_hex, *old_sha1_hex;
if (!ref->peer_ref)
continue;
- if (is_zero_sha1(ref->peer_ref->new_sha1)) {
+ if (is_null_sha1(ref->peer_ref->new_sha1)) {
if (delete_remote_branch(ref->name, 1) == -1) {
error("Could not remove %s", ref->name);
+ if (helper_status)
+ printf("error %s cannot remove\n", ref->name);
rc = -4;
}
+ else if (helper_status)
+ printf("ok %s\n", ref->name);
new_refs++;
continue;
}
if (!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
if (push_verbosely || 1)
fprintf(stderr, "'%s': up-to-date\n", ref->name);
+ if (helper_status)
+ printf("ok %s up to date\n", ref->name);
continue;
}
if (!force_all &&
- !is_zero_sha1(ref->old_sha1) &&
+ !is_null_sha1(ref->old_sha1) &&
!ref->force) {
if (!has_sha1_file(ref->old_sha1) ||
!ref_newer(ref->peer_ref->new_sha1,
"need to pull first?",
ref->name,
ref->peer_ref->name);
+ if (helper_status)
+ printf("error %s non-fast forward\n", ref->name);
rc = -2;
continue;
}
if (strcmp(ref->name, ref->peer_ref->name))
fprintf(stderr, " using '%s'", ref->peer_ref->name);
fprintf(stderr, "\n from %s\n to %s\n", old_hex, new_hex);
- if (dry_run)
+ if (dry_run) {
+ if (helper_status)
+ printf("ok %s\n", ref->name);
continue;
+ }
/* Lock remote branch ref */
ref_lock = lock_remote(ref->name, LOCK_TIME);
if (ref_lock == NULL) {
fprintf(stderr, "Unable to lock remote branch %s\n",
ref->name);
+ if (helper_status)
+ printf("error %s lock error\n", ref->name);
rc = 1;
continue;
}
old_sha1_hex = NULL;
commit_argv[1] = "--objects";
commit_argv[2] = new_sha1_hex;
- if (!push_all && !is_zero_sha1(ref->old_sha1)) {
+ if (!push_all && !is_null_sha1(ref->old_sha1)) {
old_sha1_hex = xmalloc(42);
sprintf(old_sha1_hex, "^%s",
sha1_to_hex(ref->old_sha1));
commit_argv[3] = old_sha1_hex;
commit_argc++;
}
+ commit_argv[commit_argc] = NULL;
init_revisions(&revs, setup_git_directory());
setup_revisions(commit_argc, commit_argv, &revs, NULL);
revs.edge_hint = 0; /* just in case */
if (!rc)
fprintf(stderr, " done\n");
+ if (helper_status)
+ printf("%s %s\n", !rc ? "ok" : "error", ref->name);
unlock_remote(ref_lock);
check_locks();
}