commit - eb7dbff56162affe65de4f91e4dd4d52702751b5
commit + c42961445798bbd0ae6062fee1914b918ecca593
blob - a0d4587d54e186ee756ec2c4e91ce23ca709d5c4
blob + a11f5adfc60eb76b1bde6823af16c5920318e355
--- got/Makefile
+++ got/Makefile
diffreg.c error.c fileindex.c object.c object_cache.c \
object_idset.c object_parse.c opentemp.c path.c pack.c \
privsep.c reference.c repository.c sha1.c worktree.c \
- inflate.c buf.c worklist.c rcsutil.c diff3.c lockfile.c
+ inflate.c buf.c worklist.c rcsutil.c diff3.c lockfile.c \
+ deflate.c object_create.c
CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib \
-DGOT_LIBEXECDIR=${GOT_LIBEXECDIR}
blob - 3ae9806466513fb65046cb695b43db95ee9175ec
blob + f48a524c2e941f59e208fe7590e665f47e95256a
--- got/got.c
+++ got/got.c
__dead static void usage_add(void);
__dead static void usage_rm(void);
__dead static void usage_revert(void);
+__dead static void usage_commit(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_add(int, char *[]);
static const struct got_error* cmd_rm(int, char *[]);
static const struct got_error* cmd_revert(int, char *[]);
+static const struct got_error* cmd_commit(int, char *[]);
static struct cmd got_commands[] = {
{ "checkout", cmd_checkout, usage_checkout,
"remove a versioned file" },
{ "revert", cmd_revert, usage_revert,
"revert uncommitted changes" },
+ { "commit", cmd_commit, usage_commit,
+ "create blob from file (WIP; can't create commits yet)" },
};
int
free(path);
free(cwd);
return error;
+}
+
+__dead static void
+usage_commit(void)
+{
+ fprintf(stderr, "usage: %s commit file-path\n", getprogname());
+ exit(1);
}
+
+static const struct got_error *
+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;
+ struct got_object_id *id = NULL;
+ const char *logmsg = "<no log message was specified>";
+ int ch;
+
+ while ((ch = getopt(argc, argv, "m:")) != -1) {
+ switch (ch) {
+ case 'm':
+ logmsg = optarg;
+ break;
+ default:
+ usage_commit();
+ /* NOTREACHED */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 1) {
+ path = realpath(argv[0], NULL);
+ if (path == NULL) {
+ error = got_error_from_errno();
+ goto done;
+ }
+ } else if (argc != 0)
+ usage_commit();
+
+
+ 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 = got_repo_open(&repo, got_worktree_get_repo_path(worktree));
+ if (error != NULL)
+ goto done;
+
+ error = apply_unveil(got_repo_get_path(repo), 0,
+ got_worktree_get_root_path(worktree));
+ if (error)
+ goto done;
+
+ error = got_worktree_commit(&id, worktree, path, logmsg, repo);
+ if (error)
+ goto done;
+
+ error = got_object_id_str(&id_str, id);
+ if (error)
+ goto done;
+ printf("created commit %s\n", id_str);
+done:
+ if (repo)
+ got_repo_close(repo);
+ if (worktree)
+ got_worktree_close(worktree);
+ free(path);
+ free(cwd);
+ free(id_str);
+ return error;
+}
blob - 9a844fe6ad7c549a025a23900bd9ef0fdc379f6b
blob + c5fb394b57b4336f094177e1a3d4f59a67e0794f
--- include/got_error.h
+++ include/got_error.h
#define GOT_ERR_WORKTREE_REPO 66
#define GOT_ERR_FILE_MODIFIED 67
#define GOT_ERR_FILE_STATUS 68
+#define GOT_ERR_COMMIT_CONFLICT 69
static const struct got_error {
int code;
{ GOT_ERR_WORKTREE_REPO,"cannot create worktree inside a git repository" },
{ GOT_ERR_FILE_MODIFIED,"file contains modifications" },
{ GOT_ERR_FILE_STATUS, "file has unexpected status" },
+ { GOT_ERR_COMMIT_CONFLICT,"cannot commit file in conflicted status" },
};
/*
blob - b3cc035a8b5e38c3d0857b2483ef8813565d0922
blob + debcb302f5ffcd4e16b249bb81b570b59be23923
--- include/got_worktree.h
+++ include/got_worktree.h
*/
const struct got_error *got_worktree_revert(struct got_worktree *,
const char *, got_worktree_checkout_cb, void *, struct got_repository *);
+
+/*
+ * Create a new commit from changes in the work tree.
+ * Return the ID of the newly created commit.
+ * The worktree's base commit will be set to this new commit.
+ * Files unaffected by this commit operation will retain their
+ * current base commit.
+ * A non-empty log message must be specified.
+ * 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 *,
+ struct got_repository *);
blob - 92720c552b8f12476bbc668ea2e70a5bb52cda8e
blob + e8812fdabeda99b6ce0029cdca495592a63d6c3d
--- lib/worktree.c
+++ lib/worktree.c
unlockerr = lock_worktree(worktree, LOCK_SH);
if (unlockerr && err == NULL)
err = unlockerr;
+ return err;
+}
+
+static const struct got_error *
+collect_committables(void *arg, unsigned char status, const char *path,
+ struct got_object_id *id)
+{
+ struct got_pathlist_head *paths = arg;
+ const struct got_error *err = NULL;
+ char *new_path = NULL;
+ unsigned char *new_status = NULL;
+ struct got_pathlist_entry *new = NULL;
+
+ if (status == GOT_STATUS_CONFLICT)
+ return got_error(GOT_ERR_COMMIT_CONFLICT);
+
+ if (status != GOT_STATUS_MODIFY && status != GOT_STATUS_ADD &&
+ status != GOT_STATUS_DELETE)
+ return NULL;
+
+ new_path = strdup(path);
+ if (new_path == NULL)
+ return got_error_from_errno();
+
+ new_status = malloc(sizeof(*new_status));
+ if (new_status == NULL) {
+ err = got_error_from_errno();
+ goto done;
+ }
+
+ *new_status = status;
+ err = got_pathlist_insert(&new, paths, new_path, new_status);
+done:
+ if (err || new == NULL) {
+ free(new_path);
+ free(new_status);
+ }
return err;
}
+
+const struct got_error *
+got_worktree_commit(struct got_object_id **new_commit_id,
+ struct got_worktree *worktree, const char *ondisk_path,
+ const char *logmsg, struct got_repository *repo)
+{
+ const struct got_error *err = NULL, *unlockerr = NULL;
+ struct got_pathlist_head paths;
+ struct got_pathlist_entry *pe;
+ char *relpath = NULL;
+ struct got_commit_object *base_commit = NULL;
+ struct got_tree_object *base_tree = NULL;
+
+ *new_commit_id = NULL;
+
+ TAILQ_INIT(&paths);
+
+ if (ondisk_path) {
+ err = got_path_skip_common_ancestor(&relpath,
+ worktree->root_path, ondisk_path);
+ if (err)
+ return err;
+ }
+
+ err = lock_worktree(worktree, LOCK_EX);
+ if (err)
+ goto done;
+
+ err = got_object_open_as_commit(&base_commit, repo,
+ worktree->base_commit_id);
+ if (err)
+ goto done;
+ err = got_object_open_as_tree(&base_tree, repo,
+ worktree->base_commit_id);
+ if (err)
+ goto done;
+
+ err = got_worktree_status(worktree, relpath ? relpath : "",
+ repo, collect_committables, &paths, NULL, NULL);
+ if (err)
+ goto done;
+
+ /* TODO: walk base tree and patch it to create a new tree */
+ printf("committables:\n");
+ TAILQ_FOREACH(pe, &paths, entry) {
+ unsigned char *status = pe->data;
+ printf("%c %s\n", *status, pe->path);
+ }
+done:
+ unlockerr = lock_worktree(worktree, LOCK_SH);
+ if (unlockerr && err == NULL)
+ err = unlockerr;
+ got_object_tree_close(base_tree);
+ free(relpath);
+ return err;
+}
blob - /dev/null
blob + 0a36cee5ce87d6281a30d8b60719e054ec0fba0d (mode 755)
--- /dev/null
+++ regress/cmdline/commit.sh
+#!/bin/sh
+#
+# Copyright (c) 2019 Stefan Sperling <stsp@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+. ./common.sh
+
+function test_commit_basic {
+ local testroot=`test_init commit_basic`
+
+ 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
+ (cd $testroot/wt && got add new >/dev/null)
+
+ (cd $testroot/wt && got commit -m 'test commit_basic' > $testroot/stdout)
+
+ local head_rev=`git_show_head $testroot/repo`
+ echo "M alpha" > $testroot/stdout.expected
+ echo "D beta" >> $testroot/stdout.expected
+ echo "A new" >> $testroot/stdout.expected
+ echo "created commit $head_rev" >> $testroot/stdout.expected
+
+ cmp $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