void test_object_tree_write__cruel_paths(void) { static const char *the_paths[] = { "C:\\", " : * ? \" \n < > |", "a\\b", "\\\\b\a", ":\\", "COM1", "foo.aux", REP1024("1234"), /* 4096 char string */ REP1024("12345678"), /* 8192 char string */ "\xC5\xAA\x6E\xC4\xAD\x63\xC5\x8D\x64\x65\xCC\xBD", /* Ūnĭcōde̽ */ NULL }; git_treebuilder *builder; git_tree *tree; git_oid id, bid, subid; const char **scan; int count = 0, i, j; git_tree_entry *te; git_oid_fromstr(&bid, blob_oid); /* create tree */ cl_git_pass(git_treebuilder_create(&builder, NULL)); for (scan = the_paths; *scan; ++scan) { cl_git_pass(git_treebuilder_insert( NULL, builder, *scan, &bid, GIT_FILEMODE_BLOB)); count++; } cl_git_pass(git_treebuilder_write(&id, g_repo, builder)); git_treebuilder_free(builder); /* check data is correct */ cl_git_pass(git_tree_lookup(&tree, g_repo, &id)); cl_assert_equal_i(count, git_tree_entrycount(tree)); for (scan = the_paths; *scan; ++scan) { const git_tree_entry *cte = git_tree_entry_byname(tree, *scan); cl_assert(cte != NULL); cl_assert_equal_s(*scan, git_tree_entry_name(cte)); } for (scan = the_paths; *scan; ++scan) { cl_git_pass(git_tree_entry_bypath(&te, tree, *scan)); cl_assert_equal_s(*scan, git_tree_entry_name(te)); git_tree_entry_free(te); } git_tree_free(tree); /* let's try longer paths */ cl_git_pass(git_treebuilder_create(&builder, NULL)); for (scan = the_paths; *scan; ++scan) { cl_git_pass(git_treebuilder_insert( NULL, builder, *scan, &id, GIT_FILEMODE_TREE)); } cl_git_pass(git_treebuilder_write(&subid, g_repo, builder)); git_treebuilder_free(builder); /* check data is correct */ cl_git_pass(git_tree_lookup(&tree, g_repo, &subid)); cl_assert_equal_i(count, git_tree_entrycount(tree)); for (i = 0; i < count; ++i) { for (j = 0; j < count; ++j) { git_buf b = GIT_BUF_INIT; cl_git_pass(git_buf_joinpath(&b, the_paths[i], the_paths[j])); cl_git_pass(git_tree_entry_bypath(&te, tree, b.ptr)); cl_assert_equal_s(the_paths[j], git_tree_entry_name(te)); git_tree_entry_free(te); git_buf_free(&b); } } git_tree_free(tree); }
static int find_repo( git_buf *repo_path, git_buf *parent_path, const char *start_path, uint32_t flags, const char *ceiling_dirs) { int error; git_buf path = GIT_BUF_INIT; struct stat st; dev_t initial_device = 0; bool try_with_dot_git = ((flags & GIT_REPOSITORY_OPEN_BARE) != 0); int ceiling_offset; git_buf_free(repo_path); if ((error = git_path_prettify(&path, start_path, NULL)) < 0) return error; ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs); if (!try_with_dot_git && (error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0) return error; while (!error && !git_buf_len(repo_path)) { if (p_stat(path.ptr, &st) == 0) { /* check that we have not crossed device boundaries */ if (initial_device == 0) initial_device = st.st_dev; else if (st.st_dev != initial_device && (flags & GIT_REPOSITORY_OPEN_CROSS_FS) == 0) break; if (S_ISDIR(st.st_mode)) { if (valid_repository_path(&path)) { git_path_to_dir(&path); git_buf_set(repo_path, path.ptr, path.size); break; } } else if (S_ISREG(st.st_mode)) { git_buf repo_link = GIT_BUF_INIT; if (!(error = read_gitfile(&repo_link, path.ptr))) { if (valid_repository_path(&repo_link)) git_buf_swap(repo_path, &repo_link); git_buf_free(&repo_link); break; } git_buf_free(&repo_link); } } /* move up one directory level */ if (git_path_dirname_r(&path, path.ptr) < 0) { error = -1; break; } if (try_with_dot_git) { /* if we tried original dir with and without .git AND either hit * directory ceiling or NO_SEARCH was requested, then be done. */ if (path.ptr[ceiling_offset] == '\0' || (flags & GIT_REPOSITORY_OPEN_NO_SEARCH) != 0) break; /* otherwise look first for .git item */ error = git_buf_joinpath(&path, path.ptr, DOT_GIT); } try_with_dot_git = !try_with_dot_git; } if (!error && parent_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) { if (!git_buf_len(repo_path)) git_buf_clear(parent_path); else { git_path_dirname_r(parent_path, path.ptr); git_path_to_dir(parent_path); } if (git_buf_oom(parent_path)) return -1; } git_buf_free(&path); if (!git_buf_len(repo_path) && !error) { giterr_set(GITERR_REPOSITORY, "Could not find repository from '%s'", start_path); error = GIT_ENOTFOUND; } return error; }
/* Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100 */ int git_reference__normalize_name( git_buf *buf, const char *name, unsigned int flags) { char *current; int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC; unsigned int process_flags; bool normalize = (buf != NULL); #ifdef GIT_USE_ICONV git_path_iconv_t ic = GIT_PATH_ICONV_INIT; #endif assert(name); process_flags = flags; current = (char *)name; if (*current == '/') goto cleanup; if (normalize) git_buf_clear(buf); #ifdef GIT_USE_ICONV if ((flags & GIT_REF_FORMAT__PRECOMPOSE_UNICODE) != 0) { size_t namelen = strlen(current); if ((error = git_path_iconv_init_precompose(&ic)) < 0 || (error = git_path_iconv(&ic, ¤t, &namelen)) < 0) goto cleanup; error = GIT_EINVALIDSPEC; } #endif while (true) { segment_len = ensure_segment_validity(current); if (segment_len < 0) { if ((process_flags & GIT_REF_FORMAT_REFSPEC_PATTERN) && current[0] == '*' && (current[1] == '\0' || current[1] == '/')) { /* Accept one wildcard as a full refname component. */ process_flags &= ~GIT_REF_FORMAT_REFSPEC_PATTERN; segment_len = 1; } else goto cleanup; } if (segment_len > 0) { if (normalize) { size_t cur_len = git_buf_len(buf); git_buf_joinpath(buf, git_buf_cstr(buf), current); git_buf_truncate(buf, cur_len + segment_len + (segments_count ? 1 : 0)); if (git_buf_oom(buf)) { error = -1; goto cleanup; } } segments_count++; } /* No empty segment is allowed when not normalizing */ if (segment_len == 0 && !normalize) goto cleanup; if (current[segment_len] == '\0') break; current += segment_len + 1; } /* A refname can not be empty */ if (segment_len == 0 && segments_count == 0) goto cleanup; /* A refname can not end with "." */ if (current[segment_len - 1] == '.') goto cleanup; /* A refname can not end with "/" */ if (current[segment_len - 1] == '/') goto cleanup; if ((segments_count == 1 ) && !(flags & GIT_REF_FORMAT_ALLOW_ONELEVEL)) goto cleanup; if ((segments_count == 1 ) && !(flags & GIT_REF_FORMAT_REFSPEC_SHORTHAND) && !(is_all_caps_and_underscore(name, (size_t)segment_len) || ((flags & GIT_REF_FORMAT_REFSPEC_PATTERN) && !strcmp("*", name)))) goto cleanup; if ((segments_count > 1) && (is_all_caps_and_underscore(name, strchr(name, '/') - name))) goto cleanup; error = 0; cleanup: if (error == GIT_EINVALIDSPEC) giterr_set( GITERR_REFERENCE, "The given reference name '%s' is not valid", name); if (error && normalize) git_buf_free(buf); #ifdef GIT_USE_ICONV git_path_iconv_clear(&ic); #endif return error; }
void test_repo_config__read_with_no_configs_at_all(void) { git_repository *repo; int val; cl_git_pass(git_libgit2_opts( GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); cl_git_pass(git_libgit2_opts( GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr)); cl_git_pass(git_libgit2_opts( GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr)); /* with none */ cl_must_pass(p_unlink("empty_standard_repo/.git/config")); cl_assert(!git_path_isfile("empty_standard_repo/.git/config")); cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); git_repository__cvar_cache_clear(repo); val = -1; cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV)); cl_assert_equal_i(GIT_ABBREV_DEFAULT, val); git_repository_free(repo); /* with no local config, just system */ cl_sandbox_set_search_path_defaults(); cl_must_pass(p_mkdir("alternate/1", 0777)); cl_git_pass(git_buf_joinpath(&path, path.ptr, "1")); cl_git_rewritefile("alternate/1/gitconfig", "[core]\n\tabbrev = 10\n"); cl_git_pass(git_libgit2_opts( GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr)); cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); git_repository__cvar_cache_clear(repo); val = -1; cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV)); cl_assert_equal_i(10, val); git_repository_free(repo); /* with just xdg + system */ cl_must_pass(p_mkdir("alternate/2", 0777)); path.ptr[path.size - 1] = '2'; cl_git_rewritefile("alternate/2/config", "[core]\n\tabbrev = 20\n"); cl_git_pass(git_libgit2_opts( GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr)); cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); git_repository__cvar_cache_clear(repo); val = -1; cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV)); cl_assert_equal_i(20, val); git_repository_free(repo); /* with global + xdg + system */ cl_must_pass(p_mkdir("alternate/3", 0777)); path.ptr[path.size - 1] = '3'; cl_git_rewritefile("alternate/3/.gitconfig", "[core]\n\tabbrev = 30\n"); cl_git_pass(git_libgit2_opts( GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); git_repository__cvar_cache_clear(repo); val = -1; cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV)); cl_assert_equal_i(30, val); git_repository_free(repo); /* with all configs */ cl_git_rewritefile("empty_standard_repo/.git/config", "[core]\n\tabbrev = 40\n"); cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); git_repository__cvar_cache_clear(repo); val = -1; cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV)); cl_assert_equal_i(40, val); git_repository_free(repo); /* with all configs but delete the files ? */ cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); git_repository__cvar_cache_clear(repo); val = -1; cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV)); cl_assert_equal_i(40, val); cl_must_pass(p_unlink("empty_standard_repo/.git/config")); cl_assert(!git_path_isfile("empty_standard_repo/.git/config")); cl_must_pass(p_unlink("alternate/1/gitconfig")); cl_assert(!git_path_isfile("alternate/1/gitconfig")); cl_must_pass(p_unlink("alternate/2/config")); cl_assert(!git_path_isfile("alternate/2/config")); cl_must_pass(p_unlink("alternate/3/.gitconfig")); cl_assert(!git_path_isfile("alternate/3/.gitconfig")); git_repository__cvar_cache_clear(repo); val = -1; cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV)); cl_assert_equal_i(40, val); git_repository_free(repo); /* reopen */ cl_assert(!git_path_isfile("empty_standard_repo/.git/config")); cl_assert(!git_path_isfile("alternate/3/.gitconfig")); cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); git_repository__cvar_cache_clear(repo); val = -1; cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV)); cl_assert_equal_i(7, val); git_repository_free(repo); cl_assert(!git_path_exists("empty_standard_repo/.git/config")); cl_assert(!git_path_exists("alternate/3/.gitconfig")); }
void test_repo_open__from_git_new_workdir(void) { /* The git-new-workdir script that ships with git sets up a bunch of * symlinks to create a second workdir that shares the object db with * another checkout. Libgit2 can open a repo that has been configured * this way. */ cl_git_sandbox_init("empty_standard_repo"); #ifndef GIT_WIN32 git_repository *repo2; git_buf link_tgt = GIT_BUF_INIT, link = GIT_BUF_INIT, body = GIT_BUF_INIT; const char **scan; int link_fd; static const char *links[] = { "config", "refs", "logs/refs", "objects", "info", "hooks", "packed-refs", "remotes", "rr-cache", "svn", NULL }; static const char *copies[] = { "HEAD", NULL }; cl_git_pass(p_mkdir("alternate", 0777)); cl_git_pass(p_mkdir("alternate/.git", 0777)); for (scan = links; *scan != NULL; scan++) { git_buf_joinpath(&link_tgt, "empty_standard_repo/.git", *scan); if (git_path_exists(link_tgt.ptr)) { git_buf_joinpath(&link_tgt, "../../empty_standard_repo/.git", *scan); git_buf_joinpath(&link, "alternate/.git", *scan); if (strchr(*scan, '/')) git_futils_mkpath2file(link.ptr, 0777); cl_assert_(symlink(link_tgt.ptr, link.ptr) == 0, strerror(errno)); } } for (scan = copies; *scan != NULL; scan++) { git_buf_joinpath(&link_tgt, "empty_standard_repo/.git", *scan); if (git_path_exists(link_tgt.ptr)) { git_buf_joinpath(&link, "alternate/.git", *scan); cl_git_pass(git_futils_readbuffer(&body, link_tgt.ptr)); cl_assert((link_fd = git_futils_creat_withpath(link.ptr, 0777, 0666)) >= 0); cl_must_pass(p_write(link_fd, body.ptr, body.size)); p_close(link_fd); } } git_buf_free(&link_tgt); git_buf_free(&link); git_buf_free(&body); cl_git_pass(git_repository_open(&repo2, "alternate")); cl_assert(git_repository_path(repo2) != NULL); cl_assert_(git__suffixcmp(git_repository_path(repo2), "alternate/.git/") == 0, git_repository_path(repo2)); cl_assert(git_repository_workdir(repo2) != NULL); cl_assert_(git__suffixcmp(git_repository_workdir(repo2), "alternate/") == 0, git_repository_workdir(repo2)); git_repository_free(repo2); #endif }
int git_path_apply_relative(git_buf *target, const char *relpath) { git_buf_joinpath(target, git_buf_cstr(target), relpath); return git_path_resolve_relative(target, 0); }
int git_blob__create_from_paths( git_oid *id, struct stat *out_st, git_repository *repo, const char *content_path, const char *hint_path, mode_t hint_mode, bool try_load_filters) { int error; struct stat st; git_odb *odb = NULL; git_off_t size; mode_t mode; git_buf path = GIT_BUF_INIT; assert(hint_path || !try_load_filters); if (!content_path) { if (git_repository__ensure_not_bare(repo, "create blob from file") < 0) return GIT_EBAREREPO; if (git_buf_joinpath( &path, git_repository_workdir(repo), hint_path) < 0) return -1; content_path = path.ptr; } if ((error = git_path_lstat(content_path, &st)) < 0 || (error = git_repository_odb(&odb, repo)) < 0) goto done; if (S_ISDIR(st.st_mode)) { giterr_set(GITERR_ODB, "cannot create blob from '%s': it is a directory", content_path); error = GIT_EDIRECTORY; goto done; } if (out_st) memcpy(out_st, &st, sizeof(st)); size = st.st_size; mode = hint_mode ? hint_mode : st.st_mode; if (S_ISLNK(mode)) { error = write_symlink(id, odb, content_path, (size_t)size); } else { git_filter_list *fl = NULL; if (try_load_filters) /* Load the filters for writing this file to the ODB */ error = git_filter_list_load( &fl, repo, NULL, hint_path, GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT); if (error < 0) /* well, that didn't work */; else if (fl == NULL) /* No filters need to be applied to the document: we can stream * directly from disk */ error = write_file_stream(id, odb, content_path, size); else { /* We need to apply one or more filters */ error = write_file_filtered(id, &size, odb, content_path, fl); git_filter_list_free(fl); } /* * TODO: eventually support streaming filtered files, for files * which are bigger than a given threshold. This is not a priority * because applying a filter in streaming mode changes the final * size of the blob, and without knowing its final size, the blob * cannot be written in stream mode to the ODB. * * The plan is to do streaming writes to a tempfile on disk and then * opening streaming that file to the ODB, using * `write_file_stream`. * * CAREFULLY DESIGNED APIS YO */ } done: git_odb_free(odb); git_buf_dispose(&path); return error; }
void test_submodule_status__ignore_none(void) { unsigned int status; git_submodule *sm; git_buf path = GIT_BUF_INIT; cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged")); cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES)); cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_dir")); cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, g_repo, "not-submodule")); cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, g_repo, "not")); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index")); cl_git_pass(git_submodule_status(&status, sm)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); cl_git_pass(git_submodule_status(&status, sm)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file")); cl_git_pass(git_submodule_status(&status, sm)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file")); cl_git_pass(git_submodule_status(&status, sm)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNTRACKED) != 0); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits")); cl_git_pass(git_submodule_status(&status, sm)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); cl_git_pass(git_submodule_status(&status, sm)); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0); /* removed sm_unchanged for deleted workdir */ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); cl_git_pass(git_submodule_status(&status, sm)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0); /* now mkdir sm_unchanged to test uninitialized */ cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0)); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); cl_git_pass(git_submodule_reload(sm)); cl_git_pass(git_submodule_status(&status, sm)); cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0); /* update sm_changed_head in index */ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); cl_git_pass(git_submodule_add_to_index(sm, true)); /* reload is not needed because add_to_index updates the submodule data */ cl_git_pass(git_submodule_status(&status, sm)); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0); /* remove sm_changed_head from index */ { git_index *index; size_t pos; cl_git_pass(git_repository_index(&index, g_repo)); cl_assert(!git_index_find(&pos, index, "sm_changed_head")); cl_git_pass(git_index_remove(index, "sm_changed_head", 0)); cl_git_pass(git_index_write(index)); git_index_free(index); } cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); cl_git_pass(git_submodule_reload(sm)); cl_git_pass(git_submodule_status(&status, sm)); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_DELETED) != 0); git_buf_free(&path); }
void test_submodule_status__ignore_all(void) { unsigned int status; git_submodule *sm; git_buf path = GIT_BUF_INIT; git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_ALL; cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged")); cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES)); cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign)); cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_dir")); cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, g_repo, "not-submodule")); cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, g_repo, "not")); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index")); cl_git_pass(git_submodule_status(&status, sm)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); cl_git_pass(git_submodule_status(&status, sm)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file")); cl_git_pass(git_submodule_status(&status, sm)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file")); cl_git_pass(git_submodule_status(&status, sm)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits")); cl_git_pass(git_submodule_status(&status, sm)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); cl_git_pass(git_submodule_status(&status, sm)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); /* removed sm_unchanged for deleted workdir */ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); cl_git_pass(git_submodule_status(&status, sm)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); /* now mkdir sm_unchanged to test uninitialized */ cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0)); cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); cl_git_pass(git_submodule_reload(sm)); cl_git_pass(git_submodule_status(&status, sm)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); /* update sm_changed_head in index */ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); cl_git_pass(git_submodule_add_to_index(sm, true)); /* reload is not needed because add_to_index updates the submodule data */ cl_git_pass(git_submodule_status(&status, sm)); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); git_buf_free(&path); }
/* rewrite gitmodules -> .gitmodules * rewrite the empty or relative urls inside each module * rename the .gitted directory inside any submodule to .git */ void rewrite_gitmodules(const char *workdir) { git_buf in_f = GIT_BUF_INIT, out_f = GIT_BUF_INIT, path = GIT_BUF_INIT; FILE *in, *out; char line[256]; cl_git_pass(git_buf_joinpath(&in_f, workdir, "gitmodules")); cl_git_pass(git_buf_joinpath(&out_f, workdir, ".gitmodules")); cl_assert((in = fopen(in_f.ptr, "rb")) != NULL); cl_assert((out = fopen(out_f.ptr, "wb")) != NULL); while (fgets(line, sizeof(line), in) != NULL) { char *scan = line; while (*scan == ' ' || *scan == '\t') scan++; /* rename .gitted -> .git in submodule directories */ if (git__prefixcmp(scan, "path =") == 0) { scan += strlen("path ="); while (*scan == ' ') scan++; git_buf_joinpath(&path, workdir, scan); git_buf_rtrim(&path); git_buf_joinpath(&path, path.ptr, ".gitted"); if (!git_buf_oom(&path) && p_access(path.ptr, F_OK) == 0) { git_buf_joinpath(&out_f, workdir, scan); git_buf_rtrim(&out_f); git_buf_joinpath(&out_f, out_f.ptr, ".git"); if (!git_buf_oom(&out_f)) p_rename(path.ptr, out_f.ptr); } } /* copy non-"url =" lines verbatim */ if (git__prefixcmp(scan, "url =") != 0) { fputs(line, out); continue; } /* convert relative URLs in "url =" lines */ scan += strlen("url ="); while (*scan == ' ') scan++; if (*scan == '.') { git_buf_joinpath(&path, workdir, scan); git_buf_rtrim(&path); } else if (!*scan || *scan == '\n') { git_buf_joinpath(&path, workdir, "../testrepo.git"); } else { fputs(line, out); continue; } git_path_prettify(&path, path.ptr, NULL); git_buf_putc(&path, '\n'); cl_assert(!git_buf_oom(&path)); fwrite(line, scan - line, sizeof(char), out); fputs(path.ptr, out); } fclose(in); fclose(out); cl_must_pass(p_unlink(in_f.ptr)); git_buf_free(&in_f); git_buf_free(&out_f); git_buf_free(&path); }
int git_rebase_open( git_rebase **out, git_repository *repo, const git_rebase_options *given_opts) { git_rebase *rebase; git_buf path = GIT_BUF_INIT, orig_head_name = GIT_BUF_INIT, orig_head_id = GIT_BUF_INIT, onto_id = GIT_BUF_INIT; int state_path_len, error; assert(repo); if ((error = rebase_check_versions(given_opts)) < 0) return error; if (rebase_alloc(&rebase, given_opts) < 0) return -1; rebase->repo = repo; if ((error = rebase_state_type(&rebase->type, &rebase->state_path, repo)) < 0) goto done; if (rebase->type == GIT_REBASE_TYPE_NONE) { giterr_set(GITERR_REBASE, "there is no rebase in progress"); error = GIT_ENOTFOUND; goto done; } if ((error = git_buf_puts(&path, rebase->state_path)) < 0) goto done; state_path_len = git_buf_len(&path); if ((error = git_buf_joinpath(&path, path.ptr, HEAD_NAME_FILE)) < 0 || (error = git_futils_readbuffer(&orig_head_name, path.ptr)) < 0) goto done; git_buf_rtrim(&orig_head_name); if (strcmp(ORIG_DETACHED_HEAD, orig_head_name.ptr) == 0) rebase->head_detached = 1; git_buf_truncate(&path, state_path_len); if ((error = git_buf_joinpath(&path, path.ptr, ORIG_HEAD_FILE)) < 0) goto done; if (!git_path_isfile(path.ptr)) { /* Previous versions of git.git used 'head' here; support that. */ git_buf_truncate(&path, state_path_len); if ((error = git_buf_joinpath(&path, path.ptr, HEAD_FILE)) < 0) goto done; } if ((error = git_futils_readbuffer(&orig_head_id, path.ptr)) < 0) goto done; git_buf_rtrim(&orig_head_id); if ((error = git_oid_fromstr(&rebase->orig_head_id, orig_head_id.ptr)) < 0) goto done; git_buf_truncate(&path, state_path_len); if ((error = git_buf_joinpath(&path, path.ptr, ONTO_FILE)) < 0 || (error = git_futils_readbuffer(&onto_id, path.ptr)) < 0) goto done; git_buf_rtrim(&onto_id); if ((error = git_oid_fromstr(&rebase->onto_id, onto_id.ptr)) < 0) goto done; if (!rebase->head_detached) rebase->orig_head_name = git_buf_detach(&orig_head_name); switch (rebase->type) { case GIT_REBASE_TYPE_INTERACTIVE: giterr_set(GITERR_REBASE, "interactive rebase is not supported"); error = -1; break; case GIT_REBASE_TYPE_MERGE: error = rebase_open_merge(rebase); break; case GIT_REBASE_TYPE_APPLY: giterr_set(GITERR_REBASE, "patch application rebase is not supported"); error = -1; break; default: abort(); } done: if (error == 0) *out = rebase; else git_rebase_free(rebase); git_buf_free(&path); git_buf_free(&orig_head_name); git_buf_free(&orig_head_id); git_buf_free(&onto_id); return error; }
static int rebase_copy_notes( git_rebase *rebase, const git_signature *committer) { git_buf path = GIT_BUF_INIT, rewritten = GIT_BUF_INIT, notes_ref = GIT_BUF_INIT; char *pair_list, *fromstr, *tostr, *end; git_oid from, to; unsigned int linenum = 1; int error = 0; if ((error = notes_ref_lookup(¬es_ref, rebase)) < 0) { if (error == GIT_ENOTFOUND) { giterr_clear(); error = 0; } goto done; } if ((error = git_buf_joinpath(&path, rebase->state_path, REWRITTEN_FILE)) < 0 || (error = git_futils_readbuffer(&rewritten, path.ptr)) < 0) goto done; pair_list = rewritten.ptr; while (*pair_list) { fromstr = pair_list; if ((end = strchr(fromstr, '\n')) == NULL) goto on_error; pair_list = end+1; *end = '\0'; if ((end = strchr(fromstr, ' ')) == NULL) goto on_error; tostr = end+1; *end = '\0'; if (strlen(fromstr) != GIT_OID_HEXSZ || strlen(tostr) != GIT_OID_HEXSZ || git_oid_fromstr(&from, fromstr) < 0 || git_oid_fromstr(&to, tostr) < 0) goto on_error; if ((error = rebase_copy_note(rebase, notes_ref.ptr, &from, &to, committer)) < 0) goto done; linenum++; } goto done; on_error: giterr_set(GITERR_REBASE, "invalid rewritten file at line %d", linenum); error = -1; done: git_buf_free(&rewritten); git_buf_free(&path); git_buf_free(¬es_ref); return error; }