commit - 5dffb1a14a4e706b322f648797d0fea44e684aa8
commit + a92a20426c3078772e9cc8671b4c58ed9a07b9bf
blob - 5f7f00007937a048564e685aac0a8c6b9e98adab
blob + 4e69daf4498d68765b5db8f7264410bc4a6d7b41
--- got/got.c
+++ got/got.c
patch_progress(void *arg, const char *old, const char *new,
unsigned char status, const struct got_error *error, long old_from,
long old_lines, long new_from, long new_lines, long offset,
- const struct got_error *hunk_err)
+ int ws_mangled, const struct got_error *hunk_err)
{
const char *path = new == NULL ? old : new;
if (error != NULL)
fprintf(stderr, "%s: %s\n", getprogname(), error->msg);
- if (offset != 0 || hunk_err != NULL) {
+ if (offset != 0 || hunk_err != NULL || ws_mangled) {
printf("@@ -%ld,%ld +%ld,%ld @@ ", old_from,
old_lines, new_from, new_lines);
if (hunk_err != NULL)
printf("%s\n", hunk_err->msg);
- else
+ else if (offset != 0)
printf("applied with offset %ld\n", offset);
+ else
+ printf("hunk contains mangled whitespace\n");
}
return NULL;
blob - bc2e95bb4fa535617bc28f76935a78e36dea8a3e
blob + e72ded72ccf8311606f1c945e5241311de5cc179
--- include/got_patch.h
+++ include/got_patch.h
*/
typedef const struct got_error *(*got_patch_progress_cb)(void *,
const char *, const char *, unsigned char, const struct got_error *,
- long, long, long, long, long, const struct got_error *);
+ long, long, long, long, long, int, const struct got_error *);
/*
* Apply the (already opened) patch to the repository and register the
blob - 2cd5efa8a0d460c9850691dc1feb2a9f38ad1927
blob + f12c747f214e8e4ae165a7c3b1b15212bfdfa531
--- lib/patch.c
+++ lib/patch.c
#include <sys/stat.h>
#include <sys/uio.h>
+#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <sha1.h>
struct got_patch_hunk {
STAILQ_ENTRY(got_patch_hunk) entries;
const struct got_error *err;
+ int ws_mangled;
int offset;
int old_nonl;
int new_nonl;
free(line);
return err;
+}
+
+static int
+linecmp(const char *a, const char *b, int *mangled)
+{
+ int c;
+
+ *mangled = 0;
+ c = strcmp(a, b);
+ if (c == 0)
+ return c;
+
+ *mangled = 1;
+ for (;;) {
+ while (isspace(*a))
+ a++;
+ while (isspace(*b))
+ b++;
+ if (*a == '\0' || *b == '\0' || *a != *b)
+ break;
+ a++, b++;
+ }
+
+ return *a - *b;
}
static const struct got_error *
char *line = NULL;
size_t linesize = 0, i = 0;
ssize_t linelen;
+ int mangled;
for (i = 0; i < h->len; ++i) {
switch (*h->lines[i]) {
}
if (line[linelen - 1] == '\n')
line[linelen - 1] = '\0';
- if (strcmp(h->lines[i] + 1, line)) {
+ if (linecmp(h->lines[i] + 1, line, &mangled)) {
err = got_error(GOT_ERR_HUNK_FAILED);
goto done;
}
+ if (mangled)
+ h->ws_mangled = 1;
break;
}
}
}
static const struct got_error *
-apply_hunk(FILE *tmp, struct got_patch_hunk *h, int *lineno)
+apply_hunk(FILE *orig, FILE *tmp, struct got_patch_hunk *h, int *lineno,
+ off_t from)
{
- size_t i, new = 0;
+ const struct got_error *err = NULL;
+ const char *t;
+ size_t linesize = 0, i, new = 0;
+ char *line = NULL;
+ char mode;
+ ssize_t linelen;
+ if (orig != NULL && fseeko(orig, from, SEEK_SET) == -1)
+ return got_error_from_errno("fseeko");
+
for (i = 0; i < h->len; ++i) {
- switch (*h->lines[i]) {
- case ' ':
- if (fprintf(tmp, "%s\n", h->lines[i] + 1) < 0)
- return got_error_from_errno("fprintf");
- /* fallthrough */
+ switch (mode = *h->lines[i]) {
case '-':
+ case ' ':
(*lineno)++;
+ if (orig != NULL) {
+ linelen = getline(&line, &linesize, orig);
+ if (linelen == -1) {
+ err = got_error_from_errno("getline");
+ goto done;
+ }
+ if (line[linelen - 1] == '\n')
+ line[linelen - 1] = '\0';
+ t = line;
+ } else
+ t = h->lines[i] + 1;
+ if (mode == '-')
+ continue;
+ if (fprintf(tmp, "%s\n", t) < 0) {
+ err = got_error_from_errno("fprintf");
+ goto done;
+ }
break;
case '+':
new++;
- if (fprintf(tmp, "%s", h->lines[i] + 1) < 0)
- return got_error_from_errno("fprintf");
+ if (fprintf(tmp, "%s", h->lines[i] + 1) < 0) {
+ err = got_error_from_errno("fprintf");
+ goto done;
+ }
if (new != h->new_lines || !h->new_nonl) {
- if (fprintf(tmp, "\n") < 0)
- return got_error_from_errno(
- "fprintf");
+ if (fprintf(tmp, "\n") < 0) {
+ err = got_error_from_errno("fprintf");
+ goto done;
+ }
}
break;
}
}
- return NULL;
+
+done:
+ free(line);
+ return err;
}
static const struct got_error *
h = STAILQ_FIRST(&p->head);
if (h == NULL || STAILQ_NEXT(h, entries) != NULL)
return got_error(GOT_ERR_PATCH_MALFORMED);
- return apply_hunk(tmp, h, &lineno);
+ return apply_hunk(orig, tmp, h, &lineno, 0);
}
if (fstat(fileno(orig), &sb) == -1)
if (lineno + 1 != h->old_from)
h->offset = lineno + 1 - h->old_from;
- err = apply_hunk(tmp, h, &lineno);
+ err = apply_hunk(orig, tmp, h, &lineno, pos);
if (err != NULL)
return err;
struct got_patch_hunk *h;
err = pa->progress_cb(pa->progress_arg, old, new, status,
- orig_error, 0, 0, 0, 0, 0, NULL);
+ orig_error, 0, 0, 0, 0, 0, 0, NULL);
if (err)
return err;
STAILQ_FOREACH(h, pa->head, entries) {
- if (h->offset == 0 && h->err == NULL)
+ if (h->offset == 0 && !h->ws_mangled && h->err == NULL)
continue;
err = pa->progress_cb(pa->progress_arg, old, new, 0, NULL,
h->old_from, h->old_lines, h->new_from, h->new_lines,
- h->offset, h->err);
+ h->offset, h->ws_mangled, h->err);
if (err)
return err;
}
blob - c16e62c4b053ddda85d0a0c0b1761790032b168c
blob + 7901777201377549e2dce265cf173b09d716ee4d
--- regress/cmdline/patch.sh
+++ regress/cmdline/patch.sh
diff -u $testroot/stderr.expected $testroot/stderr
fi
test_done $testroot 0
+}
+
+test_patch_whitespace() {
+ local testroot=`test_init patch_whitespace`
+
+ got checkout $testroot/repo $testroot/wt > /dev/null
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ test_done $testroot $ret
+ return 1
+ fi
+
+ trailing=" "
+
+ cat <<EOF > $testroot/wt/hello.c
+#include <stdio.h>
+
+int
+main(void)
+{
+ /* the trailing whitespace is on purpose */
+ printf("hello, world\n");$trailing
+ return 0;
+}
+EOF
+
+ (cd $testroot/wt && got add hello.c && got ci -m '+hello.c') \
+ > /dev/null
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ test_done $testroot $ret
+ return 1
+ fi
+
+ # test with a diff with various whitespace corruptions
+ cat <<EOF > $testroot/wt/patch
+--- hello.c
++++ hello.c
+@@ -5,5 +5,5 @@
+ {
+ /* the trailing whitespace is on purpose */
+ printf("hello, world\n");
+- return 0;
++ return 5; /* always fails */
+ }
+EOF
+
+ (cd $testroot/wt && got patch patch) \
+ 2>$testroot/stderr >$testroot/stdout
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "failed to apply diff" >&2
+ test_done $testroot $ret
+ return 1
+ fi
+
+ echo 'M hello.c' > $testroot/stdout.expected
+ echo '@@ -5,5 +5,5 @@ hunk contains mangled whitespace' \
+ >> $testroot/stdout.expected
+ 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
+
+ cat <<EOF > $testroot/wt/hello.c.expected
+#include <stdio.h>
+
+int
+main(void)
+{
+ /* the trailing whitespace is on purpose */
+ printf("hello, world\n");$trailing
+ return 5; /* always fails */
+}
+EOF
+
+ cmp -s $testroot/wt/hello.c.expected $testroot/wt/hello.c
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/wt/hello.c.expected $testroot/wt/hello.c
+ fi
+ test_done $testroot $ret
}
test_patch_relative_paths() {
run_test test_patch_prefer_new_path
run_test test_patch_no_newline
run_test test_patch_strip
+run_test test_patch_whitespace
run_test test_patch_relative_paths
run_test test_patch_with_path_prefix
run_test test_patch_relpath_with_path_prefix