aboutsummaryrefslogtreecommitdiffstats
path: root/subversion
diff options
context:
space:
mode:
authorPeter Wemm <peter@FreeBSD.org>2018-05-08 03:44:38 +0000
committerPeter Wemm <peter@FreeBSD.org>2018-05-08 03:44:38 +0000
commit3faf8d6bffc5d0fb2525ba37bb504c53366caf9d (patch)
tree7e47911263e75034b767fe34b2f8d3d17e91f66d /subversion
parenta55fb3c0d5eca7d887798125d5b95942b1f01d4b (diff)
downloadsrc-3faf8d6bffc5d0fb2525ba37bb504c53366caf9d.tar.gz
src-3faf8d6bffc5d0fb2525ba37bb504c53366caf9d.zip
Import Subversion-1.10.0vendor/subversion/subversion-1.10.0
Notes
Notes: svn path=/vendor/subversion/dist/; revision=333347 svn path=/vendor/subversion/subversion-1.10.0/; revision=333348; tag=vendor/subversion/subversion-1.10.0
Diffstat (limited to 'subversion')
-rw-r--r--subversion/include/private/ra_svn_sasl.h10
-rw-r--r--subversion/include/private/ra_svn_wrapped_sasl.h131
-rw-r--r--subversion/include/private/svn_atomic.h68
-rw-r--r--subversion/include/private/svn_branch.h679
-rw-r--r--subversion/include/private/svn_branch_compat.h273
-rw-r--r--subversion/include/private/svn_branch_impl.h197
-rw-r--r--subversion/include/private/svn_branch_nested.h216
-rw-r--r--subversion/include/private/svn_branch_repos.h104
-rw-r--r--subversion/include/private/svn_cache.h21
-rw-r--r--subversion/include/private/svn_cmdline_private.h41
-rw-r--r--subversion/include/private/svn_config_private.h129
-rw-r--r--subversion/include/private/svn_delta_private.h19
-rw-r--r--subversion/include/private/svn_dep_compat.h43
-rw-r--r--subversion/include/private/svn_diff_private.h34
-rw-r--r--subversion/include/private/svn_diff_tree.h18
-rw-r--r--subversion/include/private/svn_element.h399
-rw-r--r--subversion/include/private/svn_fs_fs_private.h17
-rw-r--r--subversion/include/private/svn_fs_private.h18
-rw-r--r--subversion/include/private/svn_fs_util.h9
-rw-r--r--subversion/include/private/svn_io_private.h33
-rw-r--r--subversion/include/private/svn_log.h13
-rw-r--r--subversion/include/private/svn_mergeinfo_private.h15
-rw-r--r--subversion/include/private/svn_mutex.h13
-rw-r--r--subversion/include/private/svn_object_pool.h67
-rw-r--r--subversion/include/private/svn_packed_data.h5
-rw-r--r--subversion/include/private/svn_ra_svn_private.h178
-rw-r--r--subversion/include/private/svn_repos_private.h127
-rw-r--r--subversion/include/private/svn_sorts_private.h4
-rw-r--r--subversion/include/private/svn_sqlite.h10
-rw-r--r--subversion/include/private/svn_string_private.h8
-rw-r--r--subversion/include/private/svn_subr_private.h79
-rw-r--r--subversion/include/private/svn_temp_serializer.h2
-rw-r--r--subversion/include/private/svn_utf_private.h36
-rw-r--r--subversion/include/private/svn_wc_private.h239
-rw-r--r--subversion/include/svn_auth.h8
-rw-r--r--subversion/include/svn_base64.h21
-rw-r--r--subversion/include/svn_checksum.h13
-rw-r--r--subversion/include/svn_client.h944
-rw-r--r--subversion/include/svn_cmdline.h1
-rw-r--r--subversion/include/svn_config.h6
-rw-r--r--subversion/include/svn_dav.h35
-rw-r--r--subversion/include/svn_delta.h120
-rw-r--r--subversion/include/svn_diff.h79
-rw-r--r--subversion/include/svn_error.h12
-rw-r--r--subversion/include/svn_error_codes.h60
-rw-r--r--subversion/include/svn_fs.h352
-rw-r--r--subversion/include/svn_hash.h25
-rw-r--r--subversion/include/svn_io.h197
-rw-r--r--subversion/include/svn_props.h28
-rw-r--r--subversion/include/svn_ra.h77
-rw-r--r--subversion/include/svn_ra_svn.h51
-rw-r--r--subversion/include/svn_repos.h566
-rw-r--r--subversion/include/svn_string.h75
-rw-r--r--subversion/include/svn_types.h9
-rw-r--r--subversion/include/svn_user.h3
-rw-r--r--subversion/include/svn_version.h8
-rw-r--r--subversion/include/svn_wc.h54
-rw-r--r--subversion/include/svn_x509.h4
-rw-r--r--subversion/include/svn_xml.h10
-rw-r--r--subversion/libsvn_auth_gnome_keyring/gnome_keyring.c234
-rw-r--r--subversion/libsvn_auth_kwallet/kwallet.cpp30
-rw-r--r--subversion/libsvn_client/checkout.c6
-rw-r--r--subversion/libsvn_client/client.h90
-rw-r--r--subversion/libsvn_client/conflicts.c11207
-rw-r--r--subversion/libsvn_client/copy.c64
-rw-r--r--subversion/libsvn_client/deprecated.c20
-rw-r--r--subversion/libsvn_client/diff.c466
-rw-r--r--subversion/libsvn_client/diff_local.c5
-rw-r--r--subversion/libsvn_client/export.c52
-rw-r--r--subversion/libsvn_client/externals.c6
-rw-r--r--subversion/libsvn_client/import.c84
-rw-r--r--subversion/libsvn_client/info.c10
-rw-r--r--subversion/libsvn_client/list.c160
-rw-r--r--subversion/libsvn_client/merge.c215
-rw-r--r--subversion/libsvn_client/merge_elements.c248
-rw-r--r--subversion/libsvn_client/mergeinfo.c8
-rw-r--r--subversion/libsvn_client/mtcc.c67
-rw-r--r--subversion/libsvn_client/patch.c1580
-rw-r--r--subversion/libsvn_client/ra.c4
-rw-r--r--subversion/libsvn_client/relocate.c20
-rw-r--r--subversion/libsvn_client/resolved.c3
-rw-r--r--subversion/libsvn_client/revisions.c14
-rw-r--r--subversion/libsvn_client/shelve.c552
-rw-r--r--subversion/libsvn_client/upgrade.c267
-rw-r--r--subversion/libsvn_delta/branch.c1699
-rw-r--r--subversion/libsvn_delta/branch_compat.c2070
-rw-r--r--subversion/libsvn_delta/branch_migrate.c366
-rw-r--r--subversion/libsvn_delta/branch_nested.c660
-rw-r--r--subversion/libsvn_delta/branch_repos.c132
-rw-r--r--subversion/libsvn_delta/cancel.c21
-rw-r--r--subversion/libsvn_delta/compat.c30
-rw-r--r--subversion/libsvn_delta/debug_editor.c2
-rw-r--r--subversion/libsvn_delta/default_editor.c30
-rw-r--r--subversion/libsvn_delta/element.c471
-rw-r--r--subversion/libsvn_delta/svndiff.c527
-rw-r--r--subversion/libsvn_delta/text_delta.c33
-rw-r--r--subversion/libsvn_delta/xdelta.c2
-rw-r--r--subversion/libsvn_diff/binary_diff.c74
-rw-r--r--subversion/libsvn_diff/diff.h10
-rw-r--r--subversion/libsvn_diff/diff3.c27
-rw-r--r--subversion/libsvn_diff/diff_file.c70
-rw-r--r--subversion/libsvn_diff/diff_memory.c34
-rw-r--r--subversion/libsvn_diff/parse-diff.c996
-rw-r--r--subversion/libsvn_fs/deprecated.c157
-rw-r--r--subversion/libsvn_fs/editor.c18
-rw-r--r--subversion/libsvn_fs/fs-loader.c460
-rw-r--r--subversion/libsvn_fs/fs-loader.h67
-rw-r--r--subversion/libsvn_fs_base/bdb/rev-table.c3
-rw-r--r--subversion/libsvn_fs_base/fs.c44
-rw-r--r--subversion/libsvn_fs_base/fs_init.h33
-rw-r--r--subversion/libsvn_fs_base/lock.c2
-rw-r--r--subversion/libsvn_fs_base/revs-txns.c24
-rw-r--r--subversion/libsvn_fs_base/revs-txns.h8
-rw-r--r--subversion/libsvn_fs_base/tree.c80
-rw-r--r--subversion/libsvn_fs_fs/cached_data.c648
-rw-r--r--subversion/libsvn_fs_fs/cached_data.h37
-rw-r--r--subversion/libsvn_fs_fs/caching.c205
-rw-r--r--subversion/libsvn_fs_fs/dag.c13
-rw-r--r--subversion/libsvn_fs_fs/fs.c66
-rw-r--r--subversion/libsvn_fs_fs/fs.h117
-rw-r--r--subversion/libsvn_fs_fs/fs_fs.c370
-rw-r--r--subversion/libsvn_fs_fs/fs_fs.h18
-rw-r--r--subversion/libsvn_fs_fs/fs_init.h32
-rw-r--r--subversion/libsvn_fs_fs/hotcopy.c131
-rw-r--r--subversion/libsvn_fs_fs/hotcopy.h26
-rw-r--r--subversion/libsvn_fs_fs/id.c12
-rw-r--r--subversion/libsvn_fs_fs/index.c45
-rw-r--r--subversion/libsvn_fs_fs/load-index.c98
-rw-r--r--subversion/libsvn_fs_fs/lock.c8
-rw-r--r--subversion/libsvn_fs_fs/low_level.c238
-rw-r--r--subversion/libsvn_fs_fs/low_level.h9
-rw-r--r--subversion/libsvn_fs_fs/pack.c415
-rw-r--r--subversion/libsvn_fs_fs/recovery.c6
-rw-r--r--subversion/libsvn_fs_fs/rep-cache-db.h61
-rw-r--r--subversion/libsvn_fs_fs/rep-cache-db.sql31
-rw-r--r--subversion/libsvn_fs_fs/rep-cache.c60
-rw-r--r--subversion/libsvn_fs_fs/rep-cache.h6
-rw-r--r--subversion/libsvn_fs_fs/rev_file.c1
-rw-r--r--subversion/libsvn_fs_fs/revprops.c362
-rw-r--r--subversion/libsvn_fs_fs/revprops.h26
-rw-r--r--subversion/libsvn_fs_fs/stats.c256
-rw-r--r--subversion/libsvn_fs_fs/structure17
-rw-r--r--subversion/libsvn_fs_fs/structure-indexes24
-rw-r--r--subversion/libsvn_fs_fs/temp_serializer.c229
-rw-r--r--subversion/libsvn_fs_fs/temp_serializer.h128
-rw-r--r--subversion/libsvn_fs_fs/transaction.c763
-rw-r--r--subversion/libsvn_fs_fs/tree.c558
-rw-r--r--subversion/libsvn_fs_fs/util.c93
-rw-r--r--subversion/libsvn_fs_fs/util.h9
-rw-r--r--subversion/libsvn_fs_fs/verify.c53
-rw-r--r--subversion/libsvn_fs_util/fs-util.c15
-rw-r--r--subversion/libsvn_fs_x/batch_fsync.c588
-rw-r--r--subversion/libsvn_fs_x/batch_fsync.h92
-rw-r--r--subversion/libsvn_fs_x/cached_data.c1219
-rw-r--r--subversion/libsvn_fs_x/cached_data.h39
-rw-r--r--subversion/libsvn_fs_x/caching.c351
-rw-r--r--subversion/libsvn_fs_x/changes.c81
-rw-r--r--subversion/libsvn_fs_x/changes.h34
-rw-r--r--subversion/libsvn_fs_x/dag.c603
-rw-r--r--subversion/libsvn_fs_x/dag.h144
-rw-r--r--subversion/libsvn_fs_x/dag_cache.c1103
-rw-r--r--subversion/libsvn_fs_x/dag_cache.h174
-rw-r--r--subversion/libsvn_fs_x/fs.c77
-rw-r--r--subversion/libsvn_fs_x/fs.h123
-rw-r--r--subversion/libsvn_fs_x/fs_init.h33
-rw-r--r--subversion/libsvn_fs_x/fs_x.c169
-rw-r--r--subversion/libsvn_fs_x/fs_x.h24
-rw-r--r--subversion/libsvn_fs_x/hotcopy.c367
-rw-r--r--subversion/libsvn_fs_x/hotcopy.h33
-rw-r--r--subversion/libsvn_fs_x/index.c308
-rw-r--r--subversion/libsvn_fs_x/index.h40
-rw-r--r--subversion/libsvn_fs_x/lock.c434
-rw-r--r--subversion/libsvn_fs_x/lock.h6
-rw-r--r--subversion/libsvn_fs_x/low_level.c195
-rw-r--r--subversion/libsvn_fs_x/low_level.h36
-rw-r--r--subversion/libsvn_fs_x/noderevs.c37
-rw-r--r--subversion/libsvn_fs_x/noderevs.h8
-rw-r--r--subversion/libsvn_fs_x/pack.c381
-rw-r--r--subversion/libsvn_fs_x/pack.h23
-rw-r--r--subversion/libsvn_fs_x/recovery.c138
-rw-r--r--subversion/libsvn_fs_x/recovery.h4
-rw-r--r--subversion/libsvn_fs_x/rep-cache-db.h2
-rw-r--r--subversion/libsvn_fs_x/rep-cache.c54
-rw-r--r--subversion/libsvn_fs_x/rep-cache.h13
-rw-r--r--subversion/libsvn_fs_x/reps.c15
-rw-r--r--subversion/libsvn_fs_x/reps.h10
-rw-r--r--subversion/libsvn_fs_x/rev_file.c321
-rw-r--r--subversion/libsvn_fs_x/rev_file.h208
-rw-r--r--subversion/libsvn_fs_x/revprops.c1647
-rw-r--r--subversion/libsvn_fs_x/revprops.h106
-rw-r--r--subversion/libsvn_fs_x/string_table.c34
-rw-r--r--subversion/libsvn_fs_x/string_table.h18
-rw-r--r--subversion/libsvn_fs_x/temp_serializer.c406
-rw-r--r--subversion/libsvn_fs_x/temp_serializer.h160
-rw-r--r--subversion/libsvn_fs_x/transaction.c1133
-rw-r--r--subversion/libsvn_fs_x/transaction.h17
-rw-r--r--subversion/libsvn_fs_x/tree.c2004
-rw-r--r--subversion/libsvn_fs_x/tree.h17
-rw-r--r--subversion/libsvn_fs_x/util.c182
-rw-r--r--subversion/libsvn_fs_x/util.h68
-rw-r--r--subversion/libsvn_fs_x/verify.c116
-rw-r--r--subversion/libsvn_fs_x/verify.h6
-rw-r--r--subversion/libsvn_ra/ra_loader.c65
-rw-r--r--subversion/libsvn_ra/ra_loader.h37
-rw-r--r--subversion/libsvn_ra_local/ra_plugin.c132
-rw-r--r--subversion/libsvn_ra_serf/README84
-rw-r--r--subversion/libsvn_ra_serf/blame.c20
-rw-r--r--subversion/libsvn_ra_serf/commit.c356
-rw-r--r--subversion/libsvn_ra_serf/eagain_bucket.c7
-rw-r--r--subversion/libsvn_ra_serf/get_file.c33
-rw-r--r--subversion/libsvn_ra_serf/getlocations.c5
-rw-r--r--subversion/libsvn_ra_serf/getlocationsegments.c8
-rw-r--r--subversion/libsvn_ra_serf/libsvn_ra_serf.pc.in2
-rw-r--r--subversion/libsvn_ra_serf/list.c301
-rw-r--r--subversion/libsvn_ra_serf/lock.c28
-rw-r--r--subversion/libsvn_ra_serf/log.c10
-rw-r--r--subversion/libsvn_ra_serf/merge.c21
-rw-r--r--subversion/libsvn_ra_serf/mergeinfo.c4
-rw-r--r--subversion/libsvn_ra_serf/multistatus.c11
-rw-r--r--subversion/libsvn_ra_serf/options.c27
-rw-r--r--subversion/libsvn_ra_serf/property.c35
-rw-r--r--subversion/libsvn_ra_serf/ra_serf.h106
-rw-r--r--subversion/libsvn_ra_serf/replay.c71
-rw-r--r--subversion/libsvn_ra_serf/request_body.c232
-rw-r--r--subversion/libsvn_ra_serf/sb_bucket.c7
-rw-r--r--subversion/libsvn_ra_serf/serf.c86
-rw-r--r--subversion/libsvn_ra_serf/stat.c63
-rw-r--r--subversion/libsvn_ra_serf/stream_bucket.c120
-rw-r--r--subversion/libsvn_ra_serf/update.c217
-rw-r--r--subversion/libsvn_ra_serf/util.c165
-rw-r--r--subversion/libsvn_ra_serf/xml.c173
-rw-r--r--subversion/libsvn_ra_svn/client.c610
-rw-r--r--subversion/libsvn_ra_svn/cram.c4
-rw-r--r--subversion/libsvn_ra_svn/cyrus_auth.c89
-rw-r--r--subversion/libsvn_ra_svn/deprecated.c51
-rw-r--r--subversion/libsvn_ra_svn/editorp.c415
-rw-r--r--subversion/libsvn_ra_svn/internal_auth.c10
-rw-r--r--subversion/libsvn_ra_svn/marshal.c880
-rw-r--r--subversion/libsvn_ra_svn/protocol21
-rw-r--r--subversion/libsvn_ra_svn/ra_svn.h47
-rw-r--r--subversion/libsvn_ra_svn/wrapped_sasl.c197
-rw-r--r--subversion/libsvn_repos/authz.c2233
-rw-r--r--subversion/libsvn_repos/authz.h364
-rw-r--r--subversion/libsvn_repos/authz_info.c184
-rw-r--r--subversion/libsvn_repos/authz_parse.c1442
-rw-r--r--subversion/libsvn_repos/authz_pool.c226
-rw-r--r--subversion/libsvn_repos/commit.c140
-rw-r--r--subversion/libsvn_repos/compat.c179
-rw-r--r--subversion/libsvn_repos/config_file.c386
-rw-r--r--subversion/libsvn_repos/config_file.h74
-rw-r--r--subversion/libsvn_repos/config_pool.c485
-rw-r--r--subversion/libsvn_repos/delta.c24
-rw-r--r--subversion/libsvn_repos/deprecated.c180
-rw-r--r--subversion/libsvn_repos/dump.c140
-rw-r--r--subversion/libsvn_repos/fs-wrap.c102
-rw-r--r--subversion/libsvn_repos/hooks.c15
-rw-r--r--subversion/libsvn_repos/list.c340
-rw-r--r--subversion/libsvn_repos/load-fs-vtable.c271
-rw-r--r--subversion/libsvn_repos/load.c132
-rw-r--r--subversion/libsvn_repos/log.c686
-rw-r--r--subversion/libsvn_repos/replay.c193
-rw-r--r--subversion/libsvn_repos/reporter.c41
-rw-r--r--subversion/libsvn_repos/repos.c68
-rw-r--r--subversion/libsvn_repos/repos.h38
-rw-r--r--subversion/libsvn_repos/rev_hunt.c123
-rw-r--r--subversion/libsvn_subr/atomic.c203
-rw-r--r--subversion/libsvn_subr/auth.c19
-rw-r--r--subversion/libsvn_subr/base64.c16
-rw-r--r--subversion/libsvn_subr/cache-inprocess.c2
-rw-r--r--subversion/libsvn_subr/cache-membuffer.c428
-rw-r--r--subversion/libsvn_subr/cache-null.c157
-rw-r--r--subversion/libsvn_subr/checksum.c97
-rw-r--r--subversion/libsvn_subr/cmdline.c229
-rw-r--r--subversion/libsvn_subr/compress_lz4.c144
-rw-r--r--subversion/libsvn_subr/compress_zlib.c (renamed from subversion/libsvn_subr/compress.c)74
-rw-r--r--subversion/libsvn_subr/config.c206
-rw-r--r--subversion/libsvn_subr/config_auth.c2
-rw-r--r--subversion/libsvn_subr/config_file.c321
-rw-r--r--subversion/libsvn_subr/config_impl.h12
-rw-r--r--subversion/libsvn_subr/config_win.c17
-rw-r--r--subversion/libsvn_subr/deprecated.c55
-rw-r--r--subversion/libsvn_subr/dirent_uri.c27
-rw-r--r--subversion/libsvn_subr/dso.c1
-rw-r--r--subversion/libsvn_subr/encode.c107
-rw-r--r--subversion/libsvn_subr/eol.c38
-rw-r--r--subversion/libsvn_subr/error.c111
-rw-r--r--subversion/libsvn_subr/errorcode.inc121
-rw-r--r--subversion/libsvn_subr/fnv1a.c17
-rw-r--r--subversion/libsvn_subr/fnv1a.h10
-rw-r--r--subversion/libsvn_subr/gpg_agent.c112
-rw-r--r--subversion/libsvn_subr/hash.c21
-rw-r--r--subversion/libsvn_subr/internal_statements.h2
-rw-r--r--subversion/libsvn_subr/io.c299
-rw-r--r--subversion/libsvn_subr/libsvn_subr.pc.in2
-rw-r--r--subversion/libsvn_subr/log.c34
-rw-r--r--subversion/libsvn_subr/lz4/LICENSE24
-rw-r--r--subversion/libsvn_subr/lz4/lz4.c1481
-rw-r--r--subversion/libsvn_subr/lz4/lz4internal.h466
-rw-r--r--subversion/libsvn_subr/mergeinfo.c547
-rw-r--r--subversion/libsvn_subr/mutex.c10
-rw-r--r--subversion/libsvn_subr/object_pool.c115
-rw-r--r--subversion/libsvn_subr/opt.c53
-rw-r--r--subversion/libsvn_subr/packed_data.c21
-rw-r--r--subversion/libsvn_subr/path.c29
-rw-r--r--subversion/libsvn_subr/pool.c32
-rw-r--r--subversion/libsvn_subr/pools.h (renamed from subversion/libsvn_delta/debug_editor.h)33
-rw-r--r--subversion/libsvn_subr/prefix_string.c37
-rw-r--r--subversion/libsvn_subr/prompt.c14
-rw-r--r--subversion/libsvn_subr/properties.c6
-rw-r--r--subversion/libsvn_subr/skel.c2
-rw-r--r--subversion/libsvn_subr/sorts.c2
-rw-r--r--subversion/libsvn_subr/spillbuf.c10
-rw-r--r--subversion/libsvn_subr/sqlite.c28
-rw-r--r--subversion/libsvn_subr/sqlite3wrapper.c10
-rw-r--r--subversion/libsvn_subr/stream.c655
-rw-r--r--subversion/libsvn_subr/string.c127
-rw-r--r--subversion/libsvn_subr/subst.c45
-rw-r--r--subversion/libsvn_subr/sysinfo.c19
-rw-r--r--subversion/libsvn_subr/temp_serializer.c2
-rw-r--r--subversion/libsvn_subr/user.c19
-rw-r--r--subversion/libsvn_subr/utf.c31
-rw-r--r--subversion/libsvn_subr/utf8proc.c98
-rw-r--r--subversion/libsvn_subr/utf8proc/LICENSE.md (renamed from subversion/libsvn_subr/utf8proc/LICENSE)41
-rw-r--r--subversion/libsvn_subr/utf8proc/NEWS.md303
-rw-r--r--subversion/libsvn_subr/utf8proc/README116
-rw-r--r--subversion/libsvn_subr/utf8proc/README.md69
-rw-r--r--subversion/libsvn_subr/utf8proc/lump.md27
-rw-r--r--subversion/libsvn_subr/utf8proc/utf8proc.c631
-rw-r--r--subversion/libsvn_subr/utf8proc/utf8proc.h447
-rw-r--r--subversion/libsvn_subr/utf8proc/utf8proc_data.c27753
-rw-r--r--subversion/libsvn_subr/utf8proc/utf8proc_internal.h708
-rw-r--r--subversion/libsvn_subr/version.c21
-rw-r--r--subversion/libsvn_subr/win32_crashrpt.c75
-rw-r--r--subversion/libsvn_subr/win32_crashrpt_dll.h3
-rw-r--r--subversion/libsvn_subr/win32_crypto.c7
-rw-r--r--subversion/libsvn_subr/win32_xlate.c7
-rw-r--r--subversion/libsvn_subr/x509info.c7
-rw-r--r--subversion/libsvn_subr/x509parse.c53
-rw-r--r--subversion/libsvn_subr/xml.c86
-rw-r--r--subversion/libsvn_wc/adm_crawler.c114
-rw-r--r--subversion/libsvn_wc/conflicts.c752
-rw-r--r--subversion/libsvn_wc/copy.c13
-rw-r--r--subversion/libsvn_wc/crop.c7
-rw-r--r--subversion/libsvn_wc/deprecated.c46
-rw-r--r--subversion/libsvn_wc/diff.h2
-rw-r--r--subversion/libsvn_wc/diff_editor.c182
-rw-r--r--subversion/libsvn_wc/diff_local.c21
-rw-r--r--subversion/libsvn_wc/entries.c4
-rw-r--r--subversion/libsvn_wc/externals.c18
-rw-r--r--subversion/libsvn_wc/node.c3
-rw-r--r--subversion/libsvn_wc/old-and-busted.c3
-rw-r--r--subversion/libsvn_wc/props.c7
-rw-r--r--subversion/libsvn_wc/status.c10
-rw-r--r--subversion/libsvn_wc/translate.c17
-rw-r--r--subversion/libsvn_wc/upgrade.c506
-rw-r--r--subversion/libsvn_wc/wc-checks.h2
-rw-r--r--subversion/libsvn_wc/wc-metadata.h291
-rw-r--r--subversion/libsvn_wc/wc-metadata.sql243
-rw-r--r--subversion/libsvn_wc/wc-queries.h617
-rw-r--r--subversion/libsvn_wc/wc-queries.sql17
-rw-r--r--subversion/libsvn_wc/wc.h4
-rw-r--r--subversion/libsvn_wc/wc_db.c132
-rw-r--r--subversion/libsvn_wc/wc_db.h54
-rw-r--r--subversion/libsvn_wc/wc_db_pristine.c82
-rw-r--r--subversion/libsvn_wc/wc_db_update_move.c1837
-rw-r--r--subversion/libsvn_wc/wc_db_util.c2
-rw-r--r--subversion/libsvn_wc/wcroot_anchor.c20
-rw-r--r--subversion/libsvn_wc/workqueue.c21
-rw-r--r--subversion/svn/cl-conflicts.c308
-rw-r--r--subversion/svn/cl-conflicts.h7
-rw-r--r--subversion/svn/cl-log.h5
-rw-r--r--subversion/svn/cl.h96
-rw-r--r--subversion/svn/cleanup-cmd.c5
-rw-r--r--subversion/svn/conflict-callbacks.c2103
-rw-r--r--subversion/svn/diff-cmd.c37
-rw-r--r--subversion/svn/help-cmd.c34
-rw-r--r--subversion/svn/info-cmd.c181
-rw-r--r--subversion/svn/list-cmd.c31
-rw-r--r--subversion/svn/log-cmd.c42
-rw-r--r--subversion/svn/merge-cmd.c123
-rw-r--r--subversion/svn/notify.c134
-rw-r--r--subversion/svn/propdel-cmd.c8
-rw-r--r--subversion/svn/propedit-cmd.c46
-rw-r--r--subversion/svn/propget-cmd.c43
-rw-r--r--subversion/svn/propset-cmd.c20
-rw-r--r--subversion/svn/resolve-cmd.c193
-rw-r--r--subversion/svn/shelve-cmd.c369
-rw-r--r--subversion/svn/status.c7
-rw-r--r--subversion/svn/svn.c499
-rw-r--r--subversion/svn/switch-cmd.c10
-rw-r--r--subversion/svn/update-cmd.c13
-rw-r--r--subversion/svn/util.c22
-rw-r--r--subversion/svn_private_config.h.in29
-rw-r--r--subversion/svn_private_config.hw12
-rw-r--r--subversion/svnadmin/svnadmin.c823
-rw-r--r--subversion/svnbench/cl.h4
-rw-r--r--subversion/svnbench/null-export-cmd.c22
-rw-r--r--subversion/svnbench/null-list-cmd.c35
-rw-r--r--subversion/svnbench/null-log-cmd.c12
-rw-r--r--subversion/svnbench/svnbench.c167
-rw-r--r--subversion/svndumpfilter/svndumpfilter.c73
-rw-r--r--subversion/svnfsfs/load-index-cmd.c2
-rw-r--r--subversion/svnfsfs/stats-cmd.c13
-rw-r--r--subversion/svnfsfs/svnfsfs.c77
-rw-r--r--subversion/svnfsfs/svnfsfs.h3
-rw-r--r--subversion/svnlook/svnlook.c107
-rw-r--r--subversion/svnmucc/svnmucc.c58
-rw-r--r--subversion/svnrdump/dump_editor.c11
-rw-r--r--subversion/svnrdump/load_editor.c45
-rw-r--r--subversion/svnrdump/svnrdump.c148
-rw-r--r--subversion/svnrdump/svnrdump.h17
-rw-r--r--subversion/svnrdump/util.c34
-rw-r--r--subversion/svnserve/cyrus_auth.c56
-rw-r--r--subversion/svnserve/serve.c979
-rw-r--r--subversion/svnserve/server.h11
-rw-r--r--subversion/svnserve/svnserve.c112
-rw-r--r--subversion/svnsync/svnsync.c131
417 files changed, 77438 insertions, 33038 deletions
diff --git a/subversion/include/private/ra_svn_sasl.h b/subversion/include/private/ra_svn_sasl.h
index 428e20e8033c..e8284517a83a 100644
--- a/subversion/include/private/ra_svn_sasl.h
+++ b/subversion/include/private/ra_svn_sasl.h
@@ -27,14 +27,8 @@
#ifndef RA_SVN_SASL_H
#define RA_SVN_SASL_H
-#ifdef WIN32
-/* This prevents sasl.h from redefining iovec, which is always defined by APR
- on win32. */
-#define STRUCT_IOVEC_DEFINED
-#include <sasl.h>
-#else
-#include <sasl/sasl.h>
-#endif
+/* Keep this include statement at the top of this file. */
+#include "private/ra_svn_wrapped_sasl.h"
#include <apr_errno.h>
#include <apr_pools.h>
diff --git a/subversion/include/private/ra_svn_wrapped_sasl.h b/subversion/include/private/ra_svn_wrapped_sasl.h
new file mode 100644
index 000000000000..266a4f8279de
--- /dev/null
+++ b/subversion/include/private/ra_svn_wrapped_sasl.h
@@ -0,0 +1,131 @@
+/*
+ * svn_wrapped_sasl.h : wrapped SASL API
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+#ifndef RA_SVN_WRAPPED_SASL_H
+#define RA_SVN_WRAPPED_SASL_H
+
+#include <stddef.h>
+
+#ifdef WIN32
+# define APR_WANT_IOVEC
+# include <apr_want.h>
+ /* This prevents sasl.h from redefining iovec,
+ which is always defined by APR on win32. */
+# define STRUCT_IOVEC_DEFINED
+# include <sasl.h>
+#else
+# include <sasl/sasl.h>
+#endif
+
+/* Apple deprecated the SASL API on Mac OS X 10.11, causing a
+ moderately huge number of deprecation warnings to be emitted during
+ compilation. Consequently, we wrap the parts of the SASL API that
+ we use in a set of private functions and disable the deprecation
+ warnings for this header and the implementation file. */
+#ifdef __APPLE__
+# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+# endif
+#endif /* __APPLE__ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+svn_sasl__set_mutex(sasl_mutex_alloc_t *, sasl_mutex_lock_t *,
+ sasl_mutex_unlock_t *, sasl_mutex_free_t *);
+
+void
+svn_sasl__done(void);
+
+void
+svn_sasl__dispose(sasl_conn_t **);
+
+const char *
+svn_sasl__errstring(int, const char *, const char **);
+
+const char *
+svn_sasl__errdetail(sasl_conn_t *);
+
+int
+svn_sasl__getprop(sasl_conn_t *, int, const void **);
+
+int
+svn_sasl__setprop(sasl_conn_t *, int, const void *);
+
+int
+svn_sasl__client_init(const sasl_callback_t *);
+
+int
+svn_sasl__client_new(const char *, const char *, const char *, const char *,
+ const sasl_callback_t *, unsigned, sasl_conn_t **);
+
+int
+svn_sasl__client_start(sasl_conn_t *, const char *, sasl_interact_t **,
+ const char **, unsigned *, const char **);
+
+int
+svn_sasl__client_step(sasl_conn_t *, const char *, unsigned,
+ sasl_interact_t **, const char **, unsigned *);
+
+int
+svn_sasl__server_init(const sasl_callback_t *, const char *);
+
+int
+svn_sasl__server_new(const char *, const char *, const char *,
+ const char *, const char *, const sasl_callback_t *,
+ unsigned, sasl_conn_t **);
+
+int
+svn_sasl__listmech(sasl_conn_t *, const char *, const char *, const char *,
+ const char *, const char **, unsigned *, int *);
+
+int
+svn_sasl__server_start(sasl_conn_t *, const char *, const char *, unsigned,
+ const char **, unsigned *);
+
+int
+svn_sasl__server_step(sasl_conn_t *, const char *, unsigned,
+ const char **, unsigned *);
+
+int
+svn_sasl__encode(sasl_conn_t *, const char *, unsigned,
+ const char **, unsigned *);
+
+int
+svn_sasl__decode(sasl_conn_t *, const char *, unsigned,
+ const char **, unsigned *);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#ifdef __APPLE__
+# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)
+# pragma GCC diagnostic pop
+# endif
+#endif /* __APPLE__ */
+
+#endif /* RA_SVN_WRAPPED_SASL_H */
diff --git a/subversion/include/private/svn_atomic.h b/subversion/include/private/svn_atomic.h
index 6a6b2dca6be1..f86a39ec68c6 100644
--- a/subversion/include/private/svn_atomic.h
+++ b/subversion/include/private/svn_atomic.h
@@ -76,21 +76,81 @@ extern "C" {
/** @} */
/**
+ * @name Single-threaded atomic initialization
+ * @{
+ */
+
+/**
+ * Callback for svn_atomic__init_once().
+ * @return an #svn_error_t if the initialization fails.
+ * @since New in 1.10
+ */
+typedef svn_error_t *(*svn_atomic__err_init_func_t)(void *baton,
+ apr_pool_t *pool);
+
+/**
+ * Callback for svn_atomic__init_no_error().
+ * @return a string containing an error message if the initialization fails.
+ * @since New in 1.10
+ */
+typedef const char *(*svn_atomic__str_init_func_t)(void *baton);
+
+/**
* Call an initialization function in a thread-safe manner.
*
* @a global_status must be a pointer to a global, zero-initialized
- * #svn_atomic_t. @a init_func is a pointer to the function that performs
- * the actual initialization. @a baton and and @a pool are passed on to the
- * init_func for its use.
+ * #svn_atomic_t. @a err_init_func is a pointer to the function that
+ * performs the actual initialization. @a baton and and @a pool are
+ * passed on to @a err_init_func for its use.
+ *
+ * @return the error returned by @a err_init_func.
*
* @since New in 1.5.
*/
svn_error_t *
svn_atomic__init_once(volatile svn_atomic_t *global_status,
- svn_error_t *(*init_func)(void*,apr_pool_t*),
+ svn_atomic__err_init_func_t err_init_func,
void *baton,
apr_pool_t* pool);
+/**
+ * Call an initialization function in a thread-safe manner.
+ *
+ * Unlike svn_atomic__init_once(), this function does not need a pool
+ * and does not create an #svn_error_t, and neither should the
+ * @a str_init_func implementation.
+ *
+ * @a global_status must be a pointer to a global, zero-initialized
+ * #svn_atomic_t. @a str_init_func is a pointer to the function that
+ * performs the actual initialization. @a baton is passed on to
+ * @a str_init_func for its use.
+ *
+ * @return the error string returned by @a str_init_func.
+ *
+ * @since New in 1.10.
+ */
+const char *
+svn_atomic__init_once_no_error(volatile svn_atomic_t *global_status,
+ svn_atomic__str_init_func_t str_init_func,
+ void *baton);
+
+
+/**
+ * Query and increment the global counter and set @a value to the new
+ * counter value.
+ *
+ * This function is thread-safe and you should call it whenever you need
+ * a number that is unique within the current process. The values are > 0.
+ *
+ * @return the error object in case of a synchronization failure.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_atomic__unique_counter(apr_uint64_t* value);
+
+/** @} */
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/subversion/include/private/svn_branch.h b/subversion/include/private/svn_branch.h
new file mode 100644
index 000000000000..3fbaeb7ef77c
--- /dev/null
+++ b/subversion/include/private/svn_branch.h
@@ -0,0 +1,679 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file svn_branch.h
+ * @brief Operating on a branched version history
+ *
+ * @since New in ???.
+ */
+
+/* Transactions
+ *
+ * A 'txn' contains a set of changes to the branches/elements.
+ *
+ * To make changes you say, for example, "for element 5: I want the parent
+ * element to be 3 now, and its name to be 'bar', and its content to be
+ * {props=... text=...}". That sets up a move and/or rename and/or
+ * content-change (or possibly a no-op for all three aspects) for element 5.
+ *
+ * Before or after (or at the same time, if we make a parallelizable
+ * implementation) we can make edits to the other elements, including
+ * element 3.
+ *
+ * So at the time of the edit method 'change e5: let its parent be e3'
+ * we might or might not have even created e3, if that happens to be an
+ * element that we wish to create rather than one that already existed.
+ *
+ * We allow this non-ordering because we want the changes to different
+ * elements to be totally independent.
+ *
+ * So at any given 'moment' in time during specifying the changes to a
+ * txn, the txn state is not necessarily one that maps directly to a
+ * flat tree (single-rooted, no cycles, no clashes of paths, etc.).
+ *
+ * Once we've finished specifying the edits, then the txn state will be
+ * converted to a flat tree, and that's the final result. But we can't
+ * query an arbitrary txn (potentially in the middle of making changes
+ * to it) by path, because the paths are not fully defined yet.
+ *
+ * So there are three kinds of operations:
+ *
+ * - query involving paths
+ * => requires a flat tree state to query, not an in-progress txn
+ *
+ * - query, not involving paths
+ * => accepts a txn-in-progress or a flat tree
+ *
+ * - modify (not involving paths)
+ * => requires a txn
+ *
+ * Currently, a txn is represented by 'svn_branch__txn_t', with
+ * 'svn_branch__state_t' for the individual branches in it. A flat tree is
+ * represented by 'svn_branch__subtree_t'. But there is currently not a
+ * clean separation; there is some overlap and some warts such as the
+ * 'svn_branch__txn_sequence_point' method.
+ */
+
+
+#ifndef SVN_BRANCH_H
+#define SVN_BRANCH_H
+
+#include <apr_pools.h>
+
+#include "svn_types.h"
+#include "svn_error.h"
+#include "svn_io.h" /* for svn_stream_t */
+#include "svn_delta.h"
+
+#include "private/svn_element.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/* ### */
+#define SVN_BRANCH__ERR 123456
+
+/** Element Identifier (EID).
+ *
+ * An element may appear in any or all branches, and its EID is the same in
+ * each branch in which the element appears.
+ *
+ * By definition, an element keeps the same EID for its whole lifetime, even
+ * if deleted from all branches and later 'resurrected'.
+ *
+ * In principle, an EID is an arbitrary token and has no intrinsic
+ * relationships (except equality) to other EIDs. The current implementation
+ * uses integers and allocates them sequentially from a central counter, but
+ * the implementation may be changed.
+ *
+ * ### In most places the code currently says 'int', verbatim.
+ */
+typedef int svn_branch__eid_t;
+
+typedef struct svn_branch__el_rev_id_t svn_branch__el_rev_id_t;
+
+typedef struct svn_branch__rev_bid_eid_t svn_branch__rev_bid_eid_t;
+
+typedef struct svn_branch__rev_bid_t svn_branch__rev_bid_t;
+
+typedef struct svn_branch__state_t svn_branch__state_t;
+
+/* Per-repository branching info.
+ */
+typedef struct svn_branch__repos_t svn_branch__repos_t;
+
+/* Methods (conceptually public, but called indirectly) for a transaction.
+ */
+typedef struct svn_branch__txn_vtable_t svn_branch__txn_vtable_t;
+
+/* Private data for a transaction.
+ */
+typedef struct svn_branch__txn_priv_t svn_branch__txn_priv_t;
+
+/* A container for all the branching metadata for a specific revision (or
+ * an uncommitted transaction).
+ */
+typedef struct svn_branch__txn_t
+{
+ /* Methods (conceptually public, but called indirectly). */
+ svn_branch__txn_vtable_t *vtable;
+
+ /* Private data. */
+ svn_branch__txn_priv_t *priv;
+
+ /* Public data. */
+
+ /* The repository in which this revision exists. */
+ svn_branch__repos_t *repos;
+
+ /* If committed, the revision number; else SVN_INVALID_REVNUM. */
+ svn_revnum_t rev;
+
+ /* If committed, the previous revision number, else the revision number
+ on which this transaction is based. */
+ svn_revnum_t base_rev;
+
+} svn_branch__txn_t;
+
+/* Create a new branch txn object.
+ */
+svn_branch__txn_t *
+svn_branch__txn_create(const svn_branch__txn_vtable_t *vtable,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *result_pool);
+
+/* Return all the branches in TXN.
+ *
+ * These branches are available for reading. (Some of them may also be
+ * mutable.)
+ *
+ * ### Rename to 'list_branches' & return only their ids?
+ *
+ * Return an empty array if there are none.
+ */
+apr_array_header_t *
+svn_branch__txn_get_branches(const svn_branch__txn_t *txn,
+ apr_pool_t *result_pool);
+
+/* Return the branch whose id is BRANCH_ID in TXN.
+ *
+ * Return NULL if not found.
+ *
+ * Note: a branch id is, in behavioural terms, an arbitrary token. In the
+ * current implementation it is constructed from the hierarchy of subbranch
+ * root EIDs leading to the branch, but that may be changed in future.
+ *
+ * See also: svn_branch__get_id().
+ */
+svn_branch__state_t *
+svn_branch__txn_get_branch_by_id(const svn_branch__txn_t *txn,
+ const char *branch_id,
+ apr_pool_t *scratch_pool);
+
+svn_error_t *
+svn_branch__txn_get_num_new_eids(const svn_branch__txn_t *txn,
+ int *num_new_eids_p,
+ apr_pool_t *scratch_pool);
+
+/* Assign a new txn-scope element id in TXN.
+ */
+svn_error_t *
+svn_branch__txn_new_eid(svn_branch__txn_t *txn,
+ int *new_eid_p,
+ apr_pool_t *scratch_pool);
+
+/** Open for writing, either a new branch or an existing branch.
+ *
+ * When creating a new branch, declare its root element id to be ROOT_EID. Do
+ * not instantiate the root element, nor any other elements.
+ *
+ * TREE_REF specifies the initial tree content, by reference to a committed
+ * tree. It overwrites any existing tree, even if the branch was already
+ * mutable in the txn.
+ *
+ * If TREE_REF is null, then the initial tree is empty for a new branch
+ * (not already present in the txn), or the branch's current tree if the
+ * branch was already present (readable or mutable) in the txn.
+ *
+ * ### TODO: Take a 'history' parameter; 'none' is a valid option.
+ *
+ * We use a common 'open subbranch' method for both 'find' and 'add'
+ * cases, according to the principle that 'editing' a txn should dictate
+ * the new state without reference to the old state.
+ *
+ * This method returns a mutable 'branch state' object which is a part of
+ * the txn.
+ *
+ * ### When opening ('finding') an existing branch, ROOT_EID should match
+ * it. (Should we check, and throw an error if not?)
+ */
+svn_error_t *
+svn_branch__txn_open_branch(svn_branch__txn_t *txn,
+ svn_branch__state_t **new_branch_p,
+ const char *branch_id,
+ int root_eid,
+ svn_branch__rev_bid_eid_t *tree_ref,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/** Register a sequence point.
+ *
+ * At a sequence point, elements are arranged in a tree hierarchy: each
+ * element has exactly one parent element, except the root, and so on.
+ * Translation between paths and element addressing is defined only at
+ * a sequence point.
+ *
+ * The other edit operations -- add, alter, delete, etc. -- result in a
+ * state that is not a sequence point.
+ *
+ * The new transaction begins at a sequence point. Completion of editing
+ * (svn_branch__txn_complete()) also creates a sequence point.
+ */
+svn_error_t *
+svn_branch__txn_sequence_point(svn_branch__txn_t *txn,
+ apr_pool_t *scratch_pool);
+
+/** Finalize this transaction.
+ *
+ * Notify that the edit has been completed successfully.
+ */
+svn_error_t *
+svn_branch__txn_complete(svn_branch__txn_t *txn,
+ apr_pool_t *scratch_pool);
+
+/** Abandon this transaction.
+ *
+ * Notify that editing this transaction was not successful.
+ */
+svn_error_t *
+svn_branch__txn_abort(svn_branch__txn_t *txn,
+ apr_pool_t *scratch_pool);
+
+/* Change txn-local EIDs (negative integers) in TXN to revision EIDs, by
+ * assigning a new revision-EID (positive integer) for each one.
+ *
+ * Rewrite TXN->first_eid and TXN->next_eid accordingly.
+ */
+svn_error_t *
+svn_branch__txn_finalize_eids(svn_branch__txn_t *txn,
+ apr_pool_t *scratch_pool);
+
+/* Often, branches have the same root element. For example,
+ * branching /trunk to /branches/br1 results in:
+ *
+ * branch 1: (root-EID=100)
+ * EID 100 => /trunk
+ * ...
+ * branch 2: (root-EID=100)
+ * EID 100 => /branches/br1
+ * ...
+ *
+ * However, the root element of one branch may correspond to a non-root
+ * element of another branch.
+ *
+ * Continuing the same example, branching from the trunk subtree
+ * /trunk/D (which is not itself a branch root) results in:
+ *
+ * branch 3: (root-EID=104)
+ * EID 100 => (nil)
+ * ...
+ * EID 104 => /branches/branch-of-trunk-subtree-D
+ * ...
+ */
+
+/* Methods (conceptually public, but called indirectly) for a branch state.
+ */
+typedef struct svn_branch__state_vtable_t svn_branch__state_vtable_t;
+
+/* Private data for a branch state.
+ */
+typedef struct svn_branch__state_priv_t svn_branch__state_priv_t;
+
+/* A branch state.
+ *
+ * A branch state object describes one version of one branch.
+ */
+struct svn_branch__state_t
+{
+ /* Methods (conceptually public, but called indirectly). */
+ svn_branch__state_vtable_t *vtable;
+
+ /* Private data. */
+ svn_branch__state_priv_t *priv;
+
+ /* Public data. */
+
+ /* The branch identifier (starting with 'B') */
+ const char *bid;
+
+ /* The revision to which this branch state belongs */
+ /* ### Later we should remove this and let a single state be sharable
+ by multiple txns. */
+ svn_branch__txn_t *txn;
+
+};
+
+/* Create a new branch state object.
+ */
+svn_branch__state_t *
+svn_branch__state_create(const svn_branch__state_vtable_t *vtable,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *result_pool);
+
+/* Get the full id of branch BRANCH.
+ *
+ * Branch id format:
+ * B<top-level-branch-num>[.<1st-level-eid>[.<2nd-level-eid>[...]]]
+ *
+ * Note: a branch id is, in behavioural terms, an arbitrary token. In the
+ * current implementation it is constructed from the hierarchy of subbranch
+ * root EIDs leading to the branch, but that may be changed in future.
+ *
+ * See also: svn_branch__txn_get_branch_by_id().
+ */
+const char *
+svn_branch__get_id(const svn_branch__state_t *branch,
+ apr_pool_t *result_pool);
+
+/* Return the element id of the root element of BRANCH.
+ */
+int
+svn_branch__root_eid(const svn_branch__state_t *branch);
+
+/* Return the id of the branch nested in OUTER_BID at element OUTER_EID.
+ *
+ * For a top-level branch, OUTER_BID is null and OUTER_EID is the
+ * top-level branch number.
+ *
+ * (Such branches need not exist. This works purely with ids, making use
+ * of the fact that nested branch ids are predictable based on the nesting
+ * element id.)
+ */
+const char *
+svn_branch__id_nest(const char *outer_bid,
+ int outer_eid,
+ apr_pool_t *result_pool);
+
+/* Given a nested branch id BID, set *OUTER_BID to the outer branch's id
+ * and *OUTER_EID to the nesting element in the outer branch.
+ *
+ * For a top-level branch, set *OUTER_BID to NULL and *OUTER_EID to the
+ * top-level branch number.
+ *
+ * (Such branches need not exist. This works purely with ids, making use
+ * of the fact that nested branch ids are predictable based on the nesting
+ * element id.)
+ */
+void
+svn_branch__id_unnest(const char **outer_bid,
+ int *outer_eid,
+ const char *bid,
+ apr_pool_t *result_pool);
+
+/* Remove the branch with id BID from the list of branches in TXN.
+ */
+svn_error_t *
+svn_branch__txn_delete_branch(svn_branch__txn_t *txn,
+ const char *bid,
+ apr_pool_t *scratch_pool);
+
+/* Branch-Element-Revision */
+struct svn_branch__el_rev_id_t
+{
+ /* The branch state that applies to REV. */
+ svn_branch__state_t *branch;
+ /* Element. */
+ int eid;
+ /* Revision. SVN_INVALID_REVNUM means 'in this transaction', not 'head'.
+ ### Do we need this if BRANCH refers to a particular branch-revision? */
+ svn_revnum_t rev;
+
+};
+
+/* Revision-branch-element id. */
+struct svn_branch__rev_bid_eid_t
+{
+ /* Revision. SVN_INVALID_REVNUM means 'in this transaction', not 'head'. */
+ svn_revnum_t rev;
+ /* The branch id in revision REV. */
+ const char *bid;
+ /* Element id. */
+ int eid;
+
+};
+
+/* Revision-branch id. */
+struct svn_branch__rev_bid_t
+{
+ /* Revision. SVN_INVALID_REVNUM means 'in this transaction', not 'head'. */
+ svn_revnum_t rev;
+ /* The branch id in revision REV. */
+ const char *bid;
+
+};
+
+/* Return a new el_rev_id object constructed with *shallow* copies of BRANCH,
+ * EID and REV, allocated in RESULT_POOL.
+ */
+svn_branch__el_rev_id_t *
+svn_branch__el_rev_id_create(svn_branch__state_t *branch,
+ int eid,
+ svn_revnum_t rev,
+ apr_pool_t *result_pool);
+
+/* Return a new id object constructed with a deep copy of OLD_ID,
+ * allocated in RESULT_POOL. */
+svn_branch__el_rev_id_t *
+svn_branch__el_rev_id_dup(const svn_branch__el_rev_id_t *old_id,
+ apr_pool_t *result_pool);
+
+/* Return a new id object constructed with deep copies of REV, BRANCH_ID
+ * and EID, allocated in RESULT_POOL.
+ */
+svn_branch__rev_bid_eid_t *
+svn_branch__rev_bid_eid_create(svn_revnum_t rev,
+ const char *branch_id,
+ int eid,
+ apr_pool_t *result_pool);
+svn_branch__rev_bid_t *
+svn_branch__rev_bid_create(svn_revnum_t rev,
+ const char *branch_id,
+ apr_pool_t *result_pool);
+
+/* Return a new id object constructed with a deep copy of OLD_ID,
+ * allocated in RESULT_POOL. */
+svn_branch__rev_bid_eid_t *
+svn_branch__rev_bid_eid_dup(const svn_branch__rev_bid_eid_t *old_id,
+ apr_pool_t *result_pool);
+svn_branch__rev_bid_t *
+svn_branch__rev_bid_dup(const svn_branch__rev_bid_t *old_id,
+ apr_pool_t *result_pool);
+
+svn_boolean_t
+svn_branch__rev_bid_equal(const svn_branch__rev_bid_t *id1,
+ const svn_branch__rev_bid_t *id2);
+
+typedef struct svn_branch__history_t
+{
+ /* The immediate parents of this state in the branch/merge graph.
+ Hash of (BID -> svn_branch__rev_bid_t). */
+ apr_hash_t *parents;
+} svn_branch__history_t;
+
+svn_branch__history_t *
+svn_branch__history_create_empty(apr_pool_t *result_pool);
+
+svn_branch__history_t *
+svn_branch__history_create(apr_hash_t *parents,
+ apr_pool_t *result_pool);
+
+svn_branch__history_t *
+svn_branch__history_dup(const svn_branch__history_t *old,
+ apr_pool_t *result_pool);
+
+/* Return the mapping of elements in branch BRANCH.
+ */
+svn_error_t *
+svn_branch__state_get_elements(const svn_branch__state_t *branch,
+ svn_element__tree_t **element_tree_p,
+ apr_pool_t *result_pool);
+
+/* In BRANCH, get element EID (parent, name, payload).
+ *
+ * If element EID is not present, return null.
+ */
+svn_error_t *
+svn_branch__state_get_element(const svn_branch__state_t *branch,
+ svn_element__content_t **element_p,
+ int eid,
+ apr_pool_t *result_pool);
+
+/** Equivalent to
+ * alter_one(..., element->parent_eid, element->name, element->payload),
+ * or, if @a element is null, to
+ * delete_one(...).
+ */
+svn_error_t *
+svn_branch__state_set_element(svn_branch__state_t *branch,
+ int eid,
+ const svn_element__content_t *element,
+ apr_pool_t *result_pool);
+
+/** Specify that the element of @a branch identified by @a eid shall not
+ * be present.
+ *
+ * The delete is not explicitly recursive. However, as an effect of the
+ * final 'flattening' of a branch state into a single tree, each element
+ * in the final state that still has this element as its parent will also
+ * be deleted, recursively.
+ *
+ * The element @a eid must not be the root element of @a branch.
+ *
+ * ### Options for Out-Of-Date Checking on Rebase
+ *
+ * We may want to specify what kind of OOD check takes place. The
+ * following two options differ in what happens to an element that is
+ * added, on the other side, as a child of this deleted element.
+ *
+ * Rebase option 1: The rebase checks for changes in the whole subtree,
+ * excluding any portions of the subtree for which an explicit delete or
+ * move-away has been issued. The check includes checking that the other
+ * side has not added any child. In other words, the deletion is
+ * interpreted as an action affecting a subtree (dynamically rooted at
+ * this element), rather than as an action affecting a single element or
+ * a fixed set of elements that was explicitly or implicitly specified
+ * by the sender.
+ *
+ * To delete a mixed-rev subtree, the client sends an explicit delete for
+ * each subtree that has a different base revision from its parent.
+ *
+ * Rebase option 2: The rebase checks for changes to this element only.
+ * The sender can send an explicit delete for each existing child element
+ * that it requires to be checked as well. However, there is no way for
+ * the sender to specify whether a child element added by the other side
+ * should be considered an out-of-date error or silently deleted.
+ *
+ * It would also be possible to let the caller specify, at some suitable
+ * granularity, which option to use.
+ */
+svn_error_t *
+svn_branch__state_delete_one(svn_branch__state_t *branch,
+ svn_branch__eid_t eid,
+ apr_pool_t *scratch_pool);
+
+/** Specify the tree position and payload of the element of @a branch
+ * identified by @a eid.
+ *
+ * Set the element's parent EID, name and payload to @a new_parent_eid,
+ * @a new_name and @a new_payload respectively.
+ *
+ * This may create a new element or alter an existing element.
+ *
+ * If the element ... we can describe the effect as ...
+ *
+ * exists in the branch => altering it;
+ * previously existed in the branch => resurrecting it;
+ * only existed in other branches => branching it;
+ * never existed anywhere => creating or adding it.
+ *
+ * However, these are imprecise descriptions and not mutually exclusive.
+ * For example, if it existed previously in this branch and another, then
+ * we may describe the result as 'resurrecting' and/or as 'branching'.
+ *
+ * Duplicate @a new_name and @a new_payload into the branch's pool.
+ */
+svn_error_t *
+svn_branch__state_alter_one(svn_branch__state_t *branch,
+ svn_branch__eid_t eid,
+ svn_branch__eid_t new_parent_eid,
+ const char *new_name,
+ const svn_element__payload_t *new_payload,
+ apr_pool_t *scratch_pool);
+
+svn_error_t *
+svn_branch__state_copy_tree(svn_branch__state_t *branch,
+ const svn_branch__rev_bid_eid_t *src_el_rev,
+ svn_branch__eid_t new_parent_eid,
+ const char *new_name,
+ apr_pool_t *scratch_pool);
+
+/* Purge orphaned elements in BRANCH.
+ */
+svn_error_t *
+svn_branch__state_purge(svn_branch__state_t *branch,
+ apr_pool_t *scratch_pool);
+
+/* Get the merge history of BRANCH.
+ */
+svn_error_t *
+svn_branch__state_get_history(svn_branch__state_t *branch,
+ svn_branch__history_t **merge_history_p,
+ apr_pool_t *result_pool);
+
+/* Set the merge history of BRANCH.
+ */
+svn_error_t *
+svn_branch__state_set_history(svn_branch__state_t *branch,
+ const svn_branch__history_t *merge_history,
+ apr_pool_t *scratch_pool);
+
+/* Return the branch-relative path of element EID in BRANCH.
+ *
+ * If the element EID does not currently exist in BRANCH, return NULL.
+ *
+ * ### TODO: Clarify sequencing requirements.
+ */
+const char *
+svn_branch__get_path_by_eid(const svn_branch__state_t *branch,
+ int eid,
+ apr_pool_t *result_pool);
+
+/* Return the EID for the branch-relative path PATH in BRANCH.
+ *
+ * If no element of BRANCH is at this path, return -1.
+ *
+ * ### TODO: Clarify sequencing requirements.
+ */
+int
+svn_branch__get_eid_by_path(const svn_branch__state_t *branch,
+ const char *path,
+ apr_pool_t *scratch_pool);
+
+/* Get the default branching metadata for r0 of a new repository.
+ */
+svn_string_t *
+svn_branch__get_default_r0_metadata(apr_pool_t *result_pool);
+
+/* Create a new txn object *TXN_P, initialized with info
+ * parsed from STREAM, allocated in RESULT_POOL.
+ */
+svn_error_t *
+svn_branch__txn_parse(svn_branch__txn_t **txn_p,
+ svn_branch__repos_t *repos,
+ svn_stream_t *stream,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* Write to STREAM a parseable representation of TXN.
+ */
+svn_error_t *
+svn_branch__txn_serialize(svn_branch__txn_t *txn,
+ svn_stream_t *stream,
+ apr_pool_t *scratch_pool);
+
+/* Write to STREAM a parseable representation of BRANCH.
+ */
+svn_error_t *
+svn_branch__state_serialize(svn_stream_t *stream,
+ svn_branch__state_t *branch,
+ apr_pool_t *scratch_pool);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVN_BRANCH_H */
diff --git a/subversion/include/private/svn_branch_compat.h b/subversion/include/private/svn_branch_compat.h
new file mode 100644
index 000000000000..8c7d0b2a04dc
--- /dev/null
+++ b/subversion/include/private/svn_branch_compat.h
@@ -0,0 +1,273 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file svn_branch_compat.h
+ * @brief Compatibility with svn_delta_editor_t etc.
+ *
+ * @since New in ???.
+ */
+
+#ifndef SVN_BRANCH_COMPAT_H
+#define SVN_BRANCH_COMPAT_H
+
+#include <apr_pools.h>
+
+#include "svn_types.h"
+#include "svn_error.h"
+#include "svn_delta.h"
+#include "svn_ra.h"
+#include "private/svn_branch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/** Callback to retrieve a node's kind and content. This is
+ * needed by the various editor shims in order to effect backwards
+ * compatibility.
+ *
+ * Implementations should set @a *kind to the node kind of @a repos_relpath
+ * in @a revision.
+ *
+ * Implementations should set @a *props to the hash of properties
+ * associated with @a repos_relpath in @a revision, allocating that hash
+ * and its contents in @a result_pool. Only the 'regular' props should be
+ * included, not special props such as 'entry props'.
+ *
+ * Implementations should set @a *filename to the name of a file
+ * suitable for use as a delta base for @a repos_relpath in @a revision
+ * (allocating @a *filename from @a result_pool), or to @c NULL if the
+ * base stream is empty.
+ *
+ * Any output argument may be NULL if the output is not wanted.
+ *
+ * @a baton is an implementation-specific closure.
+ * @a repos_relpath is relative to the repository root.
+ * The implementation should ensure that @a new_content, including any
+ * file therein, lives at least for the life time of @a result_pool.
+ * @a scratch_pool is provided for temporary allocations.
+ */
+typedef svn_error_t *(*svn_branch__compat_fetch_func_t)(
+ svn_node_kind_t *kind,
+ apr_hash_t **props,
+ svn_stringbuf_t **file_text,
+ apr_hash_t **children_names,
+ void *baton,
+ const char *repos_relpath,
+ svn_revnum_t revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool
+ );
+
+/*
+ */
+svn_error_t *
+svn_branch__compat_fetch(svn_element__payload_t **payload_p,
+ svn_branch__txn_t *txn,
+ svn_element__branch_ref_t branch_ref,
+ svn_branch__compat_fetch_func_t fetch_func,
+ void *fetch_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* An object for communicating out-of-band details between an Ev1-to-Ev3
+ * shim and an Ev3-to-Ev1 shim. */
+typedef struct svn_branch__compat_shim_connector_t svn_branch__compat_shim_connector_t;
+
+/* Return an Ev3 editor in *EDITOR_P which will drive the Ev1 delta
+ * editor DEDITOR/DEDIT_BATON.
+ *
+ * This editor buffers all the changes and then drives the Ev1 when the
+ * returned editor's "close" method is called.
+ *
+ * This editor converts moves into copy-and-delete. It presently makes a
+ * one-way (lossy) conversion.
+ *
+ * TODO: Option to pass the 'move' information through as some sort of
+ * metadata so that it can be preserved in an Ev3-Ev1-Ev3 round-trip
+ * conversion.
+ * - Use 'entry-props'?
+ * - Send copy-and-delete with copy-from-rev = -1?
+ *
+ * This editor implements the "independent per-element changes" variant
+ * of the Ev3 commit editor interface.
+ *
+ * Use *BRANCHING_TXN as the branching state info ...
+ *
+ * SHIM_CONNECTOR can be used to enable a more exact round-trip conversion
+ * from an Ev1 drive to Ev3 and back to Ev1. The caller should pass the
+ * returned *SHIM_CONNECTOR value to svn_delta__delta_from_ev3_for_commit().
+ * SHIM_CONNECTOR may be null if not wanted.
+ *
+ * REPOS_ROOT_URL is the repository root URL.
+ *
+ * FETCH_FUNC/FETCH_BATON is a callback by which the shim may retrieve the
+ * original or copy-from kind/properties/text for a path being committed.
+ *
+ * CANCEL_FUNC / CANCEL_BATON: The usual cancellation callback; folded
+ * into the produced editor. May be NULL/NULL if not wanted.
+ *
+ * Allocate the new editor in RESULT_POOL, which may become large and must
+ * live for the lifetime of the edit. Use SCRATCH_POOL for temporary
+ * allocations.
+ */
+svn_error_t *
+svn_branch__compat_txn_from_delta_for_commit(
+ svn_branch__txn_t **txn_p,
+ svn_branch__compat_shim_connector_t **shim_connector,
+ const svn_delta_editor_t *deditor,
+ void *dedit_baton,
+ svn_branch__txn_t *branching_txn,
+ const char *repos_root_url,
+ svn_branch__compat_fetch_func_t fetch_func,
+ void *fetch_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* Return a delta editor in DEDITOR/DEDITOR_BATON which will drive EDITOR.
+ *
+ * REPOS_ROOT_URL is the repository root URL, and BASE_RELPATH is the
+ * relative path within the repository of the root directory of the edit.
+ * (An Ev1 edit must be rooted at a directory, not at a file.)
+ *
+ * FETCH_FUNC/FETCH_BATON is a callback by which the shim may retrieve the
+ * original or copy-from kind/properties/text for a path being committed.
+ *
+ * SHIM_CONNECTOR can be used to enable a more exact round-trip conversion
+ * from an Ev1 drive to Ev3 and back to Ev1. It must live for the lifetime
+ * of the edit. It may be null if not wanted.
+ *
+ * Allocate the new editor in RESULT_POOL, which may become large and must
+ * live for the lifetime of the edit. Use SCRATCH_POOL for temporary
+ * allocations.
+ */
+svn_error_t *
+svn_branch__compat_delta_from_txn_for_commit(
+ const svn_delta_editor_t **deditor,
+ void **dedit_baton,
+ svn_branch__txn_t *edit_txn,
+ const char *repos_root_url,
+ const char *base_relpath,
+ svn_branch__compat_fetch_func_t fetch_func,
+ void *fetch_baton,
+ const svn_branch__compat_shim_connector_t *shim_connector,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* Return in NEW_DEDITOR/NEW_DETIT_BATON a delta editor that wraps
+ * OLD_DEDITOR/OLD_DEDIT_BATON, inserting a pair of shims that convert
+ * Ev1 to Ev3 and back to Ev1.
+ *
+ * REPOS_ROOT_URL is the repository root URL, and BASE_RELPATH is the
+ * relative path within the repository of the root directory of the edit.
+ *
+ * FETCH_FUNC/FETCH_BATON is a callback by which the shim may retrieve the
+ * original or copy-from kind/properties/text for a path being committed.
+ */
+svn_error_t *
+svn_branch__compat_insert_shims(
+ const svn_delta_editor_t **new_deditor,
+ void **new_dedit_baton,
+ const svn_delta_editor_t *old_deditor,
+ void *old_dedit_baton,
+ const char *repos_root,
+ const char *base_relpath,
+ svn_branch__compat_fetch_func_t fetch_func,
+ void *fetch_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* A callback for declaring the target revision of an update or switch.
+ */
+typedef svn_error_t *(*svn_branch__compat_set_target_revision_func_t)(
+ void *baton,
+ svn_revnum_t target_revision,
+ apr_pool_t *scratch_pool);
+
+/* An update (or switch) editor.
+ *
+ * This consists of a plain Ev3 editor and the additional methods or
+ * resources needed for use as an update or switch editor.
+ */
+typedef struct svn_branch__compat_update_editor3_t {
+ /* The txn we're driving. */
+ svn_branch__txn_t *edit_txn;
+
+ /* A method to communicate the target revision of the update (or switch),
+ * to be called before driving the editor. It has its own baton, rather
+ * than using the editor's baton, so that the editor can be replaced (by
+ * a wrapper editor, typically) without having to wrap this callback. */
+ svn_branch__compat_set_target_revision_func_t set_target_revision_func;
+ void *set_target_revision_baton;
+} svn_branch__compat_update_editor3_t;
+
+/* Like svn_delta__ev3_from_delta_for_commit() but for an update editor.
+ */
+svn_error_t *
+svn_branch__compat_txn_from_delta_for_update(
+ svn_branch__compat_update_editor3_t **editor_p,
+ const svn_delta_editor_t *deditor,
+ void *dedit_baton,
+ svn_branch__txn_t *branching_txn,
+ const char *repos_root_url,
+ const char *base_repos_relpath,
+ svn_branch__compat_fetch_func_t fetch_func,
+ void *fetch_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* Like svn_delta__delta_from_ev3_for_commit() but for an update editor.
+ */
+svn_error_t *
+svn_branch__compat_delta_from_txn_for_update(
+ const svn_delta_editor_t **deditor,
+ void **dedit_baton,
+ svn_branch__compat_update_editor3_t *update_editor,
+ const char *repos_root_url,
+ const char *base_repos_relpath,
+ svn_branch__compat_fetch_func_t fetch_func,
+ void *fetch_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* An Ev1 editor that drives (heuristically) a move-tracking editor.
+ */
+svn_error_t *
+svn_branch__compat_get_migration_editor(
+ const svn_delta_editor_t **old_editor,
+ void **old_edit_baton,
+ svn_branch__txn_t *edit_txn,
+ svn_ra_session_t *from_session,
+ svn_revnum_t revision,
+ apr_pool_t *result_pool);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVN_BRANCH_COMPAT_H */
diff --git a/subversion/include/private/svn_branch_impl.h b/subversion/include/private/svn_branch_impl.h
new file mode 100644
index 000000000000..ad4df3b1b12d
--- /dev/null
+++ b/subversion/include/private/svn_branch_impl.h
@@ -0,0 +1,197 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file svn_branch_impl.h
+ * @brief Declarations needed by implementators of branch classes
+ *
+ * @since New in ???.
+ */
+
+#ifndef SVN_BRANCH_IMPL_H
+#define SVN_BRANCH_IMPL_H
+
+#include "private/svn_branch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Common aspects od a txn/branch 'editor' class (derived from Ev2) */
+typedef struct svn_branch__vtable_priv_t
+{
+ /* Standard cancellation function. Called before each callback. */
+ svn_cancel_func_t cancel_func;
+ void *cancel_baton;
+
+#ifdef ENABLE_ORDERING_CHECK
+ svn_boolean_t within_callback;
+ svn_boolean_t finished;
+ apr_pool_t *state_pool;
+#endif
+
+} svn_branch__vtable_priv_t;
+
+/* The methods of svn_branch__txn_t.
+ * See the corresponding public API functions for details.
+ */
+
+typedef apr_array_header_t *(*svn_branch__txn_v_get_branches_t)(
+ const svn_branch__txn_t *txn,
+ apr_pool_t *result_pool);
+
+typedef svn_error_t *(*svn_branch__txn_v_delete_branch_t)(
+ svn_branch__txn_t *txn,
+ const char *bid,
+ apr_pool_t *scratch_pool);
+
+typedef svn_error_t *(*svn_branch__txn_v_get_num_new_eids_t)(
+ const svn_branch__txn_t *txn,
+ int *num_new_eids_p,
+ apr_pool_t *scratch_pool);
+
+typedef svn_error_t *(*svn_branch__txn_v_new_eid_t)(
+ svn_branch__txn_t *txn,
+ svn_branch__eid_t *eid_p,
+ apr_pool_t *scratch_pool);
+
+typedef svn_error_t *(*svn_branch__txn_v_open_branch_t)(
+ svn_branch__txn_t *txn,
+ svn_branch__state_t **new_branch_p,
+ const char *new_branch_id,
+ int root_eid,
+ svn_branch__rev_bid_eid_t *from,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+typedef svn_error_t *(*svn_branch__txn_v_finalize_eids_t)(
+ svn_branch__txn_t *txn,
+ apr_pool_t *scratch_pool);
+
+typedef svn_error_t *(*svn_branch__txn_v_serialize_t)(
+ svn_branch__txn_t *txn,
+ svn_stream_t *stream,
+ apr_pool_t *scratch_pool);
+
+typedef svn_error_t *(*svn_branch__txn_v_sequence_point_t)(
+ svn_branch__txn_t *txn,
+ apr_pool_t *scratch_pool);
+
+typedef svn_error_t *(*svn_branch__txn_v_complete_t)(
+ svn_branch__txn_t *txn,
+ apr_pool_t *scratch_pool);
+
+typedef svn_error_t *(*svn_branch__txn_v_abort_t)(
+ svn_branch__txn_t *txn,
+ apr_pool_t *scratch_pool);
+
+struct svn_branch__txn_vtable_t
+{
+ svn_branch__vtable_priv_t vpriv;
+
+ /* Methods. */
+ svn_branch__txn_v_get_branches_t get_branches;
+ svn_branch__txn_v_delete_branch_t delete_branch;
+ svn_branch__txn_v_get_num_new_eids_t get_num_new_eids;
+ svn_branch__txn_v_new_eid_t new_eid;
+ svn_branch__txn_v_open_branch_t open_branch;
+ svn_branch__txn_v_finalize_eids_t finalize_eids;
+ svn_branch__txn_v_serialize_t serialize;
+ svn_branch__txn_v_sequence_point_t sequence_point;
+ svn_branch__txn_v_complete_t complete;
+ svn_branch__txn_v_complete_t abort;
+
+};
+
+/* The methods of svn_branch__state_t.
+ * See the corresponding public API functions for details.
+ */
+
+typedef svn_error_t *(*svn_branch__state_v_get_elements_t)(
+ const svn_branch__state_t *branch,
+ svn_element__tree_t **element_tree_p,
+ apr_pool_t *result_pool);
+
+typedef svn_error_t *(*svn_branch__state_v_get_element_t)(
+ const svn_branch__state_t *branch,
+ svn_element__content_t **element_p,
+ int eid,
+ apr_pool_t *result_pool);
+
+typedef svn_error_t *(*svn_branch__state_v_set_element_t)(
+ svn_branch__state_t *branch,
+ svn_branch__eid_t eid,
+ const svn_element__content_t *element,
+ apr_pool_t *scratch_pool);
+
+typedef svn_error_t *(*svn_branch__state_v_copy_one_t)(
+ svn_branch__state_t *branch,
+ const svn_branch__rev_bid_eid_t *src_el_rev,
+ svn_branch__eid_t local_eid,
+ svn_branch__eid_t new_parent_eid,
+ const char *new_name,
+ const svn_element__payload_t *new_payload,
+ apr_pool_t *scratch_pool);
+
+typedef svn_error_t *(*svn_branch__state_v_copy_tree_t)(
+ svn_branch__state_t *branch,
+ const svn_branch__rev_bid_eid_t *src_el_rev,
+ svn_branch__eid_t new_parent_eid,
+ const char *new_name,
+ apr_pool_t *scratch_pool);
+
+typedef svn_error_t *(*svn_branch__state_v_purge_t)(
+ svn_branch__state_t *branch,
+ apr_pool_t *scratch_pool);
+
+typedef svn_error_t *(*svn_branch__state_v_get_history_t)(
+ svn_branch__state_t *branch,
+ svn_branch__history_t **history_p,
+ apr_pool_t *scratch_pool);
+
+typedef svn_error_t *(*svn_branch__state_v_set_history_t)(
+ svn_branch__state_t *branch,
+ const svn_branch__history_t *history,
+ apr_pool_t *scratch_pool);
+
+struct svn_branch__state_vtable_t
+{
+ svn_branch__vtable_priv_t vpriv;
+
+ svn_branch__state_v_get_elements_t get_elements;
+ svn_branch__state_v_get_element_t get_element;
+ svn_branch__state_v_set_element_t set_element;
+ svn_branch__state_v_copy_one_t copy_one;
+ svn_branch__state_v_copy_tree_t copy_tree;
+ svn_branch__state_v_purge_t purge;
+ svn_branch__state_v_get_history_t get_history;
+ svn_branch__state_v_set_history_t set_history;
+
+};
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SVN_BRANCH_IMPL_H */
+
diff --git a/subversion/include/private/svn_branch_nested.h b/subversion/include/private/svn_branch_nested.h
new file mode 100644
index 000000000000..5f262739b092
--- /dev/null
+++ b/subversion/include/private/svn_branch_nested.h
@@ -0,0 +1,216 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file svn_branch_nested.h
+ * @brief Nested branches and subbranch-root elements
+ *
+ * @since New in ???.
+ */
+
+#ifndef SVN_BRANCH_NESTED_H
+#define SVN_BRANCH_NESTED_H
+
+#include <apr_pools.h>
+
+#include "svn_types.h"
+
+#include "private/svn_branch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/*
+ */
+void
+svn_branch__get_outer_branch_and_eid(svn_branch__state_t **outer_branch_p,
+ int *outer_eid_p,
+ const svn_branch__state_t *branch,
+ apr_pool_t *scratch_pool);
+
+/* Return the root repos-relpath of BRANCH.
+ *
+ * ### TODO: Clarify sequencing requirements.
+ */
+const char *
+svn_branch__get_root_rrpath(const svn_branch__state_t *branch,
+ apr_pool_t *result_pool);
+
+/* Return the repos-relpath of element EID in BRANCH.
+ *
+ * If the element EID does not currently exist in BRANCH, return NULL.
+ *
+ * ### TODO: Clarify sequencing requirements.
+ */
+const char *
+svn_branch__get_rrpath_by_eid(const svn_branch__state_t *branch,
+ int eid,
+ apr_pool_t *result_pool);
+
+/* Return the EID for the repos-relpath RRPATH in BRANCH.
+ *
+ * If no element of BRANCH is at this path, return -1.
+ *
+ * ### TODO: Clarify sequencing requirements.
+ */
+/*int*/
+/*svn_branch__get_eid_by_rrpath(svn_branch__state_t *branch,*/
+/* const char *rrpath,*/
+/* apr_pool_t *scratch_pool);*/
+
+/* Find the (deepest) branch of which the path RELPATH is either the root
+ * path or a normal, non-sub-branch path. An element need not exist at
+ * RELPATH.
+ *
+ * Set *BRANCH_P to the deepest branch within ROOT_BRANCH (recursively,
+ * including itself) that contains the path RELPATH.
+ *
+ * If EID_P is not null then set *EID_P to the element id of RELPATH in
+ * *BRANCH_P, or to -1 if no element exists at RELPATH in that branch.
+ *
+ * If RELPATH is not within any branch in ROOT_BRANCH, set *BRANCH_P to
+ * NULL and (if EID_P is not null) *EID_P to -1.
+ *
+ * ### TODO: Clarify sequencing requirements.
+ */
+svn_error_t *
+svn_branch__find_nested_branch_element_by_relpath(
+ svn_branch__state_t **branch_p,
+ int *eid_p,
+ svn_branch__state_t *root_branch,
+ const char *relpath,
+ apr_pool_t *scratch_pool);
+
+/* Set *EL_REV_P to the el-rev-id of the element at relative path RELPATH
+ * anywhere in or under branch BRANCH_ID in revision REVNUM in REPOS.
+ *
+ * If there is no element there, set *EL_REV_P to point to an id in which
+ * the BRANCH field is the nearest enclosing branch of RRPATH and the EID
+ * field is -1.
+ *
+ * Allocate *EL_REV_P (but not the branch object that it refers to) in
+ * RESULT_POOL.
+ *
+ * ### TODO: Clarify sequencing requirements.
+ */
+svn_error_t *
+svn_branch__repos_find_el_rev_by_path_rev(svn_branch__el_rev_id_t **el_rev_p,
+ const svn_branch__repos_t *repos,
+ svn_revnum_t revnum,
+ const char *branch_id,
+ const char *relpath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* Set *SUBBRANCHES_P an array of pointers to the branches that are immediate
+ * sub-branches of BRANCH.
+ */
+svn_error_t *
+svn_branch__get_immediate_subbranches(svn_branch__state_t *branch,
+ apr_array_header_t **subbranches_p,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* Return the subbranch rooted at BRANCH:EID, or NULL if that is
+ * not a subbranch root.
+ */
+svn_error_t *
+svn_branch__get_subbranch_at_eid(svn_branch__state_t *branch,
+ svn_branch__state_t **subbranch_p,
+ int eid,
+ apr_pool_t *scratch_pool);
+
+/* A subtree of a branch, including any nested branches.
+ */
+typedef struct svn_branch__subtree_t
+{
+ svn_branch__rev_bid_t *predecessor;
+
+ /* EID -> svn_element__content_t mapping. */
+ svn_element__tree_t *tree;
+
+ /* Subbranches to be included: each subbranch-root element in E_MAP
+ should be mapped here.
+
+ A mapping of (int)EID -> (svn_branch__subtree_t *). */
+ apr_hash_t *subbranches;
+} svn_branch__subtree_t;
+
+/* Create an empty subtree (no elements populated, not even ROOT_EID).
+ *
+ * The result contains a *shallow* copy of E_MAP, or a new empty mapping
+ * if E_MAP is null.
+ */
+svn_branch__subtree_t *
+svn_branch__subtree_create(apr_hash_t *e_map,
+ int root_eid,
+ apr_pool_t *result_pool);
+
+/* Return the subtree of BRANCH rooted at EID.
+ * Recursive: includes subbranches.
+ *
+ * The result is limited by the lifetime of BRANCH. It includes a shallow
+ * copy of the element maps in BRANCH and its subbranches: the hash tables
+ * are duplicated but the keys and values (element content data) are not.
+ * It assumes that modifications on a svn_branch__state_t treat element
+ * map keys and values as immutable -- which they do.
+ */
+svn_error_t *
+svn_branch__get_subtree(svn_branch__state_t *branch,
+ svn_branch__subtree_t **subtree_p,
+ int eid,
+ apr_pool_t *result_pool);
+
+/* Return the subbranch rooted at SUBTREE:EID, or NULL if that is
+ * not a subbranch root. */
+svn_branch__subtree_t *
+svn_branch__subtree_get_subbranch_at_eid(svn_branch__subtree_t *subtree,
+ int eid,
+ apr_pool_t *result_pool);
+
+/* Instantiate elements in a branch.
+ *
+ * In TO_BRANCH, instantiate (or alter, if existing) each element of
+ * ELEMENTS, each with its given tree structure (parent, name) and payload.
+ *
+ * Also branch the subbranches in ELEMENTS, creating corresponding new
+ * subbranches in TO_BRANCH, recursively.
+ */
+svn_error_t *
+svn_branch__instantiate_elements_r(svn_branch__state_t *to_branch,
+ svn_branch__subtree_t elements,
+ apr_pool_t *scratch_pool);
+
+/* Create a branch txn object that implements nesting, and wraps a plain
+ * branch txn (that doesn't support nesting) WRAPPED_TXN.
+ */
+svn_branch__txn_t *
+svn_branch__nested_txn_create(svn_branch__txn_t *wrapped_txn,
+ apr_pool_t *result_pool);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVN_BRANCH_NESTED_H */
diff --git a/subversion/include/private/svn_branch_repos.h b/subversion/include/private/svn_branch_repos.h
new file mode 100644
index 000000000000..663d017ebf36
--- /dev/null
+++ b/subversion/include/private/svn_branch_repos.h
@@ -0,0 +1,104 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file svn_branch_repos.h
+ * @brief Operating on a repository
+ *
+ * @since New in ???.
+ */
+
+
+#ifndef SVN_BRANCH_REPOS_H
+#define SVN_BRANCH_REPOS_H
+
+#include <apr_pools.h>
+
+#include "svn_types.h"
+
+#include "private/svn_branch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/* Create a new branching metadata object */
+svn_branch__repos_t *
+svn_branch__repos_create(apr_pool_t *result_pool);
+
+/* Add REV_ROOT as the next revision in the repository REPOS.
+ *
+ * (This does not change the REV and BASE_REV fields of REV_ROOT. The
+ * caller should set those, before or after this call.)
+ */
+svn_error_t *
+svn_branch__repos_add_revision(svn_branch__repos_t *repos,
+ svn_branch__txn_t *rev_root);
+
+/* Return a pointer to revision REVNUM of the repository REPOS.
+ */
+struct svn_branch__txn_t *
+svn_branch__repos_get_revision(const svn_branch__repos_t *repos,
+ svn_revnum_t revnum);
+
+/* Return the revision root that represents the base revision (or,
+ * potentially, txn) of the revision or txn REV_ROOT.
+ */
+svn_branch__txn_t *
+svn_branch__repos_get_base_revision_root(svn_branch__txn_t *rev_root);
+
+/* Set *BRANCH_P to the branch found in REPOS : REVNUM : BRANCH_ID.
+ *
+ * Return an error if REVNUM or BRANCH_ID is not found.
+ */
+svn_error_t *
+svn_branch__repos_get_branch_by_id(svn_branch__state_t **branch_p,
+ const svn_branch__repos_t *repos,
+ svn_revnum_t revnum,
+ const char *branch_id,
+ apr_pool_t *scratch_pool);
+
+/* Set *EL_REV_P to the el-rev-id of the element at branch id BRANCH_ID,
+ * element id EID, in revision REVNUM in REPOS.
+ *
+ * If there is no element there, set *EL_REV_P to point to an id in which
+ * the BRANCH field is the nearest enclosing branch of RRPATH and the EID
+ * field is -1.
+ *
+ * Allocate *EL_REV_P (but not the branch object that it refers to) in
+ * RESULT_POOL.
+ */
+svn_error_t *
+svn_branch__repos_find_el_rev_by_id(svn_branch__el_rev_id_t **el_rev_p,
+ const svn_branch__repos_t *repos,
+ svn_revnum_t revnum,
+ const char *branch_id,
+ int eid,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVN_BRANCH_REPOS_H */
diff --git a/subversion/include/private/svn_cache.h b/subversion/include/private/svn_cache.h
index 166295d6ce90..bedd0a8bfaf0 100644
--- a/subversion/include/private/svn_cache.h
+++ b/subversion/include/private/svn_cache.h
@@ -356,7 +356,12 @@ svn_cache__membuffer_cache_create(svn_membuffer_t **cache,
*
* If @a thread_safe is true, and APR is compiled with threads, all
* accesses to the cache will be protected with a mutex, if the shared
- * @a memcache has also been created with thread_safe flag set.
+ * @a membuffer has also been created with thread_safe flag set.
+ *
+ * If @a short_lived is set, assume that the data stored through this
+ * cache will probably only be needed for a short period of time.
+ * Typically, some UUID is used as part of the prefix in that scenario.
+ * This flag is a mere hint and does not affect functionality.
*
* These caches do not support svn_cache__iter.
*/
@@ -369,10 +374,24 @@ svn_cache__create_membuffer_cache(svn_cache__t **cache_p,
const char *prefix,
apr_uint32_t priority,
svn_boolean_t thread_safe,
+ svn_boolean_t short_lived,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
/**
+ * Creates a null-cache instance in @a *cache_p, allocated from
+ * @a result_pool. The given @c id is the only data stored in it and can
+ * be retrieved through svn_cache__get_info().
+ *
+ * The cache object will immediately evict (reject) any data being added
+ * to it and will always report as empty.
+ */
+svn_error_t *
+svn_cache__create_null(svn_cache__t **cache_p,
+ const char *id,
+ apr_pool_t *result_pool);
+
+/**
* Sets @a handler to be @a cache's error handling routine. If any
* error is returned from a call to svn_cache__get or svn_cache__set, @a
* handler will be called with @a baton and the error, and the
diff --git a/subversion/include/private/svn_cmdline_private.h b/subversion/include/private/svn_cmdline_private.h
index f21a5d2e911b..ac5fb7b07924 100644
--- a/subversion/include/private/svn_cmdline_private.h
+++ b/subversion/include/private/svn_cmdline_private.h
@@ -63,6 +63,7 @@ svn_cmdline__print_xml_prop(svn_stringbuf_t **outstr,
* Expects a @c svn_cmdline_prompt_baton2_t to be passed as @a baton.
*
* @since New in 1.6.
+ * @deprecated Only used by old libgome-keyring implementation.
*/
svn_error_t *
svn_cmdline__auth_gnome_keyring_unlock_prompt(char **keyring_password,
@@ -88,7 +89,7 @@ typedef struct svn_cmdline__config_argument_t
* containing svn_cmdline__config_argument_t* elements, allocating the option
* data in @a pool
*
- * [Since 1.9/1.10:] If the file, section, or option value is not recognized,
+ * [Since 1.9:] If the file, section, or option value is not recognized,
* warn to @c stderr, using @a prefix as in svn_handle_warning2().
*
* @since New in 1.7.
@@ -213,6 +214,18 @@ svn_cmdline__getopt_init(apr_getopt_t **os,
const char *argv[],
apr_pool_t *pool);
+/* */
+svn_boolean_t
+svn_cmdline__stdin_is_a_terminal(void);
+
+/* */
+svn_boolean_t
+svn_cmdline__stdout_is_a_terminal(void);
+
+/* */
+svn_boolean_t
+svn_cmdline__stderr_is_a_terminal(void);
+
/* Determine whether interactive mode should be enabled, based on whether
* the user passed the --non-interactive or --force-interactive options.
* If neither option was passed, interactivity is enabled if standard
@@ -239,6 +252,32 @@ svn_cmdline__parse_trust_options(
const char *opt_arg,
apr_pool_t *scratch_pool);
+/* Setup signal handlers for signals such as SIGINT and return a
+ cancellation handler function. This also sets some other signals
+ to be ignored. */
+svn_cancel_func_t
+svn_cmdline__setup_cancellation_handler(void);
+
+/* Set the handlers for signals such as SIGINT back to default. */
+void
+svn_cmdline__disable_cancellation_handler(void);
+
+/* Exit this process with a status that indicates the cancellation
+ signal, or return without exiting if there is no signal. This
+ allows the shell to use WIFSIGNALED and WTERMSIG to detect the
+ signal. See http://www.cons.org/cracauer/sigint.html */
+void
+svn_cmdline__cancellation_exit(void);
+
+/** Reads a string from stdin until a newline or EOF is found
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_cmdline__stdin_readline(const char **result,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/subversion/include/private/svn_config_private.h b/subversion/include/private/svn_config_private.h
new file mode 100644
index 000000000000..95cca27b20f9
--- /dev/null
+++ b/subversion/include/private/svn_config_private.h
@@ -0,0 +1,129 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file svn_config_private.h
+ * @brief Private config file parsing API.
+ */
+
+#ifndef SVN_CONFIG_PRIVATE_H
+#define SVN_CONFIG_PRIVATE_H
+
+#include <apr_pools.h>
+
+#include "svn_error.h"
+#include "svn_io.h"
+#include "svn_string.h"
+#include "svn_config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Description of a constructor for in-memory config file
+ * representations.
+ */
+typedef struct svn_config__constructor_t svn_config__constructor_t;
+
+/*
+ * Constructor callback: called when the parsing of a new SECTION
+ * begins. If the implementation stores the value of SECTION, it
+ * must copy it into a permanent pool.
+ *
+ * May return SVN_ERR_CEASE_INVOCATION to stop further parsing.
+ */
+typedef svn_error_t *(*svn_config__open_section_fn)(
+ void *baton, svn_stringbuf_t *section);
+
+/*
+ * Constructor callback: called when the parsing of SECTION ends. If
+ * the implementation stores the value of SECTION, it must copy it
+ * into a permanent pool.
+ *
+ * May return SVN_ERR_CEASE_INVOCATION to stop further parsing.
+ */
+typedef svn_error_t *(*svn_config__close_section_fn)(
+ void *baton, svn_stringbuf_t *section);
+
+/*
+ * Constructor callback: called OPTION with VALUE in SECTION was
+ * parsed. If the implementation stores the values of SECTION, OPTION
+ * or VALUE, it must copy them into a permanent pool.
+ *
+ * May return SVN_ERR_CEASE_INVOCATION to stop further parsing.
+ */
+typedef svn_error_t *(*svn_config__add_value_fn)(
+ void *baton, svn_stringbuf_t *section,
+ svn_stringbuf_t *option, svn_stringbuf_t *value);
+
+
+/*
+ * Create a new constuctor allocated from RESULT_POOL.
+ * Any of the callback functions may be NULL.
+ * The constructor implementation is responsible for implementing any
+ * case-insensitivity, value expansion, or other features on top of
+ * the basic parser.
+ */
+svn_config__constructor_t *
+svn_config__constructor_create(
+ svn_config__open_section_fn open_section_callback,
+ svn_config__close_section_fn close_section_callback,
+ svn_config__add_value_fn add_value_callback,
+ apr_pool_t *result_pool);
+
+/*
+ * Parse the configuration from STREAM, using CONSTRUCTOR to build the
+ * in-memory representation of the parsed configuration.
+ * CONSTRUCTOR_BATON is passed unchanged to the constructor
+ * callbacks. The parser guarantees that sections and options will be
+ * passed to the callback in the same order as they're defined in
+ * STREAM.
+ *
+ * The lifetome of section names, option names and values passed to
+ * the constructor does not extend past the invocation of each
+ * callback; see calback docs, above.
+ *
+ * The parser will use SCRATCH_POOL for its own allocations.
+ */
+svn_error_t *
+svn_config__parse_stream(svn_stream_t *stream,
+ svn_config__constructor_t *constructor,
+ void *constructor_baton,
+ apr_pool_t *scratch_pool);
+
+/*
+ * Write the configuration CFG to STREAM, using SCRATCH_POOL for
+ * temporary allocations.
+ *
+ * Note that option values will not be expanded and that the order
+ * of sections as well as the options within them is undefined.
+ */
+svn_error_t *
+svn_config__write(svn_stream_t *stream,
+ const svn_config_t *cfg,
+ apr_pool_t *scratch_pool);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVN_CONFIG_PRIVATE_H */
diff --git a/subversion/include/private/svn_delta_private.h b/subversion/include/private/svn_delta_private.h
index 260327c43c69..fbf66e1be7b7 100644
--- a/subversion/include/private/svn_delta_private.h
+++ b/subversion/include/private/svn_delta_private.h
@@ -108,6 +108,25 @@ svn_txdelta__read_raw_window_len(apr_size_t *window_len,
svn_stream_t *stream,
apr_pool_t *pool);
+/* Return a debug editor that wraps @a wrapped_editor.
+ *
+ * The debug editor simply prints an indication of what callbacks are being
+ * called to @c stdout, and is only intended for use in debugging subversion
+ * editors.
+ *
+ * @a prefix, if non-null, is printed between "DBG: " and each indication.
+ *
+ * Note: Our test suite generally ignores stdout lines starting with "DBG:".
+ */
+svn_error_t *
+svn_delta__get_debug_editor(const svn_delta_editor_t **editor,
+ void **edit_baton,
+ const svn_delta_editor_t *wrapped_editor,
+ void *wrapped_baton,
+ const char *prefix,
+ apr_pool_t *pool);
+
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/subversion/include/private/svn_dep_compat.h b/subversion/include/private/svn_dep_compat.h
index 729cf7e4df11..6b381d2ce81c 100644
--- a/subversion/include/private/svn_dep_compat.h
+++ b/subversion/include/private/svn_dep_compat.h
@@ -75,6 +75,27 @@ extern "C" {
#endif
/**
+ * Indicate whether we are running on a POSIX platform. This has
+ * implications on the way e.g. fsync() works.
+ *
+ * For details on this check, see
+ * http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system#POSIX
+ *
+ * @since New in 1.10.
+ */
+#ifndef SVN_ON_POSIX
+#if !defined(_WIN32) \
+ && ( defined(__unix__) \
+ || defined(__unix) \
+ || (defined(__APPLE__) && defined(__MACH__))) /* UNIX-style OS? */
+# include <unistd.h>
+# if defined(_POSIX_VERSION)
+# define SVN_ON_POSIX
+# endif
+#endif
+#endif
+
+/**
* APR keeps a few interesting defines hidden away in its private
* headers apr_arch_file_io.h, so we redefined them here.
*
@@ -94,6 +115,28 @@ extern "C" {
#endif
/**
+ * APR 1 has volatile qualifier bugs in some atomic prototypes that
+ * are fixed in APR 2:
+ * https://issues.apache.org/bugzilla/show_bug.cgi?id=50731
+ * Subversion code should put the volatile qualifier in the correct
+ * place when declaring variables which means that casting at the call
+ * site is necessary when using APR 1. No casts should be used with
+ * APR 2 as this allows the compiler to check that the variable has
+ * the correct volatile qualifier.
+ */
+#if APR_VERSION_AT_LEAST(2,0,0)
+#define svn_atomic_casptr(mem, with, cmp) \
+ apr_atomic_casptr((mem), (with), (cmp))
+#define svn_atomic_xchgptr(mem, val) \
+ apr_atomic_xchgptr((mem), (val))
+#else
+#define svn_atomic_casptr(mem, with, cmp) \
+ apr_atomic_casptr((void volatile **)(mem), (with), (cmp))
+#define svn_atomic_xchgptr(mem, val) \
+ apr_atomic_xchgptr((void volatile **)(mem), (val))
+#endif
+
+/**
* Check at compile time if the Serf version is at least a certain
* level.
* @param major The major version component of the version checked
diff --git a/subversion/include/private/svn_diff_private.h b/subversion/include/private/svn_diff_private.h
index 48b4d5266200..bf7f490d37e9 100644
--- a/subversion/include/private/svn_diff_private.h
+++ b/subversion/include/private/svn_diff_private.h
@@ -113,6 +113,40 @@ svn_diff__display_prop_diffs(svn_stream_t *outstream,
void *cancel_baton,
apr_pool_t *scratch_pool);
+/** Create a hunk object that adds a single line without newline. Return the
+ * new object in @a *hunk.
+ *
+ * @a line is the added text, without a trailing newline.
+ *
+ * The hunk will be associated with @a patch.
+ */
+svn_error_t *
+svn_diff_hunk__create_adds_single_line(svn_diff_hunk_t **hunk,
+ const char *line,
+ const svn_patch_t *patch,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/** Create a hunk object that deletes a single line without newline. Return
+ * the new object in @a *hunk.
+ *
+ * @a line is the deleted text, without a trailing newline.
+ *
+ * The hunk will be associated with @a patch.
+ */
+svn_error_t *
+svn_diff_hunk__create_deletes_single_line(svn_diff_hunk_t **hunk,
+ const char *line,
+ const svn_patch_t *patch,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/** Fetches the penalty fuzz of the diff hunk. The patch file parser applies
+ * an additional penalty on some cases of bad patch files. These cases may
+ * include errors as headers that aren't consistent with bodies, etc.
+ */
+svn_linenum_t
+svn_diff_hunk__get_fuzz_penalty(const svn_diff_hunk_t *hunk);
#ifdef __cplusplus
}
diff --git a/subversion/include/private/svn_diff_tree.h b/subversion/include/private/svn_diff_tree.h
index 4554da2b47c9..713644d08c4e 100644
--- a/subversion/include/private/svn_diff_tree.h
+++ b/subversion/include/private/svn_diff_tree.h
@@ -114,12 +114,24 @@ extern "C" {
* ### How come many users don't set the 'repos_relpath' field? */
typedef struct svn_diff_source_t
{
- /* Always available */
+ /* Always available
+ In case of copyfrom: the revision copied from
+ */
svn_revnum_t revision;
- /* Depending on the driver available for copyfrom */
- /* ### What? */
+ /* In case of copyfrom: the repository relative path copied from.
+
+ NULL if the node wasn't copied or moved, or when the driver doesn't
+ have this information */
const char *repos_relpath;
+
+ /* In case of copyfrom: the relative path of source location before the
+ move. This path is relative WITHIN THE DIFF. The repository path is
+ typically in repos_relpath
+
+ NULL if the node wasn't moved or if the driver doesn't have this
+ information. */
+ const char *moved_from_relpath;
} svn_diff_source_t;
/**
diff --git a/subversion/include/private/svn_element.h b/subversion/include/private/svn_element.h
new file mode 100644
index 000000000000..c467175c748e
--- /dev/null
+++ b/subversion/include/private/svn_element.h
@@ -0,0 +1,399 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ * @endcopyright
+ *
+ * @file svn_element.h
+ * @brief Tree elements
+ *
+ * @since New in ???.
+ */
+
+#ifndef SVN_BRANCH_ELEMENT_H
+#define SVN_BRANCH_ELEMENT_H
+
+#include <apr_pools.h>
+#include <apr_tables.h>
+
+#include "svn_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/* ====================================================================== */
+
+/** Like apr_hash_get() but the hash key is an integer. */
+void *
+svn_eid__hash_get(apr_hash_t *ht,
+ int key);
+
+/** Like apr_hash_set() but the hash key is an integer. */
+void
+svn_eid__hash_set(apr_hash_t *ht,
+ int key,
+ const void *val);
+
+/** Like apr_hash_this_key() but the hash key is an integer. */
+int
+svn_eid__hash_this_key(apr_hash_index_t *hi);
+
+struct svn_sort__item_t;
+
+/** A hash iterator for iterating over an array or a hash table in
+ * its natural order or in sorted order.
+ *
+ * For an array, the @a i and @a val members provide the index and value
+ * of the current item.
+ */
+typedef struct svn_eid__hash_iter_t
+{
+ /* private: an array of (svn_sort__item_t) hash items for sorted iteration */
+ const apr_array_header_t *array;
+
+ /* current element: iteration order index */
+ int i;
+ /* current element: key */
+ int eid;
+ /* current element: value */
+ void *val;
+} svn_eid__hash_iter_t;
+
+svn_eid__hash_iter_t *
+svn_eid__hash_sorted_first(apr_pool_t *pool,
+ apr_hash_t *ht,
+ int (*comparison_func)(const struct svn_sort__item_t *,
+ const struct svn_sort__item_t *));
+
+svn_eid__hash_iter_t *
+svn_eid__hash_sorted_next(svn_eid__hash_iter_t *hi);
+
+/** A sort ordering callback function that returns an indication whether
+ * A sorts before or after or equal to B, by comparing their keys as EIDs.
+ */
+int
+svn_eid__hash_sort_compare_items_by_eid(const struct svn_sort__item_t *a,
+ const struct svn_sort__item_t *b);
+
+#define SVN_EID__HASH_ITER_SORTED(i, ht, comparison_func, pool) \
+ i = (void *)svn_eid__hash_sorted_first(pool, ht, comparison_func); \
+ i; \
+ i = (void *)svn_eid__hash_sorted_next((void *)i)
+
+#define SVN_EID__HASH_ITER_SORTED_BY_EID(i, ht, pool) \
+ SVN_EID__HASH_ITER_SORTED(i, ht, svn_eid__hash_sort_compare_items_by_eid, pool)
+
+
+/* ====================================================================== */
+
+/**
+ */
+typedef struct svn_element__branch_ref_t
+{
+ svn_revnum_t rev;
+ const char *branch_id;
+ int eid;
+} svn_element__branch_ref_t;
+
+/** Versioned payload of an element, excluding tree structure information.
+ *
+ * This specifies the properties and the text of a file or target of a
+ * symlink, directly, or by reference to an existing committed element, or
+ * by a delta against such a reference payload.
+ *
+ * ### An idea: If the sender and receiver agree, the payload for an element
+ * may be specified as "null" to designate that the payload is not
+ * available. For example, when a client performing a WC update has
+ * no read authorization for a given path, the server may send null
+ * payload and the client may record an 'absent' WC node. (This
+ * would not make sense in a commit.)
+ */
+typedef struct svn_element__payload_t svn_element__payload_t;
+
+/*
+ * ========================================================================
+ * Element Payload Interface
+ * ========================================================================
+ *
+ * @defgroup svn_element_payload Element payload interface
+ * @{
+ */
+
+/** Versioned payload of a node, excluding tree structure information.
+ *
+ * Payload is described by setting fields in one of the following ways.
+ * Other fields SHOULD be null (or equivalent).
+ *
+ * by reference: (kind=unknown, ref)
+ * dir: (kind=dir, props)
+ * file: (kind=file, props, text)
+ * symlink: (kind=symlink, props, target)
+ *
+ * ### Idea for the future: Specify payload as an (optional) reference
+ * plus (optional) overrides or deltas against the reference?
+ */
+struct svn_element__payload_t
+{
+ /* Is this a subbranch-root element, in other words a link to a nested
+ * branch? If so, all other fields are irrelevant. */
+ svn_boolean_t is_subbranch_root;
+
+ /* The node kind for this payload: dir, file, symlink, or unknown. */
+ svn_node_kind_t kind;
+
+ /* Reference an existing, committed payload. (Use with kind=unknown if
+ * there is no content in props/text/targe fields.)
+ * The 'null' value is (SVN_INVALID_REVNUM, NULL, *). */
+ svn_element__branch_ref_t branch_ref;
+
+ /* The pool in which the payload's content is allocated. Used when
+ * resolving (populating the props/text/target in) a payload that was
+ * originally defined by reference. */
+ apr_pool_t *pool;
+
+ /* Properties (for kind != unknown).
+ * Maps (const char *) name -> (svn_string_t) value.
+ * An empty hash means no properties. (SHOULD NOT be NULL.)
+ * ### Presently NULL means 'no change' in some contexts. */
+ apr_hash_t *props;
+
+ /* File text (for kind=file; otherwise SHOULD be NULL). */
+ svn_stringbuf_t *text;
+
+ /* Symlink target (for kind=symlink; otherwise SHOULD be NULL). */
+ const char *target;
+
+};
+
+/* Return true iff PAYLOAD satisfies all its invariants.
+ */
+svn_boolean_t
+svn_element__payload_invariants(const svn_element__payload_t *payload);
+
+/** Duplicate a node-payload @a old into @a result_pool.
+ */
+svn_element__payload_t *
+svn_element__payload_dup(const svn_element__payload_t *old,
+ apr_pool_t *result_pool);
+
+/* Return true iff the payload of LEFT is identical to that of RIGHT.
+ * References are not supported. Node kind 'unknown' is not supported.
+ */
+svn_boolean_t
+svn_element__payload_equal(const svn_element__payload_t *left,
+ const svn_element__payload_t *right,
+ apr_pool_t *scratch_pool);
+
+/** Create a new node-payload object for a subbranch-root (link to a
+ * nested branch).
+ *
+ * Allocate the result in @a result_pool.
+ */
+svn_element__payload_t *
+svn_element__payload_create_subbranch(apr_pool_t *result_pool);
+
+/** Create a new node-payload object by reference to an existing payload.
+ *
+ * Set the node kind to 'unknown'.
+ *
+ * Allocate the result in @a result_pool.
+ */
+svn_element__payload_t *
+svn_element__payload_create_ref(svn_revnum_t rev,
+ const char *branch_id,
+ int eid,
+ apr_pool_t *result_pool);
+
+/** Create a new node-payload object for a directory node.
+ *
+ * Allocate the result in @a result_pool.
+ */
+svn_element__payload_t *
+svn_element__payload_create_dir(apr_hash_t *props,
+ apr_pool_t *result_pool);
+
+/** Create a new node-payload object for a file node.
+ *
+ * Allocate the result in @a result_pool.
+ */
+svn_element__payload_t *
+svn_element__payload_create_file(apr_hash_t *props,
+ svn_stringbuf_t *text,
+ apr_pool_t *result_pool);
+
+/** Create a new node-payload object for a symlink node.
+ *
+ * Allocate the result in @a result_pool.
+ */
+svn_element__payload_t *
+svn_element__payload_create_symlink(apr_hash_t *props,
+ const char *target,
+ apr_pool_t *result_pool);
+
+/** @} */
+
+
+/*
+ * ========================================================================
+ * Element-Revision Content
+ * ========================================================================
+ *
+ * @defgroup svn_el_rev_content Element-Revision Content
+ * @{
+ */
+
+/* The content (parent, name and payload) of an element-revision.
+ * In other words, an el-rev node in a (mixed-rev) directory-tree.
+ */
+typedef struct svn_element__content_t
+{
+ /* eid of the parent element, or -1 if this is the root element */
+ int parent_eid;
+ /* element name, or "" for root element; never null */
+ const char *name;
+ /* payload (kind, props, text, ...) */
+ svn_element__payload_t *payload;
+
+} svn_element__content_t;
+
+/* Return a new content object constructed with deep copies of PARENT_EID,
+ * NAME and PAYLOAD, allocated in RESULT_POOL.
+ */
+svn_element__content_t *
+svn_element__content_create(int parent_eid,
+ const char *name,
+ const svn_element__payload_t *payload,
+ apr_pool_t *result_pool);
+
+/* Return a deep copy of OLD, allocated in RESULT_POOL.
+ */
+svn_element__content_t *
+svn_element__content_dup(const svn_element__content_t *old,
+ apr_pool_t *result_pool);
+
+/* Return TRUE iff CONTENT_LEFT is the same as CONTENT_RIGHT. */
+svn_boolean_t
+svn_element__content_equal(const svn_element__content_t *content_left,
+ const svn_element__content_t *content_right,
+ apr_pool_t *scratch_pool);
+
+/** @} */
+
+
+/*
+ * ========================================================================
+ * Element Tree
+ * ========================================================================
+ *
+ * The elements in an Element Tree do not necessarily form a single,
+ * complete tree at all times.
+ *
+ * @defgroup svn_element_tree Element Tree
+ * @{
+ */
+
+/* A (sub)tree of elements.
+ *
+ * An element tree is described by the content of element ROOT_EID in E_MAP,
+ * and its children (as determined by their parent links) and their names
+ * and their content recursively. For the element ROOT_EID itself, only
+ * its content is relevant; its parent and name are to be ignored.
+ *
+ * E_MAP may also contain entries that are not part of the subtree. Thus,
+ * to select a sub-subtree, it is only necessary to change ROOT_EID.
+ *
+ * The EIDs used in here may be considered either as global EIDs (known to
+ * the repo), or as local stand-alone EIDs (in their own local name-space),
+ * according to the context.
+ */
+typedef struct svn_element__tree_t
+{
+ /* EID -> svn_element__content_t mapping. */
+ apr_hash_t *e_map;
+
+ /* Subtree root EID. (ROOT_EID must be an existing key in E_MAP.) */
+ int root_eid;
+
+} svn_element__tree_t;
+
+/* Create an element tree object.
+ *
+ * The result contains a *shallow* copy of E_MAP, or a new empty mapping
+ * if E_MAP is null.
+ */
+svn_element__tree_t *
+svn_element__tree_create(apr_hash_t *e_map,
+ int root_eid,
+ apr_pool_t *result_pool);
+
+svn_element__content_t *
+svn_element__tree_get(const svn_element__tree_t *tree,
+ int eid);
+
+svn_error_t *
+svn_element__tree_set(svn_element__tree_t *tree,
+ int eid,
+ const svn_element__content_t *element);
+
+/* Purge entries from E_MAP that don't connect, via parent directory hierarchy,
+ * to ROOT_EID. In other words, remove elements that have been implicitly
+ * deleted.
+ *
+ * ROOT_EID must be present in E_MAP.
+ *
+ * ### Does not detect cycles: current implementation will not purge a cycle
+ * that is disconnected from ROOT_EID. This could be a problem.
+ */
+void
+svn_element__tree_purge_orphans(apr_hash_t *e_map,
+ int root_eid,
+ apr_pool_t *scratch_pool);
+
+/* Return the subtree-relative path of element EID in TREE.
+ *
+ * If the element EID does not currently exist in TREE, return NULL.
+ *
+ * ### TODO: Clarify sequencing requirements.
+ */
+const char *
+svn_element__tree_get_path_by_eid(const svn_element__tree_t *tree,
+ int eid,
+ apr_pool_t *result_pool);
+
+/* Return the subtree rooted at EID within ELEMENT_TREE.
+ *
+ * The result is limited by the lifetime of ELEMENT_TREE. It includes a
+ * shallow copy of the mapping in ELEMENT_TREE: the hash table is
+ * duplicated but the keys and values (element content data) are not.
+ */
+svn_element__tree_t *
+svn_element__tree_get_subtree_at_eid(svn_element__tree_t *element_tree,
+ int eid,
+ apr_pool_t *result_pool);
+
+/** @} */
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SVN_BRANCH_ELEMENT_H */
diff --git a/subversion/include/private/svn_fs_fs_private.h b/subversion/include/private/svn_fs_fs_private.h
index 59aede1c7a62..d2573d447c9d 100644
--- a/subversion/include/private/svn_fs_fs_private.h
+++ b/subversion/include/private/svn_fs_fs_private.h
@@ -53,7 +53,8 @@ typedef struct svn_fs_fs__large_change_info_t
/* size of the (deltified) representation */
apr_uint64_t size;
- /* Revision of the representation. SVN_INVALID_REVNUM for unused entries. */
+ /* Revision of the representation. SVN_INVALID_REVNUM for unused entries.
+ */
svn_revnum_t revision;
/* node path. "" for unused instances */
@@ -151,6 +152,9 @@ typedef struct svn_fs_fs__representation_stats_t
/* sum of ref_count * expanded_size,
* i.e. total plaintext content if there was no rep sharing */
apr_uint64_t expanded_size;
+
+ /* sum of all representation delta chain lengths */
+ apr_uint64_t chain_len;
} svn_fs_fs__representation_stats_t;
/* Basic statistics we collect over a given set of noderevs.
@@ -267,19 +271,20 @@ svn_fs_fs__get_stats(svn_fs_fs__stats_t **stats,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
-/* Node-revision IDs in FSFS consist of 3 of sub-IDs ("parts") that consist
+/* A node-revision ID in FSFS consists of 3 sub-IDs ("parts") that consist
* of a creation REVISION number and some revision- / transaction-local
* counter value (NUMBER). Old-style ID parts use global counter values.
*
* The parts are: node_id, copy_id and txn_id for in-txn IDs as well as
- * node_id, copy_id and rev_offset for in-revision IDs. This struct the
+ * node_id, copy_id and rev_item for in-revision IDs. This struct is the
* data structure used for each of those parts.
*/
typedef struct svn_fs_fs__id_part_t
{
- /* SVN_INVALID_REVNUM for txns -> not a txn, COUNTER must be 0.
- SVN_INVALID_REVNUM for others -> not assigned to a revision, yet.
- 0 for others -> old-style ID or the root in rev 0. */
+ /* SVN_INVALID_REVNUM for txn_id part -> not a txn, NUMBER must be 0.
+ SVN_INVALID_REVNUM for other parts -> not assigned to a revision, yet.
+ 0 for other parts -> old-style ID or the root in rev 0.
+ */
svn_revnum_t revision;
/* sub-id value relative to REVISION. Its interpretation depends on
diff --git a/subversion/include/private/svn_fs_private.h b/subversion/include/private/svn_fs_private.h
index 5bd89c0280c0..4bd0a5052731 100644
--- a/subversion/include/private/svn_fs_private.h
+++ b/subversion/include/private/svn_fs_private.h
@@ -183,7 +183,7 @@ svn_fs__editor_commit(svn_revnum_t *revision,
*
* If there is no mergeinfo, set @a *mergeinfo to NULL.
*
- * See svn_fs_get_mergeinfo2() but for the meanings of @a inherit and
+ * See svn_fs_get_mergeinfo3() but for the meanings of @a inherit and
* @a adjust_inheritable_mergeinfo and other details.
*/
svn_error_t *
@@ -195,6 +195,22 @@ svn_fs__get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
+/** Determine the previous location of @a path under @a root and return it
+ * as @a *node_path under @a *node_root. This may be called for arbitrary
+ * nodes but is intended for nodes that got deleted in @a root, i.e. when
+ * standard navigation fails. It also works if @a root is transaction root.
+ *
+ * Allocate @a *node_path and @a *node_root in @a result_pool while using
+ * @a scratch_pool for temporaries.
+ */
+svn_error_t *
+svn_fs__get_deleted_node(svn_fs_root_t **node_root,
+ const char **node_path,
+ svn_fs_root_t *root,
+ const char *path,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
/** @} */
diff --git a/subversion/include/private/svn_fs_util.h b/subversion/include/private/svn_fs_util.h
index c9f74a126858..fc548ed15ee0 100644
--- a/subversion/include/private/svn_fs_util.h
+++ b/subversion/include/private/svn_fs_util.h
@@ -206,6 +206,15 @@ svn_fs__path_change_create_internal(const svn_fs_id_t *node_rev_id,
svn_fs_path_change_kind_t change_kind,
apr_pool_t *pool);
+/* Allocate an svn_fs_path_change3_t structure in RESULT_POOL, initialize
+ and return it.
+
+ Set the change_kind to CHANGE_KIND. Set all other fields to their
+ _unknown, NULL or invalid value, respectively. */
+svn_fs_path_change3_t *
+svn_fs__path_change_create_internal2(svn_fs_path_change_kind_t change_kind,
+ apr_pool_t *result_pool);
+
/* Append REL_PATH (which may contain slashes) to each path that exists in
the mergeinfo INPUT, and return a new mergeinfo in *OUTPUT. Deep
copies the values. Perform all allocations in POOL. */
diff --git a/subversion/include/private/svn_io_private.h b/subversion/include/private/svn_io_private.h
index 814c27aea422..2c9028a9c31d 100644
--- a/subversion/include/private/svn_io_private.h
+++ b/subversion/include/private/svn_io_private.h
@@ -85,32 +85,6 @@ svn_io__file_lock_autocreate(const char *lock_file,
apr_pool_t *pool);
-/** Buffer test handler function for a generic stream. @see svn_stream_t
- * and svn_stream__is_buffered().
- *
- * @since New in 1.7.
- */
-typedef svn_boolean_t (*svn_stream__is_buffered_fn_t)(void *baton);
-
-/** Set @a stream's buffer test function to @a is_buffered_fn
- *
- * @since New in 1.7.
- */
-void
-svn_stream__set_is_buffered(svn_stream_t *stream,
- svn_stream__is_buffered_fn_t is_buffered_fn);
-
-/** Return whether this generic @a stream uses internal buffering.
- * This may be used to work around subtle differences between buffered
- * and non-buffered APR files. A lazy-open stream cannot report the
- * true buffering state until after the lazy open: a stream that
- * initially reports as non-buffered may report as buffered later.
- *
- * @since New in 1.7.
- */
-svn_boolean_t
-svn_stream__is_buffered(svn_stream_t *stream);
-
/** Return the underlying file, if any, associated with the stream, or
* NULL if not available. Accessing the file bypasses the stream.
*/
@@ -153,6 +127,13 @@ svn_stream__install_get_info(apr_finfo_t *finfo,
apr_int32_t wanted,
apr_pool_t *scratch_pool);
+/* Internal version of svn_stream_from_aprfile2() supporting the
+ additional TRUNCATE_ON_SEEK argument. */
+svn_stream_t *
+svn_stream__from_aprfile(apr_file_t *file,
+ svn_boolean_t disown,
+ svn_boolean_t truncate_on_seek,
+ apr_pool_t *pool);
#if defined(WIN32)
diff --git a/subversion/include/private/svn_log.h b/subversion/include/private/svn_log.h
index 1ad8d559b9df..c2da89d173c1 100644
--- a/subversion/include/private/svn_log.h
+++ b/subversion/include/private/svn_log.h
@@ -93,7 +93,7 @@ svn_log__get_file(const char *path, svn_revnum_t rev,
const char *
svn_log__get_dir(const char *path, svn_revnum_t rev,
svn_boolean_t want_contents, svn_boolean_t want_props,
- apr_uint64_t dirent_fields,
+ apr_uint32_t dirent_fields,
apr_pool_t *pool);
/**
@@ -253,6 +253,17 @@ const char *
svn_log__get_inherited_props(const char *path,
svn_revnum_t rev,
apr_pool_t *pool);
+
+/**
+ * Return a log string for a list action.
+ *
+ * @since New in 1.10.
+ */
+const char *
+svn_log__list(const char *path, svn_revnum_t revision,
+ apr_array_header_t *patterns, svn_depth_t depth,
+ apr_uint32_t dirent_fields, apr_pool_t *pool);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/subversion/include/private/svn_mergeinfo_private.h b/subversion/include/private/svn_mergeinfo_private.h
index 716d0c9f90e2..a34cb39ce3da 100644
--- a/subversion/include/private/svn_mergeinfo_private.h
+++ b/subversion/include/private/svn_mergeinfo_private.h
@@ -52,18 +52,21 @@ svn_rangelist__set_inheritance(svn_rangelist_t *rangelist,
* Unlike svn_mergeinfo_parse(), this does not sort the ranges into order
* or combine adjacent and overlapping ranges.
*
- * The compaction can be done with svn_rangelist__combine_adjacent_ranges().
+ * The compaction can be done with svn_rangelist__canonicalize().
*/
svn_error_t *
svn_rangelist__parse(svn_rangelist_t **rangelist,
const char *str,
apr_pool_t *result_pool);
-/* In-place combines adjacent ranges in a rangelist.
- SCRATCH_POOL is just used for providing error messages. */
-svn_error_t *
-svn_rangelist__combine_adjacent_ranges(svn_rangelist_t *rangelist,
- apr_pool_t *scratch_pool);
+/* Return TRUE, if all ranges in RANGELIST are in ascending order and do
+* not overlap and are not adjacent.
+*
+* If this returns FALSE, you probaly want to call
+* svn_rangelist__canonicalize().
+*/
+svn_boolean_t
+svn_rangelist__is_canonical(const svn_rangelist_t *rangelist);
/** Canonicalize the @a rangelist: sort the ranges, and combine adjacent or
* overlapping ranges into single ranges where possible.
diff --git a/subversion/include/private/svn_mutex.h b/subversion/include/private/svn_mutex.h
index c04820bacbbb..0b779474c350 100644
--- a/subversion/include/private/svn_mutex.h
+++ b/subversion/include/private/svn_mutex.h
@@ -27,8 +27,6 @@
#ifndef SVN_MUTEX_H
#define SVN_MUTEX_H
-#include <apr_thread_mutex.h>
-
#include "svn_error.h"
#ifdef __cplusplus
@@ -106,6 +104,17 @@ do { \
SVN_ERR(svn_mutex__unlock(svn_mutex__m, (expr))); \
} while (0)
+#if APR_HAS_THREADS
+
+/** Return the APR mutex encapsulated in @a mutex.
+ *
+ * @note This function should only be called by APR wrapper code.
+ */
+apr_thread_mutex_t *
+svn_mutex__get(svn_mutex__t *mutex);
+
+#endif
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/subversion/include/private/svn_object_pool.h b/subversion/include/private/svn_object_pool.h
index 7a9383e5f625..767a48bc6a12 100644
--- a/subversion/include/private/svn_object_pool.h
+++ b/subversion/include/private/svn_object_pool.h
@@ -65,80 +65,46 @@
/* The opaque object container type. */
typedef struct svn_object_pool__t svn_object_pool__t;
-/* Extract the actual object from the WRAPPER using optional information
- * from BATON (provided through #svn_object_pool__lookup) and return it.
- * The result will be used with POOL and must remain valid throughout
- * POOL's lifetime.
- *
- * It is legal to return a copy, allocated in POOL, of the wrapped object.
- */
-typedef void * (* svn_object_pool__getter_t)(void *wrapper,
- void *baton,
- apr_pool_t *pool);
-
-/* Copy the information from the SOURCE object wrapper into the already
- * existing *TARGET object wrapper using POOL for allocations and BATON
- * for optional context (provided through #svn_object_pool__insert).
- */
-typedef svn_error_t * (* svn_object_pool__setter_t)(void **target,
- void *source,
- void *baton,
- apr_pool_t *pool);
-
/* Create a new object pool in POOL and return it in *OBJECT_POOL.
- * Objects will be extracted using GETTER and updated using SETTER. Either
- * one (or both) may be NULL and the default implementation assumes that
- * wrapper == object and updating is a no-op.
+ * Objects are reference-counted and stored as opaque pointers. Each
+ * must be allocated in a separate pool ceated by
+ * svn_object_pool__new_item_pool. Unused objects get destroyed at
+ * the object pool's discretion.
*
* If THREAD_SAFE is not set, neither the object pool nor the object
* references returned from it may be accessed from multiple threads.
*
* It is not legal to call any API on the object pool after POOL got
- * cleared or destroyed. However, existing object references handed out
- * from the object pool remain valid and will keep the internal pool data
- * structures alive for as long as such object references exist.
+ * cleared or destroyed nor to use any objects from this object pool.
*/
svn_error_t *
svn_object_pool__create(svn_object_pool__t **object_pool,
- svn_object_pool__getter_t getter,
- svn_object_pool__setter_t setter,
svn_boolean_t thread_safe,
apr_pool_t *pool);
-/* Return the root pool containing the OBJECT_POOL and all sub-structures.
+/* Return a pool to allocate the new object.
*/
apr_pool_t *
-svn_object_pool__new_wrapper_pool(svn_object_pool__t *object_pool);
-
-/* Return the mutex used to serialize all OBJECT_POOL access.
- */
-svn_mutex__t *
-svn_object_pool__mutex(svn_object_pool__t *object_pool);
-
-/* Return the number of object instances (used or unused) in OBJECT_POOL.
- */
-unsigned
-svn_object_pool__count(svn_object_pool__t *object_pool);
+svn_object_pool__new_item_pool(svn_object_pool__t *object_pool);
/* In OBJECT_POOL, look for an available object by KEY and return a
* reference to it in *OBJECT. If none can be found, *OBJECT will be NULL.
- * BATON will be passed to OBJECT_POOL's getter function. The reference
- * will be returned when *RESULT_POOL gets cleaned up or destroyed.
+ *
+ * The reference will be returned when *RESULT_POOL and may be destroyed
+ * or recycled by OBJECT_POOL.
*/
svn_error_t *
svn_object_pool__lookup(void **object,
svn_object_pool__t *object_pool,
svn_membuf_t *key,
- void *baton,
apr_pool_t *result_pool);
-/* Store the wrapped object WRAPPER under KEY in OBJECT_POOL and return
- * a reference to the object in *OBJECT (just like lookup).
+/* Store the object ITEM under KEY in OBJECT_POOL and return a reference
+ * to the object in *OBJECT (just like lookup).
*
- * The object must have been created in WRAPPER_POOL and the latter must
- * be a sub-pool of OBJECT_POOL's root POOL (see #svn_object_pool__pool).
+ * The object must have been created in ITEM_POOL and the latter must
+ * have been created by svn_object_pool__new_item_pool.
*
- * BATON will be passed to OBJECT_POOL's setter and getter functions.
* The reference will be returned when *RESULT_POOL gets cleaned up or
* destroyed.
*/
@@ -146,9 +112,8 @@ svn_error_t *
svn_object_pool__insert(void **object,
svn_object_pool__t *object_pool,
const svn_membuf_t *key,
- void *wrapper,
- void *baton,
- apr_pool_t *wrapper_pool,
+ void *item,
+ apr_pool_t *item_pool,
apr_pool_t *result_pool);
#endif /* SVN_OBJECT_POOL_H */
diff --git a/subversion/include/private/svn_packed_data.h b/subversion/include/private/svn_packed_data.h
index 6faf0dd0e116..841be3a0cebb 100644
--- a/subversion/include/private/svn_packed_data.h
+++ b/subversion/include/private/svn_packed_data.h
@@ -218,6 +218,11 @@ svn_packed__int_count(svn_packed__int_stream_t *stream);
apr_size_t
svn_packed__byte_count(svn_packed__byte_stream_t *stream);
+/* Return the number of entries left to read from STREAM.
+ */
+apr_size_t
+svn_packed__byte_block_count(svn_packed__byte_stream_t *stream);
+
/* Return the next number from STREAM as unsigned integer. Returns 0 when
* reading beyond the end of the stream.
*/
diff --git a/subversion/include/private/svn_ra_svn_private.h b/subversion/include/private/svn_ra_svn_private.h
index bc2fa4533f31..42fd31ee060c 100644
--- a/subversion/include/private/svn_ra_svn_private.h
+++ b/subversion/include/private/svn_ra_svn_private.h
@@ -34,6 +34,104 @@
extern "C" {
#endif /* __cplusplus */
+/** Memory representation of an on-the-wire data item. */
+typedef struct svn_ra_svn__item_t svn_ra_svn__item_t;
+
+/* A list of svn_ra_svn__item_t objects. */
+typedef struct svn_ra_svn__list_t
+{
+ /* List contents (array). May be NULL if NELTS is 0. */
+ struct svn_ra_svn__item_t *items;
+
+ /* Number of elements in ITEMS. */
+ int nelts;
+} svn_ra_svn__list_t;
+
+/* List element access macro. */
+#define SVN_RA_SVN__LIST_ITEM(list, idx) (list)->items[idx]
+
+/** Memory representation of an on-the-wire data item. */
+struct svn_ra_svn__item_t
+{
+ /** Variant indicator. */
+ svn_ra_svn_item_kind_t kind;
+
+ /** Variant data. */
+ union {
+ apr_uint64_t number;
+ svn_string_t string;
+ svn_string_t word;
+ svn_ra_svn__list_t list;
+ } u;
+};
+
+/** Command handler, used by svn_ra_svn__handle_commands(). */
+typedef svn_error_t *(*svn_ra_svn__command_handler)(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ svn_ra_svn__list_t *params,
+ void *baton);
+
+/** Command table, used by svn_ra_svn_handle_commands().
+ */
+typedef struct svn_ra_svn__cmd_entry_t
+{
+ /** Name of the command */
+ const char *cmdname;
+
+ /** Handler for the command */
+ svn_ra_svn__command_handler handler;
+
+ /** Only set when used through a deprecated API.
+ * HANDLER is NULL in that case. */
+ svn_ra_svn_command_handler deprecated_handler;
+
+ /** Termination flag. If set, command-handling will cease after
+ * command is processed. */
+ svn_boolean_t terminate;
+} svn_ra_svn__cmd_entry_t;
+
+
+/* Return a deep copy of the SOURCE array containing private API
+ * svn_ra_svn__item_t SOURCE to public API *TARGET, allocating
+ * sub-structures in RESULT_POOL. */
+apr_array_header_t *
+svn_ra_svn__to_public_array(const svn_ra_svn__list_t *source,
+ apr_pool_t *result_pool);
+
+/* Deep copy contents from private API *SOURCE to public API *TARGET,
+ * allocating sub-structures in RESULT_POOL. */
+void
+svn_ra_svn__to_public_item(svn_ra_svn_item_t *target,
+ const svn_ra_svn__item_t *source,
+ apr_pool_t *result_pool);
+
+svn_ra_svn__list_t *
+svn_ra_svn__to_private_array(const apr_array_header_t *source,
+ apr_pool_t *result_pool);
+
+/* Deep copy contents from public API *SOURCE to private API *TARGET,
+ * allocating sub-structures in RESULT_POOL. */
+void
+svn_ra_svn__to_private_item(svn_ra_svn__item_t *target,
+ const svn_ra_svn_item_t *source,
+ apr_pool_t *result_pool);
+
+/** Add the capabilities in @a list to @a conn's capabilities.
+ * @a list contains svn_ra_svn__item_t entries (which should be of type
+ * SVN_RA_SVN_WORD; a malformed data error will result if any are not).
+ *
+ * This is idempotent: if a given capability was already set for
+ * @a conn, it remains set.
+ */
+svn_error_t *
+svn_ra_svn__set_capabilities(svn_ra_svn_conn_t *conn,
+ const svn_ra_svn__list_t *list);
+
+/** Returns the preferred svndiff version to be used with connection @a conn.
+ */
+int
+svn_ra_svn__svndiff_version(svn_ra_svn_conn_t *conn);
+
/**
* Set the shim callbacks to be used by @a conn to @a shim_callbacks.
@@ -161,10 +259,10 @@ svn_ra_svn__flush(svn_ra_svn_conn_t *conn,
* to transmit an array or other unusual data. For example, to write
* a tuple containing a revision, an array of words, and a boolean:
* @code
- SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "r(!", rev));
+ SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "r(!", rev));
for (i = 0; i < n; i++)
- SVN_ERR(svn_ra_svn_write_word(conn, pool, words[i]));
- SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!)b", flag)); @endcode
+ SVN_ERR(svn_ra_svn__write_word(conn, pool, words[i]));
+ SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)b", flag)); @endcode
*/
svn_error_t *
svn_ra_svn__write_tuple(svn_ra_svn_conn_t *conn,
@@ -175,7 +273,7 @@ svn_ra_svn__write_tuple(svn_ra_svn_conn_t *conn,
svn_error_t *
svn_ra_svn__read_item(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- svn_ra_svn_item_t **item);
+ svn_ra_svn__item_t **item);
/** Scan data on @a conn until we find something which looks like the
* beginning of an svn server greeting (an open paren followed by a
@@ -221,13 +319,12 @@ svn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn,
*
* If an optional part of a tuple contains no data, 'r' values will be
* set to @c SVN_INVALID_REVNUM; 'n' and 'B' values will be set to
- * #SVN_RA_SVN_UNSPECIFIED_NUMBER; 's', 'c', 'w', and 'l' values
- * will be set to @c NULL; and '3' values will be set to #svn_tristate_unknown
- * 'b' may not appear inside an optional tuple specification; use '3' instead.
+ * #SVN_RA_SVN_UNSPECIFIED_NUMBER; 's', 'c', 'w', and 'l' values will
+ * be set to @c NULL; '3' values will be set to #svn_tristate_unknown;
+ * and 'b' values will be set to @c FALSE.
*/
svn_error_t *
-svn_ra_svn__parse_tuple(const apr_array_header_t *list,
- apr_pool_t *pool,
+svn_ra_svn__parse_tuple(const svn_ra_svn__list_t *list,
const char *fmt, ...);
/** Read a tuple from the network and parse it as a tuple, using the
@@ -238,13 +335,13 @@ svn_ra_svn__read_tuple(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *fmt, ...);
-/** Parse an array of @c svn_ra_svn_item_t structures as a list of
+/** Parse an array of @c svn_ra_svn__item_t structures as a list of
* properties, storing the properties in a hash table.
*
* @since New in 1.5.
*/
svn_error_t *
-svn_ra_svn__parse_proplist(const apr_array_header_t *list,
+svn_ra_svn__parse_proplist(const svn_ra_svn__list_t *list,
apr_pool_t *pool,
apr_hash_t **props);
@@ -300,7 +397,7 @@ svn_ra_svn__handle_command(svn_boolean_t *terminate,
svn_error_t *
svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const svn_ra_svn_cmd_entry_t *commands,
+ const svn_ra_svn__cmd_entry_t *commands,
void *baton,
svn_boolean_t error_on_disconnect);
@@ -347,7 +444,7 @@ svn_error_t *
svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
svn_revnum_t rev,
- const char *token);
+ const svn_string_t *token);
/** Send a "delete-entry" command over connection @a conn. Delete the
* @a path at optional revision @a rev below @a parent_token.
@@ -358,7 +455,7 @@ svn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
svn_revnum_t rev,
- const char *parent_token);
+ const svn_string_t *parent_token);
/** Send a "add-dir" command over connection @a conn. Add a new directory
* node named @a path under the directory identified by @a parent_token.
@@ -370,8 +467,8 @@ svn_error_t *
svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token,
- const char *token,
+ const svn_string_t *parent_token,
+ const svn_string_t *token,
const char *copy_path,
svn_revnum_t copy_rev);
@@ -384,8 +481,8 @@ svn_error_t *
svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token,
- const char *token,
+ const svn_string_t *parent_token,
+ const svn_string_t *token,
svn_revnum_t rev);
/** Send a "change-dir-prop" command over connection @a conn. Set the
@@ -395,7 +492,7 @@ svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn,
svn_error_t *
svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token,
+ const svn_string_t *token,
const char *name,
const svn_string_t *value);
@@ -406,7 +503,7 @@ svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn,
svn_error_t *
svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token);
+ const svn_string_t *token);
/** Send a "absent-dir" command over connection @a conn. Directory node
* named @a path under the directory identified by @a parent_token is
@@ -416,7 +513,7 @@ svn_error_t *
svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token);
+ const svn_string_t *parent_token);
/** Send a "add-file" command over connection @a conn. Add a new file
* node named @a path under the directory identified by @a parent_token.
@@ -428,8 +525,8 @@ svn_error_t *
svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token,
- const char *token,
+ const svn_string_t *parent_token,
+ const svn_string_t *token,
const char *copy_path,
svn_revnum_t copy_rev);
@@ -442,8 +539,8 @@ svn_error_t *
svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token,
- const char *token,
+ const svn_string_t *parent_token,
+ const svn_string_t *token,
svn_revnum_t rev);
/** Send a "change-file-prop" command over connection @a conn. Set the
@@ -453,7 +550,7 @@ svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn,
svn_error_t *
svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token,
+ const svn_string_t *token,
const char *name,
const svn_string_t *value);
@@ -465,7 +562,7 @@ svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn,
svn_error_t *
svn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token,
+ const svn_string_t *token,
const char *text_checksum);
/** Send a "absent-file" command over connection @a conn. File node
@@ -476,7 +573,7 @@ svn_error_t *
svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *parent_token);
+ const svn_string_t *parent_token);
/** Send a "apply-textdelta" command over connection @a conn. Starts a
* series of text deltas to be applied to the file identified by @a token.
@@ -486,7 +583,7 @@ svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn,
svn_error_t *
svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token,
+ const svn_string_t *token,
const char *base_checksum);
/** Send a "textdelta-chunk" command over connection @a conn. Apply
@@ -496,7 +593,7 @@ svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn,
svn_error_t *
svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token,
+ const svn_string_t *token,
const svn_string_t *chunk);
/** Send a "textdelta-end" command over connection @a conn. Ends the
@@ -506,7 +603,7 @@ svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn,
svn_error_t *
svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *token);
+ const svn_string_t *token);
/** Send a "close-edit" command over connection @a conn. Ends the editor
* drive (successfully). Use @a pool for allocations.
@@ -790,7 +887,7 @@ svn_error_t *
svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *path,
- const char *token,
+ const svn_string_t *token,
svn_boolean_t break_lock);
/** Send a "get-lock" command over connection @a conn.
@@ -886,7 +983,7 @@ svn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t *conn,
svn_error_t *
svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
- const char *path,
+ const svn_string_t *path,
char action,
const char *copyfrom_path,
svn_revnum_t copyfrom_rev,
@@ -916,6 +1013,19 @@ svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn,
svn_boolean_t invalid_revnum,
unsigned revprop_count);
+/** Send a directory entry @a dirent for @a path over connection @a conn.
+ * Use @a pool for allocations.
+ *
+ * Depending on the flags in @a dirent_fields, only selected elements will
+ * be transmitted.
+ */
+svn_error_t *
+svn_ra_svn__write_dirent(svn_ra_svn_conn_t *conn,
+ apr_pool_t *pool,
+ const char *path,
+ svn_dirent_t *dirent,
+ apr_uint32_t dirent_fields);
+
/**
* @}
*/
@@ -931,7 +1041,7 @@ svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn,
* @see svn_log_changed_path2_t for a description of the output parameters.
*/
svn_error_t *
-svn_ra_svn__read_data_log_changed_entry(const apr_array_header_t *items,
+svn_ra_svn__read_data_log_changed_entry(const svn_ra_svn__list_t *items,
svn_string_t **cpath,
const char **action,
const char **copy_path,
diff --git a/subversion/include/private/svn_repos_private.h b/subversion/include/private/svn_repos_private.h
index c5a232f29b55..c65b73fc1e69 100644
--- a/subversion/include/private/svn_repos_private.h
+++ b/subversion/include/private/svn_repos_private.h
@@ -34,6 +34,7 @@
#include "svn_editor.h"
#include "svn_config.h"
+#include "private/svn_object_pool.h"
#include "private/svn_string_private.h"
#ifdef __cplusplus
@@ -74,6 +75,30 @@ svn_repos__validate_prop(const char *name,
const svn_string_t *value,
apr_pool_t *pool);
+/* Attempt to normalize a Subversion property if it "needs translation"
+ * (according to svn_prop_needs_translation(), currently all svn:* props).
+ *
+ * At this time, the only performed normalization is translation of
+ * the line endings of the property value so that it would only contain
+ * LF (\n) characters. "\r" characters found mid-line are replaced with "\n".
+ * "\r\n" sequences are replaced with "\n".
+ *
+ * NAME is used to check that VALUE should be normalized, and if this
+ * is the case, VALUE is then normalized, allocated from RESULT_POOL.
+ * If no normalization is required, VALUE will be copied to RESULT_POOL
+ * unchanged. If NORMALIZED_P is not NULL, and the normalization
+ * happened, set *NORMALIZED_P to non-zero. If the property is returned
+ * unchanged and NORMALIZED_P is not NULL, then *NORMALIZED_P will be
+ * set to zero. SCRATCH_POOL will be used for temporary allocations.
+ */
+svn_error_t *
+svn_repos__normalize_prop(const svn_string_t **result_p,
+ svn_boolean_t *normalized_p,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
/**
* Given the error @a err from svn_repos_fs_commit_txn(), return an
* string containing either or both of the svn_fs_commit_txn() error
@@ -127,26 +152,27 @@ svn_repos__replay_ev2(svn_fs_root_t *root,
void *authz_read_baton,
apr_pool_t *scratch_pool);
-/* Given a PATH which might be a relative repo URL (^/), an absolute
- * local repo URL (file://), an absolute path outside of the repo
- * or a location in the Windows registry.
- *
- * Retrieve the configuration data that PATH points at and parse it into
- * CFG_P allocated in POOL.
- *
- * If PATH cannot be parsed as a config file then an error is returned. The
- * contents of CFG_P is then undefined. If MUST_EXIST is TRUE, a missing
- * authz file is also an error. The CASE_SENSITIVE controls the lookup
- * behavior for section and option names alike.
+/**
+ * Non-deprecated alias for svn_repos_get_logs4.
*
- * REPOS_ROOT points at the root of the repos you are
- * going to apply the authz against, can be NULL if you are sure that you
- * don't have a repos relative URL in PATH. */
+ * Since the mapping of log5 to ra_get_log is would basically duplicate the
+ * log5->log4 adapter, we provide this log4 wrapper that does not create a
+ * deprecation warning.
+ */
svn_error_t *
-svn_repos__retrieve_config(svn_config_t **cfg_p,
- const char *path,
- svn_boolean_t must_exist,
- svn_boolean_t case_sensitive,
+svn_repos__get_logs_compat(svn_repos_t *repos,
+ const apr_array_header_t *paths,
+ svn_revnum_t start,
+ svn_revnum_t end,
+ int limit,
+ svn_boolean_t discover_changed_paths,
+ svn_boolean_t strict_node_history,
+ svn_boolean_t include_merged_revisions,
+ const apr_array_header_t *revprops,
+ svn_repos_authz_func_t authz_read_func,
+ void *authz_read_baton,
+ svn_log_entry_receiver_t receiver,
+ void *receiver_baton,
apr_pool_t *pool);
/**
@@ -160,7 +186,7 @@ svn_repos__retrieve_config(svn_config_t **cfg_p,
* from multiple threads. Configuration objects no longer referenced by
* any user may linger for a while before being cleaned up.
*/
-typedef struct svn_repos__config_pool_t svn_repos__config_pool_t;
+typedef svn_object_pool__t svn_repos__config_pool_t;
/* Create a new configuration pool object with a lifetime determined by
* POOL and return it in *CONFIG_POOL.
@@ -177,11 +203,10 @@ svn_repos__config_pool_create(svn_repos__config_pool_t **config_pool,
* configuration specified by PATH. If the latter is a URL, we read the
* data from a local repository. CONFIG_POOL will store the configuration
* and make further callers use the same instance if the content matches.
- * If KEY is not NULL, *KEY will be set to a unique ID - if available.
+ * Section and option names will be case-insensitive.
*
* If MUST_EXIST is TRUE, a missing config file is also an error, *CFG
- * is otherwise simply NULL. The CASE_SENSITIVE controls the lookup
- * behavior for section and option names alike.
+ * is otherwise simply NULL.
*
* PREFERRED_REPOS is only used if it is not NULL and PATH is a URL.
* If it matches the URL, access the repository through this object
@@ -194,72 +219,14 @@ svn_repos__config_pool_create(svn_repos__config_pool_t **config_pool,
*/
svn_error_t *
svn_repos__config_pool_get(svn_config_t **cfg,
- svn_membuf_t **key,
svn_repos__config_pool_t *config_pool,
const char *path,
svn_boolean_t must_exist,
- svn_boolean_t case_sensitive,
svn_repos_t *preferred_repos,
apr_pool_t *pool);
/** @} */
-/**
- * @defgroup svn_authz_pool Authz object pool API
- * @{
- */
-
-/* Opaque thread-safe factory and container for authorization objects.
- *
- * Instances handed out are read-only and may be given to multiple callers
- * from multiple threads. Authorization objects no longer referenced by
- * any user may linger for a while before being cleaned up.
- */
-typedef struct svn_repos__authz_pool_t svn_repos__authz_pool_t;
-
-/* Create a new authorization pool object with a lifetime determined by
- * POOL and return it in *AUTHZ_POOL. CONFIG_POOL will be the common
- * source for the configuration data underlying the authz objects and must
- * remain valid at least until POOL cleanup.
- *
- * The THREAD_SAFE flag indicates whether the pool actually needs to be
- * thread-safe and POOL must be also be thread-safe if this flag is set.
- */
-svn_error_t *
-svn_repos__authz_pool_create(svn_repos__authz_pool_t **authz_pool,
- svn_repos__config_pool_t *config_pool,
- svn_boolean_t thread_safe,
- apr_pool_t *pool);
-
-/* Set *AUTHZ_P to a read-only reference to the current contents of the
- * authorization specified by PATH and GROUPS_PATH. If these are URLs,
- * we read the data from a local repository (see #svn_repos_authz_read2).
- * AUTHZ_POOL will store the authz data and make further callers use the
- * same instance if the content matches.
- *
- * If MUST_EXIST is TRUE, a missing config file is also an error, *AUTHZ_P
- * is otherwise simply NULL.
- *
- * PREFERRED_REPOS is only used if it is not NULL and PATH is a URL.
- * If it matches the URL, access the repository through this object
- * instead of creating a new repo instance. Note that this might not
- * return the latest content.
- *
- * POOL determines the minimum lifetime of *AUTHZ_P (may remain cached
- * after release) but must not exceed the lifetime of the pool provided to
- * svn_repos__authz_pool_create.
- */
-svn_error_t *
-svn_repos__authz_pool_get(svn_authz_t **authz_p,
- svn_repos__authz_pool_t *authz_pool,
- const char *path,
- const char *groups_path,
- svn_boolean_t must_exist,
- svn_repos_t *preferred_repos,
- apr_pool_t *pool);
-
-/** @} */
-
/* Adjust mergeinfo paths and revisions in ways that are useful when loading
* a dump stream.
*
diff --git a/subversion/include/private/svn_sorts_private.h b/subversion/include/private/svn_sorts_private.h
index c358513e9233..2d5f1887f1b7 100644
--- a/subversion/include/private/svn_sorts_private.h
+++ b/subversion/include/private/svn_sorts_private.h
@@ -53,12 +53,12 @@ struct svn_sort__item_t {
/** Sort @a ht according to its keys, return an @c apr_array_header_t
* containing @c svn_sort__item_t structures holding those keys and values
* (i.e. for each @c svn_sort__item_t @a item in the returned array,
- * @a item->key and @a item->size are the hash key, and @a item->value points to
+ * @a item.key and @a item.size are the hash key, and @a item.value points to
* the hash value).
*
* Storage is shared with the original hash, not copied.
*
- * @a comparison_func should take two @c svn_sort__item_t's and return an
+ * @a comparison_func should take pointers to two items and return an
* integer greater than, equal to, or less than 0, according as the first item
* is greater than, equal to, or less than the second.
*
diff --git a/subversion/include/private/svn_sqlite.h b/subversion/include/private/svn_sqlite.h
index 4c6cb97cd506..cd01348ad595 100644
--- a/subversion/include/private/svn_sqlite.h
+++ b/subversion/include/private/svn_sqlite.h
@@ -555,8 +555,14 @@ svn_sqlite__hotcopy(const char *src_path,
const char *dst_path,
apr_pool_t *scratch_pool);
-/* Backported version of SVN_ERR_SQLITE_ROLLBACK_FAILED. */
-#define SVN_SQLITE__ERR_ROLLBACK_FAILED (SVN_ERR_MISC_CATEGORY_START + 44)
+/* Evaluate the expression EXPR. If any error is returned, close
+ * the connection in DB. */
+#define SVN_SQLITE__ERR_CLOSE(expr, db) do \
+{ \
+ svn_error_t *svn__err = (expr); \
+ if (svn__err) \
+ return svn_error_compose_create(svn__err, svn_sqlite__close(db)); \
+} while (0)
#ifdef __cplusplus
}
diff --git a/subversion/include/private/svn_string_private.h b/subversion/include/private/svn_string_private.h
index 2f162733db81..25e42049db7b 100644
--- a/subversion/include/private/svn_string_private.h
+++ b/subversion/include/private/svn_string_private.h
@@ -131,6 +131,14 @@ svn_membuf__nzero(svn_membuf_t *membuf, apr_size_t size);
svn_string_t *
svn_stringbuf__morph_into_string(svn_stringbuf_t *strbuf);
+/** Utility macro to define static svn_string_t objects. @a value must
+ * be a static string; the "" in the macro declaration tries to ensure this.
+ *
+ * Usage:
+ * static const svn_string_t my_string = SVN__STATIC_STRING("my text");
+ */
+#define SVN__STATIC_STRING(value) { value "", sizeof(value "") - 1 }
+
/** Like strtoul but with a fixed base of 10 and without overflow checks.
* This allows the compiler to generate massively faster (4x on 64bit LINUX)
* code. Overflow checks may be added on the caller side where you might
diff --git a/subversion/include/private/svn_subr_private.h b/subversion/include/private/svn_subr_private.h
index 095d71c5df2c..d18c564748d0 100644
--- a/subversion/include/private/svn_subr_private.h
+++ b/subversion/include/private/svn_subr_private.h
@@ -112,12 +112,12 @@ svn_spillbuf__get_size(const svn_spillbuf_t *buf);
svn_filesize_t
svn_spillbuf__get_memory_size(const svn_spillbuf_t *buf);
-/* Retrieve the name of the spill file. The returned value can be NULL
- if the file has not been created yet. */
+/* Retrieve the name of the spill file. The returned value will be
+ NULL if the file has not been created yet. */
const char *
svn_spillbuf__get_filename(const svn_spillbuf_t *buf);
-/* Retrieve the handle of the spill file. The returned value can be
+/* Retrieve the handle of the spill file. The returned value will be
NULL if the file has not been created yet. */
apr_file_t *
svn_spillbuf__get_file(const svn_spillbuf_t *buf);
@@ -133,8 +133,8 @@ svn_spillbuf__write(svn_spillbuf_t *buf,
/* Read a block of memory from the spill buffer. @a *data will be set to
NULL if no content remains. Otherwise, @a data and @a len will point to
data that must be fully-consumed by the caller. This data will remain
- valid until another call to svn_spillbuf_write(), svn_spillbuf_read(),
- or svn_spillbuf_process(), or if the spill buffer's pool is cleared. */
+ valid until another call to svn_spillbuf__write(), svn_spillbuf__read(),
+ or svn_spillbuf__process(), or if the spill buffer's pool is cleared. */
svn_error_t *
svn_spillbuf__read(const char **data,
apr_size_t *len,
@@ -143,7 +143,7 @@ svn_spillbuf__read(const char **data,
/* Callback for reading content out of the spill buffer. Set @a stop if
- you want to stop the processing (and will call svn_spillbuf_process
+ you want to stop the processing (and will call svn_spillbuf__process
again, at a later time). */
typedef svn_error_t * (*svn_spillbuf_read_t)(svn_boolean_t *stop,
void *baton,
@@ -472,7 +472,7 @@ svn_version__parse_version_string(svn_version_t **version,
* @since New in 1.8.
*/
svn_boolean_t
-svn_version__at_least(svn_version_t *version,
+svn_version__at_least(const svn_version_t *version,
int major,
int minor,
int patch);
@@ -525,6 +525,16 @@ svn_version__at_least(svn_version_t *version,
unsigned char *
svn__encode_uint(unsigned char *p, apr_uint64_t val);
+/* Wrapper around svn__encode_uint using the LSB to store the sign:
+ *
+ * If VAL >= 0
+ * UINT_VAL = 2 * VAL
+ * else
+ * UINT_VAL = (- 2 * VAL) - 1
+ */
+unsigned char *
+svn__encode_int(unsigned char *p, apr_int64_t val);
+
/* Decode an unsigned 7b/8b-encoded integer into *VAL and return a pointer
to the byte after the integer. The bytes to be decoded live in the
range [P..END-1]. If these bytes do not contain a whole encoded
@@ -537,22 +547,49 @@ svn__decode_uint(apr_uint64_t *val,
const unsigned char *p,
const unsigned char *end);
-/* Get the data from IN, compress it according to the specified
- * COMPRESSION_METHOD and write the result to OUT.
+/* Wrapper around svn__decode_uint, reversing the transformation performed
+ * by svn__encode_int.
+ */
+const unsigned char *
+svn__decode_int(apr_int64_t *val,
+ const unsigned char *p,
+ const unsigned char *end);
+
+/* Compress the data from DATA with length LEN, it according to the
+ * specified COMPRESSION_METHOD and write the result to OUT.
* SVN__COMPRESSION_NONE is valid for COMPRESSION_METHOD.
*/
svn_error_t *
-svn__compress(svn_stringbuf_t *in,
- svn_stringbuf_t *out,
- int compression_method);
+svn__compress_zlib(const void *data, apr_size_t len,
+ svn_stringbuf_t *out,
+ int compression_method);
-/* Get the compressed data from IN, decompress it and write the result to
- * OUT. Return an error if the decompressed size is larger than LIMIT.
+/* Decompress the compressed data from DATA with length LEN and write the
+ * result to OUT. Return an error if the decompressed size is larger than
+ * LIMIT.
*/
svn_error_t *
-svn__decompress(svn_stringbuf_t *in,
- svn_stringbuf_t *out,
- apr_size_t limit);
+svn__decompress_zlib(const void *data, apr_size_t len,
+ svn_stringbuf_t *out,
+ apr_size_t limit);
+
+/* Same as svn__compress_zlib(), but use LZ4 compression. Note that
+ * while the declaration of this function uses apr_size_t, it expects
+ * blocks of size not exceeding LZ4_MAX_INPUT_SIZE. The caller should
+ * ensure that the proper size is passed to this function.
+ */
+svn_error_t *
+svn__compress_lz4(const void *data, apr_size_t len,
+ svn_stringbuf_t *out);
+
+/* Same as svn__decompress_zlib(), but use LZ4 compression. The caller
+ * should ensure that the size and limit passed to this function do not
+ * exceed INT_MAX.
+ */
+svn_error_t *
+svn__decompress_lz4(const void *data, apr_size_t len,
+ svn_stringbuf_t *out,
+ apr_size_t limit);
/** @} */
@@ -701,6 +738,14 @@ const char *svn_zlib__compiled_version(void);
/* Return the zlib version we run against. */
const char *svn_zlib__runtime_version(void);
+/* Return the lz4 version we compiled against. */
+const char *svn_lz4__compiled_version(void);
+
+/* Return the lz4 version we run against as a composed value:
+ * major * 100 * 100 + minor * 100 + release
+ */
+int svn_lz4__runtime_version(void);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/subversion/include/private/svn_temp_serializer.h b/subversion/include/private/svn_temp_serializer.h
index dd2e0478e21c..6d648b133338 100644
--- a/subversion/include/private/svn_temp_serializer.h
+++ b/subversion/include/private/svn_temp_serializer.h
@@ -206,7 +206,7 @@ svn_temp_serializer__get(svn_temp_serializer__context_t *context);
* the pointer to resolve in @a ptr.
*/
void
-svn_temp_deserializer__resolve(void *buffer, void **ptr);
+svn_temp_deserializer__resolve(const void *buffer, void **ptr);
/**
* Similar to svn_temp_deserializer__resolve() but instead of modifying
diff --git a/subversion/include/private/svn_utf_private.h b/subversion/include/private/svn_utf_private.h
index 4584944fb47c..473e731141bf 100644
--- a/subversion/include/private/svn_utf_private.h
+++ b/subversion/include/private/svn_utf_private.h
@@ -150,6 +150,40 @@ svn_utf__normalize(const char **result,
const char *str, apr_size_t len,
svn_membuf_t *buf);
+/* Transform the UTF-8 string to a shape suitable for comparison with
+ * strcmp(). The tranformation is defined by CASE_INSENSITIVE and
+ * ACCENT_INSENSITIVE arguments. If CASE_INSENSITIVE is non-zero,
+ * remove case distinctions from the string. If ACCENT_INSENSITIVE
+ * is non-zero, remove diacritical marks from the string.
+ *
+ * Use BUF as a temporary storage. If LEN is SVN_UTF__UNKNOWN_LENGTH,
+ * assume STR is null-terminated; otherwise, consider the string only
+ * up to the given length. Place the tranformed string in *RESULT, which
+ * shares storage with BUF and is valid only until the next time BUF is
+ * modified.
+ *
+ * A returned error may indicate that STRING contains invalid UTF-8 or
+ * invalid Unicode codepoints.
+ */
+svn_error_t *
+svn_utf__xfrm(const char **result,
+ const char *str, apr_size_t len,
+ svn_boolean_t case_insensitive,
+ svn_boolean_t accent_insensitive,
+ svn_membuf_t *buf);
+
+/* Return TRUE if S matches any of the const char * glob patterns in
+ * PATTERNS.
+ *
+ * S will internally be normalized to lower-case and accents removed
+ * using svn_utf__xfrm. To get a match, the PATTERNS must have been
+ * normalized accordingly before calling this function.
+ */
+svn_boolean_t
+svn_utf__fuzzy_glob_match(const char *str,
+ const apr_array_header_t *patterns,
+ svn_membuf_t *buf);
+
/* Check if STRING is a valid, NFC-normalized UTF-8 string. Note that
* a FALSE return value may indicate that STRING is not valid UTF-8 at
* all.
@@ -176,7 +210,7 @@ svn_utf__encode_ucs4_string(svn_membuf_t *buffer,
apr_size_t length,
apr_size_t *result_length);
-/* Pattern matching similar to the the SQLite LIKE and GLOB
+/* Pattern matching similar to the SQLite LIKE and GLOB
* operators. PATTERN, KEY and ESCAPE must all point to UTF-8
* strings. Furthermore, ESCAPE, if provided, must be a character from
* the ASCII subset.
diff --git a/subversion/include/private/svn_wc_private.h b/subversion/include/private/svn_wc_private.h
index 28d22479118e..521d092be692 100644
--- a/subversion/include/private/svn_wc_private.h
+++ b/subversion/include/private/svn_wc_private.h
@@ -82,6 +82,8 @@ svn_wc__get_file_external_editor(const svn_delta_editor_t **editor,
const char *recorded_url,
const svn_opt_revision_t *recorded_peg_rev,
const svn_opt_revision_t *recorded_rev,
+ svn_wc_conflict_resolver_func2_t conflict_func,
+ void *conflict_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
@@ -346,6 +348,20 @@ svn_wc__get_wcroot(const char **wcroot_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
+/** Set @a *dir to the abspath of the directory in which shelved patches
+ * are stored, which is inside the WC's administrative directory, and ensure
+ * the directory exists.
+ *
+ * @a local_abspath is any path in the WC, and is used to find the WC root.
+ */
+SVN_EXPERIMENTAL
+svn_error_t *
+svn_wc__get_shelves_dir(char **dir,
+ svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
/**
* The following are temporary APIs to aid in the transition from wc-1 to
* wc-ng. Use them for new development now, but they may be disappearing
@@ -1746,6 +1762,207 @@ svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx,
void *notify_baton,
apr_pool_t *scratch_pool);
+/**
+ * Resolve the text conflict at LOCAL_ABSPATH as per CHOICE, and then
+ * mark the conflict resolved.
+ * The working copy must already be locked for resolving, e.g. by calling
+ * svn_wc__acquire_write_lock_for_resolve() first.
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_wc__conflict_text_mark_resolved(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ svn_wc_conflict_choice_t choice,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Resolve the conflicted property PROPNAME at LOCAL_ABSPATH as per CHOICE,
+ * and then mark the conflict resolved. If MERGED_VALUE is not NULL, this is
+ * the new merged property, used when choosing #svn_wc_conflict_choose_merged.
+ *
+ * The working copy must already be locked for resolving, e.g. by calling
+ * svn_wc__acquire_write_lock_for_resolve() first.
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_wc__conflict_prop_mark_resolved(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ const char *propname,
+ svn_wc_conflict_choice_t choice,
+ const svn_string_t *merged_value,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool);
+
+/* Resolve a tree conflict where the victim at LOCAL_ABSPATH is a directory
+ * which was locally deleted, replaced or moved away, and which received an
+ * arbitrary incoming change during an update or switch operation.
+ *
+ * The conflict is resolved by accepting the current working copy state and
+ * breaking the 'moved-here' link for any files or directories which were
+ * moved out of the victim directory before the update operation.
+ * As a result, any such files or directories become copies (rather than moves)
+ * of content which the victim directory contained before it was updated.
+ *
+ * The tree conflict at LOCAL_ABSPATH must have the following properties or
+ * SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE will be returned:
+ *
+ * operation: svn_wc_operation_update or svn_wc_operation_switch
+ * local change: svn_wc_conflict_reason_deleted or
+ * svn_wc_conflict_reason_replaced or
+ * svn_wc_conflict_reason_moved_away
+ * incoming change: any
+ *
+ * The working copy must already be locked for resolving, e.g. by calling
+ * svn_wc__acquire_write_lock_for_resolve() first.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_wc__conflict_tree_update_break_moved_away(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool);
+
+
+/* Resolve a tree conflict where the victim at LOCAL_ABSPATH is a directory
+ * which was locally deleted or replaced, and which received an edit (some
+ * change inside the directory, or a change to the direcotory's properties)
+ * during an update or switch operation.
+ *
+ * The conflict is resolved by keeping the victim deleted, and propagating
+ * its tree conflict to any children which were moved out of the directory
+ * before the update operation.
+ * As a result, any such files or directories become victims of the tree
+ * conflict as well and must be resolved independently.
+ * Additionally, LOCAL_ABSPATH itself may become the victim of a different
+ * tree conflict as a result of resolving the existing tree conflict.
+ *
+ * The tree conflict at LOCAL_ABSPATH must have the following properties or
+ * SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE will be returned:
+ *
+ * operation: svn_wc_operation_update or svn_wc_operation_switch
+ * local change: svn_wc_conflict_reason_deleted or
+ * svn_wc_conflict_reason_replaced
+ * incoming change: svn_wc_conflict_action_edit
+ *
+ * If this conflict cannot be resolved because the conflict cannot be
+ * propagated to moved-away children, this function returns
+ * SVN_ERR_WC_OBSTRUCTED_UPDATE or SVN_ERR_WC_FOUND_CONFLICT.
+ * The caller should continue by resolving other conflicts and attempt to
+ * resolve this conflict again later.
+ *
+ * The working copy must already be locked for resolving, e.g. by calling
+ * svn_wc__acquire_write_lock_for_resolve() first.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_wc__conflict_tree_update_raise_moved_away(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool);
+
+/* Resolve a tree conflict where the victim at LOCAL_ABSPATH is a file or
+ * directory which was locally moved away, and which received an edit (some
+ * change inside the directory or file, or a change to properties) during an
+ * update or switch operation.
+ *
+ * The conflict is resolved by keeping the victim moved-away, and propagating
+ * the incoming edits to the victim's moved-to location.
+ *
+ * The tree conflict at LOCAL_ABSPATH must have the following properties or
+ * SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE will be returned:
+ *
+ * operation: svn_wc_operation_update or svn_wc_operation_switch
+ * local change: svn_wc_conflict_reason_moved_away
+ * incoming change: svn_wc_conflict_action_edit
+ *
+ * If this conflict cannot be resolved this function returns
+ * SVN_ERR_WC_OBSTRUCTED_UPDATE or SVN_ERR_WC_FOUND_CONFLICT.
+ * The caller should continue by resolving other conflicts and attempt to
+ * resolve this conflict again later.
+ *
+ * The working copy must already be locked for resolving, e.g. by calling
+ * svn_wc__acquire_write_lock_for_resolve() first.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_wc__conflict_tree_update_moved_away_node(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool);
+
+/* Merge local changes from a tree conflict victim of an incoming deletion
+ * to the specified DEST_ABSPATH added during an update. Both LOCAL_ABSPATH
+ * and DEST_ABSPATH must be directories.
+ *
+ * Assuming DEST_ABSPATH is the correct move destination, this function
+ * allows local changes to "follow" incoming moves during updates.
+ *
+ * @since New in 1.10. */
+svn_error_t *
+svn_wc__conflict_tree_update_incoming_move(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ const char *dest_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool);
+
+/* Resolve a 'local dir add vs incoming dir add' tree conflict upon update
+ * by merging the locally added directory with the incoming added directory.
+ *
+ * @since New in 1.10. */
+svn_error_t *
+svn_wc__conflict_tree_update_local_add(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool);
+
+/* Find nodes in the working copy which corresponds to the new location
+ * MOVED_TO_REPOS_RELPATH of the tree conflict victim at VICTIM_ABSPATH.
+ * The nodes must be of the same node kind as VICTIM_NODE_KIND.
+ * If no such node can be found, set *POSSIBLE_TARGETS to an empty array.
+ *
+ * The nodes should be useful for conflict resolution, e.g. it should be
+ * possible to merge changes into these nodes to resolve an incoming-move
+ * tree conflict. But the exact criteria for selecting a node are left
+ * to the implementation of this function.
+ * Note that this function may not necessarily return a node which was
+ * actually moved. The only hard guarantee is that the node corresponds to
+ * the repository relpath MOVED_TO_REPOS_RELPATH specified by the caller.
+ * Users should perform a sanity check on the results returned from this
+ * function, e.g. establish whether the MOVED_TO_REPOS_RELPATH at its
+ * current checked-out revision shares ancestry with the conflict victim.
+ */
+svn_error_t *
+svn_wc__guess_incoming_move_target_nodes(apr_array_header_t **possible_targets,
+ svn_wc_context_t *wc_ctx,
+ const char *victim_abspath,
+ svn_node_kind_t victim_node_kind,
+ const char *moved_to_repos_relpath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
/**
* Move @a src_abspath to @a dst_abspath, by scheduling @a dst_abspath
* for addition to the repository, remembering the history. Mark @a src_abspath
@@ -1847,6 +2064,28 @@ svn_wc__diff7(const char **root_relpath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
+/**
+ * Read all conflicts at LOCAL_ABSPATH into an array containing pointers to
+ * svn_wc_conflict_description2_t data structures alloated in RESULT_POOL.
+ */
+svn_error_t *
+svn_wc__read_conflict_descriptions2_t(const apr_array_header_t **conflicts,
+ svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* Internal version of svn_wc_translated_stream(), accepting a working
+ copy context. */
+svn_error_t *
+svn_wc__translated_stream(svn_stream_t **stream,
+ svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ const char *versioned_abspath,
+ apr_uint32_t flags,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/subversion/include/svn_auth.h b/subversion/include/svn_auth.h
index 3a78ba6ace5f..ca3b703a4970 100644
--- a/subversion/include/svn_auth.h
+++ b/subversion/include/svn_auth.h
@@ -1012,11 +1012,13 @@ typedef svn_error_t *(*svn_auth_gnome_keyring_unlock_prompt_func_t)(
/** @brief The pointer to function which prompts user for GNOME Keyring
* password.
- * The type of this pointer should be svn_auth_gnome_keyring_unlock_prompt_func_t. */
+ * The type of this pointer should be svn_auth_gnome_keyring_unlock_prompt_func_t.
+ * @deprecated Only used by old libgnome-keyring implementation. */
#define SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_FUNC "gnome-keyring-unlock-prompt-func"
/** @brief The baton which is passed to
- * @c *SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_FUNC. */
+ * @c *SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_FUNC.
+ * @deprecated Only used by old libgnome-keyring implementation. */
#define SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_BATON "gnome-keyring-unlock-prompt-baton"
#if (!defined(DARWIN) && !defined(WIN32)) || defined(DOXYGEN)
@@ -1037,7 +1039,7 @@ svn_auth_gnome_keyring_version(void);
* This is like svn_client_get_simple_provider(), except that the
* password is stored in GNOME Keyring.
*
- * If the GNOME Keyring is locked the provider calls
+ * If the GNOME Keyring is locked the old libgnome-keyring provider calls
* @c *SVN_AUTH_PARAM_GNOME_KEYRING_UNLOCK_PROMPT_FUNC in order to unlock
* the keyring.
*
diff --git a/subversion/include/svn_base64.h b/subversion/include/svn_base64.h
index cc1820fb93cf..90316afba334 100644
--- a/subversion/include/svn_base64.h
+++ b/subversion/include/svn_base64.h
@@ -46,11 +46,26 @@ extern "C" {
*/
/** Return a writable generic stream which will encode binary data in
- * base64 format and write the encoded data to @a output. Be sure to
- * close the stream when done writing in order to squeeze out the last
- * bit of encoded data. The stream is allocated in @a pool.
+ * base64 format and write the encoded data to @a output. If @a break_lines
+ * is true, newlines will be inserted periodically; otherwise the output
+ * stream will only consist of base64 encoding characters. Be sure to close
+ * the stream when done writing in order to squeeze out the last bit of
+ * encoded data. The stream is allocated in @a pool.
+ *
+ * @since New in 1.10.
*/
svn_stream_t *
+svn_base64_encode2(svn_stream_t *output,
+ svn_boolean_t break_lines,
+ apr_pool_t *pool);
+
+/**
+ * Same as svn_base64_encode2, but with @a break_lines always TRUE.
+ *
+ * @deprecated Provided for backward compatibility with the 1.9 API.
+ */
+SVN_DEPRECATED
+svn_stream_t *
svn_base64_encode(svn_stream_t *output,
apr_pool_t *pool);
diff --git a/subversion/include/svn_checksum.h b/subversion/include/svn_checksum.h
index e332e87e328d..1e58167ab068 100644
--- a/subversion/include/svn_checksum.h
+++ b/subversion/include/svn_checksum.h
@@ -177,6 +177,10 @@ svn_checksum_deserialize(const svn_checksum_t **checksum,
*
* @since New in 1.6.
*/
+/* ### TODO: When revving this, make it set @a *checksum to a non-NULL struct
+ * ### when @a hex is the all-zeroes checksum. See
+ * ### http://mail-archives.apache.org/mod_mbox/subversion-dev/201609.mbox/%3c00cd26ab-bdb3-67b4-ca6b-063266493874%40apache.org%3e
+ */
svn_error_t *
svn_checksum_parse_hex(svn_checksum_t **checksum,
svn_checksum_kind_t kind,
@@ -219,6 +223,15 @@ svn_checksum_ctx_create(svn_checksum_kind_t kind,
apr_pool_t *pool);
/**
+ * Reset an existing checksum @a ctx to initial state.
+ * @see svn_checksum_ctx_create()
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_checksum_ctx_reset(svn_checksum_ctx_t *ctx);
+
+/**
* Update the checksum represented by @a ctx, with @a len bytes starting at
* @a data.
*
diff --git a/subversion/include/svn_client.h b/subversion/include/svn_client.h
index cb0f49d98d49..06dbbc35a7b3 100644
--- a/subversion/include/svn_client.h
+++ b/subversion/include/svn_client.h
@@ -1975,7 +1975,7 @@ typedef svn_error_t *(*svn_client_import_filter_func_t)(
* on @a url if @a url is already under versioned control, or the nearest parents
* of @a path which are already under version control if not.
*
- * If @a ignore_unknown_node_types is @c FALSE, ignore files of which the
+ * If @a ignore_unknown_node_types is @c TRUE, ignore files of which the
* node type is unknown, such as device files and pipes.
*
* If @a filter_callback is non-NULL, call it for each node that isn't ignored
@@ -4356,6 +4356,760 @@ svn_client_revert(const apr_array_header_t *paths,
/** @} */
/**
+ * @defgroup Conflicts Dealing with conflicted paths.
+ *
+ * @{
+ */
+
+/**
+ * An opaque type which represents a conflicted node in the working copy.
+ *
+ * @since New in 1.10.
+ */
+typedef struct svn_client_conflict_t svn_client_conflict_t;
+
+/**
+ * An opaque type which represents a resolution option for a conflict.
+ *
+ * @since New in 1.10.
+ */
+typedef struct svn_client_conflict_option_t svn_client_conflict_option_t;
+
+/**
+ * A public enumeration of conflict option IDs.
+ *
+ * @since New in 1.10, unless noted otherwise.
+ */
+typedef enum svn_client_conflict_option_id_t {
+
+ /* Options for text and property conflicts.
+ * These values intentionally mirror svn_wc_conflict_choice_t. */
+ svn_client_conflict_option_undefined = -1, /* for private use only */
+ svn_client_conflict_option_postpone = 0,
+ svn_client_conflict_option_base_text,
+ svn_client_conflict_option_incoming_text, /* "theirs-full" */
+ svn_client_conflict_option_working_text, /* "mine-full" */
+ svn_client_conflict_option_incoming_text_where_conflicted,
+ svn_client_conflict_option_working_text_where_conflicted,
+ svn_client_conflict_option_merged_text,
+ svn_client_conflict_option_unspecified,
+ /* Values derived from svn_wc_conflict_choice_t end here. */
+
+ /* Tree conflict resolution options start here. */
+
+ /* Accept current working copy state. */
+ svn_client_conflict_option_accept_current_wc_state,
+
+ /* Options for local move vs incoming edit on update. */
+ svn_client_conflict_option_update_move_destination,
+
+ /* Options for local delete/replace vs incoming edit on update. */
+ svn_client_conflict_option_update_any_moved_away_children,
+
+ /* Options for incoming add vs local add or obstruction. */
+ svn_client_conflict_option_incoming_add_ignore,
+
+ /* Options for incoming file add vs local file add or obstruction. */
+ svn_client_conflict_option_incoming_added_file_text_merge,
+ svn_client_conflict_option_incoming_added_file_replace_and_merge,
+
+ /* Options for incoming dir add vs local dir add or obstruction. */
+ svn_client_conflict_option_incoming_added_dir_merge,
+ svn_client_conflict_option_incoming_added_dir_replace,
+ svn_client_conflict_option_incoming_added_dir_replace_and_merge,
+
+ /* Options for incoming delete vs any */
+ svn_client_conflict_option_incoming_delete_ignore,
+ svn_client_conflict_option_incoming_delete_accept,
+
+ /* Options for incoming move vs local edit */
+ svn_client_conflict_option_incoming_move_file_text_merge,
+ svn_client_conflict_option_incoming_move_dir_merge,
+
+ /* Options for local move vs incoming edit on merge. */
+ svn_client_conflict_option_local_move_file_text_merge
+} svn_client_conflict_option_id_t;
+
+/**
+ * Set a merged property value on @a option to @a merged_propval.
+ *
+ * Setting the merged value is required before resolving the property
+ * conflict using an option with ID svn_client_conflict_option_merged_text.
+ *
+ * The contents of @a merged_propval are not copied, so the storage it
+ * points to needs to remain valid until svn_client_conflict_prop_resolve()
+ * has been called with @a option.
+ *
+ * @since New in 1.10.
+ */
+void
+svn_client_conflict_option_set_merged_propval(
+ svn_client_conflict_option_t *option,
+ const svn_string_t *merged_propval);
+
+/**
+ * Get a list of possible repository paths which can be applied to the
+ * svn_client_conflict_option_incoming_move_file_text_merge or
+ * svn_client_conflict_option_incoming_move_dir_merge resolution
+ * @a option. (If a different option is passed in, this function will
+ * raise an assertion failure.)
+ *
+ * In some situations, there can be multiple possible destinations for an
+ * incoming move. One such situation is where a file was copied and moved
+ * in the same revision: svn cp a b; svn mv a c; svn commit
+ * When this move is merged elsewhere, both b and c will appear as valid move
+ * destinations to the conflict resolver. To resolve such ambiguity, the client
+ * may call this function to obtain a list of possible destinations the user
+ * may choose from.
+ *
+ * The array is allocated in @a result_pool and will have "const char *"
+ * elements pointing to strings also allocated in @a result_pool.
+ * All paths are relpaths, and relative to the repository root.
+ *
+ * @see svn_client_conflict_option_set_moved_to_repos_relpath()
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_option_get_moved_to_repos_relpath_candidates(
+ apr_array_header_t **possible_moved_to_repos_relpaths,
+ svn_client_conflict_option_t *option,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Set the preferred moved target repository path for the
+ * svn_client_conflict_option_incoming_move_file_text_merge or
+ * svn_client_conflict_option_incoming_move_dir_merge resolution option.
+ *
+ * @a preferred_move_target_idx must be a valid index into the list returned
+ * by svn_client_conflict_option_get_moved_to_repos_relpath_candidates().
+ *
+ * This function can be called multiple times.
+ * It affects the output of svn_client_conflict_tree_get_description() and
+ * svn_client_conflict_option_get_description(). Call these functions again
+ * to get updated descriptions containing the newly selected move target.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_option_set_moved_to_repos_relpath(
+ svn_client_conflict_option_t *option,
+ int preferred_move_target_idx,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Get a list of possible moved-to abspaths in the working copy which can be
+ * applied to the svn_client_conflict_option_incoming_move_file_text_merge
+ * or svn_client_conflict_option_incoming_move_dir_merge resolution @a option.
+ * (If a different option is passed in, this function will raise an assertion
+ * failure.)
+ *
+ * All paths in the returned list correspond to the repository path which
+ * is assumed to be the destination of the incoming move operation.
+ * To support cases where this destination path is ambiguous, the client may
+ * call svn_client_conflict_option_get_moved_to_repos_relpath_candidates()
+ * before calling this function to let the user select a repository path first.
+ *
+ * If no possible moved-to paths can be found, return an empty array.
+ * This doesn't mean that no move happened in the repository. It is possible
+ * that the move destination is outside the scope of the current working copy,
+ * for example, in which case the conflict must be resolved in some other way.
+ *
+ * @see svn_client_conflict_option_set_moved_to_abspath()
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_option_get_moved_to_abspath_candidates(
+ apr_array_header_t **possible_moved_to_abspaths,
+ svn_client_conflict_option_t *option,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Set the preferred moved target abspath for the
+ * svn_client_conflict_option_incoming_move_file_text_merge or
+ * svn_client_conflict_option_incoming_move_dir_merge resolution option.
+ *
+ * @a preferred_move_target_idx must be a valid index into the list
+ * returned by svn_client_conflict_option_get_moved_to_abspath_candidates().
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_option_set_moved_to_abspath(
+ svn_client_conflict_option_t *option,
+ int preferred_move_target_idx,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Given an @a option_id, try to find the corresponding option in @a options,
+ * which is an array of svn_client_conflict_option_t * elements.
+ *
+ * Return NULL if no corresponding option can be be found.
+ *
+ * @since New in 1.10.
+ */
+svn_client_conflict_option_t *
+svn_client_conflict_option_find_by_id(
+ apr_array_header_t *options,
+ svn_client_conflict_option_id_t option_id);
+
+/**
+ * Return a conflict for the conflicted path @a local_abspath.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_get(svn_client_conflict_t **conflict,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Callback for svn_client_conflict_conflict_walk();
+ *
+ * The lifetime of @a conflict is limited. Its allocation in
+ * memory will not persist beyond this callback's execution.
+ *
+ * @since New in 1.10.
+ */
+typedef svn_error_t *(*svn_client_conflict_walk_func_t)(
+ void *baton,
+ svn_client_conflict_t *conflict,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Walk all conflicts within the specified @a depth of @a local_abspath.
+ * Pass each conflict found during the walk to the @conflict_walk_func
+ * callback, along with @a conflict_walk_func_baton.
+ * Use cancellation and notification support provided by client context @a ctx.
+ *
+ * This callback may choose to resolve the conflict. If the act of resolving
+ * a conflict creates new conflicts within the walked working copy (as might
+ * be the case for some tree conflicts), the callback will be invoked for each
+ * such new conflict as well.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_walk(const char *local_abspath,
+ svn_depth_t depth,
+ svn_client_conflict_walk_func_t conflict_walk_func,
+ void *conflict_walk_func_baton,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
+/**
+* Indicate the types of conflicts present on the working copy node
+* described by @a conflict. Any output argument may be @c NULL if
+* the caller is not interested in the status of a particular type.
+*
+* The returned @a *props_conflicted array is allocated in @a result_pool.
+* It contains the names of conflicted properties. If no property conflict
+* exists, the array will contain no elements.
+*
+* @since New in 1.10.
+*/
+svn_error_t *
+svn_client_conflict_get_conflicted(svn_boolean_t *text_conflicted,
+ apr_array_header_t **props_conflicted,
+ svn_boolean_t *tree_conflicted,
+ svn_client_conflict_t *conflict,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Return a textual human-readable description of the property conflict
+ * described by @a conflict, allocated in @a result_pool. The description
+ * is encoded in UTF-8 and may contain multiple lines separated by
+ * @c APR_EOL_STR. The last line is not terminated by a newline.
+ *
+ * Additionally, the description may be localized to the language used
+ * by the current locale.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_prop_get_description(const char **description,
+ svn_client_conflict_t *conflict,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Return a textual human-readable description of the tree conflict
+ * described by @a conflict, allocated in @a result_pool. The description
+ * is encoded in UTF-8 and may contain multiple lines separated by
+ * @c APR_EOL_STR. The last line is not terminated by a newline.
+ *
+ * Additionally, the description may be localized to the language used
+ * by the current locale.
+ *
+ * While client implementors are free to enhance descriptions by adding
+ * additional information to the text, or break up very long lines for
+ * formatting purposes, there is no syntax defined for descriptions, and
+ * implementors should not rely on any particular parts of descriptions
+ * to remain stable over time, apart from what is stated below.
+ * Descriptions may or may not form complete sentences. They may contain
+ * paths relative to the repository root; such paths always start with "^/",
+ * and end with either a peg revision (e.g. "@100") or a colon followed by
+ * a range of revisions (as in svn:mergeinfo, e.g. ":100-200").
+ * Paths may appear on a line of their own to avoid overlong lines.
+ * Any revision numbers mentioned elsewhere in the description are
+ * prefixed with the letter 'r' (e.g. "r99").
+ *
+ * The description has two parts: One part describes the "incoming change"
+ * applied by an update, merge, or switch operation. The other part
+ * describes the "local change" which occurred in the working copy or
+ * perhaps in the history of a merge target branch.
+ * Both parts are provided independently to allow for some flexibility
+ * when displaying the description. As a convention, displaying the
+ * "incoming change" first and the "local change" second is recommended.
+ *
+ * By default, the description is based only on information available in
+ * the working copy. If svn_client_conflict_tree_get_details() was already
+ * called for @a conflict, the description might also contain useful
+ * information obtained from the repository and provide for a much better
+ * user experience.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_tree_get_description(
+ const char **incoming_change_description,
+ const char **local_change_description,
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Set @a *options to an array of pointers to svn_client_conflict_option_t
+ * objects applicable to text conflicts described by @a conflict.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_text_get_resolution_options(apr_array_header_t **options,
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Set @a *options to an array of pointers to svn_client_conflict_option_t
+ * objects applicable to property conflicts described by @a conflict.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_prop_get_resolution_options(apr_array_header_t **options,
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Set @a *options to an array of pointers to svn_client_conflict_option_t
+ * objects applicable to the tree conflict described by @a conflict.
+ *
+ * By default, the list of options is based only on information available in
+ * the working copy. If svn_client_conflict_tree_get_details() was already
+ * called for @a conflict, a more useful list of options might be returned.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_tree_get_resolution_options(apr_array_header_t **options,
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Find more information about the tree conflict represented by @a conflict.
+ *
+ * A call to svn_client_conflict_tree_get_description() may yield much better
+ * results after this function has been called.
+ *
+ * A call to svn_client_conflict_tree_get_resolution_options() may provide
+ * more useful resolution options if this function has been called.
+ *
+ * This function may contact the repository. Use the authentication baton
+ * cached in @a ctx for authentication if contacting the repository.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_tree_get_details(svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Return an ID for @a option. This ID can be used by callers to associate
+ * arbitrary data with a particular conflict resolution option.
+ *
+ * The ID of a particular resolution option will never change in future
+ * revisions of this API.
+ *
+ * @since New in 1.10.
+ */
+svn_client_conflict_option_id_t
+svn_client_conflict_option_get_id(svn_client_conflict_option_t *option);
+
+/**
+ * Return a textual human-readable label of @a option, allocated in
+ * @a result_pool. The label is encoded in UTF-8 and usually
+ * contains up to three words.
+ *
+ * Additionally, the label may be localized to the language used
+ * by the current locale.
+ *
+ * @since New in 1.10.
+ */
+const char *
+svn_client_conflict_option_get_label(svn_client_conflict_option_t *option,
+ apr_pool_t *result_pool);
+
+/**
+ * Return a textual human-readable description of @a option, allocated in
+ * @a result_pool. The description is encoded in UTF-8 and may contain
+ * multiple lines separated by @c APR_EOL_STR.
+ *
+ * Additionally, the description may be localized to the language used
+ * by the current locale.
+ *
+ * @since New in 1.10.
+ */
+const char *
+svn_client_conflict_option_get_description(svn_client_conflict_option_t *option,
+ apr_pool_t *result_pool);
+
+/**
+ * Return the ID of the recommended resolution option. If no specific option
+ * is recommended, return @c svn_client_conflict_option_unspecified;
+ *
+ * Client implementations which aim to avoid excessive interactive prompting
+ * may wish to try a recommended resolution option before falling back to
+ * asking the user which option to use.
+ *
+ * Conflict resolution with a recommended option is not guaranteed to succeed.
+ * Clients should check for errors when trying to resolve a conflict and fall
+ * back to other options and/or interactive prompting when the recommended
+ * option fails to resolve a conflict.
+ *
+ * If @a conflict is a tree conflict, svn_client_conflict_tree_get_details()
+ * should be called before this function to allow for useful recommendations.
+ *
+ * @since New in 1.10.
+ */
+svn_client_conflict_option_id_t
+svn_client_conflict_get_recommended_option_id(svn_client_conflict_t *conflict);
+
+/**
+ * Return the absolute path to the conflicted working copy node described
+ * by @a conflict.
+ *
+ * @since New in 1.10.
+ */
+const char *
+svn_client_conflict_get_local_abspath(svn_client_conflict_t *conflict);
+
+/**
+ * Return the operation during which the conflict described by @a
+ * conflict was recorded.
+ *
+ * @since New in 1.10.
+ */
+svn_wc_operation_t
+svn_client_conflict_get_operation(svn_client_conflict_t *conflict);
+
+/**
+ * Return the action an update, switch, or merge operation attempted to
+ * perform on the working copy node described by @a conflict.
+ *
+ * @since New in 1.10.
+ */
+svn_wc_conflict_action_t
+svn_client_conflict_get_incoming_change(svn_client_conflict_t *conflict);
+
+/**
+ * Return the reason why the attempted action performed by an update, switch,
+ * or merge operation conflicted with the state of the node in the working copy.
+ *
+ * During update and switch operations this local change is part of uncommitted
+ * modifications in the working copy. During merge operations it may
+ * additionally be part of the history of the merge target branch, anywhere
+ * between the common ancestor revision and the working copy revision.
+ *
+ * @since New in 1.10.
+ */
+svn_wc_conflict_reason_t
+svn_client_conflict_get_local_change(svn_client_conflict_t *conflict);
+
+/**
+ * Return information about the repository associated with @a conflict.
+ * In case of a foreign-repository merge this will differ from the
+ * repository information associated with the merge target working copy.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_get_repos_info(const char **repos_root_url,
+ const char **repos_uuid,
+ svn_client_conflict_t *conflict,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Return the repository-relative location and the node kind of the incoming
+ * old version of the conflicted node described by @a conflict.
+ *
+ * If the repository-relative path is not available, the @a
+ * *incoming_old_repos_relpath will be set to @c NULL,
+ *
+ * If the peg revision is not available, @a *incoming_old_regrev will be
+ * set to SVN_INVALID_REVNUM.
+ *
+ * If the node kind is not available or if the node does not exist at the
+ * specified path and revision, @a *incoming_old_node_kind will be set to
+ * svn_node_none.
+ * ### Should return svn_node_unkown if not available?
+ *
+ * Any output parameter may be set to @c NULL by the caller to indicate that
+ * a particular piece of information should not be returned.
+ *
+ * In case of tree conflicts, this path@revision does not necessarily exist
+ * in the repository, and it does not necessarily represent the incoming
+ * change which is responsible for the occurance of the tree conflict.
+ * The responsible incoming change is generally located somewhere between
+ * the old and new incoming versions.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_get_incoming_old_repos_location(
+ const char **incoming_old_repos_relpath,
+ svn_revnum_t *incoming_old_regrev,
+ svn_node_kind_t *incoming_old_node_kind,
+ svn_client_conflict_t *conflict,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Like svn_client_conflict_get_incoming_old_repos_location(), expect this
+ * function returns the same data for the incoming new version.
+ *
+ * The same note about tree conflicts applies.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_get_incoming_new_repos_location(
+ const char **incoming_new_repos_relpath,
+ svn_revnum_t *incoming_new_regrev,
+ svn_node_kind_t *incoming_new_node_kind,
+ svn_client_conflict_t *conflict,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Return the node kind of the tree conflict victim described by @a conflict.
+ * The victim is the local node in the working copy which was affected by the
+ * tree conflict at the time the conflict was raised.
+ *
+ * @since New in 1.10.
+ */
+svn_node_kind_t
+svn_client_conflict_tree_get_victim_node_kind(svn_client_conflict_t *conflict);
+
+/**
+ * Resolve a tree @a conflict using resolution option @a option.
+ *
+ * May raise an error in case the tree conflict cannot be resolved yet, for
+ * instance @c SVN_ERR_WC_OBSTRUCTED_UPDATE, @c SVN_ERR_WC_FOUND_CONFLICT,
+ * or @c SVN_ERR_WC_CONFLICT_RESOLVER_FAILUE.
+ * This may happen when other tree conflicts, or unversioned obstructions,
+ * block the resolution of this tree conflict. In such a case the other
+ * conflicts should be resolved first and resolution of this conflict should
+ * be attempted again later.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_tree_resolve(svn_client_conflict_t *conflict,
+ svn_client_conflict_option_t *option,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Like svn_client_conflict_tree_resolve(), except that it identifies
+ * the desired resolution option by ID @a option_id.
+ *
+ * If the provided @a option_id is the ID of an option which resolves
+ * @a conflict, try to resolve the tree conflict using that option.
+ * Else, return @c SVN_ERR_CLIENT_CONFLICT_OPTION_NOT_APPLICABLE.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_tree_resolve_by_id(
+ svn_client_conflict_t *conflict,
+ svn_client_conflict_option_id_t option_id,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Return the ID of the option this tree @a conflict has been resolved to.
+ * If the conflict has not been resolved yet, then return
+ * @c svn_client_conflict_option_unspecified.
+ *
+ * @since New in 1.10.
+ */
+svn_client_conflict_option_id_t
+svn_client_conflict_tree_get_resolution(svn_client_conflict_t *conflict);
+
+/**
+ * Return the path to the legacy property conflicts reject file
+ * for the property conflicts represented by @a conflict.
+ *
+ * This function exists for backwards compatibility only and should not be
+ * used in new code.
+ *
+ * @since New in 1.10.
+ */
+const char *
+svn_client_conflict_prop_get_reject_abspath(svn_client_conflict_t *conflict);
+
+/**
+ * Return the set of property values involved in the conflict of property
+ * PROPNAME described by @a conflict. If a property value is unavailable the
+ * corresponding output argument is set to @c NULL.
+ *
+ * A 3-way diff of these property values can be generated with
+ * svn_diff_mem_string_diff3(). A merged version with conflict
+ * markers can be generated with svn_diff_mem_string_output_merge3().
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_prop_get_propvals(const svn_string_t **base_propval,
+ const svn_string_t **working_propval,
+ const svn_string_t **incoming_old_propval,
+ const svn_string_t **incoming_new_propval,
+ svn_client_conflict_t *conflict,
+ const char *propname,
+ apr_pool_t *result_pool);
+
+/**
+ * Resolve a property @a conflict in property @a propname using resolution
+ * option @a option. To resolve all properties to the same option at once,
+ * set @a propname to the empty string "".
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_prop_resolve(svn_client_conflict_t *conflict,
+ const char *propname,
+ svn_client_conflict_option_t *option,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+/**
+ * If the provided @a option_id is the ID of an option which resolves
+ * @a conflict, resolve the property conflict in property @a propname
+ * using that option.
+ * Else, return @c SVN_ERR_CLIENT_CONFLICT_OPTION_NOT_APPLICABLE.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_prop_resolve_by_id(
+ svn_client_conflict_t *conflict,
+ const char *propname,
+ svn_client_conflict_option_id_t option_id,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Return the ID of the option this property @a conflict in property
+ * @a propname has been resolved to.
+ * If the conflict has not been resolved yet, then return
+ * @c svn_client_conflict_option_unspecified.
+ *
+ * @since New in 1.10.
+ */
+svn_client_conflict_option_id_t
+svn_client_conflict_prop_get_resolution(svn_client_conflict_t *conflict,
+ const char *propname);
+
+/**
+ * Return the MIME-type of the working version of the text-conflicted file
+ * described by @a conflict.
+ *
+ * ### Really needed? What about base/incoming_old/incoming_new values?
+ * @since: New in 1.10.
+ */
+const char *
+svn_client_conflict_text_get_mime_type(svn_client_conflict_t *conflict);
+
+/**
+ * Return absolute paths to the versions of the text-conflicted file
+ * described by @a conflict.
+ *
+ * If a particular content is not available, it is set to @c NULL.
+ *
+ * ### Should this be returning svn_stream_t instead of paths?
+ * @since: New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_text_get_contents(const char **base_abspath,
+ const char **working_abspath,
+ const char **incoming_old_abspath,
+ const char **incoming_new_abspath,
+ svn_client_conflict_t *conflict,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Resolve a text @a conflict using resolution option @a option.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_text_resolve(svn_client_conflict_t *conflict,
+ svn_client_conflict_option_t *option,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
+/**
+ * If the provided @a option_id is the ID of an option which resolves
+ * @a conflict, resolve the text conflict using that option.
+ * Else, return @c SVN_ERR_CLIENT_CONFLICT_OPTION_NOT_APPLICABLE.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_text_resolve_by_id(
+ svn_client_conflict_t *conflict,
+ svn_client_conflict_option_id_t option_id,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Return the ID of the option this text @a conflict has been resolved to.
+ * If the conflict has not been resolved yet, then return
+ * @c svn_client_conflict_option_unspecified.
+ *
+ * @since New in 1.10.
+ */
+svn_client_conflict_option_id_t
+svn_client_conflict_text_get_resolution(svn_client_conflict_t *conflict);
+
+/** @} */
+
+/**
* @defgroup Resolved Mark conflicted paths as resolved.
*
* @{
@@ -4402,7 +5156,11 @@ svn_client_resolved(const char *path,
* - svn_wc_conflict_choose_unspecified
* invoke @a ctx->conflict_func2 with @a ctx->conflict_baton2 to obtain
* a resolution decision for each conflict. This can be used to
- * implement interactive conflict resolution.
+ * implement interactive conflict resolution but is NOT RECOMMENDED for
+ * new code. To perform conflict resolution based on interactive user
+ * input on a per-conflict basis, use svn_client_conflict_text_resolve(),
+ * svn_client_conflict_prop_resolve(), and
+ * svn_client_conflict_tree_resolve() instead of svn_client_resolve().
*
* #svn_wc_conflict_choose_theirs_conflict and
* #svn_wc_conflict_choose_mine_conflict are not legal for binary
@@ -5493,8 +6251,7 @@ svn_client_revprop_list(apr_hash_t **props,
* #svn_opt_revision_unspecified, then it defaults to #svn_opt_revision_head
* for URLs or #svn_opt_revision_working for WC targets.
*
- * @a revision is the revision that should be exported, which is only used
- * when exporting from a repository.
+ * @a revision is the revision that should be exported.
*
* @a peg_revision and @a revision must not be @c NULL.
*
@@ -5706,13 +6463,18 @@ typedef svn_error_t *(*svn_client_list_func_t)(void *baton,
* its children. If @a path_or_url is non-existent, return
* #SVN_ERR_FS_NOT_FOUND.
*
+ * If the @a patterns array of <tt>const char *</tt> is not @c NULL, only
+ * report paths whose last segment matches one of the specified glob
+ * patterns. This does not affect the size of the tree nor the number of
+ * externals being covered.
+ *
* If @a fetch_locks is TRUE, include locks when reporting directory entries.
*
* If @a include_externals is TRUE, also list all external items
* reached by recursion. @a depth value passed to the original list target
* applies for the externals also.
*
- * Use @a pool for temporary allocations.
+ * Use @a scratch_pool for temporary allocations.
*
* Use authentication baton cached in @a ctx to authenticate against the
* repository.
@@ -5728,8 +6490,29 @@ typedef svn_error_t *(*svn_client_list_func_t)(void *baton,
* otherwise simply bitwise OR together the combination of @c SVN_DIRENT_
* fields you care about.
*
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_list4(const char *path_or_url,
+ const svn_opt_revision_t *peg_revision,
+ const svn_opt_revision_t *revision,
+ const apr_array_header_t *patterns,
+ svn_depth_t depth,
+ apr_uint32_t dirent_fields,
+ svn_boolean_t fetch_locks,
+ svn_boolean_t include_externals,
+ svn_client_list_func2_t list_func,
+ void *baton,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
+/** Similar to svn_client_list4(), but with @a patterns set to @c NULL.
+ *
* @since New in 1.8.
+ *
+ * @deprecated Provided for backwards compatibility with the 1.9 API.
*/
+SVN_DEPRECATED
svn_error_t *
svn_client_list3(const char *path_or_url,
const svn_opt_revision_t *peg_revision,
@@ -5747,9 +6530,9 @@ svn_client_list3(const char *path_or_url,
/** Similar to svn_client_list3(), but with @a include_externals set
* to FALSE, and using a #svn_client_list_func_t as callback.
*
- * @deprecated Provided for backwards compatibility with the 1.7 API.
- *
* @since New in 1.5.
+ *
+ * @deprecated Provided for backwards compatibility with the 1.7 API.
*/
SVN_DEPRECATED
svn_error_t *
@@ -5932,6 +6715,153 @@ svn_client_cat(svn_stream_t *out,
+/** Shelving commands
+ *
+ * @defgroup svn_client_shelve_funcs Client Shelving Functions
+ * @{
+ */
+
+/** Shelve a change.
+ *
+ * Shelve as @a name the local modifications found by @a paths, @a depth,
+ * @a changelists. Revert the shelved change from the WC unless @a keep_local
+ * is true.
+ *
+ * If @a dry_run is true, don't actually do it.
+ *
+ * @since New in 1.10.
+ * @warning EXPERIMENTAL.
+ */
+SVN_EXPERIMENTAL
+svn_error_t *
+svn_client_shelve(const char *name,
+ const apr_array_header_t *paths,
+ svn_depth_t depth,
+ const apr_array_header_t *changelists,
+ svn_boolean_t keep_local,
+ svn_boolean_t dry_run,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool);
+
+/** Unshelve the shelved change @a name.
+ *
+ * @a local_abspath is any path in the WC and is used to find the WC root.
+ * Rename the shelved patch to add a '.bak' extension unless @a keep is true.
+ *
+ * If @a dry_run is true, don't actually do it.
+ *
+ * @since New in 1.10.
+ * @warning EXPERIMENTAL.
+ */
+SVN_EXPERIMENTAL
+svn_error_t *
+svn_client_unshelve(const char *name,
+ const char *local_abspath,
+ svn_boolean_t keep,
+ svn_boolean_t dry_run,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool);
+
+/** Delete the shelved patch @a name.
+ *
+ * @a local_abspath is any path in the WC and is used to find the WC root.
+ *
+ * If @a dry_run is true, don't actually do it.
+ *
+ * @since New in 1.10.
+ * @warning EXPERIMENTAL.
+ */
+SVN_EXPERIMENTAL
+svn_error_t *
+svn_client_shelves_delete(const char *name,
+ const char *local_abspath,
+ svn_boolean_t dry_run,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool);
+
+/** Information about a shelved patch.
+ *
+ * @since New in 1.10.
+ * @warning EXPERIMENTAL.
+ */
+typedef struct svn_client_shelved_patch_info_t
+{
+ const char *message; /* first line of log message */
+ const char *patch_path; /* abspath of the patch file */
+ svn_io_dirent2_t *dirent; /* info about the patch file */
+ apr_time_t mtime; /* a copy of dirent->mtime */
+} svn_client_shelved_patch_info_t;
+
+/** Set @a *shelved_patch_infos to a hash, keyed by patch name, of pointers to
+ * @c svn_client_shelved_patch_info_t structures.
+ *
+ * @a local_abspath is any path in the WC and is used to find the WC root.
+ *
+ * @since New in 1.10.
+ * @warning EXPERIMENTAL.
+ */
+SVN_EXPERIMENTAL
+svn_error_t *
+svn_client_shelves_list(apr_hash_t **shelved_patch_infos,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/** Set @a *any_shelved to indicate if there are any shelved changes in this WC.
+ *
+ * This shall provide the answer fast, regardless of how many changes
+ * are stored, unlike svn_client_shelves_list().
+ *
+ * ### Initial implementation isn't O(1) fast -- it just calls
+ * svn_client_shelves_list().
+ *
+ * @a local_abspath is any path in the WC and is used to find the WC root.
+ *
+ * @since New in 1.10.
+ * @warning EXPERIMENTAL.
+ */
+SVN_EXPERIMENTAL
+svn_error_t *
+svn_client_shelves_any(svn_boolean_t *any_shelved,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
+/** Set @a *affected_paths to a hash with one entry for each path affected
+ * by the shelf @a name. The hash key is the old path and value is
+ * the new path, both relative to the WC root. The key and value are the
+ * same except when a path is moved or copied.
+ *
+ * @since New in 1.10.
+ * @warning EXPERIMENTAL.
+ */
+SVN_EXPERIMENTAL
+svn_error_t *
+svn_client_shelf_get_paths(apr_hash_t **affected_paths,
+ const char *name,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/** Set @a *has_changes to indicate whether the shelf @a name
+ * contains any modifications, in other words if svn_client_shelf_get_paths()
+ * would return a non-empty set of paths.
+ *
+ * @since New in 1.10.
+ * @warning EXPERIMENTAL.
+ */
+SVN_EXPERIMENTAL
+svn_error_t *
+svn_client_shelf_has_changes(svn_boolean_t *has_changes,
+ const char *name,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
+/** @} */
+
/** Changelist commands
*
* @defgroup svn_client_changelist_funcs Client Changelist Functions
diff --git a/subversion/include/svn_cmdline.h b/subversion/include/svn_cmdline.h
index 923aeee6235a..2789aa411310 100644
--- a/subversion/include/svn_cmdline.h
+++ b/subversion/include/svn_cmdline.h
@@ -374,6 +374,7 @@ svn_cmdline_create_auth_baton2(svn_auth_baton_t **ab,
* @deprecated Provided for backward compatibility with the 1.8 API.
* @since New in 1.6.
*/
+SVN_DEPRECATED
svn_error_t *
svn_cmdline_create_auth_baton(svn_auth_baton_t **ab,
svn_boolean_t non_interactive,
diff --git a/subversion/include/svn_config.h b/subversion/include/svn_config.h
index 5ad3b0bae234..d194af09efbf 100644
--- a/subversion/include/svn_config.h
+++ b/subversion/include/svn_config.h
@@ -63,6 +63,12 @@ typedef struct svn_config_t svn_config_t;
* @{
*/
+/* If you add a new SVN_CONFIG_* category/section/option macro to this group,
+ * you have to re-run gen-make.py manually.
+ *
+ * ### This should be fixed in the build system; see issue #4581.
+ */
+
/* This list of #defines is intentionally presented as a nested list
that matches the in-config hierarchy. */
diff --git a/subversion/include/svn_dav.h b/subversion/include/svn_dav.h
index e9092d541938..9af9bc58f7ed 100644
--- a/subversion/include/svn_dav.h
+++ b/subversion/include/svn_dav.h
@@ -386,6 +386,41 @@ extern "C" {
#define SVN_DAV_NS_DAV_SVN_REVERSE_FILE_REVS\
SVN_DAV_PROP_NS_DAV "svn/reverse-file-revs"
+/** Presence of this in a DAV header in an OPTIONS response indicates
+ * that the transmitter (in this case, the server) knows how to handle
+ * svndiff1 format encoding.
+ *
+ * @since New in 1.10.
+ */
+#define SVN_DAV_NS_DAV_SVN_SVNDIFF1\
+ SVN_DAV_PROP_NS_DAV "svn/svndiff1"
+
+/** Presence of this in a DAV header in an OPTIONS response indicates
+ * that the transmitter (in this case, the server) knows how to handle
+ * 'list' requests.
+ *
+ * @since New in 1.10.
+ */
+#define SVN_DAV_NS_DAV_SVN_LIST\
+ SVN_DAV_PROP_NS_DAV "svn/list"
+
+/** Presence of this in a DAV header in an OPTIONS response indicates
+ * that the transmitter (in this case, the server) knows how to handle
+ * svndiff2 format encoding.
+ *
+ * @since New in 1.10.
+ */
+#define SVN_DAV_NS_DAV_SVN_SVNDIFF2\
+ SVN_DAV_PROP_NS_DAV "svn/svndiff2"
+
+/** Presence of this in a DAV header in an OPTIONS response indicates
+ * that the transmitter (in this case, the server) sends the result
+ * checksum in the response to a successful PUT request.
+ *
+ * @since New in 1.10.
+ */
+#define SVN_DAV_NS_DAV_SVN_PUT_RESULT_CHECKSUM\
+ SVN_DAV_PROP_NS_DAV "svn/put-result-checksum"
/** @} */
diff --git a/subversion/include/svn_delta.h b/subversion/include/svn_delta.h
index d949ced66b8b..c15788ee4d00 100644
--- a/subversion/include/svn_delta.h
+++ b/subversion/include/svn_delta.h
@@ -330,6 +330,18 @@ typedef svn_error_t *
typedef const unsigned char *
(*svn_txdelta_md5_digest_fn_t)(void *baton);
+/** A typedef for a function that opens an #svn_txdelta_stream_t object,
+ * allocated in @a result_pool. @a baton is provided by the caller.
+ * Any temporary allocations may be performed in @a scratch_pool.
+ *
+ * @since New in 1.10.
+ */
+typedef svn_error_t *
+(*svn_txdelta_stream_open_func_t)(svn_txdelta_stream_t **txdelta_stream,
+ void *baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
/** Create and return a generic text delta stream with @a baton, @a
* next_window and @a md5_digest. Allocate the new stream in @a
* pool.
@@ -508,7 +520,9 @@ svn_txdelta_apply(svn_stream_t *source,
* version is @a svndiff_version. @a compression_level is the zlib
* compression level from 0 (no compression) and 9 (maximum compression).
*
- * @since New in 1.7.
+ * @since New in 1.7. Since 1.10, @a svndiff_version can be 2 for the
+ * svndiff2 format. @a compression_level is currently ignored if
+ * @a svndiff_version is set to 2.
*/
void
svn_txdelta_to_svndiff3(svn_txdelta_window_handler_t *handler,
@@ -544,6 +558,20 @@ svn_txdelta_to_svndiff(svn_stream_t *output,
svn_txdelta_window_handler_t *handler,
void **handler_baton);
+/** Return a readable generic stream which will produce svndiff-encoded
+ * text delta from the delta stream @a txstream. @a svndiff_version and
+ * @a compression_level are same as in svn_txdelta_to_svndiff3().
+ *
+ * Allocate the stream in @a pool.
+ *
+ * @since New in 1.10.
+ */
+svn_stream_t *
+svn_txdelta_to_svndiff_stream(svn_txdelta_stream_t *txstream,
+ int svndiff_version,
+ int compression_level,
+ apr_pool_t *pool);
+
/** Return a writable generic stream which will parse svndiff-format
* data into a text delta, invoking @a handler with @a handler_baton
* whenever a new window is ready.
@@ -666,8 +694,10 @@ svn_txdelta_skip_svndiff_window(apr_file_t *file,
* as it produces the delta.
*
* @note Don't try to allocate one of these yourself. Instead, always
- * use svn_delta_default_editor() or some other constructor, to ensure
- * that unused slots are filled in with no-op functions.
+ * use svn_delta_default_editor() or some other constructor, to avoid
+ * backwards compatibility problems if the structure is extended in
+ * future releases and to ensure that unused slots are filled in with
+ * no-op functions.
*
* <h3>Function Usage</h3>
*
@@ -738,10 +768,11 @@ svn_txdelta_skip_svndiff_window(apr_file_t *file,
*
* The @c add_file and @c open_file callbacks each return a baton
* for the file being created or changed. This baton can then be
- * passed to @c apply_textdelta to change the file's contents, or
- * @c change_file_prop to change the file's properties. When the
- * producer is finished making changes to a file, it should call
- * @c close_file, to let the consumer clean up and free the baton.
+ * passed to @c apply_textdelta or @c apply_textdelta_stream to change
+ * the file's contents, or @c change_file_prop to change the file's
+ * properties. When the producer is finished making changes to a
+ * file, it should call @c close_file, to let the consumer clean up
+ * and free the baton.
*
* The @c add_file and @c add_directory functions each take arguments
* @a copyfrom_path and @a copyfrom_revision. If @a copyfrom_path is
@@ -783,15 +814,16 @@ svn_txdelta_skip_svndiff_window(apr_file_t *file,
* 5. When the producer calls @c open_file or @c add_file, either:
*
* (a) The producer must follow with any changes to the file
- * (@c change_file_prop and/or @c apply_textdelta, as applicable),
- * followed by a @c close_file call, before issuing any other file
- * or directory calls, or
+ * (@c change_file_prop and/or @c apply_textdelta /
+ * @c apply_textdelta_stream, as applicable), followed by
+ * a @c close_file call, before issuing any other file or
+ * directory calls, or
*
* (b) The producer must follow with a @c change_file_prop call if
* it is applicable, before issuing any other file or directory
* calls; later, after all directory batons including the root
- * have been closed, the producer must issue @c apply_textdelta
- * and @c close_file calls.
+ * have been closed, the producer must issue @c apply_textdelta /
+ * @c apply_textdelta_stream and @c close_file calls.
*
* 6. When the producer calls @c apply_textdelta, it must make all of
* the window handler calls (including the @c NULL window at the
@@ -800,7 +832,7 @@ svn_txdelta_skip_svndiff_window(apr_file_t *file,
* So, the producer needs to use directory and file batons as if it
* is doing a single depth-first traversal of the tree, with the
* exception that the producer may keep file batons open in order to
- * make @c apply_textdelta calls at the end.
+ * make @c apply_textdelta / @c apply_textdelta_stream calls at the end.
*
*
* <h3>Pool Usage</h3>
@@ -824,12 +856,13 @@ svn_txdelta_skip_svndiff_window(apr_file_t *file,
* Note that close_directory can be called *before* a file in that
* directory has been closed. That is, the directory's baton is
* closed before the file's baton. The implication is that
- * @c apply_textdelta and @c close_file should not refer to a parent
- * directory baton UNLESS the editor has taken precautions to
- * allocate it in a pool of the appropriate lifetime (the @a dir_pool
- * passed to @c open_directory and @c add_directory definitely does not
- * have the proper lifetime). In general, it is recommended to simply
- * avoid keeping a parent directory baton in a file baton.
+ * @c apply_textdelta / @c apply_textdelta_stream and @c close_file
+ * should not refer to a parent directory baton UNLESS the editor has
+ * taken precautions to allocate it in a pool of the appropriate
+ * lifetime (the @a dir_pool passed to @c open_directory and
+ * @c add_directory definitely does not have the proper lifetime).
+ * In general, it is recommended to simply avoid keeping a parent
+ * directory baton in a file baton.
*
*
* <h3>Errors</h3>
@@ -977,7 +1010,8 @@ typedef struct svn_delta_editor_t
/** We are going to add a new file at @a path, a child of the
* directory represented by @a parent_baton. The callback can
* store a baton for this new file in @a **file_baton; whatever value
- * it stores there should be passed through to @c apply_textdelta.
+ * it stores there should be passed through to @c apply_textdelta or
+ * @c apply_textdelta_stream.
*
* If @a copyfrom_path is non-@c NULL, this add has history (i.e., is a
* copy), and the origin of the copy may be recorded as
@@ -1009,8 +1043,8 @@ typedef struct svn_delta_editor_t
*
* The callback can store a baton for this new file in @a **file_baton;
* whatever value it stores there should be passed through to
- * @c apply_textdelta. If a valid revnum, @a base_revision is the
- * current revision of the file.
+ * @c apply_textdelta or @c apply_textdelta_stream. If a valid revnum,
+ * @a base_revision is the current revision of the file.
*
* Allocations for the returned @a file_baton should be performed in
* @a result_pool. It is also typical to save this pool for later usage
@@ -1075,11 +1109,11 @@ typedef struct svn_delta_editor_t
* more, so whatever resources it refers to may now be freed.
*
* @a text_checksum is the hex MD5 digest for the fulltext that
- * resulted from a delta application, see @c apply_textdelta. The
- * checksum is ignored if NULL. If not null, it is compared to the
- * checksum of the new fulltext, and the error
- * SVN_ERR_CHECKSUM_MISMATCH is returned if they do not match. If
- * there is no new fulltext, @a text_checksum is ignored.
+ * resulted from a delta application, see @c apply_textdelta and
+ * @c apply_textdelta_stream. The checksum is ignored if NULL.
+ * If not null, it is compared to the checksum of the new fulltext,
+ * and the error SVN_ERR_CHECKSUM_MISMATCH is returned if they do
+ * not match. If there is no new fulltext, @a text_checksum is ignored.
*
* Any temporary allocations may be performed in @a scratch_pool.
*/
@@ -1115,6 +1149,38 @@ typedef struct svn_delta_editor_t
svn_error_t *(*abort_edit)(void *edit_baton,
apr_pool_t *scratch_pool);
+ /** Apply a text delta stream, yielding the new revision of a file.
+ * This callback operates on the passed-in @a editor instance.
+ *
+ * @a file_baton indicates the file we're creating or updating, and the
+ * ancestor file on which it is based; it is the baton set by some
+ * prior @c add_file or @c open_file callback.
+ *
+ * @a open_func is a function that opens a #svn_txdelta_stream_t object.
+ * @a open_baton is provided by the caller.
+ *
+ * @a base_checksum is the hex MD5 digest for the base text against
+ * which the delta is being applied; it is ignored if NULL, and may
+ * be ignored even if not NULL. If it is not ignored, it must match
+ * the checksum of the base text against which svndiff data is being
+ * applied; if it does not, @c apply_textdelta_stream call which detects
+ * the mismatch will return the error #SVN_ERR_CHECKSUM_MISMATCH
+ * (if there is no base text, there may still be an error if
+ * @a base_checksum is neither NULL nor the hex MD5 checksum of the
+ * empty string).
+ *
+ * Any temporary allocations may be performed in @a scratch_pool.
+ *
+ * @since New in 1.10.
+ */
+ svn_error_t *(*apply_textdelta_stream)(
+ const struct svn_delta_editor_t *editor,
+ void *file_baton,
+ const char *base_checksum,
+ svn_txdelta_stream_open_func_t open_func,
+ void *open_baton,
+ apr_pool_t *scratch_pool);
+
/* Be sure to update svn_delta_get_cancellation_editor() and
* svn_delta_default_editor() if you add a new callback here. */
} svn_delta_editor_t;
diff --git a/subversion/include/svn_diff.h b/subversion/include/svn_diff.h
index 5b8f8d7fc9f2..bd2c97084bfb 100644
--- a/subversion/include/svn_diff.h
+++ b/subversion/include/svn_diff.h
@@ -1212,7 +1212,43 @@ typedef struct svn_prop_patch_t {
} svn_prop_patch_t;
/**
+ * A binary patch representation. This basically describes replacing one
+ * exact binary representation with another one.
+ *
+ * @since New in 1.10. */
+typedef struct svn_diff_binary_patch_t svn_diff_binary_patch_t;
+
+/**
+ * Creates a stream allocated in @a result_pool from which the original
+ * (pre-patch-application) version of the binary patched file can be read.
+ *
+ * @note Like many svn_diff_get functions over patches, this is implemented
+ * as reading from the backing patch file. Therefore it is recommended to
+ * read the whole stream before using other functions on the same patch file.
+ *
+ * @since New in 1.10 */
+svn_stream_t *
+svn_diff_get_binary_diff_original_stream(const svn_diff_binary_patch_t *bpatch,
+ apr_pool_t *result_pool);
+
+/**
+ * Creates a stream allocated in @a result_pool from which the resulting
+ * (post-patch-application) version of the binary patched file can be read.
+ *
+ * @note Like many svn_diff_get functions over patches, this is implemented
+ * as reading from the backing patch file. Therefore it is recommended to
+ * read the whole stream before using other functions on the same patch file.
+ *
+ * @since New in 1.10 */
+svn_stream_t *
+svn_diff_get_binary_diff_result_stream(const svn_diff_binary_patch_t *bpatch,
+ apr_pool_t *result_pool);
+
+/**
* Data type to manage parsing of patches.
+ *
+ * Represents a patch to one target file.
+ *
* API users should not allocate structures of this type directly.
*
* @since New in 1.7. */
@@ -1239,7 +1275,9 @@ typedef struct svn_patch_t {
svn_diff_operation_kind_t operation;
/**
- * Indicates whether the patch is being interpreted in reverse. */
+ * Indicates whether the patch is being interpreted in reverse.
+ * ### If so, how does this affect the interpretation of other fields?
+ */
svn_boolean_t reverse;
/**
@@ -1249,6 +1287,45 @@ typedef struct svn_patch_t {
* @since New in 1.9. */
svn_mergeinfo_t mergeinfo;
svn_mergeinfo_t reverse_mergeinfo;
+
+ /**
+ * Declares that there is a binary conflict and contains the information
+ * to apply it as parsed from the file.
+ * @since New in 1.10. */
+ svn_diff_binary_patch_t *binary_patch;
+
+ /** The old and new executability bits, as retrieved from the patch file, from
+ * the git-like mode headers.
+ *
+ * A patch may specify an executability change via @a old_executable_bit and
+ * @a new_executable_bit, via a #SVN_PROP_EXECUTABLE propchange hunk, or both
+ * ways. It is upto caller how to decide how conflicting information is
+ * handled.
+ *
+ * #svn_tristate_unknown indicates the patch does not specify the
+ * corresponding bit.
+ *
+ * @since New in 1.10.
+ */
+ svn_tristate_t old_executable_bit;
+ svn_tristate_t new_executable_bit;
+
+ /** The old and new symlink bits, as retrieved from the patch file, from
+ * the git-like mode headers.
+ *
+ * A patch may specify a symlink change via @a old_symlink_bit and
+ * @a new_symlink_bit, via a #SVN_PROP_SPECIAL propchange hunk, or both
+ * ways. It is upto caller how to decide how conflicting information is
+ * handled. Most implementations will currently just describe a replacement
+ * of the file though.
+ *
+ * #svn_tristate_unknown indicates the patch does not specify the
+ * corresponding bit.
+ *
+ * @since New in 1.10.
+ */
+ svn_tristate_t old_symlink_bit;
+ svn_tristate_t new_symlink_bit;
} svn_patch_t;
/** An opaque type representing an open patch file.
diff --git a/subversion/include/svn_error.h b/subversion/include/svn_error.h
index 5681644fd194..290aaa2cbb22 100644
--- a/subversion/include/svn_error.h
+++ b/subversion/include/svn_error.h
@@ -165,18 +165,16 @@ svn_error_wrap_apr(apr_status_t status,
...)
__attribute__((format(printf, 2, 3)));
-/** A quick n' easy way to create a wrapped exception with your own
- * message, before throwing it up the stack. (It uses all of the
- * @a child's fields.)
+/** If @a child is SVN_NO_ERROR, return SVN_NO_ERROR.
+ * Else, prepend a new error to the error chain of @a child. The new error
+ * uses @a new_msg as error message but all other error attributes (such
+ * as the error code) are copied from @a child.
*/
svn_error_t *
svn_error_quick_wrap(svn_error_t *child,
const char *new_msg);
-/** A quick n' easy way to create a wrapped exception with your own
- * printf-style error message produced by passing @a fmt, using
- * apr_psprintf(), before throwing it up the stack. (It uses all of the
- * @a child's fields.)
+/** Like svn_error_quick_wrap(), but with format string support.
*
* @since New in 1.9.
*/
diff --git a/subversion/include/svn_error_codes.h b/subversion/include/svn_error_codes.h
index f8348f48dec9..f95f0a0f0e84 100644
--- a/subversion/include/svn_error_codes.h
+++ b/subversion/include/svn_error_codes.h
@@ -240,6 +240,11 @@ SVN_ERROR_START
SVN_ERR_BAD_CATEGORY_START + 16,
"Invalid compression method")
+ /** @since New in 1.10. */
+ SVN_ERRDEF(SVN_ERR_BAD_PROPERTY_VALUE_EOL,
+ SVN_ERR_BAD_CATEGORY_START + 17,
+ "Unexpected line ending in the property value")
+
/* xml errors */
SVN_ERRDEF(SVN_ERR_XML_ATTRIB_NOT_FOUND,
@@ -868,6 +873,21 @@ SVN_ERROR_START
SVN_ERR_FS_CATEGORY_START + 63,
"Invalid generation number data.")
+ /** @since New in 1.10. */
+ SVN_ERRDEF(SVN_ERR_FS_CORRUPT_REVPROP_MANIFEST,
+ SVN_ERR_FS_CATEGORY_START + 64,
+ "Revprop manifest corrupt.")
+
+ /** @since New in 1.10. */
+ SVN_ERRDEF(SVN_ERR_FS_CORRUPT_PROPLIST,
+ SVN_ERR_FS_CATEGORY_START + 65,
+ "Property list is corrupt.")
+
+ /** @since New in 1.10. */
+ SVN_ERRDEF(SVN_ERR_FS_AMBIGUOUS_CHECKSUM_REP,
+ SVN_ERR_FS_CATEGORY_START + 67,
+ "Content checksums supposedly match but content does not.")
+
/* repos errors */
SVN_ERRDEF(SVN_ERR_REPOS_LOCKED,
@@ -1236,6 +1256,11 @@ SVN_ERROR_START
SVN_ERR_CLIENT_CATEGORY_START + 23,
"The operation is forbidden by the server")
+ /** @since New in 1.10. */
+ SVN_ERRDEF(SVN_ERR_CLIENT_CONFLICT_OPTION_NOT_APPLICABLE,
+ SVN_ERR_CLIENT_CATEGORY_START + 24,
+ "The conflict resolution option is not applicable")
+
/* misc errors */
SVN_ERRDEF(SVN_ERR_BASE,
@@ -1442,6 +1467,21 @@ SVN_ERROR_START
SVN_ERR_MISC_CATEGORY_START + 43,
"Parser error: invalid input")
+ /** @since New in 1.10. */
+ SVN_ERRDEF(SVN_ERR_SQLITE_ROLLBACK_FAILED,
+ SVN_ERR_MISC_CATEGORY_START + 44,
+ "SQLite transaction rollback failed")
+
+ /** @since New in 1.10. */
+ SVN_ERRDEF(SVN_ERR_LZ4_COMPRESSION_FAILED,
+ SVN_ERR_MISC_CATEGORY_START + 45,
+ "LZ4 compression failed")
+
+ /** @since New in 1.10. */
+ SVN_ERRDEF(SVN_ERR_LZ4_DECOMPRESSION_FAILED,
+ SVN_ERR_MISC_CATEGORY_START + 46,
+ "LZ4 decompression failed")
+
/* command-line client errors */
SVN_ERRDEF(SVN_ERR_CL_ARG_PARSING_ERROR,
@@ -1537,6 +1577,16 @@ SVN_ERROR_START
SVN_ERR_RA_SVN_CATEGORY_START + 8,
"Editor drive was aborted")
+ /** @since New in 1.10 */
+ SVN_ERRDEF(SVN_ERR_RA_SVN_REQUEST_SIZE,
+ SVN_ERR_RA_SVN_CATEGORY_START + 9,
+ "Client request too long")
+
+ /** @since New in 1.10 */
+ SVN_ERRDEF(SVN_ERR_RA_SVN_RESPONSE_SIZE,
+ SVN_ERR_RA_SVN_CATEGORY_START + 10,
+ "Server response too long")
+
/* libsvn_auth errors */
/* this error can be used when an auth provider doesn't have
@@ -1594,6 +1644,11 @@ SVN_ERROR_START
SVN_ERR_DIFF_CATEGORY_START + 0,
"Diff data source modified unexpectedly")
+ /** @since New in 1.10 */
+ SVN_ERRDEF(SVN_ERR_DIFF_UNEXPECTED_DATA,
+ SVN_ERR_DIFF_CATEGORY_START + 1,
+ "Diff data unexpected")
+
/* libsvn_ra_serf errors */
/** @since New in 1.5.
@deprecated SSPI now handled by serf rather than libsvn_ra_serf. */
@@ -1617,6 +1672,11 @@ SVN_ERROR_START
SVN_ERR_RA_SERF_CATEGORY_START + 3,
"While handling serf response:")
+ /** @since New in 1.10. */
+ SVN_ERRDEF(SVN_ERR_RA_SERF_STREAM_BUCKET_READ_ERROR,
+ SVN_ERR_RA_SERF_CATEGORY_START + 4,
+ "Can't read from stream")
+
/* malfunctions such as assertion failures */
SVN_ERRDEF(SVN_ERR_ASSERTION_FAIL,
diff --git a/subversion/include/svn_fs.h b/subversion/include/svn_fs.h
index e34146d16dff..179774e16a1e 100644
--- a/subversion/include/svn_fs.h
+++ b/subversion/include/svn_fs.h
@@ -113,7 +113,7 @@ typedef struct svn_fs_t svn_fs_t;
*
* "2" is allowed, too and means "enable if efficient",
* i.e. this will not create warning at runtime if there
- * if no efficient support for revprop caching.
+ * is no efficient support for revprop caching.
*
* @since New in 1.8.
*/
@@ -134,6 +134,12 @@ typedef struct svn_fs_t svn_fs_t;
*/
#define SVN_FS_CONFIG_FSFS_CACHE_NS "fsfs-cache-namespace"
+/** Enable / disable caching of node properties for a FSFS repository.
+ *
+ * @since New in 1.10.
+ */
+#define SVN_FS_CONFIG_FSFS_CACHE_NODEPROPS "fsfs-cache-nodeprops"
+
/** Enable / disable the FSFS format 7 "block read" feature.
*
* @since New in 1.9.
@@ -207,6 +213,20 @@ typedef struct svn_fs_t svn_fs_t;
* @since New in 1.9.
*/
#define SVN_FS_CONFIG_COMPATIBLE_VERSION "compatible-version"
+
+/** Specifies whether the filesystem should be forcing a physical write of
+ * the data to disk. Enabling the option allows the filesystem to return
+ * from the API calls without forcing the write to disk. If this option
+ * is disabled, the changes are always written to disk.
+ *
+ * @note Avoiding the forced write to disk usually is more efficient, but
+ * doesn't guarantee data integrity after a system crash or power failure
+ * and should be used with caution.
+ *
+ * @since New in 1.10.
+ */
+#define SVN_FS_CONFIG_NO_FLUSH_TO_DISK "no-flush-to-disk"
+
/** @} */
@@ -269,11 +289,13 @@ svn_fs_set_warning_func(svn_fs_t *fs,
* @c NULL, the options it contains modify the behavior of the
* filesystem. The interpretation of @a fs_config is specific to the
* filesystem back-end. The new filesystem may be closed by
- * destroying @a pool.
+ * destroying @a result_pool.
+ *
+ * Use @a scratch_pool for temporary allocations.
*
* @note The lifetime of @a fs_config must not be shorter than @a
- * pool's. It's a good idea to allocate @a fs_config from @a pool or
- * one of its ancestors.
+ * result_pool's. It's a good idea to allocate @a fs_config from
+ * @a result_pool or one of its ancestors.
*
* If @a fs_config contains a value for #SVN_FS_CONFIG_FS_TYPE, that
* value determines the filesystem type for the new filesystem.
@@ -289,8 +311,22 @@ svn_fs_set_warning_func(svn_fs_t *fs,
* though the caller should not rely upon any particular default if they
* wish to ensure that a filesystem of a specific type is created.
*
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_fs_create2(svn_fs_t **fs_p,
+ const char *path,
+ apr_hash_t *fs_config,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Like svn_fs_create2(), but without @a scratch_pool.
+ *
+ * @deprecated Provided for backward compatibility with the 1.9 API.
* @since New in 1.1.
*/
+SVN_DEPRECATED
svn_error_t *
svn_fs_create(svn_fs_t **fs_p,
const char *path,
@@ -1060,15 +1096,12 @@ svn_fs_unparse_id(const svn_fs_id_t *id,
* set.
*
* The Subversion filesystem will make a best effort to not reuse
- * transaction names. The Berkeley DB backend generates transaction
- * names using a sequence, or a counter, which is stored in the BDB
+ * transaction names. The BDB and FSFS backends generate transaction
+ * names using a sequence, or a counter, which is stored in the
* database. Each new transaction increments the counter. The
* current value of the counter is not serialized into a filesystem
* dump file, so dumping and restoring the repository will reset the
- * sequence and reuse transaction names. The FSFS backend generates a
- * transaction name using the hostname, process ID and current time in
- * microseconds since 00:00:00 January 1, 1970 UTC. So it is
- * extremely unlikely that a transaction name will be reused.
+ * sequence and so may reuse transaction names.
*
* @defgroup svn_fs_txns Filesystem transactions
* @{
@@ -1425,7 +1458,7 @@ svn_fs_revision_root_revision(svn_fs_root_t *root);
* Unicode canonical decomposition and ordering. No directory entry
* may be named '.', '..', or the empty string. Given a directory
* entry name which fails to meet these requirements, a filesystem
- * function returns an SVN_ERR_FS_PATH_SYNTAX error.
+ * function returns an #SVN_ERR_FS_PATH_SYNTAX error.
*
* A directory path is a sequence of zero or more directory entry
* names, separated by slash characters (U+002f), and possibly ending
@@ -1433,7 +1466,7 @@ svn_fs_revision_root_revision(svn_fs_root_t *root);
* characters are treated as if they were a single slash. If a path
* ends with a slash, it refers to the same node it would without the
* slash, but that node must be a directory, or else the function
- * returns an SVN_ERR_FS_NOT_DIRECTORY error.
+ * may return an #SVN_ERR_FS_NOT_DIRECTORY error.
*
* A path consisting of the empty string, or a string containing only
* slashes, refers to the root directory.
@@ -1474,7 +1507,75 @@ typedef enum svn_fs_path_change_kind_t
* by the commit API; this does not mean the new value is different from
* the old value.
*
- * @since New in 1.6. */
+ * @since New in 1.10. */
+typedef struct svn_fs_path_change3_t
+{
+ /** path of the node that got changed. */
+ svn_string_t path;
+
+ /** kind of change */
+ svn_fs_path_change_kind_t change_kind;
+
+ /** what node kind is the path?
+ (Note: it is legal for this to be #svn_node_unknown.) */
+ svn_node_kind_t node_kind;
+
+ /** was the text touched?
+ * For node_kind=dir: always false. For node_kind=file:
+ * modify: true iff text touched.
+ * add (copy): true iff text touched.
+ * add (plain): always true.
+ * delete: always false.
+ * replace: as for the add/copy part of the replacement.
+ */
+ svn_boolean_t text_mod;
+
+ /** were the properties touched?
+ * modify: true iff props touched.
+ * add (copy): true iff props touched.
+ * add (plain): true iff props touched.
+ * delete: always false.
+ * replace: as for the add/copy part of the replacement.
+ */
+ svn_boolean_t prop_mod;
+
+ /** was the mergeinfo property touched?
+ * modify: } true iff svn:mergeinfo property add/del/mod
+ * add (copy): } and fs format supports this flag.
+ * add (plain): }
+ * delete: always false.
+ * replace: as for the add/copy part of the replacement.
+ * (Note: Pre-1.9 repositories will report #svn_tristate_unknown.)
+ */
+ svn_tristate_t mergeinfo_mod;
+
+ /** Copyfrom revision and path; this is only valid if copyfrom_known
+ * is true. */
+ svn_boolean_t copyfrom_known;
+ svn_revnum_t copyfrom_rev;
+ const char *copyfrom_path;
+
+ /* NOTE! Please update svn_fs_path_change3_create() when adding new
+ fields here. */
+} svn_fs_path_change3_t;
+
+
+/** Similar to #svn_fs_path_change3_t, but with @a node_rev_id and without
+ * path information.
+ *
+ * @note Fields may be added to the end of this structure in future
+ * versions. Therefore, to preserve binary compatibility, users
+ * should not directly allocate structures of this type.
+ *
+ * @note The @c text_mod, @c prop_mod and @c mergeinfo_mod flags mean the
+ * text, properties and mergeinfo property (respectively) were "touched"
+ * by the commit API; this does not mean the new value is different from
+ * the old value.
+ *
+ * @since New in 1.6.
+ *
+ * @deprecated Provided for backwards compatibility with the 1.9 API.
+ */
typedef struct svn_fs_path_change2_t
{
/** node revision id of changed path */
@@ -1563,22 +1664,94 @@ svn_fs_path_change2_create(const svn_fs_id_t *node_rev_id,
svn_fs_path_change_kind_t change_kind,
apr_pool_t *pool);
+/**
+ * Allocate an #svn_fs_path_change3_t structure in @a result_pool,
+ * initialize and return it.
+ *
+ * Set the @c change_kind field to @a change_kind. Set all other fields
+ * to their @c _unknown, @c NULL or invalid value, respectively.
+ *
+ * @since New in 1.10.
+ */
+svn_fs_path_change3_t *
+svn_fs_path_change3_create(svn_fs_path_change_kind_t change_kind,
+ apr_pool_t *result_pool);
+
+/**
+ * Return a deep copy of @a *change, allocated in @a result_pool.
+ *
+ * @since New in 1.10.
+ */
+svn_fs_path_change3_t *
+svn_fs_path_change3_dup(svn_fs_path_change3_t *change,
+ apr_pool_t *result_pool);
+
+/**
+ * Opaque iterator object type for a changed paths list.
+ *
+ * @since New in 1.10.
+ */
+typedef struct svn_fs_path_change_iterator_t svn_fs_path_change_iterator_t;
+
+/**
+ * Set @a *change to the path change that @a iterator currently points to
+ * and advance the @a iterator. If the change list has been exhausted,
+ * @a change will be set to @c NULL.
+ *
+ * You may modify @a **change but its content becomes invalid as soon as
+ * either @a iterator becomes invalid or you call this function again.
+ *
+ * @note The @c node_kind field in @a change may be #svn_node_unknown and
+ * the @c copyfrom_known fields may be FALSE.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_fs_path_change_get(svn_fs_path_change3_t **change,
+ svn_fs_path_change_iterator_t *iterator);
+
+
/** Determine what has changed under a @a root.
*
+ * Set @a *iterator to an iterator object, allocated in @a result_pool,
+ * which will give access to the full list of changed paths under @a root.
+ * Each call to @a svn_fs_path_change_get will return a new unique path
+ * change and has amortized O(1) runtime. The iteration order is undefined
+ * and may change even for the same @a root.
+ *
+ * If @a root becomes invalid, @a *iterator becomes invalid, too.
+ *
+ * Use @a scratch_pool for temporary allocations.
+ *
+ * @note The @a *iterator may be a large object and bind limited system
+ * resources such as file handles. Be sure to clear the owning
+ * pool once you don't need that iterator anymore.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_fs_paths_changed3(svn_fs_path_change_iterator_t **iterator,
+ svn_fs_root_t *root,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/** Same as svn_fs_paths_changed3() but returning all changes in a single,
+ * large data structure and using a single pool for all allocations.
+ *
* Allocate and return a hash @a *changed_paths2_p containing descriptions
* of the paths changed under @a root. The hash is keyed with
* <tt>const char *</tt> paths, and has #svn_fs_path_change2_t * values.
*
- * Callers can assume that this function takes time proportional to
- * the amount of data output, and does not need to do tree crawls;
- * however, it is possible that some of the @c node_kind fields in the
- * #svn_fs_path_change2_t * values will be #svn_node_unknown or
- * that and some of the @c copyfrom_known fields will be FALSE.
- *
* Use @a pool for all allocations, including the hash and its values.
*
+ * @note Retrieving the #node_rev_id element of #svn_fs_path_change2_t may
+ * be expensive in some FS backends.
+ *
* @since New in 1.6.
+ *
+ * @deprecated Provided for backward compatibility with the 1.9 API.
*/
+SVN_DEPRECATED
svn_error_t *
svn_fs_paths_changed2(apr_hash_t **changed_paths2_p,
svn_fs_root_t *root,
@@ -1989,11 +2162,23 @@ svn_fs_closest_copy(svn_fs_root_t **root_p,
const char *path,
apr_pool_t *pool);
+/** Receives parsed @a mergeinfo for the file system path @a path.
+ *
+ * The user-provided @a baton is being passed through by the retrieval
+ * function and @a scratch_pool will be cleared between invocations.
+ *
+ * @since New in 1.10.
+ */
+typedef svn_error_t *
+(*svn_fs_mergeinfo_receiver_t)(const char *path,
+ svn_mergeinfo_t mergeinfo,
+ void *baton,
+ apr_pool_t *scratch_pool);
/** Retrieve mergeinfo for multiple nodes.
*
- * @a *catalog is a catalog for @a paths. It will never be @c NULL,
- * but may be empty.
+ * For each node found with mergeinfo on it, invoke @a receiver with
+ * the provided @a baton.
*
* @a root is revision root to use when looking up paths.
*
@@ -2003,7 +2188,7 @@ svn_fs_closest_copy(svn_fs_root_t **root_p,
* explicit-or-inherited, or only inherited mergeinfo.
*
* If @a adjust_inherited_mergeinfo is @c TRUE, then any inherited
- * mergeinfo returned in @a *catalog is normalized to represent the
+ * mergeinfo reported to @a *receiver is normalized to represent the
* inherited mergeinfo on the path which inherits it. This adjusted
* mergeinfo is keyed by the path which inherits it. If
* @a adjust_inherited_mergeinfo is @c FALSE, then any inherited
@@ -2018,13 +2203,31 @@ svn_fs_closest_copy(svn_fs_root_t **root_p,
* the #SVN_PROP_MERGEINFO property explicitly set on it. (Note
* that inheritance is only taken into account for the elements in @a
* paths; descendants of the elements in @a paths which get their
- * mergeinfo via inheritance are not included in @a *catalog.)
+ * mergeinfo via inheritance are not reported to @a receiver.)
+ *
+ * Do any necessary temporary allocations in @a scratch_pool.
*
- * Allocate @a *catalog in result_pool. Do any necessary temporary
- * allocations in @a scratch_pool.
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_fs_get_mergeinfo3(svn_fs_root_t *root,
+ const apr_array_header_t *paths,
+ svn_mergeinfo_inheritance_t inherit,
+ svn_boolean_t include_descendants,
+ svn_boolean_t adjust_inherited_mergeinfo,
+ svn_fs_mergeinfo_receiver_t receiver,
+ void *baton,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Same as svn_fs_get_mergeinfo3(), but all mergeinfo is being collected
+ * and returned in @a *catalog. It will never be @c NULL, but may be empty.
*
* @since New in 1.8.
+ *
+ * @deprecated Provided for backward compatibility with the 1.9 API.
*/
+SVN_DEPRECATED
svn_error_t *
svn_fs_get_mergeinfo2(svn_mergeinfo_catalog_t *catalog,
svn_fs_root_t *root,
@@ -2545,11 +2748,70 @@ svn_fs_deltify_revision(svn_fs_t *fs,
svn_revnum_t revision,
apr_pool_t *pool);
+/** Make sure that all completed revision property changes to the filesystem
+ * underlying @a fs are actually visible through @a fs. Use @a scratch_pool
+ * for temporary allocations.
+ *
+ * This is an explicit synchronization barrier for revprop changes made
+ * through different #svn_fs_t for the same underlying filesystem. Any
+ * revprop change through @a fs acts as an implicit barrier, i.e. that
+ * object will see all completed revprop changes up to an including its own.
+ * Only #svn_fs_revision_prop2 and #svn_fs_revision_proplist2 have an option
+ * to not synchronize with on-disk data and potentially return outdated data
+ * as old as the last barrier.
+ *
+ * The intended use of this is implementing efficient queries in upper layers
+ * where the result only needs to include all changes up to the start of
+ * that query but does not need to pick up on changes while the query is
+ * running:
+ *
+ * @code
+ SVN_ERR(svn_fs_deltify_revision(fs, pool);
+ for (i = 0; i < n; i++)
+ SVN_ERR(svn_fs_revision_prop2(&authors[i], fs, revs[i], "svn:author",
+ FALSE, pool, pool)); @endcode
+ *
+ * @see svn_fs_revision_prop2, svn_fs_revision_proplist2
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_fs_refresh_revision_props(svn_fs_t *fs,
+ apr_pool_t *scratch_pool);
/** Set @a *value_p to the value of the property named @a propname on
* revision @a rev in the filesystem @a fs. If @a rev has no property by
- * that name, set @a *value_p to zero. Allocate the result in @a pool.
+ * that name, set @a *value_p to zero.
+ *
+ * If @a refresh is set, this call acts as a read barrier and is guaranteed
+ * to return the latest value. Otherwise, it may return data as old as the
+ * last synchronization point but can be much faster to access - in
+ * particular for packed repositories.
+ *
+ * Allocate the result in @a result_pool and use @a scratch_pool for
+ * temporary allocations.
+ *
+ * @see svn_fs_refresh_revision_props
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_fs_revision_prop2(svn_string_t **value_p,
+ svn_fs_t *fs,
+ svn_revnum_t rev,
+ const char *propname,
+ svn_boolean_t refresh,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/** Like #svn_fs_revision_prop2 but using @a pool for @a scratch_pool as
+ * well as @a result_pool and setting @a refresh to #TRUE.
+ *
+ * @see svn_fs_refresh_revision_props
+ *
+ * @deprecated For backward compatibility with 1.9.
*/
+SVN_DEPRECATED
svn_error_t *
svn_fs_revision_prop(svn_string_t **value_p,
svn_fs_t *fs,
@@ -2561,15 +2823,41 @@ svn_fs_revision_prop(svn_string_t **value_p,
/** Set @a *table_p to the entire property list of revision @a rev in
* filesystem @a fs, as an APR hash table allocated in @a pool. The table
* maps <tt>char *</tt> property names to #svn_string_t * values; the names
- * and values are allocated in @a pool.
+ * and values are allocated in @a result_pool. Use @a scratch_pool for
+ * temporary allocations.
+ *
+ * If @a refresh is set, this call acts as a read barrier and is guaranteed
+ * to return the latest value. Otherwise, it may return data as old as the
+ * last synchronization point but can be much faster to access - in
+ * particular for packed repositories.
+ *
+ * @see svn_fs_refresh_revision_props
+ *
+ * @since New in 1.10.
+ *
+ */
+svn_error_t *
+svn_fs_revision_proplist2(apr_hash_t **table_p,
+ svn_fs_t *fs,
+ svn_revnum_t rev,
+ svn_boolean_t refresh,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/** Like svn_fs_revision_proplist2 but using @a pool for @a scratch_pool as
+ * well as @a result_pool and setting @a refresh to #TRUE.
+ *
+ * @see svn_fs_refresh_revision_props
+ *
+ * @deprecated For backward compatibility with 1.9.
*/
+SVN_DEPRECATED
svn_error_t *
svn_fs_revision_proplist(apr_hash_t **table_p,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool);
-
/** Change a revision's property's value, or add/delete a property.
*
* - @a fs is a filesystem, and @a rev is the revision in that filesystem
@@ -2992,7 +3280,13 @@ typedef enum svn_fs_pack_notify_action_t
/** packing of the shard revprops has completed
@since New in 1.7. */
- svn_fs_pack_notify_end_revprop
+ svn_fs_pack_notify_end_revprop,
+
+ /** pack has been a no-op for this repository. The next / future packable
+ shard will be given. If the shard is -1, then the repository does not
+ support packing at all.
+ @since New in 1.10. */
+ svn_fs_pack_notify_noop
} svn_fs_pack_notify_action_t;
diff --git a/subversion/include/svn_hash.h b/subversion/include/svn_hash.h
index 50ed10a4b3f8..8303f077e18e 100644
--- a/subversion/include/svn_hash.h
+++ b/subversion/include/svn_hash.h
@@ -239,19 +239,44 @@ svn_hash_from_cstring_keys(apr_hash_t **hash,
const apr_array_header_t *keys,
apr_pool_t *pool);
+/* For the Subversion developers, this #define makes the svn_hash_gets and
+ * svn_hash_sets macros forward their parameters through functions in order to
+ * gain type checking for the 'key' parameter which the basic apr_hash_* APIs
+ * declare only as 'void *'.
+ */
+#ifdef SVN_DEBUG
+#define SVN_HASH__GETS_SETS
+#endif
+
+#ifdef SVN_HASH__GETS_SETS
+void *
+svn_hash__gets_debug(apr_hash_t *ht, const char *key);
+
+#define svn_hash_gets(ht, key) \
+ svn_hash__gets_debug(ht, key)
+#else
/** Shortcut for apr_hash_get() with a const char * key.
*
* @since New in 1.8.
*/
#define svn_hash_gets(ht, key) \
apr_hash_get(ht, key, APR_HASH_KEY_STRING)
+#endif
+
+#ifdef SVN_HASH__GETS_SETS
+void
+svn_hash__sets_debug(apr_hash_t *ht, const char *key, const void *value);
+#define svn_hash_sets(ht, key, val) \
+ svn_hash__sets_debug(ht, key, val)
+#else
/** Shortcut for apr_hash_set() with a const char * key.
*
* @since New in 1.8.
*/
#define svn_hash_sets(ht, key, val) \
apr_hash_set(ht, key, APR_HASH_KEY_STRING, val)
+#endif
/** @} */
diff --git a/subversion/include/svn_io.h b/subversion/include/svn_io.h
index 42eb422bfab9..802e5caa5c12 100644
--- a/subversion/include/svn_io.h
+++ b/subversion/include/svn_io.h
@@ -918,6 +918,17 @@ typedef svn_error_t *(*svn_stream_seek_fn_t)(void *baton,
typedef svn_error_t *(*svn_stream_data_available_fn_t)(void *baton,
svn_boolean_t *data_available);
+/** Readline handler function for a generic stream. @see svn_stream_t and
+ * svn_stream_readline().
+ *
+ * @since New in 1.10.
+ */
+typedef svn_error_t *(*svn_stream_readline_fn_t)(void *baton,
+ svn_stringbuf_t **stringbuf,
+ const char *eol,
+ svn_boolean_t *eof,
+ apr_pool_t *pool);
+
/** Create a generic stream. @see svn_stream_t. */
svn_stream_t *
svn_stream_create(void *baton,
@@ -992,6 +1003,14 @@ void
svn_stream_set_data_available(svn_stream_t *stream,
svn_stream_data_available_fn_t data_available);
+/** Set @a stream's readline function to @a readline_fn
+ *
+ * @since New in 1.10.
+ */
+void
+svn_stream_set_readline(svn_stream_t *stream,
+ svn_stream_readline_fn_t readline_fn);
+
/** Create a stream that is empty for reading and infinite for writing. */
svn_stream_t *
svn_stream_empty(apr_pool_t *pool);
@@ -1102,11 +1121,29 @@ svn_stream_from_aprfile(apr_file_t *file,
apr_pool_t *pool);
/** Set @a *in to a generic stream connected to stdin, allocated in
- * @a pool. The stream and its underlying APR handle will be closed
- * when @a pool is cleared or destroyed.
+ * @a pool. If @a buffered is set, APR buffering will be enabled.
+ * The stream and its underlying APR handle will be closed when @a pool
+ * is cleared or destroyed.
+ *
+ * @note APR buffering will try to fill the whole internal buffer before
+ * serving read requests. This may be inappropriate for interactive
+ * applications where stdin will not deliver any more data unless
+ * the application processed the data already received.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_stream_for_stdin2(svn_stream_t **in,
+ svn_boolean_t buffered,
+ apr_pool_t *pool);
+
+/** Similar to svn_stream_for_stdin2(), but with buffering being disabled.
*
* @since New in 1.7.
+ *
+ * @deprecated Provided for backward compatibility with the 1.9 API.
*/
+SVN_DEPRECATED
svn_error_t *
svn_stream_for_stdin(svn_stream_t **in,
apr_pool_t *pool);
@@ -1129,16 +1166,27 @@ svn_error_t *
svn_stream_for_stdout(svn_stream_t **out,
apr_pool_t *pool);
-/** Set @a *str to a string buffer allocated in @a result_pool that contains
- * all data from the current position in @a stream to its end. @a len_hint
- * specifies the initial capacity of the string buffer and may be 0. The
- * buffer gets automatically resized to fit the actual amount of data being
- * read from @a stream.
+/** Read the contents of @a stream into memory, from its current position
+ * to its end, returning the data in @a *result. This function does not
+ * close the @a stream upon completion.
+ *
+ * @a len_hint gives a hint about the expected length, in bytes, of the
+ * actual data that will be read from the stream. It may be 0, meaning no
+ * hint is being provided. Efficiency in time and/or in space may be
+ * better (and in general will not be worse) when the actual data length
+ * is equal or approximately equal to the length hint.
+ *
+ * The returned memory is allocated in @a result_pool.
+ *
+ * @note The present implementation is efficient when @a len_hint is big
+ * enough (but not vastly bigger than necessary), and also for actual
+ * lengths up to 64 bytes when @a len_hint is 0. Otherwise it can incur
+ * significant time and space overheads. See source code for details.
*
* @since New in 1.9.
*/
svn_error_t *
-svn_stringbuf_from_stream(svn_stringbuf_t **str,
+svn_stringbuf_from_stream(svn_stringbuf_t **result,
svn_stream_t *stream,
apr_size_t len_hint,
apr_pool_t *result_pool);
@@ -1200,7 +1248,8 @@ svn_stream_compressed(svn_stream_t *stream,
* The @a stream passed into this function is closed when the created
* stream is closed.
*
- * @since New in 1.6.
+ * @since New in 1.6. Since 1.10, the resulting stream supports reset
+ * via stream_stream_reset().
*/
svn_stream_t *
svn_stream_checksummed2(svn_stream_t *stream,
@@ -1225,6 +1274,24 @@ svn_stream_checksummed(svn_stream_t *stream,
svn_boolean_t read_all,
apr_pool_t *pool);
+/** Read the contents of the readable stream @a stream and return its
+ * checksum of type @a kind in @a *checksum.
+ *
+ * The stream will be closed before this function returns (regardless
+ * of the result, or any possible error).
+ *
+ * Use @a scratch_pool for temporary allocations and @a result_pool
+ * to allocate @a *checksum.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_stream_contents_checksum(svn_checksum_t **checksum,
+ svn_stream_t *stream,
+ svn_checksum_kind_t kind,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
/** Read from a generic stream until @a buffer is filled upto @a *len or
* until EOF is reached. @see svn_stream_t
*
@@ -1318,6 +1385,14 @@ svn_stream_reset(svn_stream_t *stream);
svn_boolean_t
svn_stream_supports_mark(svn_stream_t *stream);
+/** Returns @c TRUE if the generic @a stream supports svn_stream_reset().
+ *
+ * @see svn_stream_reset()
+ * @since New in 1.10.
+ */
+svn_boolean_t
+svn_stream_supports_reset(svn_stream_t *stream);
+
/** Set a @a mark at the current position of a generic @a stream,
* which can later be sought back to using svn_stream_seek().
* The @a mark is allocated in @a pool.
@@ -1502,25 +1577,44 @@ svn_stream_contents_same(svn_boolean_t *same,
apr_pool_t *pool);
-/** Read the contents of @a stream into memory, returning the data in
- * @a result. The stream will be closed when it has been successfully and
- * completely read.
+/** Read the contents of @a stream into memory, from its current position
+ * to its end, returning the data in @a *result. The stream will be closed
+ * when it has been successfully and completely read.
+ *
+ * @a len_hint gives a hint about the expected length, in bytes, of the
+ * actual data that will be read from the stream. It may be 0, meaning no
+ * hint is being provided. Efficiency in time and/or in space may be
+ * better (and in general will not be worse) when the actual data length
+ * is equal or approximately equal to the length hint.
*
* The returned memory is allocated in @a result_pool, and any temporary
- * allocations are performed in @a scratch_pool.
+ * allocations may be performed in @a scratch_pool.
*
- * @note due to memory pseudo-reallocation behavior (due to pools), this
- * can be a memory-intensive operation for large files.
+ * @note The present implementation is efficient when @a len_hint is big
+ * enough (but not vastly bigger than necessary), and also for actual
+ * lengths up to 64 bytes when @a len_hint is 0. Otherwise it can incur
+ * significant time and space overheads. See source code for details.
*
- * @since New in 1.6
+ * @since New in 1.10
+ */
+svn_error_t *
+svn_string_from_stream2(svn_string_t **result,
+ svn_stream_t *stream,
+ apr_size_t len_hint,
+ apr_pool_t *result_pool);
+
+/** Similar to svn_string_from_stream2(), but always passes 0 for
+ * @a len_hint.
+ *
+ * @deprecated Provided for backwards compatibility with the 1.9 API.
*/
+SVN_DEPRECATED
svn_error_t *
svn_string_from_stream(svn_string_t **result,
svn_stream_t *stream,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
-
/** A function type provided for use as a callback from
* @c svn_stream_lazyopen_create().
*
@@ -2175,6 +2269,28 @@ svn_io_file_info_get(apr_finfo_t *finfo,
apr_pool_t *pool);
+/** Set @a *filesize_p to the size of @a file. Use @a pool for temporary
+ * allocations.
+ *
+ * @note Use svn_io_file_info_get() to get more information about
+ * apr_file_t.
+ *
+ * @since New in 1.10
+ */
+svn_error_t *
+svn_io_file_size_get(svn_filesize_t *filesize_p, apr_file_t *file,
+ apr_pool_t *pool);
+
+/** Fetch the current offset of @a file into @a *offset_p. Use @a pool for
+ * temporary allocations.
+ *
+ * @since New in 1.10
+ */
+svn_error_t *
+svn_io_file_get_offset(apr_off_t *offset_p,
+ apr_file_t *file,
+ apr_pool_t *pool);
+
/** Wrapper for apr_file_read(). */
svn_error_t *
svn_io_file_read(apr_file_t *file,
@@ -2276,13 +2392,31 @@ svn_io_file_write_full(apr_file_t *file,
* If @a copy_perms_path is not NULL, copy the permissions applied on @a
* @a copy_perms_path on the temporary file before renaming.
*
- * @note This function uses advanced file control operations to flush buffers
- * to disk that aren't always accessible and can be very expensive. Avoid
- * using this function in cases where the file should just work on any
- * network filesystem.
+ * If @a flush_to_disk is non-zero, do not return until the node has
+ * actually been written on the disk.
+ *
+ * @note The flush to disk operation can be very expensive on systems
+ * that implement flushing on all IO layers, like Windows. Please use
+ * @a flush_to_disk flag only for critical data.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_io_write_atomic2(const char *final_path,
+ const void *buf,
+ apr_size_t nbytes,
+ const char *copy_perms_path,
+ svn_boolean_t flush_to_disk,
+ apr_pool_t *scratch_pool);
+
+/** Similar to svn_io_write_atomic2(), but with @a flush_to_disk set
+ * to @c TRUE.
*
* @since New in 1.9.
+ *
+ * @deprecated Provided for backward compatibility with the 1.9 API
*/
+SVN_DEPRECATED
svn_error_t *
svn_io_write_atomic(const char *final_path,
const void *buf,
@@ -2332,9 +2466,26 @@ svn_io_stat(apr_finfo_t *finfo,
* @a from_path to a new path @a to_path within the same filesystem.
* In some cases, an existing node at @a to_path will be overwritten.
*
- * A wrapper for apr_file_rename(). @a from_path and @a to_path are
- * utf8-encoded.
+ * @a from_path and @a to_path are utf8-encoded. If @a flush_to_disk
+ * is non-zero, do not return until the node has actually been moved on
+ * the disk.
+ *
+ * @note The flush to disk operation can be very expensive on systems
+ * that implement flushing on all IO layers, like Windows. Please use
+ * @a flush_to_disk flag only for critical data.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_io_file_rename2(const char *from_path, const char *to_path,
+ svn_boolean_t flush_to_disk, apr_pool_t *pool);
+
+/** Similar to svn_io_file_rename2(), but with @a flush_to_disk set
+ * to @c FALSE.
+ *
+ * @deprecated Provided for backward compatibility with the 1.9 API
*/
+SVN_DEPRECATED
svn_error_t *
svn_io_file_rename(const char *from_path,
const char *to_path,
diff --git a/subversion/include/svn_props.h b/subversion/include/svn_props.h
index e920b03dee6d..7ea8bba0c048 100644
--- a/subversion/include/svn_props.h
+++ b/subversion/include/svn_props.h
@@ -475,22 +475,42 @@ svn_prop_name_is_valid(const char *prop_name);
/** The files' last modification time.
* This is stored as string in the form @c "2008-08-07T07:38:51.008782Z", to
* be converted by the functions @c svn_time_to_cstring() and
- * @c svn_time_from_cstring(). */
+ * @c svn_time_from_cstring().
+ *
+ * @note This property name is reserved for future usage, but currently unused.
+ *
+ * @since New in 1.6.
+ */
#define SVN_PROP_TEXT_TIME SVN_PROP_PREFIX "text-time"
/** The files' owner.
* Stored as numeric ID, optionally followed by whitespace and the string:
* @c "1000 pmarek". Parsers @b should accept any number of whitespace,
- * and writers @b should put exactly a single space. */
+ * and writers @b should put exactly a single space.
+ *
+ * @note This property name is reserved for future usage, but currently unused.
+ *
+ * @since New in 1.6.
+ */
#define SVN_PROP_OWNER SVN_PROP_PREFIX "owner"
/** The files' group.
- * The same format as for @c SVN_PROP_OWNER, the owner-property. */
+ * The same format as for @c SVN_PROP_OWNER, the owner-property.
+ *
+ * @note This property name is reserved for future usage, but currently unused.
+ *
+ * @since New in 1.6.
+ */
#define SVN_PROP_GROUP SVN_PROP_PREFIX "group"
/** The files' unix-mode.
* Stored in octal, with a leading @c 0; may have 5 digits if any of @c setuid,
- * @c setgid or @c sticky are set; an example is @c "0644". */
+ * @c setgid or @c sticky are set; an example is @c "0644".
+ *
+ * @note This property name is reserved for future usage, but currently unused.
+ *
+ * @since New in 1.6.
+ */
#define SVN_PROP_UNIX_MODE SVN_PROP_PREFIX "unix-mode"
/** @} */ /* Meta-data properties */
diff --git a/subversion/include/svn_ra.h b/subversion/include/svn_ra.h
index a3ab672dda92..030458d0e16a 100644
--- a/subversion/include/svn_ra.h
+++ b/subversion/include/svn_ra.h
@@ -1089,7 +1089,7 @@ svn_ra_get_file(svn_ra_session_t *session,
* @a path is interpreted relative to the URL in @a session.
*
* If @a revision is @c SVN_INVALID_REVNUM (meaning 'head') and
- * @a *fetched_rev is not @c NULL, then this function will set
+ * @a fetched_rev is not @c NULL, then this function will set
* @a *fetched_rev to the actual revision that was retrieved. (Some
* callers want to know, and some don't.)
*
@@ -1130,6 +1130,63 @@ svn_ra_get_dir(svn_ra_session_t *session,
apr_pool_t *pool);
/**
+ * Callback type to be used with svn_ra_list(). It will be invoked for
+ * every directory entry found.
+ *
+ * The full path of the entry is given in @a rel_path and @a dirent contains
+ * various additional information. Only the elements of @a dirent specified
+ * by the @a dirent_fields argument to svn_ra_list() will be valid.
+ *
+ * @a baton is the user-provided receiver baton. @a scratch_pool may be
+ * used for temporary allocations.
+ *
+ * @since New in 1.10.
+ */
+typedef svn_error_t *(* svn_ra_dirent_receiver_t)(const char *rel_path,
+ svn_dirent_t *dirent,
+ void *baton,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Efficiently list everything within a sub-tree. Specify a glob pattern
+ * to search for specific files and folders.
+ *
+ * In @a session, walk the sub-tree starting at @a path at @a revision down
+ * to the given @a depth. For each directory entry found, @a receiver will
+ * be called with @a receiver_baton. The starting @a path will be reported
+ * as well. Because retrieving elements of a #svn_dirent_t can be
+ * expensive, you need to select them individually via flags set in
+ * @a dirent_fields.
+ *
+ * @a patterns is an optional array of <tt>const char *</tt>. If it is
+ * not @c NULL, only those directory entries will be reported whose last
+ * path segment matches at least one of these patterns. This feature uses
+ * apr_fnmatch() for glob matching and requiring '.' to matched by dots
+ * in the path.
+ *
+ * @a path must point to a directory and @a depth must be at least
+ * #svn_depth_empty.
+ *
+ * If the server doesn't support the 'list' command, return
+ * #SVN_ERR_UNSUPPORTED_FEATURE in preference to any other error that
+ * might otherwise be returned.
+ *
+ * Use @a scratch_pool for temporary memory allocation.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_ra_list(svn_ra_session_t *session,
+ const char *path,
+ svn_revnum_t revision,
+ const apr_array_header_t *patterns,
+ svn_depth_t depth,
+ apr_uint32_t dirent_fields,
+ svn_ra_dirent_receiver_t receiver,
+ void *receiver_baton,
+ apr_pool_t *scratch_pool);
+
+/**
* Set @a *catalog to a mergeinfo catalog for the paths in @a paths.
* If no mergeinfo is available, set @a *catalog to @c NULL. The
* requested mergeinfo hashes are for @a paths (which are relative to
@@ -1550,7 +1607,7 @@ svn_ra_do_diff(svn_ra_session_t *session,
* revisions in which at least one of @a paths was changed (i.e., if
* file, text or props changed; if dir, props changed or an entry
* was added or deleted). Each path is an <tt>const char *</tt>, relative
- * to the @a session's common parent.
+ * to the repository root of @a session.
*
* If @a limit is greater than zero only invoke @a receiver on the first
* @a limit logs.
@@ -1717,9 +1774,10 @@ svn_ra_get_repos_root(svn_ra_session_t *session,
/**
* Set @a *locations to the locations (at the repository revisions
* @a location_revisions) of the file identified by @a path in
- * @a peg_revision. @a path is relative to the URL to which
- * @a session was opened. @a location_revisions is an array of
- * @c svn_revnum_t's. @a *locations will be a mapping from the revisions to
+ * @a peg_revision (passing @c SVN_INVALID_REVNUM is an error).
+ * @a path is relative to the URL to which @a session was opened.
+ * @a location_revisions is an array of @c svn_revnum_t's.
+ * @a *locations will be a mapping from the revisions to
* their appropriate absolute paths. If the file doesn't exist in a
* location_revision, that revision will be ignored.
*
@@ -1966,7 +2024,7 @@ svn_ra_get_locks(svn_ra_session_t *session,
/**
* Replay the changes from a range of revisions between @a start_revision
- * and @a end_revision.
+ * and @a end_revision (inclusive).
*
* When receiving information for one revision, a callback @a revstart_func is
* called; this callback will provide an editor and baton through which the
@@ -2174,6 +2232,13 @@ svn_ra_has_capability(svn_ra_session_t *session,
*/
#define SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE "get-file-revs-reversed"
+/**
+ * The capability of a server to understand the list command.
+ *
+ * @since New in 1.10.
+ */
+#define SVN_RA_CAPABILITY_LIST "list"
+
/* *** PLEASE READ THIS IF YOU ADD A NEW CAPABILITY ***
*
diff --git a/subversion/include/svn_ra_svn.h b/subversion/include/svn_ra_svn.h
index c968e400805f..6293255daba9 100644
--- a/subversion/include/svn_ra_svn.h
+++ b/subversion/include/svn_ra_svn.h
@@ -49,6 +49,7 @@ extern "C" {
/** Currently-defined capabilities. */
#define SVN_RA_SVN_CAP_EDIT_PIPELINE "edit-pipeline"
#define SVN_RA_SVN_CAP_SVNDIFF1 "svndiff1"
+#define SVN_RA_SVN_CAP_SVNDIFF2_ACCEPTED "accepts-svndiff2"
#define SVN_RA_SVN_CAP_ABSENT_ENTRIES "absent-entries"
/* maps to SVN_RA_CAPABILITY_COMMIT_REVPROPS: */
#define SVN_RA_SVN_CAP_COMMIT_REVPROPS "commit-revprops"
@@ -68,6 +69,8 @@ extern "C" {
#define SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS "ephemeral-txnprops"
/* maps to SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE */
#define SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE "file-revs-reverse"
+/* maps to SVN_RA_CAPABILITY_LIST */
+#define SVN_RA_SVN_CAP_LIST "list"
/** ra_svn passes @c svn_dirent_t fields over the wire as a list of
@@ -141,16 +144,23 @@ typedef struct svn_ra_svn_cmd_entry_t
svn_boolean_t terminate;
} svn_ra_svn_cmd_entry_t;
+/** Data types defined by the svn:// protocol.
+ *
+ * @since The typedef name is new in 1.10; the enumerators are not. */
+typedef enum
+{
+ SVN_RA_SVN_NUMBER,
+ SVN_RA_SVN_STRING,
+ SVN_RA_SVN_WORD,
+ SVN_RA_SVN_LIST
+} svn_ra_svn_item_kind_t;
+
/** Memory representation of an on-the-wire data item. */
typedef struct svn_ra_svn_item_t
{
/** Variant indicator. */
- enum {
- SVN_RA_SVN_NUMBER,
- SVN_RA_SVN_STRING,
- SVN_RA_SVN_WORD,
- SVN_RA_SVN_LIST
- } kind;
+ svn_ra_svn_item_kind_t kind;
+
/** Variant data. */
union {
apr_uint64_t number;
@@ -169,7 +179,9 @@ typedef svn_error_t *(*svn_ra_svn_edit_callback)(void *baton);
*
* Either @a sock or @a in_stream/@a out_stream must be set, not both.
* @a compression_level specifies the desired network data compression
- * level (zlib) from 0 (no compression) to 9 (best but slowest).
+ * level from 0 (no compression) to 9 (best but slowest). The effect
+ * of the parameter depends on the compression algorithm; for example,
+ * it is used verbatim by zlib/deflate but ignored by LZ4.
*
* If @a zero_copy_limit is not 0, cached file contents smaller than the
* given limit may be sent directly to the network socket. Otherwise,
@@ -184,13 +196,38 @@ typedef svn_error_t *(*svn_ra_svn_edit_callback)(void *baton);
* It defines the number of bytes that must have been sent since the last
* check before the next check will be made.
*
+ * If @a max_in is not 0, error out and close the connection whenever more
+ * than @a max_in bytes are received for a command (e.g. a client request).
+ * If @a max_out is not 0, error out and close the connection whenever more
+ * than @a max_out bytes have been send as response to some command.
+ *
+ * @note The limits enforced may vary slightly by +/- the I/O buffer size.
+ *
* @note If @a out_stream is an wrapped apr_file_t* the backing file will be
* used for some operations.
*
* Allocate the result in @a pool.
*
+ * @since New in 1.10
+ */
+svn_ra_svn_conn_t *svn_ra_svn_create_conn5(apr_socket_t *sock,
+ svn_stream_t *in_stream,
+ svn_stream_t *out_stream,
+ int compression_level,
+ apr_size_t zero_copy_limit,
+ apr_size_t error_check_interval,
+ apr_uint64_t max_in,
+ apr_uint64_t max_out,
+ apr_pool_t *result_pool);
+
+
+/** Similar to svn_ra_svn_create_conn5() but with @a max_in and @a max_out
+ * set to 0.
+ *
* @since New in 1.9
+ * @deprecated Provided for backward compatibility with the 1.9 API.
*/
+SVN_DEPRECATED
svn_ra_svn_conn_t *svn_ra_svn_create_conn4(apr_socket_t *sock,
svn_stream_t *in_stream,
svn_stream_t *out_stream,
diff --git a/subversion/include/svn_repos.h b/subversion/include/svn_repos.h
index 45b7a8fe579b..9bb462abbb3c 100644
--- a/subversion/include/svn_repos.h
+++ b/subversion/include/svn_repos.h
@@ -244,7 +244,13 @@ typedef enum svn_repos_notify_action_t
svn_repos_notify_format_bumped,
/** A revision range was copied. @since New in 1.9. */
- svn_repos_notify_hotcopy_rev_range
+ svn_repos_notify_hotcopy_rev_range,
+
+ /** The repository pack did not do anything. @since New in 1.10. */
+ svn_repos_notify_pack_noop,
+
+ /** The revision properties got set. @since New in 1.10. */
+ svn_repos_notify_load_revprop_set
} svn_repos_notify_action_t;
/** The type of warning occurring.
@@ -369,6 +375,24 @@ typedef void (*svn_repos_notify_func_t)(void *baton,
const svn_repos_notify_t *notify,
apr_pool_t *scratch_pool);
+/** Callback for filtering repository contents during dump.
+ *
+ * Set @a *include to TRUE to indicate that node, identified by path
+ * @a path in @a root should be included in dump, or set it to @c FALSE
+ * to indicate that node should be excluded (presumably according to state
+ * stored in @a baton).
+ *
+ * Do not assume @a scratch_pool has any lifetime beyond this call.
+ *
+ * @since New in 1.10.
+ */
+typedef svn_error_t * (*svn_repos_dump_filter_func_t)(
+ svn_boolean_t *include,
+ svn_fs_root_t *root,
+ const char *path,
+ void *baton,
+ apr_pool_t *scratch_pool);
+
/**
* Allocate an #svn_repos_notify_t structure in @a result_pool, initialize
* and return it.
@@ -1701,6 +1725,68 @@ svn_repos_stat(svn_dirent_t **dirent,
const char *path,
apr_pool_t *pool);
+/**
+ * Callback type to be used with svn_repos_list(). It will be invoked for
+ * every directory entry found.
+ *
+ * The full path of the entry is given in @a path and @a dirent contains
+ * various additional information. If svn_repos_list() has been called
+ * with @a path_info_only set, only the @a kind element of this struct
+ * will be valid.
+ *
+ * @a baton is the user-provided receiver baton. @a scratch_pool may be
+ * used for temporary allocations.
+ *
+ * @since New in 1.10.
+ */
+typedef svn_error_t *(* svn_repos_dirent_receiver_t)(const char *path,
+ svn_dirent_t *dirent,
+ void *baton,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Efficiently list everything within a sub-tree. Specify glob patterns
+ * to search for specific files and folders.
+ *
+ * Walk the sub-tree starting at @a path under @a root up to the given
+ * @a depth. For each directory entry found, @a receiver will be called
+ * with @a receiver_baton. The starting @a path will be reported as well.
+ * Because retrieving all elements of a #svn_dirent_t can be expensive,
+ * you may set @a path_info_only to receive only the path name and the node
+ * kind. The entries will be reported ordered by their path.
+ *
+ * @a patterns is an optional array of <tt>const char *</tt>. If it is
+ * not @c NULL, only those directory entries will be reported whose last
+ * path segment matches at least one of these patterns. This feature uses
+ * apr_fnmatch() for glob matching and requiring '.' to matched by dots
+ * in the path.
+ *
+ * If @a authz_read_func is not @c NULL, this function will neither report
+ * entries nor recurse into directories that the user has no access to.
+ *
+ * Cancellation support is provided in the usual way through the optional
+ * @a cancel_func and @a cancel_baton.
+ *
+ * @a path must point to a directory and @a depth must be at least
+ * #svn_depth_empty.
+ *
+ * Use @a scratch_pool for temporary memory allocation.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_repos_list(svn_fs_root_t *root,
+ const char *path,
+ const apr_array_header_t *patterns,
+ svn_depth_t depth,
+ svn_boolean_t path_info_only,
+ svn_repos_authz_func_t authz_read_func,
+ void *authz_read_baton,
+ svn_repos_dirent_receiver_t receiver,
+ void *receiver_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool);
/**
* Given @a path which exists at revision @a start in @a fs, set
@@ -1864,13 +1950,181 @@ svn_repos_node_location_segments(svn_repos_t *repos,
/* Retrieving log messages. */
+/** Path change descriptor.
+ *
+ * @note Identical to #svn_fs_path_change3_t but with all information
+ * known, i.e. @a node_kind is never #svn_node_unknown and
+ * @a copyfrom_known is always @c TRUE.
+ *
+ * @note To allow for extending this structure in future releases,
+ * always use svn_repos_path_change_create() to allocate the stucture.
+ *
+ * @see svn_fs_path_change3_t
+ *
+ * @since New in 1.10.
+ */
+typedef svn_fs_path_change3_t svn_repos_path_change_t;
+
+/**
+ * Return an #svn_repos_path_change_t structure, allocated in @a result_pool,
+ * with all fields initialized to their respective null/none/empty/invalid
+ * values.
+ *
+ * @note To allow for extending the #svn_repos_path_change_t structure in
+ * future releases, this function should always be used to allocate it.
+ *
+ * @since New in 1.10.
+ */
+svn_repos_path_change_t *
+svn_repos_path_change_create(apr_pool_t *result_pool);
+
+/**
+ * Return a deep copy of @a change, allocated in @a result_pool.
+ *
+ * @since New in 1.10.
+ */
+svn_repos_path_change_t *
+svn_repos_path_change_dup(svn_repos_path_change_t *change,
+ apr_pool_t *result_pool);
+
+/** The callback invoked by log message loopers, such as
+ * svn_repos_get_logs5().
+ *
+ * This function is invoked once on each changed path, in a potentially
+ * random order that may even change between invocations for the same
+ * revisions.
+ *
+ * @a baton is what you think it is, and @a change contains relevant
+ * information for the changed path. Please note that @a change may be
+ * modified within this callback but it will become invalid as soon as
+ * the callback returns.
+ *
+ * Use @a scratch_pool for temporary allocation. The caller may clear it
+ * between or after invocations.
+ *
+ * @since New in 1.10.
+ */
+typedef svn_error_t *(*svn_repos_path_change_receiver_t)(
+ void *baton,
+ svn_repos_path_change_t *change,
+ apr_pool_t *scratch_pool);
+
/**
- * Invoke @a receiver with @a receiver_baton on each log message from
- * @a start to @a end in @a repos's filesystem. @a start may be greater
- * or less than @a end; this just controls whether the log messages are
+ * A structure to represent all the information about a particular log entry.
+ *
+ * @note To allow for extending this structure in future releases,
+ * always use svn_repos_log_entry_create() to allocate the stucture.
+ *
+ * @since New in 1.10.
+ */
+typedef struct svn_repos_log_entry_t
+{
+ /** The revision of the commit. */
+ svn_revnum_t revision;
+
+ /** The hash of requested revision properties, which may be NULL if it
+ * would contain no revprops. Maps (const char *) property name to
+ * (svn_string_t *) property value. */
+ apr_hash_t *revprops;
+
+ /**
+ * Whether or not this message has children.
+ *
+ * When a log operation requests additional merge information, extra log
+ * entries may be returned as a result of this entry. The new entries, are
+ * considered children of the original entry, and will follow it. When
+ * the HAS_CHILDREN flag is set, the receiver should increment its stack
+ * depth, and wait until an entry is provided with SVN_INVALID_REVNUM which
+ * indicates the end of the children.
+ *
+ * For log operations which do not request additional merge information, the
+ * HAS_CHILDREN flag is always FALSE.
+ *
+ * For more information see:
+ * https://svn.apache.org/repos/asf/subversion/trunk/notes/merge-tracking/design.html#commutative-reporting
+ */
+ svn_boolean_t has_children;
+
+ /**
+ * Whether @a revision should be interpreted as non-inheritable in the
+ * same sense of #svn_merge_range_t.
+ *
+ * Currently always FALSE.
+ */
+ svn_boolean_t non_inheritable;
+
+ /**
+ * Whether @a revision is a merged revision resulting from a reverse merge.
+ */
+ svn_boolean_t subtractive_merge;
+
+ /* NOTE: Add new fields at the end to preserve binary compatibility. */
+} svn_repos_log_entry_t;
+
+/**
+ * Return an #svn_repos_log_entry_t, allocated in @a result_pool,
+ * with all fields initialized to their respective null/none/empty/invalid
+ * values.
+ *
+ * @note To allow for extending the #svn_repos_log_entry_t structure in
+ * future releases, this function should always be used to allocate it.
+ *
+ * @since New in 1.10.
+ */
+svn_repos_log_entry_t *
+svn_repos_log_entry_create(apr_pool_t *result_pool);
+
+/** Return a deep copy of @a log_entry, allocated in @a result_pool.
+ *
+ * @since New in 1.10.
+ */
+svn_repos_log_entry_t *
+svn_repos_log_entry_dup(const svn_repos_log_entry_t *log_entry,
+ apr_pool_t *result_pool);
+
+
+/** The callback invoked by log message loopers, such as
+ * svn_repos_get_logs5().
+ *
+ * This function is invoked once on each log message, in the order
+ * determined by the caller (see above-mentioned functions).
+ *
+ * @a baton is what you think it is, and @a log_entry contains relevant
+ * information for the log message.
+ *
+ * If @a log_entry->has_children is @c TRUE, the message will be followed
+ * immediately by any number of merged revisions (child messages), which are
+ * terminated by an invocation with SVN_INVALID_REVNUM. This usage may
+ * be recursive.
+ *
+ * Use @a scratch_pool for temporary allocation. The caller may clear it
+ * between or after invocations.
+ *
+ * @since New in 1.10.
+ */
+typedef svn_error_t *(*svn_repos_log_entry_receiver_t)(
+ void *baton,
+ svn_repos_log_entry_t *log_entry,
+ apr_pool_t *scratch_pool);
+
+
+/**
+ * Invoke @a revision_receiver with @a revision_receiver_baton on each
+ * revision from @a start to @a end in @a repos's filesystem. @a start may
+ * be greater or less than @a end; this just controls whether the log is
* processed in descending or ascending revision number order.
*
+ * If not @c NULL, @a path_change_receiver will be invoked with
+ * @a path_change_receiver_baton for each changed path in the respective
+ * revision. These changes will be reported before the @a revision_receiver
+ * is invoked for that revision. So, for each revision in the log, there
+ * is a number of calls to @a path_change_receiver followed by a single
+ * invocation of @a revision_receiver, implicitly marking the end of the
+ * changes list for that revision. If a revision does not contain any
+ * changes (or if none are visible due to @a authz_read_func),
+ * @a path_change_receiver will not be called for that revision.
+ *
* If @a start or @a end is #SVN_INVALID_REVNUM, it defaults to youngest.
*
* If @a paths is non-NULL and has one or more elements, then only show
@@ -1881,13 +2135,8 @@ svn_repos_node_location_segments(svn_repos_t *repos,
* show all revisions regardless of what paths were changed in those
* revisions.
*
- * If @a limit is greater than zero then only invoke @a receiver on the first
- * @a limit logs.
- *
- * If @a discover_changed_paths, then each call to @a receiver passes a
- * hash mapping paths committed in that revision to information about them
- * as the receiver's @a changed_paths argument.
- * Otherwise, each call to @a receiver passes NULL for @a changed_paths.
+ * If @a limit is greater than zero then only invoke @a revision_receiver
+ * on the first @a limit logs.
*
* If @a strict_node_history is set, copy history (if any exists) will
* not be traversed while harvesting revision logs for each path.
@@ -1902,28 +2151,64 @@ svn_repos_node_location_segments(svn_repos_t *repos,
* only the revision properties named by the (const char *) array elements
* (i.e. retrieve none if the array is empty).
*
- * If any invocation of @a receiver returns error, return that error
- * immediately and without wrapping it.
+ * If any invocation of @a revision_receiver or @a path_change_receiver
+ * returnn an error, return that error immediately and without wrapping it.
*
* If @a start or @a end is a non-existent revision, return the error
- * #SVN_ERR_FS_NO_SUCH_REVISION, without ever invoking @a receiver.
+ * #SVN_ERR_FS_NO_SUCH_REVISION, without ever invoking @a revision_receiver.
*
* If optional @a authz_read_func is non-NULL, then use this function
* (along with optional @a authz_read_baton) to check the readability
* of each changed-path in each revision about to be "pushed" at
- * @a receiver. If a revision has some changed-paths readable and
- * others unreadable, unreadable paths are omitted from the
- * changed_paths field and only svn:author and svn:date will be
- * available in the revprops field. If a revision has no
- * changed-paths readable at all, then all paths are omitted and no
- * revprops are available.
+ * @a path_change_receiver. If a revision has some changed-paths readable
+ * and others unreadable, unreadable paths are omitted from the
+ * @a path_change_receiver invocations and only svn:author and svn:date
+ * will be available in the revprops field in the @a revision_receiver
+ * callback. If a revision has no changed-paths readable at all, then all
+ * paths are omitted and no revprops are available. If
+ * @a path_change_receiver is @c NULL, the same filtering is performed
+ * just without reporting any path changes.
*
- * See also the documentation for #svn_log_entry_receiver_t.
+ * Use @a scratch_pool for temporary allocations.
*
- * Use @a pool for temporary allocations.
+ * @see svn_repos_path_change_receiver_t, svn_repos_log_entry_receiver_t
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_repos_get_logs5(svn_repos_t *repos,
+ const apr_array_header_t *paths,
+ svn_revnum_t start,
+ svn_revnum_t end,
+ int limit,
+ svn_boolean_t strict_node_history,
+ svn_boolean_t include_merged_revisions,
+ const apr_array_header_t *revprops,
+ svn_repos_authz_func_t authz_read_func,
+ void *authz_read_baton,
+ svn_repos_path_change_receiver_t path_change_receiver,
+ void *path_change_receiver_baton,
+ svn_repos_log_entry_receiver_t revision_receiver,
+ void *revision_receiver_baton,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Similar to svn_repos_get_logs5 but using a #svn_log_entry_receiver_t
+ * @a receiver to receive revision properties and changed paths through a
+ * single callback and the @a discover_changed_paths flag to control it.
+ *
+ * If @a discover_changed_paths, then each call to @a receiver passes a
+ * hash mapping paths committed in that revision to information about them
+ * as the receiver's @a changed_paths argument.
+ * Otherwise, each call to @a receiver passes NULL for @a changed_paths.
+ *
+ * @see svn_log_entry_receiver_t
*
* @since New in 1.5.
+ *
+ * @deprecated Provided for backward compatibility with the 1.9 API.
*/
+SVN_DEPRECATED
svn_error_t *
svn_repos_get_logs4(svn_repos_t *repos,
const apr_array_header_t *paths,
@@ -2009,12 +2294,20 @@ svn_repos_get_logs(svn_repos_t *repos,
/* Retrieving mergeinfo. */
+/** Receives parsed @a mergeinfo for the file system path @a path.
+ *
+ * The user-provided @a baton is being passed through by the retrieval
+ * function and @a scratch_pool will be cleared between invocations.
+ *
+ * @since New in 1.10.
+ */
+typedef svn_fs_mergeinfo_receiver_t svn_repos_mergeinfo_receiver_t;
+
/**
- * Fetch the mergeinfo for @a paths at @a revision in @a repos, and
- * set @a *catalog to a catalog of this mergeinfo. @a *catalog will
- * never be @c NULL but may be empty.
+ * For each node found with mergeinfo on it, invoke @a receiver with
+ * the provided @a receiver_baton.
*
- * The paths in @a paths, and the keys of @a catalog, start with '/'.
+ * The paths in @a paths start with '/'.
*
* @a inherit indicates whether explicit, explicit or inherited, or
* only inherited mergeinfo for @a paths is fetched.
@@ -2026,17 +2319,38 @@ svn_repos_get_logs(svn_repos_t *repos,
* the #SVN_PROP_MERGEINFO property explicitly set on it. (Note
* that inheritance is only taken into account for the elements in @a
* paths; descendants of the elements in @a paths which get their
- * mergeinfo via inheritance are not included in @a *catalog.)
+ * mergeinfo via inheritance are not reported to @a receiver.)
*
* If optional @a authz_read_func is non-NULL, then use this function
* (along with optional @a authz_read_baton) to check the readability
* of each path which mergeinfo was requested for (from @a paths).
* Silently omit unreadable paths from the request for mergeinfo.
*
- * Use @a pool for all allocations.
+ * Use @a scratch_pool for temporary allocations.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_repos_fs_get_mergeinfo2(svn_repos_t *repos,
+ const apr_array_header_t *paths,
+ svn_revnum_t revision,
+ svn_mergeinfo_inheritance_t inherit,
+ svn_boolean_t include_descendants,
+ svn_repos_authz_func_t authz_read_func,
+ void *authz_read_baton,
+ svn_repos_mergeinfo_receiver_t receiver,
+ void *receiver_baton,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Same as svn_repos_fs_get_mergeinfo2(), but all mergeinfo is being collected
+ * and returned in @a *catalog. It will never be @c NULL, but may be empty.
*
* @since New in 1.5.
+ *
+ * @deprecated Provided for backward compatibility with the 1.9 API.
*/
+SVN_DEPRECATED
svn_error_t *
svn_repos_fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog,
svn_repos_t *repos,
@@ -2983,6 +3297,12 @@ svn_repos_verify_fs(svn_repos_t *repos,
* be done with full plain text. A dump with @a use_deltas set cannot
* be loaded by Subversion 1.0.x.
*
+ * If @a include_revprops is @c TRUE, output the revision properties as
+ * well, otherwise omit them.
+ *
+ * If @a include_changes is @c TRUE, output the revision contents, i.e.
+ * tree and node changes.
+ *
* If @a notify_func is not null, then call it with @a notify_baton and
* with a notification structure in which the fields are set as follows.
* (For a warning or error notification that does not apply to a specific
@@ -3008,14 +3328,43 @@ svn_repos_verify_fs(svn_repos_t *repos,
* reiterating the existence of previous warnings
* ### This is a presentation issue. Caller could do this itself.
*
+ * If @a filter_func is not @c NULL, it is called for each node being
+ * dumped, allowing the caller to exclude it from dump.
+ *
* If @a cancel_func is not @c NULL, it is called periodically with
* @a cancel_baton as argument to see if the client wishes to cancel
* the dump.
*
* Use @a scratch_pool for temporary allocation.
*
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_repos_dump_fs4(svn_repos_t *repos,
+ svn_stream_t *stream,
+ svn_revnum_t start_rev,
+ svn_revnum_t end_rev,
+ svn_boolean_t incremental,
+ svn_boolean_t use_deltas,
+ svn_boolean_t include_revprops,
+ svn_boolean_t include_changes,
+ svn_repos_notify_func_t notify_func,
+ void *notify_baton,
+ svn_repos_dump_filter_func_t filter_func,
+ void *filter_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *pool);
+
+/**
+ * Similar to svn_repos_dump_fs4(), but with @a include_revprops and
+ * @a include_changes both set to @c TRUE and @a filter_func and
+ * @a filter_baton set to @c NULL.
+ *
* @since New in 1.7.
+ * @deprecated Provided for backward compatibility with the 1.9 API.
*/
+SVN_DEPRECATED
svn_error_t *
svn_repos_dump_fs3(svn_repos_t *repos,
svn_stream_t *dumpstream,
@@ -3111,6 +3460,15 @@ svn_repos_dump_fs(svn_repos_t *repos,
* to be stamped as if they were newly created via the normal commit
* process.
*
+ * If @a normalize_props is set, attempt to normalize invalid Subversion
+ * revision and node properties (those in the svn: namespace) so that
+ * their values would follow the established rules for them. For example,
+ * for such properties, typically the value must be in UTF-8 with LF
+ * line endings.
+ *
+ * @note The details or the performed normalizations are deliberately
+ * left unspecified and may change in the future.
+ *
* If non-NULL, use @a notify_func and @a notify_baton to send notification
* of events to the caller.
*
@@ -3118,8 +3476,34 @@ svn_repos_dump_fs(svn_repos_t *repos,
* @a cancel_baton as argument to see if the client wishes to cancel
* the load.
*
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_repos_load_fs6(svn_repos_t *repos,
+ svn_stream_t *dumpstream,
+ svn_revnum_t start_rev,
+ svn_revnum_t end_rev,
+ enum svn_repos_load_uuid uuid_action,
+ const char *parent_dir,
+ svn_boolean_t use_pre_commit_hook,
+ svn_boolean_t use_post_commit_hook,
+ svn_boolean_t validate_props,
+ svn_boolean_t ignore_dates,
+ svn_boolean_t normalize_props,
+ svn_repos_notify_func_t notify_func,
+ void *notify_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *pool);
+
+/**
+ * Similar to svn_repos_load_fs6(), but with the @a normalize_props
+ * parameter always set to @c FALSE.
+ *
* @since New in 1.9.
+ * @deprecated Provided for backward compatibility with the 1.9 API.
*/
+SVN_DEPRECATED
svn_error_t *
svn_repos_load_fs5(svn_repos_t *repos,
svn_stream_t *dumpstream,
@@ -3219,6 +3603,62 @@ svn_repos_load_fs(svn_repos_t *repos,
void *cancel_baton,
apr_pool_t *pool);
+/**
+ * Read and parse dumpfile-formatted @a dumpstream, extracting the
+ * revision properties from it and apply them to the already-open
+ * @a repos. Use @a scratch_pool for temporary allocations.
+ *
+ * If, after filtering by the @a start_rev and @a end_rev, the dumpstream
+ * contains revisions missing in @a repos, an error will be thrown.
+ *
+ * @a start_rev and @a end_rev act as filters, the lower and upper
+ * (inclusive) range values of revisions in @a dumpstream which will
+ * be loaded. Either both of these values are #SVN_INVALID_REVNUM (in
+ * which case no revision-based filtering occurs at all), or both are
+ * valid revisions (where @a start_rev is older than or equivalent to
+ * @a end_rev).
+ *
+ * If @a validate_props is set, then validate Subversion revision
+ * properties (those in the svn: namespace) against established
+ * rules for those things.
+ *
+ * If @a ignore_dates is set, ignore any revision datestamps found in
+ * @a dumpstream, keeping whatever timestamps the revisions currently
+ * have.
+ *
+ * If @a normalize_props is set, attempt to normalize invalid Subversion
+ * revision and node properties (those in the svn: namespace) so that
+ * their values would follow the established rules for them. For example,
+ * for such properties, typically the value must be in UTF-8 with LF
+ * line endings.
+ *
+ * @note The details or the performed normalizations are deliberately
+ * left unspecified and may change in the future.
+ *
+ * If non-NULL, use @a notify_func and @a notify_baton to send notification
+ * of events to the caller.
+ *
+ * If @a cancel_func is not @c NULL, it is called periodically with
+ * @a cancel_baton as argument to see if the client wishes to cancel
+ * the load.
+ *
+ * @remark No repository hooks will be triggered.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_repos_load_fs_revprops(svn_repos_t *repos,
+ svn_stream_t *dumpstream,
+ svn_revnum_t start_rev,
+ svn_revnum_t end_rev,
+ svn_boolean_t validate_props,
+ svn_boolean_t ignore_dates,
+ svn_boolean_t normalize_props,
+ svn_repos_notify_func_t notify_func,
+ void *notify_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool);
/**
* A vtable that is driven by svn_repos_parse_dumpstream3().
@@ -3362,6 +3802,9 @@ typedef struct svn_repos_parse_fns3_t
* could stop reading entirely after the youngest required rev.
*
* @since New in 1.8.
+
+ * @since Starting in 1.10, @a parse_fns may contain #NULL pointers for
+ * those callbacks that the caller is not interested in.
*/
svn_error_t *
svn_repos_parse_dumpstream3(svn_stream_t *stream,
@@ -3414,12 +3857,47 @@ svn_repos_parse_dumpstream3(svn_stream_t *stream,
* to be stamped as if they were newly created via the normal commit
* process.
*
+ * If @a normalize_props is set, attempt to normalize invalid Subversion
+ * revision and node properties (those in the svn: namespace) so that
+ * their values would follow the established rules for them. For example,
+ * for such properties, typically the value must be in UTF-8 with LF
+ * line endings.
+ *
+ * @note The details or the performed normalizations are deliberately
+ * left unspecified and may change in the future.
+ *
* If @a parent_dir is not NULL, then the parser will reparent all the
* loaded nodes, from root to @a parent_dir. The directory @a parent_dir
* must be an existing directory in the repository.
*
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_repos_get_fs_build_parser6(const svn_repos_parse_fns3_t **parser,
+ void **parse_baton,
+ svn_repos_t *repos,
+ svn_revnum_t start_rev,
+ svn_revnum_t end_rev,
+ svn_boolean_t use_history,
+ svn_boolean_t validate_props,
+ enum svn_repos_load_uuid uuid_action,
+ const char *parent_dir,
+ svn_boolean_t use_pre_commit_hook,
+ svn_boolean_t use_post_commit_hook,
+ svn_boolean_t ignore_dates,
+ svn_boolean_t normalize_props,
+ svn_repos_notify_func_t notify_func,
+ void *notify_baton,
+ apr_pool_t *pool);
+
+/**
+ * Similar to svn_repos_get_fs_build_parser6(), but with the
+ * @a normalize_props parameter always set to @c FALSE.
+ *
* @since New in 1.9.
+ * @deprecated Provided for backward compatibility with the 1.9 API.
*/
+SVN_DEPRECATED
svn_error_t *
svn_repos_get_fs_build_parser5(const svn_repos_parse_fns3_t **parser,
void **parse_baton,
@@ -3655,6 +4133,17 @@ svn_repos_get_fs_build_parser(const svn_repos_parser_fns_t **parser,
typedef struct svn_authz_t svn_authz_t;
/**
+ * This should be called before any other authz function.
+ *
+ * @a pool must support multi-threaded access if the application will use
+ * authz from multiple threads.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_repos_authz_initialize(apr_pool_t *pool);
+
+/**
* Read authz configuration data from @a path (a dirent, an absolute file url
* or a registry path) into @a *authz_p, allocated in @a pool.
*
@@ -3667,8 +4156,29 @@ typedef struct svn_authz_t svn_authz_t;
* is also an error other than #SVN_ERR_AUTHZ_INVALID_CONFIG (exact error
* depends on the access type).
*
+ * For efficient access of in-repository authz, you may provide @a repos_hint
+ * which will be tried first and may remove the need to open a temporary
+ * repository instance. Otherwise, set it to NULL and the repositories will
+ * be opened as needed.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_repos_authz_read3(svn_authz_t **authz_p,
+ const char *path,
+ const char *groups_path,
+ svn_boolean_t must_exist,
+ svn_repos_t *repos_hint,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Similar to svn_repos_authz_read3(), but with @a repos_hint set to @c NULL.
+ *
* @since New in 1.8.
+ * @deprecated Provided for backward compatibility with the 1.9 API.
*/
+SVN_DEPRECATED
svn_error_t *
svn_repos_authz_read2(svn_authz_t **authz_p,
const char *path,
diff --git a/subversion/include/svn_string.h b/subversion/include/svn_string.h
index 82c6fd693e20..9b0cf5cf7f8f 100644
--- a/subversion/include/svn_string.h
+++ b/subversion/include/svn_string.h
@@ -294,6 +294,14 @@ svn_stringbuf_isempty(const svn_stringbuf_t *str);
void
svn_stringbuf_chop(svn_stringbuf_t *str, apr_size_t nbytes);
+/**
+ * Chop @a nbytes bytes off the start of @a str, but not more than @a str->len.
+ *
+ * @since New in 1.10.
+ */
+void
+svn_stringbuf_leftchop(svn_stringbuf_t *str, apr_size_t nbytes);
+
/** Fill @a str with character @a c. */
void
svn_stringbuf_fillchar(svn_stringbuf_t *str, unsigned char c);
@@ -404,6 +412,16 @@ svn_stringbuf_replace(svn_stringbuf_t *str,
const char *bytes,
apr_size_t new_count);
+/** Replace all occurrences of @a to_find in @a str with @a replacement.
+ * Return the number of replacements made.
+ *
+ * @since New in 1.10.
+ */
+apr_size_t
+svn_stringbuf_replace_all(svn_stringbuf_t *str,
+ const char *to_find,
+ const char *replacement);
+
/** Return a duplicate of @a original_string. */
svn_stringbuf_t *
svn_stringbuf_dup(const svn_stringbuf_t *original_string, apr_pool_t *pool);
@@ -512,12 +530,27 @@ svn_cstring_count_newlines(const char *msg);
/**
* Return a cstring which is the concatenation of @a strings (an array
- * of char *) each followed by @a separator (that is, @a separator
- * will also end the resulting string). Allocate the result in @a pool.
+ * of char *) joined by @a separator. Allocate the result in @a pool.
* If @a strings is empty, then return the empty string.
+ * If @a trailing_separator is non-zero, also append the separator
+ * after the last joined element.
+ *
+ * @since New in 1.10.
+ */
+char *
+svn_cstring_join2(const apr_array_header_t *strings,
+ const char *separator,
+ svn_boolean_t trailing_separator,
+ apr_pool_t *pool);
+
+/**
+ * Similar to svn_cstring_join2(), but always includes the trailing
+ * separator.
*
* @since New in 1.2.
+ * @deprecated Provided for backwards compatibility with the 1.9 API.
*/
+SVN_DEPRECATED
char *
svn_cstring_join(const apr_array_header_t *strings,
const char *separator,
@@ -541,7 +574,17 @@ svn_cstring_casecmp(const char *str1, const char *str2);
* Assume that the number is represented in base @a base.
* Raise an error if conversion fails (e.g. due to overflow), or if the
* converted number is smaller than @a minval or larger than @a maxval.
+ *
* Leading whitespace in @a str is skipped in a locale-dependent way.
+ * After that, the string may contain an optional '+' (positive, default)
+ * or '-' (negative) character, followed by an optional '0x' prefix if
+ * @a base is 0 or 16, followed by numeric digits appropriate for the base.
+ * If there are any more characters after the numeric digits, an error is
+ * returned.
+ *
+ * If @a base is zero, then a leading '0x' or '0X' prefix means hexadecimal,
+ * else a leading '0' means octal (implemented, though not documented, in
+ * apr_strtoi64() in APR 0.9.0 through 1.5.0), else use base ten.
*
* @since New in 1.7.
*/
@@ -554,7 +597,8 @@ svn_cstring_strtoi64(apr_int64_t *n, const char *str,
* Parse the C string @a str into a 64 bit number, and return it in @a *n.
* Assume that the number is represented in base 10.
* Raise an error if conversion fails (e.g. due to overflow).
- * Leading whitespace in @a str is skipped in a locale-dependent way.
+ *
+ * The behaviour otherwise is as described for svn_cstring_strtoi64().
*
* @since New in 1.7.
*/
@@ -565,7 +609,8 @@ svn_cstring_atoi64(apr_int64_t *n, const char *str);
* Parse the C string @a str into a 32 bit number, and return it in @a *n.
* Assume that the number is represented in base 10.
* Raise an error if conversion fails (e.g. due to overflow).
- * Leading whitespace in @a str is skipped in a locale-dependent way.
+ *
+ * The behaviour otherwise is as described for svn_cstring_strtoi64().
*
* @since New in 1.7.
*/
@@ -577,7 +622,21 @@ svn_cstring_atoi(int *n, const char *str);
* it in @a *n. Assume that the number is represented in base @a base.
* Raise an error if conversion fails (e.g. due to overflow), or if the
* converted number is smaller than @a minval or larger than @a maxval.
+ *
* Leading whitespace in @a str is skipped in a locale-dependent way.
+ * After that, the string may contain an optional '+' (positive, default)
+ * or '-' (negative) character, followed by an optional '0x' prefix if
+ * @a base is 0 or 16, followed by numeric digits appropriate for the base.
+ * If there are any more characters after the numeric digits, an error is
+ * returned.
+ *
+ * If @a base is zero, then a leading '0x' or '0X' prefix means hexadecimal,
+ * else a leading '0' means octal (implemented, though not documented, in
+ * apr_strtoi64() in APR 0.9.0 through 1.5.0), else use base ten.
+ *
+ * @warning The implementation used since version 1.7 returns an error
+ * if the parsed number is greater than APR_INT64_MAX, even if it is not
+ * greater than @a maxval.
*
* @since New in 1.7.
*/
@@ -590,7 +649,9 @@ svn_cstring_strtoui64(apr_uint64_t *n, const char *str,
* Parse the C string @a str into an unsigned 64 bit number, and return
* it in @a *n. Assume that the number is represented in base 10.
* Raise an error if conversion fails (e.g. due to overflow).
- * Leading whitespace in @a str is skipped in a locale-dependent way.
+ *
+ * The behaviour otherwise is as described for svn_cstring_strtoui64(),
+ * including the upper limit of APR_INT64_MAX.
*
* @since New in 1.7.
*/
@@ -601,7 +662,9 @@ svn_cstring_atoui64(apr_uint64_t *n, const char *str);
* Parse the C string @a str into an unsigned 32 bit number, and return
* it in @a *n. Assume that the number is represented in base 10.
* Raise an error if conversion fails (e.g. due to overflow).
- * Leading whitespace in @a str is skipped in a locale-dependent way.
+ *
+ * The behaviour otherwise is as described for svn_cstring_strtoui64(),
+ * including the upper limit of APR_INT64_MAX.
*
* @since New in 1.7.
*/
diff --git a/subversion/include/svn_types.h b/subversion/include/svn_types.h
index f1a0850bcf8a..394eda866937 100644
--- a/subversion/include/svn_types.h
+++ b/subversion/include/svn_types.h
@@ -416,7 +416,7 @@ svn_tristate__from_word(const char * word);
* 2. Creating a new textual name similar to
* SVN_SUBST__SPECIAL_LINK_STR in libsvn_subr/subst.c.
* 3. Handling the translation/detranslation case for the new type in
- * create_special_file and detranslate_special_file, using the
+ * create_special_file_from_stream and detranslate_special_file, using the
* routines from 1.
*/
@@ -652,7 +652,7 @@ typedef struct svn_dirent_t
/** node kind */
svn_node_kind_t kind;
- /** length of file text, or 0 for directories */
+ /** length of file text, otherwise SVN_INVALID_FILESIZE */
svn_filesize_t size;
/** does the node have props? */
@@ -1078,6 +1078,11 @@ typedef svn_error_t *(*svn_log_message_receiver_t)(
* @a commit_info, along with the @a baton closure.
* @a pool can be used for temporary allocations.
*
+ * @note Implementers of this callback that pass this callback to
+ * svn_ra_get_commit_editor3() should be careful with returning errors
+ * as these might be returned as commit errors. See the documentation
+ * of svn_ra_get_commit_editor3() for more details.
+ *
* @since New in 1.4.
*/
typedef svn_error_t *(*svn_commit_callback2_t)(
diff --git a/subversion/include/svn_user.h b/subversion/include/svn_user.h
index 65e28200b6d0..922fdcf0a175 100644
--- a/subversion/include/svn_user.h
+++ b/subversion/include/svn_user.h
@@ -45,6 +45,9 @@ svn_user_get_name(apr_pool_t *pool);
* any necessary allocation, returning NULL on error.
*
* @since New in 1.4.
+ * @since 1.10 returns a canonical path. Earlier versions returned a
+ * non-canonical path if the operating system reported a non-canonical
+ * path such as "/home/user/" or "//home/user".
*/
const char *
svn_user_get_homedir(apr_pool_t *pool);
diff --git a/subversion/include/svn_version.h b/subversion/include/svn_version.h
index 39f7154847e5..9c88b5b2f71d 100644
--- a/subversion/include/svn_version.h
+++ b/subversion/include/svn_version.h
@@ -61,7 +61,7 @@ extern "C" {
* Modify when new functionality is added or new interfaces are
* defined, but all changes are backward compatible.
*/
-#define SVN_VER_MINOR 9
+#define SVN_VER_MINOR 10
/**
* Patch number.
@@ -70,7 +70,7 @@ extern "C" {
*
* @since New in 1.1.
*/
-#define SVN_VER_PATCH 7
+#define SVN_VER_PATCH 0
/** @deprecated Provided for backward compatibility with the 1.0 API. */
@@ -93,7 +93,7 @@ extern "C" {
*
* Always change this at the same time as SVN_VER_NUMTAG.
*/
-#define SVN_VER_TAG " (r1800392)"
+#define SVN_VER_TAG " (r1827917)"
/** Number tag: a string describing the version.
@@ -117,7 +117,7 @@ extern "C" {
* file version. Its value remains 0 in the repository except in release
* tags where it is the revision from which the tag was created.
*/
-#define SVN_VER_REVISION 1800392
+#define SVN_VER_REVISION 1827917
/* Version strings composed from the above definitions. */
diff --git a/subversion/include/svn_wc.h b/subversion/include/svn_wc.h
index 010597bd6eeb..e632673e0ae4 100644
--- a/subversion/include/svn_wc.h
+++ b/subversion/include/svn_wc.h
@@ -998,7 +998,10 @@ typedef enum svn_wc_notify_action_t
/** A revert operation has failed. */
svn_wc_notify_failed_revert,
- /** Resolving a conflict. */
+ /** All conflicts on a path were marked as resolved.
+ * @note As of 1.10, separate notifications are sent for individually
+ * resolved text, property, and tree conflicts. This notification is used
+ * only if all conflicts on a path were marked resolved at once. */
svn_wc_notify_resolved,
/** Skipping a path. */
@@ -1274,7 +1277,34 @@ typedef enum svn_wc_notify_action_t
/** Finalizing commit.
* @since New in 1.9. */
- svn_wc_notify_commit_finalizing
+ svn_wc_notify_commit_finalizing,
+
+ /** All text conflicts in a file were marked as resolved.
+ * @since New in 1.10. */
+ svn_wc_notify_resolved_text,
+
+ /** A property conflict on a path was marked as resolved.
+ * The name of the property is specified in #svn_wc_notify_t.prop_name.
+ * @since New in 1.10. */
+ svn_wc_notify_resolved_prop,
+
+ /** A tree conflict on a path was marked as resolved.
+ * @since New in 1.10. */
+ svn_wc_notify_resolved_tree,
+
+ /** Starting to search the repository for details about a tree conflict.
+ * @since New in 1.10. */
+ svn_wc_notify_begin_search_tree_conflict_details,
+
+ /** Progressing in search of repository for details about a tree conflict.
+ * The revision being searched is specified in #svn_wc_notify_t.revision.
+ * @since New in 1.10. */
+ svn_wc_notify_tree_conflict_details_progress,
+
+ /** Done searching the repository for details about a conflict.
+ * @since New in 1.10. */
+ svn_wc_notify_end_search_tree_conflict_details
+
} svn_wc_notify_action_t;
@@ -1911,8 +1941,9 @@ typedef struct svn_wc_conflict_description_t
/** The path that is in conflict (for a tree conflict, it is the victim) */
const char *path;
- /** The node type of the path being operated on (for a tree conflict,
- * ### which version?) */
+ /** The local node type of the path being operated on (for a tree conflict,
+ * this specifies the local node kind, which may be (and typically is)
+ * different than the left and right kind) */
svn_node_kind_t node_kind;
/** What sort of conflict are we describing? */
@@ -2063,7 +2094,7 @@ svn_wc_conflict_description_create_prop(const char *path,
*
* Set the @c local_abspath field of the created struct to @a local_abspath
* (which must be an absolute path), the @c kind field to
- * #svn_wc_conflict_kind_tree, the @c local_node_kind to @a local_node_kind,
+ * #svn_wc_conflict_kind_tree, the @c node_kind to @a node_kind,
* the @c operation to @a operation, the @c src_left_version field to
* @a src_left_version, and the @c src_right_version field to
* @a src_right_version.
@@ -2131,8 +2162,11 @@ svn_wc__conflict_description2_dup(
*/
typedef enum svn_wc_conflict_choice_t
{
- /** Undefined; for internal use only.
- This value is never returned in svn_wc_conflict_result_t.
+ /** Undefined; for private use only.
+ This value must never be returned in svn_wc_conflict_result_t,
+ but a separate value, unequal to all other pre-defined values may
+ be useful in conflict resolver implementations to signal that no
+ choice is made yet.
* @since New in 1.9
*/
svn_wc_conflict_choose_undefined = -1,
@@ -4544,7 +4578,7 @@ svn_wc_move(svn_wc_context_t *wc_ctx,
* and everything below @a local_abspath.
*
* If @a keep_local is FALSE, this function immediately deletes all files,
- * modified and unmodified, versioned and of @a delete_unversioned is TRUE,
+ * modified and unmodified, versioned and if @a delete_unversioned is TRUE,
* unversioned from the working copy.
* It also immediately deletes unversioned directories and directories that
* are scheduled to be added below @a local_abspath. Only versioned may
@@ -5050,7 +5084,11 @@ svn_wc_remove_from_revision_control(svn_wc_adm_access_t *adm_access,
* Temporary allocations will be performed in @a scratch_pool.
*
* @since New in 1.7.
+ * @deprecated Provided for backward compatibility with the 1.9 API.
+ * Use svn_client_conflict_text_resolve(), svn_client_conflict_prop_resolve(),
+ * and svn_client_conflict_tree_resolve() instead.
*/
+SVN_DEPRECATED
svn_error_t *
svn_wc_resolved_conflict5(svn_wc_context_t *wc_ctx,
const char *local_abspath,
diff --git a/subversion/include/svn_x509.h b/subversion/include/svn_x509.h
index eabe3ed46c8b..e3cb7941d357 100644
--- a/subversion/include/svn_x509.h
+++ b/subversion/include/svn_x509.h
@@ -166,7 +166,7 @@ svn_x509_certinfo_get_valid_from(const svn_x509_certinfo_t *certinfo);
*
* @since New in 1.9.
*/
-const apr_time_t
+apr_time_t
svn_x509_certinfo_get_valid_to(const svn_x509_certinfo_t *certinfo);
/**
@@ -188,7 +188,7 @@ svn_x509_certinfo_get_hostnames(const svn_x509_certinfo_t *certinfo);
* Given an @a oid return a null-terminated C string representation.
* For example an OID with the bytes "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x01"
* would be converted to the string "1.2.840.113549.1.9.1". Returns
- * NULL if the @oid can't be represented as a string.
+ * NULL if the @a oid can't be represented as a string.
*
* @since New in 1.9. */
const char *
diff --git a/subversion/include/svn_xml.h b/subversion/include/svn_xml.h
index 8791b143750c..1a52166feaea 100644
--- a/subversion/include/svn_xml.h
+++ b/subversion/include/svn_xml.h
@@ -169,7 +169,15 @@ typedef void (*svn_xml_char_data)(void *baton,
apr_size_t len);
-/** Create a general Subversion XML parser */
+/** Create a general Subversion XML parser.
+ *
+ * The @c svn_xml_parser_t object itself will be allocated from @a pool,
+ * but some internal structures may be allocated out of pool. Use
+ * svn_xml_free_parser() to free all memory used by the parser.
+ *
+ * @since Since Subversion 1.10 parser will be freed automatically on pool
+ * cleanup or by svn_xml_free_parser() call.
+ */
svn_xml_parser_t *
svn_xml_make_parser(void *baton,
svn_xml_start_elem start_handler,
diff --git a/subversion/libsvn_auth_gnome_keyring/gnome_keyring.c b/subversion/libsvn_auth_gnome_keyring/gnome_keyring.c
index 030023a72b47..0e77586e051e 100644
--- a/subversion/libsvn_auth_gnome_keyring/gnome_keyring.c
+++ b/subversion/libsvn_auth_gnome_keyring/gnome_keyring.c
@@ -23,31 +23,169 @@
/* ==================================================================== */
-
/*** Includes. ***/
-
#include <apr_pools.h>
#include <apr_strings.h>
-#include <glib.h>
-#include <gnome-keyring.h>
-
#include "svn_auth.h"
-#include "svn_config.h"
-#include "svn_error.h"
#include "svn_hash.h"
-#include "svn_pools.h"
-
+#include "svn_version.h"
#include "private/svn_auth_private.h"
-
#include "svn_private_config.h"
+#ifdef SVN_HAVE_LIBSECRET
-
-/*-----------------------------------------------------------------------*/
-/* GNOME Keyring simple provider, puts passwords in GNOME Keyring */
-/*-----------------------------------------------------------------------*/
+#include <libsecret/secret.h>
+/* Return TRUE if the default collection is available and FALSE
+ otherwise. In interactive mode the collection only has to exist to
+ be available, it can be locked or unlocked. The default collection
+ will be created if necessary.
+
+ In non-interactive mode the collection is only available if it
+ already exists and is unlocked. Such an available collection can
+ be used without prompting. Strictly this is racy: nothing ensures
+ the collection remains unlocked. A similar issue affects the
+ KWallet and original GNOME Keyring providers.
+
+ As a non-racy alternative one could override prompt_async in the
+ _SecretServiceClass vtable, the get/set would still fail but there
+ would be no prompt and no race. This "works" but it is not clear
+ to me whether it is legitimate since the SecretService is a
+ singleton and the effect would be application-wide.
+ */
+static svn_boolean_t
+available_collection(svn_boolean_t non_interactive,
+ apr_pool_t *pool)
+{
+ GError *gerror = NULL;
+ SecretService *service = NULL;
+ SecretCollection *collection = NULL;
+
+ service = secret_service_get_sync(SECRET_SERVICE_NONE, NULL, &gerror);
+ if (gerror || !service)
+ goto error_return;
+
+ collection = secret_collection_for_alias_sync(service,
+ SECRET_COLLECTION_DEFAULT,
+ SECRET_COLLECTION_NONE,
+ NULL, &gerror);
+ if (gerror)
+ goto error_return;
+
+ if (!collection)
+ {
+ if (non_interactive)
+ goto error_return;
+
+ /* "Default" is the label used by the old libgnome-keyring. */
+ collection = secret_collection_create_sync(service, "Default",
+ SECRET_COLLECTION_DEFAULT,
+ 0, NULL, &gerror);
+ if (gerror || !collection)
+ goto error_return;
+ }
+
+ if (non_interactive && secret_collection_get_locked(collection))
+ goto error_return;
+
+ g_object_unref(collection);
+ g_object_unref(service);
+
+ return TRUE;
+
+ error_return:
+ if (gerror)
+ g_error_free(gerror);
+ if (collection)
+ g_object_unref(collection);
+ if (service)
+ g_object_unref(service);
+ return FALSE;
+}
+
+/* Implementation of svn_auth__password_get_t that retrieves the password
+ using libsecret. */
+static svn_error_t *
+password_get_gnome_keyring(svn_boolean_t *done,
+ const char **password,
+ apr_hash_t *creds,
+ const char *realmstring,
+ const char *username,
+ apr_hash_t *parameters,
+ svn_boolean_t non_interactive,
+ apr_pool_t *pool)
+{
+ GError *gerror = NULL;
+ gchar *gpassword;
+
+ if (!available_collection(non_interactive, pool))
+ return SVN_NO_ERROR;
+
+ gpassword = secret_password_lookup_sync(SECRET_SCHEMA_COMPAT_NETWORK, NULL,
+ &gerror,
+ "domain", realmstring,
+ "user", username,
+ NULL);
+ if (gerror)
+ {
+ g_error_free(gerror);
+ }
+ else if (gpassword)
+ {
+ *password = apr_pstrdup(pool, gpassword);
+ g_free(gpassword);
+ *done = TRUE;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Implementation of svn_auth__password_set_t that stores the password
+ using libsecret. */
+static svn_error_t *
+password_set_gnome_keyring(svn_boolean_t *done,
+ apr_hash_t *creds,
+ const char *realmstring,
+ const char *username,
+ const char *password,
+ apr_hash_t *parameters,
+ svn_boolean_t non_interactive,
+ apr_pool_t *pool)
+{
+ GError *gerror = NULL;
+ gboolean gstatus;
+
+ if (!available_collection(non_interactive, pool))
+ return SVN_NO_ERROR;
+
+ /* "network password" is the label used by the old libgnome-keyring. */
+ gstatus = secret_password_store_sync(SECRET_SCHEMA_COMPAT_NETWORK,
+ SECRET_COLLECTION_DEFAULT,
+ "network password",
+ password,
+ NULL, &gerror,
+ "domain", realmstring,
+ "user", username,
+ NULL);
+ if (gerror)
+ {
+ g_error_free(gerror);
+ }
+ else if (gstatus)
+ {
+ *done = TRUE;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+#endif /* SVN_HAVE_LIBSECRET */
+
+#ifdef SVN_HAVE_GNOME_KEYRING
+
+#include <glib.h>
+#include <gnome-keyring.h>
/* Returns the default keyring name, allocated in RESULT_POOL. */
static char*
@@ -252,6 +390,41 @@ password_set_gnome_keyring(svn_boolean_t *done,
return SVN_NO_ERROR;
}
+#if GLIB_CHECK_VERSION(2,6,0)
+static void
+log_noop(const gchar *log_domain, GLogLevelFlags log_level,
+ const gchar *message, gpointer user_data)
+{
+ /* do nothing */
+}
+#endif
+
+static void
+init_gnome_keyring(void)
+{
+ const char *application_name = NULL;
+ application_name = g_get_application_name();
+ if (!application_name)
+ g_set_application_name("Subversion");
+
+ /* Ideally we call g_log_set_handler() with a log_domain specific to
+ libgnome-keyring. Unfortunately, at least as of gnome-keyring
+ 2.22.3, it doesn't have its own log_domain. As a result, we
+ suppress stderr spam for not only libgnome-keyring, but for
+ anything else the app is linked to that uses glib logging and
+ doesn't specify a log_domain. */
+#if GLIB_CHECK_VERSION(2,6,0)
+ g_log_set_default_handler(log_noop, NULL);
+#endif
+}
+
+#endif /* SVN_HAVE_GNOME_KEYRING */
+
+
+/*-----------------------------------------------------------------------*/
+/* GNOME Keyring simple provider, puts passwords in GNOME Keyring */
+/*-----------------------------------------------------------------------*/
+
/* Get cached encrypted credentials from the simple provider's cache. */
static svn_error_t *
simple_gnome_keyring_first_creds(void **credentials,
@@ -286,34 +459,6 @@ simple_gnome_keyring_save_creds(svn_boolean_t *saved,
pool);
}
-#if GLIB_CHECK_VERSION(2,6,0)
-static void
-log_noop(const gchar *log_domain, GLogLevelFlags log_level,
- const gchar *message, gpointer user_data)
-{
- /* do nothing */
-}
-#endif
-
-static void
-init_gnome_keyring(void)
-{
- const char *application_name = NULL;
- application_name = g_get_application_name();
- if (!application_name)
- g_set_application_name("Subversion");
-
- /* Ideally we call g_log_set_handler() with a log_domain specific to
- libgnome-keyring. Unfortunately, at least as of gnome-keyring
- 2.22.3, it doesn't have its own log_domain. As a result, we
- suppress stderr spam for not only libgnome-keyring, but for
- anything else the app is linked to that uses glib logging and
- doesn't specify a log_domain. */
-#if GLIB_CHECK_VERSION(2,6,0)
- g_log_set_default_handler(log_noop, NULL);
-#endif
-}
-
static const svn_auth_provider_t gnome_keyring_simple_provider = {
SVN_AUTH_CRED_SIMPLE,
simple_gnome_keyring_first_creds,
@@ -332,9 +477,12 @@ svn_auth_get_gnome_keyring_simple_provider
po->vtable = &gnome_keyring_simple_provider;
*provider = po;
+#ifdef SVN_HAVE_GNOME_KEYRING
init_gnome_keyring();
+#endif
}
+
/*-----------------------------------------------------------------------*/
/* GNOME Keyring SSL client certificate passphrase provider, */
@@ -391,5 +539,7 @@ svn_auth_get_gnome_keyring_ssl_client_cert_pw_provider
po->vtable = &gnome_keyring_ssl_client_cert_pw_provider;
*provider = po;
+#ifdef SVN_HAVE_GNOME_KEYRING
init_gnome_keyring();
+#endif
}
diff --git a/subversion/libsvn_auth_kwallet/kwallet.cpp b/subversion/libsvn_auth_kwallet/kwallet.cpp
index 74e4eaf846ff..f59e04778b3c 100644
--- a/subversion/libsvn_auth_kwallet/kwallet.cpp
+++ b/subversion/libsvn_auth_kwallet/kwallet.cpp
@@ -39,8 +39,6 @@
#include <QtCore/QString>
#include <kaboutdata.h>
-#include <kcmdlineargs.h>
-#include <kcomponentdata.h>
#include <klocalizedstring.h>
#include <kwallet.h>
@@ -57,6 +55,10 @@
#include "svn_private_config.h"
+#ifndef SVN_HAVE_KF5
+#include <kcmdlineargs.h>
+#include <kcomponentdata.h>
+#endif
/*-----------------------------------------------------------------------*/
/* KWallet simple provider, puts passwords in KWallet */
@@ -221,6 +223,16 @@ kwallet_password_get(svn_boolean_t *done,
app = new QCoreApplication(argc, q_argv);
}
+#if SVN_HAVE_KF5
+ KLocalizedString::setApplicationDomain("subversion"); /* translation domain */
+
+ /* componentName appears in KDE GUI prompts */
+ KAboutData aboutData(QStringLiteral("subversion"), /* componentName */
+ i18n(get_application_name(parameters,
+ pool)), /* displayName */
+ QStringLiteral(SVN_VER_NUMBER));
+ KAboutData::setApplicationData(aboutData);
+#else
KCmdLineArgs::init(q_argc, q_argv,
get_application_name(parameters, pool),
"subversion",
@@ -229,6 +241,8 @@ kwallet_password_get(svn_boolean_t *done,
ki18n("Version control system"),
KCmdLineArgs::CmdLineArgKDE);
KComponentData component_data(KCmdLineArgs::aboutData());
+#endif
+
QString folder = QString::fromUtf8("Subversion");
QString key =
QString::fromUtf8(username) + "@" + QString::fromUtf8(realmstring);
@@ -291,6 +305,16 @@ kwallet_password_set(svn_boolean_t *done,
app = new QCoreApplication(argc, q_argv);
}
+#if SVN_HAVE_KF5
+ KLocalizedString::setApplicationDomain("subversion"); /* translation domain */
+
+ /* componentName appears in KDE GUI prompts */
+ KAboutData aboutData(QStringLiteral("subversion"), /* componentName */
+ i18n(get_application_name(parameters,
+ pool)), /* displayName */
+ QStringLiteral(SVN_VER_NUMBER));
+ KAboutData::setApplicationData(aboutData);
+#else
KCmdLineArgs::init(q_argc, q_argv,
get_application_name(parameters, pool),
"subversion",
@@ -299,6 +323,8 @@ kwallet_password_set(svn_boolean_t *done,
ki18n("Version control system"),
KCmdLineArgs::CmdLineArgKDE);
KComponentData component_data(KCmdLineArgs::aboutData());
+#endif
+
QString q_password = QString::fromUtf8(password);
QString folder = QString::fromUtf8("Subversion");
KWallet::Wallet *wallet = get_wallet(wallet_name, parameters);
diff --git a/subversion/libsvn_client/checkout.c b/subversion/libsvn_client/checkout.c
index 0d20e24e117a..b9138b0b6606 100644
--- a/subversion/libsvn_client/checkout.c
+++ b/subversion/libsvn_client/checkout.c
@@ -81,6 +81,7 @@ svn_client__checkout_internal(svn_revnum_t *result_rev,
{
svn_node_kind_t kind;
svn_client__pathrev_t *pathrev;
+ svn_opt_revision_t resolved_rev = { svn_opt_revision_number };
/* Sanity check. Without these, the checkout is meaningless. */
SVN_ERR_ASSERT(local_abspath != NULL);
@@ -125,6 +126,7 @@ svn_client__checkout_internal(svn_revnum_t *result_rev,
}
SVN_ERR(svn_ra_check_path(ra_session, "", pathrev->rev, &kind, scratch_pool));
+ resolved_rev.value.number = pathrev->rev;
if (kind == svn_node_none)
return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
@@ -185,8 +187,8 @@ svn_client__checkout_internal(svn_revnum_t *result_rev,
/* Have update fix the incompleteness. */
SVN_ERR(svn_client__update_internal(result_rev, timestamp_sleep,
- local_abspath, revision, depth, TRUE,
- ignore_externals,
+ local_abspath, &resolved_rev, depth,
+ TRUE, ignore_externals,
allow_unver_obstructions,
TRUE /* adds_as_modification */,
FALSE, FALSE, ra_session,
diff --git a/subversion/libsvn_client/client.h b/subversion/libsvn_client/client.h
index 58354cf9b2c4..c0a794712f3f 100644
--- a/subversion/libsvn_client/client.h
+++ b/subversion/libsvn_client/client.h
@@ -1072,9 +1072,13 @@ svn_client__ensure_revprop_table(apr_hash_t **revprop_table_out,
EXPAND_KEYWORDS operates as per the EXPAND argument to
svn_subst_stream_translated, which see. If NORMALIZE_EOLS is TRUE and
LOCAL_ABSPATH requires translation, then normalize the line endings in
- *NORMAL_STREAM.
+ *NORMAL_STREAM to "\n" if the stream has svn:eol-style set.
- Uses SCRATCH_POOL for temporary allocations. */
+ Note that this IS NOT the repository normal form of the stream as that
+ would use "\r\n" if set to CRLF and "\r" if set to CR.
+
+ The stream is allocated in RESULT_POOL and temporary SCRATCH_POOL is
+ used for temporary allocations. */
svn_error_t *
svn_client__get_normalized_stream(svn_stream_t **normal_stream,
svn_wc_context_t *wc_ctx,
@@ -1181,6 +1185,88 @@ svn_client__remote_propget(apr_hash_t *props,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
+/* */
+typedef struct merge_source_t
+{
+ /* "left" side URL and revision (inclusive iff youngest) */
+ const svn_client__pathrev_t *loc1;
+
+ /* "right" side URL and revision (inclusive iff youngest) */
+ const svn_client__pathrev_t *loc2;
+
+ /* True iff LOC1 is an ancestor of LOC2 or vice-versa (history-wise). */
+ svn_boolean_t ancestral;
+} merge_source_t;
+
+/* Description of the merge target root node (a WC working node) */
+typedef struct merge_target_t
+{
+ /* Absolute path to the WC node */
+ const char *abspath;
+
+ /* The repository location of the base node of the target WC. If the node
+ * is locally added, then URL & REV are NULL & SVN_INVALID_REVNUM.
+ * REPOS_ROOT_URL and REPOS_UUID are always valid. */
+ svn_client__pathrev_t loc;
+
+} merge_target_t;
+
+/*
+ * Similar API to svn_client_merge_peg5().
+ */
+svn_error_t *
+svn_client__merge_elements(svn_boolean_t *use_sleep,
+ apr_array_header_t *merge_sources,
+ merge_target_t *target,
+ svn_ra_session_t *ra_session,
+ svn_boolean_t diff_ignore_ancestry,
+ svn_boolean_t force_delete,
+ svn_boolean_t dry_run,
+ const apr_array_header_t *merge_options,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* Data for reporting when a merge aborted because of raising conflicts.
+ *
+ * ### TODO: More info, including the ranges (or other parameters) the user
+ * needs to complete the merge.
+ */
+typedef struct svn_client__conflict_report_t
+{
+ const char *target_abspath;
+ /* The revision range during which conflicts were raised */
+ const merge_source_t *conflicted_range;
+ /* Was the conflicted range the last range in the whole requested merge? */
+ svn_boolean_t was_last_range;
+} svn_client__conflict_report_t;
+
+/* Create and return an error structure appropriate for the unmerged
+ revisions range(s). */
+svn_error_t *
+svn_client__make_merge_conflict_error(svn_client__conflict_report_t *report,
+ apr_pool_t *scratch_pool);
+
+/* The body of svn_client_merge5(), which see for details. */
+svn_error_t *
+svn_client__merge_locked(svn_client__conflict_report_t **conflict_report,
+ const char *source1,
+ const svn_opt_revision_t *revision1,
+ const char *source2,
+ const svn_opt_revision_t *revision2,
+ const char *target_abspath,
+ svn_depth_t depth,
+ svn_boolean_t ignore_mergeinfo,
+ svn_boolean_t diff_ignore_ancestry,
+ svn_boolean_t force_delete,
+ svn_boolean_t record_only,
+ svn_boolean_t dry_run,
+ svn_boolean_t allow_mixed_rev,
+ const apr_array_header_t *merge_options,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/subversion/libsvn_client/conflicts.c b/subversion/libsvn_client/conflicts.c
new file mode 100644
index 000000000000..0fd9a2bbec5e
--- /dev/null
+++ b/subversion/libsvn_client/conflicts.c
@@ -0,0 +1,11207 @@
+/*
+ * conflicts.c: conflict resolver implementation
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+
+
+/*** Includes. ***/
+
+#include "svn_types.h"
+#include "svn_wc.h"
+#include "svn_client.h"
+#include "svn_error.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_pools.h"
+#include "svn_props.h"
+#include "svn_hash.h"
+#include "svn_sorts.h"
+#include "svn_subst.h"
+#include "client.h"
+
+#include "private/svn_diff_tree.h"
+#include "private/svn_ra_private.h"
+#include "private/svn_sorts_private.h"
+#include "private/svn_token.h"
+#include "private/svn_wc_private.h"
+
+#include "svn_private_config.h"
+
+#define ARRAY_LEN(ary) ((sizeof (ary)) / (sizeof ((ary)[0])))
+
+
+/*** Dealing with conflicts. ***/
+
+/* Describe a tree conflict. */
+typedef svn_error_t *(*tree_conflict_get_description_func_t)(
+ const char **change_description,
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* Get more information about a tree conflict.
+ * This function may contact the repository. */
+typedef svn_error_t *(*tree_conflict_get_details_func_t)(
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
+struct svn_client_conflict_t
+{
+ const char *local_abspath;
+ apr_hash_t *prop_conflicts;
+
+ /* Indicate which options were chosen to resolve a text or tree conflict
+ * on the conflicted node. */
+ svn_client_conflict_option_id_t resolution_text;
+ svn_client_conflict_option_id_t resolution_tree;
+
+ /* A mapping from const char* property name to pointers to
+ * svn_client_conflict_option_t for all properties which had their
+ * conflicts resolved. Indicates which options were chosen to resolve
+ * the property conflicts. */
+ apr_hash_t *resolved_props;
+
+ /* Ask a tree conflict to describe itself. */
+ tree_conflict_get_description_func_t
+ tree_conflict_get_incoming_description_func;
+ tree_conflict_get_description_func_t
+ tree_conflict_get_local_description_func;
+
+ /* Ask a tree conflict to find out more information about itself
+ * by contacting the repository. */
+ tree_conflict_get_details_func_t tree_conflict_get_incoming_details_func;
+ tree_conflict_get_details_func_t tree_conflict_get_local_details_func;
+
+ /* Any additional information found can be stored here and may be used
+ * when describing a tree conflict. */
+ void *tree_conflict_incoming_details;
+ void *tree_conflict_local_details;
+
+ /* The pool this conflict was allocated from. */
+ apr_pool_t *pool;
+
+ /* Conflict data provided by libsvn_wc. */
+ const svn_wc_conflict_description2_t *legacy_text_conflict;
+ const char *legacy_prop_conflict_propname;
+ const svn_wc_conflict_description2_t *legacy_tree_conflict;
+
+ /* The recommended resolution option's ID. */
+ svn_client_conflict_option_id_t recommended_option_id;
+};
+
+/* Resolves conflict to OPTION and sets CONFLICT->RESOLUTION accordingly.
+ *
+ * May raise an error in case the conflict could not be resolved. A common
+ * case would be a tree conflict the resolution of which depends on other
+ * tree conflicts to be resolved first. */
+typedef svn_error_t *(*conflict_option_resolve_func_t)(
+ svn_client_conflict_option_t *option,
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
+struct svn_client_conflict_option_t
+{
+ svn_client_conflict_option_id_t id;
+ const char *label;
+ const char *description;
+
+ svn_client_conflict_t *conflict;
+ conflict_option_resolve_func_t do_resolve_func;
+
+ /* The pool this option was allocated from. */
+ apr_pool_t *pool;
+
+ /* Data which is specific to particular conflicts and options. */
+ union {
+ struct {
+ /* Indicates the property to resolve in case of a property conflict.
+ * If set to "", all properties are resolved to this option. */
+ const char *propname;
+
+ /* A merged property value, if supplied by the API user, else NULL. */
+ const svn_string_t *merged_propval;
+ } prop;
+ } type_data;
+
+};
+
+/*
+ * Return a legacy conflict choice corresponding to OPTION_ID.
+ * Return svn_wc_conflict_choose_undefined if no corresponding
+ * legacy conflict choice exists.
+ */
+static svn_wc_conflict_choice_t
+conflict_option_id_to_wc_conflict_choice(
+ svn_client_conflict_option_id_t option_id)
+{
+
+ switch (option_id)
+ {
+ case svn_client_conflict_option_undefined:
+ return svn_wc_conflict_choose_undefined;
+
+ case svn_client_conflict_option_postpone:
+ return svn_wc_conflict_choose_postpone;
+
+ case svn_client_conflict_option_base_text:
+ return svn_wc_conflict_choose_base;
+
+ case svn_client_conflict_option_incoming_text:
+ return svn_wc_conflict_choose_theirs_full;
+
+ case svn_client_conflict_option_working_text:
+ return svn_wc_conflict_choose_mine_full;
+
+ case svn_client_conflict_option_incoming_text_where_conflicted:
+ return svn_wc_conflict_choose_theirs_conflict;
+
+ case svn_client_conflict_option_working_text_where_conflicted:
+ return svn_wc_conflict_choose_mine_conflict;
+
+ case svn_client_conflict_option_merged_text:
+ return svn_wc_conflict_choose_merged;
+
+ case svn_client_conflict_option_unspecified:
+ return svn_wc_conflict_choose_unspecified;
+
+ default:
+ break;
+ }
+
+ return svn_wc_conflict_choose_undefined;
+}
+
+static void
+add_legacy_desc_to_conflict(const svn_wc_conflict_description2_t *desc,
+ svn_client_conflict_t *conflict,
+ apr_pool_t *result_pool)
+{
+ switch (desc->kind)
+ {
+ case svn_wc_conflict_kind_text:
+ conflict->legacy_text_conflict = desc;
+ break;
+
+ case svn_wc_conflict_kind_property:
+ if (conflict->prop_conflicts == NULL)
+ conflict->prop_conflicts = apr_hash_make(result_pool);
+ svn_hash_sets(conflict->prop_conflicts, desc->property_name, desc);
+ conflict->legacy_prop_conflict_propname = desc->property_name;
+ break;
+
+ case svn_wc_conflict_kind_tree:
+ conflict->legacy_tree_conflict = desc;
+ break;
+
+ default:
+ SVN_ERR_ASSERT_NO_RETURN(FALSE); /* unknown kind of conflict */
+ }
+}
+
+/* A map for svn_wc_conflict_action_t values to strings */
+static const svn_token_map_t map_conflict_action[] =
+{
+ { "edit", svn_wc_conflict_action_edit },
+ { "delete", svn_wc_conflict_action_delete },
+ { "add", svn_wc_conflict_action_add },
+ { "replace", svn_wc_conflict_action_replace },
+ { NULL, 0 }
+};
+
+/* A map for svn_wc_conflict_reason_t values to strings */
+static const svn_token_map_t map_conflict_reason[] =
+{
+ { "edit", svn_wc_conflict_reason_edited },
+ { "delete", svn_wc_conflict_reason_deleted },
+ { "missing", svn_wc_conflict_reason_missing },
+ { "obstruction", svn_wc_conflict_reason_obstructed },
+ { "add", svn_wc_conflict_reason_added },
+ { "replace", svn_wc_conflict_reason_replaced },
+ { "unversioned", svn_wc_conflict_reason_unversioned },
+ { "moved-away", svn_wc_conflict_reason_moved_away },
+ { "moved-here", svn_wc_conflict_reason_moved_here },
+ { NULL, 0 }
+};
+
+/* Describes a server-side move (really a copy+delete within the same
+ * revision) which was identified by scanning the revision log.
+ * This structure can represent one or more "chains" of moves, i.e.
+ * multiple move operations which occurred across a range of revisions. */
+struct repos_move_info {
+ /* The revision in which this move was committed. */
+ svn_revnum_t rev;
+
+ /* The author who commited the revision in which this move was committed. */
+ const char *rev_author;
+
+ /* The repository relpath the node was moved from in this revision. */
+ const char *moved_from_repos_relpath;
+
+ /* The repository relpath the node was moved to in this revision. */
+ const char *moved_to_repos_relpath;
+
+ /* The copyfrom revision of the moved-to path. */
+ svn_revnum_t copyfrom_rev;
+
+ /* The node kind of the item being moved. */
+ svn_node_kind_t node_kind;
+
+ /* Prev pointer. NULL if no prior move exists in the chain. */
+ struct repos_move_info *prev;
+
+ /* An array of struct repos_move_info * elements, each representing
+ * a possible way forward in the move chain. NULL if no next move
+ * exists in this chain. If the deleted node was copied only once in
+ * this revision, then this array has only one element and the move
+ * chain does not fork. But if this revision contains multiple copies of
+ * the deleted node, each of these copies appears as an element of this
+ * array, and each element represents a different path the next move
+ * might have taken. */
+ apr_array_header_t *next;
+};
+
+static svn_revnum_t
+rev_below(svn_revnum_t rev)
+{
+ SVN_ERR_ASSERT_NO_RETURN(rev != SVN_INVALID_REVNUM);
+ SVN_ERR_ASSERT_NO_RETURN(rev > 0);
+
+ return rev == 1 ? 1 : rev - 1;
+}
+
+/* Set *RELATED to true if the deleted node DELETED_REPOS_RELPATH@DELETED_REV
+ * is an ancestor of the copied node COPYFROM_PATH@COPYFROM_REV.
+ * If CHECK_LAST_CHANGED_REV is non-zero, also ensure that the copied node
+ * is a copy of the deleted node's last-changed revision's content, rather
+ * than a copy of some older content. If it's not, set *RELATED to false. */
+static svn_error_t *
+check_move_ancestry(svn_boolean_t *related,
+ svn_ra_session_t *ra_session,
+ const char *repos_root_url,
+ const char *deleted_repos_relpath,
+ svn_revnum_t deleted_rev,
+ const char *copyfrom_path,
+ svn_revnum_t copyfrom_rev,
+ svn_boolean_t check_last_changed_rev,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_t *locations;
+ const char *deleted_url;
+ const char *deleted_location;
+ apr_array_header_t *location_revisions;
+ const char *old_session_url;
+
+ location_revisions = apr_array_make(scratch_pool, 1, sizeof(svn_revnum_t));
+ APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = copyfrom_rev;
+ deleted_url = svn_uri_canonicalize(apr_pstrcat(scratch_pool,
+ repos_root_url, "/",
+ deleted_repos_relpath,
+ NULL),
+ scratch_pool);
+ SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session,
+ deleted_url, scratch_pool));
+ SVN_ERR(svn_ra_get_locations(ra_session, &locations, "",
+ rev_below(deleted_rev), location_revisions,
+ scratch_pool));
+
+ deleted_location = apr_hash_get(locations, &copyfrom_rev,
+ sizeof(svn_revnum_t));
+ if (deleted_location)
+ {
+ if (deleted_location[0] == '/')
+ deleted_location++;
+ if (strcmp(deleted_location, copyfrom_path) != 0)
+ {
+ *related = FALSE;
+ return SVN_NO_ERROR;
+ }
+ }
+ else
+ {
+ *related = FALSE;
+ return SVN_NO_ERROR;
+ }
+
+ if (check_last_changed_rev)
+ {
+ svn_dirent_t *dirent;
+
+ /* Verify that copyfrom_rev >= last-changed revision of the
+ * deleted node. */
+ SVN_ERR(svn_ra_stat(ra_session, "", rev_below(deleted_rev), &dirent,
+ scratch_pool));
+ if (dirent == NULL || copyfrom_rev < dirent->created_rev)
+ {
+ *related = FALSE;
+ return SVN_NO_ERROR;
+ }
+ }
+
+ *related = TRUE;
+ return SVN_NO_ERROR;
+}
+
+struct copy_info {
+ const char *copyto_path;
+ const char *copyfrom_path;
+ svn_revnum_t copyfrom_rev;
+ svn_node_kind_t node_kind;
+};
+
+/* Allocate and return a NEW_MOVE, and update MOVED_PATHS with this new move. */
+static svn_error_t *
+add_new_move(struct repos_move_info **new_move,
+ const char *deleted_repos_relpath,
+ const char *copyto_path,
+ svn_revnum_t copyfrom_rev,
+ svn_node_kind_t node_kind,
+ svn_revnum_t revision,
+ const char *author,
+ apr_hash_t *moved_paths,
+ svn_ra_session_t *ra_session,
+ const char *repos_root_url,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct repos_move_info *move;
+ struct repos_move_info *next_move;
+
+ move = apr_pcalloc(result_pool, sizeof(*move));
+ move->moved_from_repos_relpath = apr_pstrdup(result_pool,
+ deleted_repos_relpath);
+ move->moved_to_repos_relpath = apr_pstrdup(result_pool, copyto_path);
+ move->rev = revision;
+ move->rev_author = apr_pstrdup(result_pool, author);
+ move->copyfrom_rev = copyfrom_rev;
+ move->node_kind = node_kind;
+
+ /* Link together multiple moves of the same node.
+ * Note that we're traversing history backwards, so moves already
+ * present in the list happened in younger revisions. */
+ next_move = svn_hash_gets(moved_paths, move->moved_to_repos_relpath);
+ if (next_move)
+ {
+ svn_boolean_t related;
+
+ /* Tracing back history of the delete-half of the next move
+ * to the copyfrom-revision of the prior move we must end up
+ * at the delete-half of the prior move. */
+ SVN_ERR(check_move_ancestry(&related, ra_session, repos_root_url,
+ next_move->moved_from_repos_relpath,
+ next_move->rev,
+ move->moved_from_repos_relpath,
+ move->copyfrom_rev,
+ FALSE, scratch_pool));
+ if (related)
+ {
+ SVN_ERR_ASSERT(move->rev < next_move->rev);
+
+ /* Prepend this move to the linked list. */
+ if (move->next == NULL)
+ move->next = apr_array_make(result_pool, 1,
+ sizeof (struct repos_move_info *));
+ APR_ARRAY_PUSH(move->next, struct repos_move_info *) = next_move;
+ next_move->prev = move;
+ }
+ }
+
+ /* Make this move the head of our next-move linking map. */
+ svn_hash_sets(moved_paths, move->moved_from_repos_relpath, move);
+
+ *new_move = move;
+ return SVN_NO_ERROR;
+}
+
+/* Push a MOVE into the MOVES_TABLE. */
+static void
+push_move(struct repos_move_info *move, apr_hash_t *moves_table,
+ apr_pool_t *result_pool)
+{
+ apr_array_header_t *moves;
+
+ /* Add this move to the list of moves in the revision. */
+ moves = apr_hash_get(moves_table, &move->rev, sizeof(svn_revnum_t));
+ if (moves == NULL)
+ {
+ /* It is the first move in this revision. Create the list. */
+ moves = apr_array_make(result_pool, 1, sizeof(struct repos_move_info *));
+ apr_hash_set(moves_table, &move->rev, sizeof(svn_revnum_t), moves);
+ }
+ APR_ARRAY_PUSH(moves, struct repos_move_info *) = move;
+}
+
+/* Find the youngest common ancestor of REPOS_RELPATH1@PEG_REV1 and
+ * REPOS_RELPATH2@PEG_REV2. Return the result in *YCA_LOC.
+ * Set *YCA_LOC to NULL if no common ancestor exists. */
+static svn_error_t *
+find_yca(svn_client__pathrev_t **yca_loc,
+ const char *repos_relpath1,
+ svn_revnum_t peg_rev1,
+ const char *repos_relpath2,
+ svn_revnum_t peg_rev2,
+ const char *repos_root_url,
+ const char *repos_uuid,
+ svn_ra_session_t *ra_session,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_client__pathrev_t *loc1;
+ svn_client__pathrev_t *loc2;
+
+ *yca_loc = NULL;
+
+ loc1 = svn_client__pathrev_create_with_relpath(repos_root_url, repos_uuid,
+ peg_rev1, repos_relpath1,
+ scratch_pool);
+ loc2 = svn_client__pathrev_create_with_relpath(repos_root_url, repos_uuid,
+ peg_rev2, repos_relpath2,
+ scratch_pool);
+ SVN_ERR(svn_client__get_youngest_common_ancestor(yca_loc, loc1, loc2,
+ ra_session, ctx,
+ result_pool, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* Like find_yca, expect that a YCA could also be found via a brute-force
+ * search of parents of REPOS_RELPATH1 and REPOS_RELPATH2, if no "direct"
+ * YCA exists. An implicit assumption is that some parent of REPOS_RELPATH1
+ * is a branch of some parent of REPOS_RELPATH2.
+ *
+ * This function can guess a "good enough" YCA for 'missing nodes' which do
+ * not exist in the working copy, e.g. when a file edit is merged to a path
+ * which does not exist in the working copy.
+ */
+static svn_error_t *
+find_nearest_yca(svn_client__pathrev_t **yca_locp,
+ const char *repos_relpath1,
+ svn_revnum_t peg_rev1,
+ const char *repos_relpath2,
+ svn_revnum_t peg_rev2,
+ const char *repos_root_url,
+ const char *repos_uuid,
+ svn_ra_session_t *ra_session,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_client__pathrev_t *yca_loc;
+ svn_error_t *err;
+ apr_pool_t *iterpool;
+ const char *p1, *p2;
+ apr_size_t c1, c2;
+
+ *yca_locp = NULL;
+
+ iterpool = svn_pool_create(scratch_pool);
+
+ p1 = repos_relpath1;
+ c1 = svn_path_component_count(repos_relpath1);
+ while (c1--)
+ {
+ svn_pool_clear(iterpool);
+
+ p2 = repos_relpath2;
+ c2 = svn_path_component_count(repos_relpath2);
+ while (c2--)
+ {
+ err = find_yca(&yca_loc, p1, peg_rev1, p2, peg_rev2,
+ repos_root_url, repos_uuid, ra_session, ctx,
+ result_pool, iterpool);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ yca_loc = NULL;
+ }
+ else
+ return svn_error_trace(err);
+ }
+
+ if (yca_loc)
+ {
+ *yca_locp = yca_loc;
+ svn_pool_destroy(iterpool);
+ return SVN_NO_ERROR;
+ }
+
+ p2 = svn_relpath_dirname(p2, scratch_pool);
+ }
+
+ p1 = svn_relpath_dirname(p1, scratch_pool);
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+/* Check if the copied node described by COPY and the DELETED_PATH@DELETED_REV
+ * share a common ancestor. If so, return new repos_move_info in *MOVE which
+ * describes a move from the deleted path to that copy's destination. */
+static svn_error_t *
+find_related_move(struct repos_move_info **move,
+ struct copy_info *copy,
+ const char *deleted_repos_relpath,
+ svn_revnum_t deleted_rev,
+ const char *author,
+ apr_hash_t *moved_paths,
+ const char *repos_root_url,
+ const char *repos_uuid,
+ svn_client_ctx_t *ctx,
+ svn_ra_session_t *ra_session,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_client__pathrev_t *yca_loc;
+ svn_error_t *err;
+
+ *move = NULL;
+ err = find_yca(&yca_loc, copy->copyfrom_path, copy->copyfrom_rev,
+ deleted_repos_relpath, rev_below(deleted_rev),
+ repos_root_url, repos_uuid, ra_session, ctx,
+ scratch_pool, scratch_pool);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ yca_loc = NULL;
+ }
+ else
+ return svn_error_trace(err);
+ }
+
+ if (yca_loc)
+ SVN_ERR(add_new_move(move, deleted_repos_relpath,
+ copy->copyto_path, copy->copyfrom_rev,
+ copy->node_kind, deleted_rev, author,
+ moved_paths, ra_session, repos_root_url,
+ result_pool, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* Detect moves by matching DELETED_REPOS_RELPATH@DELETED_REV to the copies
+ * in COPIES. Add any moves found to MOVES_TABLE and update MOVED_PATHS. */
+static svn_error_t *
+match_copies_to_deletion(const char *deleted_repos_relpath,
+ svn_revnum_t deleted_rev,
+ const char *author,
+ apr_hash_t *copies,
+ apr_hash_t *moves_table,
+ apr_hash_t *moved_paths,
+ const char *repos_root_url,
+ const char *repos_uuid,
+ svn_ra_session_t *ra_session,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_index_t *hi;
+ apr_pool_t *iterpool;
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (hi = apr_hash_first(scratch_pool, copies);
+ hi != NULL;
+ hi = apr_hash_next(hi))
+ {
+ const char *copyfrom_path = apr_hash_this_key(hi);
+ apr_array_header_t *copies_with_same_source_path;
+ int i;
+
+ svn_pool_clear(iterpool);
+
+ copies_with_same_source_path = apr_hash_this_val(hi);
+
+ if (strcmp(copyfrom_path, deleted_repos_relpath) == 0)
+ {
+ /* We found a copyfrom path which matches a deleted node.
+ * Check if the deleted node is an ancestor of the copied node. */
+ for (i = 0; i < copies_with_same_source_path->nelts; i++)
+ {
+ struct copy_info *copy;
+ svn_boolean_t related;
+ struct repos_move_info *move;
+
+ copy = APR_ARRAY_IDX(copies_with_same_source_path, i,
+ struct copy_info *);
+ SVN_ERR(check_move_ancestry(&related,
+ ra_session, repos_root_url,
+ deleted_repos_relpath,
+ deleted_rev,
+ copy->copyfrom_path,
+ copy->copyfrom_rev,
+ TRUE, iterpool));
+ if (!related)
+ continue;
+
+ /* Remember details of this move. */
+ SVN_ERR(add_new_move(&move, deleted_repos_relpath,
+ copy->copyto_path, copy->copyfrom_rev,
+ copy->node_kind, deleted_rev, author,
+ moved_paths, ra_session, repos_root_url,
+ result_pool, iterpool));
+ push_move(move, moves_table, result_pool);
+ }
+ }
+ else
+ {
+ /* Check if this deleted node is related to any copies in this
+ * revision. These could be moves of the deleted node which
+ * were merged here from other lines of history. */
+ for (i = 0; i < copies_with_same_source_path->nelts; i++)
+ {
+ struct copy_info *copy;
+ struct repos_move_info *move = NULL;
+
+ copy = APR_ARRAY_IDX(copies_with_same_source_path, i,
+ struct copy_info *);
+ SVN_ERR(find_related_move(&move, copy, deleted_repos_relpath,
+ deleted_rev, author,
+ moved_paths,
+ repos_root_url, repos_uuid,
+ ctx, ra_session,
+ result_pool, iterpool));
+ if (move)
+ push_move(move, moves_table, result_pool);
+ }
+ }
+ }
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+/* Update MOVES_TABLE and MOVED_PATHS based on information from
+ * revision data in LOG_ENTRY, COPIES, and DELETED_PATHS.
+ * Use RA_SESSION to perform the necessary requests. */
+static svn_error_t *
+find_moves_in_revision(svn_ra_session_t *ra_session,
+ apr_hash_t *moves_table,
+ apr_hash_t *moved_paths,
+ svn_log_entry_t *log_entry,
+ apr_hash_t *copies,
+ apr_array_header_t *deleted_paths,
+ const char *repos_root_url,
+ const char *repos_uuid,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *iterpool;
+ int i;
+ const svn_string_t *author;
+
+ author = svn_hash_gets(log_entry->revprops, SVN_PROP_REVISION_AUTHOR);
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < deleted_paths->nelts; i++)
+ {
+ const char *deleted_repos_relpath;
+
+ svn_pool_clear(iterpool);
+
+ deleted_repos_relpath = APR_ARRAY_IDX(deleted_paths, i, const char *);
+ SVN_ERR(match_copies_to_deletion(deleted_repos_relpath,
+ log_entry->revision,
+ author ? author->data
+ : _("unknown author"),
+ copies, moves_table, moved_paths,
+ repos_root_url, repos_uuid, ra_session,
+ ctx, result_pool, iterpool));
+ }
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+struct find_deleted_rev_baton
+{
+ /* Variables below are arguments provided by the caller of
+ * svn_ra_get_log2(). */
+ const char *deleted_repos_relpath;
+ const char *related_repos_relpath;
+ svn_revnum_t related_peg_rev;
+ const char *repos_root_url;
+ const char *repos_uuid;
+ svn_client_ctx_t *ctx;
+ const char *victim_abspath; /* for notifications */
+
+ /* Variables below are results for the caller of svn_ra_get_log2(). */
+ svn_revnum_t deleted_rev;
+ const char *deleted_rev_author;
+ svn_node_kind_t replacing_node_kind;
+ apr_pool_t *result_pool;
+
+ apr_hash_t *moves_table; /* Obtained from find_moves_in_revision(). */
+ struct repos_move_info *move; /* Last known move which affected the node. */
+
+ /* Extra RA session that can be used to make additional requests. */
+ svn_ra_session_t *extra_ra_session;
+};
+
+/* If DELETED_RELPATH matches the moved-from path of a move in MOVES,
+ * or if DELETED_RELPATH is a child of a moved-to path in MOVES, return
+ * a struct move_info for the corresponding move. Else, return NULL. */
+static struct repos_move_info *
+map_deleted_path_to_move(const char *deleted_relpath,
+ apr_array_header_t *moves,
+ apr_pool_t *scratch_pool)
+{
+ struct repos_move_info *closest_move = NULL;
+ apr_size_t min_components = 0;
+ int i;
+
+ for (i = 0; i < moves->nelts; i++)
+ {
+ const char *relpath;
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(moves, i, struct repos_move_info *);
+ if (strcmp(move->moved_from_repos_relpath, deleted_relpath) == 0)
+ return move;
+
+ relpath = svn_relpath_skip_ancestor(move->moved_to_repos_relpath,
+ deleted_relpath);
+ if (relpath)
+ {
+ /* This could be a nested move. Return the path-wise closest move. */
+ const apr_size_t c = svn_path_component_count(relpath);
+ if (c == 0)
+ return move;
+ else if (min_components == 0 || c < min_components)
+ {
+ min_components = c;
+ closest_move = move;
+ }
+ }
+ }
+
+ if (closest_move)
+ {
+ const char *relpath;
+ const char *moved_along_path;
+ struct repos_move_info *move;
+
+ /* See if we can find an even closer move for this moved-along path. */
+ relpath = svn_relpath_skip_ancestor(closest_move->moved_to_repos_relpath,
+ deleted_relpath);
+ moved_along_path =
+ svn_relpath_join(closest_move->moved_from_repos_relpath, relpath,
+ scratch_pool);
+ move = map_deleted_path_to_move(moved_along_path, moves, scratch_pool);
+ if (move)
+ return move;
+ }
+
+ return closest_move;
+}
+
+/* Search for nested moves in REVISION, given the already found MOVES,
+ * all DELETED_PATHS, and all COPIES, from the same revision.
+ * Append any nested moves to the MOVES array. */
+static svn_error_t *
+find_nested_moves(apr_array_header_t *moves,
+ apr_hash_t *copies,
+ apr_array_header_t *deleted_paths,
+ apr_hash_t *moved_paths,
+ svn_revnum_t revision,
+ const char *author,
+ const char *repos_root_url,
+ const char *repos_uuid,
+ svn_ra_session_t *ra_session,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *nested_moves;
+ int i;
+ apr_pool_t *iterpool;
+
+ nested_moves = apr_array_make(result_pool, 0,
+ sizeof(struct repos_move_info *));
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < deleted_paths->nelts; i++)
+ {
+ const char *deleted_path;
+ const char *child_relpath;
+ const char *moved_along_repos_relpath;
+ struct repos_move_info *move;
+ apr_array_header_t *copies_with_same_source_path;
+ int j;
+ svn_boolean_t related;
+
+ svn_pool_clear(iterpool);
+
+ deleted_path = APR_ARRAY_IDX(deleted_paths, i, const char *);
+ move = map_deleted_path_to_move(deleted_path, moves, iterpool);
+ if (move == NULL)
+ continue;
+ child_relpath = svn_relpath_skip_ancestor(move->moved_to_repos_relpath,
+ deleted_path);
+ if (child_relpath == NULL || child_relpath[0] == '\0')
+ continue; /* not a nested move */
+
+ /* Consider: svn mv A B; svn mv B/foo C/foo
+ * Copyfrom for C/foo is A/foo, even though C/foo was moved here from
+ * B/foo. A/foo was not deleted. It is B/foo which was deleted.
+ * We now know about the move A->B and moved-along child_relpath "foo".
+ * Try to detect an ancestral relationship between A/foo and the
+ * moved-along path. */
+ moved_along_repos_relpath =
+ svn_relpath_join(move->moved_from_repos_relpath, child_relpath,
+ iterpool);
+ copies_with_same_source_path = svn_hash_gets(copies,
+ moved_along_repos_relpath);
+ if (copies_with_same_source_path == NULL)
+ continue; /* not a nested move */
+
+ for (j = 0; j < copies_with_same_source_path->nelts; j++)
+ {
+ struct copy_info *copy;
+
+ copy = APR_ARRAY_IDX(copies_with_same_source_path, j,
+ struct copy_info *);
+ SVN_ERR(check_move_ancestry(&related, ra_session, repos_root_url,
+ moved_along_repos_relpath,
+ revision,
+ copy->copyfrom_path,
+ copy->copyfrom_rev,
+ TRUE, iterpool));
+ if (related)
+ {
+ struct repos_move_info *nested_move;
+
+ /* Remember details of this move. */
+ SVN_ERR(add_new_move(&nested_move, moved_along_repos_relpath,
+ copy->copyto_path, copy->copyfrom_rev,
+ copy->node_kind,
+ revision, author, moved_paths,
+ ra_session, repos_root_url,
+ result_pool, iterpool));
+
+ /* Add this move to the list of nested moves in this revision. */
+ APR_ARRAY_PUSH(nested_moves, struct repos_move_info *) =
+ nested_move;
+ }
+ }
+ }
+ svn_pool_destroy(iterpool);
+
+ /* Add all nested moves found to the list of all moves in this revision. */
+ apr_array_cat(moves, nested_moves);
+
+ return SVN_NO_ERROR;
+}
+
+/* Make a shallow copy of the copied LOG_ITEM in COPIES. */
+static void
+cache_copied_item(apr_hash_t *copies, const char *changed_path,
+ svn_log_changed_path2_t *log_item)
+{
+ apr_pool_t *result_pool = apr_hash_pool_get(copies);
+ struct copy_info *copy = apr_palloc(result_pool, sizeof(*copy));
+ apr_array_header_t *copies_with_same_source_path;
+
+ copy->copyfrom_path = log_item->copyfrom_path;
+ if (log_item->copyfrom_path[0] == '/')
+ copy->copyfrom_path++;
+ copy->copyto_path = changed_path;
+ copy->copyfrom_rev = log_item->copyfrom_rev;
+ copy->node_kind = log_item->node_kind;
+
+ copies_with_same_source_path = apr_hash_get(copies, copy->copyfrom_path,
+ APR_HASH_KEY_STRING);
+ if (copies_with_same_source_path == NULL)
+ {
+ copies_with_same_source_path = apr_array_make(result_pool, 1,
+ sizeof(struct copy_info *));
+ apr_hash_set(copies, copy->copyfrom_path, APR_HASH_KEY_STRING,
+ copies_with_same_source_path);
+ }
+ APR_ARRAY_PUSH(copies_with_same_source_path, struct copy_info *) = copy;
+}
+
+/* Implements svn_log_entry_receiver_t.
+ *
+ * Find the revision in which a node, optionally ancestrally related to the
+ * node specified via find_deleted_rev_baton, was deleted, When the revision
+ * was found, store it in BATON->DELETED_REV and abort the log operation
+ * by raising SVN_ERR_CEASE_INVOCATION.
+ *
+ * If no such revision can be found, leave BATON->DELETED_REV and
+ * BATON->REPLACING_NODE_KIND alone.
+ *
+ * If the node was replaced, set BATON->REPLACING_NODE_KIND to the node
+ * kind of the node which replaced the original node. If the node was not
+ * replaced, set BATON->REPLACING_NODE_KIND to svn_node_none.
+ *
+ * This function answers the same question as svn_ra_get_deleted_rev() but
+ * works in cases where we do not already know a revision in which the deleted
+ * node once used to exist.
+ *
+ * If the node was moved, rather than deleted, return move information
+ * in BATON->MOVE.
+ */
+static svn_error_t *
+find_deleted_rev(void *baton,
+ svn_log_entry_t *log_entry,
+ apr_pool_t *scratch_pool)
+{
+ struct find_deleted_rev_baton *b = baton;
+ apr_hash_index_t *hi;
+ apr_pool_t *iterpool;
+ svn_boolean_t deleted_node_found = FALSE;
+ svn_node_kind_t replacing_node_kind = svn_node_none;
+
+ if (b->ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify;
+
+ notify = svn_wc_create_notify(
+ b->victim_abspath,
+ svn_wc_notify_tree_conflict_details_progress,
+ scratch_pool),
+ notify->revision = log_entry->revision;
+ b->ctx->notify_func2(b->ctx->notify_baton2, notify, scratch_pool);
+ }
+
+ /* No paths were changed in this revision. Nothing to do. */
+ if (! log_entry->changed_paths2)
+ return SVN_NO_ERROR;
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (hi = apr_hash_first(scratch_pool, log_entry->changed_paths2);
+ hi != NULL;
+ hi = apr_hash_next(hi))
+ {
+ const char *changed_path = apr_hash_this_key(hi);
+ svn_log_changed_path2_t *log_item = apr_hash_this_val(hi);
+
+ svn_pool_clear(iterpool);
+
+ /* ### Remove leading slash from paths in log entries. */
+ if (changed_path[0] == '/')
+ changed_path++;
+
+ /* Check if we already found the deleted node we're looking for. */
+ if (!deleted_node_found &&
+ svn_path_compare_paths(b->deleted_repos_relpath, changed_path) == 0 &&
+ (log_item->action == 'D' || log_item->action == 'R'))
+ {
+ deleted_node_found = TRUE;
+
+ if (b->related_repos_relpath != NULL &&
+ b->related_peg_rev != SVN_INVALID_REVNUM)
+ {
+ svn_client__pathrev_t *yca_loc;
+ svn_error_t *err;
+
+ /* We found a deleted node which occupies the correct path.
+ * To be certain that this is the deleted node we're looking for,
+ * we must establish whether it is ancestrally related to the
+ * "related node" specified in our baton. */
+ err = find_yca(&yca_loc,
+ b->related_repos_relpath,
+ b->related_peg_rev,
+ b->deleted_repos_relpath,
+ rev_below(log_entry->revision),
+ b->repos_root_url, b->repos_uuid,
+ b->extra_ra_session, b->ctx, iterpool, iterpool);
+ if (err)
+ {
+ /* ### Happens for moves within other moves and copies. */
+ if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ yca_loc = NULL;
+ }
+ else
+ return svn_error_trace(err);
+ }
+
+ deleted_node_found = (yca_loc != NULL);
+ }
+
+ if (deleted_node_found && log_item->action == 'R')
+ replacing_node_kind = log_item->node_kind;
+ }
+ }
+ svn_pool_destroy(iterpool);
+
+ if (!deleted_node_found)
+ {
+ apr_array_header_t *moves;
+
+ moves = apr_hash_get(b->moves_table, &log_entry->revision,
+ sizeof(svn_revnum_t));
+ if (moves)
+ {
+ struct repos_move_info *move;
+
+ move = map_deleted_path_to_move(b->deleted_repos_relpath,
+ moves, scratch_pool);
+ if (move)
+ {
+ const char *relpath;
+
+ /* The node was moved. Update our search path accordingly. */
+ b->move = move;
+ relpath = svn_relpath_skip_ancestor(move->moved_to_repos_relpath,
+ b->deleted_repos_relpath);
+ if (relpath)
+ b->deleted_repos_relpath =
+ svn_relpath_join(move->moved_from_repos_relpath, relpath,
+ b->result_pool);
+ }
+ }
+ }
+ else
+ {
+ svn_string_t *author;
+
+ b->deleted_rev = log_entry->revision;
+ author = svn_hash_gets(log_entry->revprops,
+ SVN_PROP_REVISION_AUTHOR);
+ if (author)
+ b->deleted_rev_author = apr_pstrdup(b->result_pool, author->data);
+ else
+ b->deleted_rev_author = _("unknown author");
+
+ b->replacing_node_kind = replacing_node_kind;
+
+ /* We're done. Abort the log operation. */
+ return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Return a localised string representation of the local part of a tree
+ conflict on a file. */
+static svn_error_t *
+describe_local_file_node_change(const char **description,
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc_conflict_reason_t local_change;
+ svn_wc_operation_t operation;
+
+ local_change = svn_client_conflict_get_local_change(conflict);
+ operation = svn_client_conflict_get_operation(conflict);
+
+ switch (local_change)
+ {
+ case svn_wc_conflict_reason_edited:
+ if (operation == svn_wc_operation_update ||
+ operation == svn_wc_operation_switch)
+ *description = _("A file containing uncommitted changes was "
+ "found in the working copy.");
+ else if (operation == svn_wc_operation_merge)
+ *description = _("A file which differs from the corresponding "
+ "file on the merge source branch was found "
+ "in the working copy.");
+ break;
+ case svn_wc_conflict_reason_obstructed:
+ *description = _("A file which already occupies this path was found "
+ "in the working copy.");
+ break;
+ case svn_wc_conflict_reason_unversioned:
+ *description = _("An unversioned file was found in the working "
+ "copy.");
+ break;
+ case svn_wc_conflict_reason_deleted:
+ *description = _("A deleted file was found in the working copy.");
+ break;
+ case svn_wc_conflict_reason_missing:
+ if (operation == svn_wc_operation_update ||
+ operation == svn_wc_operation_switch)
+ *description = _("No such file was found in the working copy.");
+ else if (operation == svn_wc_operation_merge)
+ {
+ /* ### display deleted revision */
+ *description = _("No such file was found in the merge target "
+ "working copy.\nPerhaps the file has been "
+ "deleted or moved away in the repository's "
+ "history?");
+ }
+ break;
+ case svn_wc_conflict_reason_added:
+ case svn_wc_conflict_reason_replaced:
+ {
+ /* ### show more details about copies or replacements? */
+ *description = _("A file scheduled to be added to the "
+ "repository in the next commit was found in "
+ "the working copy.");
+ }
+ break;
+ case svn_wc_conflict_reason_moved_away:
+ {
+ const char *moved_to_abspath;
+ svn_error_t *err;
+
+ err = svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
+ ctx->wc_ctx,
+ conflict->local_abspath,
+ scratch_pool,
+ scratch_pool);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+ {
+ moved_to_abspath = NULL;
+ svn_error_clear(err);
+ }
+ else
+ return svn_error_trace(err);
+ }
+ if (operation == svn_wc_operation_update ||
+ operation == svn_wc_operation_switch)
+ {
+ if (moved_to_abspath == NULL)
+ {
+ /* The move no longer exists. */
+ *description = _("The file in the working copy had "
+ "been moved away at the time this "
+ "conflict was recorded.");
+ }
+ else
+ {
+ const char *wcroot_abspath;
+
+ SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath,
+ ctx->wc_ctx,
+ conflict->local_abspath,
+ scratch_pool,
+ scratch_pool));
+ *description = apr_psprintf(
+ result_pool,
+ _("The file in the working copy was "
+ "moved away to\n'%s'."),
+ svn_dirent_local_style(
+ svn_dirent_skip_ancestor(
+ wcroot_abspath,
+ moved_to_abspath),
+ scratch_pool));
+ }
+ }
+ else if (operation == svn_wc_operation_merge)
+ {
+ if (moved_to_abspath == NULL)
+ {
+ /* The move probably happened in branch history.
+ * This case cannot happen until we detect incoming
+ * moves, which we currently don't do. */
+ /* ### find deleted/moved revision? */
+ *description = _("The file in the working copy had "
+ "been moved away at the time this "
+ "conflict was recorded.");
+ }
+ else
+ {
+ /* This is a local move in the working copy. */
+ const char *wcroot_abspath;
+
+ SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath,
+ ctx->wc_ctx,
+ conflict->local_abspath,
+ scratch_pool,
+ scratch_pool));
+ *description = apr_psprintf(
+ result_pool,
+ _("The file in the working copy was "
+ "moved away to\n'%s'."),
+ svn_dirent_local_style(
+ svn_dirent_skip_ancestor(
+ wcroot_abspath,
+ moved_to_abspath),
+ scratch_pool));
+ }
+ }
+ break;
+ }
+ case svn_wc_conflict_reason_moved_here:
+ {
+ const char *moved_from_abspath;
+
+ SVN_ERR(svn_wc__node_was_moved_here(&moved_from_abspath, NULL,
+ ctx->wc_ctx,
+ conflict->local_abspath,
+ scratch_pool,
+ scratch_pool));
+ if (operation == svn_wc_operation_update ||
+ operation == svn_wc_operation_switch)
+ {
+ if (moved_from_abspath == NULL)
+ {
+ /* The move no longer exists. */
+ *description = _("A file had been moved here in the "
+ "working copy at the time this "
+ "conflict was recorded.");
+ }
+ else
+ {
+ const char *wcroot_abspath;
+
+ SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath,
+ ctx->wc_ctx,
+ conflict->local_abspath,
+ scratch_pool,
+ scratch_pool));
+ *description = apr_psprintf(
+ result_pool,
+ _("A file was moved here in the "
+ "working copy from\n'%s'."),
+ svn_dirent_local_style(
+ svn_dirent_skip_ancestor(
+ wcroot_abspath,
+ moved_from_abspath),
+ scratch_pool));
+ }
+ }
+ else if (operation == svn_wc_operation_merge)
+ {
+ if (moved_from_abspath == NULL)
+ {
+ /* The move probably happened in branch history.
+ * This case cannot happen until we detect incoming
+ * moves, which we currently don't do. */
+ /* ### find deleted/moved revision? */
+ *description = _("A file had been moved here in the "
+ "working copy at the time this "
+ "conflict was recorded.");
+ }
+ else
+ {
+ const char *wcroot_abspath;
+
+ SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath,
+ ctx->wc_ctx,
+ conflict->local_abspath,
+ scratch_pool,
+ scratch_pool));
+ /* This is a local move in the working copy. */
+ *description = apr_psprintf(
+ result_pool,
+ _("A file was moved here in the "
+ "working copy from\n'%s'."),
+ svn_dirent_local_style(
+ svn_dirent_skip_ancestor(
+ wcroot_abspath,
+ moved_from_abspath),
+ scratch_pool));
+ }
+ }
+ break;
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Return a localised string representation of the local part of a tree
+ conflict on a directory. */
+static svn_error_t *
+describe_local_dir_node_change(const char **description,
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc_conflict_reason_t local_change;
+ svn_wc_operation_t operation;
+
+ local_change = svn_client_conflict_get_local_change(conflict);
+ operation = svn_client_conflict_get_operation(conflict);
+
+ switch (local_change)
+ {
+ case svn_wc_conflict_reason_edited:
+ if (operation == svn_wc_operation_update ||
+ operation == svn_wc_operation_switch)
+ *description = _("A directory containing uncommitted changes "
+ "was found in the working copy.");
+ else if (operation == svn_wc_operation_merge)
+ *description = _("A directory which differs from the "
+ "corresponding directory on the merge source "
+ "branch was found in the working copy.");
+ break;
+ case svn_wc_conflict_reason_obstructed:
+ *description = _("A directory which already occupies this path was "
+ "found in the working copy.");
+ break;
+ case svn_wc_conflict_reason_unversioned:
+ *description = _("An unversioned directory was found in the "
+ "working copy.");
+ break;
+ case svn_wc_conflict_reason_deleted:
+ *description = _("A deleted directory was found in the "
+ "working copy.");
+ break;
+ case svn_wc_conflict_reason_missing:
+ if (operation == svn_wc_operation_update ||
+ operation == svn_wc_operation_switch)
+ *description = _("No such directory was found in the working copy.");
+ else if (operation == svn_wc_operation_merge)
+ {
+ /* ### display deleted revision */
+ *description = _("No such directory was found in the merge "
+ "target working copy.\nPerhaps the "
+ "directory has been deleted or moved away "
+ "in the repository's history?");
+ }
+ break;
+ case svn_wc_conflict_reason_added:
+ case svn_wc_conflict_reason_replaced:
+ {
+ /* ### show more details about copies or replacements? */
+ *description = _("A directory scheduled to be added to the "
+ "repository in the next commit was found in "
+ "the working copy.");
+ }
+ break;
+ case svn_wc_conflict_reason_moved_away:
+ {
+ const char *moved_to_abspath;
+ svn_error_t *err;
+
+ err = svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
+ ctx->wc_ctx,
+ conflict->local_abspath,
+ scratch_pool,
+ scratch_pool);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+ {
+ moved_to_abspath = NULL;
+ svn_error_clear(err);
+ }
+ else
+ return svn_error_trace(err);
+ }
+
+ if (operation == svn_wc_operation_update ||
+ operation == svn_wc_operation_switch)
+ {
+ if (moved_to_abspath == NULL)
+ {
+ /* The move no longer exists. */
+ *description = _("The directory in the working copy "
+ "had been moved away at the time "
+ "this conflict was recorded.");
+ }
+ else
+ {
+ const char *wcroot_abspath;
+
+ SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath,
+ ctx->wc_ctx,
+ conflict->local_abspath,
+ scratch_pool,
+ scratch_pool));
+ *description = apr_psprintf(
+ result_pool,
+ _("The directory in the working copy "
+ "was moved away to\n'%s'."),
+ svn_dirent_local_style(
+ svn_dirent_skip_ancestor(
+ wcroot_abspath,
+ moved_to_abspath),
+ scratch_pool));
+ }
+ }
+ else if (operation == svn_wc_operation_merge)
+ {
+ if (moved_to_abspath == NULL)
+ {
+ /* The move probably happened in branch history.
+ * This case cannot happen until we detect incoming
+ * moves, which we currently don't do. */
+ /* ### find deleted/moved revision? */
+ *description = _("The directory had been moved away "
+ "at the time this conflict was "
+ "recorded.");
+ }
+ else
+ {
+ /* This is a local move in the working copy. */
+ const char *wcroot_abspath;
+
+ SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath,
+ ctx->wc_ctx,
+ conflict->local_abspath,
+ scratch_pool,
+ scratch_pool));
+ *description = apr_psprintf(
+ result_pool,
+ _("The directory was moved away to\n"
+ "'%s'."),
+ svn_dirent_local_style(
+ svn_dirent_skip_ancestor(
+ wcroot_abspath,
+ moved_to_abspath),
+ scratch_pool));
+ }
+ }
+ }
+ break;
+ case svn_wc_conflict_reason_moved_here:
+ {
+ const char *moved_from_abspath;
+
+ SVN_ERR(svn_wc__node_was_moved_here(&moved_from_abspath, NULL,
+ ctx->wc_ctx,
+ conflict->local_abspath,
+ scratch_pool,
+ scratch_pool));
+ if (operation == svn_wc_operation_update ||
+ operation == svn_wc_operation_switch)
+ {
+ if (moved_from_abspath == NULL)
+ {
+ /* The move no longer exists. */
+ *description = _("A directory had been moved here at "
+ "the time this conflict was "
+ "recorded.");
+ }
+ else
+ {
+ const char *wcroot_abspath;
+
+ SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath,
+ ctx->wc_ctx,
+ conflict->local_abspath,
+ scratch_pool,
+ scratch_pool));
+ *description = apr_psprintf(
+ result_pool,
+ _("A directory was moved here from\n"
+ "'%s'."),
+ svn_dirent_local_style(
+ svn_dirent_skip_ancestor(
+ wcroot_abspath,
+ moved_from_abspath),
+ scratch_pool));
+ }
+ }
+ else if (operation == svn_wc_operation_merge)
+ {
+ if (moved_from_abspath == NULL)
+ {
+ /* The move probably happened in branch history.
+ * This case cannot happen until we detect incoming
+ * moves, which we currently don't do. */
+ /* ### find deleted/moved revision? */
+ *description = _("A directory had been moved here at "
+ "the time this conflict was "
+ "recorded.");
+ }
+ else
+ {
+ /* This is a local move in the working copy. */
+ const char *wcroot_abspath;
+
+ SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath,
+ ctx->wc_ctx,
+ conflict->local_abspath,
+ scratch_pool,
+ scratch_pool));
+ *description = apr_psprintf(
+ result_pool,
+ _("A directory was moved here in "
+ "the working copy from\n'%s'."),
+ svn_dirent_local_style(
+ svn_dirent_skip_ancestor(
+ wcroot_abspath,
+ moved_from_abspath),
+ scratch_pool));
+ }
+ }
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+struct find_moves_baton
+{
+ /* Variables below are arguments provided by the caller of
+ * svn_ra_get_log2(). */
+ const char *repos_root_url;
+ const char *repos_uuid;
+ svn_client_ctx_t *ctx;
+ const char *victim_abspath; /* for notifications */
+ apr_pool_t *result_pool;
+
+ /* A hash table mapping a revision number to an array of struct
+ * repos_move_info * elements, describing moves.
+ *
+ * Must be allocated in RESULT_POOL by the caller of svn_ra_get_log2().
+ *
+ * If the node was moved, the DELETED_REV is present in this table,
+ * perhaps along with additional revisions.
+ *
+ * Given a sequence of moves which happened in the repository, such as:
+ * rA: mv x->z
+ * rA: mv a->b
+ * rB: mv b->c
+ * rC: mv c->d
+ * we map each revision number to all the moves which happened in the
+ * revision, which looks as follows:
+ * rA : [(x->z), (a->b)]
+ * rB : [(b->c)]
+ * rC : [(c->d)]
+ * This allows us to later find relevant moves based on a revision number.
+ *
+ * Additionally, we embed the number of the revision in which a move was
+ * found inside the repos_move_info structure:
+ * rA : [(rA, x->z), (rA, a->b)]
+ * rB : [(rB, b->c)]
+ * rC : [(rC, c->d)]
+ * And also, all moves pertaining to the same node are chained into a
+ * doubly-linked list via 'next' and 'prev' pointers (see definition of
+ * struct repos_move_info). This can be visualized as follows:
+ * rA : [(rA, x->z, prev=>NULL, next=>NULL),
+ * (rA, a->b, prev=>NULL, next=>(rB, b->c))]
+ * rB : [(rB, b->c), prev=>(rA, a->b), next=>(rC, c->d)]
+ * rC : [(rC, c->d), prev=>(rB, c->d), next=>NULL]
+ * This way, we can look up all moves relevant to a node, forwards and
+ * backwards in history, once we have located one move in the chain.
+ *
+ * In the above example, the data tells us that within the revision
+ * range rA:C, a was moved to d. However, within the revision range
+ * rA;B, a was moved to b.
+ */
+ apr_hash_t *moves_table;
+
+ /* Variables below hold state for find_moves() and are not
+ * intended to be used by the caller of svn_ra_get_log2().
+ * Like all other variables, they must be initialized, however. */
+
+ /* Temporary map of moved paths to struct repos_move_info.
+ * Used to link multiple moves of the same node across revisions. */
+ apr_hash_t *moved_paths;
+
+ /* Extra RA session that can be used to make additional requests. */
+ svn_ra_session_t *extra_ra_session;
+};
+
+/* Implements svn_log_entry_receiver_t. */
+static svn_error_t *
+find_moves(void *baton, svn_log_entry_t *log_entry, apr_pool_t *scratch_pool)
+{
+ struct find_moves_baton *b = baton;
+ apr_hash_index_t *hi;
+ apr_pool_t *iterpool;
+ apr_array_header_t *deleted_paths;
+ apr_hash_t *copies;
+ apr_array_header_t *moves;
+
+ if (b->ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify;
+
+ notify = svn_wc_create_notify(
+ b->victim_abspath,
+ svn_wc_notify_tree_conflict_details_progress,
+ scratch_pool),
+ notify->revision = log_entry->revision;
+ b->ctx->notify_func2(b->ctx->notify_baton2, notify, scratch_pool);
+ }
+
+ /* No paths were changed in this revision. Nothing to do. */
+ if (! log_entry->changed_paths2)
+ return SVN_NO_ERROR;
+
+ copies = apr_hash_make(scratch_pool);
+ deleted_paths = apr_array_make(scratch_pool, 0, sizeof(const char *));
+ iterpool = svn_pool_create(scratch_pool);
+ for (hi = apr_hash_first(scratch_pool, log_entry->changed_paths2);
+ hi != NULL;
+ hi = apr_hash_next(hi))
+ {
+ const char *changed_path = apr_hash_this_key(hi);
+ svn_log_changed_path2_t *log_item = apr_hash_this_val(hi);
+
+ svn_pool_clear(iterpool);
+
+ /* ### Remove leading slash from paths in log entries. */
+ if (changed_path[0] == '/')
+ changed_path++;
+
+ /* For move detection, scan for copied nodes in this revision. */
+ if (log_item->action == 'A' && log_item->copyfrom_path)
+ cache_copied_item(copies, changed_path, log_item);
+
+ /* For move detection, store all deleted_paths. */
+ if (log_item->action == 'D' || log_item->action == 'R')
+ APR_ARRAY_PUSH(deleted_paths, const char *) =
+ apr_pstrdup(scratch_pool, changed_path);
+ }
+ svn_pool_destroy(iterpool);
+
+ /* Check for moves in this revision */
+ SVN_ERR(find_moves_in_revision(b->extra_ra_session,
+ b->moves_table, b->moved_paths,
+ log_entry, copies, deleted_paths,
+ b->repos_root_url, b->repos_uuid,
+ b->ctx, b->result_pool, scratch_pool));
+
+ moves = apr_hash_get(b->moves_table, &log_entry->revision,
+ sizeof(svn_revnum_t));
+ if (moves)
+ {
+ const svn_string_t *author;
+
+ author = svn_hash_gets(log_entry->revprops, SVN_PROP_REVISION_AUTHOR);
+ SVN_ERR(find_nested_moves(moves, copies, deleted_paths,
+ b->moved_paths, log_entry->revision,
+ author ? author->data : _("unknown author"),
+ b->repos_root_url,
+ b->repos_uuid,
+ b->extra_ra_session, b->ctx,
+ b->result_pool, scratch_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Find all moves which occured in repository history starting at
+ * REPOS_RELPATH@START_REV until END_REV (where START_REV > END_REV).
+ * Return results in *MOVES_TABLE (see struct find_moves_baton for details). */
+static svn_error_t *
+find_moves_in_revision_range(struct apr_hash_t **moves_table,
+ const char *repos_relpath,
+ const char *repos_root_url,
+ const char *repos_uuid,
+ const char *victim_abspath,
+ svn_revnum_t start_rev,
+ svn_revnum_t end_rev,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_ra_session_t *ra_session;
+ const char *url;
+ const char *corrected_url;
+ apr_array_header_t *paths;
+ apr_array_header_t *revprops;
+ struct find_moves_baton b = { 0 };
+
+ SVN_ERR_ASSERT(start_rev > end_rev);
+
+ url = svn_path_url_add_component2(repos_root_url, repos_relpath,
+ scratch_pool);
+ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url,
+ url, NULL, NULL, FALSE, FALSE,
+ ctx, scratch_pool,
+ scratch_pool));
+
+ paths = apr_array_make(scratch_pool, 1, sizeof(const char *));
+ APR_ARRAY_PUSH(paths, const char *) = "";
+
+ revprops = apr_array_make(scratch_pool, 1, sizeof(const char *));
+ APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_AUTHOR;
+
+ b.repos_root_url = repos_root_url;
+ b.repos_uuid = repos_uuid;
+ b.ctx = ctx;
+ b.victim_abspath = victim_abspath;
+ b.moves_table = apr_hash_make(result_pool);
+ b.moved_paths = apr_hash_make(scratch_pool);
+ b.result_pool = result_pool;
+ SVN_ERR(svn_ra__dup_session(&b.extra_ra_session, ra_session, NULL,
+ scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_ra_get_log2(ra_session, paths, start_rev, end_rev,
+ 0, /* no limit */
+ TRUE, /* need the changed paths list */
+ FALSE, /* need to traverse copies */
+ FALSE, /* no need for merged revisions */
+ revprops,
+ find_moves, &b,
+ scratch_pool));
+
+ *moves_table = b.moves_table;
+
+ return SVN_NO_ERROR;
+}
+
+/* Return new move information for a moved-along child MOVED_ALONG_RELPATH.
+ * Set MOVE->NODE_KIND to MOVED_ALONG_NODE_KIND.
+ * Do not copy MOVE->NEXT and MOVE-PREV.
+ * If MOVED_ALONG_RELPATH is empty, this effectively copies MOVE to
+ * RESULT_POOL with NEXT and PREV pointers cleared. */
+static struct repos_move_info *
+new_path_adjusted_move(struct repos_move_info *move,
+ const char *moved_along_relpath,
+ svn_node_kind_t moved_along_node_kind,
+ apr_pool_t *result_pool)
+{
+ struct repos_move_info *new_move;
+
+ new_move = apr_pcalloc(result_pool, sizeof(*new_move));
+ new_move->moved_from_repos_relpath =
+ svn_relpath_join(move->moved_from_repos_relpath, moved_along_relpath,
+ result_pool);
+ new_move->moved_to_repos_relpath =
+ svn_relpath_join(move->moved_to_repos_relpath, moved_along_relpath,
+ result_pool);
+ new_move->rev = move->rev;
+ new_move->rev_author = apr_pstrdup(result_pool, move->rev_author);
+ new_move->copyfrom_rev = move->copyfrom_rev;
+ new_move->node_kind = moved_along_node_kind;
+ /* Ignore prev and next pointers. Caller will set them if needed. */
+
+ return new_move;
+}
+
+/* Given a list of MOVES_IN_REVISION, figure out which of these moves again
+ * move the node which was already moved by PREV_MOVE in the past . */
+static svn_error_t *
+find_next_moves_in_revision(apr_array_header_t **next_moves,
+ apr_array_header_t *moves_in_revision,
+ struct repos_move_info *prev_move,
+ svn_ra_session_t *ra_session,
+ const char *repos_root_url,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ int i;
+ apr_pool_t *iterpool;
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < moves_in_revision->nelts; i++)
+ {
+ struct repos_move_info *move;
+ const char *relpath;
+ const char *deleted_repos_relpath;
+ svn_boolean_t related;
+ svn_error_t *err;
+
+ svn_pool_clear(iterpool);
+
+ /* Check if this move affects the current known path of our node. */
+ move = APR_ARRAY_IDX(moves_in_revision, i, struct repos_move_info *);
+ relpath = svn_relpath_skip_ancestor(move->moved_from_repos_relpath,
+ prev_move->moved_to_repos_relpath);
+ if (relpath == NULL)
+ continue;
+
+ /* It does. So our node must have been deleted again. */
+ deleted_repos_relpath = svn_relpath_join(move->moved_from_repos_relpath,
+ relpath, iterpool);
+
+ /* Tracing back history of the delete-half of this move to the
+ * copyfrom-revision of the prior move we must end up at the
+ * delete-half of the prior move. */
+ err = check_move_ancestry(&related, ra_session, repos_root_url,
+ deleted_repos_relpath, move->rev,
+ prev_move->moved_from_repos_relpath,
+ prev_move->copyfrom_rev,
+ FALSE, scratch_pool);
+ if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ continue;
+ }
+ else
+ SVN_ERR(err);
+
+ if (related)
+ {
+ struct repos_move_info *new_move;
+
+ /* We have a winner. */
+ new_move = new_path_adjusted_move(move, relpath, prev_move->node_kind,
+ result_pool);
+ if (*next_moves == NULL)
+ *next_moves = apr_array_make(result_pool, 1,
+ sizeof(struct repos_move_info *));
+ APR_ARRAY_PUSH(*next_moves, struct repos_move_info *) = new_move;
+ }
+ }
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+static int
+compare_items_as_revs(const svn_sort__item_t *a, const svn_sort__item_t *b)
+{
+ return svn_sort_compare_revisions(a->key, b->key);
+}
+
+/* Starting at MOVE->REV, loop over future revisions which contain moves,
+ * and look for matching next moves in each. Once found, return a list of
+ * (ambiguous, if more than one) moves in *NEXT_MOVES. */
+static svn_error_t *
+find_next_moves(apr_array_header_t **next_moves,
+ apr_hash_t *moves_table,
+ struct repos_move_info *move,
+ svn_ra_session_t *ra_session,
+ const char *repos_root_url,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *moves;
+ apr_array_header_t *revisions;
+ apr_pool_t *iterpool;
+ int i;
+
+ *next_moves = NULL;
+ revisions = svn_sort__hash(moves_table, compare_items_as_revs, scratch_pool);
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < revisions->nelts; i++)
+ {
+ svn_sort__item_t item = APR_ARRAY_IDX(revisions, i, svn_sort__item_t);
+ svn_revnum_t rev = *(svn_revnum_t *)item.key;
+
+ svn_pool_clear(iterpool);
+
+ if (rev <= move->rev)
+ continue;
+
+ moves = apr_hash_get(moves_table, &rev, sizeof(rev));
+ SVN_ERR(find_next_moves_in_revision(next_moves, moves, move,
+ ra_session, repos_root_url,
+ result_pool, iterpool));
+ if (*next_moves)
+ break;
+ }
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+/* Trace all future moves of the node moved by MOVE.
+ * Update MOVE->PREV and MOVE->NEXT accordingly. */
+static svn_error_t *
+trace_moved_node(apr_hash_t *moves_table,
+ struct repos_move_info *move,
+ svn_ra_session_t *ra_session,
+ const char *repos_root_url,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *next_moves;
+
+ SVN_ERR(find_next_moves(&next_moves, moves_table, move,
+ ra_session, repos_root_url,
+ result_pool, scratch_pool));
+ if (next_moves)
+ {
+ int i;
+ apr_pool_t *iterpool;
+
+ move->next = next_moves;
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < next_moves->nelts; i++)
+ {
+ struct repos_move_info *next_move;
+
+ svn_pool_clear(iterpool);
+ next_move = APR_ARRAY_IDX(next_moves, i, struct repos_move_info *);
+ next_move->prev = move;
+ SVN_ERR(trace_moved_node(moves_table, next_move,
+ ra_session, repos_root_url,
+ result_pool, iterpool));
+ }
+ svn_pool_destroy(iterpool);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Given a list of MOVES_IN_REVISION, figure out which of these moves
+ * move the node which was later on moved by NEXT_MOVE. */
+static svn_error_t *
+find_prev_move_in_revision(struct repos_move_info **prev_move,
+ apr_array_header_t *moves_in_revision,
+ struct repos_move_info *next_move,
+ svn_ra_session_t *ra_session,
+ const char *repos_root_url,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ int i;
+ apr_pool_t *iterpool;
+
+ *prev_move = NULL;
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < moves_in_revision->nelts; i++)
+ {
+ struct repos_move_info *move;
+ const char *relpath;
+ const char *deleted_repos_relpath;
+ svn_boolean_t related;
+ svn_error_t *err;
+
+ svn_pool_clear(iterpool);
+
+ /* Check if this move affects the current known path of our node. */
+ move = APR_ARRAY_IDX(moves_in_revision, i, struct repos_move_info *);
+ relpath = svn_relpath_skip_ancestor(next_move->moved_from_repos_relpath,
+ move->moved_to_repos_relpath);
+ if (relpath == NULL)
+ continue;
+
+ /* It does. So our node must have been deleted. */
+ deleted_repos_relpath = svn_relpath_join(
+ next_move->moved_from_repos_relpath,
+ relpath, iterpool);
+
+ /* Tracing back history of the delete-half of the next move to the
+ * copyfrom-revision of the prior move we must end up at the
+ * delete-half of the prior move. */
+ err = check_move_ancestry(&related, ra_session, repos_root_url,
+ deleted_repos_relpath, next_move->rev,
+ move->moved_from_repos_relpath,
+ move->copyfrom_rev,
+ FALSE, scratch_pool);
+ if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ continue;
+ }
+ else
+ SVN_ERR(err);
+
+ if (related)
+ {
+ /* We have a winner. */
+ *prev_move = new_path_adjusted_move(move, relpath,
+ next_move->node_kind,
+ result_pool);
+ break;
+ }
+ }
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+static int
+compare_items_as_revs_reverse(const svn_sort__item_t *a,
+ const svn_sort__item_t *b)
+{
+ int c = svn_sort_compare_revisions(a->key, b->key);
+ if (c < 0)
+ return 1;
+ if (c > 0)
+ return -1;
+ return c;
+}
+
+/* Starting at MOVE->REV, loop over past revisions which contain moves,
+ * and look for a matching previous move in each. Once found, return
+ * it in *PREV_MOVE */
+static svn_error_t *
+find_prev_move(struct repos_move_info **prev_move,
+ apr_hash_t *moves_table,
+ struct repos_move_info *move,
+ svn_ra_session_t *ra_session,
+ const char *repos_root_url,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *moves;
+ apr_array_header_t *revisions;
+ apr_pool_t *iterpool;
+ int i;
+
+ *prev_move = NULL;
+ revisions = svn_sort__hash(moves_table, compare_items_as_revs_reverse,
+ scratch_pool);
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < revisions->nelts; i++)
+ {
+ svn_sort__item_t item = APR_ARRAY_IDX(revisions, i, svn_sort__item_t);
+ svn_revnum_t rev = *(svn_revnum_t *)item.key;
+
+ svn_pool_clear(iterpool);
+
+ if (rev >= move->rev)
+ continue;
+
+ moves = apr_hash_get(moves_table, &rev, sizeof(rev));
+ SVN_ERR(find_prev_move_in_revision(prev_move, moves, move,
+ ra_session, repos_root_url,
+ result_pool, iterpool));
+ if (*prev_move)
+ break;
+ }
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Trace all past moves of the node moved by MOVE.
+ * Update MOVE->PREV and MOVE->NEXT accordingly. */
+static svn_error_t *
+trace_moved_node_backwards(apr_hash_t *moves_table,
+ struct repos_move_info *move,
+ svn_ra_session_t *ra_session,
+ const char *repos_root_url,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct repos_move_info *prev_move;
+
+ SVN_ERR(find_prev_move(&prev_move, moves_table, move,
+ ra_session, repos_root_url,
+ result_pool, scratch_pool));
+ if (prev_move)
+ {
+ move->prev = prev_move;
+ prev_move->next = apr_array_make(result_pool, 1,
+ sizeof(struct repos_move_info *));
+ APR_ARRAY_PUSH(prev_move->next, struct repos_move_info *) = move;
+
+ SVN_ERR(trace_moved_node_backwards(moves_table, prev_move,
+ ra_session, repos_root_url,
+ result_pool, scratch_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+reparent_session_and_fetch_node_kind(svn_node_kind_t *node_kind,
+ svn_ra_session_t *ra_session,
+ const char *url,
+ svn_revnum_t peg_rev,
+ apr_pool_t *scratch_pool)
+{
+ svn_error_t *err;
+
+ err = svn_ra_reparent(ra_session, url, scratch_pool);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_RA_ILLEGAL_URL)
+ {
+ svn_error_clear(err);
+ *node_kind = svn_node_unknown;
+ return SVN_NO_ERROR;
+ }
+
+ return svn_error_trace(err);
+ }
+
+ SVN_ERR(svn_ra_check_path(ra_session, "", peg_rev, node_kind, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* Scan MOVES_TABLE for moves which affect a particular deleted node, and
+ * build a set of new move information for this node.
+ * Return heads of all possible move chains in *MOVES.
+ *
+ * MOVES_TABLE describes moves which happened at arbitrary paths in the
+ * repository. DELETED_REPOS_RELPATH may have been moved directly or it
+ * may have been moved along with a parent path. Move information returned
+ * from this function represents how DELETED_REPOS_RELPATH itself was moved
+ * from one path to another, effectively "zooming in" on the effective move
+ * operations which occurred for this particular node. */
+static svn_error_t *
+find_operative_moves(apr_array_header_t **moves,
+ apr_hash_t *moves_table,
+ const char *deleted_repos_relpath,
+ svn_revnum_t deleted_rev,
+ svn_ra_session_t *ra_session,
+ const char *repos_root_url,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *moves_in_deleted_rev;
+ int i;
+ apr_pool_t *iterpool;
+ const char *session_url, *url = NULL;
+
+ moves_in_deleted_rev = apr_hash_get(moves_table, &deleted_rev,
+ sizeof(deleted_rev));
+ if (moves_in_deleted_rev == NULL)
+ {
+ *moves = NULL;
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, scratch_pool));
+
+ /* Look for operative moves in the revision where the node was deleted. */
+ *moves = apr_array_make(scratch_pool, 0, sizeof(struct repos_move_info *));
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < moves_in_deleted_rev->nelts; i++)
+ {
+ struct repos_move_info *move;
+ const char *relpath;
+
+ svn_pool_clear(iterpool);
+
+ move = APR_ARRAY_IDX(moves_in_deleted_rev, i, struct repos_move_info *);
+ relpath = svn_relpath_skip_ancestor(move->moved_from_repos_relpath,
+ deleted_repos_relpath);
+ if (relpath && relpath[0] != '\0')
+ {
+ svn_node_kind_t node_kind;
+
+ url = svn_path_url_add_component2(repos_root_url,
+ deleted_repos_relpath,
+ iterpool);
+ SVN_ERR(reparent_session_and_fetch_node_kind(&node_kind,
+ ra_session, url,
+ rev_below(deleted_rev),
+ iterpool));
+ move = new_path_adjusted_move(move, relpath, node_kind, result_pool);
+ }
+ APR_ARRAY_PUSH(*moves, struct repos_move_info *) = move;
+ }
+
+ if (url != NULL)
+ SVN_ERR(svn_ra_reparent(ra_session, session_url, scratch_pool));
+
+ /* If we didn't find any applicable moves, return NULL. */
+ if ((*moves)->nelts == 0)
+ {
+ *moves = NULL;
+ svn_pool_destroy(iterpool);
+ return SVN_NO_ERROR;
+ }
+
+ /* Figure out what happened to these moves in future revisions. */
+ for (i = 0; i < (*moves)->nelts; i++)
+ {
+ struct repos_move_info *move;
+
+ svn_pool_clear(iterpool);
+
+ move = APR_ARRAY_IDX(*moves, i, struct repos_move_info *);
+ SVN_ERR(trace_moved_node(moves_table, move, ra_session, repos_root_url,
+ result_pool, iterpool));
+ }
+
+ svn_pool_destroy(iterpool);
+ return SVN_NO_ERROR;
+}
+
+/* Try to find a revision older than START_REV, and its author, which deleted
+ * DELETED_BASENAME in the directory PARENT_REPOS_RELPATH. Assume the deleted
+ * node is ancestrally related to RELATED_REPOS_RELPATH@RELATED_PEG_REV.
+ * If no such revision can be found, set *DELETED_REV to SVN_INVALID_REVNUM
+ * and *DELETED_REV_AUTHOR to NULL.
+ * If the node was replaced rather than deleted, set *REPLACING_NODE_KIND to
+ * the node kind of the replacing node. Else, set it to svn_node_unknown.
+ * Only request the log for revisions up to END_REV from the server.
+ * If the deleted node was moved, provide heads of move chains in *MOVES.
+ * If the node was not moved,set *MOVES to NULL.
+ */
+static svn_error_t *
+find_revision_for_suspected_deletion(svn_revnum_t *deleted_rev,
+ const char **deleted_rev_author,
+ svn_node_kind_t *replacing_node_kind,
+ struct apr_array_header_t **moves,
+ svn_client_conflict_t *conflict,
+ const char *deleted_basename,
+ const char *parent_repos_relpath,
+ svn_revnum_t start_rev,
+ svn_revnum_t end_rev,
+ const char *related_repos_relpath,
+ svn_revnum_t related_peg_rev,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_ra_session_t *ra_session;
+ const char *url;
+ const char *corrected_url;
+ apr_array_header_t *paths;
+ apr_array_header_t *revprops;
+ const char *repos_root_url;
+ const char *repos_uuid;
+ struct find_deleted_rev_baton b = { 0 };
+ const char *victim_abspath;
+ svn_error_t *err;
+ apr_hash_t *moves_table;
+
+ SVN_ERR_ASSERT(start_rev > end_rev);
+
+ SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, &repos_uuid,
+ conflict, scratch_pool,
+ scratch_pool));
+ victim_abspath = svn_client_conflict_get_local_abspath(conflict);
+
+ SVN_ERR(find_moves_in_revision_range(&moves_table, parent_repos_relpath,
+ repos_root_url, repos_uuid,
+ victim_abspath, start_rev, end_rev,
+ ctx, result_pool, scratch_pool));
+
+ url = svn_path_url_add_component2(repos_root_url, parent_repos_relpath,
+ scratch_pool);
+ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url,
+ url, NULL, NULL, FALSE, FALSE,
+ ctx, scratch_pool,
+ scratch_pool));
+
+ paths = apr_array_make(scratch_pool, 1, sizeof(const char *));
+ APR_ARRAY_PUSH(paths, const char *) = "";
+
+ revprops = apr_array_make(scratch_pool, 1, sizeof(const char *));
+ APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_AUTHOR;
+
+ b.victim_abspath = victim_abspath;
+ b.deleted_repos_relpath = svn_relpath_join(parent_repos_relpath,
+ deleted_basename, scratch_pool);
+ b.related_repos_relpath = related_repos_relpath;
+ b.related_peg_rev = related_peg_rev;
+ b.deleted_rev = SVN_INVALID_REVNUM;
+ b.replacing_node_kind = svn_node_unknown;
+ b.repos_root_url = repos_root_url;
+ b.repos_uuid = repos_uuid;
+ b.ctx = ctx;
+ b.moves_table = moves_table;
+ b.result_pool = result_pool;
+ SVN_ERR(svn_ra__dup_session(&b.extra_ra_session, ra_session, NULL,
+ scratch_pool, scratch_pool));
+
+ err = svn_ra_get_log2(ra_session, paths, start_rev, end_rev,
+ 0, /* no limit */
+ TRUE, /* need the changed paths list */
+ FALSE, /* need to traverse copies */
+ FALSE, /* no need for merged revisions */
+ revprops,
+ find_deleted_rev, &b,
+ scratch_pool);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_CEASE_INVOCATION &&
+ b.deleted_rev != SVN_INVALID_REVNUM)
+
+ {
+ /* Log operation was aborted because we found deleted rev. */
+ svn_error_clear(err);
+ }
+ else
+ return svn_error_trace(err);
+ }
+
+ if (b.deleted_rev == SVN_INVALID_REVNUM)
+ {
+ struct repos_move_info *move = b.move;
+
+ if (move)
+ {
+ *deleted_rev = move->rev;
+ *deleted_rev_author = move->rev_author;
+ *replacing_node_kind = b.replacing_node_kind;
+ SVN_ERR(find_operative_moves(moves, moves_table,
+ b.deleted_repos_relpath,
+ move->rev,
+ ra_session, repos_root_url,
+ result_pool, scratch_pool));
+ }
+ else
+ {
+ /* We could not determine the revision in which the node was
+ * deleted. */
+ *deleted_rev = SVN_INVALID_REVNUM;
+ *deleted_rev_author = NULL;
+ *replacing_node_kind = svn_node_unknown;
+ *moves = NULL;
+ }
+ return SVN_NO_ERROR;
+ }
+ else
+ {
+ *deleted_rev = b.deleted_rev;
+ *deleted_rev_author = b.deleted_rev_author;
+ *replacing_node_kind = b.replacing_node_kind;
+ SVN_ERR(find_operative_moves(moves, moves_table,
+ b.deleted_repos_relpath, b.deleted_rev,
+ ra_session, repos_root_url,
+ result_pool, scratch_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Details for tree conflicts involving a locally missing node. */
+struct conflict_tree_local_missing_details
+{
+ /* If not SVN_INVALID_REVNUM, the node was deleted in DELETED_REV. */
+ svn_revnum_t deleted_rev;
+
+ /* Author who committed DELETED_REV. */
+ const char *deleted_rev_author;
+
+ /* The path which was deleted relative to the repository root. */
+ const char *deleted_repos_relpath;
+
+ /* Move information about the conflict victim. If not NULL, this is an
+ * array of repos_move_info elements. Each element is the head of a
+ * move chain which starts in DELETED_REV. */
+ apr_array_header_t *moves;
+
+ /* Move information about siblings. Siblings are nodes which share
+ * a youngest common ancestor with the conflict victim. E.g. in case
+ * of a merge operation they are part of the merge source branch.
+ * If not NULL, this is an array of repos_move_info elements.
+ * Each element is the head of a move chain, which starts at some
+ * point in history after siblings and conflict victim forked off
+ * their common ancestor. */
+ apr_array_header_t *sibling_moves;
+
+ /* If not NULL, this is the move target abspath. */
+ const char *moved_to_abspath;
+};
+
+static svn_error_t *
+find_related_node(const char **related_repos_relpath,
+ svn_revnum_t *related_peg_rev,
+ const char *younger_related_repos_relpath,
+ svn_revnum_t younger_related_peg_rev,
+ const char *older_repos_relpath,
+ svn_revnum_t older_peg_rev,
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *repos_root_url;
+ const char *related_url;
+ const char *corrected_url;
+ svn_node_kind_t related_node_kind;
+ svn_ra_session_t *ra_session;
+
+ *related_repos_relpath = NULL;
+ *related_peg_rev = SVN_INVALID_REVNUM;
+
+ SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, NULL,
+ conflict,
+ scratch_pool, scratch_pool));
+ related_url = svn_path_url_add_component2(repos_root_url,
+ younger_related_repos_relpath,
+ scratch_pool);
+ SVN_ERR(svn_client__open_ra_session_internal(&ra_session,
+ &corrected_url,
+ related_url, NULL,
+ NULL,
+ FALSE,
+ FALSE,
+ ctx,
+ scratch_pool,
+ scratch_pool));
+ SVN_ERR(svn_ra_check_path(ra_session, "", younger_related_peg_rev,
+ &related_node_kind, scratch_pool));
+ if (related_node_kind == svn_node_none)
+ {
+ svn_revnum_t related_deleted_rev;
+ const char *related_deleted_rev_author;
+ svn_node_kind_t related_replacing_node_kind;
+ const char *related_basename;
+ const char *related_parent_repos_relpath;
+ apr_array_header_t *related_moves;
+
+ /* Looks like the younger node, which we'd like to use as our
+ * 'related node', was deleted. Try to find its deleted revision
+ * so we can calculate a peg revision at which it exists.
+ * The younger node is related to the older node, so we can use
+ * the older node to guide us in our search. */
+ related_basename = svn_relpath_basename(younger_related_repos_relpath,
+ scratch_pool);
+ related_parent_repos_relpath =
+ svn_relpath_dirname(younger_related_repos_relpath, scratch_pool);
+ SVN_ERR(find_revision_for_suspected_deletion(
+ &related_deleted_rev, &related_deleted_rev_author,
+ &related_replacing_node_kind, &related_moves,
+ conflict, related_basename,
+ related_parent_repos_relpath,
+ younger_related_peg_rev, 0,
+ older_repos_relpath, older_peg_rev,
+ ctx, conflict->pool, scratch_pool));
+
+ /* If we can't find a related node, bail. */
+ if (related_deleted_rev == SVN_INVALID_REVNUM)
+ return SVN_NO_ERROR;
+
+ /* The node should exist in the revision before it was deleted. */
+ *related_repos_relpath = younger_related_repos_relpath;
+ *related_peg_rev = rev_below(related_deleted_rev);
+ }
+ else
+ {
+ *related_repos_relpath = younger_related_repos_relpath;
+ *related_peg_rev = younger_related_peg_rev;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Determine if REPOS_RELPATH@PEG_REV was moved at some point in its history.
+ * History's range of interest ends at END_REV which must be older than PEG_REV.
+ *
+ * VICTIM_ABSPATH is the abspath of a conflict victim in the working copy and
+ * will be used in notifications.
+ *
+ * Return any applicable move chain heads in *MOVES.
+ * If no moves can be found, set *MOVES to NULL. */
+static svn_error_t *
+find_moves_in_natural_history(apr_array_header_t **moves,
+ const char *repos_relpath,
+ svn_revnum_t peg_rev,
+ svn_node_kind_t node_kind,
+ svn_revnum_t end_rev,
+ const char *victim_abspath,
+ const char *repos_root_url,
+ const char *repos_uuid,
+ svn_ra_session_t *ra_session,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_t *moves_table;
+ apr_array_header_t *revs;
+ apr_array_header_t *most_recent_moves = NULL;
+ int i;
+ apr_pool_t *iterpool;
+
+ *moves = NULL;
+
+ SVN_ERR(find_moves_in_revision_range(&moves_table, repos_relpath,
+ repos_root_url, repos_uuid,
+ victim_abspath, peg_rev, end_rev,
+ ctx, scratch_pool, scratch_pool));
+
+ iterpool = svn_pool_create(scratch_pool);
+
+ /* Scan the moves table for applicable moves. */
+ revs = svn_sort__hash(moves_table, compare_items_as_revs, scratch_pool);
+ for (i = revs->nelts - 1; i >= 0; i--)
+ {
+ svn_sort__item_t item = APR_ARRAY_IDX(revs, i, svn_sort__item_t);
+ apr_array_header_t *moves_in_rev = apr_hash_get(moves_table, item.key,
+ sizeof(svn_revnum_t));
+ int j;
+
+ svn_pool_clear(iterpool);
+
+ /* Was repos relpath moved to its location in this revision? */
+ for (j = 0; j < moves_in_rev->nelts; j++)
+ {
+ struct repos_move_info *move;
+ const char *relpath;
+
+ move = APR_ARRAY_IDX(moves_in_rev, j, struct repos_move_info *);
+ relpath = svn_relpath_skip_ancestor(move->moved_to_repos_relpath,
+ repos_relpath);
+ if (relpath)
+ {
+ /* If the move did not happen in our peg revision, make
+ * sure this move happened on the same line of history. */
+ if (move->rev != peg_rev)
+ {
+ svn_client__pathrev_t *yca_loc;
+ svn_error_t *err;
+
+ err = find_yca(&yca_loc, repos_relpath, peg_rev,
+ repos_relpath, move->rev,
+ repos_root_url, repos_uuid,
+ NULL, ctx, iterpool, iterpool);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ yca_loc = NULL;
+ }
+ else
+ return svn_error_trace(err);
+ }
+
+ if (yca_loc == NULL || yca_loc->rev != move->rev)
+ continue;
+ }
+
+ if (most_recent_moves == NULL)
+ most_recent_moves =
+ apr_array_make(result_pool, 1,
+ sizeof(struct repos_move_info *));
+
+ /* Copy the move to result pool (even if relpath is ""). */
+ move = new_path_adjusted_move(move, relpath, node_kind,
+ result_pool);
+ APR_ARRAY_PUSH(most_recent_moves,
+ struct repos_move_info *) = move;
+ }
+ }
+
+ /* If we found one move, or several ambiguous moves, we're done. */
+ if (most_recent_moves)
+ break;
+ }
+
+ if (most_recent_moves && most_recent_moves->nelts > 0)
+ {
+ *moves = apr_array_make(result_pool, 1,
+ sizeof(struct repos_move_info *));
+
+ /* Figure out what happened to the most recent moves in prior
+ * revisions and build move chains. */
+ for (i = 0; i < most_recent_moves->nelts; i++)
+ {
+ struct repos_move_info *move;
+
+ svn_pool_clear(iterpool);
+
+ move = APR_ARRAY_IDX(most_recent_moves, i, struct repos_move_info *);
+ SVN_ERR(trace_moved_node_backwards(moves_table, move,
+ ra_session, repos_root_url,
+ result_pool, iterpool));
+ /* Follow the move chain backwards. */
+ while (move->prev)
+ move = move->prev;
+
+ /* Return move heads. */
+ APR_ARRAY_PUSH(*moves, struct repos_move_info *) = move;
+ }
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+/* Implements tree_conflict_get_details_func_t. */
+static svn_error_t *
+conflict_tree_get_details_local_missing(svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ const char *old_repos_relpath;
+ const char *new_repos_relpath;
+ const char *parent_repos_relpath;
+ svn_revnum_t parent_peg_rev;
+ svn_revnum_t old_rev;
+ svn_revnum_t new_rev;
+ svn_revnum_t deleted_rev;
+ const char *deleted_rev_author;
+ svn_node_kind_t replacing_node_kind;
+ const char *deleted_basename;
+ struct conflict_tree_local_missing_details *details;
+ apr_array_header_t *moves = NULL;
+ apr_array_header_t *sibling_moves = NULL;
+ const char *related_repos_relpath;
+ svn_revnum_t related_peg_rev;
+ const char *repos_root_url;
+ const char *repos_uuid;
+
+ SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
+ &old_repos_relpath, &old_rev, NULL, conflict,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_client_conflict_get_incoming_new_repos_location(
+ &new_repos_relpath, &new_rev, NULL, conflict,
+ scratch_pool, scratch_pool));
+
+ /* Scan the conflict victim's parent's log to find a revision which
+ * deleted the node. */
+ deleted_basename = svn_dirent_basename(conflict->local_abspath,
+ scratch_pool);
+ SVN_ERR(svn_wc__node_get_repos_info(&parent_peg_rev, &parent_repos_relpath,
+ &repos_root_url, &repos_uuid,
+ ctx->wc_ctx,
+ svn_dirent_dirname(
+ conflict->local_abspath,
+ scratch_pool),
+ scratch_pool,
+ scratch_pool));
+
+ /* Pick the younger incoming node as our 'related node' which helps
+ * pin-pointing the deleted conflict victim in history. */
+ related_repos_relpath =
+ (old_rev < new_rev ? new_repos_relpath : old_repos_relpath);
+ related_peg_rev = (old_rev < new_rev ? new_rev : old_rev);
+
+ /* Make sure we're going to search the related node in a revision where
+ * it exists. The younger incoming node might have been deleted in HEAD. */
+ if (related_repos_relpath != NULL && related_peg_rev != SVN_INVALID_REVNUM)
+ SVN_ERR(find_related_node(
+ &related_repos_relpath, &related_peg_rev,
+ related_repos_relpath, related_peg_rev,
+ (old_rev < new_rev ? old_repos_relpath : new_repos_relpath),
+ (old_rev < new_rev ? old_rev : new_rev),
+ conflict, ctx, scratch_pool, scratch_pool));
+
+ SVN_ERR(find_revision_for_suspected_deletion(
+ &deleted_rev, &deleted_rev_author, &replacing_node_kind, &moves,
+ conflict, deleted_basename, parent_repos_relpath,
+ parent_peg_rev, 0, related_repos_relpath, related_peg_rev,
+ ctx, conflict->pool, scratch_pool));
+
+ /* If the victim was not deleted then check if the related path was moved. */
+ if (deleted_rev == SVN_INVALID_REVNUM)
+ {
+ const char *victim_abspath;
+ svn_ra_session_t *ra_session;
+ const char *url, *corrected_url;
+ svn_client__pathrev_t *yca_loc;
+ svn_revnum_t end_rev;
+ svn_node_kind_t related_node_kind;
+
+ /* ### The following describes all moves in terms of forward-merges,
+ * should do we something else for reverse-merges? */
+
+ victim_abspath = svn_client_conflict_get_local_abspath(conflict);
+ url = svn_path_url_add_component2(repos_root_url, related_repos_relpath,
+ scratch_pool);
+ SVN_ERR(svn_client__open_ra_session_internal(&ra_session,
+ &corrected_url,
+ url, NULL, NULL,
+ FALSE,
+ FALSE,
+ ctx,
+ scratch_pool,
+ scratch_pool));
+
+ /* Set END_REV to our best guess of the nearest YCA revision. */
+ SVN_ERR(find_nearest_yca(&yca_loc, related_repos_relpath, related_peg_rev,
+ parent_repos_relpath, parent_peg_rev,
+ repos_root_url, repos_uuid, ra_session, ctx,
+ scratch_pool, scratch_pool));
+ if (yca_loc == NULL)
+ return SVN_NO_ERROR;
+ end_rev = yca_loc->rev;
+
+ /* END_REV must be smaller than RELATED_PEG_REV, else the call
+ to find_moves_in_natural_history() below will error out. */
+ if (end_rev >= related_peg_rev)
+ end_rev = related_peg_rev > 0 ? related_peg_rev - 1 : 0;
+
+ SVN_ERR(svn_ra_check_path(ra_session, "", related_peg_rev,
+ &related_node_kind, scratch_pool));
+ SVN_ERR(find_moves_in_natural_history(&sibling_moves,
+ related_repos_relpath,
+ related_peg_rev,
+ related_node_kind,
+ end_rev,
+ victim_abspath,
+ repos_root_url, repos_uuid,
+ ra_session, ctx,
+ conflict->pool, scratch_pool));
+
+ if (sibling_moves == NULL)
+ return SVN_NO_ERROR;
+
+ /* ## TODO: Find the missing node in the WC. */
+ }
+
+ details = apr_pcalloc(conflict->pool, sizeof(*details));
+ details->deleted_rev = deleted_rev;
+ details->deleted_rev_author = deleted_rev_author;
+ if (deleted_rev != SVN_INVALID_REVNUM)
+ details->deleted_repos_relpath = svn_relpath_join(parent_repos_relpath,
+ deleted_basename,
+ conflict->pool);
+ details->moves = moves;
+ details->sibling_moves = sibling_moves;
+
+ conflict->tree_conflict_local_details = details;
+
+ return SVN_NO_ERROR;
+}
+
+/* Return a localised string representation of the local part of a tree
+ conflict on a non-existent node. */
+static svn_error_t *
+describe_local_none_node_change(const char **description,
+ svn_client_conflict_t *conflict,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc_conflict_reason_t local_change;
+ svn_wc_operation_t operation;
+
+ local_change = svn_client_conflict_get_local_change(conflict);
+ operation = svn_client_conflict_get_operation(conflict);
+
+ switch (local_change)
+ {
+ case svn_wc_conflict_reason_edited:
+ *description = _("An item containing uncommitted changes was "
+ "found in the working copy.");
+ break;
+ case svn_wc_conflict_reason_obstructed:
+ *description = _("An item which already occupies this path was found in "
+ "the working copy.");
+ break;
+ case svn_wc_conflict_reason_deleted:
+ *description = _("A deleted item was found in the working copy.");
+ break;
+ case svn_wc_conflict_reason_missing:
+ if (operation == svn_wc_operation_update ||
+ operation == svn_wc_operation_switch)
+ *description = _("No such file or directory was found in the "
+ "working copy.");
+ else if (operation == svn_wc_operation_merge)
+ {
+ /* ### display deleted revision */
+ *description = _("No such file or directory was found in the "
+ "merge target working copy.\nThe item may "
+ "have been deleted or moved away in the "
+ "repository's history.");
+ }
+ break;
+ case svn_wc_conflict_reason_unversioned:
+ *description = _("An unversioned item was found in the working "
+ "copy.");
+ break;
+ case svn_wc_conflict_reason_added:
+ case svn_wc_conflict_reason_replaced:
+ *description = _("An item scheduled to be added to the repository "
+ "in the next commit was found in the working "
+ "copy.");
+ break;
+ case svn_wc_conflict_reason_moved_away:
+ *description = _("The item in the working copy had been moved "
+ "away at the time this conflict was recorded.");
+ break;
+ case svn_wc_conflict_reason_moved_here:
+ *description = _("An item had been moved here in the working copy "
+ "at the time this conflict was recorded.");
+ break;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Append a description of a move chain beginning at NEXT to DESCRIPTION. */
+static const char *
+append_moved_to_chain_description(const char *description,
+ apr_array_header_t *next,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ if (next == NULL)
+ return description;
+
+ while (next)
+ {
+ struct repos_move_info *move;
+
+ /* Describe the first possible move chain only. Adding multiple chains
+ * to the description would just be confusing. The user may select a
+ * different move destination while resolving the conflict. */
+ move = APR_ARRAY_IDX(next, 0, struct repos_move_info *);
+
+ description = apr_psprintf(scratch_pool,
+ _("%s\nAnd then moved away to '^/%s' by "
+ "%s in r%ld."),
+ description, move->moved_to_repos_relpath,
+ move->rev_author, move->rev);
+ next = move->next;
+ }
+
+ return apr_pstrdup(result_pool, description);
+}
+
+/* Implements tree_conflict_get_description_func_t. */
+static svn_error_t *
+conflict_tree_get_local_description_generic(const char **description,
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_node_kind_t victim_node_kind;
+
+ victim_node_kind = svn_client_conflict_tree_get_victim_node_kind(conflict);
+
+ *description = NULL;
+
+ switch (victim_node_kind)
+ {
+ case svn_node_file:
+ case svn_node_symlink:
+ SVN_ERR(describe_local_file_node_change(description, conflict, ctx,
+ result_pool, scratch_pool));
+ break;
+ case svn_node_dir:
+ SVN_ERR(describe_local_dir_node_change(description, conflict, ctx,
+ result_pool, scratch_pool));
+ break;
+ case svn_node_none:
+ case svn_node_unknown:
+ SVN_ERR(describe_local_none_node_change(description, conflict,
+ result_pool, scratch_pool));
+ break;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Implements tree_conflict_get_description_func_t. */
+static svn_error_t *
+conflict_tree_get_description_local_missing(const char **description,
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct conflict_tree_local_missing_details *details;
+
+ details = conflict->tree_conflict_local_details;
+ if (details == NULL)
+ return svn_error_trace(conflict_tree_get_local_description_generic(
+ description, conflict, ctx,
+ result_pool, scratch_pool));
+
+ if (details->moves || details->sibling_moves)
+ {
+ struct repos_move_info *move;
+
+ *description = _("No such file or directory was found in the "
+ "merge target working copy.\n");
+
+ if (details->moves)
+ {
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ if (move->node_kind == svn_node_file)
+ *description = apr_psprintf(
+ result_pool,
+ _("%sThe file was moved to '^/%s' in r%ld by %s."),
+ *description, move->moved_to_repos_relpath,
+ move->rev, move->rev_author);
+ else if (move->node_kind == svn_node_dir)
+ *description = apr_psprintf(
+ result_pool,
+ _("%sThe directory was moved to '^/%s' in "
+ "r%ld by %s."),
+ *description, move->moved_to_repos_relpath,
+ move->rev, move->rev_author);
+ else
+ *description = apr_psprintf(
+ result_pool,
+ _("%sThe item was moved to '^/%s' in r%ld by %s."),
+ *description, move->moved_to_repos_relpath,
+ move->rev, move->rev_author);
+ *description = append_moved_to_chain_description(*description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+
+ if (details->sibling_moves)
+ {
+ move = APR_ARRAY_IDX(details->sibling_moves, 0,
+ struct repos_move_info *);
+ if (move->node_kind == svn_node_file)
+ *description = apr_psprintf(
+ result_pool,
+ _("%sThe file '^/%s' was moved to '^/%s' "
+ "in r%ld by %s."),
+ *description, move->moved_from_repos_relpath,
+ move->moved_to_repos_relpath,
+ move->rev, move->rev_author);
+ else if (move->node_kind == svn_node_dir)
+ *description = apr_psprintf(
+ result_pool,
+ _("%sThe directory '^/%s' was moved to '^/%s' "
+ "in r%ld by %s."),
+ *description, move->moved_from_repos_relpath,
+ move->moved_to_repos_relpath,
+ move->rev, move->rev_author);
+ else
+ *description = apr_psprintf(
+ result_pool,
+ _("%sThe item '^/%s' was moved to '^/%s' "
+ "in r%ld by %s."),
+ *description, move->moved_from_repos_relpath,
+ move->moved_to_repos_relpath,
+ move->rev, move->rev_author);
+ *description = append_moved_to_chain_description(*description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ }
+ else
+ *description = apr_psprintf(
+ result_pool,
+ _("No such file or directory was found in the "
+ "merge target working copy.\n'^/%s' was deleted "
+ "in r%ld by %s."),
+ details->deleted_repos_relpath,
+ details->deleted_rev, details->deleted_rev_author);
+
+ return SVN_NO_ERROR;
+}
+
+/* Return a localised string representation of the incoming part of a
+ conflict; NULL for non-localised odd cases. */
+static const char *
+describe_incoming_change(svn_node_kind_t kind, svn_wc_conflict_action_t action,
+ svn_wc_operation_t operation)
+{
+ switch (kind)
+ {
+ case svn_node_file:
+ case svn_node_symlink:
+ if (operation == svn_wc_operation_update)
+ {
+ switch (action)
+ {
+ case svn_wc_conflict_action_edit:
+ return _("An update operation tried to edit a file.");
+ case svn_wc_conflict_action_add:
+ return _("An update operation tried to add a file.");
+ case svn_wc_conflict_action_delete:
+ return _("An update operation tried to delete or move "
+ "a file.");
+ case svn_wc_conflict_action_replace:
+ return _("An update operation tried to replace a file.");
+ }
+ }
+ else if (operation == svn_wc_operation_switch)
+ {
+ switch (action)
+ {
+ case svn_wc_conflict_action_edit:
+ return _("A switch operation tried to edit a file.");
+ case svn_wc_conflict_action_add:
+ return _("A switch operation tried to add a file.");
+ case svn_wc_conflict_action_delete:
+ return _("A switch operation tried to delete or move "
+ "a file.");
+ case svn_wc_conflict_action_replace:
+ return _("A switch operation tried to replace a file.");
+ }
+ }
+ else if (operation == svn_wc_operation_merge)
+ {
+ switch (action)
+ {
+ case svn_wc_conflict_action_edit:
+ return _("A merge operation tried to edit a file.");
+ case svn_wc_conflict_action_add:
+ return _("A merge operation tried to add a file.");
+ case svn_wc_conflict_action_delete:
+ return _("A merge operation tried to delete or move "
+ "a file.");
+ case svn_wc_conflict_action_replace:
+ return _("A merge operation tried to replace a file.");
+ }
+ }
+ break;
+ case svn_node_dir:
+ if (operation == svn_wc_operation_update)
+ {
+ switch (action)
+ {
+ case svn_wc_conflict_action_edit:
+ return _("An update operation tried to change a directory.");
+ case svn_wc_conflict_action_add:
+ return _("An update operation tried to add a directory.");
+ case svn_wc_conflict_action_delete:
+ return _("An update operation tried to delete or move "
+ "a directory.");
+ case svn_wc_conflict_action_replace:
+ return _("An update operation tried to replace a directory.");
+ }
+ }
+ else if (operation == svn_wc_operation_switch)
+ {
+ switch (action)
+ {
+ case svn_wc_conflict_action_edit:
+ return _("A switch operation tried to edit a directory.");
+ case svn_wc_conflict_action_add:
+ return _("A switch operation tried to add a directory.");
+ case svn_wc_conflict_action_delete:
+ return _("A switch operation tried to delete or move "
+ "a directory.");
+ case svn_wc_conflict_action_replace:
+ return _("A switch operation tried to replace a directory.");
+ }
+ }
+ else if (operation == svn_wc_operation_merge)
+ {
+ switch (action)
+ {
+ case svn_wc_conflict_action_edit:
+ return _("A merge operation tried to edit a directory.");
+ case svn_wc_conflict_action_add:
+ return _("A merge operation tried to add a directory.");
+ case svn_wc_conflict_action_delete:
+ return _("A merge operation tried to delete or move "
+ "a directory.");
+ case svn_wc_conflict_action_replace:
+ return _("A merge operation tried to replace a directory.");
+ }
+ }
+ break;
+ case svn_node_none:
+ case svn_node_unknown:
+ if (operation == svn_wc_operation_update)
+ {
+ switch (action)
+ {
+ case svn_wc_conflict_action_edit:
+ return _("An update operation tried to edit an item.");
+ case svn_wc_conflict_action_add:
+ return _("An update operation tried to add an item.");
+ case svn_wc_conflict_action_delete:
+ return _("An update operation tried to delete or move "
+ "an item.");
+ case svn_wc_conflict_action_replace:
+ return _("An update operation tried to replace an item.");
+ }
+ }
+ else if (operation == svn_wc_operation_switch)
+ {
+ switch (action)
+ {
+ case svn_wc_conflict_action_edit:
+ return _("A switch operation tried to edit an item.");
+ case svn_wc_conflict_action_add:
+ return _("A switch operation tried to add an item.");
+ case svn_wc_conflict_action_delete:
+ return _("A switch operation tried to delete or move "
+ "an item.");
+ case svn_wc_conflict_action_replace:
+ return _("A switch operation tried to replace an item.");
+ }
+ }
+ else if (operation == svn_wc_operation_merge)
+ {
+ switch (action)
+ {
+ case svn_wc_conflict_action_edit:
+ return _("A merge operation tried to edit an item.");
+ case svn_wc_conflict_action_add:
+ return _("A merge operation tried to add an item.");
+ case svn_wc_conflict_action_delete:
+ return _("A merge operation tried to delete or move "
+ "an item.");
+ case svn_wc_conflict_action_replace:
+ return _("A merge operation tried to replace an item.");
+ }
+ }
+ break;
+ }
+
+ return NULL;
+}
+
+/* Return a localised string representation of the operation part of a
+ conflict. */
+static const char *
+operation_str(svn_wc_operation_t operation)
+{
+ switch (operation)
+ {
+ case svn_wc_operation_update: return _("upon update");
+ case svn_wc_operation_switch: return _("upon switch");
+ case svn_wc_operation_merge: return _("upon merge");
+ case svn_wc_operation_none: return _("upon none");
+ }
+ SVN_ERR_MALFUNCTION_NO_RETURN();
+ return NULL;
+}
+
+svn_error_t *
+svn_client_conflict_prop_get_description(const char **description,
+ svn_client_conflict_t *conflict,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *reason_str, *action_str;
+
+ /* We provide separately translatable strings for the values that we
+ * know about, and a fall-back in case any other values occur. */
+ switch (svn_client_conflict_get_local_change(conflict))
+ {
+ case svn_wc_conflict_reason_edited:
+ reason_str = _("local edit");
+ break;
+ case svn_wc_conflict_reason_added:
+ reason_str = _("local add");
+ break;
+ case svn_wc_conflict_reason_deleted:
+ reason_str = _("local delete");
+ break;
+ case svn_wc_conflict_reason_obstructed:
+ reason_str = _("local obstruction");
+ break;
+ default:
+ reason_str = apr_psprintf(
+ scratch_pool, _("local %s"),
+ svn_token__to_word(
+ map_conflict_reason,
+ svn_client_conflict_get_local_change(conflict)));
+ break;
+ }
+ switch (svn_client_conflict_get_incoming_change(conflict))
+ {
+ case svn_wc_conflict_action_edit:
+ action_str = _("incoming edit");
+ break;
+ case svn_wc_conflict_action_add:
+ action_str = _("incoming add");
+ break;
+ case svn_wc_conflict_action_delete:
+ action_str = _("incoming delete");
+ break;
+ default:
+ action_str = apr_psprintf(
+ scratch_pool, _("incoming %s"),
+ svn_token__to_word(
+ map_conflict_action,
+ svn_client_conflict_get_incoming_change(conflict)));
+ break;
+ }
+ SVN_ERR_ASSERT(reason_str && action_str);
+
+ *description = apr_psprintf(result_pool, _("%s, %s %s"),
+ reason_str, action_str,
+ operation_str(
+ svn_client_conflict_get_operation(conflict)));
+
+ return SVN_NO_ERROR;
+}
+
+/* Implements tree_conflict_get_description_func_t. */
+static svn_error_t *
+conflict_tree_get_incoming_description_generic(
+ const char **incoming_change_description,
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *action;
+ svn_node_kind_t incoming_kind;
+ svn_wc_conflict_action_t conflict_action;
+ svn_wc_operation_t conflict_operation;
+
+ conflict_action = svn_client_conflict_get_incoming_change(conflict);
+ conflict_operation = svn_client_conflict_get_operation(conflict);
+
+ /* Determine the node kind of the incoming change. */
+ incoming_kind = svn_node_unknown;
+ if (conflict_action == svn_wc_conflict_action_edit ||
+ conflict_action == svn_wc_conflict_action_delete)
+ {
+ /* Change is acting on 'src_left' version of the node. */
+ SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
+ NULL, NULL, &incoming_kind, conflict, scratch_pool,
+ scratch_pool));
+ }
+ else if (conflict_action == svn_wc_conflict_action_add ||
+ conflict_action == svn_wc_conflict_action_replace)
+ {
+ /* Change is acting on 'src_right' version of the node.
+ *
+ * ### For 'replace', the node kind is ambiguous. However, src_left
+ * ### is NULL for replace, so we must use src_right. */
+ SVN_ERR(svn_client_conflict_get_incoming_new_repos_location(
+ NULL, NULL, &incoming_kind, conflict, scratch_pool,
+ scratch_pool));
+ }
+
+ action = describe_incoming_change(incoming_kind, conflict_action,
+ conflict_operation);
+ if (action)
+ {
+ *incoming_change_description = apr_pstrdup(result_pool, action);
+ }
+ else
+ {
+ /* A catch-all message for very rare or nominally impossible cases.
+ It will not be pretty, but is closer to an internal error than
+ an ordinary user-facing string. */
+ *incoming_change_description = apr_psprintf(result_pool,
+ _("incoming %s %s"),
+ svn_node_kind_to_word(incoming_kind),
+ svn_token__to_word(map_conflict_action,
+ conflict_action));
+ }
+ return SVN_NO_ERROR;
+}
+
+/* Details for tree conflicts involving incoming deletions and replacements. */
+struct conflict_tree_incoming_delete_details
+{
+ /* If not SVN_INVALID_REVNUM, the node was deleted in DELETED_REV. */
+ svn_revnum_t deleted_rev;
+
+ /* If not SVN_INVALID_REVNUM, the node was added in ADDED_REV. The incoming
+ * delete is the result of a reverse application of this addition. */
+ svn_revnum_t added_rev;
+
+ /* The path which was deleted/added relative to the repository root. */
+ const char *repos_relpath;
+
+ /* Author who committed DELETED_REV/ADDED_REV. */
+ const char *rev_author;
+
+ /* New node kind for a replaced node. This is svn_node_none for deletions. */
+ svn_node_kind_t replacing_node_kind;
+
+ /* Move information. If not NULL, this is an array of repos_move_info *
+ * elements. Each element is the head of a move chain which starts in
+ * DELETED_REV or in ADDED_REV (in which case moves should be interpreted
+ * in reverse). */
+ apr_array_header_t *moves;
+
+ /* A map of repos_relpaths and working copy nodes for an incoming move.
+ *
+ * Each key is a "const char *" repository relpath corresponding to a
+ * possible repository-side move destination node in the revision which
+ * is the target revision in case of update and switch, or the merge-right
+ * revision in case of a merge.
+ *
+ * Each value is an apr_array_header_t *.
+ * Each array consists of "const char *" absolute paths to working copy
+ * nodes which correspond to the repository node selected by the map key.
+ * Each such working copy node is a potential local move target which can
+ * be chosen to "follow" the incoming move when resolving a tree conflict.
+ *
+ * This may be an empty hash map in case if there is no move target path
+ * in the working copy. */
+ apr_hash_t *wc_move_targets;
+
+ /* The preferred move target repository relpath. This is our key into
+ * the WC_MOVE_TARGETS map above (can be overridden by the user). */
+ const char *move_target_repos_relpath;
+
+ /* The current index into the list of working copy nodes corresponding to
+ * MOVE_TARGET_REPOS_REPLATH (can be overridden by the user). */
+ int wc_move_target_idx;
+};
+
+/* Get the currently selected repository-side move target path.
+ * If none was selected yet, determine and return a default one. */
+static const char *
+get_moved_to_repos_relpath(
+ struct conflict_tree_incoming_delete_details *details,
+ apr_pool_t *scratch_pool)
+{
+ struct repos_move_info *move;
+
+ if (details->move_target_repos_relpath)
+ return details->move_target_repos_relpath;
+
+ if (details->wc_move_targets && apr_hash_count(details->wc_move_targets) > 0)
+ {
+ svn_sort__item_t item;
+ apr_array_header_t *repos_relpaths;
+
+ repos_relpaths = svn_sort__hash(details->wc_move_targets,
+ svn_sort_compare_items_as_paths,
+ scratch_pool);
+ item = APR_ARRAY_IDX(repos_relpaths, 0, svn_sort__item_t);
+ return (const char *)item.key;
+ }
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ return move->moved_to_repos_relpath;
+}
+
+static const char *
+describe_incoming_deletion_upon_update(
+ struct conflict_tree_incoming_delete_details *details,
+ svn_node_kind_t victim_node_kind,
+ svn_revnum_t old_rev,
+ svn_revnum_t new_rev,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ if (details->replacing_node_kind == svn_node_file ||
+ details->replacing_node_kind == svn_node_symlink)
+ {
+ if (victim_node_kind == svn_node_dir)
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Directory updated from r%ld to r%ld was "
+ "replaced with a file by %s in r%ld."),
+ old_rev, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced directory was moved to "
+ "'^/%s'."), description,
+ get_moved_to_repos_relpath(details, scratch_pool));
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
+ else if (victim_node_kind == svn_node_file ||
+ victim_node_kind == svn_node_symlink)
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("File updated from r%ld to r%ld was replaced "
+ "with a file from another line of history by "
+ "%s in r%ld."),
+ old_rev, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced file was moved to '^/%s'."),
+ description,
+ get_moved_to_repos_relpath(details, scratch_pool));
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
+ else
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Item updated from r%ld to r%ld was replaced "
+ "with a file by %s in r%ld."), old_rev, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced item was moved to '^/%s'."),
+ description,
+ get_moved_to_repos_relpath(details, scratch_pool));
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
+ }
+ else if (details->replacing_node_kind == svn_node_dir)
+ {
+ if (victim_node_kind == svn_node_dir)
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Directory updated from r%ld to r%ld was "
+ "replaced with a directory from another line "
+ "of history by %s in r%ld."),
+ old_rev, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced directory was moved to "
+ "'^/%s'."), description,
+ get_moved_to_repos_relpath(details, scratch_pool));
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
+ else if (victim_node_kind == svn_node_file ||
+ victim_node_kind == svn_node_symlink)
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("File updated from r%ld to r%ld was "
+ "replaced with a directory by %s in r%ld."),
+ old_rev, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced file was moved to '^/%s'."),
+ description,
+ get_moved_to_repos_relpath(details, scratch_pool));
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
+ else
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Item updated from r%ld to r%ld was replaced "
+ "by %s in r%ld."), old_rev, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced item was moved to '^/%s'."),
+ description,
+ get_moved_to_repos_relpath(details, scratch_pool));
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
+ }
+ else
+ {
+ if (victim_node_kind == svn_node_dir)
+ {
+ if (details->moves)
+ {
+ const char *description;
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("Directory updated from r%ld to r%ld was "
+ "moved to '^/%s' by %s in r%ld."),
+ old_rev, new_rev,
+ get_moved_to_repos_relpath(details, scratch_pool),
+ details->rev_author, details->deleted_rev);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ else
+ return apr_psprintf(result_pool,
+ _("Directory updated from r%ld to r%ld was "
+ "deleted by %s in r%ld."),
+ old_rev, new_rev,
+ details->rev_author, details->deleted_rev);
+ }
+ else if (victim_node_kind == svn_node_file ||
+ victim_node_kind == svn_node_symlink)
+ {
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+ const char *description;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("File updated from r%ld to r%ld was moved "
+ "to '^/%s' by %s in r%ld."), old_rev, new_rev,
+ get_moved_to_repos_relpath(details, scratch_pool),
+ details->rev_author, details->deleted_rev);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ else
+ return apr_psprintf(result_pool,
+ _("File updated from r%ld to r%ld was "
+ "deleted by %s in r%ld."), old_rev, new_rev,
+ details->rev_author, details->deleted_rev);
+ }
+ else
+ {
+ if (details->moves)
+ {
+ const char *description;
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("Item updated from r%ld to r%ld was moved "
+ "to '^/%s' by %s in r%ld."), old_rev, new_rev,
+ get_moved_to_repos_relpath(details, scratch_pool),
+ details->rev_author, details->deleted_rev);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ else
+ return apr_psprintf(result_pool,
+ _("Item updated from r%ld to r%ld was "
+ "deleted by %s in r%ld."), old_rev, new_rev,
+ details->rev_author, details->deleted_rev);
+ }
+ }
+}
+
+static const char *
+describe_incoming_reverse_addition_upon_update(
+ struct conflict_tree_incoming_delete_details *details,
+ svn_node_kind_t victim_node_kind,
+ svn_revnum_t old_rev,
+ svn_revnum_t new_rev,
+ apr_pool_t *result_pool)
+{
+ if (details->replacing_node_kind == svn_node_file ||
+ details->replacing_node_kind == svn_node_symlink)
+ {
+ if (victim_node_kind == svn_node_dir)
+ return apr_psprintf(result_pool,
+ _("Directory updated backwards from r%ld to r%ld "
+ "was a file before the replacement made by %s "
+ "in r%ld."), old_rev, new_rev,
+ details->rev_author, details->added_rev);
+ else if (victim_node_kind == svn_node_file ||
+ victim_node_kind == svn_node_symlink)
+ return apr_psprintf(result_pool,
+ _("File updated backwards from r%ld to r%ld was a "
+ "file from another line of history before the "
+ "replacement made by %s in r%ld."),
+ old_rev, new_rev,
+ details->rev_author, details->added_rev);
+ else
+ return apr_psprintf(result_pool,
+ _("Item updated backwards from r%ld to r%ld was "
+ "replaced with a file by %s in r%ld."),
+ old_rev, new_rev,
+ details->rev_author, details->added_rev);
+ }
+ else if (details->replacing_node_kind == svn_node_dir)
+ {
+ if (victim_node_kind == svn_node_dir)
+ return apr_psprintf(result_pool,
+ _("Directory updated backwards from r%ld to r%ld "
+ "was a directory from another line of history "
+ "before the replacement made by %s in "
+ "r%ld."), old_rev, new_rev,
+ details->rev_author, details->added_rev);
+ else if (victim_node_kind == svn_node_file ||
+ victim_node_kind == svn_node_symlink)
+ return apr_psprintf(result_pool,
+ _("File updated backwards from r%ld to r%ld was a "
+ "directory before the replacement made by %s "
+ "in r%ld."), old_rev, new_rev,
+ details->rev_author, details->added_rev);
+ else
+ return apr_psprintf(result_pool,
+ _("Item updated backwards from r%ld to r%ld was "
+ "replaced with a directory by %s in r%ld."),
+ old_rev, new_rev,
+ details->rev_author, details->added_rev);
+ }
+ else
+ {
+ if (victim_node_kind == svn_node_dir)
+ return apr_psprintf(result_pool,
+ _("Directory updated backwards from r%ld to r%ld "
+ "did not exist before it was added by %s in "
+ "r%ld."), old_rev, new_rev,
+ details->rev_author, details->added_rev);
+ else if (victim_node_kind == svn_node_file ||
+ victim_node_kind == svn_node_symlink)
+ return apr_psprintf(result_pool,
+ _("File updated backwards from r%ld to r%ld did "
+ "not exist before it was added by %s in r%ld."),
+ old_rev, new_rev,
+ details->rev_author, details->added_rev);
+ else
+ return apr_psprintf(result_pool,
+ _("Item updated backwards from r%ld to r%ld did "
+ "not exist before it was added by %s in r%ld."),
+ old_rev, new_rev,
+ details->rev_author, details->added_rev);
+ }
+}
+
+static const char *
+describe_incoming_deletion_upon_switch(
+ struct conflict_tree_incoming_delete_details *details,
+ svn_node_kind_t victim_node_kind,
+ const char *old_repos_relpath,
+ svn_revnum_t old_rev,
+ const char *new_repos_relpath,
+ svn_revnum_t new_rev,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ if (details->replacing_node_kind == svn_node_file ||
+ details->replacing_node_kind == svn_node_symlink)
+ {
+ if (victim_node_kind == svn_node_dir)
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Directory switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was replaced with a file by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced directory was moved "
+ "to '^/%s'."), description,
+ get_moved_to_repos_relpath(details, scratch_pool));
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
+ else if (victim_node_kind == svn_node_file ||
+ victim_node_kind == svn_node_symlink)
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("File switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "replaced with a file from another line of "
+ "history by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced file was moved to '^/%s'."),
+ description,
+ get_moved_to_repos_relpath(details, scratch_pool));
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
+ else
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Item switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "replaced with a file by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced item was moved to '^/%s'."),
+ description,
+ get_moved_to_repos_relpath(details, scratch_pool));
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
+ }
+ else if (details->replacing_node_kind == svn_node_dir)
+ {
+ if (victim_node_kind == svn_node_dir)
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Directory switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was replaced with a directory from another "
+ "line of history by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced directory was moved to "
+ "'^/%s'."), description,
+ get_moved_to_repos_relpath(details, scratch_pool));
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
+ else if (victim_node_kind == svn_node_file ||
+ victim_node_kind == svn_node_symlink)
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("File switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was replaced with a directory by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced file was moved to '^/%s'."),
+ description,
+ get_moved_to_repos_relpath(details, scratch_pool));
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
+ else
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Item switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "replaced with a directory by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced item was moved to '^/%s'."),
+ description,
+ get_moved_to_repos_relpath(details, scratch_pool));
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
+ }
+ else
+ {
+ if (victim_node_kind == svn_node_dir)
+ {
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+ const char *description;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("Directory switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was moved to '^/%s' by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ get_moved_to_repos_relpath(details, scratch_pool),
+ details->rev_author, details->deleted_rev);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ else
+ return apr_psprintf(result_pool,
+ _("Directory switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was deleted by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ }
+ else if (victim_node_kind == svn_node_file ||
+ victim_node_kind == svn_node_symlink)
+ {
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+ const char *description;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("File switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "moved to '^/%s' by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ get_moved_to_repos_relpath(details, scratch_pool),
+ details->rev_author, details->deleted_rev);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ else
+ return apr_psprintf(result_pool,
+ _("File switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "deleted by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ }
+ else
+ {
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+ const char *description;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("Item switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "moved to '^/%s' by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ get_moved_to_repos_relpath(details, scratch_pool),
+ details->rev_author, details->deleted_rev);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ else
+ return apr_psprintf(result_pool,
+ _("Item switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "deleted by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ }
+ }
+}
+
+static const char *
+describe_incoming_reverse_addition_upon_switch(
+ struct conflict_tree_incoming_delete_details *details,
+ svn_node_kind_t victim_node_kind,
+ const char *old_repos_relpath,
+ svn_revnum_t old_rev,
+ const char *new_repos_relpath,
+ svn_revnum_t new_rev,
+ apr_pool_t *result_pool)
+{
+ if (details->replacing_node_kind == svn_node_file ||
+ details->replacing_node_kind == svn_node_symlink)
+ {
+ if (victim_node_kind == svn_node_dir)
+ return apr_psprintf(result_pool,
+ _("Directory switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was a file before the replacement made by %s "
+ "in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->added_rev);
+ else if (victim_node_kind == svn_node_file ||
+ victim_node_kind == svn_node_symlink)
+ return apr_psprintf(result_pool,
+ _("File switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas a "
+ "file from another line of history before the "
+ "replacement made by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->added_rev);
+ else
+ return apr_psprintf(result_pool,
+ _("Item switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "replaced with a file by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->added_rev);
+ }
+ else if (details->replacing_node_kind == svn_node_dir)
+ {
+ if (victim_node_kind == svn_node_dir)
+ return apr_psprintf(result_pool,
+ _("Directory switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was a directory from another line of history "
+ "before the replacement made by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->added_rev);
+ else if (victim_node_kind == svn_node_file ||
+ victim_node_kind == svn_node_symlink)
+ return apr_psprintf(result_pool,
+ _("Directory switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was a file before the replacement made by %s "
+ "in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->added_rev);
+ else
+ return apr_psprintf(result_pool,
+ _("Item switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "replaced with a directory by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->added_rev);
+ }
+ else
+ {
+ if (victim_node_kind == svn_node_dir)
+ return apr_psprintf(result_pool,
+ _("Directory switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "did not exist before it was added by %s in "
+ "r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->added_rev);
+ else if (victim_node_kind == svn_node_file ||
+ victim_node_kind == svn_node_symlink)
+ return apr_psprintf(result_pool,
+ _("File switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\ndid "
+ "not exist before it was added by %s in "
+ "r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->added_rev);
+ else
+ return apr_psprintf(result_pool,
+ _("Item switched from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\ndid "
+ "not exist before it was added by %s in "
+ "r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->added_rev);
+ }
+}
+
+static const char *
+describe_incoming_deletion_upon_merge(
+ struct conflict_tree_incoming_delete_details *details,
+ svn_node_kind_t victim_node_kind,
+ const char *old_repos_relpath,
+ svn_revnum_t old_rev,
+ const char *new_repos_relpath,
+ svn_revnum_t new_rev,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ if (details->replacing_node_kind == svn_node_file ||
+ details->replacing_node_kind == svn_node_symlink)
+ {
+ if (victim_node_kind == svn_node_dir)
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Directory merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was replaced with a file by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced directory was moved to "
+ "'^/%s'."), description,
+ get_moved_to_repos_relpath(details, scratch_pool));
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
+ else if (victim_node_kind == svn_node_file ||
+ victim_node_kind == svn_node_symlink)
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("File merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "replaced with a file from another line of "
+ "history by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced file was moved to '^/%s'."),
+ description,
+ get_moved_to_repos_relpath(details, scratch_pool));
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
+ else
+ return apr_psprintf(result_pool,
+ _("Item merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "replaced with a file by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ }
+ else if (details->replacing_node_kind == svn_node_dir)
+ {
+ if (victim_node_kind == svn_node_dir)
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Directory merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was replaced with a directory from another "
+ "line of history by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced directory was moved to "
+ "'^/%s'."), description,
+ get_moved_to_repos_relpath(details, scratch_pool));
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
+ else if (victim_node_kind == svn_node_file ||
+ victim_node_kind == svn_node_symlink)
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("File merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was replaced with a directory by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced file was moved to '^/%s'."),
+ description,
+ get_moved_to_repos_relpath(details, scratch_pool));
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
+ else
+ {
+ const char *description =
+ apr_psprintf(result_pool,
+ _("Item merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "replaced with a directory by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("%s\nThe replaced item was moved to '^/%s'."),
+ description,
+ get_moved_to_repos_relpath(details, scratch_pool));
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ return description;
+ }
+ }
+ else
+ {
+ if (victim_node_kind == svn_node_dir)
+ {
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+ const char *description;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("Directory merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "moved to '^/%s' by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ get_moved_to_repos_relpath(details, scratch_pool),
+ details->rev_author, details->deleted_rev);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ else
+ return apr_psprintf(result_pool,
+ _("Directory merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "deleted by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ }
+ else if (victim_node_kind == svn_node_file ||
+ victim_node_kind == svn_node_symlink)
+ {
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+ const char *description;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("File merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "moved to '^/%s' by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ get_moved_to_repos_relpath(details, scratch_pool),
+ details->rev_author, details->deleted_rev);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ else
+ return apr_psprintf(result_pool,
+ _("File merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "deleted by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ }
+ else
+ {
+ if (details->moves)
+ {
+ struct repos_move_info *move;
+ const char *description;
+
+ move = APR_ARRAY_IDX(details->moves, 0, struct repos_move_info *);
+ description =
+ apr_psprintf(result_pool,
+ _("Item merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "moved to '^/%s' by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ get_moved_to_repos_relpath(details, scratch_pool),
+ details->rev_author, details->deleted_rev);
+ return append_moved_to_chain_description(description,
+ move->next,
+ result_pool,
+ scratch_pool);
+ }
+ else
+ return apr_psprintf(result_pool,
+ _("Item merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\nwas "
+ "deleted by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->deleted_rev);
+ }
+ }
+}
+
+static const char *
+describe_incoming_reverse_addition_upon_merge(
+ struct conflict_tree_incoming_delete_details *details,
+ svn_node_kind_t victim_node_kind,
+ const char *old_repos_relpath,
+ svn_revnum_t old_rev,
+ const char *new_repos_relpath,
+ svn_revnum_t new_rev,
+ apr_pool_t *result_pool)
+{
+ if (details->replacing_node_kind == svn_node_file ||
+ details->replacing_node_kind == svn_node_symlink)
+ {
+ if (victim_node_kind == svn_node_dir)
+ return apr_psprintf(result_pool,
+ _("Directory reverse-merged from\n'^/%s@%ld'\nto "
+ "^/%s@%ld was a file before the replacement "
+ "made by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->added_rev);
+ else if (victim_node_kind == svn_node_file ||
+ victim_node_kind == svn_node_symlink)
+ return apr_psprintf(result_pool,
+ _("File reverse-merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was a file from another line of history before "
+ "the replacement made by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->added_rev);
+ else
+ return apr_psprintf(result_pool,
+ _("Item reverse-merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was replaced with a file by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->added_rev);
+ }
+ else if (details->replacing_node_kind == svn_node_dir)
+ {
+ if (victim_node_kind == svn_node_dir)
+ return apr_psprintf(result_pool,
+ _("Directory reverse-merged from\n'^/%s@%ld'\nto "
+ "^/%s@%ld was a directory from another line "
+ "of history before the replacement made by %s "
+ "in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->added_rev);
+ else if (victim_node_kind == svn_node_file ||
+ victim_node_kind == svn_node_symlink)
+ return apr_psprintf(result_pool,
+ _("Directory reverse-merged from\n'^/%s@%ld'\nto "
+ "^/%s@%ld was a file before the replacement "
+ "made by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->added_rev);
+ else
+ return apr_psprintf(result_pool,
+ _("Item reverse-merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "was replaced with a directory by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->added_rev);
+ }
+ else
+ {
+ if (victim_node_kind == svn_node_dir)
+ return apr_psprintf(result_pool,
+ _("Directory reverse-merged from\n'^/%s@%ld'\nto "
+ "^/%s@%ld did not exist before it was added "
+ "by %s in r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->added_rev);
+ else if (victim_node_kind == svn_node_file ||
+ victim_node_kind == svn_node_symlink)
+ return apr_psprintf(result_pool,
+ _("File reverse-merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "did not exist before it was added by %s in "
+ "r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->added_rev);
+ else
+ return apr_psprintf(result_pool,
+ _("Item reverse-merged from\n"
+ "'^/%s@%ld'\nto\n'^/%s@%ld'\n"
+ "did not exist before it was added by %s in "
+ "r%ld."),
+ old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev,
+ details->rev_author, details->added_rev);
+ }
+}
+
+/* Implements tree_conflict_get_description_func_t. */
+static svn_error_t *
+conflict_tree_get_description_incoming_delete(
+ const char **incoming_change_description,
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *action;
+ svn_node_kind_t victim_node_kind;
+ svn_wc_operation_t conflict_operation;
+ const char *old_repos_relpath;
+ svn_revnum_t old_rev;
+ const char *new_repos_relpath;
+ svn_revnum_t new_rev;
+ struct conflict_tree_incoming_delete_details *details;
+
+ if (conflict->tree_conflict_incoming_details == NULL)
+ return svn_error_trace(conflict_tree_get_incoming_description_generic(
+ incoming_change_description,
+ conflict, ctx, result_pool, scratch_pool));
+
+ conflict_operation = svn_client_conflict_get_operation(conflict);
+ victim_node_kind = svn_client_conflict_tree_get_victim_node_kind(conflict);
+ SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
+ &old_repos_relpath, &old_rev, NULL, conflict, scratch_pool,
+ scratch_pool));
+ SVN_ERR(svn_client_conflict_get_incoming_new_repos_location(
+ &new_repos_relpath, &new_rev, NULL, conflict, scratch_pool,
+ scratch_pool));
+
+ details = conflict->tree_conflict_incoming_details;
+
+ if (conflict_operation == svn_wc_operation_update)
+ {
+ if (details->deleted_rev != SVN_INVALID_REVNUM)
+ {
+ action = describe_incoming_deletion_upon_update(details,
+ victim_node_kind,
+ old_rev,
+ new_rev,
+ result_pool,
+ scratch_pool);
+ }
+ else /* details->added_rev != SVN_INVALID_REVNUM */
+ {
+ /* This deletion is really the reverse change of an addition. */
+ action = describe_incoming_reverse_addition_upon_update(
+ details, victim_node_kind, old_rev, new_rev, result_pool);
+ }
+ }
+ else if (conflict_operation == svn_wc_operation_switch)
+ {
+ if (details->deleted_rev != SVN_INVALID_REVNUM)
+ {
+ action = describe_incoming_deletion_upon_switch(details,
+ victim_node_kind,
+ old_repos_relpath,
+ old_rev,
+ new_repos_relpath,
+ new_rev,
+ result_pool,
+ scratch_pool);
+ }
+ else /* details->added_rev != SVN_INVALID_REVNUM */
+ {
+ /* This deletion is really the reverse change of an addition. */
+ action = describe_incoming_reverse_addition_upon_switch(
+ details, victim_node_kind, old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev, result_pool);
+
+ }
+ }
+ else if (conflict_operation == svn_wc_operation_merge)
+ {
+ if (details->deleted_rev != SVN_INVALID_REVNUM)
+ {
+ action = describe_incoming_deletion_upon_merge(details,
+ victim_node_kind,
+ old_repos_relpath,
+ old_rev,
+ new_repos_relpath,
+ new_rev,
+ result_pool,
+ scratch_pool);
+ }
+ else /* details->added_rev != SVN_INVALID_REVNUM */
+ {
+ /* This deletion is really the reverse change of an addition. */
+ action = describe_incoming_reverse_addition_upon_merge(
+ details, victim_node_kind, old_repos_relpath, old_rev,
+ new_repos_relpath, new_rev, result_pool);
+ }
+ }
+
+ *incoming_change_description = apr_pstrdup(result_pool, action);
+
+ return SVN_NO_ERROR;
+}
+
+/* Baton for find_added_rev(). */
+struct find_added_rev_baton
+{
+ const char *victim_abspath;
+ svn_client_ctx_t *ctx;
+ svn_revnum_t added_rev;
+ const char *repos_relpath;
+ const char *parent_repos_relpath;
+ apr_pool_t *pool;
+};
+
+/* Implements svn_location_segment_receiver_t.
+ * Finds the revision in which a node was added by tracing 'start'
+ * revisions in location segments reported for the node.
+ * If the PARENT_REPOS_RELPATH in the baton is not NULL, only consider
+ * segments in which the node existed somwhere beneath this path. */
+static svn_error_t *
+find_added_rev(svn_location_segment_t *segment,
+ void *baton,
+ apr_pool_t *scratch_pool)
+{
+ struct find_added_rev_baton *b = baton;
+
+ if (b->ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify;
+
+ notify = svn_wc_create_notify(
+ b->victim_abspath,
+ svn_wc_notify_tree_conflict_details_progress,
+ scratch_pool),
+ notify->revision = segment->range_start;
+ b->ctx->notify_func2(b->ctx->notify_baton2, notify, scratch_pool);
+ }
+
+ if (segment->path) /* not interested in gaps */
+ {
+ if (b->parent_repos_relpath == NULL ||
+ svn_relpath_skip_ancestor(b->parent_repos_relpath,
+ segment->path) != NULL)
+ {
+ b->added_rev = segment->range_start;
+ b->repos_relpath = apr_pstrdup(b->pool, segment->path);
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Find conflict details in the case where a revision which added a node was
+ * applied in reverse, resulting in an incoming deletion. */
+static svn_error_t *
+get_incoming_delete_details_for_reverse_addition(
+ struct conflict_tree_incoming_delete_details **details,
+ const char *repos_root_url,
+ const char *old_repos_relpath,
+ svn_revnum_t old_rev,
+ svn_revnum_t new_rev,
+ svn_client_ctx_t *ctx,
+ const char *victim_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_ra_session_t *ra_session;
+ const char *url;
+ const char *corrected_url;
+ svn_string_t *author_revprop;
+ struct find_added_rev_baton b = { 0 };
+
+ url = svn_path_url_add_component2(repos_root_url, old_repos_relpath,
+ scratch_pool);
+ SVN_ERR(svn_client__open_ra_session_internal(&ra_session,
+ &corrected_url,
+ url, NULL, NULL,
+ FALSE,
+ FALSE,
+ ctx,
+ scratch_pool,
+ scratch_pool));
+
+ *details = apr_pcalloc(result_pool, sizeof(**details));
+ b.ctx = ctx;
+ b.victim_abspath = victim_abspath;
+ b.added_rev = SVN_INVALID_REVNUM;
+ b.repos_relpath = NULL;
+ b.parent_repos_relpath = NULL;
+ b.pool = scratch_pool;
+
+ /* Figure out when this node was added. */
+ SVN_ERR(svn_ra_get_location_segments(ra_session, "", old_rev,
+ old_rev, new_rev,
+ find_added_rev, &b,
+ scratch_pool));
+
+ SVN_ERR(svn_ra_rev_prop(ra_session, b.added_rev,
+ SVN_PROP_REVISION_AUTHOR,
+ &author_revprop, scratch_pool));
+ (*details)->deleted_rev = SVN_INVALID_REVNUM;
+ (*details)->added_rev = b.added_rev;
+ (*details)->repos_relpath = apr_pstrdup(result_pool, b.repos_relpath);
+ if (author_revprop)
+ (*details)->rev_author = apr_pstrdup(result_pool, author_revprop->data);
+ else
+ (*details)->rev_author = _("unknown author");
+
+ /* Check for replacement. */
+ (*details)->replacing_node_kind = svn_node_none;
+ if ((*details)->added_rev > 0)
+ {
+ svn_node_kind_t replaced_node_kind;
+
+ SVN_ERR(svn_ra_check_path(ra_session, "",
+ rev_below((*details)->added_rev),
+ &replaced_node_kind, scratch_pool));
+ if (replaced_node_kind != svn_node_none)
+ SVN_ERR(svn_ra_check_path(ra_session, "", (*details)->added_rev,
+ &(*details)->replacing_node_kind,
+ scratch_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Follow each move chain starting a MOVE all the way to the end to find
+ * the possible working copy locations for VICTIM_ABSPATH which corresponds
+ * to VICTIM_REPOS_REPLATH@VICTIM_REVISION.
+ * Add each such location to the WC_MOVE_TARGETS hash table, keyed on the
+ * repos_relpath which is the corresponding move destination in the repository.
+ * This function is recursive. */
+static svn_error_t *
+follow_move_chains(apr_hash_t *wc_move_targets,
+ struct repos_move_info *move,
+ svn_client_ctx_t *ctx,
+ const char *victim_abspath,
+ svn_node_kind_t victim_node_kind,
+ const char *victim_repos_relpath,
+ svn_revnum_t victim_revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ /* If this is the end of a move chain, look for matching paths in
+ * the working copy and add them to our collection if found. */
+ if (move->next == NULL)
+ {
+ apr_array_header_t *candidate_abspaths;
+
+ /* Gather candidate nodes which represent this moved_to_repos_relpath. */
+ SVN_ERR(svn_wc__guess_incoming_move_target_nodes(
+ &candidate_abspaths, ctx->wc_ctx,
+ victim_abspath, victim_node_kind,
+ move->moved_to_repos_relpath,
+ scratch_pool, scratch_pool));
+ if (candidate_abspaths->nelts > 0)
+ {
+ apr_array_header_t *moved_to_abspaths;
+ int i;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+ moved_to_abspaths = apr_array_make(result_pool, 1,
+ sizeof (const char *));
+
+ for (i = 0; i < candidate_abspaths->nelts; i++)
+ {
+ const char *candidate_abspath;
+ const char *repos_root_url;
+ const char *repos_uuid;
+ const char *candidate_repos_relpath;
+ svn_revnum_t candidate_revision;
+
+ svn_pool_clear(iterpool);
+
+ candidate_abspath = APR_ARRAY_IDX(candidate_abspaths, i,
+ const char *);
+ SVN_ERR(svn_wc__node_get_origin(NULL, &candidate_revision,
+ &candidate_repos_relpath,
+ &repos_root_url,
+ &repos_uuid,
+ NULL, NULL,
+ ctx->wc_ctx,
+ candidate_abspath,
+ FALSE,
+ iterpool, iterpool));
+
+ if (candidate_revision == SVN_INVALID_REVNUM)
+ continue;
+
+ /* If the conflict victim and the move target candidate
+ * are not from the same revision we must ensure that
+ * they are related. */
+ if (candidate_revision != victim_revision)
+ {
+ svn_client__pathrev_t *yca_loc;
+ svn_error_t *err;
+
+ err = find_yca(&yca_loc, victim_repos_relpath,
+ victim_revision,
+ candidate_repos_relpath,
+ candidate_revision,
+ repos_root_url, repos_uuid,
+ NULL, ctx, iterpool, iterpool);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ yca_loc = NULL;
+ }
+ else
+ return svn_error_trace(err);
+ }
+
+ if (yca_loc == NULL)
+ continue;
+ }
+
+ APR_ARRAY_PUSH(moved_to_abspaths, const char *) =
+ apr_pstrdup(result_pool, candidate_abspath);
+ }
+ svn_pool_destroy(iterpool);
+
+ svn_hash_sets(wc_move_targets, move->moved_to_repos_relpath,
+ moved_to_abspaths);
+ }
+ }
+ else
+ {
+ int i;
+ apr_pool_t *iterpool;
+
+ /* Recurse into each of the possible move chains. */
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < move->next->nelts; i++)
+ {
+ struct repos_move_info *next_move;
+
+ svn_pool_clear(iterpool);
+
+ next_move = APR_ARRAY_IDX(move->next, i, struct repos_move_info *);
+ SVN_ERR(follow_move_chains(wc_move_targets, next_move,
+ ctx, victim_abspath, victim_node_kind,
+ victim_repos_relpath, victim_revision,
+ result_pool, iterpool));
+
+ }
+ svn_pool_destroy(iterpool);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+init_wc_move_targets(struct conflict_tree_incoming_delete_details *details,
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ int i;
+ const char *victim_abspath;
+ svn_node_kind_t victim_node_kind;
+ const char *incoming_new_repos_relpath;
+ svn_revnum_t incoming_new_pegrev;
+ svn_wc_operation_t operation;
+
+ victim_abspath = svn_client_conflict_get_local_abspath(conflict);
+ victim_node_kind = svn_client_conflict_tree_get_victim_node_kind(conflict);
+ operation = svn_client_conflict_get_operation(conflict);
+ /* ### Should we get the old location in case of reverse-merges? */
+ SVN_ERR(svn_client_conflict_get_incoming_new_repos_location(
+ &incoming_new_repos_relpath, &incoming_new_pegrev,
+ NULL, conflict,
+ scratch_pool, scratch_pool));
+ details->wc_move_targets = apr_hash_make(conflict->pool);
+ for (i = 0; i < details->moves->nelts; i++)
+ {
+ struct repos_move_info *move;
+
+ move = APR_ARRAY_IDX(details->moves, i, struct repos_move_info *);
+ SVN_ERR(follow_move_chains(details->wc_move_targets, move,
+ ctx, victim_abspath,
+ victim_node_kind,
+ incoming_new_repos_relpath,
+ incoming_new_pegrev,
+ conflict->pool, scratch_pool));
+ }
+
+ /* Initialize to the first possible move target. Hopefully,
+ * in most cases there will only be one candidate anyway. */
+ details->move_target_repos_relpath =
+ get_moved_to_repos_relpath(details, scratch_pool);
+ details->wc_move