source: trunk/lib/SVN.cc @ 195

Last change on this file since 195 was 195, checked in by Jari Häkkinen, 15 years ago

Added more verbose output. Improved error diagnstics in SVN.

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