Commit Diff


commit - 3f17dee4f66ddd410b6ab5a184e3a8c3608d3892
commit + 72ea6654e747de4879e06bd45e50ea7d9f5b7ed6
blob - cb00a11ad2f7c41e4c84673e981173ed0592308e
blob + d3e564d4699851132f9a64ad9d11d4e5f5bc7643
--- got/got.1
+++ got/got.1
@@ -212,7 +212,7 @@ branch will be used.
 .It Cm up
 Short alias for
 .Cm update .
-.It Cm status [ Ar path ]
+.It Cm status [ Ar path ... ]
 Show the current modification status of files in a work tree,
 using the following status codes:
 .Bl -column YXZ description
@@ -226,9 +226,10 @@ using the following status codes:
 .Nm
 .El
 .Pp
-If a
+If no
 .Ar path
-is specified, only show modifications within this path.
+is specified, show modifications in the entire work tree.
+Otherwise, show modifications at or within the specified paths.
 .It Cm st
 Short alias for
 .Cm status .
blob - 3e915e8dfa84371b3c80fdb1394bf157c98299e7
blob + 90f2c5558432c2c95b5ee07a924b9fb1bacb2c90
--- got/got.c
+++ got/got.c
@@ -1802,7 +1802,11 @@ cmd_diff(int argc, char *argv[])
 
 	if (argc <= 1) {
 		struct print_diff_arg arg;
+		struct got_pathlist_head paths;
 		char *id_str;
+
+		TAILQ_INIT(&paths);
+
 		error = got_object_id_str(&id_str,
 		    got_worktree_get_base_commit_id(worktree));
 		if (error)
@@ -1813,9 +1817,14 @@ cmd_diff(int argc, char *argv[])
 		arg.id_str = id_str;
 		arg.header_shown = 0;
 
-		error = got_worktree_status(worktree, path, repo, print_diff,
+		error = got_pathlist_append(NULL, &paths, path, NULL);
+		if (error)
+			goto done;
+
+		error = got_worktree_status(worktree, &paths, repo, print_diff,
 		    &arg, check_cancelled, NULL);
 		free(id_str);
+		got_pathlist_free(&paths);
 		goto done;
 	}
 
@@ -2291,7 +2300,7 @@ done:
 __dead static void
 usage_status(void)
 {
-	fprintf(stderr, "usage: %s status [path]\n", getprogname());
+	fprintf(stderr, "usage: %s status [path ...]\n", getprogname());
 	exit(1);
 }
 
@@ -2310,8 +2319,11 @@ cmd_status(int argc, char *argv[])
 	struct got_repository *repo = NULL;
 	struct got_worktree *worktree = NULL;
 	char *cwd = NULL, *path = NULL;
-	int ch;
+	struct got_pathlist_head paths;
+	int ch, i;
 
+	TAILQ_INIT(&paths);
+
 	while ((ch = getopt(argc, argv, "")) != -1) {
 		switch (ch) {
 		default:
@@ -2339,15 +2351,21 @@ cmd_status(int argc, char *argv[])
 		goto done;
 
 	if (argc == 0) {
-		path = strdup("");
-		if (path == NULL) {
-			error = got_error_from_errno("strdup");
-			goto done;
-		}
-	} else if (argc == 1) {
-		error = got_worktree_resolve_path(&path, worktree, argv[0]);
+		error = got_pathlist_append(NULL, &paths, "", NULL);
 		if (error)
 			goto done;
+	} else if (argc >= 1) {
+		for (i = 0; i < argc; i++) {
+			error = got_worktree_resolve_path(&path, worktree,
+			    argv[i]);
+			if (error)
+				goto done;
+			error = got_pathlist_append(NULL, &paths, path, NULL);
+			if (error) {
+				free(path);
+				goto done;
+			}
+		}
 	} else
 		usage_status();
 
@@ -2360,9 +2378,10 @@ cmd_status(int argc, char *argv[])
 	if (error)
 		goto done;
 
-	error = got_worktree_status(worktree, path, repo, print_status, NULL,
+	error = got_worktree_status(worktree, &paths, repo, print_status, NULL,
 	    check_cancelled, NULL);
 done:
+	got_pathlist_free(&paths);
 	free(cwd);
 	free(path);
 	return error;
blob - 588e227a26ee5dff0066f6f1dd4dbf7581b3f5ec
blob + c5070b49c1c0ade4b575d6fa753b6b239b460635
--- include/got_path.h
+++ include/got_path.h
@@ -78,6 +78,17 @@ TAILQ_HEAD(got_pathlist_head, got_pathlist_entry);
 const struct got_error *got_pathlist_insert(struct got_pathlist_entry **,
     struct got_pathlist_head *, const char *, void *);
 
+/*
+ * Append a path to the list of paths.
+ * The caller should already have initialized the list head. This list stores
+ * the pointer to the path as-is, i.e. the path is not copied internally and
+ * must remain available until the list is freed with got_pathlist_free().
+ * If the first argument is not NULL, set it to a pointer to the newly inserted
+ * element, or to a NULL pointer in case the path was already on the list.
+ */
+const struct got_error *got_pathlist_append(struct got_pathlist_entry **,
+    struct got_pathlist_head *, const char *, void *);
+
 /* Free resources allocated for a path list. */
 void got_pathlist_free(struct got_pathlist_head *);
 
blob - e9f5a6c90d4b5185ce58d45c93a4c57a850306a3
blob + e98f4eefa2ca47670e414b5ece91a68d435e66d0
--- include/got_worktree.h
+++ include/got_worktree.h
@@ -148,8 +148,8 @@ typedef const struct got_error *(*got_worktree_status_
  * a path, and a corresponding status code.
  */
 const struct got_error *got_worktree_status(struct got_worktree *,
-    const char *, struct got_repository *, got_worktree_status_cb, void *,
-    got_worktree_cancel_cb cancel_cb, void *);
+    struct got_pathlist_head *, struct got_repository *,
+    got_worktree_status_cb, void *, got_worktree_cancel_cb cancel_cb, void *);
 
 /*
  * Try to resolve a user-provided path to an on-disk path in the work tree.
blob - 8d2ef334e7a05632a8884779d1d12b8dbf60bdc0
blob + f3e258fea9747229b54c77f3ec2be3c804b0f38c
--- lib/path.c
+++ lib/path.c
@@ -252,7 +252,24 @@ got_pathlist_insert(struct got_pathlist_entry **insert
 		*inserted = new;
 	return NULL;
 }
+
+const struct got_error *
+got_pathlist_append(struct got_pathlist_entry **pe,
+    struct got_pathlist_head *pathlist, const char *path, void *data)
+{
+	struct got_pathlist_entry *new;
 
+	new = malloc(sizeof(*new));
+	if (new == NULL)
+		return got_error_from_errno("malloc");
+	new->path = path;
+	new->data = data;
+	TAILQ_INSERT_TAIL(pathlist, new, entry);
+	if (pe)
+		*pe = new;
+	return NULL;
+}
+
 void
 got_pathlist_free(struct got_pathlist_head *pathlist)
 {
blob - c15d384d2c146fcd939be25190f8eb596b845ca2
blob + 4101d79ebd4fde3380f09ac29feed253aa25c1ec
--- lib/worktree.c
+++ lib/worktree.c
@@ -2265,10 +2265,14 @@ worktree_status(struct got_worktree *worktree, const c
 		if (errno == ENOTDIR || errno == ENOENT) {
 			struct got_fileindex_entry *ie;
 			ie = got_fileindex_entry_get(fileindex, path);
-			if (ie == NULL) {
-				err = got_error(GOT_ERR_BAD_PATH);
+			if (ie == NULL)
+				err = (*status_cb)(status_arg,
+				    GOT_STATUS_UNVERSIONED, path, NULL, NULL);
+			else
+				err = report_file_status(ie, ondisk_path,
+				    status_cb, status_arg, repo);
+			if (err)
 				goto done;
-			}
 			err = report_file_status(ie, ondisk_path,
 			    status_cb, status_arg, repo);
 			goto done;
@@ -2299,20 +2303,26 @@ done:
 }
 
 const struct got_error *
-got_worktree_status(struct got_worktree *worktree, const char *path,
-    struct got_repository *repo, got_worktree_status_cb status_cb,
-    void *status_arg, got_worktree_cancel_cb cancel_cb, void *cancel_arg)
+got_worktree_status(struct got_worktree *worktree,
+    struct got_pathlist_head *paths, struct got_repository *repo,
+    got_worktree_status_cb status_cb, void *status_arg,
+    got_worktree_cancel_cb cancel_cb, void *cancel_arg)
 {
 	const struct got_error *err = NULL;
 	char *fileindex_path = NULL;
 	struct got_fileindex *fileindex = NULL;
+	struct got_pathlist_entry *pe;
 
 	err = open_fileindex(&fileindex, &fileindex_path, worktree);
 	if (err)
 		return err;
 
-	err = worktree_status(worktree, path, fileindex, repo,
-	    status_cb, status_arg, cancel_cb, cancel_arg);
+	TAILQ_FOREACH(pe, paths, entry) {
+		err = worktree_status(worktree, pe->path, fileindex, repo,
+			status_cb, status_arg, cancel_cb, cancel_arg);
+		if (err)
+			break;
+	}
 	free(fileindex_path);
 	got_fileindex_free(fileindex);
 	return err;
blob - caf49bac02e4804db8079107c17f7b157837f688
blob + b77e1d3aed4da758cdd72648e410e4a105ebac25
--- regress/cmdline/status.sh
+++ regress/cmdline/status.sh
@@ -441,7 +441,45 @@ function test_status_empty_dir_unversioned_file {
 	fi
 	test_done "$testroot" "$ret"
 }
+
+function test_status_many_paths {
+	local testroot=`test_init status_many_paths`
 
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	echo "modified alpha" > $testroot/wt/alpha
+	(cd $testroot/wt && got rm beta >/dev/null)
+	echo "unversioned file" > $testroot/wt/foo
+	rm $testroot/wt/epsilon/zeta
+	touch $testroot/wt/beta
+	echo "new file" > $testroot/wt/new
+	mkdir $testroot/wt/newdir
+	(cd $testroot/wt && got add new >/dev/null)
+
+	(cd $testroot/wt && got status newdir > $testroot/stdout.expected)
+	(cd $testroot/wt && got status alpha >> $testroot/stdout.expected)
+	(cd $testroot/wt && got status epsilon >> $testroot/stdout.expected)
+	(cd $testroot/wt && got status foo >> $testroot/stdout.expected)
+	(cd $testroot/wt && got status new >> $testroot/stdout.expected)
+	(cd $testroot/wt && got status beta >> $testroot/stdout.expected)
+	(cd $testroot/wt && got status . >> $testroot/stdout.expected)
+
+	(cd $testroot/wt && got status newdir alpha epsilon foo new beta . \
+		> $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"
+}
+
 run_test test_status_basic
 run_test test_status_subdir_no_mods
 run_test test_status_subdir_no_mods2
@@ -453,3 +491,4 @@ run_test test_status_shows_no_mods_after_complete_merg
 run_test test_status_shows_conflict
 run_test test_status_empty_dir
 run_test test_status_empty_dir_unversioned_file
+run_test test_status_many_paths