commit - 5e3ce57ad89618bd4872440fab5fd8a5ffc7b4c9
commit + d00136be1116f6f2147a0984ac8461a1b19d11f6
blob - a66952acd39b2364c670b6528a97e0e30767454e
blob + 7bf08a3c2ec5f97189abca71a3b19ca7b24a3923
--- got/got.1
+++ got/got.1
.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
__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 *[]);
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,
"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
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) {
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();
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
*/
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
{
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
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
}
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 &&
*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
-REGRESS_TARGETS=checkout update status log
+REGRESS_TARGETS=checkout update status log add
NOOBJ=Yes
checkout:
log:
./log.sh
+add:
+ ./add.sh
+
.include <bsd.regress.mk>
blob - 72eb5ceab648e97bb44c7724ab30d699eb1327eb
blob + bdf87dc616a765027d46ff0a82ddf9c161e79ff8
--- regress/cmdline/status.sh
+++ regress/cmdline/status.sh
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)