static int try_to_update(git_repository *repo, git_remote *origin, git_reference *local, git_reference *remote, enum remote_transport rt) { git_oid base; const git_oid *local_id, *remote_id; if (!git_reference_cmp(local, remote)) return 0; // Dirty modified state in the working tree? We're not going // to update either way if (git_status_foreach(repo, check_clean, NULL)) return report_error("local cached copy is dirty, skipping update"); local_id = git_reference_target(local); remote_id = git_reference_target(remote); if (!local_id || !remote_id) return report_error("Unable to get local or remote SHA1"); if (git_merge_base(&base, repo, local_id, remote_id)) return report_error("Unable to find common commit of local and remote branches"); /* Is the remote strictly newer? Use it */ if (git_oid_equal(&base, local_id)) return reset_to_remote(repo, local, remote_id); /* Is the local repo the more recent one? See if we can update upstream */ if (git_oid_equal(&base, remote_id)) return update_remote(repo, origin, local, remote, rt); /* Merging a bare repository always needs user action */ if (git_repository_is_bare(repo)) return report_error("Local and remote have diverged, merge of bare branch needed"); /* Merging will definitely need the head branch too */ if (git_branch_is_head(local) != 1) return report_error("Local and remote do not match, local branch not HEAD - cannot update"); /* * Some day we migth try a clean merge here. * * But I couldn't find any good examples of this, so for now * you'd need to merge divergent histories manually. But we've * at least verified above that we have a working tree and the * current branch is checked out and clean, so we *could* try * to merge. */ return report_error("Local and remote have diverged, need to merge"); }
static int try_to_update(git_repository *repo, git_remote *origin, git_reference *local, git_reference *remote, enum remote_transport rt) { git_oid base; const git_oid *local_id, *remote_id; if (!git_reference_cmp(local, remote)) return 0; // Dirty modified state in the working tree? We're not going // to update either way if (git_status_foreach(repo, check_clean, NULL)) return report_error("local cached copy is dirty, skipping update"); local_id = git_reference_target(local); remote_id = git_reference_target(remote); if (!local_id || !remote_id) return report_error("Unable to get local or remote SHA1"); if (git_merge_base(&base, repo, local_id, remote_id)) return report_error("Unable to find common commit of local and remote branches"); /* Is the remote strictly newer? Use it */ if (git_oid_equal(&base, local_id)) return reset_to_remote(repo, local, remote_id); /* Is the local repo the more recent one? See if we can update upstream */ if (git_oid_equal(&base, remote_id)) return update_remote(repo, origin, local, remote, rt); /* Merging a bare repository always needs user action */ if (git_repository_is_bare(repo)) return report_error("Local and remote have diverged, merge of bare branch needed"); /* Merging will definitely need the head branch too */ if (git_branch_is_head(local) != 1) return report_error("Local and remote do not match, local branch not HEAD - cannot update"); /* Ok, let's try to merge these */ return try_to_git_merge(repo, local, remote, &base, local_id, remote_id); }
static int try_to_update(git_repository *repo, git_remote *origin, git_reference *local, git_reference *remote, const char *remote_url, const char *branch, enum remote_transport rt) { git_oid base; const git_oid *local_id, *remote_id; int ret = 0; if (verbose) fprintf(stderr, "git storage: try to update\n"); if (!git_reference_cmp(local, remote)) return 0; // Dirty modified state in the working tree? We're not going // to update either way if (git_status_foreach(repo, check_clean, NULL)) { if (is_subsurface_cloud) goto cloud_data_error; else return report_error("local cached copy is dirty, skipping update"); } local_id = git_reference_target(local); remote_id = git_reference_target(remote); if (!local_id || !remote_id) { if (is_subsurface_cloud) goto cloud_data_error; else return report_error("Unable to get local or remote SHA1"); } if (git_merge_base(&base, repo, local_id, remote_id)) { // TODO: // if they have no merge base, they actually are different repos // so instead merge this as merging a commit into a repo - git_merge() appears to do that // but needs testing and cleanup afterwards // if (is_subsurface_cloud) goto cloud_data_error; else return report_error("Unable to find common commit of local and remote branches"); } /* Is the remote strictly newer? Use it */ if (git_oid_equal(&base, local_id)) { git_storage_update_progress(translate("gettextFromC", "Update local storage to match cloud storage")); return reset_to_remote(repo, local, remote_id); } /* Is the local repo the more recent one? See if we can update upstream */ if (git_oid_equal(&base, remote_id)) { if (verbose) fprintf(stderr, "local is newer than remote, update remote\n"); git_storage_update_progress(translate("gettextFromC", "Push local changes to cloud storage")); return update_remote(repo, origin, local, remote, rt); } /* Merging a bare repository always needs user action */ if (git_repository_is_bare(repo)) { if (is_subsurface_cloud) goto cloud_data_error; else return report_error("Local and remote have diverged, merge of bare branch needed"); } /* Merging will definitely need the head branch too */ if (git_branch_is_head(local) != 1) { if (is_subsurface_cloud) goto cloud_data_error; else return report_error("Local and remote do not match, local branch not HEAD - cannot update"); } /* Ok, let's try to merge these */ git_storage_update_progress(translate("gettextFromC", "Try to merge local changes into cloud storage")); ret = try_to_git_merge(repo, &local, remote, &base, local_id, remote_id); if (ret == 0) return update_remote(repo, origin, local, remote, rt); else return ret; cloud_data_error: // since we are working with Subsurface cloud storage we want to make the user interaction // as painless as possible. So if something went wrong with the local cache, tell the user // about it an move it away return cleanup_local_cache(remote_url, branch); }
int main(int argc, const char **argv) { HifSack *sack = hif_sack_new (); HyRepo repo; char *md_repo; char *md_primary_xml; char *md_filelists; char *md_repo_updates; char *md_primary_updates_xml; char *md_filelists_updates; int ret; g_autoptr(GError) error = NULL; if (!hif_sack_setup(sack, HIF_SACK_SETUP_FLAG_MAKE_CACHE_DIR, NULL)) return 1; if (read_repopaths(&md_repo, &md_primary_xml, &md_filelists, &md_repo_updates, &md_primary_updates_xml, &md_filelists_updates)) { fprintf(stderr, "This is hawkey testing hack, it needs a readable %s file " "containing the following paths on separate lines:\n" "<main repomd.xml path>\n" "<main primary.xml.gz path>\n" "<main filelist.xml.gz path>\n" "<updates repomd.xml path>\n" "<updates primary.xml.gz path>\n" "<updates filelists.xml.gz path>\n", CFG_FILE); return 1; } int load_flags = HIF_SACK_LOAD_FLAG_BUILD_CACHE; /* rpmdb */ repo = hy_repo_create(HY_SYSTEM_REPO_NAME); hif_sack_load_system_repo(sack, NULL, load_flags, &error); hy_repo_free(repo); if (need_filelists(argc, argv)) load_flags |= HIF_SACK_LOAD_FLAG_USE_FILELISTS; /* Fedora repo */ repo = config_repo("Fedora", md_repo, md_primary_xml, md_filelists); ret = hif_sack_load_repo(sack, repo, load_flags, &error); assert(ret == 0); (void)ret; hy_repo_free(repo); /* Fedora updates repo */ repo = config_repo("updates", md_repo_updates, md_primary_updates_xml, md_filelists_updates); ret = hif_sack_load_repo(sack, repo, load_flags, &error); assert(ret == 0); (void)ret; hy_repo_free(repo); free(md_repo); free(md_primary_xml); free(md_filelists); free(md_repo_updates); free(md_primary_updates_xml); free(md_filelists_updates); hif_sack_set_installonly(sack, installonly); hif_sack_set_installonly_limit(sack, 3); if (argc == 2 && !strcmp(argv[1], "-o")) { obsoletes(sack); } else if (argc == 2) { search_and_print(sack, argv[1]); } else if (argc == 3 && !strcmp(argv[1], "-f")) { search_filter_files(sack, argv[2]); } else if (argc == 3 && !strcmp(argv[1], "-r")) { search_filter_repos(sack, argv[2]); } else if (argc == 3 && !strcmp(argv[1], "-u")) { updatables_query_name(sack, argv[2]); } else if (argc == 3 && !strcmp(argv[1], "-ul")) { update_local(sack, argv[2]); } else if (argc == 3 && !strcmp(argv[1], "-ur")) { update_remote(sack, argv[2]); } else if (argc == 3 && !strcmp(argv[1], "-e")) { erase(sack, argv[2]); } else if (argc == 3) { search_anded(sack, argv[1], argv[2]); } else if (argc == 4 && !strcmp(argv[1], "-p")) { search_provides(sack, argv[2], argv[3]); } g_object_unref(sack); return 0; }
int cmd_main(int argc, const char **argv) { struct transfer_request *request; struct transfer_request *next_request; int nr_refspec = 0; const char **refspec = NULL; struct remote_lock *ref_lock = NULL; struct remote_lock *info_ref_lock = NULL; struct rev_info revs; 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; repo = xcalloc(1, sizeof(*repo)); 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 = argv; nr_refspec = argc - i; break; } #ifndef USE_CURL_MULTI die("git-push is not available for http/https repository when not compiled with USE_CURL_MULTI"); #endif if (!repo->url) usage(http_push_usage); 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); http_init(NULL, repo->url, 1); #ifdef USE_CURL_MULTI is_running_queue = 0; #endif /* 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) { 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 (match_push_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) { struct argv_array commit_argv = ARGV_ARRAY_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 (!oidcmp(&ref->old_oid, &ref->peer_ref->new_oid)) { if (push_verbosely) 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_file(&ref->old_oid) || !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. */ 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 == 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; } /* Set up revision info for this refspec */ argv_array_push(&commit_argv, ""); /* ignored */ argv_array_push(&commit_argv, "--objects"); argv_array_push(&commit_argv, oid_to_hex(&ref->new_oid)); if (!push_all && !is_null_oid(&ref->old_oid)) argv_array_pushf(&commit_argv, "^%s", oid_to_hex(&ref->old_oid)); init_revisions(&revs, setup_git_directory()); setup_revisions(commit_argv.argc, commit_argv.argv, &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); 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.hash, 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(); argv_array_clear(&commit_argv); } /* 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); http_cleanup(); request = request_queue_head; while (request != NULL) { next_request = request->next; release_request(request); request = next_request; } return rc; }