in http-push.c [1711:2006]
int cmd_main(int argc, const char **argv)
{
struct transfer_request *request;
struct transfer_request *next_request;
struct refspec rs = REFSPEC_INIT_PUSH;
struct remote_lock *ref_lock = NULL;
struct remote_lock *info_ref_lock = NULL;
int delete_branch = 0;
int force_delete = 0;
int objects_to_send;
int rc = 0;
int i;
int new_refs;
struct ref *ref, *local_refs = NULL;
CALLOC_ARRAY(repo, 1);
argv++;
for (i = 1; i < argc; i++, argv++) {
const char *arg = *argv;
if (*arg == '-') {
if (!strcmp(arg, "--all")) {
push_all = MATCH_REFS_ALL;
continue;
}
if (!strcmp(arg, "--force")) {
force_all = 1;
continue;
}
if (!strcmp(arg, "--dry-run")) {
dry_run = 1;
continue;
}
if (!strcmp(arg, "--helper-status")) {
helper_status = 1;
continue;
}
if (!strcmp(arg, "--verbose")) {
push_verbosely = 1;
http_is_verbose = 1;
continue;
}
if (!strcmp(arg, "-d")) {
delete_branch = 1;
continue;
}
if (!strcmp(arg, "-D")) {
delete_branch = 1;
force_delete = 1;
continue;
}
if (!strcmp(arg, "-h"))
usage(http_push_usage);
}
if (!repo->url) {
char *path = strstr(arg, "//");
str_end_url_with_slash(arg, &repo->url);
repo->path_len = strlen(repo->url);
if (path) {
repo->path = strchr(path+2, '/');
if (repo->path)
repo->path_len = strlen(repo->path);
}
continue;
}
refspec_appendn(&rs, argv, argc - i);
break;
}
if (!repo->url)
usage(http_push_usage);
if (delete_branch && rs.nr != 1)
die("You must specify only one branch name when deleting a remote branch");
setup_git_directory();
memset(remote_dir_exists, -1, 256);
http_init(NULL, repo->url, 1);
is_running_queue = 0;
/* Verify DAV compliance/lock support */
if (!locking_available()) {
rc = 1;
goto cleanup;
}
sigchain_push_common(remove_locks_on_signal);
/* Check whether the remote has server info files */
repo->can_update_info_refs = 0;
repo->has_info_refs = remote_exists("info/refs");
repo->has_info_packs = remote_exists("objects/info/packs");
if (repo->has_info_refs) {
info_ref_lock = lock_remote("info/refs", LOCK_TIME);
if (info_ref_lock)
repo->can_update_info_refs = 1;
else {
error("cannot lock existing info/refs");
rc = 1;
goto cleanup;
}
}
if (repo->has_info_packs)
fetch_indices();
/* Get a list of all local and remote heads to validate refspecs */
local_refs = get_local_heads();
fprintf(stderr, "Fetching remote heads...\n");
get_dav_remote_heads();
run_request_queue();
/* Remove a remote branch if -d or -D was specified */
if (delete_branch) {
const char *branch = rs.items[i].src;
if (delete_remote_branch(branch, force_delete) == -1) {
fprintf(stderr, "Unable to delete remote branch %s\n",
branch);
if (helper_status)
printf("error %s cannot remove\n", branch);
}
goto cleanup;
}
/* match them up */
if (match_push_refs(local_refs, &remote_refs, &rs, 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) {
struct rev_info revs;
struct strvec commit_argv = STRVEC_INIT;
if (!ref->peer_ref)
continue;
if (is_null_oid(&ref->peer_ref->new_oid)) {
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 (oideq(&ref->old_oid, &ref->peer_ref->new_oid)) {
if (push_verbosely)
/* stable plumbing output; do not modify or localize */
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_null_oid(&ref->old_oid) &&
!ref->force) {
if (!has_object(the_repository, &ref->old_oid,
HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) ||
!ref_newer(&ref->peer_ref->new_oid,
&ref->old_oid)) {
/*
* We do not have the remote ref, or
* we know that the remote ref is not
* an ancestor of what we are trying to
* push. Either way this can be losing
* commits at the remote end and likely
* we were not up to date to begin with.
*/
/* stable plumbing output; do not modify or localize */
error("remote '%s' is not an ancestor of\n"
"local '%s'.\n"
"Maybe you are not up-to-date and "
"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;
}
}
oidcpy(&ref->new_oid, &ref->peer_ref->new_oid);
new_refs++;
fprintf(stderr, "updating '%s'", ref->name);
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",
oid_to_hex(&ref->old_oid), oid_to_hex(&ref->new_oid));
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) {
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;
}
/* Set up revision info for this refspec */
strvec_push(&commit_argv, ""); /* ignored */
strvec_push(&commit_argv, "--objects");
strvec_push(&commit_argv, oid_to_hex(&ref->new_oid));
if (!push_all && !is_null_oid(&ref->old_oid))
strvec_pushf(&commit_argv, "^%s",
oid_to_hex(&ref->old_oid));
repo_init_revisions(the_repository, &revs, setup_git_directory());
setup_revisions(commit_argv.nr, commit_argv.v, &revs, NULL);
revs.edge_hint = 0; /* just in case */
/* Generate a list of objects that need to be pushed */
pushing = 0;
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
mark_edges_uninteresting(&revs, NULL, 0);
objects_to_send = get_delta(&revs, ref_lock);
finish_all_active_slots();
/* Push missing objects to remote, this would be a
convenient time to pack them first if appropriate. */
pushing = 1;
if (objects_to_send)
fprintf(stderr, " sending %d objects\n",
objects_to_send);
run_request_queue();
/* Update the remote branch if all went well */
if (aborted || !update_remote(&ref->new_oid, ref_lock))
rc = 1;
if (!rc)
fprintf(stderr, " done\n");
if (helper_status)
printf("%s %s\n", !rc ? "ok" : "error", ref->name);
unlock_remote(ref_lock);
check_locks();
strvec_clear(&commit_argv);
release_revisions(&revs);
}
/* Update remote server info if appropriate */
if (repo->has_info_refs && new_refs) {
if (info_ref_lock && repo->can_update_info_refs) {
fprintf(stderr, "Updating remote server info\n");
if (!dry_run)
update_remote_info_refs(info_ref_lock);
} else {
fprintf(stderr, "Unable to update server info\n");
}
}
cleanup:
if (info_ref_lock)
unlock_remote(info_ref_lock);
free(repo->url);
free(repo);
http_cleanup();
request = request_queue_head;
while (request != NULL) {
next_request = request->next;
release_request(request);
request = next_request;
}
refspec_clear(&rs);
free_refs(local_refs);
return rc;
}