Example #1
0
int git_reference__update_for_commit(
	git_repository *repo,
	git_reference *ref,
	const char *ref_name,
	const git_oid *id,
	const git_signature *committer,
	const char *operation)
{
	git_reference *ref_new = NULL;
	git_commit *commit = NULL;
	git_buf reflog_msg = GIT_BUF_INIT;
	int error;

	if ((error = git_commit_lookup(&commit, repo, id)) < 0 ||
		(error = git_buf_printf(&reflog_msg, "%s%s: %s",
			operation ? operation : "commit",
			git_commit_parentcount(commit) == 0 ? " (initial)" : "",
			git_commit_summary(commit))) < 0)
		goto done;

	if (ref)
		error = git_reference_set_target(
			&ref_new, ref, id, committer, git_buf_cstr(&reflog_msg));
	else
		error = git_reference__update_terminal(
			repo, ref_name, id, committer, git_buf_cstr(&reflog_msg));

done:
	git_reference_free(ref_new);
	git_buf_free(&reflog_msg);
	git_commit_free(commit);
	return error;
}
Example #2
0
/**
 * ggit_ref_set_target:
 * @ref: a #GgitRef.
 * @oid: a #GgitOId.
 * @log_message: The one line long message to be appended to the reflog.
 * @error: a #GError for error reporting, or %NULL.
 *
 * Create a new reference with the same name as the given reference but a
 * different OID target. The reference must be a direct reference, otherwise
 * this will fail.
 *
 * The new reference will be written to disk, overwriting the given reference.
 *
 * Returns: (transfer full): the newly created #GgitRef.
 */
GgitRef *
ggit_ref_set_target (GgitRef       *ref,
                     GgitOId       *oid,
                     const gchar   *log_message,
                     GError       **error)
{
	git_reference *out;
	gint ret;

	g_return_val_if_fail (ref != NULL, NULL);
	g_return_val_if_fail (oid != NULL, NULL);
	g_return_val_if_fail (error == NULL || *error == NULL, NULL);

	ret = git_reference_set_target (&out,
	                                _ggit_native_get (ref),
	                                _ggit_oid_get_oid (oid),
	                                log_message);

	if (ret != GIT_OK)
	{
		_ggit_error_set (error, ret);
		return NULL;
	}

	return _ggit_ref_wrap (out, FALSE);
}
Example #3
0
int
Reference_oid__set__(Reference *self, PyObject *py_hex)
{
    git_oid oid;
    int err;
    git_reference *new_ref;

    CHECK_REFERENCE_INT(self);

    /* Get the oid */
    err = py_str_to_git_oid_expand(git_reference_owner(self->reference),
                                   py_hex, &oid);
    if (err < 0) {
        Error_set(err);
        return -1;
    }

    /* Set the oid */
    err = git_reference_set_target(&new_ref, self->reference, &oid);
    if (err < 0) {
        Error_set(err);
        return -1;
    }

    git_reference_free(self->reference);
    self->reference = new_ref;
    return 0;
}
Example #4
0
/*
 * The remote is strictly newer than the local branch.
 */
static int reset_to_remote(git_repository *repo, git_reference *local, const git_oid *new_id)
{
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
	git_object *target;

	// If it's not checked out (bare or not HEAD), just update the reference */
	if (git_repository_is_bare(repo) || git_branch_is_head(local) != 1) {
		git_reference *out;

		if (git_reference_set_target(&out, local, new_id, "Update to remote"))
			return report_error("Could not update local ref to newer remote ref");

		git_reference_free(out);

		// Not really an error, just informational
		report_error("Updated local branch from remote");

		return 0;
	}

	if (git_object_lookup(&target, repo, new_id, GIT_OBJ_COMMIT))
		return report_error("Could not look up remote commit");

	opts.checkout_strategy = GIT_CHECKOUT_SAFE;
	if (git_reset(repo, target, GIT_RESET_HARD, &opts))
		return report_error("Local head checkout failed after update");

	// Not really an error, just informational
	report_error("Updated local information from remote");

	return 0;
}
Example #5
0
void test_refs_settargetwithlog__updating_a_direct_reference_adds_a_reflog_entry(void)
{
	git_reference *reference, *reference_out;
	git_oid current_id, target_id;
	git_signature *signature;
	git_reflog *reflog;
	const git_reflog_entry *entry;

	const char *message = "You've been logged, mate!";

	git_oid_fromstr(&current_id, br2_tip);
	git_oid_fromstr(&target_id, master_tip);

	cl_git_pass(git_reference_lookup(&reference, g_repo, br2_name));

	cl_git_pass(git_signature_now(&signature, "foo", "foo@bar"));

	cl_git_pass(git_reference_set_target(
		&reference_out, reference, &target_id, signature, message));

	cl_git_pass(git_reflog_read(&reflog, g_repo, br2_name));

	entry = git_reflog_entry_byindex(reflog, 0);
	cl_assert(git_oid_cmp(&current_id, &entry->oid_old) == 0);
	cl_assert(git_oid_cmp(&target_id, &entry->oid_cur) == 0);
	cl_assert_equal_s(message, entry->msg);

	git_reflog_free(reflog);
	git_reference_free(reference_out);
	git_reference_free(reference);
	git_signature_free(signature);
}
Example #6
0
PyObject *
Reference_set_target(Reference *self, PyObject *args, PyObject *kwds)
{
    git_oid oid;
    char *c_name;
    int err;
    git_reference *new_ref;
    const git_signature *sig = NULL;
    PyObject *py_target = NULL;
    Signature *py_signature = NULL;
    const char *message = NULL;
    char *keywords[] = {"target", "signature", "message", NULL};

    CHECK_REFERENCE(self);

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O!s", keywords,
                                     &py_target, &SignatureType, &py_signature, &message))
        return NULL;

    if (py_signature)
        sig = py_signature->signature;

    /* Case 1: Direct */
    if (GIT_REF_OID == git_reference_type(self->reference)) {
        err = py_oid_to_git_oid_expand(self->repo->repo, py_target, &oid);
        if (err < 0)
            goto error;

        err = git_reference_set_target(&new_ref, self->reference, &oid, sig, message);
        if (err < 0)
            goto error;

        git_reference_free(self->reference);
        self->reference = new_ref;
        Py_RETURN_NONE;
    }

    /* Case 2: Symbolic */
    c_name = py_path_to_c_str(py_target);
    if (c_name == NULL)
        return NULL;

    err = git_reference_symbolic_set_target(&new_ref, self->reference, c_name, sig, message);
    free(c_name);
    if (err < 0)
        goto error;

    git_reference_free(self->reference);
    self->reference = new_ref;

    Py_RETURN_NONE;

error:
    Error_set(err);
    return NULL;
}
Example #7
0
void test_stash_save__cannot_stash_against_an_unborn_branch(void)
{
	git_reference *head;

	cl_git_pass(git_reference_lookup(&head, repo, "HEAD"));
	cl_git_pass(git_reference_set_target(head, "refs/heads/unborn"));

	cl_assert_equal_i(GIT_EORPHANEDHEAD,
		git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));

	git_reference_free(head);
}
Example #8
0
int luagi_reference_gen_set_target( lua_State *L, const char *tablename )
{
   git_reference **ref = luaL_checkudata( L, 1, tablename );
   git_oid oid;
   if( luagi_check_oid( &oid, L, 2 ) )
   {
      ltk_error_abort( L );
   }
   const char *log_message = luaL_checkstring( L, 4 );

   git_reference **out = lua_newuserdata( L, sizeof( git_reference * ) );
   if( git_reference_set_target( out, *ref, &oid, log_message ) )
   {
      return ltk_push_git_error( L );
   }
   ltk_setmetatable( L, LUAGI_REFERENCE_FUNCS );
   return 1;
}
Example #9
0
/*
 * The remote is strictly newer than the local branch.
 */
static int reset_to_remote(git_repository *repo, git_reference *local, const git_oid *new_id)
{
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
	opts.progress_cb = &progress_cb;
	git_object *target;

	if (verbose)
		fprintf(stderr, "git storage: reset to remote\n");

	// If it's not checked out (bare or not HEAD), just update the reference */
	if (git_repository_is_bare(repo) || git_branch_is_head(local) != 1) {
		git_reference *out;

		if (git_reference_set_target(&out, local, new_id, "Update to remote"))
			return report_error(translate("gettextFromC", "Could not update local cache to newer remote data"));

		git_reference_free(out);

#ifdef DEBUG
		// Not really an error, just informational
		report_error("Updated local branch from remote");
#endif
		return 0;
	}

	if (git_object_lookup(&target, repo, new_id, GIT_OBJ_COMMIT)) {
		if (is_subsurface_cloud)
			return report_error(translate("gettextFromC", "Subsurface cloud storage corrupted"));
		else
			return report_error("Could not look up remote commit");
	}
	opts.checkout_strategy = GIT_CHECKOUT_SAFE;
	if (git_reset(repo, target, GIT_RESET_HARD, &opts)) {
		if (is_subsurface_cloud)
			return report_error(translate("gettextFromC", "Could not update local cache to newer remote data"));
		else
			return report_error("Local head checkout failed after update");
	}
	// Not really an error, just informational
#ifdef DEBUG
	report_error("Updated local information from remote");
#endif
	return 0;
}
Example #10
0
int
Reference_target__set__(Reference *self, PyObject *py_target)
{
    git_oid oid;
    char *c_name;
    int err;
    git_reference *new_ref;

    CHECK_REFERENCE_INT(self);

    /* Case 1: Direct */
    if (GIT_REF_OID == git_reference_type(self->reference)) {
        err = py_oid_to_git_oid_expand(self->repo->repo, py_target, &oid);
        if (err < 0)
            return err;

        err = git_reference_set_target(&new_ref, self->reference, &oid);
        if (err < 0)
            goto error;

        git_reference_free(self->reference);
        self->reference = new_ref;
        return 0;
    }

    /* Case 2: Symbolic */
    c_name = py_path_to_c_str(py_target);
    if (c_name == NULL)
        return -1;

    err = git_reference_symbolic_set_target(&new_ref, self->reference, c_name);
    free(c_name);
    if (err < 0)
        goto error;

    git_reference_free(self->reference);
    self->reference = new_ref;
    return 0;

error:
    Error_set(err);
    return -1;
}
Example #11
0
void test_refs_reflog_messages__updating_a_direct_reference(void)
{
	git_reference *ref, *ref_out, *target_ref;
	git_oid target_id;
	const char *message = "You've been logged, mate!";

	git_reference_name_to_id(&target_id, g_repo, "refs/heads/haacked");
	cl_git_pass(git_reference_lookup(&target_ref, g_repo, "refs/heads/haacked"));

	cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master"));

	cl_git_pass(git_reference_set_target(&ref_out, ref, &target_id, message));

	cl_reflog_check_entry(g_repo, "refs/heads/master", 0,
		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
		"258f0e2a959a364e40ed6603d5d44fbb24765b10",
		NULL, message);

	git_reference_free(target_ref);
	git_reference_free(ref);
	git_reference_free(ref_out);
}
Example #12
0
static int update_head(git_repository *repo, git_object *commit)
{
	int error;
	git_reference *head = NULL, *target = NULL;

	error = git_repository_head(&head, repo);

	if (error < 0 && error != GIT_EORPHANEDHEAD)
		return error;

	if (error == GIT_EORPHANEDHEAD) {
		giterr_clear();

		/*
		 * TODO: This is a bit weak as this doesn't support chained
		 * symbolic references. yet.
		 */
		if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0)
			goto cleanup;

		if ((error = git_reference_create(
			&target,
			repo,
			git_reference_symbolic_target(head),
			git_object_id(commit), 0)) < 0)
				goto cleanup;
	} else {
		if ((error = git_reference_set_target(head, git_object_id(commit))) < 0)
			goto cleanup;
	}

	error = 0;

cleanup:
	git_reference_free(head);
	git_reference_free(target);
	return error;
}
Example #13
0
/**
 * Advances the ref with the given name, causing it to point to the object
 * with the given id.
 */
static int sync_fast_forward(git_repository *repo,
                             const char *name,
                             git_oid *id)
{
    int e = 0;
    git_reference *old_ref = NULL;
    git_reference *new_ref = NULL;

    e = git_reference_lookup(&old_ref, repo, name);
    if (!e)
    {
        git_reference_set_target(&new_ref, old_ref, id, "fast-forward");
    }
    else if (e == GIT_ENOTFOUND)
    {
        git_check(git_reference_create(&new_ref, repo, name, id, 0, "create branch"));
    }

exit:
    if (old_ref)        git_reference_free(old_ref);
    if (new_ref)        git_reference_free(new_ref);
    return e;
}
Example #14
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);
}
Example #15
0
/**
 * Perform a fast-forward merge
 *
 * @param merge_result S4 class git_merge_result
 * @param merge_head The merge head to fast-forward merge
 * @param repository The repository
 * @param log_message First part of the one line long message in the reflog
 * @return 0 on success, or error code
 */
static int git2r_fast_forward_merge(
    SEXP merge_result,
    const git_annotated_commit *merge_head,
    git_repository *repository,
    const char *log_message)
{
    int err;
    const git_oid *oid;
    git_buf buf = GIT_BUF_INIT;
    git_commit *commit = NULL;
    git_tree *tree = NULL;
    git_reference *reference = NULL;
    git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;

    oid = git_annotated_commit_id(merge_head);
    err = git_commit_lookup(&commit, repository, oid);
    if (err)
        goto cleanup;

    err = git_commit_tree(&tree, commit);
    if (err)
        goto cleanup;

    opts.checkout_strategy = GIT_CHECKOUT_SAFE;
    err = git_checkout_tree(repository, (git_object*)tree, &opts);
    if (err)
        goto cleanup;

    err = git_repository_head(&reference, repository);
    if (err) {
        if (GIT_ENOTFOUND != err)
            goto cleanup;
    }

    err = git_buf_printf(&buf, "%s: Fast-forward", log_message);
    if (err)
        goto cleanup;

    if (GIT_ENOTFOUND == err) {
        err = git_reference_create(
            &reference,
            repository,
            "HEAD",
            git_commit_id(commit),
            0, /* force */
            buf.ptr);
    } else {
        git_reference *target_ref = NULL;

        err = git_reference_set_target(
            &target_ref,
            reference,
            git_commit_id(commit),
            buf.ptr);

        if (target_ref)
            git_reference_free(target_ref);
    }

    SET_SLOT(
        merge_result,
        Rf_install("fast_forward"),
        ScalarLogical(1));

    SET_SLOT(
        merge_result,
        Rf_install("conflicts"),
        ScalarLogical(0));

cleanup:
    git_buf_free(&buf);

    if (commit)
        git_commit_free(commit);

    if (reference)
        git_reference_free(reference);

    if (tree)
        git_tree_free(tree);

    return err;
}
Example #16
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 #17
0
static int try_to_git_merge(git_repository *repo, git_reference *local, git_reference *remote, git_oid *base, const git_oid *local_id, const git_oid *remote_id)
{
	git_tree *local_tree, *remote_tree, *base_tree;
	git_commit *local_commit, *remote_commit, *base_commit;
	git_index *merged_index;
	git_merge_options merge_options;

	if (verbose) {
		char outlocal[41], outremote[41];
		outlocal[40] = outremote[40] = 0;
		git_oid_fmt(outlocal, local_id);
		git_oid_fmt(outremote, remote_id);
		fprintf(stderr, "trying to merge local SHA %s remote SHA %s\n", outlocal, outremote);
	}

	git_merge_init_options(&merge_options, GIT_MERGE_OPTIONS_VERSION);
#ifdef USE_LIBGIT23_API
	merge_options.tree_flags = GIT_MERGE_TREE_FIND_RENAMES;
#else
	merge_options.flags = GIT_MERGE_TREE_FIND_RENAMES;
#endif
	merge_options.file_favor = GIT_MERGE_FILE_FAVOR_UNION;
	merge_options.rename_threshold = 100;
	if (git_commit_lookup(&local_commit, repo, local_id))
		return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: can't get commit (%s)"), giterr_last()->message);
	if (git_commit_tree(&local_tree, local_commit))
		return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: failed local tree lookup (%s)"), giterr_last()->message);
	if (git_commit_lookup(&remote_commit, repo, remote_id))
		return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: can't get commit (%s)"), giterr_last()->message);
	if (git_commit_tree(&remote_tree, remote_commit))
		return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: failed remote tree lookup (%s)"), giterr_last()->message);
	if (git_commit_lookup(&base_commit, repo, base))
		return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: can't get commit: (%s)"), giterr_last()->message);
	if (git_commit_tree(&base_tree, base_commit))
		return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: failed base tree lookup: (%s)"), giterr_last()->message);
	if (git_merge_trees(&merged_index, repo, base_tree, local_tree, remote_tree, &merge_options))
		return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge failed (%s)"), giterr_last()->message);
	if (git_index_has_conflicts(merged_index)) {
		int error;
		const git_index_entry *ancestor = NULL,
				*ours = NULL,
				*theirs = NULL;
		git_index_conflict_iterator *iter = NULL;
		error = git_index_conflict_iterator_new(&iter, merged_index);
		while (git_index_conflict_next(&ancestor, &ours, &theirs, iter)
		       != GIT_ITEROVER) {
			/* Mark this conflict as resolved */
			fprintf(stderr, "conflict in %s / %s / %s -- ",
				ours ? ours->path : "-",
				theirs ? theirs->path : "-",
				ancestor ? ancestor->path : "-");
			if ((!ours && theirs && ancestor) ||
			    (ours && !theirs && ancestor)) {
				// the file was removed on one side or the other - just remove it
				fprintf(stderr, "looks like a delete on one side; removing the file from the index\n");
				error = git_index_remove(merged_index, ours ? ours->path : theirs->path, GIT_INDEX_STAGE_ANY);
			} else {
				error = git_index_conflict_remove(merged_index, ours ? ours->path : theirs ? theirs->path : ancestor->path);
			}
			if (error) {
				fprintf(stderr, "error at conflict resplution (%s)", giterr_last()->message);
			}
		}
		git_index_conflict_cleanup(merged_index);
		git_index_conflict_iterator_free(iter);
		report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge conflict - manual intervention needed"));
	}
	git_oid merge_oid, commit_oid;
	git_tree *merged_tree;
	git_signature *author;
	git_commit *commit;

	if (git_index_write_tree_to(&merge_oid, merged_index, repo))
		return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: writing the tree failed (%s)"), giterr_last()->message);
	if (git_tree_lookup(&merged_tree, repo, &merge_oid))
		return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: tree lookup failed (%s)"), giterr_last()->message);
	if (git_signature_default(&author, repo) < 0)
		return report_error(translate("gettextFromC", "Failed to get author: (%s)"), giterr_last()->message);
	if (git_commit_create_v(&commit_oid, repo, NULL, author, author, NULL, "automatic merge", merged_tree, 2, local_commit, remote_commit))
		return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: git commit create failed (%s)"), giterr_last()->message);
	if (git_commit_lookup(&commit, repo, &commit_oid))
		return report_error(translate("gettextFromC", "Error: could not lookup the merge commit I just created (%s)"), giterr_last()->message);
	if (git_branch_is_head(local) && !git_repository_is_bare(repo)) {
		git_object *parent;
		git_reference_peel(&parent, local, GIT_OBJ_COMMIT);
		if (update_git_checkout(repo, parent, merged_tree)) {
			report_error("Warning: checked out branch is inconsistent with git data");
		}
	}
	if (git_reference_set_target(&local, local, &commit_oid, "Subsurface merge event"))
		return report_error("Error: failed to update branch (%s)", giterr_last()->message);
	set_git_id(&commit_oid);
	git_signature_free(author);

	return 0;
}
Example #18
0
static int try_to_git_merge(git_repository *repo, git_reference **local_p, git_reference *remote, git_oid *base, const git_oid *local_id, const git_oid *remote_id)
{
	UNUSED(remote);
	git_tree *local_tree, *remote_tree, *base_tree;
	git_commit *local_commit, *remote_commit, *base_commit;
	git_index *merged_index;
	git_merge_options merge_options;

	if (verbose) {
		char outlocal[41], outremote[41];
		outlocal[40] = outremote[40] = 0;
		git_oid_fmt(outlocal, local_id);
		git_oid_fmt(outremote, remote_id);
		fprintf(stderr, "trying to merge local SHA %s remote SHA %s\n", outlocal, outremote);
	}

	git_merge_init_options(&merge_options, GIT_MERGE_OPTIONS_VERSION);
#if !LIBGIT2_VER_MAJOR && LIBGIT2_VER_MINOR > 23
	merge_options.flags = GIT_MERGE_FIND_RENAMES;
#else
	merge_options.tree_flags = GIT_MERGE_TREE_FIND_RENAMES;
#endif
	merge_options.file_favor = GIT_MERGE_FILE_FAVOR_UNION;
	merge_options.rename_threshold = 100;
	if (git_commit_lookup(&local_commit, repo, local_id)) {
		fprintf(stderr, "Remote storage and local data diverged. Error: can't get commit (%s)", giterr_last()->message);
		goto diverged_error;
	}
	if (git_commit_tree(&local_tree, local_commit)) {
		fprintf(stderr, "Remote storage and local data diverged. Error: failed local tree lookup (%s)", giterr_last()->message);
		goto diverged_error;
	}
	if (git_commit_lookup(&remote_commit, repo, remote_id)) {
		fprintf(stderr, "Remote storage and local data diverged. Error: can't get commit (%s)", giterr_last()->message);
		goto diverged_error;
	}
	if (git_commit_tree(&remote_tree, remote_commit)) {
		fprintf(stderr, "Remote storage and local data diverged. Error: failed local tree lookup (%s)", giterr_last()->message);
		goto diverged_error;
	}
	if (git_commit_lookup(&base_commit, repo, base)) {
		fprintf(stderr, "Remote storage and local data diverged. Error: can't get commit (%s)", giterr_last()->message);
		goto diverged_error;
	}
	if (git_commit_tree(&base_tree, base_commit)) {
		fprintf(stderr, "Remote storage and local data diverged. Error: failed base tree lookup (%s)", giterr_last()->message);
		goto diverged_error;
	}
	if (git_merge_trees(&merged_index, repo, base_tree, local_tree, remote_tree, &merge_options)) {
		fprintf(stderr, "Remote storage and local data diverged. Error: merge failed (%s)", giterr_last()->message);
		// this is the one where I want to report more detail to the user - can't quite explain why
		return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge failed (%s)"), giterr_last()->message);
	}
	if (git_index_has_conflicts(merged_index)) {
		int error;
		const git_index_entry *ancestor = NULL,
				*ours = NULL,
				*theirs = NULL;
		git_index_conflict_iterator *iter = NULL;
		error = git_index_conflict_iterator_new(&iter, merged_index);
		while (git_index_conflict_next(&ancestor, &ours, &theirs, iter)
		       != GIT_ITEROVER) {
			/* Mark this conflict as resolved */
			fprintf(stderr, "conflict in %s / %s / %s -- ",
				ours ? ours->path : "-",
				theirs ? theirs->path : "-",
				ancestor ? ancestor->path : "-");
			if ((!ours && theirs && ancestor) ||
			    (ours && !theirs && ancestor)) {
				// the file was removed on one side or the other - just remove it
				fprintf(stderr, "looks like a delete on one side; removing the file from the index\n");
				error = git_index_remove(merged_index, ours ? ours->path : theirs->path, GIT_INDEX_STAGE_ANY);
			} else if (ancestor) {
				error = git_index_conflict_remove(merged_index, ours ? ours->path : theirs ? theirs->path : ancestor->path);
			}
			if (error) {
				fprintf(stderr, "error at conflict resplution (%s)", giterr_last()->message);
			}
		}
		git_index_conflict_cleanup(merged_index);
		git_index_conflict_iterator_free(iter);
		report_error(translate("gettextFromC", "Remote storage and local data diverged. Cannot combine local and remote changes"));
	}
	git_oid merge_oid, commit_oid;
	git_tree *merged_tree;
	git_signature *author;
	git_commit *commit;

	if (git_index_write_tree_to(&merge_oid, merged_index, repo))
		goto write_error;
	if (git_tree_lookup(&merged_tree, repo, &merge_oid))
		goto write_error;
	if (git_signature_default(&author, repo) < 0)
		if (git_signature_now(&author, "Subsurface", "noemail@given") < 0)
			goto write_error;
	if (git_commit_create_v(&commit_oid, repo, NULL, author, author, NULL, "automatic merge", merged_tree, 2, local_commit, remote_commit))
		goto write_error;
	if (git_commit_lookup(&commit, repo, &commit_oid))
		goto write_error;
	if (git_branch_is_head(*local_p) && !git_repository_is_bare(repo)) {
		git_object *parent;
		git_reference_peel(&parent, *local_p, GIT_OBJ_COMMIT);
		if (update_git_checkout(repo, parent, merged_tree)) {
			goto write_error;
		}
	}
	if (git_reference_set_target(local_p, *local_p, &commit_oid, "Subsurface merge event"))
		goto write_error;
	set_git_id(&commit_oid);
	git_signature_free(author);
	if (verbose)
		fprintf(stderr, "Successfully merged repositories");
	return 0;

diverged_error:
	return report_error(translate("gettextFromC", "Remote storage and local data diverged"));

write_error:
	return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: writing the data failed (%s)"), giterr_last()->message);
}
Example #19
0
void Reference::setTarget(const OId& oid, const Signature &signature, const QString &message)
{
    git_reference* rp;
    qGitThrow(git_reference_set_target(&rp, data(), oid.constData(), signature.data(), message.toUtf8()));
    d = ptr_type(rp, git_reference_free);
}