commit 927df6b74a3358d1eaa0f2482cac2204a4f8f9fa from: Stefan Sperling date: Sun Feb 10 17:42:29 2019 UTC allow restricting 'got diff' and 'got status' to a path in work tree commit - 13ff9e901c193017bf37229e261588b7bdbfe446 commit + 927df6b74a3358d1eaa0f2482cac2204a4f8f9fa blob - 8a6407f100352d662d8260e1d3fcb69ddadc5a8f blob + 5d11ec962e2aa555e428742810acdc50377403a6 --- got/got.1 +++ got/got.1 @@ -122,7 +122,7 @@ Update the work tree to the specified .Ar commit . The expected argument is a SHA1 hash which corresponds to a commit object. .El -.It Cm status [ Ar worktree-path ] +.It Cm status [ Ar path ] Show the current modification status of files in a worktree, using the following status codes: .Bl -column YXZ description @@ -133,9 +133,9 @@ using the following status codes: .Nm .El .Pp -If the -.Ar work tree path -is omitted, use the current working directory. +If a +.Ar path +is specified, only show modifications within this path. .It Cm log [ Fl c Ar commit ] [ Fl C Ar number ] [ Fl f ] [ Fl l Ar N ] [ Fl p ] [ Fl r Ar repository-path ] [ path ] Display history of a repository. If a @@ -172,12 +172,16 @@ If this directory is a .Nm work tree, use the repository path associated with this work tree. .El -.It Cm diff [ Fl C Ar number ] [ Fl r Ar repository-path ] [ Ar object1 Ar object2 ] -Display differences between blobs in the repository and files in the -work tree, or between two specified objects in a repository. -If specified, each -.Ar object -argument is a SHA1 hash which corresponds to the object. +.It Cm diff [ Fl C Ar number ] [ Fl r Ar repository-path ] [ Ar object1 Ar object2 | Ar path ] +When invoked within a work tree with less than two arguments, display +uncommitted changes in the work tree. +If a +.Ar path +is specified, only show changes within this path. +.Pp +If two arguments are provided, treat each argument as a SHA1 hash which +corresponds to an object in the repository, and display differences +between these objects. Both objects must be of the same type (blobs, trees, or commits). .Pp The options for blob - 38251537f753faed2f94818ddb4bd42c5780eb6d blob + ca784f2d06a5b67919ef72127efe1ce8215efb01 --- got/got.c +++ got/got.c @@ -942,7 +942,7 @@ __dead static void usage_diff(void) { fprintf(stderr, "usage: %s diff [-C number] [-r repository-path] " - "[object1 object2]\n", getprogname()); + "[object1 object2 | path]\n", getprogname()); exit(1); } @@ -1002,6 +1002,47 @@ done: if (f2) fclose(f2); free(abspath); + return err; +} + +static const struct got_error * +get_status_path(char **status_path, struct got_worktree *worktree, + const char *arg) +{ + const struct got_error *err = NULL; + char *resolved, *path = NULL; + size_t len; + + *status_path = NULL; + + resolved = realpath(arg, NULL); + if (resolved == NULL) + return got_error_from_errno(); + + if (strncmp(got_worktree_get_root_path(worktree), resolved, + strlen(got_worktree_get_root_path(worktree)))) { + err = got_error(GOT_ERR_BAD_PATH); + goto done; + } + + path = strdup(resolved + strlen(got_worktree_get_root_path(worktree))); + if (path == NULL) { + err = got_error_from_errno(); + goto done; + } + + /* XXX status walk can't deal with trailing slash! */ + len = strlen(path); + while (path[len - 1] == '/') { + path[len - 1] = '\0'; + len--; + } +done: + free(resolved); + if (err == NULL) + *status_path = path; + else + free(path); return err; } @@ -1017,6 +1058,7 @@ cmd_diff(int argc, char *argv[]) int type1, type2; int diff_context = 3, ch; const char *errstr; + char *path = NULL; #ifndef PROFILE if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil", @@ -1050,16 +1092,33 @@ cmd_diff(int argc, char *argv[]) error = got_error_from_errno(); goto done; } - if (argc == 0) { + error = got_worktree_open(&worktree, cwd); + if (error && error->code != GOT_ERR_NOT_WORKTREE) + goto done; + if (argc <= 1) { + if (worktree == NULL) { + error = got_error(GOT_ERR_NOT_WORKTREE); + goto done; + } if (repo_path) errx(1, "-r option can't be used when diffing a work tree"); - error = got_worktree_open(&worktree, cwd); - if (error) - goto done; repo_path = strdup(got_worktree_get_repo_path(worktree)); - if (repo_path == NULL) - return got_error_from_errno(); + if (repo_path == NULL) { + error = got_error_from_errno(); + goto done; + } + if (argc == 1) { + error = get_status_path(&path, worktree, argv[0]); + if (error) + goto done; + } else { + path = strdup(""); + if (path == NULL) { + error = got_error_from_errno(); + goto done; + } + } } else if (argc == 2) { id_str1 = argv[0]; id_str2 = argv[1]; @@ -1095,7 +1154,7 @@ cmd_diff(int argc, char *argv[]) arg.id_str = id_str; arg.header_shown = 0; - error = got_worktree_status(worktree, repo, print_diff, + error = got_worktree_status(worktree, path, repo, print_diff, &arg, check_cancelled, NULL); free(id_str); goto done; @@ -1144,6 +1203,7 @@ cmd_diff(int argc, char *argv[]) done: free(id1); free(id2); + free(path); if (worktree) got_worktree_close(worktree); if (repo) { @@ -1527,7 +1587,7 @@ done: __dead static void usage_status(void) { - fprintf(stderr, "usage: %s status [worktree-path]\n", getprogname()); + fprintf(stderr, "usage: %s status [path]\n", getprogname()); exit(1); } @@ -1545,7 +1605,7 @@ cmd_status(int argc, char *argv[]) const struct got_error *error = NULL; struct got_repository *repo = NULL; struct got_worktree *worktree = NULL; - char *worktree_path = NULL; + char *cwd = NULL, *path = NULL; int ch; while ((ch = getopt(argc, argv, "")) != -1) { @@ -1564,25 +1624,29 @@ cmd_status(int argc, char *argv[]) NULL) == -1) err(1, "pledge"); #endif + cwd = getcwd(NULL, 0); + if (cwd == NULL) { + error = got_error_from_errno(); + goto done; + } + + error = got_worktree_open(&worktree, cwd); + if (error != NULL) + goto done; + if (argc == 0) { - worktree_path = getcwd(NULL, 0); - if (worktree_path == NULL) { + path = strdup(""); + if (path == NULL) { error = got_error_from_errno(); goto done; } } else if (argc == 1) { - worktree_path = realpath(argv[0], NULL); - if (worktree_path == NULL) { - error = got_error_from_errno(); + error = get_status_path(&path, worktree, argv[0]); + if (error) goto done; - } } else usage_status(); - error = got_worktree_open(&worktree, worktree_path); - if (error != NULL) - goto done; - error = got_repo_open(&repo, got_worktree_get_repo_path(worktree)); if (error != NULL) goto done; @@ -1592,9 +1656,10 @@ cmd_status(int argc, char *argv[]) if (error) goto done; - error = got_worktree_status(worktree, repo, print_status, NULL, + error = got_worktree_status(worktree, path, repo, print_status, NULL, check_cancelled, NULL); done: - free(worktree_path); + free(cwd); + free(path); return error; } blob - c28e21835670de5ae86292d65d24e0bf7139fada blob + 3961b6d7873af2a2c046fb30c1497f452d1136f5 --- include/got_worktree.h +++ include/got_worktree.h @@ -122,5 +122,6 @@ 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 *, struct got_repository *, - got_worktree_status_cb, void *, got_worktree_cancel_cb cancel_cb, void *); +got_worktree_status(struct got_worktree *, const char *, + struct got_repository *, got_worktree_status_cb, void *, + got_worktree_cancel_cb cancel_cb, void *); blob - 5d501fc0867dd9283c8d954134123191b1548362 blob + 9513f3056f3375a16cb9ccca9ebf8a3dfe294dff --- lib/fileindex.c +++ lib/fileindex.c @@ -844,12 +844,12 @@ done: const struct got_error * got_fileindex_diff_dir(struct got_fileindex *fileindex, DIR *rootdir, - const char *rootpath, struct got_repository *repo, + 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, "", + return diff_fileindex_dir(fileindex, &min, rootdir, rootpath, path, repo, cb, cb_arg); } blob - bb4a77afda033364d37dcdc5dd281795429bad65 blob + aa91c6643eb06a9a52b12801b94593ca38102c5b --- lib/got_lib_fileindex.h +++ lib/got_lib_fileindex.h @@ -141,5 +141,5 @@ struct got_fileindex_diff_dir_cb { got_fileindex_diff_dir_new_cb diff_new; }; const struct got_error *got_fileindex_diff_dir(struct got_fileindex *, DIR *, - const char *, struct got_repository *, struct got_fileindex_diff_dir_cb *, - void *); + const char *, const char *, struct got_repository *, + struct got_fileindex_diff_dir_cb *, void *); blob - 9a55298bbc84d3449a19d3cb40e0b87307690de7 blob + 949678e5c0659175b5f4de710c0d32db3121fbc7 --- lib/worktree.c +++ lib/worktree.c @@ -1155,6 +1155,8 @@ done: struct diff_dir_cb_arg { struct got_fileindex *fileindex; struct got_worktree *worktree; + const char *status_path; + size_t status_path_len; struct got_repository *repo; got_worktree_status_cb status_cb; void *status_arg; @@ -1163,15 +1165,35 @@ struct diff_dir_cb_arg { }; static const struct got_error * +report_file_status(struct got_fileindex_entry *ie, const char *abspath, + got_worktree_status_cb status_cb, void *status_arg, + struct got_repository *repo) +{ + const struct got_error *err = NULL; + unsigned char status = GOT_STATUS_NO_CHANGE; + struct stat sb; + struct got_object_id id; + + err = get_file_status(&status, &sb, ie, abspath, repo); + if (err == NULL && status != GOT_STATUS_NO_CHANGE) { + memcpy(id.sha1, ie->blob_sha1, SHA1_DIGEST_LENGTH); + err = (*status_cb)(status_arg, status, ie->path, &id); + } + return err; +} + +static const struct got_error * status_old_new(void *arg, struct got_fileindex_entry *ie, struct dirent *de, const char *parent_path) { const struct got_error *err = NULL; struct diff_dir_cb_arg *a = arg; char *abspath; - unsigned char status = GOT_STATUS_NO_CHANGE; - struct stat sb; + if (got_path_cmp(parent_path, a->status_path) != 0 && + !got_path_is_child(parent_path, a->status_path, a->status_path_len)) + return NULL; + if (parent_path[0]) { if (asprintf(&abspath, "%s/%s/%s", a->worktree->root_path, parent_path, de->d_name) == -1) @@ -1182,12 +1204,8 @@ status_old_new(void *arg, struct got_fileindex_entry * return got_error_from_errno(); } - err = get_file_status(&status, &sb, ie, abspath, a->repo); - if (err == NULL && status != GOT_STATUS_NO_CHANGE) { - struct got_object_id id; - memcpy(id.sha1, ie->blob_sha1, SHA1_DIGEST_LENGTH); - err = (*a->status_cb)(a->status_arg, status, ie->path, &id); - } + err = report_file_status(ie, abspath, a->status_cb, a->status_arg, + a->repo); free(abspath); return err; } @@ -1197,6 +1215,10 @@ status_old(void *arg, struct got_fileindex_entry *ie, { struct diff_dir_cb_arg *a = arg; struct got_object_id id; + + if (!got_path_is_child(parent_path, a->status_path, a->status_path_len)) + return NULL; + memcpy(id.sha1, ie->blob_sha1, SHA1_DIGEST_LENGTH); return (*a->status_cb)(a->status_arg, GOT_STATUS_MISSING, ie->path, &id); @@ -1216,6 +1238,9 @@ status_new(void *arg, struct dirent *de, const char *p 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]) { if (asprintf(&path, "%s/%s", parent_path, de->d_name) == -1) return got_error_from_errno(); @@ -1231,7 +1256,7 @@ status_new(void *arg, struct dirent *de, const char *p } const struct got_error * -got_worktree_status(struct got_worktree *worktree, +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) { @@ -1242,6 +1267,7 @@ got_worktree_status(struct got_worktree *worktree, FILE *index = NULL; struct got_fileindex_diff_dir_cb fdiff_cb; struct diff_dir_cb_arg arg; + char *ondisk_path = NULL; fileindex = got_fileindex_alloc(); if (fileindex == NULL) { @@ -1269,26 +1295,46 @@ got_worktree_status(struct got_worktree *worktree, goto done; } - workdir = opendir(worktree->root_path); - if (workdir == NULL) { + if (asprintf(&ondisk_path, "%s%s%s", + worktree->root_path, path[0] ? "/" : "", path) == -1) { err = got_error_from_errno(); goto done; } + workdir = opendir(ondisk_path); + if (workdir == NULL) { + if (errno == ENOTDIR) { + struct got_fileindex_entry *ie; + ie = got_fileindex_entry_get(fileindex, path); + if (ie == NULL) { + err = got_error(GOT_ERR_BAD_PATH); + goto done; + } + err = report_file_status(ie, ondisk_path, + status_cb, status_arg, repo); + goto done; + } else { + err = got_error_from_errno(); + goto done; + } + } fdiff_cb.diff_old_new = status_old_new; fdiff_cb.diff_old = status_old; fdiff_cb.diff_new = status_new; arg.fileindex = fileindex; arg.worktree = worktree; + arg.status_path = path; + arg.status_path_len = strlen(path); arg.repo = repo; arg.status_cb = status_cb; arg.status_arg = status_arg; arg.cancel_cb = cancel_cb; arg.cancel_arg = cancel_arg; err = got_fileindex_diff_dir(fileindex, workdir, worktree->root_path, - repo, &fdiff_cb, &arg); + path, repo, &fdiff_cb, &arg); done: if (workdir) closedir(workdir); + free(ondisk_path); free(fileindex_path); got_fileindex_free(fileindex); return err;