commit - 500cd40f2b6e569ffb569fdeda0f2d1765324106
commit + f8d1f275c50bc69aed2c12b2efacf5923c9c4a4b
blob - ca79672f32fa9b5393d9e4811d01fa6950643cfd
blob + 58867aeefb5aa093575f36499d23069e9b3225aa
--- include/got_worktree.h
+++ include/got_worktree.h
struct got_worktree;
/* status codes */
+#define GOT_STATUS_NO_CHANGE ' '
#define GOT_STATUS_ADD 'A'
#define GOT_STATUS_EXISTS 'E'
#define GOT_STATUS_UPDATE 'U'
#define GOT_STATUS_DELETE 'D'
+#define GOT_STATUS_MODIFIY 'M'
+#define GOT_STATUS_MISSING '!'
+#define GOT_STATUS_UNVERSIONED '?'
/*
* Attempt to initialize a new work tree on disk.
struct got_repository *, got_worktree_checkout_cb progress, void *,
got_worktree_cancel_cb, void *);
+/* A callback function which is invoked to report a path's status. */
+typedef void (*got_worktree_status_cb)(void *, unsigned char, const char *);
+
+/*
+ * Report the status of paths in the work tree.
+ * The status callback will be invoked with the provided void * argument,
+ * a path, and a corresponding status code.
+ */
+const struct got_error *
+got_worktree_status(struct got_worktree *, struct got_repository *,
+ got_worktree_status_cb, void *, got_worktree_cancel_cb cancel_cb, void *);
blob - 1d33ed029c59827d2854d7d3565d3fdfe6c5ed7e
blob + 718b43bb1e3859230f65c86aed4a5923887ab6fd
--- lib/worktree.c
+++ lib/worktree.c
unlockerr = lock_worktree(worktree, LOCK_SH);
if (unlockerr && err == NULL)
err = unlockerr;
+ return err;
+}
+
+struct diff_dir_cb_arg {
+ struct got_fileindex *fileindex;
+ struct got_worktree *worktree;
+ struct got_repository *repo;
+ got_worktree_status_cb status_cb;
+ void *status_arg;
+ got_worktree_cancel_cb cancel_cb;
+ void *cancel_arg;
+};
+
+
+static const struct got_error *
+get_modified_file_status(unsigned char *status, struct got_fileindex_entry *ie,
+ struct got_worktree *worktree, struct got_repository *repo)
+{
+ const struct got_error *err = NULL;
+ struct got_object_id id;
+ size_t hdrlen;
+ char *abspath;
+ FILE *f = NULL;
+ uint8_t fbuf[8192];
+ struct got_blob_object *blob = NULL;
+ size_t flen, blen;
+
+ *status = GOT_STATUS_NO_CHANGE;
+
+ if (asprintf(&abspath, "%s/%s", worktree->root_path, ie->path) == -1)
+ return got_error_from_errno();
+
+ memcpy(id.sha1, ie->blob_sha1, sizeof(id.sha1));
+ err = got_object_open_as_blob(&blob, repo, &id, sizeof(fbuf));
+ if (err)
+ goto done;
+
+ f = fopen(abspath, "r");
+ if (f == NULL) {
+ err = got_error_from_errno();
+ goto done;
+ }
+ hdrlen = got_object_blob_get_hdrlen(blob);
+ while (1) {
+ const uint8_t *bbuf = got_object_blob_get_read_buf(blob);
+ err = got_object_blob_read_block(&blen, blob);
+ if (err)
+ break;
+ flen = fread(fbuf, 1, sizeof(fbuf), f);
+ if (blen == 0) {
+ if (flen != 0)
+ *status = GOT_STATUS_MODIFIY;
+ break;
+ } else if (flen == 0) {
+ if (blen != 0)
+ *status = GOT_STATUS_MODIFIY;
+ break;
+ } else if (blen == flen) {
+ /* Skip blob object header first time around. */
+ if (memcmp(bbuf + hdrlen, fbuf, flen) != 0) {
+ *status = GOT_STATUS_MODIFIY;
+ break;
+ }
+ } else {
+ *status = GOT_STATUS_MODIFIY;
+ break;
+ }
+ hdrlen = 0;
+ }
+done:
+ if (blob)
+ got_object_blob_close(blob);
+ if (f)
+ fclose(f);
+ free(abspath);
+ return err;
+}
+
+static const struct got_error *
+status_old_new(void *arg, struct got_fileindex_entry *ie,
+ struct dirent *de, const char *parent_path)
+{
+ const struct got_error *err = NULL;
+ struct diff_dir_cb_arg *a = arg;
+ char *abspath;
+ struct stat sb;
+ unsigned char status = GOT_STATUS_NO_CHANGE;
+
+ if (parent_path[0]) {
+ if (asprintf(&abspath, "%s/%s/%s", a->worktree->root_path,
+ parent_path, de->d_name) == -1)
+ return got_error_from_errno();
+ } else {
+ if (asprintf(&abspath, "%s/%s", a->worktree->root_path,
+ de->d_name) == -1)
+ return got_error_from_errno();
+ }
+
+ if (lstat(abspath, &sb) == -1) {
+ err = got_error_from_errno();
+ goto done;
+ }
+
+ if (!S_ISREG(sb.st_mode))
+ goto done;
+
+ if (ie->ctime_sec == sb.st_ctime &&
+ ie->ctime_nsec == sb.st_ctimensec &&
+ ie->mtime_sec == sb.st_mtime &&
+ ie->mtime_sec == sb.st_mtime &&
+ ie->mtime_nsec == sb.st_mtimensec &&
+ ie->size == (sb.st_size & 0xffffffff))
+ goto done;
+
+ err = get_modified_file_status(&status, ie, a->worktree, a->repo);
+ if (err)
+ goto done;
+ if (status != GOT_STATUS_NO_CHANGE)
+ (*a->status_cb)(a->status_arg, status, ie->path);
+done:
+ free(abspath);
return err;
}
+
+static const struct got_error *
+status_old(void *arg, struct got_fileindex_entry *ie, const char *parent_path)
+{
+ struct diff_dir_cb_arg *a = arg;
+ (*a->status_cb)(a->status_arg, GOT_STATUS_MISSING, ie->path);
+ return NULL;
+}
+
+static const struct got_error *
+status_new(void *arg, struct dirent *de, const char *parent_path)
+{
+ struct diff_dir_cb_arg *a = arg;
+ char *path = NULL;
+
+ if (de->d_type == DT_DIR)
+ return NULL;
+
+ if (parent_path[0]) {
+ if (asprintf(&path, "%s/%s", parent_path, de->d_name) == -1)
+ return got_error_from_errno();
+ } else {
+ path = de->d_name;
+ }
+
+ (*a->status_cb)(a->status_arg, GOT_STATUS_UNVERSIONED, path);
+ if (parent_path[0])
+ free(path);
+ return NULL;
+}
+
+const struct got_error *
+got_worktree_status(struct got_worktree *worktree,
+ 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;
+ DIR *workdir = NULL;
+ char *fileindex_path = NULL;
+ struct got_fileindex *fileindex = NULL;
+ FILE *index = NULL;
+ struct got_fileindex_diff_dir_cb diff_cb;
+ struct diff_dir_cb_arg arg;
+
+ 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) {
+ if (errno != ENOENT) {
+ err = got_error_from_errno();
+ goto done;
+ }
+ } else {
+ err = got_fileindex_read(fileindex, index);
+ fclose(index);
+ if (err)
+ goto done;
+ }
+
+ workdir = opendir(worktree->root_path);
+ diff_cb.diff_old_new = status_old_new;
+ diff_cb.diff_old = status_old;
+ diff_cb.diff_new = status_new;
+ arg.fileindex = fileindex;
+ arg.worktree = worktree;
+ arg.repo = repo;
+ arg.status_cb = status_cb;
+ arg.status_arg = status_arg;
+ arg.cancel_cb = cancel_cb;
+ arg.cancel_arg = cancel_arg;
+ err = got_fileindex_diff_dir(fileindex, workdir, repo, &diff_cb, &arg);
+done:
+ if (workdir)
+ closedir(workdir);
+ free(fileindex_path);
+ got_fileindex_free(fileindex);
+ return err;
+}