1/* 2 * revisions.c: discovering revisions 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24 25 26#include <apr_pools.h> 27 28#include "svn_error.h" 29#include "svn_ra.h" 30#include "svn_dirent_uri.h" 31#include "svn_path.h" 32#include "client.h" 33 34#include "svn_private_config.h" 35#include "private/svn_wc_private.h" 36 37 38 39 40svn_error_t * 41svn_client__get_revision_number(svn_revnum_t *revnum, 42 svn_revnum_t *youngest_rev, 43 svn_wc_context_t *wc_ctx, 44 const char *local_abspath, 45 svn_ra_session_t *ra_session, 46 const svn_opt_revision_t *revision, 47 apr_pool_t *scratch_pool) 48{ 49 switch (revision->kind) 50 { 51 case svn_opt_revision_unspecified: 52 *revnum = SVN_INVALID_REVNUM; 53 break; 54 55 case svn_opt_revision_number: 56 *revnum = revision->value.number; 57 break; 58 59 case svn_opt_revision_head: 60 /* If our caller provided a value for HEAD that he wants us to 61 use, we'll use it. Otherwise, we have to query the 62 repository (and possible return our fetched value in 63 *YOUNGEST_REV, too). */ 64 if (youngest_rev && SVN_IS_VALID_REVNUM(*youngest_rev)) 65 { 66 *revnum = *youngest_rev; 67 } 68 else 69 { 70 if (! ra_session) 71 return svn_error_create(SVN_ERR_CLIENT_RA_ACCESS_REQUIRED, 72 NULL, NULL); 73 SVN_ERR(svn_ra_get_latest_revnum(ra_session, revnum, scratch_pool)); 74 if (youngest_rev) 75 *youngest_rev = *revnum; 76 } 77 break; 78 79 case svn_opt_revision_working: 80 case svn_opt_revision_base: 81 { 82 svn_error_t *err; 83 84 /* Sanity check. */ 85 if (local_abspath == NULL) 86 return svn_error_create(SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED, 87 NULL, NULL); 88 89 /* The BASE, COMMITTED, and PREV revision keywords do not 90 apply to URLs. */ 91 if (svn_path_is_url(local_abspath)) 92 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, 93 _("PREV, BASE, or COMMITTED revision " 94 "keywords are invalid for URL")); 95 96 err = svn_wc__node_get_origin(NULL, revnum, NULL, NULL, NULL, NULL, 97 NULL, 98 wc_ctx, local_abspath, TRUE, 99 scratch_pool, scratch_pool); 100 101 /* Return the same error as older code did (before and at r935091). 102 At least svn_client_proplist4 promises SVN_ERR_ENTRY_NOT_FOUND. */ 103 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 104 { 105 svn_error_clear(err); 106 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL, 107 _("'%s' is not under version control"), 108 svn_dirent_local_style(local_abspath, 109 scratch_pool)); 110 } 111 else 112 SVN_ERR(err); 113 114 if (! SVN_IS_VALID_REVNUM(*revnum)) 115 return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL, 116 _("Path '%s' has no committed " 117 "revision"), 118 svn_dirent_local_style(local_abspath, 119 scratch_pool)); 120 } 121 break; 122 123 case svn_opt_revision_committed: 124 case svn_opt_revision_previous: 125 { 126 /* Sanity check. */ 127 if (local_abspath == NULL) 128 return svn_error_create(SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED, 129 NULL, NULL); 130 131 /* The BASE, COMMITTED, and PREV revision keywords do not 132 apply to URLs. */ 133 if (svn_path_is_url(local_abspath)) 134 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, 135 _("PREV, BASE, or COMMITTED revision " 136 "keywords are invalid for URL")); 137 138 SVN_ERR(svn_wc__node_get_changed_info(revnum, NULL, NULL, 139 wc_ctx, local_abspath, 140 scratch_pool, scratch_pool)); 141 if (! SVN_IS_VALID_REVNUM(*revnum)) 142 return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL, 143 _("Path '%s' has no committed " 144 "revision"), 145 svn_dirent_local_style(local_abspath, 146 scratch_pool)); 147 148 if (revision->kind == svn_opt_revision_previous) 149 { 150 if (*revnum == 0) 151 return svn_error_createf( 152 SVN_ERR_CLIENT_BAD_REVISION, NULL, 153 _("Path '%s' has no previous revision"), 154 svn_dirent_local_style(local_abspath, scratch_pool)); 155 --(*revnum); 156 } 157 } 158 break; 159 160 case svn_opt_revision_date: 161 /* ### When revision->kind == svn_opt_revision_date, is there an 162 ### optimization such that we can compare 163 ### revision->value->date with the committed-date in the 164 ### entries file (or rather, with some range of which 165 ### committed-date is one endpoint), and sometimes avoid a 166 ### trip over the RA layer? The only optimizations I can 167 ### think of involve examining other entries to build a 168 ### timespan across which committed-revision is known to be 169 ### the head, but it doesn't seem worth it. -kff */ 170 if (! ra_session) 171 return svn_error_create(SVN_ERR_CLIENT_RA_ACCESS_REQUIRED, NULL, NULL); 172 SVN_ERR(svn_ra_get_dated_revision(ra_session, revnum, 173 revision->value.date, scratch_pool)); 174 break; 175 176 default: 177 return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL, 178 _("Unrecognized revision type requested for " 179 "'%s'"), 180 svn_dirent_local_style(local_abspath, 181 scratch_pool)); 182 } 183 184 /* Final check -- if our caller provided a youngest revision, and 185 the number we wound up with (after talking to the server) is younger 186 than that revision, we need to stick to our caller's idea of "youngest". 187 */ 188 if (youngest_rev 189 && (revision->kind == svn_opt_revision_head 190 || revision->kind == svn_opt_revision_date) 191 && SVN_IS_VALID_REVNUM(*youngest_rev) 192 && SVN_IS_VALID_REVNUM(*revnum) 193 && (*revnum > *youngest_rev)) 194 *revnum = *youngest_rev; 195 196 return SVN_NO_ERROR; 197} 198