Commit Diff


commit - a062651c55aa60f0633437e9851dd80b1d713e86
commit + c577a9cecea0c94f30a1e650e6e332fb8b43b9f6
blob - 8ff467458d80b3a0f400b0b4b668affadbc2a12a
blob + 0e62e71ac79879c8427c88f483ad9505a3d27f18
--- lib/fileindex.c
+++ lib/fileindex.c
@@ -759,9 +759,67 @@ got_fileindex_diff_tree(struct got_fileindex *fileinde
 
 static const struct got_error *
 diff_fileindex_dir(struct got_fileindex *, struct got_fileindex_entry **, DIR *,
-    const char *, const char *, struct got_repository *,
-    struct got_fileindex_diff_dir_cb *, void *);
+    struct got_pathlist_head *, const char *, const char *,
+    struct got_repository *, struct got_fileindex_diff_dir_cb *, void *);
+
+static const struct got_error *
+read_dirlist(struct got_pathlist_head *dirlist, DIR *dir, const char *path)
+{
+	const struct got_error *err = NULL;
+	struct got_pathlist_entry *new = NULL;
+	struct dirent *dep = NULL;
+	struct dirent *de = NULL;
+
+	for (;;) {
+		de = malloc(sizeof(struct dirent) + NAME_MAX + 1);
+		if (de == NULL) {
+			err = got_error_from_errno("malloc");
+			break;
+		}
+
+		if (readdir_r(dir, de, &dep) != 0) {
+			err = got_error_from_errno("readdir_r");
+			free(de);
+			break;
+		}
+		if (dep == NULL) {
+			free(de);
+			break;
+		}
+
+		if (strcmp(de->d_name, ".") == 0 ||
+		    strcmp(de->d_name, "..") == 0 ||
+		    (path[0] == '\0' &&
+		    strcmp(de->d_name, GOT_WORKTREE_GOT_DIR) == 0)) {
+			free(de);
+			continue;
+		}
+
+		err = got_pathlist_insert(&new, dirlist, de->d_name, de);
+		if (err) {
+			free(de);
+			break;
+		}
+		if (new == NULL) {
+			err = got_error(GOT_ERR_DIR_DUP_ENTRY);
+			free(de);
+			break;
+		}
+	}
 
+	return err;
+}
+
+void
+free_dirlist(struct got_pathlist_head *dirlist)
+{
+	struct got_pathlist_entry *dle;
+
+	TAILQ_FOREACH(dle, dirlist, entry)
+		free(dle->data);
+	got_pathlist_free(dirlist);
+}
+
 static const struct got_error *
 walk_dir(struct got_pathlist_entry **next, struct got_fileindex *fileindex,
     struct got_fileindex_entry **ie, struct got_pathlist_entry *dle,
@@ -778,7 +836,10 @@ walk_dir(struct got_pathlist_entry **next, struct got_
 		char *subpath;
 		char *subdirpath;
 		DIR *subdir;
+		struct got_pathlist_head subdirlist;
 
+		TAILQ_INIT(&subdirlist);
+
 		if (asprintf(&subpath, "%s%s%s", path,
 		    path[0] == '\0' ? "" : "/", de->d_name) == -1)
 			return got_error_from_errno("asprintf");
@@ -795,11 +856,19 @@ walk_dir(struct got_pathlist_entry **next, struct got_
 			return got_error_from_errno2("opendir", subdirpath);
 		}
 
-		err = diff_fileindex_dir(fileindex, ie, subdir, rootpath,
-		    subpath, repo, cb, cb_arg);
+		err = read_dirlist(&subdirlist, subdir, subdirpath);
+		if (err) {
+			free(subpath);
+			free(subdirpath);
+			closedir(subdir);
+			return err;
+		}
+		err = diff_fileindex_dir(fileindex, ie, subdir, &subdirlist,
+		    rootpath, subpath, repo, cb, cb_arg);
 		free(subpath);
 		free(subdirpath);
 		closedir(subdir);
+		free_dirlist(&subdirlist);
 		if (err)
 			return err;
 	}
@@ -810,60 +879,17 @@ walk_dir(struct got_pathlist_entry **next, struct got_
 
 static const struct got_error *
 diff_fileindex_dir(struct got_fileindex *fileindex,
-    struct got_fileindex_entry **ie, DIR *dir, const char *rootpath,
-    const char *path, struct got_repository *repo,
-    struct got_fileindex_diff_dir_cb *cb, void *cb_arg)
+    struct got_fileindex_entry **ie, DIR *dir,
+    struct got_pathlist_head *dirlist, const char *rootpath, const char *path,
+    struct got_repository *repo, struct got_fileindex_diff_dir_cb *cb,
+    void *cb_arg)
 {
 	const struct got_error *err = NULL;
 	struct dirent *de = NULL;
 	size_t path_len = strlen(path);
-	struct got_fileindex_entry *next;
-	struct got_pathlist_head dirlist;
 	struct got_pathlist_entry *dle;
 
-	TAILQ_INIT(&dirlist);
-
-	for (;;) {
-		struct got_pathlist_entry *new = NULL;
-		struct dirent *dep = NULL;
-
-		de = malloc(sizeof(struct dirent) + NAME_MAX + 1);
-		if (de == NULL) {
-			err = got_error_from_errno("malloc");
-			goto done;
-		}
-
-		if (readdir_r(dir, de, &dep) != 0) {
-			err = got_error_from_errno("readdir_r");
-			free(de);
-			goto done;
-		}
-		if (dep == NULL) {
-			free(de);
-			break;
-		}
-
-		if (strcmp(de->d_name, ".") == 0 ||
-		    strcmp(de->d_name, "..") == 0 ||
-		    (path[0] == '\0' &&
-		    strcmp(de->d_name, GOT_WORKTREE_GOT_DIR) == 0)) {
-			free(de);
-			continue;
-		}
-
-		err = got_pathlist_insert(&new, &dirlist, de->d_name, de);
-		if (err) {
-			free(de);
-			goto done;
-		}
-		if (new == NULL) {
-			err = got_error(GOT_ERR_DIR_DUP_ENTRY);
-			free(de);
-			goto done;
-		}
-	}
-
-	dle = TAILQ_FIRST(&dirlist);
+	dle = TAILQ_FIRST(dirlist);
 	while ((*ie && got_path_is_child((*ie)->path, path, path_len)) || dle) {
 		if (dle && *ie) {
 			char *de_path;
@@ -884,11 +910,10 @@ diff_fileindex_dir(struct got_fileindex *fileindex,
 				err = walk_dir(&dle, fileindex, ie, dle, path,
 				    dir, rootpath, repo, cb, cb_arg);
 			} else if (cmp < 0 ) {
-				next = walk_fileindex(fileindex, *ie);
 				err = cb->diff_old(cb_arg, *ie, path);
 				if (err)
 					break;
-				*ie = next;
+				*ie = walk_fileindex(fileindex, *ie);
 			} else {
 				err = cb->diff_new(cb_arg, de, path);
 				if (err)
@@ -899,11 +924,10 @@ diff_fileindex_dir(struct got_fileindex *fileindex,
 			if (err)
 				break;
 		} else if (*ie) {
-			next = walk_fileindex(fileindex, *ie);
 			err = cb->diff_old(cb_arg, *ie, path);
 			if (err)
 				break;
-			*ie = next;
+			*ie = walk_fileindex(fileindex, *ie);
 		} else if (dle) {
 			de = dle->data;
 			err = cb->diff_new(cb_arg, de, path);
@@ -915,10 +939,7 @@ diff_fileindex_dir(struct got_fileindex *fileindex,
 				break;
 		}
 	}
-done:
-	TAILQ_FOREACH(dle, &dirlist, entry)
-		free(dle->data);
-	got_pathlist_free(&dirlist);
+
 	return err;
 }
 
@@ -927,10 +948,21 @@ got_fileindex_diff_dir(struct got_fileindex *fileindex
     const char *rootpath, const char *path, struct got_repository *repo,
     struct got_fileindex_diff_dir_cb *cb, void *cb_arg)
 {
-	struct got_fileindex_entry *min;
-	min = RB_MIN(got_fileindex_tree, &fileindex->entries);
-	return diff_fileindex_dir(fileindex, &min, rootdir, rootpath, path,
-	    repo, cb, cb_arg);
+	const struct got_error *err;
+	struct got_fileindex_entry *ie;
+	struct got_pathlist_head dirlist;
+
+	TAILQ_INIT(&dirlist);
+	err = read_dirlist(&dirlist, rootdir, path);
+	if (err)
+		return err;
+	ie = RB_MIN(got_fileindex_tree, &fileindex->entries);
+	while (ie && !got_path_is_child(ie->path, path, strlen(path)))
+		ie = walk_fileindex(fileindex, ie);
+	err = diff_fileindex_dir(fileindex, &ie, rootdir, &dirlist, rootpath,
+	    path, repo, cb, cb_arg);
+	free_dirlist(&dirlist);
+	return err;
 }
 
 RB_GENERATE(got_fileindex_tree, got_fileindex_entry, entry, got_fileindex_cmp);
blob - 80edb4d443d90485004348c8ef3b92fa6d592f6e
blob + 686a315559b212e3c9a0e54239e92de274a498b4
--- lib/worktree.c
+++ lib/worktree.c
@@ -2198,7 +2198,7 @@ status_old(void *arg, struct got_fileindex_entry *ie, 
 	if (a->cancel_cb && a->cancel_cb(a->cancel_arg))
 		return got_error(GOT_ERR_CANCELLED);
 
-	if (!got_path_is_child(parent_path, a->status_path, a->status_path_len))
+	if (!got_path_is_child(ie->path, a->status_path, a->status_path_len))
 		return NULL;
 
 	memcpy(blob_id.sha1, ie->blob_sha1, SHA1_DIGEST_LENGTH);
@@ -2226,9 +2226,6 @@ status_new(void *arg, struct dirent *de, const char *p
 
 	/* XXX ignore symlinks for now */
 	if (de->d_type == DT_LNK)
-		return NULL;
-
-	if (!got_path_is_child(parent_path, a->status_path, a->status_path_len))
 		return NULL;
 
 	if (parent_path[0]) {
@@ -2238,8 +2235,9 @@ status_new(void *arg, struct dirent *de, const char *p
 		path = de->d_name;
 	}
 
-	err = (*a->status_cb)(a->status_arg, GOT_STATUS_UNVERSIONED, path,
-	    NULL, NULL);
+	if (got_path_is_child(path, a->status_path, a->status_path_len))
+		err = (*a->status_cb)(a->status_arg, GOT_STATUS_UNVERSIONED,
+		    path, NULL, NULL);
 	if (parent_path[0])
 		free(path);
 	return err;
blob - fa78fd826325bfbc31e69d96bf74d4329add0b6b
blob + caf49bac02e4804db8079107c17f7b157837f688
--- regress/cmdline/status.sh
+++ regress/cmdline/status.sh
@@ -383,7 +383,57 @@ function test_status_shows_conflict {
 	echo 'C  numbers' > $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
+	fi
+	test_done "$testroot" "$ret"
+}
+
+function test_status_empty_dir {
+	local testroot=`test_init status_empty_dir`
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	rm $testroot/wt/epsilon/zeta
+
+	echo '!  epsilon/zeta' > $testroot/stdout.expected
+
+	(cd $testroot/wt && got status epsilon > $testroot/stdout)
+
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+	fi
+	test_done "$testroot" "$ret"
+}
 
+function test_status_empty_dir_unversioned_file {
+	local testroot=`test_init status_empty_dir_unversioned_file`
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	rm $testroot/wt/epsilon/zeta
+	touch $testroot/wt/epsilon/unversioned
+
+	echo '?  epsilon/unversioned' > $testroot/stdout.expected
+	echo '!  epsilon/zeta' >> $testroot/stdout.expected
+
+	(cd $testroot/wt && got status epsilon > $testroot/stdout)
+
 	cmp -s $testroot/stdout.expected $testroot/stdout
 	ret="$?"
 	if [ "$ret" != "0" ]; then
@@ -401,3 +451,5 @@ run_test test_status_unversioned_subdirs
 run_test test_status_ignores_symlink
 run_test test_status_shows_no_mods_after_complete_merge
 run_test test_status_shows_conflict
+run_test test_status_empty_dir
+run_test test_status_empty_dir_unversioned_file