87
/* Get the empty file associated with the edit baton. This is cached so
88
* that it can be reused, all empty files are the same.
91
get_empty_file(struct diff_baton *eb,
92
const char **empty_file,
93
apr_pool_t *scratch_pool)
95
/* Create the file if it does not exist */
96
/* Note that we tried to use /dev/null in r857294, but
97
that won't work on Windows: it's impossible to stat NUL */
100
SVN_ERR(svn_io_open_unique_file3(NULL, &eb->empty_file, NULL,
101
svn_io_file_del_on_pool_cleanup,
102
eb->pool, scratch_pool));
105
*empty_file = eb->empty_file;
111
/* Return the value of the svn:mime-type property held in PROPS, or NULL
112
if no such property exists. */
114
get_prop_mimetype(apr_hash_t *props)
116
return svn_prop_get_value(props, SVN_PROP_MIME_TYPE);
120
/* Diff the file PATH against its text base. At this
121
* stage we are dealing with a file that does exist in the working copy.
123
* DIR_BATON is the parent directory baton, PATH is the path to the file to
126
* Do all allocation in POOL.
128
* ### TODO: Need to work on replace if the new filename used to be a
132
file_diff(struct diff_baton *eb,
133
const char *local_abspath,
135
apr_pool_t *scratch_pool)
137
svn_wc__db_t *db = eb->db;
138
const char *empty_file;
139
const char *original_repos_relpath;
140
svn_wc__db_status_t status;
141
svn_wc__db_kind_t kind;
142
svn_revnum_t revision;
143
const svn_checksum_t *checksum;
144
svn_boolean_t op_root;
145
svn_boolean_t had_props, props_mod;
146
svn_boolean_t have_base, have_more_work;
147
svn_boolean_t replaced = FALSE;
148
svn_boolean_t base_replace = FALSE;
149
svn_wc__db_status_t base_status;
150
svn_revnum_t base_revision = SVN_INVALID_REVNUM;
151
const svn_checksum_t *base_checksum;
152
const char *pristine_abspath;
154
SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, NULL, NULL, NULL,
155
NULL, NULL, NULL, NULL, &checksum, NULL,
156
&original_repos_relpath, NULL, NULL, NULL,
157
NULL, NULL, NULL, NULL, NULL,
158
&op_root, &had_props, &props_mod,
159
&have_base, &have_more_work, NULL,
160
db, local_abspath, scratch_pool, scratch_pool));
162
if ((status == svn_wc__db_status_added) && (have_base || have_more_work))
164
SVN_ERR(svn_wc__db_node_check_replace(&replaced, &base_replace,
165
NULL, db, local_abspath,
168
if (replaced && base_replace /* && !have_more_work */)
170
svn_wc__db_kind_t base_kind;
171
SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind,
173
NULL, NULL, NULL, NULL, NULL, NULL,
174
NULL, &base_checksum, NULL,
177
scratch_pool, scratch_pool));
179
if (base_status != svn_wc__db_status_normal
180
|| base_kind != kind)
182
/* We can't show a replacement here */
184
base_replace = FALSE;
189
/* We can't look in this middle working layer (yet).
190
We just report the change itself.
192
And if we could look at it, how would we report the addition
193
of this middle layer (and maybe different layers below that)?
195
For 1.7 we just do what we did before: Ignore this layering
196
problem and just show how the current file got in your wc.
199
base_replace = FALSE;
203
/* Now refine ADDED to one of: ADDED, COPIED, MOVED_HERE. Note that only
204
the latter two have corresponding pristine info to diff against. */
205
if (status == svn_wc__db_status_added)
206
SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL,
208
NULL, db, local_abspath,
209
scratch_pool, scratch_pool));
211
SVN_ERR(get_empty_file(eb, &empty_file, scratch_pool));
213
/* When we show a delete, we show a diff of the original pristine against
215
* A base-replace is treated like a delete plus an add.
217
* For this kind of diff we prefer to show the deletion of what was checked
218
* out over showing what was actually deleted (if that was defined by
219
* a higher layer). */
220
if (status == svn_wc__db_status_deleted ||
221
(base_replace && ! eb->ignore_ancestry))
223
apr_hash_t *del_props;
224
const svn_checksum_t *del_checksum;
225
const char *del_text_abspath;
226
const char *del_mimetype;
228
if (base_replace && ! eb->ignore_ancestry)
230
/* We show a deletion of the information in the BASE layer */
231
SVN_ERR(svn_wc__db_base_get_props(&del_props, db, local_abspath,
232
scratch_pool, scratch_pool));
234
del_checksum = base_checksum;
238
/* We show a deletion of what was actually deleted */
239
SVN_ERR_ASSERT(status == svn_wc__db_status_deleted);
241
SVN_ERR(svn_wc__get_pristine_props(&del_props, db, local_abspath,
242
scratch_pool, scratch_pool));
244
SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
245
NULL, &del_checksum, NULL,
246
NULL, db, local_abspath,
247
scratch_pool, scratch_pool));
250
SVN_ERR_ASSERT(del_checksum != NULL);
252
SVN_ERR(svn_wc__db_pristine_get_path(&del_text_abspath, db,
253
local_abspath, del_checksum,
254
scratch_pool, scratch_pool));
256
if (del_props == NULL)
257
del_props = apr_hash_make(scratch_pool);
259
del_mimetype = get_prop_mimetype(del_props);
261
SVN_ERR(eb->callbacks->file_deleted(NULL, NULL, path,
270
if (status == svn_wc__db_status_deleted)
272
/* We're here only for showing a delete, so we're done. */
277
if (checksum != NULL)
278
SVN_ERR(svn_wc__db_pristine_get_path(&pristine_abspath, db, local_abspath,
280
scratch_pool, scratch_pool));
281
else if (base_replace && eb->ignore_ancestry)
282
SVN_ERR(svn_wc__db_pristine_get_path(&pristine_abspath, db, local_abspath,
284
scratch_pool, scratch_pool));
286
pristine_abspath = empty_file;
288
/* Now deal with showing additions, or the add-half of replacements.
289
* If the item is schedule-add *with history*, then we usually want
290
* to see the usual working vs. text-base comparison, which will show changes
291
* made since the file was copied. But in case we're showing copies as adds,
292
* we need to compare the copied file to the empty file. If we're doing a git
293
* diff, and the file was copied, we need to report the file as added and
294
* diff it against the text base, so that a "copied" git diff header, and
295
* possibly a diff against the copy source, will be generated for it. */
296
if ((! base_replace && status == svn_wc__db_status_added) ||
297
(base_replace && ! eb->ignore_ancestry) ||
298
((status == svn_wc__db_status_copied ||
299
status == svn_wc__db_status_moved_here) &&
300
(eb->show_copies_as_adds || eb->use_git_diff_format)))
302
const char *translated = NULL;
303
apr_hash_t *pristine_props;
304
apr_hash_t *actual_props;
305
const char *actual_mimetype;
306
apr_array_header_t *propchanges;
309
/* Get svn:mime-type from ACTUAL props of PATH. */
310
SVN_ERR(svn_wc__get_actual_props(&actual_props, db, local_abspath,
311
scratch_pool, scratch_pool));
312
actual_mimetype = get_prop_mimetype(actual_props);
314
/* Set the original properties to empty, then compute "changes" from
315
that. Essentially, all ACTUAL props will be "added". */
316
pristine_props = apr_hash_make(scratch_pool);
317
SVN_ERR(svn_prop_diffs(&propchanges, actual_props, pristine_props,
320
SVN_ERR(svn_wc__internal_translated_file(
321
&translated, local_abspath, db, local_abspath,
322
SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
323
eb->cancel_func, eb->cancel_baton,
324
scratch_pool, scratch_pool));
326
SVN_ERR(eb->callbacks->file_added(NULL, NULL, NULL, path,
327
(! eb->show_copies_as_adds &&
328
eb->use_git_diff_format &&
329
status != svn_wc__db_status_added) ?
330
pristine_abspath : empty_file,
335
original_repos_relpath,
336
SVN_INVALID_REVNUM, propchanges,
337
pristine_props, eb->callback_baton,
342
const char *translated = NULL;
343
apr_hash_t *pristine_props;
344
const char *pristine_mimetype;
345
const char *actual_mimetype;
346
apr_hash_t *actual_props;
347
apr_array_header_t *propchanges;
348
svn_boolean_t modified;
350
/* Here we deal with showing pure modifications. */
351
SVN_ERR(svn_wc__internal_file_modified_p(&modified, db, local_abspath,
352
FALSE, scratch_pool));
355
/* Note that this might be the _second_ time we translate
356
the file, as svn_wc__text_modified_internal_p() might have used a
357
tmp translated copy too. But what the heck, diff is
358
already expensive, translating twice for the sake of code
359
modularity is liveable. */
360
SVN_ERR(svn_wc__internal_translated_file(
361
&translated, local_abspath, db, local_abspath,
362
SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
363
eb->cancel_func, eb->cancel_baton,
364
scratch_pool, scratch_pool));
367
/* Get the properties, the svn:mime-type values, and compute the
368
differences between the two. */
370
&& eb->ignore_ancestry)
372
/* We don't want the normal pristine properties (which are
373
from the WORKING tree). We want the pristines associated
374
with the BASE tree, which are saved as "revert" props. */
375
SVN_ERR(svn_wc__db_base_get_props(&pristine_props,
377
scratch_pool, scratch_pool));
381
/* We can only fetch the pristine props (from BASE or WORKING) if
382
the node has not been replaced, or it was copied/moved here. */
383
SVN_ERR_ASSERT(!replaced
384
|| status == svn_wc__db_status_copied
385
|| status == svn_wc__db_status_moved_here);
387
SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props, db,
389
scratch_pool, scratch_pool));
391
/* baseprops will be NULL for added nodes */
393
pristine_props = apr_hash_make(scratch_pool);
395
pristine_mimetype = get_prop_mimetype(pristine_props);
397
SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath,
398
scratch_pool, scratch_pool));
399
actual_mimetype = get_prop_mimetype(actual_props);
401
SVN_ERR(svn_prop_diffs(&propchanges, actual_props, pristine_props,
404
if (modified || propchanges->nelts > 0)
406
SVN_ERR(eb->callbacks->file_changed(NULL, NULL, NULL,
408
modified ? pristine_abspath
105
/* Recursively opens directories on the stack in EB, until LOCAL_ABSPATH
106
is reached. If RECURSIVE_SKIP is TRUE, don't open LOCAL_ABSPATH itself,
107
but create it marked with skip+skip_children.
110
ensure_state(struct diff_baton *eb,
111
const char *local_abspath,
112
svn_boolean_t recursive_skip,
113
apr_pool_t *scratch_pool)
115
struct node_state_t *ns;
119
if (!svn_dirent_is_ancestor(eb->anchor_abspath, local_abspath))
122
SVN_ERR(ensure_state(eb,
123
svn_dirent_dirname(local_abspath,scratch_pool),
127
else if (svn_dirent_is_child(eb->cur->local_abspath, local_abspath, NULL))
128
SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath,scratch_pool),
134
if (eb->cur && eb->cur->skip_children)
137
ns_pool = svn_pool_create(eb->cur ? eb->cur->pool : eb->pool);
138
ns = apr_pcalloc(ns_pool, sizeof(*ns));
141
ns->local_abspath = apr_pstrdup(ns_pool, local_abspath);
142
ns->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, ns->local_abspath);
143
ns->parent = eb->cur;
149
ns->skip_children = TRUE;
154
svn_revnum_t revision;
157
err = svn_wc__db_base_get_info(NULL, NULL, &revision, NULL, NULL, NULL,
158
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
160
eb->db, local_abspath,
161
scratch_pool, scratch_pool);
165
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
166
return svn_error_trace(err);
167
svn_error_clear(err);
169
revision = 0; /* Use original revision? */
171
ns->left_src = svn_diff__source_create(revision, ns->pool);
172
ns->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, ns->pool);
174
SVN_ERR(eb->processor->dir_opened(&ns->baton, &ns->skip,
179
NULL /* copyfrom_source */,
180
ns->parent ? ns->parent->baton : NULL,
182
ns->pool, scratch_pool));
422
185
return SVN_NO_ERROR;
430
193
apr_pool_t *scratch_pool)
432
195
struct diff_baton *eb = baton;
433
switch (status->node_status)
435
case svn_wc_status_unversioned:
436
case svn_wc_status_ignored:
437
return SVN_NO_ERROR; /* No diff */
439
case svn_wc_status_obstructed:
440
case svn_wc_status_missing:
441
return SVN_NO_ERROR; /* ### What should we do here? */
444
break; /* Go check other conditions */
196
svn_wc__db_t *db = eb->db;
198
if (! status->versioned)
199
return SVN_NO_ERROR; /* unversioned (includes dir externals) */
201
if (status->node_status == svn_wc_status_conflicted
202
&& status->text_status == svn_wc_status_none
203
&& status->prop_status == svn_wc_status_none)
205
/* Node is an actual only node describing a tree conflict */
209
/* Not text/prop modified, not copied. Easy out */
210
if (status->node_status == svn_wc_status_normal && !status->copied)
213
/* Mark all directories where we are no longer inside as closed */
215
&& !svn_dirent_is_ancestor(eb->cur->local_abspath, local_abspath))
217
struct node_state_t *ns = eb->cur;
222
SVN_ERR(eb->processor->dir_changed(ns->relpath,
232
SVN_ERR(eb->processor->dir_closed(ns->relpath,
239
eb->cur = ns->parent;
240
svn_pool_clear(ns->pool);
242
SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath, scratch_pool),
243
FALSE, scratch_pool));
245
if (eb->cur && eb->cur->skip_children)
447
248
if (eb->changelist_hash != NULL
448
249
&& (!status->changelist
449
|| ! apr_hash_get(eb->changelist_hash, status->changelist,
450
APR_HASH_KEY_STRING)))
250
|| ! svn_hash_gets(eb->changelist_hash, status->changelist)))
451
251
return SVN_NO_ERROR; /* Filtered via changelist */
453
/* ### The following checks should probably be reversed as it should decide
454
when *not* to show a diff, because generally all changed nodes should
456
if (status->kind == svn_node_file)
459
* - The text is modified
460
* - Or the properties are modified
461
* - Or when the node has been replaced
462
* - Or (if in copies as adds or git mode) when a node is copied */
463
if (status->text_status == svn_wc_status_modified
464
|| status->prop_status == svn_wc_status_modified
465
|| status->node_status == svn_wc_status_deleted
466
|| status->node_status == svn_wc_status_replaced
467
|| ((eb->show_copies_as_adds || eb->use_git_diff_format)
470
const char *path = svn_dirent_skip_ancestor(eb->anchor_abspath,
473
SVN_ERR(file_diff(eb, local_abspath, path, scratch_pool));
478
/* ### This case should probably be extended for git-diff, but this
479
is what the old diff code provided */
480
if (status->node_status == svn_wc_status_deleted
481
|| status->node_status == svn_wc_status_replaced
482
|| status->prop_status == svn_wc_status_modified)
484
apr_array_header_t *propchanges;
485
apr_hash_t *baseprops;
486
const char *path = svn_dirent_skip_ancestor(eb->anchor_abspath,
490
SVN_ERR(svn_wc__internal_propdiff(&propchanges, &baseprops,
491
eb->db, local_abspath,
492
scratch_pool, scratch_pool));
494
SVN_ERR(eb->callbacks->dir_props_changed(NULL, NULL,
495
path, FALSE /* ### ? */,
496
propchanges, baseprops,
253
/* This code does about the same thing as the inner body of
254
walk_local_nodes_diff() in diff_editor.c, except that
255
it is already filtered by the status walker, doesn't have to
256
account for remote changes (and many tiny other details) */
259
svn_boolean_t repos_only;
260
svn_boolean_t local_only;
261
svn_wc__db_status_t db_status;
262
svn_boolean_t have_base;
263
svn_node_kind_t base_kind;
264
svn_node_kind_t db_kind = status->kind;
265
svn_depth_t depth_below_here = svn_depth_unknown;
267
const char *child_abspath = local_abspath;
268
const char *child_relpath = svn_dirent_skip_ancestor(eb->anchor_abspath,
275
/* ### optimize away this call using status info. Should
276
be possible in almost every case (except conflict, missing, obst.)*/
277
SVN_ERR(svn_wc__db_read_info(&db_status, NULL, NULL, NULL, NULL, NULL,
278
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
279
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
280
NULL, NULL, NULL, NULL,
281
&have_base, NULL, NULL,
282
eb->db, local_abspath,
283
scratch_pool, scratch_pool));
286
local_only = TRUE; /* Only report additions */
288
else if (db_status == svn_wc__db_status_normal)
293
else if (db_status == svn_wc__db_status_deleted)
295
svn_wc__db_status_t base_status;
297
SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
298
NULL, NULL, NULL, NULL, NULL,
299
NULL, NULL, NULL, NULL, NULL,
301
eb->db, local_abspath,
302
scratch_pool, scratch_pool));
304
if (base_status != svn_wc__db_status_normal)
309
/* working status is either added or deleted */
310
svn_wc__db_status_t base_status;
312
SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
313
NULL, NULL, NULL, NULL, NULL,
314
NULL, NULL, NULL, NULL, NULL,
316
eb->db, local_abspath,
317
scratch_pool, scratch_pool));
319
if (base_status != svn_wc__db_status_normal)
321
else if (base_kind != db_kind || !eb->ignore_ancestry)
330
/* Report repository form deleted */
331
if (base_kind == svn_node_file)
332
SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
336
eb->cur ? eb->cur->baton : NULL,
338
else if (base_kind == svn_node_dir)
339
SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
344
eb->cur ? eb->cur->baton : NULL,
349
else if (!local_only)
351
/* Diff base against actual */
352
if (db_kind == svn_node_file)
354
SVN_ERR(svn_wc__diff_base_working_diff(db, child_abspath,
367
else if (db_kind == svn_node_dir)
369
SVN_ERR(ensure_state(eb, local_abspath, FALSE, scratch_pool));
371
if (status->prop_status != svn_wc_status_none
372
&& status->prop_status != svn_wc_status_normal)
374
apr_array_header_t *propchanges;
375
SVN_ERR(svn_wc__db_base_get_props(&eb->cur->left_props,
376
eb->db, local_abspath,
379
SVN_ERR(svn_wc__db_read_props(&eb->cur->right_props,
380
eb->db, local_abspath,
384
SVN_ERR(svn_prop_diffs(&propchanges,
385
eb->cur->right_props,
389
eb->cur->propchanges = propchanges;
394
if (local_only && (db_status != svn_wc__db_status_deleted))
396
if (db_kind == svn_node_file)
397
SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
400
eb->cur ? eb->cur->baton : NULL,
406
else if (db_kind == svn_node_dir)
407
SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
408
child_relpath, depth_below_here,
410
eb->cur ? eb->cur->baton : NULL,
418
if (db_kind == svn_node_dir && (local_only || repos_only))
419
SVN_ERR(ensure_state(eb, local_abspath, TRUE /* skip */, scratch_pool));
501
422
return SVN_NO_ERROR;