#define push_attrs(R,S,B,F) \
git_attr_cache__push_file((R),(S),(B),(F),git_attr_file__from_file)
+typedef struct {
+ git_repository *repo;
+ git_vector *files;
+} attr_walk_up_info;
+
+static int push_one_attr(void *ref, git_buf *path)
+{
+ attr_walk_up_info *info = (attr_walk_up_info *)ref;
+ return push_attrs(info->repo, info->files, path->ptr, GIT_ATTR_FILE);
+}
+
static int collect_attr_files(
git_repository *repo, const char *path, git_vector *files)
{
git_buf dir = GIT_BUF_INIT;
git_config *cfg;
const char *workdir = git_repository_workdir(repo);
+ attr_walk_up_info info;
if ((error = git_attr_cache__init(repo)) < GIT_SUCCESS)
goto cleanup;
if (error < GIT_SUCCESS)
goto cleanup;
- if (workdir && git__prefixcmp(dir.ptr, workdir) == 0) {
- ssize_t rootlen = (ssize_t)strlen(workdir);
-
- do {
- error = push_attrs(repo, files, dir.ptr, GIT_ATTR_FILE);
- if (error == GIT_SUCCESS) {
- git_path_dirname_r(&dir, dir.ptr);
- git_path_to_dir(&dir);
- error = git_buf_lasterror(&dir);
- }
- } while (!error && dir.size >= rootlen);
- } else {
- error = push_attrs(repo, files, dir.ptr, GIT_ATTR_FILE);
- }
+ info.repo = repo;
+ info.files = files;
+ error = git_path_walk_up(&dir, workdir, push_one_attr, &info);
if (error < GIT_SUCCESS)
goto cleanup;
#define push_ignore(R,S,B,F) \
git_attr_cache__push_file((R),(S),(B),(F),load_ignore_file)
+typedef struct {
+ git_repository *repo;
+ git_vector *stack;
+} ignore_walk_up_info;
+
+static int push_one_ignore(void *ref, git_buf *path)
+{
+ ignore_walk_up_info *info = (ignore_walk_up_info *)ref;
+ return push_ignore(info->repo, info->stack, path->ptr, GIT_IGNORE_FILE);
+}
+
int git_ignore__for_path(git_repository *repo, const char *path, git_vector *stack)
{
int error = GIT_SUCCESS;
- git_buf dir = GIT_BUF_INIT, scan;
+ git_buf dir = GIT_BUF_INIT;
git_config *cfg;
const char *workdir = git_repository_workdir(repo);
+ ignore_walk_up_info info;
if ((error = git_attr_cache__init(repo)) < GIT_SUCCESS)
goto cleanup;
goto cleanup;
/* load .gitignore up the path */
- git_path_walk_up(&dir, &scan, workdir, {
- error = push_ignore(repo, stack, scan.ptr, GIT_IGNORE_FILE);
- if (error < GIT_SUCCESS) break;
- });
- if (error < GIT_SUCCESS)
+ info.repo = repo;
+ info.stack = stack;
+ if ((error = git_path_walk_up(&dir, workdir, push_one_ignore, &info)) < GIT_SUCCESS)
goto cleanup;
/* load .git/info/exclude */
return error;
}
+
+int git_path_walk_up(
+ git_buf *path,
+ const char *ceiling,
+ int (*cb)(void *data, git_buf *),
+ void *data)
+{
+ int error = GIT_SUCCESS;
+ git_buf iter;
+ ssize_t stop = 0, scan;
+ char oldc = '\0';
+
+ assert(path && cb);
+
+ if (ceiling != NULL) {
+ if (git__prefixcmp(path->ptr, ceiling) == GIT_SUCCESS)
+ stop = (ssize_t)strlen(ceiling);
+ else
+ stop = path->size;
+ }
+ scan = path->size;
+
+ iter.ptr = path->ptr;
+ iter.size = path->size;
+
+ while (scan >= stop) {
+ if ((error = cb(data, &iter)) < GIT_SUCCESS)
+ break;
+ iter.ptr[scan] = oldc;
+ scan = git_buf_rfind_next(&iter, '/');
+ if (scan >= 0) {
+ scan++;
+ oldc = iter.ptr[scan];
+ iter.size = scan;
+ iter.ptr[scan] = '\0';
+ }
+ }
+
+ iter.ptr[scan] = oldc;
+
+ return error;
+}
extern int git__percent_decode(git_buf *decoded_out, const char *input);
extern int git_path_fromurl(git_buf *local_path_out, const char *file_url);
-/*
- * Use as:
- *
- * git_path_walk_up(
- * git_buf *path, git_buf *iterator, const char *root_path,
- * ... CALLBACK CODE ...)
+/**
+ * Invoke callback directory by directory up the path until the ceiling
+ * is reached (inclusive of a final call at the root_path).
*
- * to invoke callback directory by directory up the path until the root_path
- * is reached (inclusive of a final call at the root_path). If root path is
- * NULL or the path is not contained in the root_path, then the callback
- * code will be invoked just once on input path.
+ * If the ceiling is NULL, this will walk all the way up to the root.
+ * If the ceiling is not a prefix of the path, the callback will be
+ * invoked a single time on the verbatim input path. Returning anything
+ * other than GIT_SUCCESS from the callback function will stop the
+ * iteration and propogate the error to the caller.
*/
-#define git_path_walk_up(B,IB,ROOT,CODE) do { \
- ssize_t _stop = ((ROOT) && git__prefixcmp((B)->ptr, (ROOT))) ? (ssize_t)strlen(ROOT) : (B)->size; \
- ssize_t _scan = (B)->size; char _oldc = '\0'; \
- (IB)->ptr = (B)->ptr; (IB)->size = (B)->size; \
- while (_scan >= _stop) { \
- CODE; \
- (IB)->ptr[_scan] = _oldc; \
- _scan = git_buf_rfind_next((IB), '/'); \
- if (_scan >= 0) { _scan++; _oldc = (IB)->ptr[_scan]; (IB)->size = _scan; (IB)->ptr[_scan] = '\0'; } \
- } (IB)->ptr[_scan] = _oldc; } while (0)
+extern int git_path_walk_up(
+ git_buf *path, const char *ceiling,
+ int (*cb)(void *data, git_buf *), void *data);
#endif
check_fromurl(ABS_PATH_MARKER "a", "file:///a", 0);
}
+typedef struct {
+ int expect_idx;
+ char **expect;
+} check_walkup_info;
+
+static int check_one_walkup_step(void *ref, git_buf *path)
+{
+ check_walkup_info *info = (check_walkup_info *)ref;
+ cl_assert(info->expect[info->expect_idx] != NULL);
+ cl_assert_strequal(info->expect[info->expect_idx], path->ptr);
+ info->expect_idx++;
+ return GIT_SUCCESS;
+}
+
void test_core_path__11_walkup(void)
{
- git_buf p = GIT_BUF_INIT, iter;
+ git_buf p = GIT_BUF_INIT;
char *expect[] = {
"/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
"/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
};
char *root[] = { NULL, NULL, "/", "", "/a/b", "/a/b/", NULL, NULL, NULL };
int i, j;
+ check_walkup_info info;
+
+ info.expect = expect;
for (i = 0, j = 0; expect[i] != NULL; i++, j++) {
- int cb_count = 0;
git_buf_sets(&p, expect[i]);
- git_path_walk_up(&p, &iter, root[j], {
- cl_assert(expect[i + cb_count] != NULL);
- cl_assert_strequal(expect[i + cb_count], iter.ptr);
- cb_count++; });
+ info.expect_idx = i;
+ cl_git_pass(
+ git_path_walk_up(&p, root[j], check_one_walkup_step, &info)
+ );
cl_assert_strequal(p.ptr, expect[i]);