commit c42961445798bbd0ae6062fee1914b918ecca593 from: Stefan Sperling date: Thu May 09 19:36:09 2019 UTC WIP commit implementation commit - eb7dbff56162affe65de4f91e4dd4d52702751b5 commit + c42961445798bbd0ae6062fee1914b918ecca593 blob - a0d4587d54e186ee756ec2c4e91ce23ca709d5c4 blob + a11f5adfc60eb76b1bde6823af16c5920318e355 --- got/Makefile +++ got/Makefile @@ -5,7 +5,8 @@ SRCS= got.c blame.c commit_graph.c delta.c diff.c dif 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 @@ -80,6 +80,7 @@ __dead static void usage_ref(void); __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 *[]); @@ -92,6 +93,7 @@ static const struct got_error* cmd_ref(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, @@ -116,6 +118,8 @@ static struct cmd got_commands[] = { "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 @@ -2066,4 +2070,83 @@ done: 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 = ""; + 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 @@ -82,6 +82,7 @@ #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; @@ -155,6 +156,7 @@ static const struct got_error { { 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 @@ -164,3 +164,17 @@ got_worktree_schedule_delete(struct got_worktree *, co */ 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 @@ -2194,5 +2194,98 @@ done: 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 @@ -0,0 +1,53 @@ +#!/bin/sh +# +# Copyright (c) 2019 Stefan Sperling +# +# 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