commit d00136be1116f6f2147a0984ac8461a1b19d11f6 from: Stefan Sperling date: Tue Mar 26 08:03:53 2019 UTC implement a basic 'got add' command commit - 5e3ce57ad89618bd4872440fab5fd8a5ffc7b4c9 commit + d00136be1116f6f2147a0984ac8461a1b19d11f6 blob - a66952acd39b2364c670b6528a97e0e30767454e blob + 7bf08a3c2ec5f97189abca71a3b19ca7b24a3923 --- got/got.1 +++ got/got.1 @@ -283,6 +283,9 @@ List all existing references in the repository. .It Fl d Ar name Delete the reference with the specified name from the repository. .El +.It Cm add Ar file-path +Schedule an unversioned file in a work tree for addition to the +repository in the next commit. .El .Sh EXIT STATUS .Ex -std got blob - 631c01078b1852d23f17f9d132ab0af89fffd064 blob + 7815366ff85c2dfd25fd88e773323d34316ad0c6 --- got/got.c +++ got/got.c @@ -77,6 +77,7 @@ __dead static void usage_blame(void); __dead static void usage_tree(void); __dead static void usage_status(void); __dead static void usage_ref(void); +__dead static void usage_add(void); static const struct got_error* cmd_checkout(int, char *[]); static const struct got_error* cmd_update(int, char *[]); @@ -86,6 +87,7 @@ static const struct got_error* cmd_blame(int, char *[ static const struct got_error* cmd_tree(int, char *[]); static const struct got_error* cmd_status(int, char *[]); static const struct got_error* cmd_ref(int, char *[]); +static const struct got_error* cmd_add(int, char *[]); static struct cmd got_commands[] = { { "checkout", cmd_checkout, usage_checkout, @@ -104,6 +106,8 @@ static struct cmd got_commands[] = { "show modification status of files" }, { "ref", cmd_ref, usage_ref, "manage references in repository" }, + { "add", cmd_add, usage_add, + "add a new file to version control" }, }; int @@ -992,7 +996,7 @@ print_diff(void *arg, unsigned char status, const char char *abspath = NULL; struct stat sb; - if (status != GOT_STATUS_MODIFY) + if (status != GOT_STATUS_MODIFY && status != GOT_STATUS_ADD) return NULL; if (!a->header_shown) { @@ -1001,10 +1005,13 @@ print_diff(void *arg, unsigned char status, const char a->header_shown = 1; } - err = got_object_open_as_blob(&blob1, a->repo, id, 8192); - if (err) - goto done; + if (status == GOT_STATUS_MODIFY) { + err = got_object_open_as_blob(&blob1, a->repo, id, 8192); + if (err) + goto done; + } + if (asprintf(&abspath, "%s/%s", got_worktree_get_root_path(a->worktree), path) == -1) { err = got_error_from_errno(); @@ -1826,5 +1833,66 @@ done: got_worktree_close(worktree); free(cwd); free(repo_path); + return error; +} + +__dead static void +usage_add(void) +{ + fprintf(stderr, "usage: %s add file-path\n", getprogname()); + exit(1); +} + +static const struct got_error * +cmd_add(int argc, char *argv[]) +{ + const struct got_error *error = NULL; + struct got_worktree *worktree = NULL; + char *cwd = NULL, *path = NULL, *relpath = NULL; + int ch; + + while ((ch = getopt(argc, argv, "")) != -1) { + switch (ch) { + default: + usage_add(); + /* NOTREACHED */ + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) + usage_add(); + + path = realpath(argv[0], NULL); + if (path == NULL) { + error = got_error_from_errno(); + goto done; + } + + cwd = getcwd(NULL, 0); + if (cwd == NULL) { + error = got_error_from_errno(); + goto done; + } + error = got_worktree_open(&worktree, cwd); + if (error) + goto done; + + error = apply_unveil(NULL, 0, got_worktree_get_root_path(worktree)); + if (error) + goto done; + + error = got_worktree_schedule_add(&relpath, worktree, path); + if (error) + goto done; + printf("%c %s\n", GOT_STATUS_ADD, relpath); +done: + if (worktree) + got_worktree_close(worktree); + free(path); + free(relpath); + free(cwd); return error; } blob - c8d82fa3f0887124cf337b9f7ac8678c47ea8ddc blob + 6c64d84a67bcf518f83f376fa4908c701fd84b6f --- include/got_worktree.h +++ include/got_worktree.h @@ -131,3 +131,11 @@ const struct got_error *got_worktree_status(struct got */ const struct got_error *got_worktree_resolve_path(char **, struct got_worktree *, const char *); + +/* + * Schedule a file at an on-disk path for addition in the next commit. + * Return the added file's path relative to the root of the work tree. + * The caller must dispose of this relative path with free(3). + */ +const struct got_error *got_worktree_schedule_add(char **, + struct got_worktree *, const char *); blob - 022eafe9522db33fd6a9cb09c8c41b91a297588c blob + 35307267948d55390cbdb4637114ef5fb2dae80d --- lib/fileindex.c +++ lib/fileindex.c @@ -123,6 +123,18 @@ got_fileindex_entry_free(struct got_fileindex_entry *e { free(entry->path); free(entry); +} + +int +got_fileindex_entry_has_blob(struct got_fileindex_entry *ie) +{ + return (ie->flags & GOT_FILEIDX_F_NO_BLOB) == 0; +} + +int +got_fileindex_entry_has_commit(struct got_fileindex_entry *ie) +{ + return (ie->flags & GOT_FILEIDX_F_NO_COMMIT) == 0; } static const struct got_error * blob - 97d2c4fbb3f7316b91f5a0f21d35c1a772c1726f blob + 98ab532f88824e32453c1ec38d506d903df29a8f --- lib/got_lib_fileindex.h +++ lib/got_lib_fileindex.h @@ -138,3 +138,6 @@ struct got_fileindex_diff_dir_cb { const struct got_error *got_fileindex_diff_dir(struct got_fileindex *, DIR *, const char *, const char *, struct got_repository *, struct got_fileindex_diff_dir_cb *, void *); + +int got_fileindex_entry_has_blob(struct got_fileindex_entry *); +int got_fileindex_entry_has_commit(struct got_fileindex_entry *); blob - 12e2eb8b8f429c6ceffa39338e06773b13190057 blob + bd919fbf217da75c8cea5d5941bca91798510925 --- lib/worktree.c +++ lib/worktree.c @@ -984,7 +984,12 @@ get_file_status(unsigned char *status, struct stat *sb } if (ie == NULL) + return NULL; + + if (!got_fileindex_entry_has_blob(ie)) { + *status = GOT_STATUS_ADD; return NULL; + } if (ie->ctime_sec == sb->st_ctime && ie->ctime_nsec == sb->st_ctimensec && @@ -1582,5 +1587,98 @@ done: *wt_path = path; else free(path); + return err; +} + +const struct got_error * +got_worktree_schedule_add(char **relpath, struct got_worktree *worktree, + const char *ondisk_path) +{ + struct got_fileindex *fileindex = NULL; + struct got_fileindex_entry *ie = NULL; + char *fileindex_path = NULL, *new_fileindex_path = NULL; + FILE *index = NULL, *new_index = NULL; + const struct got_error *err = NULL, *unlockerr = NULL; + + *relpath = NULL; + + err = lock_worktree(worktree, LOCK_EX); + if (err) + return err; + + err = got_path_skip_common_ancestor(relpath, + got_worktree_get_root_path(worktree), ondisk_path); + if (err) + goto done; + + err = got_fileindex_entry_alloc(&ie, ondisk_path, *relpath, NULL, NULL); + if (err) + goto done; + + fileindex = got_fileindex_alloc(); + if (fileindex == NULL) { + err = got_error_from_errno(); + goto done; + } + + if (asprintf(&fileindex_path, "%s/%s/%s", worktree->root_path, + GOT_WORKTREE_GOT_DIR, GOT_WORKTREE_FILE_INDEX) == -1) { + err = got_error_from_errno(); + fileindex_path = NULL; + goto done; + } + + index = fopen(fileindex_path, "rb"); + if (index == NULL) { + err = got_error_from_errno(); + goto done; + } + + err = got_fileindex_read(fileindex, index); + if (err) + goto done; + + err = got_fileindex_entry_add(fileindex, ie); + if (err) + goto done; + ie = NULL; /* now owned by fileindex; don't free separately */ + + err = got_opentemp_named(&new_fileindex_path, &new_index, + fileindex_path); + if (err) + goto done; + + err = got_fileindex_write(fileindex, new_index); + if (err) + goto done; + + if (rename(new_fileindex_path, fileindex_path) != 0) { + err = got_error_from_errno(); + goto done; + } + + free(new_fileindex_path); + new_fileindex_path = NULL; +done: + if (index) { + if (fclose(index) != 0 && err == NULL) + err = got_error_from_errno(); + } + if (new_fileindex_path) { + if (unlink(new_fileindex_path) != 0 && err == NULL) + err = got_error_from_errno(); + free(new_fileindex_path); + } + if (ie) + got_fileindex_entry_free(ie); + if (fileindex) + got_fileindex_free(fileindex); + unlockerr = lock_worktree(worktree, LOCK_SH); + if (unlockerr && err == NULL) + err = unlockerr; + if (err) { + free(*relpath); + *relpath = NULL; + } return err; } blob - 91646777a56117c55bba1ec3d1e49eb319cbf4fe blob + fb63dcc0bfd3e3a0ceefa1d427f83a507225e6df --- regress/cmdline/Makefile +++ regress/cmdline/Makefile @@ -1,4 +1,4 @@ -REGRESS_TARGETS=checkout update status log +REGRESS_TARGETS=checkout update status log add NOOBJ=Yes checkout: @@ -13,4 +13,7 @@ status: log: ./log.sh +add: + ./add.sh + .include blob - 72eb5ceab648e97bb44c7724ab30d699eb1327eb blob + bdf87dc616a765027d46ff0a82ddf9c161e79ff8 --- regress/cmdline/status.sh +++ regress/cmdline/status.sh @@ -30,10 +30,13 @@ function test_status_basic { echo "unversioned file" > $testroot/wt/foo rm $testroot/wt/epsilon/zeta touch $testroot/wt/beta + echo "new file" > $testroot/wt/new + (cd $testroot/wt && got add new >/dev/null) echo 'M alpha' > $testroot/stdout.expected echo '! epsilon/zeta' >> $testroot/stdout.expected echo '? foo' >> $testroot/stdout.expected + echo 'A new' >> $testroot/stdout.expected (cd $testroot/wt && got status > $testroot/stdout)