PyObject * Repository_merge_analysis(Repository *self, PyObject *py_id) { int err; size_t len; git_oid id; git_merge_head *merge_head; git_merge_analysis_t analysis; git_merge_preference_t preference; len = py_oid_to_git_oid(py_id, &id); if (len == 0) return NULL; err = git_merge_head_from_id(&merge_head, self->repo, &id); if (err < 0) return Error_set(err); err = git_merge_analysis(&analysis, &preference, self->repo, (const git_merge_head **) &merge_head, 1); git_merge_head_free(merge_head); if (err < 0) return Error_set(err); return Py_BuildValue("(ii)", analysis, preference); }
PyObject * Repository_merge_analysis(Repository *self, PyObject *py_id) { int err; size_t len; git_oid id; git_annotated_commit *commit; git_merge_analysis_t analysis; git_merge_preference_t preference; len = py_oid_to_git_oid(py_id, &id); if (len == 0) return NULL; err = git_annotated_commit_lookup(&commit, self->repo, &id); if (err < 0) return Error_set(err); err = git_merge_analysis(&analysis, &preference, self->repo, (const git_annotated_commit **) &commit, 1); git_annotated_commit_free(commit); if (err < 0) return Error_set(err); return Py_BuildValue("(ii)", analysis, preference); }
int luagi_merge_analysis( lua_State *L ) { git_repository **repo = checkrepo( L, 1 ); int len = luaL_len( L, 2 ); const git_annotated_commit *commits[ len ]; fill_annotated_commit( L, 2, commits, len ); git_merge_analysis_t anout; git_merge_preference_t pout; if( git_merge_analysis( &anout, &pout, *repo, commits, len ) ) { return ltk_push_git_error( L ); } const char *aoutstr = NONE; const char *poutstr = NONE; switch( anout ) { case GIT_MERGE_ANALYSIS_NONE: break; case GIT_MERGE_ANALYSIS_NORMAL: aoutstr = NORMAL; break; case GIT_MERGE_ANALYSIS_UP_TO_DATE: aoutstr = UP_TO_DATE; break; case GIT_MERGE_ANALYSIS_FASTFORWARD: aoutstr = FASTFORWARD; break; case GIT_MERGE_ANALYSIS_UNBORN: aoutstr = UNBORN; break; } lua_pushstring( L, aoutstr ); switch( pout ) { case GIT_MERGE_PREFERENCE_NONE: break; case GIT_MERGE_PREFERENCE_NO_FASTFORWARD: poutstr = NO_FASTFORWARD; break; case GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY: poutstr = FASTFORWARD_ONLY; break; } lua_pushstring( L, poutstr ); return 2; }
static void analysis_from_branch( git_merge_analysis_t *merge_analysis, git_merge_preference_t *merge_pref, const char *branchname) { git_buf refname = GIT_BUF_INIT; git_reference *their_ref; git_annotated_commit *their_head; git_buf_printf(&refname, "%s%s", GIT_REFS_HEADS_DIR, branchname); cl_git_pass(git_reference_lookup(&their_ref, repo, git_buf_cstr(&refname))); cl_git_pass(git_annotated_commit_from_ref(&their_head, repo, their_ref)); cl_git_pass(git_merge_analysis(merge_analysis, merge_pref, repo, (const git_annotated_commit **)&their_head, 1)); git_buf_free(&refname); git_annotated_commit_free(their_head); git_reference_free(their_ref); }
/** * @param merge_result S4 class git_merge_result * @repository The repository * @param merge_head The merge head to merge * @param n The number of merge heads * @param preference The merge preference option (None [0], No * Fast-Forward [1] or Only Fast-Forward [2]) * @param name The name of the merge in the reflog * @param merger Who is performing the merge * @param commit_on_success Commit merge commit, if one was created * during a normal merge * @return 0 on success, or error code */ static int git2r_merge( SEXP merge_result, git_repository *repository, const git_annotated_commit **merge_heads, size_t n, git_merge_preference_t preference, const char *name, git_signature *merger, int commit_on_success) { int err; git_merge_analysis_t merge_analysis; git_merge_preference_t merge_preference; git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT; merge_opts.rename_threshold = 50; merge_opts.target_limit = 200; checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; err = git_merge_analysis( &merge_analysis, &merge_preference, repository, merge_heads, n); if (err) return err; if (merge_analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) { SET_SLOT(merge_result, Rf_install("up_to_date"), ScalarLogical(1)); return GIT_OK; } else { SET_SLOT(merge_result, Rf_install("up_to_date"), ScalarLogical(0)); } if (GIT_MERGE_PREFERENCE_NONE == preference) preference = merge_preference; switch (preference) { case GIT_MERGE_PREFERENCE_NONE: if (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD) { if (1 != n) { giterr_set_str( GITERR_NONE, "Unable to perform Fast-Forward merge " "with mith multiple merge heads."); return GIT_ERROR; } err = git2r_fast_forward_merge( merge_result, merge_heads[0], repository, name); } else if (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL) { err = git2r_normal_merge( merge_result, merge_heads, n, repository, name, merger, commit_on_success, &checkout_opts, &merge_opts); } break; case GIT_MERGE_PREFERENCE_NO_FASTFORWARD: if (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL) { err = git2r_normal_merge( merge_result, merge_heads, n, repository, name, merger, commit_on_success, &checkout_opts, &merge_opts); } break; case GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY: if (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD) { if (1 != n) { giterr_set_str( GITERR_NONE, "Unable to perform Fast-Forward merge " "with mith multiple merge heads."); return GIT_ERROR; } err = git2r_fast_forward_merge( merge_result, merge_heads[0], repository, name); } else { giterr_set_str(GITERR_NONE, "Unable to perform Fast-Forward merge."); return GIT_ERROR; } break; default: giterr_set_str(GITERR_NONE, "Unknown merge option"); return GIT_ERROR; } return GIT_OK; }
bool Masterlist::Update(const boost::filesystem::path& path, const std::string& repoUrl, const std::string& repoBranch) { GitHelper git; fs::path repoPath = path.parent_path(); string filename = path.filename().string(); if (repoUrl.empty() || repoBranch.empty()) throw std::invalid_argument("Repository URL and branch must not be empty."); // Initialise checkout options. BOOST_LOG_TRIVIAL(debug) << "Setting up checkout options."; char * paths = new char[filename.length() + 1]; strcpy(paths, filename.c_str()); git.GetData().checkout_options.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_REMOVE_EXISTING; git.GetData().checkout_options.paths.strings = &paths; git.GetData().checkout_options.paths.count = 1; // Initialise clone options. git.GetData().clone_options.checkout_opts = git.GetData().checkout_options; git.GetData().clone_options.bare = 0; git.GetData().clone_options.checkout_branch = repoBranch.c_str(); // Now try to access the repository if it exists, or clone one if it doesn't. BOOST_LOG_TRIVIAL(trace) << "Attempting to open the Git repository at: " << repoPath; if (!git.IsRepository(repoPath)) git.Clone(repoPath, repoUrl); else { // Repository exists: check settings are correct, then pull updates. git.SetErrorMessage((boost::format(translate("An error occurred while trying to access the local masterlist repository. If this error happens again, try deleting the \".git\" folder in %1%.")) % repoPath.string()).str()); // Open the repository. BOOST_LOG_TRIVIAL(info) << "Existing repository found, attempting to open it."; git.Call(git_repository_open(&git.GetData().repo, repoPath.string().c_str())); // Set the remote URL. BOOST_LOG_TRIVIAL(info) << "Using remote URL: " << repoUrl; git.Call(git_remote_set_url(git.GetData().repo, "origin", repoUrl.c_str())); // Now fetch updates from the remote. git.Fetch("origin"); // Check that a local branch with the correct name exists. git.SetErrorMessage((boost::format(translate("An error occurred while trying to access the local masterlist repository. If this error happens again, try deleting the \".git\" folder in %1%.")) % repoPath.string()).str()); int ret = git_branch_lookup(&git.GetData().reference, git.GetData().repo, repoBranch.c_str(), GIT_BRANCH_LOCAL); if (ret == GIT_ENOTFOUND) // Branch doesn't exist. Create a new branch using the remote branch's latest commit. git.CheckoutNewBranch("origin", repoBranch); else { // The local branch exists. Need to merge the remote branch // into it. git.Call(ret); // Handle other errors from preceding branch lookup. // Check if HEAD points to the desired branch and set it to if not. if (!git_branch_is_head(git.GetData().reference)) { BOOST_LOG_TRIVIAL(trace) << "Setting HEAD to follow branch: " << repoBranch; git.Call(git_repository_set_head(git.GetData().repo, (string("refs/heads/") + repoBranch).c_str())); } // Get remote branch reference. git.Call(git_branch_upstream(&git.GetData().reference2, git.GetData().reference)); BOOST_LOG_TRIVIAL(trace) << "Checking HEAD and remote branch's mergeability."; git_merge_analysis_t analysis; git_merge_preference_t pref; git.Call(git_annotated_commit_from_ref(&git.GetData().annotated_commit, git.GetData().repo, git.GetData().reference2)); git.Call(git_merge_analysis(&analysis, &pref, git.GetData().repo, (const git_annotated_commit **)&git.GetData().annotated_commit, 1)); if ((analysis & GIT_MERGE_ANALYSIS_FASTFORWARD) == 0 && (analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) == 0) { // The local branch can't be easily merged. Best just to delete and recreate it. BOOST_LOG_TRIVIAL(trace) << "Local branch cannot be easily merged with remote branch."; BOOST_LOG_TRIVIAL(trace) << "Deleting the local branch."; git.Call(git_branch_delete(git.GetData().reference)); // Need to free ref before calling git.CheckoutNewBranch() git_reference_free(git.GetData().reference); git.GetData().reference = nullptr; git_reference_free(git.GetData().reference2); git.GetData().reference2 = nullptr; git.CheckoutNewBranch("origin", repoBranch); } else { // Get remote branch commit ID. git.Call(git_reference_peel(&git.GetData().object, git.GetData().reference2, GIT_OBJ_COMMIT)); const git_oid * remote_commit_id = git_object_id(git.GetData().object); git_object_free(git.GetData().object); git.GetData().object = nullptr; git_reference_free(git.GetData().reference2); git.GetData().reference2 = nullptr; bool updateBranchHead = true; if ((analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) != 0) { // No merge is required, but HEAD might be ahead of the remote branch. Check // to see if that's the case, and move HEAD back to match the remote branch // if so. BOOST_LOG_TRIVIAL(trace) << "Local branch is up-to-date with remote branch."; BOOST_LOG_TRIVIAL(trace) << "Checking to see if local and remote branch heads are equal."; // Get local branch commit ID. git.Call(git_reference_peel(&git.GetData().object, git.GetData().reference, GIT_OBJ_COMMIT)); const git_oid * local_commit_id = git_object_id(git.GetData().object); git_object_free(git.GetData().object); git.GetData().object = nullptr; updateBranchHead = local_commit_id->id != remote_commit_id->id; // If the masterlist in // HEAD also matches the masterlist file, no further // action needs to be taken. Otherwise, a checkout // must be performed and the checked-out file parsed. if (!updateBranchHead) { BOOST_LOG_TRIVIAL(trace) << "Local and remote branch heads are equal."; if (!GitHelper::IsFileDifferent(repoPath, filename)) { BOOST_LOG_TRIVIAL(info) << "Local branch and masterlist file are already up to date."; return false; } } else BOOST_LOG_TRIVIAL(trace) << "Local branch heads is ahead of remote branch head."; } else BOOST_LOG_TRIVIAL(trace) << "Local branch can be fast-forwarded to remote branch."; if (updateBranchHead) { // The remote branch reference points to a particular // commit. Update the local branch reference to point // to the same commit. BOOST_LOG_TRIVIAL(trace) << "Syncing local branch head with remote branch head."; git.Call(git_reference_set_target(&git.GetData().reference2, git.GetData().reference, remote_commit_id, "Setting branch reference.")); git_reference_free(git.GetData().reference2); git.GetData().reference2 = nullptr; } git_reference_free(git.GetData().reference); git.GetData().reference = nullptr; BOOST_LOG_TRIVIAL(trace) << "Performing a Git checkout of HEAD."; git.Call(git_checkout_head(git.GetData().repo, &git.GetData().checkout_options)); } } } // Now whether the repository was cloned or updated, the working directory contains // the latest masterlist. Try parsing it: on failure, detach the HEAD back one commit // and try again. bool parsingFailed = false; std::string parsingError; git.SetErrorMessage((boost::format(translate("An error occurred while trying to read information on the updated masterlist. If this error happens again, try deleting the \".git\" folder in %1%.")) % repoPath.string()).str()); do { // Get the HEAD revision's short ID. string revision = git.GetHeadShortId(); //Now try parsing the masterlist. BOOST_LOG_TRIVIAL(debug) << "Testing masterlist parsing."; try { this->Load(path); parsingFailed = false; } catch (std::exception& e) { parsingFailed = true; if (parsingError.empty()) parsingError = boost::locale::translate("Masterlist revision").str() + " " + string(revision) + ": " + e.what() + ". " + boost::locale::translate("The latest masterlist revision contains a syntax error, LOOT is using the most recent valid revision instead. Syntax errors are usually minor and fixed within hours.").str(); //There was an error, roll back one revision. BOOST_LOG_TRIVIAL(error) << "Masterlist parsing failed. Masterlist revision " + string(revision) + ": " + e.what(); git.CheckoutRevision("HEAD^"); } } while (parsingFailed); if (!parsingError.empty()) AppendMessage(Message(MessageType::error, parsingError)); return true; }
void SyncState::sync() { m_timer.restart(); // Set up directory structure, if necessary std::string tmpPath = cpp3ds::FileSystem::getFilePath("sdmc:/3ds/BrewMan/tmp"); std::string cachePath = cpp3ds::FileSystem::getFilePath("sdmc:/3ds/BrewMan/cache"); std::string installedPath = cpp3ds::FileSystem::getFilePath("sdmc:/3ds/BrewMan/installed"); if (pathExists(tmpPath.c_str(), false)) removeDirectory(tmpPath.c_str()); mkdir(tmpPath.c_str(), 0777); if (!pathExists(cachePath.c_str(), false)) mkdir(cachePath.c_str(), 0777); if (!pathExists(installedPath.c_str(), false)) mkdir(installedPath.c_str(), 0777); // If auto-dated, boot into newest BrewMan if (autoUpdate()) { char buf[256]; size_t len; FILE *src = fopen("sdmc:/3ds/BrewMan/tmp/update.3dsx", "rb"); FILE *dst = fopen("sdmc:/3ds/BrewMan/tmp/update-copy.3dsx", "wb"); while ((len = fread(buf, 1, sizeof(buf), src)) > 0) fwrite(buf, 1, sizeof(buf), dst); fclose(src); fclose(dst); bootApp("/3ds/BrewMan/tmp/update.3dsx", ""); requestStackClear(); return; } git_repository *repo = NULL; const char *repoUrl = "git://github.com/Repo3DS/ideal-enigma.git"; const std::string path = cpp3ds::FileSystem::getFilePath(REPO_DIR); git_clone_options opts = GIT_CLONE_OPTIONS_INIT; setStatus("Fetching git repo..."); int error = git_clone(&repo, repoUrl, path.c_str(), NULL); if (error < 0) { if (error == GIT_EEXISTS) { error = git_repository_open(&repo, path.c_str()); if (error == 0) { git_remote *remote; error = git_remote_lookup(&remote, repo, "origin"); if (error == 0) { error = git_remote_fetch(remote, NULL, NULL, "pull"); if (error == 0) { git_annotated_commit *our_head, *their_heads[1]; if (git_repository_fetchhead_foreach(repo, find_master, NULL) == 0) { git_annotated_commit_from_fetchhead(&their_heads[0], repo, "master", repoUrl, &m_git_oid); git_merge_analysis_t analysis; git_merge_preference_t prefs; git_merge_analysis(&analysis, &prefs, repo, (const git_annotated_commit**)their_heads, 1); if (analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) printf("up to date\n"); else if (analysis & GIT_MERGE_ANALYSIS_FASTFORWARD) { printf("fast-forwarding\n"); // if (git_merge(repo, (const git_annotated_commit **)their_heads, 1, NULL, NULL) == 0) { git_reference *ref; git_reference *newref; if (git_reference_lookup(&ref, repo, "refs/heads/master") == 0) git_reference_set_target(&newref, ref, &m_git_oid, "BrewMan pull: Fast-forward"); git_reset_from_annotated(repo, their_heads[0], GIT_RESET_HARD, NULL); git_reference_free(ref); git_repository_state_cleanup(repo); } git_annotated_commit_free(their_heads[0]); } } } } git_repository_free(repo); const git_error *e = giterr_last(); if (e) { setStatus(_("Error %d/%d\n%s\nCloning repo again...", error, e->klass, e->message)); if (!removeDirectory(path.c_str())) { cpp3ds::sleep(cpp3ds::seconds(2.f)); sync(); } else { setStatus("Repo failure. Please report this error.\nAnd delete /3ds/BrewMan/repo/ and try again."); } return; } } const git_error *e = giterr_last(); if (e) { setStatus(_("Error %d/%d: %s", error, e->klass, e->message)); return; } } setStatus("Everything up-to-date!"); // Give the Title animation time to finish if necessary while (m_timer.getElapsedTime() < cpp3ds::seconds(5.f)) cpp3ds::sleep(cpp3ds::milliseconds(50)); requestStackClear(); requestStackPush(States::Browse); }