source: trunk/lib/SVN.cc @ 233

Last change on this file since 233 was 233, checked in by Peter Johansson, 15 years ago

Refs #117. Initial ci of SVNlog class. Class should be used and functions in SVN could be removed.

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