diff options
author | Junio C Hamano <junkio@cox.net> | 2006-03-07 17:07:40 -0800 |
---|---|---|
committer | Junio C Hamano <junkio@cox.net> | 2006-03-07 17:07:40 -0800 |
commit | 2acc35b08706234f3762992d431db301ead5797e (patch) | |
tree | a1de0806e9be091287c42e4181b70530014e24f0 | |
parent | Merge branch 'master' into next (diff) | |
parent | repo-config: give value_ a sane default so regexec won't segfault (diff) | |
download | tgif-2acc35b08706234f3762992d431db301ead5797e.tar.xz |
Merge branch 'master' into next
* master:
repo-config: give value_ a sane default so regexec won't segfault
Update http-push functionality
cvsimport: Remove master-updating code
-rw-r--r-- | Documentation/git-cvsimport.txt | 6 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rwxr-xr-x | git-cvsimport.perl | 27 | ||||
-rw-r--r-- | http-push.c | 1060 | ||||
-rw-r--r-- | repo-config.c | 5 | ||||
-rwxr-xr-x | t/t1300-repo-config.sh | 8 |
6 files changed, 747 insertions, 361 deletions
diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt index dfe86ceea3..57027b448f 100644 --- a/Documentation/git-cvsimport.txt +++ b/Documentation/git-cvsimport.txt @@ -22,6 +22,12 @@ repository, or incrementally import into an existing one. Splitting the CVS log into patch sets is done by 'cvsps'. At least version 2.1 is required. +You should *never* do any work of your own on the branches that are +created by git-cvsimport. The initial import will create and populate a +"master" branch from the CVS repository's main branch which you're free +to work with; after that, you need to 'git merge' incremental imports, or +any CVS branches, yourself. + OPTIONS ------- -d <CVSROOT>:: @@ -526,7 +526,7 @@ git-http-fetch$X: fetch.o http.o http-fetch.o $(LIB_FILE) $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIBS) $(CURL_LIBCURL) -git-http-push$X: http.o http-push.o $(LIB_FILE) +git-http-push$X: revision.o http.o http-push.o $(LIB_FILE) $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index b46469ab32..02d1928ada 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -452,7 +452,6 @@ chdir($git_tree); my $last_branch = ""; my $orig_branch = ""; -my $forward_master = 0; my %branch_date; my $git_dir = $ENV{"GIT_DIR"} || ".git"; @@ -488,21 +487,6 @@ unless(-d $git_dir) { $last_branch = "master"; } $orig_branch = $last_branch; - if (-f "$git_dir/CVS2GIT_HEAD") { - die <<EOM; -CVS2GIT_HEAD exists. -Make sure your working directory corresponds to HEAD and remove CVS2GIT_HEAD. -You may need to run - - git read-tree -m -u CVS2GIT_HEAD HEAD -EOM - } - system('cp', "$git_dir/HEAD", "$git_dir/CVS2GIT_HEAD"); - - $forward_master = - $opt_o ne 'master' && -f "$git_dir/refs/heads/master" && - system('cmp', '-s', "$git_dir/refs/heads/master", - "$git_dir/refs/heads/$opt_o") == 0; # populate index system('git-read-tree', $last_branch); @@ -889,17 +873,11 @@ if (defined $orig_git_index) { # Now switch back to the branch we were in before all of this happened if($orig_branch) { - print "DONE\n" if $opt_v; - system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master") - if $forward_master; - unless ($opt_i) { - system('git-read-tree', '-m', '-u', 'CVS2GIT_HEAD', 'HEAD'); - die "read-tree failed: $?\n" if $?; - } + print "DONE; you may need to merge manually.\n" if $opt_v; } else { $orig_branch = "master"; print "DONE; creating $orig_branch branch\n" if $opt_v; - system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master") + system("git-update-ref", "refs/heads/master", "refs/heads/$opt_o") unless -f "$git_dir/refs/heads/master"; system('git-update-ref', 'HEAD', "$orig_branch"); unless ($opt_i) { @@ -907,4 +885,3 @@ if($orig_branch) { die "checkout failed: $?\n" if $?; } } -unlink("$git_dir/CVS2GIT_HEAD"); diff --git a/http-push.c b/http-push.c index fe925609b4..226d71966d 100644 --- a/http-push.c +++ b/http-push.c @@ -5,6 +5,8 @@ #include "tag.h" #include "blob.h" #include "http.h" +#include "refs.h" +#include "revision.h" #include <expat.h> @@ -42,14 +44,24 @@ enum XML_Status { #define DAV_ACTIVELOCK_OWNER ".prop.lockdiscovery.activelock.owner.href" #define DAV_ACTIVELOCK_TIMEOUT ".prop.lockdiscovery.activelock.timeout" #define DAV_ACTIVELOCK_TOKEN ".prop.lockdiscovery.activelock.locktoken.href" +#define DAV_PROPFIND_RESP ".multistatus.response" +#define DAV_PROPFIND_NAME ".multistatus.response.href" +#define DAV_PROPFIND_COLLECTION ".multistatus.response.propstat.prop.resourcetype.collection" /* DAV request body templates */ -#define PROPFIND_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:prop xmlns:R=\"%s\">\n<D:supportedlock/>\n</D:prop>\n</D:propfind>" +#define PROPFIND_SUPPORTEDLOCK_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:prop xmlns:R=\"%s\">\n<D:supportedlock/>\n</D:prop>\n</D:propfind>" +#define PROPFIND_ALL_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/>\n</D:propfind>" #define LOCK_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:lockinfo xmlns:D=\"DAV:\">\n<D:lockscope><D:exclusive/></D:lockscope>\n<D:locktype><D:write/></D:locktype>\n<D:owner>\n<D:href>mailto:%s</D:href>\n</D:owner>\n</D:lockinfo>" #define LOCK_TIME 600 #define LOCK_REFRESH 30 +/* bits #0-4 in revision.h */ + +#define LOCAL (1u << 5) +#define REMOTE (1u << 6) +#define PUSHING (1u << 7) + static int pushing = 0; static int aborted = 0; static char remote_dir_exists[256]; @@ -61,17 +73,19 @@ static int push_verbosely = 0; static int push_all = 0; static int force_all = 0; +static struct object_list *objects = NULL; + struct repo { char *url; + int path_len; struct packed_git *packs; }; static struct repo *remote = NULL; +static struct remote_lock *remote_locks = NULL; enum transfer_state { - NEED_CHECK, - RUN_HEAD, NEED_PUSH, RUN_MKCOL, RUN_PUT, @@ -82,10 +96,10 @@ enum transfer_state { struct transfer_request { - unsigned char sha1[20]; + struct object *obj; char *url; char *dest; - struct active_lock *lock; + struct remote_lock *lock; struct curl_slist *headers; struct buffer buffer; char filename[PATH_MAX]; @@ -114,14 +128,23 @@ struct xml_ctx void *userData; }; -struct active_lock +struct remote_lock { char *url; char *owner; char *token; time_t start_time; long timeout; + int active; int refreshing; + struct remote_lock *next; +}; + +struct remote_dentry +{ + char *base; + char *name; + int is_dir; }; static void finish_request(struct transfer_request *request); @@ -134,42 +157,9 @@ static void process_response(void *callback_data) finish_request(request); } -static void start_check(struct transfer_request *request) -{ - char *hex = sha1_to_hex(request->sha1); - struct active_request_slot *slot; - char *posn; - - request->url = xmalloc(strlen(remote->url) + 55); - strcpy(request->url, remote->url); - posn = request->url + strlen(remote->url); - strcpy(posn, "objects/"); - posn += 8; - memcpy(posn, hex, 2); - posn += 2; - *(posn++) = '/'; - strcpy(posn, hex + 2); - - slot = get_active_slot(); - slot->callback_func = process_response; - slot->callback_data = request; - curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr); - curl_easy_setopt(slot->curl, CURLOPT_URL, request->url); - curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1); - - if (start_active_slot(slot)) { - request->slot = slot; - request->state = RUN_HEAD; - } else { - request->state = ABORTED; - free(request->url); - request->url = NULL; - } -} - static void start_mkcol(struct transfer_request *request) { - char *hex = sha1_to_hex(request->sha1); + char *hex = sha1_to_hex(request->obj->sha1); struct active_request_slot *slot; char *posn; @@ -203,7 +193,7 @@ static void start_mkcol(struct transfer_request *request) static void start_put(struct transfer_request *request) { - char *hex = sha1_to_hex(request->sha1); + char *hex = sha1_to_hex(request->obj->sha1); struct active_request_slot *slot; char *posn; char type[20]; @@ -214,7 +204,7 @@ static void start_put(struct transfer_request *request) ssize_t size; z_stream stream; - unpacked = read_sha1_file(request->sha1, type, &len); + unpacked = read_sha1_file(request->obj->sha1, type, &len); hdrlen = sprintf(hdr, "%s %lu", type, len) + 1; /* Set it up */ @@ -309,64 +299,90 @@ static void start_move(struct transfer_request *request) } } -static int refresh_lock(struct active_lock *lock) +static int refresh_lock(struct remote_lock *check_lock) { struct active_request_slot *slot; char *if_header; char timeout_header[25]; struct curl_slist *dav_headers = NULL; - int rc = 0; + struct remote_lock *lock; + int time_remaining; + time_t current_time; - lock->refreshing = 1; + /* Refresh all active locks if they're close to expiring */ + for (lock = remote_locks; lock; lock = lock->next) { + if (!lock->active) + continue; - if_header = xmalloc(strlen(lock->token) + 25); - sprintf(if_header, "If: (<opaquelocktoken:%s>)", lock->token); - sprintf(timeout_header, "Timeout: Second-%ld", lock->timeout); - dav_headers = curl_slist_append(dav_headers, if_header); - dav_headers = curl_slist_append(dav_headers, timeout_header); + current_time = time(NULL); + time_remaining = lock->start_time + lock->timeout + - current_time; + if (time_remaining > LOCK_REFRESH) + continue; - slot = get_active_slot(); - curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); - curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null); - curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url); - curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK); - curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); + lock->refreshing = 1; - if (start_active_slot(slot)) { - run_active_slot(slot); - if (slot->curl_result != CURLE_OK) { - fprintf(stderr, "Got HTTP error %ld\n", slot->http_code); - } else { - lock->start_time = time(NULL); - rc = 1; + if_header = xmalloc(strlen(lock->token) + 25); + sprintf(if_header, "If: (<opaquelocktoken:%s>)", lock->token); + sprintf(timeout_header, "Timeout: Second-%ld", lock->timeout); + dav_headers = curl_slist_append(dav_headers, if_header); + dav_headers = curl_slist_append(dav_headers, timeout_header); + + slot = get_active_slot(); + curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); + curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null); + curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url); + curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK); + curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); + + if (start_active_slot(slot)) { + run_active_slot(slot); + if (slot->curl_result != CURLE_OK) { + fprintf(stderr, "Got HTTP error %ld\n", slot->http_code); + lock->active = 0; + } else { + lock->active = 1; + lock->start_time = time(NULL); + } } + + lock->refreshing = 0; + curl_slist_free_all(dav_headers); + free(if_header); } - lock->refreshing = 0; - curl_slist_free_all(dav_headers); - free(if_header); + if (check_lock) + return check_lock->active; + else + return 0; +} - return rc; +static void release_request(struct transfer_request *request) +{ + struct transfer_request *entry = request_queue_head; + + if (request == request_queue_head) { + request_queue_head = request->next; + } else { + while (entry->next != NULL && entry->next != request) + entry = entry->next; + if (entry->next == request) + entry->next = entry->next->next; + } + + if (request->url != NULL) + free(request->url); + free(request); } static void finish_request(struct transfer_request *request) { - time_t current_time = time(NULL); - int time_remaining; - request->curl_result = request->slot->curl_result; request->http_code = request->slot->http_code; request->slot = NULL; - /* Refresh the lock if it is close to timing out */ - time_remaining = request->lock->start_time + request->lock->timeout - - current_time; - if (time_remaining < LOCK_REFRESH && !request->lock->refreshing) { - if (!refresh_lock(request->lock)) { - fprintf(stderr, "Unable to refresh remote lock\n"); - aborted = 1; - } - } + /* Keep locks active */ + refresh_lock(request->lock); if (request->headers != NULL) curl_slist_free_all(request->headers); @@ -375,29 +391,16 @@ static void finish_request(struct transfer_request *request) if (request->state != RUN_PUT) { free(request->url); request->url = NULL; - } + } - if (request->state == RUN_HEAD) { - if (request->http_code == 404) { - request->state = NEED_PUSH; - } else if (request->curl_result == CURLE_OK) { - remote_dir_exists[request->sha1[0]] = 1; - request->state = COMPLETE; - } else { - fprintf(stderr, "HEAD %s failed, aborting (%d/%ld)\n", - sha1_to_hex(request->sha1), - request->curl_result, request->http_code); - request->state = ABORTED; - aborted = 1; - } - } else if (request->state == RUN_MKCOL) { + if (request->state == RUN_MKCOL) { if (request->curl_result == CURLE_OK || request->http_code == 405) { - remote_dir_exists[request->sha1[0]] = 1; + remote_dir_exists[request->obj->sha1[0]] = 1; start_put(request); } else { fprintf(stderr, "MKCOL %s failed, aborting (%d/%ld)\n", - sha1_to_hex(request->sha1), + sha1_to_hex(request->obj->sha1), request->curl_result, request->http_code); request->state = ABORTED; aborted = 1; @@ -407,21 +410,21 @@ static void finish_request(struct transfer_request *request) start_move(request); } else { fprintf(stderr, "PUT %s failed, aborting (%d/%ld)\n", - sha1_to_hex(request->sha1), + sha1_to_hex(request->obj->sha1), request->curl_result, request->http_code); request->state = ABORTED; aborted = 1; } } else if (request->state == RUN_MOVE) { if (request->curl_result == CURLE_OK) { - if (push_verbosely) - fprintf(stderr, - "sent %s\n", - sha1_to_hex(request->sha1)); + fprintf(stderr, " sent %s\n", + sha1_to_hex(request->obj->sha1)); request->state = COMPLETE; + request->obj->flags |= REMOTE; + release_request(request); } else { fprintf(stderr, "MOVE %s failed, aborting (%d/%ld)\n", - sha1_to_hex(request->sha1), + sha1_to_hex(request->obj->sha1), request->curl_result, request->http_code); request->state = ABORTED; aborted = 1; @@ -429,24 +432,6 @@ static void finish_request(struct transfer_request *request) } } -static void release_request(struct transfer_request *request) -{ - struct transfer_request *entry = request_queue_head; - - if (request == request_queue_head) { - request_queue_head = request->next; - } else { - while (entry->next != NULL && entry->next != request) - entry = entry->next; - if (entry->next == request) - entry->next = entry->next->next; - } - - if (request->url != NULL) - free(request->url); - free(request); -} - void fill_active_slots(void) { struct transfer_request *request = request_queue_head; @@ -457,14 +442,12 @@ void fill_active_slots(void) return; while (active_requests < max_requests && request != NULL) { - if (!pushing && request->state == NEED_CHECK) { - start_check(request); - curl_multi_perform(curlm, &num_transfers); - } else if (pushing && request->state == NEED_PUSH) { - if (remote_dir_exists[request->sha1[0]]) + if (pushing && request->state == NEED_PUSH) { + if (remote_dir_exists[request->obj->sha1[0]] == 1) { start_put(request); - else + } else { start_mkcol(request); + } curl_multi_perform(curlm, &num_transfers); } request = request->next; @@ -476,29 +459,37 @@ void fill_active_slots(void) slot->curl = NULL; } slot = slot->next; - } + } } -static void add_request(unsigned char *sha1, struct active_lock *lock) +static void get_remote_object_list(unsigned char parent); + +static void add_request(struct object *obj, struct remote_lock *lock) { struct transfer_request *request = request_queue_head; struct packed_git *target; - - while (request != NULL && memcmp(request->sha1, sha1, 20)) - request = request->next; - if (request != NULL) - return; - target = find_sha1_pack(sha1, remote->packs); - if (target) + /* + * Don't push the object if it's known to exist on the remote + * or is already in the request queue + */ + if (remote_dir_exists[obj->sha1[0]] == -1) + get_remote_object_list(obj->sha1[0]); + if (obj->flags & (REMOTE | PUSHING)) + return; + target = find_sha1_pack(obj->sha1, remote->packs); + if (target) { + obj->flags |= REMOTE; return; + } + obj->flags |= PUSHING; request = xmalloc(sizeof(*request)); - memcpy(request->sha1, sha1, 20); + request->obj = obj; request->url = NULL; request->lock = lock; request->headers = NULL; - request->state = NEED_CHECK; + request->state = NEED_PUSH; request->next = request_queue_head; request_queue_head = request; @@ -698,14 +689,13 @@ static char *quote_ref_url(const char *base, const char *ref) int len, baselen, ch; baselen = strlen(base); - len = baselen + 12; /* "refs/heads/" + NUL */ + len = baselen + 1; for (cp = ref; (ch = *cp) != 0; cp++, len++) if (needs_quote(ch)) len += 2; /* extra two hex plus replacement % */ qref = xmalloc(len); memcpy(qref, base, baselen); - memcpy(qref + baselen, "refs/heads/", 11); - for (cp = ref, dp = qref + baselen + 11; (ch = *cp) != 0; cp++) { + for (cp = ref, dp = qref + baselen; (ch = *cp) != 0; cp++) { if (needs_quote(ch)) { *dp++ = '%'; *dp++ = hex((ch >> 4) & 0xF); @@ -751,6 +741,27 @@ int fetch_ref(char *ref, unsigned char *sha1) return 0; } +static void one_remote_object(const char *hex) +{ + unsigned char sha1[20]; + struct object *obj; + + if (get_sha1_hex(hex, sha1) != 0) + return; + + obj = lookup_object(sha1); + if (!obj) + obj = parse_object(sha1); + + /* Ignore remote objects that don't exist locally */ + if (!obj) + return; + + obj->flags |= REMOTE; + if (!object_list_contains(objects, obj)) + add_object(obj, &objects, NULL, ""); +} + static void handle_lockprop_ctx(struct xml_ctx *ctx, int tag_closed) { int *lock_flags = (int *)ctx->userData; @@ -772,7 +783,7 @@ static void handle_lockprop_ctx(struct xml_ctx *ctx, int tag_closed) static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed) { - struct active_lock *lock = (struct active_lock *)ctx->userData; + struct remote_lock *lock = (struct remote_lock *)ctx->userData; if (tag_closed && ctx->cdata) { if (!strcmp(ctx->name, DAV_ACTIVELOCK_OWNER)) { @@ -791,6 +802,57 @@ static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed) } } +static void one_remote_ref(char *refname); +static void crawl_remote_refs(char *path); + +static void handle_crawl_ref_ctx(struct xml_ctx *ctx, int tag_closed) +{ + struct remote_dentry *dentry = (struct remote_dentry *)ctx->userData; + + + if (tag_closed) { + if (!strcmp(ctx->name, DAV_PROPFIND_RESP) && dentry->name) { + if (dentry->is_dir) { + if (strcmp(dentry->name, dentry->base)) { + crawl_remote_refs(dentry->name); + } + } else { + one_remote_ref(dentry->name); + } + } else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) { + dentry->name = xmalloc(strlen(ctx->cdata) - + remote->path_len + 1); + strcpy(dentry->name, + ctx->cdata + remote->path_len); + } else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) { + dentry->is_dir = 1; + } + } else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) { + dentry->name = NULL; + dentry->is_dir = 0; + } +} + +static void handle_remote_object_list_ctx(struct xml_ctx *ctx, int tag_closed) +{ + char *path; + char *obj_hex; + + if (tag_closed) { + if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) { + path = ctx->cdata + remote->path_len; + if (strlen(path) != 50) + return; + path += 9; + obj_hex = xmalloc(strlen(path)); + strncpy(obj_hex, path, 2); + strcpy(obj_hex + 2, path + 3); + one_remote_object(obj_hex); + free(obj_hex); + } + } +} + static void xml_start_tag(void *userData, const char *name, const char **atts) { @@ -848,7 +910,7 @@ xml_cdata(void *userData, const XML_Char *s, int len) strncpy(ctx->cdata, s, len); } -static struct active_lock *lock_remote(char *file, long timeout) +static struct remote_lock *lock_remote(char *path, long timeout) { struct active_request_slot *slot; struct buffer out_buffer; @@ -858,14 +920,26 @@ static struct active_lock *lock_remote(char *file, long timeout) char *url; char *ep; char timeout_header[25]; - struct active_lock *new_lock = NULL; + struct remote_lock *lock = remote_locks; XML_Parser parser = XML_ParserCreate(NULL); enum XML_Status result; struct curl_slist *dav_headers = NULL; struct xml_ctx ctx; - url = xmalloc(strlen(remote->url) + strlen(file) + 1); - sprintf(url, "%s%s", remote->url, file); + url = xmalloc(strlen(remote->url) + strlen(path) + 1); + sprintf(url, "%s%s", remote->url, path); + + /* Make sure the url is not already locked */ + while (lock && strcmp(lock->url, url)) { + lock = lock->next; + } + if (lock) { + free(url); + if (refresh_lock(lock)) + return lock; + else + return NULL; + } /* Make sure leading directories exist for the remote ref */ ep = strchr(url + strlen(remote->url) + 11, '/'); @@ -921,11 +995,11 @@ static struct active_lock *lock_remote(char *file, long timeout) curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); - new_lock = xcalloc(1, sizeof(*new_lock)); - new_lock->owner = NULL; - new_lock->token = NULL; - new_lock->timeout = -1; - new_lock->refreshing = 0; + lock = xcalloc(1, sizeof(*lock)); + lock->owner = NULL; + lock->token = NULL; + lock->timeout = -1; + lock->refreshing = 0; if (start_active_slot(slot)) { run_active_slot(slot); @@ -934,7 +1008,7 @@ static struct active_lock *lock_remote(char *file, long timeout) ctx.len = 0; ctx.cdata = NULL; ctx.userFunc = handle_new_lock_ctx; - ctx.userData = new_lock; + ctx.userData = lock; XML_SetUserData(parser, &ctx); XML_SetElementHandler(parser, xml_start_tag, xml_end_tag); @@ -946,7 +1020,7 @@ static struct active_lock *lock_remote(char *file, long timeout) fprintf(stderr, "XML error: %s\n", XML_ErrorString( XML_GetErrorCode(parser))); - new_lock->timeout = -1; + lock->timeout = -1; } } } else { @@ -957,23 +1031,26 @@ static struct active_lock *lock_remote(char *file, long timeout) free(out_data); free(in_data); - if (new_lock->token == NULL || new_lock->timeout <= 0) { - if (new_lock->token != NULL) - free(new_lock->token); - if (new_lock->owner != NULL) - free(new_lock->owner); + if (lock->token == NULL || lock->timeout <= 0) { + if (lock->token != NULL) + free(lock->token); + if (lock->owner != NULL) + free(lock->owner); free(url); - free(new_lock); - new_lock = NULL; + free(lock); + lock = NULL; } else { - new_lock->url = url; - new_lock->start_time = time(NULL); + lock->url = url; + lock->active = 1; + lock->start_time = time(NULL); + lock->next = remote_locks; + remote_locks = lock; } - return new_lock; + return lock; } -static int unlock_remote(struct active_lock *lock) +static int unlock_remote(struct remote_lock *lock) { struct active_request_slot *slot; char *lock_token_header; @@ -1005,15 +1082,171 @@ static int unlock_remote(struct active_lock *lock) curl_slist_free_all(dav_headers); free(lock_token_header); - if (lock->owner != NULL) - free(lock->owner); - free(lock->url); - free(lock->token); - free(lock); + lock->active = 0; return rc; } +static void crawl_remote_refs(char *path) +{ + char *url; + struct active_request_slot *slot; + struct buffer in_buffer; + struct buffer out_buffer; + char *in_data; + char *out_data; + XML_Parser parser = XML_ParserCreate(NULL); + enum XML_Status result; + struct curl_slist *dav_headers = NULL; + struct xml_ctx ctx; + struct remote_dentry dentry; + + fprintf(stderr, " %s\n", path); + + dentry.base = path; + dentry.name = NULL; + dentry.is_dir = 0; + + url = xmalloc(strlen(remote->url) + strlen(path) + 1); + sprintf(url, "%s%s", remote->url, path); + + out_buffer.size = strlen(PROPFIND_ALL_REQUEST); + out_data = xmalloc(out_buffer.size + 1); + snprintf(out_data, out_buffer.size + 1, PROPFIND_ALL_REQUEST); + out_buffer.posn = 0; + out_buffer.buffer = out_data; + + in_buffer.size = 4096; + in_data = xmalloc(in_buffer.size); + in_buffer.posn = 0; + in_buffer.buffer = in_data; + + dav_headers = curl_slist_append(dav_headers, "Depth: 1"); + dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml"); + + slot = get_active_slot(); + curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer); + curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size); + curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); + curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); + curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); + curl_easy_setopt(slot->curl, CURLOPT_URL, url); + curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1); + curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND); + curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); + + if (start_active_slot(slot)) { + run_active_slot(slot); + if (slot->curl_result == CURLE_OK) { + ctx.name = xcalloc(10, 1); + ctx.len = 0; + ctx.cdata = NULL; + ctx.userFunc = handle_crawl_ref_ctx; + ctx.userData = &dentry; + XML_SetUserData(parser, &ctx); + XML_SetElementHandler(parser, xml_start_tag, + xml_end_tag); + XML_SetCharacterDataHandler(parser, xml_cdata); + result = XML_Parse(parser, in_buffer.buffer, + in_buffer.posn, 1); + free(ctx.name); + + if (result != XML_STATUS_OK) { + fprintf(stderr, "XML error: %s\n", + XML_ErrorString( + XML_GetErrorCode(parser))); + } + } + } else { + fprintf(stderr, "Unable to start request\n"); + } + + free(url); + free(out_data); + free(in_buffer.buffer); + curl_slist_free_all(dav_headers); +} + +static void get_remote_object_list(unsigned char parent) +{ + char *url; + struct active_request_slot *slot; + struct buffer in_buffer; + struct buffer out_buffer; + char *in_data; + char *out_data; + XML_Parser parser = XML_ParserCreate(NULL); + enum XML_Status result; + struct curl_slist *dav_headers = NULL; + struct xml_ctx ctx; + char path[] = "/objects/XX/"; + static const char hex[] = "0123456789abcdef"; + unsigned int val = parent; + + path[9] = hex[val >> 4]; + path[10] = hex[val & 0xf]; + url = xmalloc(strlen(remote->url) + strlen(path) + 1); + sprintf(url, "%s%s", remote->url, path); + + out_buffer.size = strlen(PROPFIND_ALL_REQUEST); + out_data = xmalloc(out_buffer.size + 1); + snprintf(out_data, out_buffer.size + 1, PROPFIND_ALL_REQUEST); + out_buffer.posn = 0; + out_buffer.buffer = out_data; + + in_buffer.size = 4096; + in_data = xmalloc(in_buffer.size); + in_buffer.posn = 0; + in_buffer.buffer = in_data; + + dav_headers = curl_slist_append(dav_headers, "Depth: 1"); + dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml"); + + slot = get_active_slot(); + curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer); + curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size); + curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); + curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); + curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); + curl_easy_setopt(slot->curl, CURLOPT_URL, url); + curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1); + curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND); + curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); + + if (start_active_slot(slot)) { + run_active_slot(slot); + if (slot->curl_result == CURLE_OK) { + remote_dir_exists[parent] = 1; + ctx.name = xcalloc(10, 1); + ctx.len = 0; + ctx.cdata = NULL; + ctx.userFunc = handle_remote_object_list_ctx; + XML_SetUserData(parser, &ctx); + XML_SetElementHandler(parser, xml_start_tag, + xml_end_tag); + XML_SetCharacterDataHandler(parser, xml_cdata); + result = XML_Parse(parser, in_buffer.buffer, + in_buffer.posn, 1); + free(ctx.name); + + if (result != XML_STATUS_OK) { + fprintf(stderr, "XML error: %s\n", + XML_ErrorString( + XML_GetErrorCode(parser))); + } + } else { + remote_dir_exists[parent] = 0; + } + } else { + fprintf(stderr, "Unable to start request\n"); + } + + free(url); + free(out_data); + free(in_buffer.buffer); + curl_slist_free_all(dav_headers); +} + static int locking_available(void) { struct active_request_slot *slot; @@ -1027,9 +1260,12 @@ static int locking_available(void) struct xml_ctx ctx; int lock_flags = 0; - out_buffer.size = strlen(PROPFIND_REQUEST) + strlen(remote->url) - 2; + out_buffer.size = + strlen(PROPFIND_SUPPORTEDLOCK_REQUEST) + + strlen(remote->url) - 2; out_data = xmalloc(out_buffer.size + 1); - snprintf(out_data, out_buffer.size + 1, PROPFIND_REQUEST, remote->url); + snprintf(out_data, out_buffer.size + 1, + PROPFIND_SUPPORTEDLOCK_REQUEST, remote->url); out_buffer.posn = 0; out_buffer.buffer = out_data; @@ -1085,85 +1321,99 @@ static int locking_available(void) return lock_flags; } -static int is_ancestor(unsigned char *sha1, struct commit *commit) +static struct object_list **process_blob(struct blob *blob, + struct object_list **p, + struct name_path *path, + const char *name) { - struct commit_list *parents; + struct object *obj = &blob->object; - if (parse_commit(commit)) - return 0; - parents = commit->parents; - for (; parents; parents = parents->next) { - if (!memcmp(sha1, parents->item->object.sha1, 20)) { - return 1; - } else if (parents->item->object.type == commit_type) { - if (is_ancestor( - sha1, - (struct commit *)&parents->item->object - )) - return 1; - } + obj->flags |= LOCAL; + + if (obj->flags & (UNINTERESTING | SEEN)) + return p; + + obj->flags |= SEEN; + return add_object(obj, p, path, name); +} + +static struct object_list **process_tree(struct tree *tree, + struct object_list **p, + struct name_path *path, + const char *name) +{ + struct object *obj = &tree->object; + struct tree_entry_list *entry; + struct name_path me; + + obj->flags |= LOCAL; + + if (obj->flags & (UNINTERESTING | SEEN)) + return p; + if (parse_tree(tree) < 0) + die("bad tree object %s", sha1_to_hex(obj->sha1)); + + obj->flags |= SEEN; + p = add_object(obj, p, NULL, name); + me.up = path; + me.elem = name; + me.elem_len = strlen(name); + entry = tree->entries; + tree->entries = NULL; + while (entry) { + struct tree_entry_list *next = entry->next; + if (entry->directory) + p = process_tree(entry->item.tree, p, &me, entry->name); + else + p = process_blob(entry->item.blob, p, &me, entry->name); + free(entry); + entry = next; } - return 0; + return p; } -static void get_delta(unsigned char *sha1, struct object *obj, - struct active_lock *lock) +static void get_delta(struct rev_info *revs, struct remote_lock *lock) { struct commit *commit; - struct commit_list *parents; - struct tree *tree; - struct tree_entry_list *entry; + struct object_list **p = &objects, *pending; - if (sha1 && !memcmp(sha1, obj->sha1, 20)) - return; + while ((commit = get_revision(revs)) != NULL) { + p = process_tree(commit->tree, p, NULL, ""); + commit->object.flags |= LOCAL; + if (!(commit->object.flags & UNINTERESTING)) + add_request(&commit->object, lock); + } - if (aborted) - return; + for (pending = revs->pending_objects; pending; pending = pending->next) { + struct object *obj = pending->item; + const char *name = pending->name; - if (obj->type == commit_type) { - if (push_verbosely) - fprintf(stderr, "walk %s\n", sha1_to_hex(obj->sha1)); - add_request(obj->sha1, lock); - commit = (struct commit *)obj; - if (parse_commit(commit)) { - fprintf(stderr, "Error parsing commit %s\n", - sha1_to_hex(obj->sha1)); - aborted = 1; - return; |