commit - 423faaa61a622c043f5a2918f3a9183ae02408d4
commit + 6e96b3268181f675fcf264d5e682d86a15ce426a
blob - 64f2cb93558b933d2ffdcc0da7dedecf78d8ee52
blob + 11708ccf458de5506eccdad8dafcaaed715432bb
--- include/got_error.h
+++ include/got_error.h
#define GOT_ERR_PATCH_MALFORMED 147
#define GOT_ERR_PATCH_TRUNCATED 148
#define GOT_ERR_PATCH_DONT_APPLY 149
-#define GOT_ERR_PATCH_PATHS_DIFFER 150
-#define GOT_ERR_NO_PATCH 151
+#define GOT_ERR_NO_PATCH 150
static const struct got_error {
int code;
{ GOT_ERR_PATCH_MALFORMED, "malformed patch" },
{ GOT_ERR_PATCH_TRUNCATED, "patch truncated" },
{ GOT_ERR_PATCH_DONT_APPLY, "patch doesn't apply" },
- { GOT_ERR_PATCH_PATHS_DIFFER, "the paths mentioned in the patch "
- "are different." },
{ GOT_ERR_NO_PATCH, "no patch found" },
};
blob - 957d639a49780d6a41491cd6e9117b1783a6b61f
blob + 8dc632ef6f52371e65674a21a73f8c9d2496afd5
--- lib/patch.c
+++ lib/patch.c
}
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)
+schedule_add(const char *path, struct got_worktree *worktree,
+ struct got_repository *repo, got_worktree_checkout_cb add_cb,
+ void *add_arg)
{
- const struct got_error *err = NULL;
+ static const struct got_error *err = NULL;
struct got_pathlist_head paths;
struct got_pathlist_entry *pe;
- char *path = NULL, *tmppath = NULL, *template = NULL;
- FILE *orig = NULL, *tmp = NULL;
- struct got_patch_hunk *h;
- size_t i;
- long lineno = 0;
- off_t copypos, pos;
- char *line = NULL;
- size_t linesize = 0;
- ssize_t linelen;
TAILQ_INIT(&paths);
- err = got_worktree_resolve_path(&path, worktree,
- p->new != NULL ? p->new : p->old);
- if (err)
- return err;
err = got_pathlist_insert(&pe, &paths, path, NULL);
- if (err)
- goto done;
+ if (err == NULL)
+ err = got_worktree_schedule_add(worktree, &paths,
+ add_cb, add_arg, repo, 1);
+ got_pathlist_free(&paths);
+ return err;
+}
- if (p->old != NULL && p->new == NULL) {
- /*
- * special case: delete a file. don't try to match
- * the lines but just schedule the removal.
- */
+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);
- goto done;
- } else if (p->old != NULL && strcmp(p->old, p->new)) {
- err = got_error(GOT_ERR_PATCH_PATHS_DIFFER);
- goto done;
- }
+ got_pathlist_free(&paths);
+ return err;
+}
- if (asprintf(&template, "%s/got-patch",
- got_worktree_get_root_path(worktree)) == -1) {
- err = got_error_from_errno(template);
- goto done;
- }
+static const struct got_error *
+patch_file(struct got_patch *p, const char *path, FILE *tmp)
+{
+ const struct got_error *err = NULL;
+ struct got_patch_hunk *h;
+ size_t i;
+ long lineno = 0;
+ FILE *orig;
+ off_t copypos, pos;
+ char *line = NULL;
+ size_t linesize = 0;
+ ssize_t linelen;
- err = got_opentemp_named(&tmppath, &tmp, template);
- if (err)
- goto done;
-
if (p->old == NULL) { /* create */
h = STAILQ_FIRST(&p->head);
- if (h == NULL || STAILQ_NEXT(h, entries) != NULL) {
- err = got_error(GOT_ERR_PATCH_MALFORMED);
- goto done;
- }
+ if (h == NULL || STAILQ_NEXT(h, entries) != NULL)
+ return got_error(GOT_ERR_PATCH_MALFORMED);
for (i = 0; i < h->len; ++i) {
- if (fprintf(tmp, "%s", h->lines[i]+1) < 0) {
- err = got_error_from_errno("fprintf");
- goto done;
- }
+ if (fprintf(tmp, "%s", h->lines[i]+1) < 0)
+ return got_error_from_errno("fprintf");
}
- goto rename;
+ return err;
}
if ((orig = fopen(path, "r")) == NULL) {
copypos = 0;
STAILQ_FOREACH(h, &p->head, entries) {
+ if (h->lines == NULL)
+ break;
+
tryagain:
err = locate_hunk(orig, h, &pos, &lineno);
if (err != NULL)
}
}
- if (!feof(orig)) {
+ if (!feof(orig))
err = copy(tmp, orig, copypos, -1);
- if (err)
- goto done;
+
+done:
+ if (orig != NULL)
+ fclose(orig);
+ return err;
+}
+
+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)
+{
+ const struct got_error *err = NULL;
+ 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);
+ if (err)
+ goto done;
+
+ err = got_worktree_resolve_path(&newpath, worktree,
+ p->new != NULL ? p->new : p->old);
+ 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);
+ goto done;
}
-rename:
- if (rename(tmppath, path) == -1) {
- err = got_error_from_errno3("rename", tmppath, path);
+ if (asprintf(&template, "%s/got-patch",
+ got_worktree_get_root_path(worktree)) == -1) {
+ err = got_error_from_errno(template);
goto done;
}
- if (p->old == NULL)
- err = got_worktree_schedule_add(worktree, &paths,
- add_cb, add_arg, repo, 1);
+ err = got_opentemp_named(&tmppath, &tmp, template);
+ if (err)
+ goto done;
+ err = patch_file(p, oldpath, tmp);
+ if (err)
+ goto done;
+
+ if (rename(tmppath, newpath) == -1) {
+ err = got_error_from_errno3("rename", tmppath, newpath);
+ 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);
+ if (err == NULL)
+ err = schedule_add(newpath, worktree, repo,
+ add_cb, add_arg);
+ } else if (p->old == NULL)
+ err = schedule_add(newpath, worktree, repo, add_cb,
+ add_arg);
else
- printf("M %s\n", path); /* XXX */
+ printf("M %s\n", oldpath); /* XXX */
+
done:
+ if (err != NULL && (file_renamed || p->old == NULL))
+ unlink(newpath);
free(template);
- if (err != NULL && p->old == NULL && path != NULL)
- unlink(path);
- if (tmp != NULL)
- fclose(tmp);
if (tmppath != NULL)
unlink(tmppath);
free(tmppath);
- if (orig != NULL) {
- if (p->old == NULL && err != NULL)
- unlink(path);
- fclose(orig);
- }
- free(path);
- free(line);
- got_pathlist_free(&paths);
+ free(oldpath);
+ free(newpath);
return err;
}
blob - 7a5ad3671fa62d02f74a21cc4f0edf7be252462a
blob + 5916921f260f5a0eaefdbf6036b813fbf3570dd5
--- regress/cmdline/patch.sh
+++ regress/cmdline/patch.sh
fi
test_done $testroot $ret
}
+
+test_patch_rename() {
+ local testroot=`test_init patch_rename`
+
+ got checkout $testroot/repo $testroot/wt > /dev/null
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ test_done $testroot $ret
+ return 1
+ fi
+
+ cat <<EOF > $testroot/wt/patch
+--- alpha
++++ eta
+@@ -0,0 +0,0 @@
+EOF
+ echo 'D alpha' > $testroot/stdout.expected
+ echo 'A eta' >> $testroot/stdout.expected
+
+ (cd $testroot/wt && got patch patch) > $testroot/stdout
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ test_done $testroot $ret
+ return 1
+ fi
+
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done $testroot $ret
+ return 1
+ fi
+
+ if [ -f $testroot/wt/alpha ]; then
+ echo "alpha was not removed" >&2
+ test_done $testroot 1
+ return 1
+ fi
+ if [ ! -f $testroot/wt/eta ]; then
+ echo "eta was not created" >&2
+ test_done $testroot 1
+ return 1
+ fi
+
+ echo alpha > $testroot/wt/eta.expected
+ cmp -s $testroot/wt/eta.expected $testroot/wt/eta
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/wt/eta.expected $testroot/wt/eta
+ test_done $testroot $ret
+ return 1
+ fi
+
+ # revert the changes and try again with a rename + edit
+ (cd $testroot/wt && got revert alpha eta) > /dev/null
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ test_done $testroot $ret
+ return 1
+ fi
+
+ cat <<EOF > $testroot/wt/patch
+--- alpha
++++ eta
+@@ -1 +1,2 @@
+ alpha
++but now is eta
+EOF
+
+ (cd $testroot/wt && got patch patch) > $testroot/stdout
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ test_done $testroot $ret
+ return 1
+ fi
+
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done $testroot $ret
+ return 1
+ fi
+
+ if [ -f $testroot/wt/alpha ]; then
+ echo "alpha was not removed" >&2
+ test_done $testroot 1
+ return 1
+ fi
+ if [ ! -f $testroot/wt/eta ]; then
+ echo "eta was not created" >&2
+ test_done $testroot 1
+ return 1
+ fi
+
+ echo alpha > $testroot/wt/eta.expected
+ echo 'but now is eta' >> $testroot/wt/eta.expected
+ cmp -s $testroot/wt/eta.expected $testroot/wt/eta
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/wt/eta.expected $testroot/wt/eta
+ 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_malformed
run_test test_patch_no_patch
run_test test_patch_equals_for_context
+run_test test_patch_rename