1/*
2 * commit-cmd.c -- Check changes into the 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 <apr_general.h>
31
32#include "svn_hash.h"
33#include "svn_error.h"
34#include "svn_error_codes.h"
35#include "svn_wc.h"
36#include "svn_client.h"
37#include "svn_path.h"
38#include "svn_dirent_uri.h"
39#include "svn_error.h"
40#include "svn_config.h"
41#include "cl.h"
42
43#include "svn_private_config.h"
44
45
46
47/* Wrapper notify_func2 function and baton for warning about
48   reduced-depth commits of copied directories.  */
49struct copy_warning_notify_baton
50{
51  svn_wc_notify_func2_t wrapped_func;
52  void *wrapped_baton;
53  svn_depth_t depth;
54  svn_boolean_t warned;
55};
56
57static void
58copy_warning_notify_func(void *baton,
59                         const svn_wc_notify_t *notify,
60                         apr_pool_t *pool)
61{
62  struct copy_warning_notify_baton *b = baton;
63
64  /* Call the wrapped notification system (if any). */
65  if (b->wrapped_func)
66    b->wrapped_func(b->wrapped_baton, notify, pool);
67
68  /* If we're being notified about a copy of a directory when our
69     commit depth is less-than-infinite, and we've not already warned
70     about this situation, then warn about it (and remember that we
71     now have.)  */
72  if ((! b->warned)
73      && (b->depth < svn_depth_infinity)
74      && (notify->kind == svn_node_dir)
75      && ((notify->action == svn_wc_notify_commit_copied) ||
76          (notify->action == svn_wc_notify_commit_copied_replaced)))
77    {
78      svn_error_t *err;
79      err = svn_cmdline_printf(pool,
80                               _("svn: The depth of this commit is '%s', "
81                                 "but copies are always performed "
82                                 "recursively in the repository.\n"),
83                               svn_depth_to_word(b->depth));
84      /* ### FIXME: Try to return this error showhow? */
85      svn_error_clear(err);
86
87      /* We'll only warn once. */
88      b->warned = TRUE;
89    }
90}
91
92
93
94
95/* This implements the `svn_opt_subcommand_t' interface. */
96svn_error_t *
97svn_cl__commit(apr_getopt_t *os,
98               void *baton,
99               apr_pool_t *pool)
100{
101  svn_error_t *err;
102  svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
103  svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
104  apr_array_header_t *targets;
105  apr_array_header_t *condensed_targets;
106  const char *base_dir;
107  svn_config_t *cfg;
108  svn_boolean_t no_unlock = FALSE;
109  struct copy_warning_notify_baton cwnb;
110
111  SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
112                                                      opt_state->targets,
113                                                      ctx, FALSE, pool));
114
115  SVN_ERR_W(svn_cl__check_targets_are_local_paths(targets),
116            _("Commit targets must be local paths"));
117
118  /* Add "." if user passed 0 arguments. */
119  svn_opt_push_implicit_dot_target(targets, pool);
120
121  SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
122
123  /* Condense the targets (like commit does)... */
124  SVN_ERR(svn_dirent_condense_targets(&base_dir, &condensed_targets, targets,
125                                      TRUE, pool, pool));
126
127  if ((! condensed_targets) || (! condensed_targets->nelts))
128    {
129      const char *parent_dir, *base_name;
130
131      SVN_ERR(svn_wc_get_actual_target2(&parent_dir, &base_name, ctx->wc_ctx,
132                                        base_dir, pool, pool));
133      if (*base_name)
134        base_dir = apr_pstrdup(pool, parent_dir);
135    }
136
137  if (opt_state->depth == svn_depth_unknown)
138    opt_state->depth = svn_depth_infinity;
139
140  cfg = svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG);
141  if (cfg)
142    SVN_ERR(svn_config_get_bool(cfg, &no_unlock,
143                                SVN_CONFIG_SECTION_MISCELLANY,
144                                SVN_CONFIG_OPTION_NO_UNLOCK, FALSE));
145
146  /* We're creating a new log message baton because we can use our base_dir
147     to store the temp file, instead of the current working directory.  The
148     client might not have write access to their working directory, but they
149     better have write access to the directory they're committing.  */
150  SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3),
151                                     opt_state, base_dir,
152                                     ctx->config, pool));
153
154  /* Copies are done server-side, and cheaply, which means they're
155     effectively always done with infinite depth.  This is a potential
156     cause of confusion for users trying to commit copied subtrees in
157     part by restricting the commit's depth.  See issues #3699 and #3752. */
158  if (opt_state->depth < svn_depth_infinity)
159    {
160      cwnb.wrapped_func = ctx->notify_func2;
161      cwnb.wrapped_baton = ctx->notify_baton2;
162      cwnb.depth = opt_state->depth;
163      cwnb.warned = FALSE;
164      ctx->notify_func2 = copy_warning_notify_func;
165      ctx->notify_baton2 = &cwnb;
166    }
167
168  /* Commit. */
169  err = svn_client_commit6(targets,
170                           opt_state->depth,
171                           no_unlock,
172                           opt_state->keep_changelists,
173                           TRUE /* commit_as_operations */,
174                           opt_state->include_externals, /* file externals */
175                           opt_state->include_externals, /* dir externals */
176                           opt_state->changelists,
177                           opt_state->revprop_table,
178                           (opt_state->quiet
179                            ? NULL : svn_cl__print_commit_info),
180                           NULL,
181                           ctx,
182                           pool);
183  SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3, err, pool));
184
185  return SVN_NO_ERROR;
186}
187