commit 5c1e53bc728ac0524d0471886bbc19f0b95e6c55 from: Stefan Sperling date: Sun Jul 28 09:34:22 2019 UTC add support for multiple path arguments to 'got commit' commit - 3c575567b5033b0e9595bc208a5ce0b3f65627b6 commit + 5c1e53bc728ac0524d0471886bbc19f0b95e6c55 blob - b137227fb4832d96bf7b2366e8798731d9f68879 blob + 035a4e65253b48d82854f14665a3b2406de2bac6 --- got/got.1 +++ got/got.1 @@ -476,12 +476,13 @@ it will be restored. .It Cm rv Short alias for .Cm revert . -.It Cm commit [ Fl m Ar message ] [ path ] +.It Cm commit [ Fl m Ar message ] [ path ... ] Create a new commit in the repository from local changes in a work tree and use this commit as the new base commit for the work tree. -If a +If no .Ar path -is specified, only commit local changes at or within this path. +is specified, commit all local changes in the work tree. +Otherwise, commit local changes at or within the specified paths. .Pp Show the status of each affected file, using the following status codes: .Bl -column YXZ description blob - 2f130ca8f5f07aa7e457fc8cffa91f54ab350e37 blob + 41f97f73c98b213f120d4ad393a9b1a6316bec7a --- got/got.c +++ got/got.c @@ -3139,7 +3139,8 @@ done: __dead static void usage_commit(void) { - fprintf(stderr, "usage: %s commit [-m msg] [path]\n", getprogname()); + fprintf(stderr, "usage: %s commit [-m msg] [path ...]\n", + getprogname()); exit(1); } @@ -3221,13 +3222,16 @@ cmd_commit(int argc, char *argv[]) const struct got_error *error = NULL; struct got_worktree *worktree = NULL; struct got_repository *repo = NULL; - char *cwd = NULL, *path = NULL, *id_str = NULL; + char *cwd = NULL, *id_str = NULL; struct got_object_id *id = NULL; const char *logmsg = NULL; const char *got_author = getenv("GOT_AUTHOR"); struct collect_commit_logmsg_arg cl_arg; char *editor = NULL; int ch, rebase_in_progress; + struct got_pathlist_head paths; + + TAILQ_INIT(&paths); while ((ch = getopt(argc, argv, "m:")) != -1) { switch (ch) { @@ -3248,16 +3252,6 @@ cmd_commit(int argc, char *argv[]) "unveil", NULL) == -1) err(1, "pledge"); #endif - if (argc == 1) { - path = realpath(argv[0], NULL); - if (path == NULL) { - error = got_error_from_errno2("realpath", argv[0]); - goto done; - } - got_path_strip_trailing_slashes(path); - } else if (argc != 0) - usage_commit(); - if (got_author == NULL) { /* TODO: Look current user up in password database */ error = got_error(GOT_ERR_COMMIT_NO_AUTHOR); @@ -3280,6 +3274,10 @@ cmd_commit(int argc, char *argv[]) error = got_error(GOT_ERR_REBASING); goto done; } + + error = get_worktree_paths_from_argv(&paths, argc, argv, worktree); + if (error) + goto done; error = got_repo_open(&repo, got_worktree_get_repo_path(worktree)); if (error != NULL) @@ -3307,7 +3305,7 @@ cmd_commit(int argc, char *argv[]) cl_arg.branch_name += 6; cl_arg.repo_path = got_repo_get_path(repo); cl_arg.logmsg_path = NULL; - error = got_worktree_commit(&id, worktree, path, got_author, NULL, + error = got_worktree_commit(&id, worktree, &paths, got_author, NULL, collect_commit_logmsg, &cl_arg, print_status, NULL, repo); if (error) { if (cl_arg.logmsg_path) @@ -3328,7 +3326,6 @@ done: got_repo_close(repo); if (worktree) got_worktree_close(worktree); - free(path); free(cwd); free(id_str); free(editor); blob - 105bebdfb14c771c8bb51d8ab106cf8540208344 blob + f4f431a41a6b1d33399a3974a26880899d231b80 --- include/got_worktree.h +++ include/got_worktree.h @@ -199,11 +199,10 @@ typedef const struct got_error *(*got_worktree_commit_ * current base commit. * An author and a non-empty log message must be specified. * The name of the committer is optional (may be NULL). - * If an on-disk path is specified, only commit changes at or within this path. */ const struct got_error *got_worktree_commit(struct got_object_id **, - struct got_worktree *, const char *, const char *, const char *, - got_worktree_commit_msg_cb, void *, + struct got_worktree *, struct got_pathlist_head *, const char *, + const char *, got_worktree_commit_msg_cb, void *, got_worktree_status_cb, void *, struct got_repository *); /* Get the path of a commitable worktree item. */ blob - 1103157f40a2fc20de0d14bda891787ffdfeeaad blob + 77022f311e883e0e67963e20af490976c2e29719 --- lib/worktree.c +++ lib/worktree.c @@ -3340,7 +3340,7 @@ const struct got_error * commit_worktree(struct got_object_id **new_commit_id, struct got_pathlist_head *commitable_paths, struct got_object_id *head_commit_id, struct got_worktree *worktree, - const char *ondisk_path, const char *author, const char *committer, + const char *author, const char *committer, got_worktree_commit_msg_cb commit_msg_cb, void *commit_arg, got_worktree_status_cb status_cb, void *status_arg, struct got_repository *repo) @@ -3464,10 +3464,35 @@ done: } return err; } + +static const struct got_error * +check_path_is_commitable(const char *path, + struct got_pathlist_head *commitable_paths) +{ + struct got_pathlist_entry *cpe = NULL; + size_t path_len = strlen(path); + TAILQ_FOREACH(cpe, commitable_paths, entry) { + struct got_commitable *ct = cpe->data; + const char *ct_path = ct->path; + + while (ct_path[0] == '/') + ct_path++; + + if (strcmp(path, ct_path) == 0 || + got_path_is_child(ct_path, path, path_len)) + break; + } + + if (cpe == NULL) + return got_error_path(path, GOT_ERR_BAD_PATH); + + return NULL; +} + const struct got_error * got_worktree_commit(struct got_object_id **new_commit_id, - struct got_worktree *worktree, const char *ondisk_path, + struct got_worktree *worktree, struct got_pathlist_head *paths, const char *author, const char *committer, got_worktree_commit_msg_cb commit_msg_cb, void *commit_arg, got_worktree_status_cb status_cb, void *status_arg, @@ -3475,7 +3500,7 @@ got_worktree_commit(struct got_object_id **new_commit_ { const struct got_error *err = NULL, *unlockerr = NULL, *sync_err; struct got_fileindex *fileindex = NULL; - char *fileindex_path = NULL, *relpath = NULL; + char *fileindex_path = NULL; struct got_pathlist_head commitable_paths; struct collect_commitables_arg cc_arg; struct got_pathlist_entry *pe; @@ -3498,21 +3523,6 @@ got_worktree_commit(struct got_object_id **new_commit_ if (err) goto done; - if (ondisk_path) { - if (strcmp(ondisk_path, worktree->root_path) == 0) { - relpath = strdup(""); - if (relpath == NULL) { - err = got_error_from_errno("strdup"); - goto done; - } - } else { - err = got_path_skip_common_ancestor(&relpath, - worktree->root_path, ondisk_path); - if (err) - return err; - } - } - err = open_fileindex(&fileindex, &fileindex_path, worktree); if (err) goto done; @@ -3520,14 +3530,22 @@ got_worktree_commit(struct got_object_id **new_commit_ cc_arg.commitable_paths = &commitable_paths; cc_arg.worktree = worktree; cc_arg.repo = repo; - err = worktree_status(worktree, relpath ? relpath : "", - fileindex, repo, collect_commitables, &cc_arg, NULL, NULL); - if (err) - goto done; + TAILQ_FOREACH(pe, paths, entry) { + err = worktree_status(worktree, pe->path, fileindex, repo, + collect_commitables, &cc_arg, NULL, NULL); + if (err) + goto done; + } if (TAILQ_EMPTY(&commitable_paths)) { err = got_error(GOT_ERR_COMMIT_NO_CHANGES); goto done; + } + + TAILQ_FOREACH(pe, paths, entry) { + err = check_path_is_commitable(pe->path, &commitable_paths); + if (err) + goto done; } TAILQ_FOREACH(pe, &commitable_paths, entry) { @@ -3538,7 +3556,7 @@ got_worktree_commit(struct got_object_id **new_commit_ } err = commit_worktree(new_commit_id, &commitable_paths, - head_commit_id, worktree, ondisk_path, author, committer, + head_commit_id, worktree, author, committer, commit_msg_cb, commit_arg, status_cb, status_arg, repo); if (err) goto done; @@ -3552,7 +3570,6 @@ done: if (fileindex) got_fileindex_free(fileindex); free(fileindex_path); - free(relpath); unlockerr = lock_worktree(worktree, LOCK_SH); if (unlockerr && err == NULL) err = unlockerr; @@ -4088,7 +4105,7 @@ rebase_commit(struct got_object_id **new_commit_id, return got_error_from_errno("strdup"); err = commit_worktree(new_commit_id, &commitable_paths, head_commit_id, - worktree, NULL, got_object_commit_get_author(orig_commit), + worktree, got_object_commit_get_author(orig_commit), got_object_commit_get_committer(orig_commit), collect_rebase_commit_msg, logmsg, rebase_status, NULL, repo); if (err) blob - a8ec799b172f52f573b64c7d8fb59eadb12ffc58 blob + 579f23ddfca6faf4a1e9709eecf9394ab27b2f18 --- regress/cmdline/commit.sh +++ regress/cmdline/commit.sh @@ -418,7 +418,59 @@ function test_commit_dir_path { fi test_done "$testroot" "$ret" } + +function test_commit_selected_paths { + local testroot=`test_init commit_selected_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 + echo "modified delta" > $testroot/wt/gamma/delta + echo "modified zeta" > $testroot/wt/epsilon/zeta + (cd $testroot/wt && got rm beta >/dev/null) + echo "new file" > $testroot/wt/new + (cd $testroot/wt && got add new >/dev/null) + (cd $testroot/wt && got commit -m 'many paths' nonexistent alpha \ + > $testroot/stdout 2> $testroot/stderr) + ret="$?" + if [ "$ret" == "0" ]; then + echo "commit succeeded unexpectedly" >&2 + test_done "$testroot" "1" + return 1 + fi + echo "got: nonexistent: bad path" > $testroot/stderr.expected + + cmp -s $testroot/stderr.expected $testroot/stderr + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stderr.expected $testroot/stderr + test_done "$testroot" "$ret" + return 1 + fi + + (cd $testroot/wt && got commit -m 'many paths' \ + beta new gamma > $testroot/stdout) + + local head_rev=`git_show_head $testroot/repo` + echo "A new" > $testroot/stdout.expected + echo "D beta" >> $testroot/stdout.expected + echo "M gamma/delta" >> $testroot/stdout.expected + echo "Created commit $head_rev" >> $testroot/stdout.expected + + 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_commit_basic run_test test_commit_new_subdir run_test test_commit_subdir @@ -430,3 +482,4 @@ run_test test_commit_single_file_multiple run_test test_commit_added_and_modified_in_same_dir run_test test_commit_path_prefix run_test test_commit_dir_path +run_test test_commit_selected_paths