1#    Licensed to the Apache Software Foundation (ASF) under one
2#    or more contributor license agreements.  See the NOTICE file
3#    distributed with this work for additional information
4#    regarding copyright ownership.  The ASF licenses this file
5#    to you under the Apache License, Version 2.0 (the
6#    "License"); you may not use this file except in compliance
7#    with the License.  You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11#    Unless required by applicable law or agreed to in writing,
12#    software distributed under the License is distributed on an
13#    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14#    KIND, either express or implied.  See the License for the
15#    specific language governing permissions and limitations
16#    under the License.
17
18
19from csvn.core import *
20from csvn.auth import *
21from ctypes import *
22import csvn.types as _types
23import os, sys
24
25class WC(object):
26    """A SVN working copy."""
27
28    def __init__(self, path="", user=None):
29        """Open a working copy directory relative to path.
30
31        Keyword arguments:
32        path -- path to the working copy (default current working directory)
33        user -- object implementing the user interface representing the user
34            performing the operation (defaults to an instance of the User class)
35        """
36        if user is None:
37            user = User()
38
39        self.pool = Pool()
40        self.iterpool = Pool()
41        self.path = path.replace(os.sep, "/")
42        self.user = user
43
44        self.client = POINTER(svn_client_ctx_t)()
45        svn_client_create_context(byref(self.client), self.pool)
46        self._as_parameter_ = POINTER(svn_ra_session_t)()
47
48        self.user.setup_auth_baton(pointer(self.client.contents.auth_baton))
49
50        self.client[0].notify_func2 = \
51            svn_wc_notify_func2_t(self._notify_func_wrapper)
52        self.client[0].notify_baton2 = c_void_p()
53        self._notify_func = None
54
55        self.client[0].cancel_func = \
56            svn_cancel_func_t(self._cancel_func_wrapper)
57        self.client[0].cancel_baton = c_void_p()
58        self._cancel_func = None
59
60        self.client[0].progress_func = \
61            svn_ra_progress_notify_func_t(self._progress_func_wrapper)
62        self.client[0].progress_baton =  c_void_p()
63        self._progress_func = None
64
65        self._status_func = \
66            svn_wc_status_func2_t(self._status_wrapper)
67        self._status = None
68
69        self._info_func = \
70            svn_info_receiver_t(self._info_wrapper)
71        self._info = None
72
73        self._list_func = \
74            svn_client_list_func_t(self._list_wrapper)
75        self._list = None
76
77        self.client[0].log_msg_func2 = \
78            svn_client_get_commit_log2_t(self._log_func_wrapper)
79        self.client[0].log_msg_baton2 = c_void_p()
80        self._log_func = None
81
82    def close(self):
83        """Close this WC object, releasing any resources."""
84        self.pool.clear()
85
86    def copy(self, src, dest, rev = ""):
87        """Copy src to dest.
88
89        Keyword arguments:
90        src -- current file name
91        dest -- new file name"""
92        opt_rev = svn_opt_revision_t()
93        dummy_rev = svn_opt_revision_t()
94        info = POINTER(svn_commit_info_t)()
95        svn_opt_parse_revision(byref(opt_rev), byref(dummy_rev),
96                               str(rev), self.iterpool)
97
98        svn_client_copy3(byref(info),
99                         self._build_path(src),
100                         byref(opt_rev),
101                         self._build_path(dest),
102                         self.client, self.iterpool)
103        self.iterpool.clear()
104
105    def move(self, src, dest, force = False):
106        """Move src to dest.
107
108        Keyword arguments:
109        src -- current file name
110        dest -- new file name
111        force -- if True, operation will be forced (default False)"""
112
113        info = POINTER(svn_commit_info_t)()
114        svn_client_move3(byref(info),
115                         self._build_path(src),
116                         self._build_path(dest),
117                         force,
118                         self.client, self.iterpool)
119        self.iterpool.clear()
120
121    def delete(self, paths, force = False):
122        """Schedule paths to be deleted.
123
124        Keyword arguments:
125        paths -- list of paths to marked for deletion
126        force -- if True, operation will be forced (default False)"""
127
128        info = POINTER(svn_commit_info_t)()
129        svn_client_delete2(byref(info), self._build_path_list(paths),
130                           force, self.client, self.iterpool)
131        self.iterpool.clear()
132
133    def add(self, path, recurse = True, force = False, no_ignore = False):
134        """Schedule path to be added.
135
136        Keyword arguments:
137        path -- path to be marked for addition
138        recurse -- if True, the contents of directories will also be
139            added (default True)
140        force -- if True, the operation will be forced (default False)
141        no_ignore -- if True, nothing will be ignored (default False)"""
142
143        svn_client_add3(self._build_path(path),
144                        recurse, force, no_ignore, self.client,
145                        self.iterpool)
146        self.iterpool.clear()
147
148    def resolve(self, path, recurse = True):
149        """Resolve a conflict on path.
150
151        This operation does not actually change path at all, it just marks the
152        conflict as resolved.
153
154        Keyword arguments:
155        path -- path to be marked as resolved
156        recurse -- if True, directories will be recursed (default True)"""
157
158        svn_client_resolved(path, recurse, self.client, self.iterpool)
159        self.iterpool.clear()
160
161    def revert(self, paths, recurse = False):
162        """Revert paths to the most recent version.
163
164        Keyword arguments:
165        paths -- list of paths to be reverted
166        recurse -- if True, directories will be recursed (default True)"""
167
168        svn_client_revert(self._build_path_list(paths), recurse,
169                          self.client, self.iterpool)
170        self.iterpool.clear()
171
172    def _build_path_list(self, paths):
173        """Build a list of canonicalized WC paths.
174
175        In general, the user does not need to call this method, paths will be
176        canonicalized as needed for you.
177
178        Returns an array of canonicalized paths.
179
180        Keyword arguments:
181        paths -- list of paths to be canonicalized"""
182
183        # If the user passed in a string, the user made a mistake.
184        # The user should be passing in a list of paths.
185        assert not isinstance(paths, str)
186
187        canonicalized_paths = [self._build_path(path) for path in paths]
188        return _types.Array(String, canonicalized_paths)
189
190    def _build_path(self, path):
191        """Build a canonicalized path.
192
193        In general, the user does not need to call this method, paths will be
194        canonicalized as needed for you.
195
196        Returns a canonical path.
197
198        Keyword arguments:
199        path -- path to be canonicalized"""
200        joined_path = os.path.join(self.path.replace("/", os.sep), path.replace("/", os.sep))
201        return svn_path_canonicalize(joined_path.replace(os.sep, "/"), self.iterpool)
202
203    def set_notify_func(self, notify_func):
204        """Setup a callback so that you can be notified when paths are
205        affected by WC operations.
206
207        When paths are affected, we will call the function with an
208        svn_wc_notify_t object. For details on the contents of an
209        svn_wc_notify_t object, see the documentation for svn_wc_notify_t.
210
211        Keyword arguments:
212        notify_func -- function to be used in the callback"""
213        self._notify_func = notify_func
214
215    # A helper function which invokes the user notify function with
216    # the appropriate arguments.
217    def _notify_func_wrapper(self, baton, notify, pool):
218        """Used to wrap the user callback."""
219        if self._notify_func:
220            pool = Pool()
221            notify = svn_wc_dup_notify(notify, pool)[0]
222            notify.pool = pool
223            self._notify_func(notify[0])
224
225    def set_cancel_func(self, cancel_func):
226        """Setup a callback so that you can cancel operations.
227
228        At various times the cancel function will be called, giving
229        the option of cancelling the operation.
230
231        Keyword arguments:
232        cancel_func -- function to be used in the callback"""
233
234        self._cancel_func = cancel_func
235
236    #Just like _notify_func_wrapper, above.
237    def _cancel_func_wrapper(self, baton):
238        """Used to wrap the cancel callback."""
239        if self._cancel_func:
240            self._cancel_func()
241
242    def set_progress_func(self, progress_func):
243        """Setup a callback for network progress information.
244
245        This callback should accept two integers, being the number of bytes
246        sent and the number of bytes to send.
247
248        Keyword arguments:
249        progress_func -- function to be used in the callback"""
250
251        self._progress_func = progress_func
252
253    def _progress_func_wrapper(self, progress, total, baton, pool):
254        if self._progress_func:
255            self._progress_func(progress, total)
256
257    def diff(self, path1="", revnum1=None, path2=None, revnum2=None,
258             diff_options=[], recurse=True, ignore_ancestry=True,
259             no_diff_deleted=False, ignore_content_type=False,
260             header_encoding="", outfile = sys.stdout, errfile = sys.stderr):
261        """Produce svn diff output that describes the difference between
262        PATH1 at REVISION1 and PATH2 at REVISION2.
263
264        Keyword arguments:
265        path1 -- path to compare in diff (defaults to working copy root)
266        revnum1 -- revision to look at path1 at (defaults to base revision)
267        path2 -- second path to compare in diff (defaults to path1)
268        revnum2 -- revision to look at path2 at (defaults to working revision)
269        diff_options -- list of options to be passed to the diff process
270            (defaults to an empty list)
271        recurse -- if True, contents of directories will also be diffed
272            (default True)
273        ignore_ancestry -- if True then items will not be checked for
274            relatedness before being diffed (default True)
275        no_diff_deleted -- if True then deleted items will not be included in
276            the diff (default False)
277        ignore_content_type -- if True diffs will be generated for binary file
278            types (default False)
279        header_encoding -- generated headers will be encoded using this
280            encoding (defaults to "")
281        outfile -- file to save output to, which can be a file like object
282            (defaults to sys.stdout)
283        errfile -- file to save output to, which can be a file like object
284            (defaults to sys.stderr)"""
285
286        diff_options = self._build_path_list(diff_options)
287
288        rev1 = svn_opt_revision_t()
289        if revnum1:
290            rev1.kind = svn_opt_revision_number
291            rev1.value.number = revnum1
292        else:
293            rev1.kind = svn_opt_revision_base
294
295        rev2 = svn_opt_revision_t()
296        if revnum2:
297            rev2.kind = svn_opt_revision_number
298            rev2.value.number = revnum2
299        else:
300            rev2.kind = svn_opt_revision_working
301
302        path1 = self._build_path(path1)
303        if path2:
304            path2 = self._build_path(path2)
305        else:
306            path2 = path1
307
308        # Create temporary objects for output and errors
309        apr_outfile = _types.APRFile(outfile)
310        apr_errfile = _types.APRFile(errfile)
311
312        svn_client_diff3(diff_options, path1, byref(rev1), path2, byref(rev2), recurse,
313            ignore_ancestry, no_diff_deleted, ignore_content_type,
314            header_encoding, apr_outfile, apr_errfile, self.client,
315            self.iterpool)
316
317        # Close the APR wrappers
318        apr_outfile.close()
319        apr_errfile.close()
320
321        self.iterpool.clear()
322
323    def cleanup(self, path=""):
324        """Recursively cleanup the working copy.
325
326        Cleanup means: finish any incomplete operations and release all locks.
327
328        Keyword arguments:
329        path -- path to cleanup (defaults to WC root)"""
330        svn_client_cleanup(self._build_path(path), self.client,
331                            self.iterpool)
332
333        self.iterpool.clear()
334
335    def export(self, from_path, to_path, overwrite=False,
336                ignore_externals=True, recurse=True, eol=NULL):
337        """Export from_path to to_path.
338
339        An export creates a clean copy of the exported items, with no
340        subversion metadata.
341
342        Keyword arguments:
343        from_path -- path to export
344        to_path -- location to export to
345        overwrite -- if True, existing items at to_path will be overwritten
346            (default False)
347        ignore_externals -- if True, export does not include externals (default
348            True)
349        recurse -- if True, directory contents will also be exported (default
350            True)
351        eol -- End of line character to use (defaults to standard eol marker)"""
352
353        rev = svn_opt_revision_t()
354        peg_rev = svn_opt_revision_t()
355
356        svn_client_export3(POINTER(svn_revnum_t)(),
357                           self._build_path(from_path),
358                           self._build_path(to_path), byref(peg_rev),
359                           byref(rev), overwrite, ignore_externals, recurse,
360                           eol, self.client, self.iterpool)
361
362        self.iterpool.clear()
363
364    def resolved(self, path="", recursive=True):
365        """Resolve a conflict on PATH. Marks the conflict as resolved, does
366        not change the text of PATH.
367
368        If RECURSIVE is True (True by default) then directories will be
369        recursed."""
370        svn_client_resolved(self._build_path(path), recursive, self.client,
371                            self.iterpool)
372        self.iterpool.clear()
373
374    def mkdir(self, paths):
375        """Create directories in the working copy.
376
377        Keyword arguments:
378        paths -- list of paths to be created"""
379        paths = self._build_path_list(paths)
380
381        # The commit info shouldn't matter, this is a method of the WC
382        # class, so it isn't intended for remote operations.
383        info = POINTER(svn_commit_info_t)()
384
385        svn_client_mkdir2(byref(info), paths, self.client, self.iterpool)
386
387        self.iterpool.clear()
388
389    def propset(self, propname, propval, target="", recurse=True,
390                skip_checks=False):
391        """Set propname to propval for target.
392
393        Keyword arguments:
394        propname -- name of property to be set
395        propval -- value to set property to
396        target -- path to set property for (default WC root)
397        recurse -- if True and target is a directory the operation will recurse
398            setting the property for the contents of that directory (default
399            True)
400        skip_checks -- if True, no sanity checks will be performed on propname
401            (default False)"""
402
403        svn_client_propset2(propname,
404                            svn_string_create(propval, self.iterpool),
405                            self._build_path(target), recurse, skip_checks,
406                            self.client, self.iterpool)
407
408        self.iterpool.clear()
409
410    def proplist(self, target="", recurse=True):
411        """List the values of the normal properties of target.
412
413        Returns an array of svn_client_proplist_item_t objects.
414
415        Keyword arguments:
416        target -- path to list properties for (defaults to WC root)
417        recurse -- if True, the operation will recurse (default True)"""
418        peg_revision = svn_opt_revision_t()
419        peg_revision.kind = svn_opt_revision_unspecified
420
421        revision = svn_opt_revision_t()
422        revision.kind = svn_opt_revision_working
423
424        props = _types.Array(svn_client_proplist_item_t)
425
426        svn_client_proplist2(byref(props.header), self._build_path(target),
427                             byref(peg_revision), byref(revision), recurse,
428                             self.client, props.pool)
429
430        self.iterpool.clear()
431
432        return props
433
434    def propget(self, propname, target="", recurse=True):
435        """Get the value of propname for target.
436
437        Returns a hash the keys of which are file paths and the values are the
438        value of PROPNAME for the corresponding file. The values of the hash
439        are c_char_p objects, which can be treated much like strings.
440
441        Keyword arguments:
442        propname -- name of property to get
443        target -- item to get properties for (defaults to Wc root)
444        recurse -- if True, operation will recurse into directories (default
445            True)"""
446        peg_revision = svn_opt_revision_t()
447        peg_revision.kind = svn_opt_revision_unspecified
448
449        revision = svn_opt_revision_t()
450        revision.kind = svn_opt_revision_working
451
452        props = _types.Hash(c_char_p)
453
454        svn_client_propget2(byref(props.hash), propname,
455                    self._build_path(target), byref(peg_revision),
456                    byref(revision), recurse, self.client, props.pool)
457
458        self.iterpool.clear()
459        return props
460
461    # Internal method to wrap status callback.
462    def _status_wrapper(self, baton, path, status):
463        """Wrapper for status callback."""
464        if self._status:
465            pool = Pool()
466            status = svn_wc_dup_status2(status, pool)[0]
467            status.pool = pool
468            self._status(path, status)
469
470    def set_status_func(self, status):
471        """Set a callback function to be used the next time the status method
472        is called.
473
474        Keyword arguments:
475        status -- function to be used in status callback"""
476        self._status = status
477
478    def status(self, path="", status=None, recurse=True,
479                get_all=False, update=False, no_ignore=False,
480                ignore_externals=False):
481        """Get the status on path using callback to status.
482
483        The status callback (which can be set when this method is called or
484        earlier) will be called for each item.
485
486        Keyword arguments:
487        path -- items to get status for (defaults to WC root)
488        status -- callback to be used (defaults to previously registered
489            callback)
490        recurse -- if True, the contents of directories will also be checked
491            (default True)
492        get_all -- if True, all entries will be retrieved, otherwise only
493            interesting entries (local modifications and/or out-of-date) will
494            be retrieved (default False)
495        update -- if True, the repository will be contacted to get information
496            about out-of-dateness and a revision number will be returned to
497            indicate which revision the Wc was compared against (default False)
498        no_ignore -- if True, nothing is ignored (default False)
499        ignore_externals -- if True, externals will be ignored (default False)
500        """
501        rev = svn_opt_revision_t()
502        rev.kind = svn_opt_revision_working
503
504        if status:
505            self.set_status_func(status)
506
507        result_rev = svn_revnum_t()
508        svn_client_status2(byref(result_rev), self._build_path(path),
509                           byref(rev), self._status_func,
510                           c_void_p(), recurse, get_all,
511                           update, no_ignore, ignore_externals, self.client,
512                           self.iterpool)
513
514        self.iterpool.clear()
515
516        return result_rev
517
518    def _info_wrapper(self, baton, path, info, pool):
519        """Internal wrapper for info method callbacks."""
520        if self._info:
521            pool = Pool()
522            info = svn_info_dup(info, pool)[0]
523            info.pool = pool
524            self._info(path, info)
525
526    def set_info_func(self, info):
527        """Set a callback to be used to process svn_info_t objects during
528        calls to the info method.
529
530        The callback function should accept a path and a svn_info_t object as
531        arguments.
532
533        Keyword arguments:
534        info -- callback to be used to process information during a call to
535            the info method."""
536        self._info = info
537
538    def info(self, path="", recurse=True, info_func=None):
539        """Get info about path.
540
541        Keyword arguments:
542        path -- path to get info about (defaults to WC root)
543        recurse -- if True, directories will be recursed
544        info_func -- callback function to use (defaults to previously
545            registered callback)"""
546
547        if info_func:
548            self.set_info_func(info_func)
549
550        svn_client_info(self._build_path(path), NULL, NULL, self._info_func,
551                        c_void_p(), recurse, self.client,
552                        self.iterpool)
553
554        self.iterpool.clear()
555
556    def checkout(self, url, revnum=None, path=None, recurse=True,
557                 ignore_externals=False):
558        """Checkout a new working copy.
559
560        Keyword arguments:
561        url -- url to check out from, should be of the form url@peg_revision
562        revnum -- revision number to check out
563        path -- location to checkout to (defaults to WC root)
564        ignore_externals -- if True, externals will not be included in checkout
565            (default False)"""
566        rev = svn_opt_revision_t()
567        if revnum is not None:
568            rev.kind = svn_opt_revision_number
569            rev.value.number = revnum
570        else:
571            rev.kind = svn_opt_revision_head
572
573        peg_rev = svn_opt_revision_t()
574        URL = String("")
575        svn_opt_parse_path(byref(peg_rev), byref(URL), url, self.iterpool)
576
577        if not path:
578            path = self.path
579        canon_path = svn_path_canonicalize(path, self.iterpool)
580
581        result_rev = svn_revnum_t()
582
583        svn_client_checkout2(byref(result_rev), URL, canon_path,
584                             byref(peg_rev), byref(rev), recurse,
585                             ignore_externals, self.client, self.iterpool)
586        self.iterpool.clear()
587
588    def set_log_func(self, log_func):
589        """Register a callback to get a log message for commit and commit-like
590        operations.
591
592        LOG_FUNC should take an array as an argument, which holds the files to
593        be committed. It should return a list of the form [LOG, FILE] where LOG
594        is a log message and FILE is the temporary file, if one was created
595        instead of a log message. If LOG is None, the operation will be
596        canceled and FILE will be treated as the temporary file holding the
597        temporary commit message.
598
599        Keyword arguments:
600        log_func -- callback to be used for getting log messages"""
601        self._log_func = log_func
602
603    def _log_func_wrapper(self, log_msg, tmp_file, commit_items, baton, pool):
604        """Internal wrapper for log function callback."""
605        if self._log_func:
606            [log, file] = self._log_func(_types.Array(String, commit_items))
607
608            if log:
609                log_msg = pointer(c_char_p(log))
610            else:
611                log_msg = NULL
612
613            if file:
614                tmp_file = pointer(c_char_p(file))
615            else:
616                tmp_file = NULL
617
618    def commit(self, paths=[""], recurse=True, keep_locks=False):
619        """Commit changes in the working copy.
620
621        Keyword arguments:
622        paths -- list of paths that should be committed (defaults to WC root)
623        recurse -- if True, the contents of directories to be committed will
624            also be committed (default True)
625        keep_locks -- if True, locks will not be released during commit
626            (default False)"""
627        commit_info = POINTER(svn_commit_info_t)()
628        pool = Pool()
629        svn_client_commit3(byref(commit_info), self._build_path_list(paths),
630                           recurse, keep_locks, self.client, pool)
631        commit_info[0].pool = pool
632        return commit_info[0]
633
634    def update(self, paths=[""], revnum=None, recurse=True,
635                ignore_externals=True):
636        """Update paths to a given revision number.
637
638        Returns an array of revision numbers to which the revision number was
639        resolved.
640
641        Keyword arguments:
642        paths -- list of path to be updated (defaults to WC root)
643        revnum -- revision number to update to (defaults to head revision)
644        recurse -- if True, the contents of directories will also be updated
645                   (default True)
646        ignore_externals -- if True, externals will not be updated (default
647                            True)"""
648
649        rev = svn_opt_revision_t()
650        if revnum is not None:
651            rev.kind = svn_opt_revision_number
652            rev.value.number = revnum
653        else:
654            rev.kind = svn_opt_revision_head
655
656        result_revs = _types.Array(svn_revnum_t, svn_revnum_t())
657
658        svn_client_update2(byref(result_revs.header),
659                self._build_path_list(paths), byref(rev), recurse,
660                ignore_externals, self.client, self.iterpool)
661
662        self.iterpool.clear()
663        return result_revs
664
665    #internal method to wrap ls callbacks
666    def _list_wrapper(self, baton, path, dirent, abs_path, pool):
667        """Internal method to wrap list callback."""
668        if self._list:
669            pool = Pool()
670            dirent = svn_dirent_dup(dirent, pool)[0]
671            lock = svn_lock_dup(lock, pool)[0]
672            dirent.pool = pool
673            lock.pool = pool
674            self._list(path, dirent, lock, abs_path)
675
676    def set_list_func(self, list_func):
677        """Set the callback function for list operations.
678
679        Keyword arguments:
680        list_func -- function to be used for callbacks"""
681        self._list = list_func
682
683    def list(self, path="", recurse=True, fetch_locks=False, list_func=None):
684        """List items in the working copy using a callback.
685
686        Keyword arguments:
687        path -- path to list (defaults to WC root)
688        recurse -- if True, list contents of directories as well (default True)
689        fetch_locks -- if True, get information from the repository about locks
690            (default False)
691        list_func -- callback to be used (defaults to previously registered
692            callback)"""
693        peg = svn_opt_revision_t()
694        peg.kind = svn_opt_revision_working
695
696        rev = svn_opt_revision_t()
697        rev.kind = svn_opt_revision_working
698
699        if list_func:
700            self.set_list_func(list_func)
701
702        svn_client_list(self._build_path(path), byref(peg), byref(rev),
703            recurse, SVN_DIRENT_ALL, fetch_locks, self._list_func,
704            c_void_p(), self.client, self.iterpool)
705
706        self.iterpool.clear()
707
708    def relocate(self, from_url, to_url, dir="", recurse=True):
709        """Modify a working copy directory, changing repository URLs that begin
710        with FROM_URL to begin with TO_URL instead, recursing into
711        subdirectories if RECURSE is True (True by default).
712
713        Keyword arguments:
714        from_url -- url to be replaced, if this url is matched at the beginning
715            of a url it will be replaced with to_url
716        to_url -- url to replace from_url
717        dir -- directory to relocate (defaults to WC root)
718        recurse -- if True, directories will be recursed (default True)"""
719
720        svn_client_relocate(self._build_path(dir), from_url, to_url, recurse,
721                            self.client, self.iterpool)
722
723        self.iterpool.clear()
724
725    def switch(self, path, url, revnum=None, recurse=True):
726        """Switch part of a working copy to a new url.
727
728        Keyword arguments:
729        path -- path to be changed to a new url
730        url -- url to be used
731        revnum -- revision to be switched to (defaults to head revision)
732        recurse -- if True, the operation will recurse (default True)"""
733        result_rev = svn_revnum_t()
734
735        revision = svn_opt_revision_t()
736        if revnum:
737            revision.kind = svn_opt_revision_number
738            revision.value.number = revnum
739        else:
740            revision.kind = svn_opt_revision_head
741
742        svn_client_switch(byref(result_rev),
743                  self._build_path(path), url, byref(revision),
744                  recurse, self.client, self.iterpool)
745        self.iterpool.clear()
746
747    def lock(self, paths, comment=NULL, steal_lock=False):
748        """Lock items.
749
750        Keyword arguments:
751        paths -- list of paths to be locked, may be WC paths (in which
752            case this is a local operation) or urls (in which case this is a
753            network operation)
754        comment -- comment for the lock (default no comment)
755        steal_lock -- if True, the lock will be created even if the file is
756            already locked (default False)"""
757        targets = self._build_path_list(paths)
758        svn_client_lock(targets, comment, steal_lock, self.client, self.iterpool)
759        self.iterpool.clear()
760
761    def unlock(self, paths, break_lock=False):
762        """Unlock items.
763
764        Keyword arguments:
765        paths - list of paths to be unlocked, may be WC paths (in which
766            case this is a local operation) or urls (in which case this is a
767            network operation)
768        break_lock -- if True, locks will be broken (default False)"""
769        targets = self._build_path_list(paths)
770        svn_client_unlock(targets, break_lock, self.client, self.iterpool)
771        self.iterpool.clear()
772
773    def merge(self, source1, revnum1, source2, revnum2, target_wcpath,
774                recurse=True, ignore_ancestry=False, force=False,
775                dry_run=False, merge_options=[]):
776        """Merge changes.
777
778        This method merges the changes from source1@revnum1 to
779        source2@revnum2 into target_wcpath.
780
781        Keyword arguments:
782        source1 -- path for beginning revision of changes to be merged
783        revnum1 -- starting revnum
784        source2 -- path for ending revision of changes to merge
785        revnum2 -- ending revnum
786        target_wcpath -- path to apply changes to
787        recurse -- if True, apply changes recursively
788        ignore_ancestry -- if True, relatedness will not be checked (False by
789            default)
790        force -- if False and the merge involves deleting locally modified
791            files the merge will fail (default False)
792        dry_run -- if True, notification will be provided but no local files
793            will be modified (default False)
794        merge_options -- a list of options to be passed to the diff process
795            (default no options)"""
796        revision1 = svn_opt_revision_t()
797        revision1.kind = svn_opt_revision_number
798        revision1.value.number = revnum1
799
800        revision2 = svn_opt_revision_t()
801        revision2.kind = svn_opt_revision_number
802        revision2.value.number = revnum2
803
804        merge_options = _types.Array(c_char_p, merge_options)
805
806        svn_client_merge2(source1, byref(revision1), source2, byref(revision2),
807            target_wcpath, recurse, ignore_ancestry, force, dry_run,
808            merge_options.header, self.client, self.iterpool)
809
810        self.iterpool.clear()
811