source: trunk/lib/SVN.cc @ 687

Last change on this file since 687 was 687, checked in by Peter Johansson, 13 years ago

Merged patch release 0.6.6 to trunk. Delta 0.6.6 - 0.6.5

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 10.6 KB
RevLine 
[91]1// $Id: SVN.cc 687 2008-08-04 19:37:10Z peter $
2
3/*
4  Copyright (C) 2006 Jari Häkkinen
[408]5  Copyright (C) 2007 Jari Häkkinen, Peter Johansson
[579]6  Copyright (C) 2008 Peter Johansson
[91]7
[687]8  This file is part of svndigest, http://dev.thep.lu.se/svndigest
[91]9
[149]10  svndigest is free software; you can redistribute it and/or modify it
[91]11  under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
[149]15  svndigest is distributed in the hope that it will be useful, but
[91]16  WITHOUT ANY WARRANTY; without even the implied warranty of
[149]17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
[91]18  General Public License for more details.
19
20  You should have received a copy of the GNU General Public License
21  along with this program; if not, write to the Free Software
22  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
23  02111-1307, USA.
24*/
25
26#include "SVN.h"
27
[185]28#include <map>
[91]29#include <string>
[185]30#include <vector>
[140]31
[642]32#include <cassert>
[225]33
[140]34#include <apr_allocator.h>
[91]35#include <apr_hash.h>
[164]36#include <subversion-1/svn_client.h>
37#include <subversion-1/svn_cmdline.h>
38#include <subversion-1/svn_path.h>
39#include <subversion-1/svn_pools.h>
40#include <subversion-1/svn_wc.h>
[185]41#include <subversion-1/svn_subst.h>
[91]42
43namespace theplu {
[149]44namespace svndigest {
[91]45
46
47  SVN* SVN::instance_=NULL;
48
49
[318]50  SVN::SVN(const std::string& path)
[91]51    : adm_access_(NULL), allocator_(NULL), context_(NULL), pool_(NULL)
52  {
[123]53    svn_error_t* err=NULL;
54
[91]55    // initialize something (APR subsystem and more). The APR
56    // subsystem is automatically destroyed at program exit. In case
57    // of several calls to svn_cmdline_init (ie. several exceptions
58    // thrown and caught with subsequent reinitializatios is safe
59    // memorywise but what about APR internal counters?)
[149]60    if (svn_cmdline_init("svndigest",stderr) != EXIT_SUCCESS)
[318]61      throw SVNException("SVN: svn_cmdline_init failed");
[91]62
63    /// create top-level pool
64    if (apr_allocator_create(&allocator_))
[318]65      throw SVNException("SVN(_allocator_create failed");
[91]66    apr_allocator_max_free_set(allocator_,SVN_ALLOCATOR_RECOMMENDED_MAX_FREE);
67    pool_ = svn_pool_create_ex(NULL, allocator_);
68    apr_allocator_owner_set(allocator_, pool_);
69
70    // initialize the repository access library
[191]71    if ((err=svn_ra_initialize(pool_)))
[318]72      cleanup_failed_init(err, "SVN: svn_ra_initialize failed");
[91]73
74    // Check that users .subversion exist. Should this really be done?
75    // If this check is to be done, we might be forced to support a
76    // command line option to change the location of the .subversion
77    // stuff (compare with the svn binary).
[191]78    if ((err=svn_config_ensure(NULL, pool_)))
[318]79      cleanup_failed_init(err, "SVN: svn_config_ensure failed");
[91]80
81    // create a client context object
[191]82    if ((err=svn_client_create_context(&context_, pool_)))
[318]83      cleanup_failed_init(err, "SVN: svn_client_create_context failed");
[91]84
[191]85    if ((err=svn_config_get_config(&(context_->config), NULL, pool_)))
[318]86      cleanup_failed_init(err, "SVN: svn_config_get_config failed");
[91]87
88    // set up authentication stuff
89    if ((err=svn_cmdline_setup_auth_baton(&(context_->auth_baton), false, NULL,
90                                          NULL, NULL, false,
[191]91                        static_cast<svn_config_t*>(apr_hash_get(context_->config,
[91]92                                                    SVN_CONFIG_CATEGORY_CONFIG,
93                                                    APR_HASH_KEY_STRING)),
94                                          context_->cancel_func,
[191]95                                          context_->cancel_baton, pool_)))
[318]96      cleanup_failed_init(err, "SVN: svn_cmdline_setup_auth_baton failed");
97
98    // Set up svn administration area access. The whole structure is
99    // setup, maybe this is unnecessary?
100    const char* canonical_path=svn_path_internal_style(path.c_str(), pool_);
[579]101    if ((err=svn_wc_adm_open3(&adm_access_, NULL, canonical_path,
102                              false, -1, context_->cancel_func,
103                              context_->cancel_baton, pool_))){
104      if (err->apr_err == SVN_ERR_WC_NOT_DIRECTORY)
105        cleanup_failed_init(err, std::string(err->message), true);
[318]106      cleanup_failed_init(err, "SVN: svn_wc_adm_open3 failed");
[579]107    }
[318]108
109    // get a session to the repository
110    struct root_url_receiver_baton rurb;
111    client_info(path, root_url_receiver, static_cast<void*>(&rurb));
[579]112    if ((err=svn_client_open_ra_session(&ra_session_,
113                                        rurb.path.c_str(),
114                                        context_, pool_)))
[318]115      cleanup_failed_init(err, "SVN: svn_client_open_ra_session failed");
[91]116  }
117
118
119  SVN::~SVN(void)
120  {
[140]121    if (adm_access_)
122      svn_error_clear(svn_wc_adm_close(adm_access_));
[91]123    svn_pool_destroy(pool_);
[579]124    pool_=NULL;
[91]125    // other apr resources acquired in svn_cmdline_init are destroyed
126    // at program exit, ok since SVN is a singleton
127  }
128
129
[318]130  void SVN::cleanup(svn_error_t *err,apr_pool_t *pool,
[579]131                    std::string message, bool mute)
[318]132  {
[579]133    if (!mute)
134      svn_handle_error2(err,stderr,false,"svndigest: ");
[318]135    svn_error_clear(err);
[579]136    if (pool){
[318]137      svn_pool_destroy(pool);
[579]138      pool=NULL;
139    }
[318]140    if (message.length()>0)
141      throw SVNException(message);
142  }
143
144
[579]145  void SVN::cleanup_failed_init(svn_error_t *err, const std::string& message,
146                                bool mute)
[318]147  {
[579]148    cleanup(err,pool_, "", true);
[318]149    throw SVNException(message);
150  }
151
152
[138]153  svn_error_t* SVN::client_blame(const std::string& path,
154                                 svn_client_blame_receiver_t receiver,
[501]155                                 void *baton, svn_revnum_t rev)
156  {
157    svn_opt_revision_t head;
158    head.kind=svn_opt_revision_number;
159    head.value.number=rev;
160    return client_blame_call(path, receiver, baton, head);
161  }
162
163  svn_error_t* SVN::client_blame(const std::string& path,
164                                 svn_client_blame_receiver_t receiver,
[138]165                                 void *baton)
166  {
[501]167    svn_opt_revision_t head;
168    head.kind = ( svn_path_is_url(path.c_str()) ?
169                  svn_opt_revision_head : svn_opt_revision_base );
170    return client_blame_call(path, receiver, baton, head);
171  }
172
173
174  svn_error_t* SVN::client_blame_call(const std::string& path,
175                                      svn_client_blame_receiver_t receiver,
176                                      void *baton, svn_opt_revision_t& head)
177  {
[138]178    // Setup to use all revisions
[501]179    svn_opt_revision_t peg, start;
[138]180    peg.kind=svn_opt_revision_unspecified;
181    start.kind=svn_opt_revision_number;
182    start.value.number=0;
[142]183    apr_pool_t *subpool = svn_pool_create(pool_);
[138]184    svn_error_t* err=svn_client_blame3(path.c_str(), &peg, &start, &head,
[142]185                                       svn_diff_file_options_create(subpool),
186                                       false, receiver, baton, context_,
187                                       subpool);
[191]188    if (err && err->apr_err!=SVN_ERR_CLIENT_IS_BINARY_FILE)
189      // cleanup will throw an exception
190      cleanup(err, subpool, "SVN::client_blame: svn_client_blame3 failed");
[142]191    svn_pool_destroy(subpool);
[138]192    return err;
193  }
194
195
[140]196  void SVN::client_info(const std::string& path, svn_info_receiver_t receiver,
197                        void *baton)
198  {
[142]199    apr_pool_t *subpool = svn_pool_create(pool_);
[140]200    if (svn_error_t *err=svn_client_info(path.c_str(), NULL, NULL, receiver,
[191]201                                         baton, false, context_, subpool))
202      // cleanup will throw an exception
203      cleanup(err, subpool, "repository: svn_client_info failed");
[142]204    svn_pool_destroy(subpool);
[140]205  }
206
207
[318]208  void SVN::client_log(const std::string& path,
209                       svn_log_message_receiver_t receiver, void *baton)
210  {
211    // Allocate space in subpool to pool_ for apr_path (here a string).
212    apr_pool_t *subpool = svn_pool_create(pool_);
213    apr_array_header_t* apr_path=apr_array_make(subpool,1,4);
214    // Copy path to apr_path.
215    (*((const char **) apr_array_push(apr_path))) =
216      apr_pstrdup(subpool, svn_path_internal_style(path.c_str(),subpool));
217
218    // Setup to retrieve all commit logs.
219    svn_opt_revision_t peg, start, head;
220    peg.kind=svn_opt_revision_unspecified;
221    start.kind=svn_opt_revision_number;
222    start.value.number=0;
223    head.kind = ( svn_path_is_url(path.c_str()) ?
224                  svn_opt_revision_head : svn_opt_revision_base );
225    svn_error_t* err=NULL;
226    if ((err=svn_client_log3(apr_path, &peg, &start, &head, 0, false, false,
227                             receiver, baton, context_, subpool)))
228      // cleanupp will throw an exception
229      cleanup(err, subpool, "commit_dates: svn_client_log3 failed");
230    svn_pool_destroy(subpool);
231  }
232
233
[185]234  void SVN::client_proplist(const std::string& path,
235                            std::map<std::string, std::string>& property)
236  {
237    svn_opt_revision_t peg, revision;
238    peg.kind=svn_opt_revision_unspecified;
[318]239    revision.kind = ( svn_path_is_url(path.c_str()) ?
240                      svn_opt_revision_head : svn_opt_revision_base );
[185]241    apr_pool_t *subpool = svn_pool_create(pool_);
242    apr_array_header_t * properties;
243    svn_error_t *err=svn_client_proplist2(&properties, path.c_str(), &peg,
244                                          &revision, false, context_, subpool);
[191]245    if (err)
246      cleanup(err, subpool, "repository: svn_client_proplist2 failed");
247
[185]248    for (int j = 0; j < properties->nelts; ++j) {
249      svn_client_proplist_item_t *item = 
250        ((svn_client_proplist_item_t **)properties->elts)[j];
251      for (apr_hash_index_t *hi = apr_hash_first(subpool, item->prop_hash); hi; 
252           hi = apr_hash_next (hi)) {
253        const void *key;
254        void *val;
255        apr_hash_this (hi, &key, NULL, &val);
256        svn_string_t *value;
257        err=svn_subst_detranslate_string(&value,
258                                         static_cast<const svn_string_t*>(val),
259                                         false, subpool);
[191]260        if (err)
[195]261          cleanup(err, subpool,
262                  path +
263                  " property: svn_subst_detranslate_string failed on key " +
264                  static_cast<const char*>(key));
[185]265        property[static_cast<const char*>(key)]=value->data;
266      }
267    }
268    svn_pool_destroy(subpool);
269  }
270
271
[318]272  SVN* SVN::instance(void)
[233]273  {
[318]274    if (!instance_)
275      throw SVNException("SVN::instance(void): SVN singleton not initialized");
276    return instance_;
[233]277  }
278
279
[318]280  SVN* SVN::instance(const std::string& url)
[140]281  {
[318]282    if (!instance_) {
283      instance_=new SVN(url);
284    }
285    return instance_;
[185]286  }
287
288
[318]289  svn_error_t* SVN::root_url_receiver(void *baton, const char *,
290                                      const svn_info_t *info, apr_pool_t*)
[185]291  {
[318]292    if (!info)
293      throw SVNException(std::string("SVN::url_receriver: ") +
294                         "Failed to acquire an svn info object");
[140]295
[318]296    root_url_receiver_baton* rurb=
297      static_cast<struct root_url_receiver_baton*>(baton);
298    if (info->repos_root_URL)
299      rurb->path=info->repos_root_URL;
[140]300
[318]301    return SVN_NO_ERROR;
[123]302  }
303
304
[91]305  SVN::vc_status SVN::version_controlled(const std::string& path)
306  {
307    svn_wc_status2_t* status=NULL;
[142]308    apr_pool_t *subpool = svn_pool_create(pool_);
[140]309    if (svn_error_t *err=
[142]310        svn_wc_status2(&status,svn_path_internal_style(path.c_str(), subpool),
[191]311                       adm_access_, subpool))
312      // cleanup will throw an exception
313      cleanup(err, subpool,"version_controlled(): svn_config_get_config failed");
[142]314    svn_pool_destroy(subpool);
[91]315
316    if ((status->text_status==svn_wc_status_none) ||
317        (status->text_status==svn_wc_status_unversioned))
318      return unversioned;
319    else if (status->text_status==svn_wc_status_normal)
320      return uptodate;
321
322    return unresolved;
323  }
324
[149]325}} // end of namespace svndigest and namespace theplu
Note: See TracBrowser for help on using the repository browser.