1 /*
2 * update-cmd.c -- Bring work tree in sync with repository
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
27
28 /*** Includes. ***/
29
30 #include "svn_pools.h"
31 #include "svn_client.h"
32 #include "svn_path.h"
33 #include "svn_error_codes.h"
34 #include "svn_error.h"
35 #include "cl.h"
36
37 #include "svn_private_config.h"
38
39
40 /*** Code. ***/
41
42 /* Print an update summary when there's more than one target to report
43 about. Each (const char *) path in TARGETS is an absolute or relative
44 dirent, and each (svn_revnum_t) entry in RESULT_REVS is the corresponding
45 updated revision, or SVN_INVALID_REVNUM if not a valid target. */
46 static svn_error_t *
print_update_summary(apr_array_header_t * targets,apr_array_header_t * result_revs,apr_pool_t * scratch_pool)47 print_update_summary(apr_array_header_t *targets,
48 apr_array_header_t *result_revs,
49 apr_pool_t *scratch_pool)
50 {
51 int i;
52 const char *path_prefix;
53 apr_pool_t *iterpool;
54 svn_boolean_t printed_header = FALSE;
55
56 if (targets->nelts < 2)
57 return SVN_NO_ERROR;
58
59 SVN_ERR(svn_dirent_get_absolute(&path_prefix, "", scratch_pool));
60
61 iterpool = svn_pool_create(scratch_pool);
62
63 for (i = 0; i < targets->nelts; i++)
64 {
65 const char *path = APR_ARRAY_IDX(targets, i, const char *);
66 svn_revnum_t rev = SVN_INVALID_REVNUM;
67
68 svn_pool_clear(iterpool);
69
70 /* PATH shouldn't be a URL. */
71 SVN_ERR_ASSERT(! svn_path_is_url(path));
72
73 /* Grab the result revision from the corresponding slot in our
74 RESULT_REVS array. */
75 if (i < result_revs->nelts)
76 rev = APR_ARRAY_IDX(result_revs, i, svn_revnum_t);
77
78 /* No result rev? We must have skipped this path. At any rate,
79 nothing to report here. */
80 if (! SVN_IS_VALID_REVNUM(rev))
81 continue;
82
83 /* Convert to an absolute path if it's not already. */
84 if (! svn_dirent_is_absolute(path))
85 SVN_ERR(svn_dirent_get_absolute(&path, path, iterpool));
86
87 /* Print an update summary for this target, removing the current
88 working directory prefix from PATH (if PATH is at or under
89 $CWD), and converting the path to local style for display. */
90 if (! printed_header)
91 {
92 SVN_ERR(svn_cmdline_printf(scratch_pool,
93 _("Summary of updates:\n")));
94 printed_header = TRUE;
95 }
96
97 SVN_ERR(svn_cmdline_printf(iterpool, _(" Updated '%s' to r%ld.\n"),
98 svn_cl__local_style_skip_ancestor(
99 path_prefix, path, iterpool),
100 rev));
101 }
102
103 svn_pool_destroy(iterpool);
104 return SVN_NO_ERROR;
105 }
106
107 /* This implements the `svn_opt_subcommand_t' interface. */
108 svn_error_t *
svn_cl__update(apr_getopt_t * os,void * baton,apr_pool_t * scratch_pool)109 svn_cl__update(apr_getopt_t *os,
110 void *baton,
111 apr_pool_t *scratch_pool)
112 {
113 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
114 svn_cl__conflict_stats_t *conflict_stats =
115 ((svn_cl__cmd_baton_t *) baton)->conflict_stats;
116 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
117 apr_array_header_t *targets;
118 svn_depth_t depth;
119 svn_boolean_t depth_is_sticky;
120 struct svn_cl__check_externals_failed_notify_baton nwb;
121 apr_array_header_t *result_revs;
122 apr_array_header_t *conflicted_paths;
123 svn_error_t *err = SVN_NO_ERROR;
124 svn_error_t *externals_err = SVN_NO_ERROR;
125
126 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
127 opt_state->targets,
128 ctx, FALSE,
129 scratch_pool));
130
131 /* Add "." if user passed 0 arguments */
132 svn_opt_push_implicit_dot_target(targets, scratch_pool);
133
134 SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, scratch_pool));
135
136 SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
137
138 /* If using changelists, convert targets into a set of paths that
139 match the specified changelist(s). */
140 if (opt_state->changelists)
141 {
142 svn_depth_t cl_depth = opt_state->depth;
143 if (cl_depth == svn_depth_unknown)
144 cl_depth = svn_depth_infinity;
145 SVN_ERR(svn_cl__changelist_paths(&targets,
146 opt_state->changelists, targets,
147 cl_depth, ctx, scratch_pool,
148 scratch_pool));
149 }
150
151 /* Deal with depthstuffs. */
152 if (opt_state->set_depth != svn_depth_unknown)
153 {
154 depth = opt_state->set_depth;
155 depth_is_sticky = TRUE;
156 }
157 else
158 {
159 depth = opt_state->depth;
160 depth_is_sticky = FALSE;
161 }
162
163 nwb.wrapped_func = ctx->notify_func2;
164 nwb.wrapped_baton = ctx->notify_baton2;
165 nwb.had_externals_error = FALSE;
166 ctx->notify_func2 = svn_cl__check_externals_failed_notify_wrapper;
167 ctx->notify_baton2 = &nwb;
168
169 SVN_ERR(svn_client_update4(&result_revs, targets,
170 &(opt_state->start_revision),
171 depth, depth_is_sticky,
172 opt_state->ignore_externals,
173 opt_state->force,
174 opt_state->adds_as_modification,
175 opt_state->parents,
176 ctx, scratch_pool));
177
178 if (nwb.had_externals_error)
179 externals_err = svn_error_create(SVN_ERR_CL_ERROR_PROCESSING_EXTERNALS,
180 NULL,
181 _("Failure occurred processing one or "
182 "more externals definitions"));
183
184 /* Run the interactive resolver if conflicts were raised. */
185 SVN_ERR(svn_cl__conflict_stats_get_paths(&conflicted_paths, conflict_stats,
186 scratch_pool, scratch_pool));
187 if (conflicted_paths)
188 SVN_ERR(svn_cl__walk_conflicts(conflicted_paths, conflict_stats,
189 opt_state, ctx, scratch_pool));
190
191 if (! opt_state->quiet)
192 {
193 err = print_update_summary(targets, result_revs, scratch_pool);
194 if (err)
195 return svn_error_compose_create(externals_err, err);
196
197 /* ### Layering problem: This call assumes that the baton we're
198 * passing is the one that was originally provided by
199 * svn_cl__get_notifier(), but that isn't promised. */
200 err = svn_cl__notifier_print_conflict_stats(nwb.wrapped_baton,
201 scratch_pool);
202 if (err)
203 return svn_error_compose_create(externals_err, err);
204 }
205
206 return svn_error_compose_create(externals_err, err);
207 }
208