vcs-svn: Implement Prop-delta handling
The rules for what file is used as delta source for each file are not documented in dump-load-format.txt. Luckily, the Apache Software Foundation repository has rich enough examples to figure out most of the rules: Node-action: replace implies the empty property set and empty text as preimage for deltas. Otherwise, if a copyfrom source is given, that node is the preimage for deltas. Lastly, if none of the above applies and the node path exists in the current revision, then that version forms the basis. [jn: refactored, with tests] Signed-off-by: David Barr <david.barr@cordelta.com> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
David Barr committed
Nov 19, 2010 at 18:57 UTC
6b01b67658e2905b550739f1aee56a00911ca13c
2 files changed
+144
-12
t/t9010-svn-fe.sh
+100
-2
@@ -514,7 +514,12 @@ test_expect_success 'deltas not supported' '
514
test_must_fail test-svn-fe delta.dump
515
'
516
517
-test_expect_success 'property deltas not supported' '
517
+test_expect_success 'property deltas supported' '
518
+ reinit_git &&
519
+ cat >expect <<-\EOF &&
520
+ OBJID
521
+ :100755 100644 OBJID OBJID M script.sh
522
+ EOF
523
{
524
properties \
525
svn:author author@example.com \
@@ -565,7 +570,100 @@ test_expect_success 'property deltas not supported' '
570
PROPS-END
571
EOF
572
} >propdelta.dump &&
568
- test_must_fail test-svn-fe propdelta.dump
573
+ test-svn-fe propdelta.dump >stream &&
574
+ git fast-import <stream &&
575
+ {
576
+ git rev-list HEAD |
577
+ git diff-tree --stdin |
578
+ sed "s/$_x40/OBJID/g"
579
+ } >actual &&
580
+ test_cmp expect actual
581
+'
582
+
583
+test_expect_success 'deltas for typechange' '
584
+ reinit_git &&
585
+ cat >expect <<-\EOF &&
586
+ OBJID
587
+ :120000 100644 OBJID OBJID T test-file
588
+ OBJID
589
+ :100755 120000 OBJID OBJID T test-file
590
+ OBJID
591
+ :000000 100755 OBJID OBJID A test-file
592
+ EOF
593
+ cat >deleteprop.dump <<-\EOF &&
594
+ SVN-fs-dump-format-version: 3
595
+
596
+ Revision-number: 1
597
+ Prop-content-length: 10
598
+ Content-length: 10
599
+
600
+ PROPS-END
601
+
602
+ Node-path: test-file
603
+ Node-kind: file
604
+ Node-action: add
605
+ Prop-delta: true
606
+ Prop-content-length: 35
607
+ Text-content-length: 17
608
+ Content-length: 52
609
+
610
+ K 14
611
+ svn:executable
612
+ V 0
613
+
614
+ PROPS-END
615
+ link testing 123
616
+
617
+ Revision-number: 2
618
+ Prop-content-length: 10
619
+ Content-length: 10
620
+
621
+ PROPS-END
622
+
623
+ Node-path: test-file
624
+ Node-kind: file
625
+ Node-action: change
626
+ Prop-delta: true
627
+ Prop-content-length: 53
628
+ Text-content-length: 17
629
+ Content-length: 70
630
+
631
+ K 11
632
+ svn:special
633
+ V 1
634
+ *
635
+ D 14
636
+ svn:executable
637
+ PROPS-END
638
+ link testing 231
639
+
640
+ Revision-number: 3
641
+ Prop-content-length: 10
642
+ Content-length: 10
643
+
644
+ PROPS-END
645
+
646
+ Node-path: test-file
647
+ Node-kind: file
648
+ Node-action: change
649
+ Prop-delta: true
650
+ Prop-content-length: 27
651
+ Text-content-length: 17
652
+ Content-length: 44
653
+
654
+ D 11
655
+ svn:special
656
+ PROPS-END
657
+ link testing 321
658
+ EOF
659
+ test-svn-fe deleteprop.dump >stream &&
660
+ git fast-import <stream &&
661
+ {
662
+ git rev-list HEAD |
663
+ git diff-tree --root --stdin |
664
+ sed "s/$_x40/OBJID/g"
665
+ } >actual &&
666
+ test_cmp expect actual
667
'
668
669
test_expect_success 't9135/svn.dump' '
vcs-svn/svndump.c
+44
-10
@@ -115,20 +115,35 @@ static void init_keys(void)
115
keys.prop_delta = pool_intern("Prop-delta");
116
}
117
118
-static void handle_property(uint32_t key, const char *val, uint32_t len)
118
+static void handle_property(uint32_t key, const char *val, uint32_t len,
119
+ uint32_t *type_set)
120
{
121
if (key == keys.svn_log) {
122
+ if (!val)
123
+ die("invalid dump: unsets svn:log");
124
/* Value length excludes terminating nul. */
125
rev_ctx.log = log_copy(len + 1, val);
126
} else if (key == keys.svn_author) {
127
rev_ctx.author = pool_intern(val);
128
} else if (key == keys.svn_date) {
129
+ if (!val)
130
+ die("invalid dump: unsets svn:date");
131
if (parse_date_basic(val, &rev_ctx.timestamp, NULL))
127
- fprintf(stderr, "Invalid timestamp: %s\n", val);
128
- } else if (key == keys.svn_executable) {
129
- node_ctx.type = REPO_MODE_EXE;
130
- } else if (key == keys.svn_special) {
131
- node_ctx.type = REPO_MODE_LNK;
132
+ warning("invalid timestamp: %s", val);
133
+ } else if (key == keys.svn_executable || key == keys.svn_special) {
134
+ if (*type_set) {
135
+ if (!val)
136
+ return;
137
+ die("invalid dump: sets type twice");
138
+ }
139
+ if (!val) {
140
+ node_ctx.type = REPO_MODE_BLB;
141
+ return;
142
+ }
143
+ *type_set = 1;
144
+ node_ctx.type = key == keys.svn_executable ?
145
+ REPO_MODE_EXE :
146
+ REPO_MODE_LNK;
147
}
148
}
149
@@ -136,6 +151,19 @@ static void read_props(void)
151
{
152
uint32_t key = ~0;
153
const char *t;
154
+ /*
155
+ * NEEDSWORK: to support simple mode changes like
156
+ * K 11
157
+ * svn:special
158
+ * V 1
159
+ * *
160
+ * D 14
161
+ * svn:executable
162
+ * we keep track of whether a mode has been set and reset to
163
+ * plain file only if not. We should be keeping track of the
164
+ * symlink and executable bits separately instead.
165
+ */
166
+ uint32_t type_set = 0;
167
while ((t = buffer_read_line()) && strcmp(t, "PROPS-END")) {
168
uint32_t len;
169
const char *val;
@@ -151,8 +179,13 @@ static void read_props(void)
179
case 'K':
180
key = pool_intern(val);
181
continue;
182
+ case 'D':
183
+ key = pool_intern(val);
184
+ val = NULL;
185
+ len = 0;
186
+ /* fall through */
187
case 'V':
155
- handle_property(key, val, len);
188
+ handle_property(key, val, len, &type_set);
189
key = ~0;
190
continue;
191
default:
@@ -167,8 +200,8 @@ static void handle_node(void)
200
const uint32_t type = node_ctx.type;
201
const int have_props = node_ctx.propLength != LENGTH_UNKNOWN;
202
170
- if (node_ctx.text_delta || node_ctx.prop_delta)
171
- die("text and property deltas not supported");
203
+ if (node_ctx.text_delta)
204
+ die("text deltas not supported");
205
if (node_ctx.textLength != LENGTH_UNKNOWN)
206
mark = next_blob_mark();
207
if (node_ctx.action == NODEACT_DELETE) {
@@ -206,7 +239,8 @@ static void handle_node(void)
239
}
240
if (have_props) {
241
const uint32_t old_mode = node_ctx.type;
209
- node_ctx.type = type;
242
+ if (!node_ctx.prop_delta)
243
+ node_ctx.type = type;
244
if (node_ctx.propLength)
245
read_props();
246
if (node_ctx.type != old_mode)