/** * We still need to fail if part of the namespace is * still in use. */ void test_refs_branches_create__name_vs_namespace_fail(void) { const char * name; struct item { const char *first; const char *first_alternate; const char *second; }; static const struct item item[] = { { "level_one/level_two", "level_one/alternate", "level_one" }, { "a/b/c/d/e", "a/b/c/d/alternate", "a/b/c/d" }, { "ss/tt/uu/vv/ww", "ss/alternate", "ss" }, { NULL, NULL, NULL }, }; const struct item *p; retrieve_known_commit(&target, repo); for (p=item; p->first; p++) { cl_git_pass(git_branch_create(&branch, repo, p->first, target, 0, NULL, NULL)); cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target))); cl_git_pass(git_branch_name(&name, branch)); cl_assert_equal_s(name, p->first); cl_git_pass(git_branch_delete(branch)); git_reference_free(branch); branch = NULL; cl_git_pass(git_branch_create(&branch, repo, p->first_alternate, target, 0, NULL, NULL)); cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target))); cl_git_pass(git_branch_name(&name, branch)); cl_assert_equal_s(name, p->first_alternate); /* we do not delete the alternate. */ git_reference_free(branch); branch = NULL; cl_git_fail(git_branch_create(&branch, repo, p->second, target, 0, NULL, NULL)); git_reference_free(branch); branch = NULL; } }
void test_refs_branches_delete__can_delete_a_local_branch(void) { git_reference *branch; cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL)); cl_git_pass(git_branch_delete(branch)); git_reference_free(branch); }
void test_refs_branches_delete__can_delete_a_remote_branch(void) { git_reference *branch; cl_git_pass(git_branch_lookup(&branch, repo, "nulltoken/master", GIT_BRANCH_REMOTE)); cl_git_pass(git_branch_delete(branch)); git_reference_free(branch); }
/** * Verify that we can create a branch with a name that matches the * namespace of a previously delete branch. * * git branch level_one/level_two * git branch -D level_one/level_two * git branch level_one * * We expect the delete to have deleted the files: * ".git/refs/heads/level_one/level_two" * ".git/logs/refs/heads/level_one/level_two" * It may or may not have deleted the (now empty) * containing directories. To match git.git behavior, * the second create needs to implicilty delete the * directories and create the new files. * "refs/heads/level_one" * "logs/refs/heads/level_one" * * We should not fail to create the branch or its * reflog because of an obsolete namespace container * directory. */ void test_refs_branches_create__name_vs_namespace(void) { const char * name; struct item { const char *first; const char *second; }; static const struct item item[] = { { "level_one/level_two", "level_one" }, { "a/b/c/d/e", "a/b/c/d" }, { "ss/tt/uu/vv/ww", "ss" }, /* And one test case that is deeper. */ { "xx1/xx2/xx3/xx4", "xx1/xx2/xx3/xx4/xx5/xx6" }, { NULL, NULL }, }; const struct item *p; retrieve_known_commit(&target, repo); for (p=item; p->first; p++) { cl_git_pass(git_branch_create(&branch, repo, p->first, target, 0, NULL, NULL)); cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target))); cl_git_pass(git_branch_name(&name, branch)); cl_assert_equal_s(name, p->first); cl_git_pass(git_branch_delete(branch)); git_reference_free(branch); branch = NULL; cl_git_pass(git_branch_create(&branch, repo, p->second, target, 0, NULL, NULL)); git_reference_free(branch); branch = NULL; } }
static void assert_non_exisitng_branch_removal(const char *branch_name, git_branch_t branch_type) { int error; error = git_branch_delete(repo, branch_name, branch_type); cl_git_fail(error); cl_assert_equal_i(GIT_ENOTFOUND, error); }
void test_refs_branches_delete__can_not_delete_a_branch_if_HEAD_is_missing(void) { git_reference *head; cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE)); git_reference_delete(head); cl_git_fail(git_branch_delete(repo, "br2", GIT_BRANCH_LOCAL)); }
void test_refs_branches_delete__can_delete_a_branch_when_HEAD_is_unborn(void) { git_reference *branch; make_head_unborn(repo, NON_EXISTING_HEAD); cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL)); cl_git_pass(git_branch_delete(branch)); git_reference_free(branch); }
void test_refs_branches_delete__can_not_delete_a_branch_pointed_at_by_HEAD(void) { git_reference *head; /* Ensure HEAD targets the local master branch */ cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE)); cl_assert(strcmp("refs/heads/master", git_reference_target(head)) == 0); git_reference_free(head); cl_git_fail(git_branch_delete(repo, "master", GIT_BRANCH_LOCAL)); }
void test_refs_branches_delete__can_delete_a_branch_pointed_at_by_detached_HEAD(void) { git_reference *master, *head; /* Detach HEAD and make it target the commit that "master" points to */ cl_git_pass(git_reference_lookup(&master, repo, "refs/heads/master")); cl_git_pass(git_reference_create_oid(&head, repo, "HEAD", git_reference_oid(master), 1)); git_reference_free(head); git_reference_free(master); cl_git_pass(git_branch_delete(repo, "master", GIT_BRANCH_LOCAL)); }
void test_refs_branches_delete__can_delete_a_branch_even_if_HEAD_is_missing(void) { git_reference *head; git_reference *branch; cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE)); git_reference_delete(head); git_reference_free(head); cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL)); cl_git_pass(git_branch_delete(branch)); git_reference_free(branch); }
void test_refs_branches_delete__deleting_a_branch_removes_related_configuration_data(void) { git_reference *branch; assert_config_entry_existence(repo, "branch.track-local.remote", true); assert_config_entry_existence(repo, "branch.track-local.merge", true); cl_git_pass(git_branch_lookup(&branch, repo, "track-local", GIT_BRANCH_LOCAL)); cl_git_pass(git_branch_delete(branch)); git_reference_free(branch); assert_config_entry_existence(repo, "branch.track-local.remote", false); assert_config_entry_existence(repo, "branch.track-local.merge", false); }
void test_refs_branches_delete__can_delete_a_branch_pointed_at_by_detached_HEAD(void) { git_reference *head, *branch; cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE)); cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head)); cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head)); git_reference_free(head); /* Detach HEAD and make it target the commit that "master" points to */ git_repository_detach_head(repo); cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL)); cl_git_pass(git_branch_delete(branch)); git_reference_free(branch); }
static void do_prune(git_repository *repo, const char *name) { git_reference *branch; int err; err = git_branch_lookup(&branch, repo, name, GIT_BRANCH_LOCAL); if (err != 0) { gbr_perror("git_branch_lookup()"); return; } if (git_branch_delete(branch) != 0) { gbr_perror("git_branch_delete()"); git_reference_free(branch); } }
/** * ggit_branch_delete: * @branch: a #GgitBranch. * @error: a #GError for error reporting, or %NULL. * * Deletes an existing branch reference. * * If the branch is successfully deleted, this object is * not useful anymore and if should be freed with g_object_unref(). */ void ggit_branch_delete (GgitBranch *branch, GError **error) { gint ret; g_return_if_fail (GGIT_IS_BRANCH (branch)); g_return_if_fail (error == NULL || *error == NULL); ret = git_branch_delete (_ggit_native_get (branch)); if (ret != GIT_OK) { _ggit_error_set (error, ret); } }
PyObject * Branch_delete(Branch *self, PyObject *args) { int err; CHECK_REFERENCE(self); /* Delete the branch */ err = git_branch_delete(self->reference); if (err < 0) return Error_set(err); git_reference_free(self->reference); self->reference = NULL; /* Invalidate the pointer */ Py_RETURN_NONE; }
/* * call-seq: * branches.delete(branch) -> nil * branches.delete(name) -> nil * * Delete the specified branch. * * If a Rugged::Branch object was passed, the object will become * invalidated and won't be able to be used for any other operations. */ static VALUE rb_git_branch_collection_delete(VALUE self, VALUE rb_name_or_branch) { git_reference *branch; git_repository *repo; VALUE rb_repo = rugged_owner(self); int error; rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); error = rugged_branch_lookup(&branch, repo, rb_name_or_branch); rugged_exception_check(error); error = git_branch_delete(branch); git_reference_free(branch); rugged_exception_check(error); return Qnil; }
void test_refs_branches_create__can_create_branch_with_unicode(void) { const char *nfc = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D"; const char *nfd = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D"; const char *emoji = "\xF0\x9F\x8D\xB7"; const char *names[] = { nfc, nfd, emoji }; const char *alt[] = { nfd, nfc, NULL }; const char *expected[] = { nfc, nfd, emoji }; unsigned int i; bool fs_decompose_unicode = git_path_does_fs_decompose_unicode(git_repository_path(repo)); retrieve_known_commit(&target, repo); if (cl_repo_get_bool(repo, "core.precomposeunicode")) expected[1] = nfc; /* test decomp. because not all Mac filesystems decompose unicode */ else if (fs_decompose_unicode) expected[0] = nfd; for (i = 0; i < ARRAY_SIZE(names); ++i) { const char *name; cl_git_pass(git_branch_create( &branch, repo, names[i], target, 0, NULL, NULL)); cl_git_pass(git_oid_cmp( git_reference_target(branch), git_commit_id(target))); cl_git_pass(git_branch_name(&name, branch)); cl_assert_equal_s(expected[i], name); assert_branch_matches_name(expected[i], names[i]); if (fs_decompose_unicode && alt[i]) assert_branch_matches_name(expected[i], alt[i]); cl_git_pass(git_branch_delete(branch)); git_reference_free(branch); branch = NULL; } }
void test_refs_branches_delete__removes_reflog(void) { git_reference *branch; git_reflog *log; git_oid oidzero = {{0}}; git_signature *sig; /* Ensure the reflog has at least one entry */ cl_git_pass(git_signature_now(&sig, "Me", "*****@*****.**")); cl_git_pass(git_reflog_read(&log, repo, "refs/heads/track-local")); cl_git_pass(git_reflog_append(log, &oidzero, sig, "message")); cl_assert(git_reflog_entrycount(log) > 0); git_signature_free(sig); git_reflog_free(log); cl_git_pass(git_branch_lookup(&branch, repo, "track-local", GIT_BRANCH_LOCAL)); cl_git_pass(git_branch_delete(branch)); git_reference_free(branch); /* Reading a nonexistant reflog creates it, but it should be empty */ cl_git_pass(git_reflog_read(&log, repo, "refs/heads/track-local")); cl_assert_equal_i(0, git_reflog_entrycount(log)); git_reflog_free(log); }
void Branch::del() { Exception::git2_assert(git_branch_delete(data())); }
void test_refs_branches_delete__can_not_delete_a_non_existing_branch(void) { cl_git_fail(git_branch_delete(repo, "i-am-not-a-local-branch", GIT_BRANCH_LOCAL)); cl_git_fail(git_branch_delete(repo, "neither/a-remote-one", GIT_BRANCH_REMOTE)); }
void test_refs_branches_delete__can_delete_a_local_branch(void) { cl_git_pass(git_branch_delete(repo, "br2", GIT_BRANCH_LOCAL)); }
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 test_refs_branches_delete__can_delete_a_remote_branch(void) { cl_git_pass(git_branch_delete(repo, "nulltoken/master", GIT_BRANCH_REMOTE)); }