Commit 0cb83759ff5e9ca28c397bb04edb8e0629eba4bd

Stefan Sperling 2019-08-03T13:31:03

implement got_worktree_stage_paths()

diff --git a/include/got_worktree.h b/include/got_worktree.h
index f4f431a..9963b82 100644
--- a/include/got_worktree.h
+++ b/include/got_worktree.h
@@ -371,3 +371,9 @@ const struct got_error *got_worktree_histedit_abort(struct got_worktree *,
 /* Get the path to this work tree's histedit script file. */
 const struct got_error *got_worktree_get_histedit_script_path(char **,
     struct got_worktree *);
+
+/* Stage the specified paths for commit. */
+const struct got_error *got_worktree_stage_paths(struct got_worktree *,
+    struct got_pathlist_head *, struct got_repository *,
+    got_worktree_status_cb, void *,
+    got_worktree_cancel_cb , void *);
diff --git a/lib/fileindex.c b/lib/fileindex.c
index 61af730..dbf735c 100644
--- a/lib/fileindex.c
+++ b/lib/fileindex.c
@@ -161,11 +161,19 @@ got_fileindex_entry_path_len(const struct got_fileindex_entry *ie)
 }
 
 uint32_t
-got_fileindex_entry_stage(const struct got_fileindex_entry *ie)
+got_fileindex_entry_stage_get(const struct got_fileindex_entry *ie)
 {
 	return ((ie->flags & GOT_FILEIDX_F_STAGE) >> GOT_FILEIDX_F_STAGE_SHIFT);
 }
 
+void
+got_fileindex_entry_stage_set(struct got_fileindex_entry *ie, uint32_t stage)
+{
+	ie->flags &= ~GOT_FILEIDX_F_STAGE;
+	ie->flags |= ((stage << GOT_FILEIDX_F_STAGE_SHIFT) &
+	    GOT_FILEIDX_F_STAGE);
+}
+
 int
 got_fileindex_entry_has_blob(struct got_fileindex_entry *ie)
 {
@@ -379,7 +387,7 @@ write_fileindex_entry(SHA1_CTX *ctx, struct got_fileindex_entry *ie,
 	if (err)
 		return err;
 
-	stage = got_fileindex_entry_stage(ie);
+	stage = got_fileindex_entry_stage_get(ie);
 	if (stage == GOT_FILEIDX_STAGE_MODIFY ||
 	    stage == GOT_FILEIDX_STAGE_ADD) {
 		SHA1Update(ctx, ie->staged_blob_sha1, SHA1_DIGEST_LENGTH);
@@ -579,7 +587,7 @@ read_fileindex_entry(struct got_fileindex_entry **iep, SHA1_CTX *ctx,
 		goto done;
 
 	if (version >= 2) {
-		uint32_t stage = got_fileindex_entry_stage(ie);
+		uint32_t stage = got_fileindex_entry_stage_get(ie);
 		if (stage == GOT_FILEIDX_STAGE_MODIFY ||
 		    stage == GOT_FILEIDX_STAGE_ADD) {
 			n = fread(ie->staged_blob_sha1, 1, SHA1_DIGEST_LENGTH,
diff --git a/lib/got_lib_fileindex.h b/lib/got_lib_fileindex.h
index 33b7f14..83e6be7 100644
--- a/lib/got_lib_fileindex.h
+++ b/lib/got_lib_fileindex.h
@@ -156,5 +156,7 @@ const struct got_error *got_fileindex_diff_dir(struct got_fileindex *, DIR *,
 int got_fileindex_entry_has_blob(struct got_fileindex_entry *);
 int got_fileindex_entry_has_commit(struct got_fileindex_entry *);
 int got_fileindex_entry_has_file_on_disk(struct got_fileindex_entry *);
+uint32_t got_fileindex_entry_stage_get(const struct got_fileindex_entry *);
+void got_fileindex_entry_stage_set(struct got_fileindex_entry *ie, uint32_t);
 
 void got_fileindex_entry_mark_deleted_from_disk(struct got_fileindex_entry *);
diff --git a/lib/worktree.c b/lib/worktree.c
index a64b0f8..e2d15ef 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -4856,3 +4856,105 @@ done:
 	free(commit_ref_name);
 	return err;
 }
+
+static const struct got_error *
+stage_path(const char *path, size_t path_len, struct got_worktree *worktree,
+    struct got_fileindex *fileindex, struct got_repository *repo,
+    got_worktree_status_cb status_cb, void *status_arg)
+{
+	const struct got_error *err = NULL;
+	char *ondisk_path;
+	struct got_fileindex_entry *ie;
+	unsigned char status;
+	struct stat sb;
+	struct got_object_id *blob_id = NULL;
+	uint32_t stage;
+
+	if (asprintf(&ondisk_path, "%s/%s", worktree->root_path, path) == -1)
+		return got_error_from_errno("asprintf");
+
+	ie = got_fileindex_entry_get(fileindex, path, path_len);
+	if (ie == NULL) {
+		err = got_error_path(path, GOT_ERR_FILE_STATUS);
+		goto done;
+	}
+
+	err = get_file_status(&status, &sb, ie, ondisk_path, repo);
+	if (err)
+		goto done;
+
+	switch (status) {
+	case GOT_STATUS_ADD:
+	case GOT_STATUS_MODIFY:
+		err = got_object_blob_create(&blob_id, ondisk_path,
+		    repo);
+		if (err)
+			goto done;
+		memcpy(ie->staged_blob_sha1, blob_id->sha1,
+		    SHA1_DIGEST_LENGTH);
+		if (status == GOT_STATUS_ADD)
+			stage = GOT_FILEIDX_STAGE_ADD;
+		else
+			stage = GOT_FILEIDX_STAGE_MODIFY;
+		break;
+	case GOT_STATUS_DELETE:
+		stage = GOT_FILEIDX_STAGE_DELETE;
+		break;
+	default:
+		err = got_error_path(path, GOT_ERR_FILE_STATUS);
+		goto done;
+	}
+
+	got_fileindex_entry_stage_set(ie, stage);
+
+	/* XXX TODO pass 'staged' status separately */
+	err = (*status_cb)(status_arg, status, path, blob_id, NULL);
+done:
+	free(blob_id);
+	free(ondisk_path);
+	return err;
+}
+
+const struct got_error *
+got_worktree_stage_paths(struct got_worktree *worktree,
+    struct got_pathlist_head *paths, struct got_repository *repo,
+    got_worktree_status_cb status_cb, void *status_arg,
+    got_worktree_cancel_cb cancel_cb, void *cancel_arg)
+{
+	const struct got_error *err = NULL, *sync_err, *unlockerr;
+	struct got_pathlist_entry *pe;
+	struct got_fileindex *fileindex = NULL;
+	char *fileindex_path = NULL;
+
+	err = lock_worktree(worktree, LOCK_EX);
+	if (err)
+		return err;
+
+	err = open_fileindex(&fileindex, &fileindex_path, worktree);
+	if (err)
+		goto done;
+
+	TAILQ_FOREACH(pe, paths, entry) {
+		if (cancel_cb) {
+			err = (*cancel_cb)(cancel_arg);
+			if (err)
+				break;
+		}
+		err = stage_path(pe->path, pe->path_len, worktree, fileindex,
+		    repo, status_cb, status_arg);
+		if (err)
+			break;
+	}
+
+	sync_err = sync_fileindex(fileindex, fileindex_path);
+	if (sync_err && err == NULL)
+		err = sync_err;
+done:
+	free(fileindex_path);
+	if (fileindex)
+		got_fileindex_free(fileindex);
+	unlockerr = lock_worktree(worktree, LOCK_SH);
+	if (unlockerr && err == NULL)
+		err = unlockerr;
+	return err;
+}