Example #1
0
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);
}
Example #2
0
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);
}
Example #3
0
File: merge.c Project: jwes/luagi
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;
}
Example #4
0
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);
}
Example #5
0
/**
 * @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;
}
Example #6
0
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;
}
Example #7
0
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);
}