commit - 46e6bdd46c729dfc7e89384a8df8f9153ada0557
commit + 10e55613cb8d8b5d1829a732694d6259647d7821
blob - 7cb9fdf4c904187bc1843d43438daa6488dc9351
blob + 909c1b2c7ed3e00761357cb9ab0aaa679878483b
--- got/got.c
+++ got/got.c
err(1, "pledge");
#endif
- error = got_patch(patchfd, worktree, repo, nop, &print_remove_status,
+ error = got_patch(patchfd, worktree, repo, &print_remove_status,
NULL, &add_progress, NULL, check_cancelled, NULL);
done:
blob - 5f28ffc619f1b4e32ba37ff8e5361c636009ba74
blob + 448bb17e65d75d8d5ba94bd577f5cde4fef566bf
--- include/got_patch.h
+++ include/got_patch.h
* The patch file descriptor *must* be seekable.
*/
const struct got_error *
-got_patch(int, struct got_worktree *, struct got_repository *, int,
+got_patch(int, struct got_worktree *, struct got_repository *,
got_worktree_delete_cb, void *, got_worktree_checkout_cb, void *,
got_cancel_cb, void *);
blob - 4f71ec07169550c3193e28bb9ff1ffb746d4f3e1
blob + 7cc802afd65fee4f4cb23b39ba22943ec3808115
--- lib/patch.c
+++ lib/patch.c
}
static const struct got_error *
-schedule_add(const char *path, struct got_worktree *worktree,
- struct got_repository *repo, got_worktree_checkout_cb add_cb,
- void *add_arg)
-{
- static const struct got_error *err = NULL;
- struct got_pathlist_head paths;
- struct got_pathlist_entry *pe;
-
- TAILQ_INIT(&paths);
-
- err = got_pathlist_insert(&pe, &paths, path, NULL);
- if (err == NULL)
- err = got_worktree_schedule_add(worktree, &paths,
- add_cb, add_arg, repo, 1);
- got_pathlist_free(&paths);
- return err;
-}
-
-static const struct got_error *
-schedule_del(const char *path, struct got_worktree *worktree,
- struct got_repository *repo, got_worktree_delete_cb delete_cb,
- void *delete_arg)
-{
- static const struct got_error *err = NULL;
- struct got_pathlist_head paths;
- struct got_pathlist_entry *pe;
-
- TAILQ_INIT(&paths);
-
- err = got_pathlist_insert(&pe, &paths, path, NULL);
- if (err == NULL)
- err = got_worktree_schedule_delete(worktree, &paths,
- 0, NULL, delete_cb, delete_arg, repo, 0, 0);
- got_pathlist_free(&paths);
- return err;
-}
-
-static const struct got_error *
patch_file(struct got_patch *p, const char *path, FILE *tmp)
{
const struct got_error *err = NULL;
}
static const struct got_error *
+build_pathlist(const char *p, char **path, struct got_pathlist_head *head,
+ struct got_worktree *worktree)
+{
+ const struct got_error *err;
+ struct got_pathlist_entry *pe;
+
+ err = got_worktree_resolve_path(path, worktree, p);
+ if (err == NULL)
+ err = got_pathlist_insert(&pe, head, *path, NULL);
+ return err;
+}
+
+static const struct got_error *
+can_rm(void *arg, unsigned char status, unsigned char staged_status,
+ const char *path, struct got_object_id *blob_id,
+ struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
+ int dirfd, const char *de_name)
+{
+ if (status == GOT_STATUS_NONEXISTENT)
+ return got_error_set_errno(ENOENT, path);
+ if (status != GOT_STATUS_NO_CHANGE &&
+ status != GOT_STATUS_ADD &&
+ status != GOT_STATUS_MODIFY &&
+ status != GOT_STATUS_MODE_CHANGE)
+ return got_error_path(path, GOT_ERR_FILE_STATUS);
+ if (staged_status == GOT_STATUS_DELETE)
+ return got_error_path(path, GOT_ERR_FILE_STATUS);
+ return NULL;
+}
+
+static const struct got_error *
+can_add(void *arg, unsigned char status, unsigned char staged_status,
+ const char *path, struct got_object_id *blob_id,
+ struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
+ int dirfd, const char *de_name)
+{
+ if (status != GOT_STATUS_NONEXISTENT)
+ return got_error_path(path, GOT_ERR_FILE_STATUS);
+ return NULL;
+}
+
+static const struct got_error *
+can_edit(void *arg, unsigned char status, unsigned char staged_status,
+ const char *path, struct got_object_id *blob_id,
+ struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
+ int dirfd, const char *de_name)
+{
+ if (status == GOT_STATUS_NONEXISTENT)
+ return got_error_set_errno(ENOENT, path);
+ if (status != GOT_STATUS_NO_CHANGE &&
+ status != GOT_STATUS_ADD &&
+ status != GOT_STATUS_MODIFY)
+ return got_error_path(path, GOT_ERR_FILE_STATUS);
+ if (staged_status == GOT_STATUS_DELETE)
+ return got_error_path(path, GOT_ERR_FILE_STATUS);
+ return NULL;
+}
+
+static const struct got_error *
+check_file_status(struct got_patch *p, int file_renamed,
+ struct got_worktree *worktree, struct got_repository *repo,
+ struct got_pathlist_head *old, struct got_pathlist_head *new,
+ got_cancel_cb cancel_cb, void *cancel_arg)
+{
+ static const struct got_error *err;
+
+ if (p->old != NULL && p->new == NULL)
+ return got_worktree_status(worktree, old, repo, 0,
+ can_rm, NULL, cancel_cb, cancel_arg);
+ else if (file_renamed) {
+ err = got_worktree_status(worktree, old, repo, 0,
+ can_rm, NULL, cancel_cb, cancel_arg);
+ if (err)
+ return err;
+ return got_worktree_status(worktree, new, repo, 0,
+ can_add, NULL, cancel_cb, cancel_arg);
+ } else if (p->old == NULL)
+ return got_worktree_status(worktree, new, repo, 0,
+ can_add, NULL, cancel_cb, cancel_arg);
+ else
+ return got_worktree_status(worktree, new, repo, 0,
+ can_edit, NULL, cancel_cb, cancel_arg);
+}
+
+static const struct got_error *
apply_patch(struct got_worktree *worktree, struct got_repository *repo,
struct got_patch *p, got_worktree_delete_cb delete_cb, void *delete_arg,
- got_worktree_checkout_cb add_cb, void *add_arg)
+ got_worktree_checkout_cb add_cb, void *add_arg, got_cancel_cb cancel_cb,
+ void *cancel_arg)
{
const struct got_error *err = NULL;
+ struct got_pathlist_head oldpaths, newpaths;
int file_renamed = 0;
char *oldpath = NULL, *newpath = NULL;
char *tmppath = NULL, *template = NULL;
FILE *tmp = NULL;
- err = got_worktree_resolve_path(&oldpath, worktree,
- p->old != NULL ? p->old : p->new);
+ TAILQ_INIT(&oldpaths);
+ TAILQ_INIT(&newpaths);
+
+ err = build_pathlist(p->old != NULL ? p->old : p->new, &oldpath,
+ &oldpaths, worktree);
if (err)
goto done;
- err = got_worktree_resolve_path(&newpath, worktree,
- p->new != NULL ? p->new : p->old);
+ err = build_pathlist(p->new != NULL ? p->new : p->old, &newpath,
+ &newpaths, worktree);
if (err)
goto done;
+ if (p->old != NULL && p->new != NULL && strcmp(p->old, p->new))
+ file_renamed = 1;
+
+ err = check_file_status(p, file_renamed, worktree, repo, &oldpaths,
+ &newpaths, cancel_cb, cancel_arg);
+ if (err)
+ goto done;
+
if (p->old != NULL && p->new == NULL) {
/*
* special case: delete a file. don't try to match
* the lines but just schedule the removal.
*/
- err = schedule_del(p->old, worktree, repo, delete_cb,
- delete_arg);
+ err = got_worktree_schedule_delete(worktree, &oldpaths,
+ 0, NULL, delete_cb, delete_arg, repo, 0, 0);
goto done;
}
goto done;
}
- file_renamed = p->old != NULL && strcmp(p->old, p->new);
if (file_renamed) {
- err = schedule_del(oldpath, worktree, repo, delete_cb,
- delete_arg);
+ err = got_worktree_schedule_delete(worktree, &oldpaths,
+ 0, NULL, delete_cb, delete_arg, repo, 0, 0);
if (err == NULL)
- err = schedule_add(newpath, worktree, repo,
- add_cb, add_arg);
+ err = got_worktree_schedule_add(worktree, &newpaths,
+ add_cb, add_arg, repo, 1);
} else if (p->old == NULL)
- err = schedule_add(newpath, worktree, repo, add_cb,
- add_arg);
+ err = got_worktree_schedule_add(worktree, &newpaths,
+ add_cb, add_arg, repo, 1);
else
printf("M %s\n", oldpath); /* XXX */
done:
- if (err != NULL && (file_renamed || p->old == NULL))
+ if (err != NULL && newpath != NULL && (file_renamed || p->old == NULL))
unlink(newpath);
free(template);
if (tmppath != NULL)
unlink(tmppath);
free(tmppath);
+ got_pathlist_free(&oldpaths);
+ got_pathlist_free(&newpaths);
free(oldpath);
free(newpath);
return err;
const struct got_error *
got_patch(int fd, struct got_worktree *worktree, struct got_repository *repo,
- int nop, got_worktree_delete_cb delete_cb, void *delete_arg,
+ got_worktree_delete_cb delete_cb, void *delete_arg,
got_worktree_checkout_cb add_cb, void *add_arg, got_cancel_cb cancel_cb,
void *cancel_arg)
{
blob - 3c32d4b102c5f846f81ff306d31409388f56973a
blob + 9a300a816a79b7eacef07263e7bd7cbce706ae25
--- regress/cmdline/patch.sh
+++ regress/cmdline/patch.sh
test_done $testroot $ret
return 1
fi
+ rm $testroot/wt/eta
cat <<EOF > $testroot/wt/patch
--- alpha
fi
test_done $testroot $ret
}
+
+test_patch_illegal_status() {
+ local testroot=`test_init patch_illegal_status`
+
+ got checkout $testroot/repo $testroot/wt > /dev/null
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ test_done $testroot $ret
+ return 1
+ fi
+
+ # edit an non-existent and unknown file
+ cat <<EOF > $testroot/wt/patch
+--- iota
++++ iota
+@@ -1 +1 @@
+- iota
++ IOTA
+EOF
+ (cd $testroot/wt && got patch patch) > /dev/null \
+ 2> $testroot/stderr
+ ret=$?
+ if [ $ret -eq 0 ]; then
+ echo "edited a missing file" >&2
+ test_done $testroot $ret
+ return 1
+ fi
+
+ echo "got: iota: No such file or directory" \
+ > $testroot/stderr.expected
+ cmp -s $testroot/stderr.expected $testroot/stderr
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/stderr.expected $testroot/stderr
+ test_done $testroot $ret
+ return 1
+ fi
+
+ # create iota and re-try
+ echo iota > $testroot/wt/iota
+
+ (cd $testroot/wt && got patch patch) > /dev/null \
+ 2> $testroot/stderr
+ ret=$?
+ if [ $ret -eq 0 ]; then
+ echo "patched an unknown file" >&2
+ test_done $testroot $ret
+ return 1
+ fi
+
+ echo "got: iota: file has unexpected status" \
+ > $testroot/stderr.expected
+ cmp -s $testroot/stderr.expected $testroot/stderr
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/stderr.expected $testroot/stderr
+ test_done $testroot $ret
+ return 1
+ fi
+
+ rm $testroot/wt/iota
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ test_done $testroot $ret
+ return 1
+ fi
+
+ # edit obstructed file
+ rm $testroot/wt/alpha
+ mkdir $testroot/wt/alpha
+ cat <<EOF > $testroot/wt/patch
+--- alpha
++++ alpha
+@@ -1 +1,2 @@
+ alpha
++was edited
+EOF
+
+ (cd $testroot/wt && got patch patch) > /dev/null \
+ 2> $testroot/stderr
+ ret=$?
+ if [ $ret -eq 0 ]; then
+ echo "edited a missing file" >&2
+ test_done $testroot $ret
+ return 1
+ fi
+
+ echo "got: alpha: file has unexpected status" \
+ > $testroot/stderr.expected
+ cmp -s $testroot/stderr.expected $testroot/stderr
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/stderr.expected $testroot/stderr
+ test_done $testroot $ret
+ return 1
+ fi
+
+ # delete an unknown file
+ cat <<EOF > $testroot/wt/patch
+--- iota
++++ /dev/null
+@@ -1 +0,0 @@
+-iota
+EOF
+
+ (cd $testroot/wt && got patch patch) > /dev/null \
+ 2> $testroot/stderr
+ ret=$?
+ if [ $ret -eq 0 ]; then
+ echo "deleted a missing file?" >&2
+ test_done $testroot $ret
+ return 1
+ fi
+
+ echo "got: iota: No such file or directory" \
+ > $testroot/stderr.expected
+ cmp -s $testroot/stderr.expected $testroot/stderr
+ ret=$?
+ if [ $ret -eq 0 ]; then
+ diff -u $testroot/stderr.expected $testroot/stderr
+ test_done $testroot $ret
+ return 1
+ fi
+
+ # try again with iota in place but still not registered
+ echo iota > $testroot/wt/iota
+ (cd $testroot/wt && got patch patch) > /dev/null \
+ 2> $testroot/stderr
+ ret=$?
+ if [ $ret -eq 0 ]; then
+ echo "deleted an unversioned file?" >&2
+ test_done $testroot $ret
+ return 1
+ fi
+
+ echo "got: iota: file has unexpected status" \
+ > $testroot/stderr.expected
+ cmp -s $testroot/stderr.expected $testroot/stderr
+ ret=$?
+ if [ $ret -eq 0 ]; then
+ diff -u $testroot/stderr.expected $testroot/stderr
+ fi
+ test_done $testroot $ret
+}
+
test_parseargs "$@"
run_test test_patch_simple_add_file
run_test test_patch_simple_rm_file
run_test test_patch_no_patch
run_test test_patch_equals_for_context
run_test test_patch_rename
+run_test test_patch_illegal_status