@matheusbsbs / matheus-bagliano / commits / c4593faf2d
Fork 0

apply: notice creation/removal patches produced by GNU diff

Unified context patch generated by GNU diff has UNIX epoch timestamp on the side that does not exist when the patch is about a creation or a deletion event. Notice this convention when reading a non-git diff. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jul 10, 2009 at 18:38 UTC c4593faf2df8f9c5e8ac409e40be991294a73e54
2 files changed +176 -1
builtin-apply.c
+81 -1
@@ -457,6 +457,76 @@ static int guess_p_value(const char *nameline)
457 return val;
458 }
459
460 +/*
461 + * Does the ---/+++ line has the POSIX timestamp after the last HT?
462 + * GNU diff puts epoch there to signal a creation/deletion event. Is
463 + * this such a timestamp?
464 + */
465 +static int has_epoch_timestamp(const char *nameline)
466 +{
467 + /*
468 + * We are only interested in epoch timestamp; any non-zero
469 + * fraction cannot be one, hence "(\.0+)?" in the regexp below.
470 + * For the same reason, the date must be either 1969-12-31 or
471 + * 1970-01-01, and the seconds part must be "00".
472 + */
473 + const char stamp_regexp[] =
474 + "^(1969-12-31|1970-01-01)"
475 + " "
476 + "[0-2][0-9]:[0-5][0-9]:00(\\.0+)?"
477 + " "
478 + "([-+][0-2][0-9][0-5][0-9])\n";
479 + const char *timestamp = NULL, *cp;
480 + static regex_t *stamp;
481 + regmatch_t m[10];
482 + int zoneoffset;
483 + int hourminute;
484 + int status;
485 +
486 + for (cp = nameline; *cp != '\n'; cp++) {
487 + if (*cp == '\t')
488 + timestamp = cp + 1;
489 + }
490 + if (!timestamp)
491 + return 0;
492 + if (!stamp) {
493 + stamp = xmalloc(sizeof(*stamp));
494 + if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) {
495 + warning("Cannot prepare timestamp regexp %s",
496 + stamp_regexp);
497 + return 0;
498 + }
499 + }
500 +
501 + status = regexec(stamp, timestamp, ARRAY_SIZE(m), m, 0);
502 + if (status) {
503 + if (status != REG_NOMATCH)
504 + warning("regexec returned %d for input: %s",
505 + status, timestamp);
506 + return 0;
507 + }
508 +
509 + zoneoffset = strtol(timestamp + m[3].rm_so + 1, NULL, 10);
510 + zoneoffset = (zoneoffset / 100) * 60 + (zoneoffset % 100);
511 + if (timestamp[m[3].rm_so] == '-')
512 + zoneoffset = -zoneoffset;
513 +
514 + /*
515 + * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31
516 + * (west of GMT) or 1970-01-01 (east of GMT)
517 + */
518 + if ((zoneoffset < 0 && memcmp(timestamp, "1969-12-31", 10)) ||
519 + (0 <= zoneoffset && memcmp(timestamp, "1970-01-01", 10)))
520 + return 0;
521 +
522 + hourminute = (strtol(timestamp + 11, NULL, 10) * 60 +
523 + strtol(timestamp + 14, NULL, 10) -
524 + zoneoffset);
525 +
526 + return ((zoneoffset < 0 && hourminute == 1440) ||
527 + (0 <= zoneoffset && !hourminute));
528 +}
529 +
530 /*
531 * Get the name etc info from the ---/+++ lines of a traditional patch header
532 *
@@ -493,7 +563,17 @@ static void parse_traditional_patch(const char *first, const char *second, struc
563 } else {
564 name = find_name(first, NULL, p_value, TERM_SPACE | TERM_TAB);
565 name = find_name(second, name, p_value, TERM_SPACE | TERM_TAB);
496 - patch->old_name = patch->new_name = name;
566 + if (has_epoch_timestamp(first)) {
567 + patch->is_new = 1;
568 + patch->is_delete = 0;
569 + patch->new_name = name;
570 + } else if (has_epoch_timestamp(second)) {
571 + patch->is_new = 0;
572 + patch->is_delete = 1;
573 + patch->old_name = name;
574 + } else {
575 + patch->old_name = patch->new_name = name;
576 + }
577 }
578 if (!name)
579 die("unable to find filename in patch at line %d", linenr);
t/t4132-apply-removal.sh new
+95
@@ -0,0 +1,95 @@
1 +#!/bin/sh
2 +#
3 +# Copyright (c) 2009 Junio C Hamano
4 +
5 +test_description='git-apply notices removal patches generated by GNU diff'
6 +
7 +. ./test-lib.sh
8 +
9 +test_expect_success setup '
10 + cat <<-EOF >c &&
11 + diff -ruN a/file b/file
12 + --- a/file TS0
13 + +++ b/file TS1
14 + @@ -0,0 +1 @@
15 + +something
16 + EOF
17 +
18 + cat <<-EOF >d &&
19 + diff -ruN a/file b/file
20 + --- a/file TS0
21 + +++ b/file TS1
22 + @@ -1 +0,0 @@
23 + -something
24 + EOF
25 +
26 + timeWest="1982-09-16 07:00:00.000000000 -0800" &&
27 + timeGMT="1982-09-16 15:00:00.000000000 +0000" &&
28 + timeEast="1982-09-17 00:00:00.000000000 +0900" &&
29 +
30 + epocWest="1969-12-31 16:00:00.000000000 -0800" &&
31 + epocGMT="1970-01-01 00:00:00.000000000 +0000" &&
32 + epocEast="1970-01-01 09:00:00.000000000 +0900" &&
33 +
34 + sed -e "s/TS0/$epocWest/" -e "s/TS1/$timeWest/" <c >createWest.patch &&
35 + sed -e "s/TS0/$epocEast/" -e "s/TS1/$timeEast/" <c >createEast.patch &&
36 + sed -e "s/TS0/$epocGMT/" -e "s/TS1/$timeGMT/" <c >createGMT.patch &&
37 +
38 + sed -e "s/TS0/$timeWest/" -e "s/TS1/$timeWest/" <c >addWest.patch &&
39 + sed -e "s/TS0/$timeEast/" -e "s/TS1/$timeEast/" <c >addEast.patch &&
40 + sed -e "s/TS0/$timeGMT/" -e "s/TS1/$timeGMT/" <c >addGMT.patch &&
41 +
42 + sed -e "s/TS0/$timeWest/" -e "s/TS1/$timeWest/" <d >emptyWest.patch &&
43 + sed -e "s/TS0/$timeEast/" -e "s/TS1/$timeEast/" <d >emptyEast.patch &&
44 + sed -e "s/TS0/$timeGMT/" -e "s/TS1/$timeGMT/" <d >emptyGMT.patch &&
45 +
46 + sed -e "s/TS0/$timeWest/" -e "s/TS1/$epocWest/" <d >removeWest.patch &&
47 + sed -e "s/TS0/$timeEast/" -e "s/TS1/$epocEast/" <d >removeEast.patch &&
48 + sed -e "s/TS0/$timeGMT/" -e "s/TS1/$epocGMT/" <d >removeGMT.patch &&
49 +
50 + echo something >something &&
51 + >empty
52 +'
53 +
54 +for patch in *.patch
55 +do
56 + test_expect_success "test $patch" '
57 + rm -f file .git/index &&
58 + case "$patch" in
59 + create*)
60 + # must be able to create
61 + git apply --index $patch &&
62 + test_cmp file something &&
63 + # must notice the file is already there
64 + >file &&
65 + git add file &&
66 + test_must_fail git apply $patch
67 + ;;
68 + add*)
69 + # must be able to create or patch
70 + git apply $patch &&
71 + test_cmp file something &&
72 + >file &&
73 + git apply $patch &&
74 + test_cmp file something
75 + ;;
76 + empty*)
77 + # must leave an empty file
78 + cat something >file &&
79 + git add file &&
80 + git apply --index $patch &&
81 + test -f file &&
82 + test_cmp empty file
83 + ;;
84 + remove*)
85 + # must remove the file
86 + cat something >file &&
87 + git add file &&
88 + git apply --index $patch &&
89 + ! test -f file
90 + ;;
91 + esac
92 + '
93 +done
94 +
95 +test_done