commit 3143d852d788e42c45a61252acb935a698efed2f from: Stefan Sperling date: Thu Jun 25 06:53:54 2020 UTC fix ignores when a path is passed to 'got status' Problem reported by semarie, who also provided initial regression test code. ok semarie commit - 054041d073d5af5bb32b7c3299af9cf2bf85c426 commit + 3143d852d788e42c45a61252acb935a698efed2f blob - f2f5a3cbeb857a547e2c1bb1e3c13aba9479efa2 blob + 656685591bdfde81e5346507229c0bd87d2fa18c --- lib/fileindex.c +++ lib/fileindex.c @@ -1002,6 +1002,12 @@ diff_fileindex_dir(struct got_fileindex *fileindex, struct dirent *de = NULL; size_t path_len = strlen(path); struct got_pathlist_entry *dle; + + if (cb->diff_traverse) { + err = cb->diff_traverse(cb_arg, path, dirfd); + if (err) + return err; + } dle = TAILQ_FIRST(dirlist); while ((*ie && got_path_is_child((*ie)->path, path, path_len)) || dle) { blob - 88f07eccfa3c94fd5c1e4b81985cf5b7558c9ef2 blob + aff771b0551ee8d209acba24e8ce1cfe8383392d --- lib/got_lib_fileindex.h +++ lib/got_lib_fileindex.h @@ -144,10 +144,13 @@ typedef const struct got_error *(*got_fileindex_diff_d struct got_fileindex_entry *, const char *); typedef const struct got_error *(*got_fileindex_diff_dir_new_cb)(void *, struct dirent *, const char *, int); +typedef const struct got_error *(*got_fileindex_diff_dir_traverse)(void *, + const char *, int); struct got_fileindex_diff_dir_cb { got_fileindex_diff_dir_old_new_cb diff_old_new; got_fileindex_diff_dir_old_cb diff_old; got_fileindex_diff_dir_new_cb diff_new; + got_fileindex_diff_dir_traverse diff_traverse; }; const struct got_error *got_fileindex_diff_dir(struct got_fileindex *, int, const char *, const char *, struct got_repository *, blob - a0cbc48d4e120a8765536e6bee2ab03cd00d4adc blob + 82a6b4b7b9e073c4d26c6e63e14032229c1784e1 --- lib/worktree.c +++ lib/worktree.c @@ -2381,6 +2381,7 @@ struct diff_dir_cb_arg { /* A pathlist containing per-directory pathlists of ignore patterns. */ struct got_pathlist_head ignores; int report_unchanged; + int no_ignores; }; static const struct got_error * @@ -2684,23 +2685,8 @@ status_new(void *arg, struct dirent *de, const char *p path = de->d_name; } - if (de->d_type == DT_DIR) { - int subdirfd = openat(dirfd, de->d_name, - O_RDONLY | O_NOFOLLOW | O_DIRECTORY); - if (subdirfd == -1) { - if (errno != ENOENT && errno != EACCES) - err = got_error_from_errno2("openat", path); - } else { - err = add_ignores(&a->ignores, a->worktree->root_path, - path, subdirfd, ".cvsignore"); - if (err == NULL) - err = add_ignores(&a->ignores, - a->worktree->root_path, path, - subdirfd, ".gitignore"); - if (close(subdirfd) == -1 && err == NULL) - err = got_error_from_errno2("close", path); - } - } else if (got_path_is_child(path, a->status_path, a->status_path_len) + if (de->d_type != DT_DIR && + got_path_is_child(path, a->status_path, a->status_path_len) && !match_ignores(&a->ignores, path)) err = (*a->status_cb)(a->status_arg, GOT_STATUS_UNVERSIONED, GOT_STATUS_NO_CHANGE, path, NULL, NULL, NULL, -1, NULL); @@ -2710,6 +2696,26 @@ status_new(void *arg, struct dirent *de, const char *p } static const struct got_error * +status_traverse(void *arg, const char *path, int dirfd) +{ + const struct got_error *err = NULL; + struct diff_dir_cb_arg *a = arg; + + if (a->no_ignores) + return NULL; + + err = add_ignores(&a->ignores, a->worktree->root_path, + path, dirfd, ".cvsignore"); + if (err) + return err; + + err = add_ignores(&a->ignores, a->worktree->root_path, path, + dirfd, ".gitignore"); + + return err; +} + +static const struct got_error * report_single_file_status(const char *path, const char *ondisk_path, struct got_fileindex *fileindex, got_worktree_status_cb status_cb, void *status_arg, struct got_repository *repo, int report_unchanged) @@ -2735,6 +2741,50 @@ void *status_arg, struct got_repository *repo, int rep GOT_STATUS_NO_CHANGE, path, NULL, NULL, NULL, -1, NULL); return NULL; +} + +static const struct got_error * +add_ignores_from_parent_paths(struct got_pathlist_head *ignores, + const char *root_path, const char *path) +{ + const struct got_error *err; + char *parent_path, *next_parent_path; + + err = add_ignores(ignores, root_path, "", -1, + ".cvsignore"); + if (err) + return err; + + err = add_ignores(ignores, root_path, "", -1, + ".gitignore"); + if (err) + return err; + + err = got_path_dirname(&parent_path, path); + if (err) { + if (err->code == GOT_ERR_BAD_PATH) + return NULL; /* cannot traverse parent */ + return err; + } + for (;;) { + err = add_ignores(ignores, root_path, parent_path, -1, + ".cvsignore"); + if (err) + break; + err = add_ignores(ignores, root_path, parent_path, -1, + ".gitignore"); + if (err) + break; + err = got_path_dirname(&next_parent_path, parent_path); + if (err) { + if (err->code != GOT_ERR_BAD_PATH) + return err; + err = NULL; /* traversed everything */ + break; + } + } + + return err; } static const struct got_error * @@ -2749,6 +2799,8 @@ worktree_status(struct got_worktree *worktree, const c struct got_fileindex_diff_dir_cb fdiff_cb; struct diff_dir_cb_arg arg; char *ondisk_path = NULL; + + TAILQ_INIT(&arg.ignores); if (asprintf(&ondisk_path, "%s%s%s", worktree->root_path, path[0] ? "/" : "", path) == -1) @@ -2766,6 +2818,7 @@ worktree_status(struct got_worktree *worktree, const c fdiff_cb.diff_old_new = status_old_new; fdiff_cb.diff_old = status_old; fdiff_cb.diff_new = status_new; + fdiff_cb.diff_traverse = status_traverse; arg.fileindex = fileindex; arg.worktree = worktree; arg.status_path = path; @@ -2776,21 +2829,18 @@ worktree_status(struct got_worktree *worktree, const c arg.cancel_cb = cancel_cb; arg.cancel_arg = cancel_arg; arg.report_unchanged = report_unchanged; - TAILQ_INIT(&arg.ignores); + arg.no_ignores = no_ignores; if (!no_ignores) { - err = add_ignores(&arg.ignores, worktree->root_path, - path, fd, ".cvsignore"); - if (err == NULL) - err = add_ignores(&arg.ignores, - worktree->root_path, path, fd, - ".gitignore"); + err = add_ignores_from_parent_paths(&arg.ignores, + worktree->root_path, path); + if (err) + goto done; } - if (err == NULL) - err = got_fileindex_diff_dir(fileindex, fd, - worktree->root_path, path, repo, &fdiff_cb, &arg); - free_ignores(&arg.ignores); + err = got_fileindex_diff_dir(fileindex, fd, + worktree->root_path, path, repo, &fdiff_cb, &arg); } - +done: + free_ignores(&arg.ignores); if (fd != -1 && close(fd) != 0 && err == NULL) err = got_error_from_errno("close"); free(ondisk_path); blob - 5abbce9475bb8bd2f1cbb3e33113883173026f03 blob + bf27874f97a3459375523f37a2f63d9a83168936 --- regress/cmdline/status.sh +++ regress/cmdline/status.sh @@ -496,10 +496,13 @@ function test_status_cvsignore { echo "unversioned file" > $testroot/wt/foo echo "unversioned file" > $testroot/wt/foop + echo "unversioned file" > $testroot/wt/epsilon/foo echo "unversioned file" > $testroot/wt/epsilon/bar echo "unversioned file" > $testroot/wt/epsilon/boo echo "unversioned file" > $testroot/wt/epsilon/moo - echo "foo" > $testroot/wt/.cvsignore + mkdir -p $testroot/wt/epsilon/new/ + echo "unversioned file" > $testroot/wt/epsilon/new/foo + echo "**/foo" > $testroot/wt/.cvsignore echo "bar" > $testroot/wt/epsilon/.cvsignore echo "moo" >> $testroot/wt/epsilon/.cvsignore @@ -508,6 +511,18 @@ function test_status_cvsignore { echo '? epsilon/boo' >> $testroot/stdout.expected echo '? foop' >> $testroot/stdout.expected (cd $testroot/wt && got status > $testroot/stdout) + + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + echo '? epsilon/.cvsignore' > $testroot/stdout.expected + echo '? epsilon/boo' >> $testroot/stdout.expected + (cd $testroot/wt && got status epsilon > $testroot/stdout) cmp -s $testroot/stdout.expected $testroot/stdout ret="$?" @@ -516,7 +531,18 @@ function test_status_cvsignore { test_done "$testroot" "$ret" return 1 fi + + echo -n '' > $testroot/stdout.expected + (cd $testroot/wt && got status epsilon/new > $testroot/stdout) + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + echo '? .cvsignore' > $testroot/stdout.expected echo '? epsilon/.cvsignore' >> $testroot/stdout.expected echo '? epsilon/boo' >> $testroot/stdout.expected