1 //
2 // aegis - project change supervisor
3 // Copyright (C) 1995, 1999, 2001-2009, 2011, 2012 Peter Miller
4 // Copyright (C) 2008 Walter Franzini
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or (at
9 // your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 //
19
20 #include <common/ac/assert.h>
21 #include <common/ac/stdio.h>
22 #include <common/ac/stdlib.h>
23 #include <common/ac/string.h>
24 #include <common/ac/time.h>
25 #include <common/ac/sys/types.h>
26 #include <common/ac/sys/stat.h>
27
28 #include <common/progname.h>
29 #include <common/quit.h>
30 #include <common/sizeof.h>
31 #include <common/trace.h>
32 #include <libaegis/ael/change/by_state.h>
33 #include <libaegis/arglex/change.h>
34 #include <libaegis/arglex/project.h>
35 #include <libaegis/arglex2.h>
36 #include <libaegis/change.h>
37 #include <libaegis/change/branch.h>
38 #include <libaegis/change/file.h>
39 #include <libaegis/change/identifier.h>
40 #include <libaegis/col.h>
41 #include <libaegis/commit.h>
42 #include <libaegis/dir.h>
43 #include <libaegis/gonzo.h>
44 #include <libaegis/help.h>
45 #include <libaegis/lock.h>
46 #include <libaegis/os.h>
47 #include <libaegis/project.h>
48 #include <libaegis/project/active.h>
49 #include <libaegis/project/history.h>
50 #include <libaegis/sub.h>
51 #include <libaegis/undo.h>
52 #include <libaegis/user.h>
53
54 #include <aegis/aenbru.h>
55
56
57 static void
new_branch_undo_usage(void)58 new_branch_undo_usage(void)
59 {
60 const char *progname;
61
62 progname = progname_get();
63 fprintf
64 (
65 stderr,
66 "usage: %s -New_Branch_Undo <change_number> [ <option>... ]\n",
67 progname
68 );
69 fprintf
70 (
71 stderr,
72 " %s -New_Branch_Undo -List [ <option>... ]\n",
73 progname
74 );
75 fprintf(stderr, " %s -New_Branch_Undo -Help\n", progname);
76 quit(1);
77 }
78
79
80 static void
new_branch_undo_help(void)81 new_branch_undo_help(void)
82 {
83 help("aenbru", new_branch_undo_usage);
84 }
85
86
87 static void
new_branch_undo_list(void)88 new_branch_undo_list(void)
89 {
90 trace(("new_branch_undo_list()\n{\n"));
91 arglex();
92 change_identifier cid;
93 cid.command_line_parse_rest(new_branch_undo_usage);
94 list_changes_in_state_mask(cid, 1 << cstate_state_being_developed);
95 trace(("}\n"));
96 }
97
98
99 static string_ty *
branch_changes_path_get(change::pointer cp)100 branch_changes_path_get(change::pointer cp)
101 {
102 project *pp;
103 string_ty *result;
104
105 assert(cp->is_a_branch());
106 pp = cp->pp->bind_branch(change_copy(cp));
107 result = str_copy(pp->changes_path_get());
108 project_free(pp);
109 return result;
110 }
111
112
113 static void
new_branch_undo_main(void)114 new_branch_undo_main(void)
115 {
116 string_ty *project_name;
117 long change_number;
118 project *pp;
119 user_ty::pointer up;
120 change::pointer cp;
121 string_ty *s;
122
123 trace(("new_branch_undo_main()\n{\n"));
124 arglex();
125 project_name = 0;
126 change_number = 0;
127 while (arglex_token != arglex_token_eoln)
128 {
129 switch (arglex_token)
130 {
131 default:
132 generic_argument(new_branch_undo_usage);
133 continue;
134
135 case arglex_token_keep:
136 case arglex_token_interactive:
137 case arglex_token_keep_not:
138 user_ty::delete_file_argument(new_branch_undo_usage);
139 break;
140
141 case arglex_token_change:
142 case arglex_token_branch:
143 arglex();
144 // fall through...
145
146 case arglex_token_number:
147 arglex_parse_branch
148 (
149 &project_name,
150 &change_number,
151 new_branch_undo_usage
152 );
153 continue;
154
155 case arglex_token_project:
156 arglex();
157 // fall through...
158
159 case arglex_token_string:
160 arglex_parse_project(&project_name, new_branch_undo_usage);
161 continue;
162
163 case arglex_token_wait:
164 case arglex_token_wait_not:
165 user_ty::lock_wait_argument(new_branch_undo_usage);
166 break;
167 }
168 arglex();
169 }
170
171 //
172 // locate project data
173 //
174 if (!project_name)
175 {
176 nstring n = user_ty::create()->default_project();
177 project_name = n.get_ref_copy();
178 }
179 pp = project_alloc(project_name);
180 str_free(project_name);
181 pp->bind_existing();
182
183 //
184 // locate user data
185 //
186 up = user_ty::create();
187 if (!project_administrator_query(pp, up->name()))
188 project_fatal(pp, 0, i18n("not an administrator"));
189
190 //
191 // locate change data
192 //
193 if (!change_number)
194 change_number = up->default_change(pp);
195 cp = change_alloc(pp, change_number);
196 change_bind_existing(cp);
197
198 //
199 // Make sure we are talking about a branch.
200 //
201 if (!cp->is_a_branch())
202 change_fatal(cp, 0, i18n("use aedbu instead"));
203
204 //
205 // Make sure the branch is not active.
206 // (project_active reports the error itself)
207 //
208 project_active_check_branch(cp, 0);
209
210 //
211 // Take an advisory write lock on the appropriate row of the change
212 // table. Take an advisory write lock on the appropriate row of the
213 // user table. Block until can get both simultaneously.
214 //
215 gonzo_gstate_lock_prepare_new();
216 pp->pstate_lock_prepare();
217 change_cstate_lock_prepare(cp);
218 up->ustate_lock_prepare();
219 lock_take();
220
221 //
222 // Race condition: check that the up is still a project
223 // administrator now that we have the project lock.
224 //
225 if (!project_administrator_query(pp, up->name()))
226 project_fatal(pp, 0, i18n("not an administrator"));
227
228 //
229 // It is an error if the change is not in the being developed state.
230 // It is an error if the change is not assigned to the current user.
231 //
232 if (!cp->is_being_developed())
233 change_fatal(cp, 0, i18n("bad dbu state"));
234
235 //
236 // Remove the change from the list of assigned changes in the user
237 // change table (in the user row).
238 //
239 up->own_remove(pp, change_number);
240
241 //
242 // remove the development directory
243 //
244 nstring dd(change_top_path_get(cp, 1));
245 if (up->delete_file_query(dd, true, true))
246 {
247 change_verbose(cp, 0, i18n("remove development directory"));
248 user_ty::become scoped(pp->get_user());
249 commit_rmdir_tree_errok(dd);
250 }
251
252 //
253 // tell the project to forget this change
254 //
255 project_change_delete(pp, change_number);
256
257 //
258 // delete the change state file of the branch
259 // and the state files of all subordinate changes
260 //
261 project_become(pp);
262 commit_unlink_errok(cp->cstate_filename_get());
263 commit_unlink_errok(change_fstate_filename_get(cp));
264 commit_rmdir_tree_errok(branch_changes_path_get(cp));
265 project_become_undo(pp);
266
267 //
268 // Remove aliases of this branch.
269 // (Punctuation?)
270 //
271 s = str_format("%s.%ld", project_name_get(pp).c_str(), change_number);
272 gonzo_alias_delete(s);
273 str_free(s);
274
275 //
276 // Update change table row (and change history table).
277 // Update user table row.
278 // Release advisory write locks.
279 //
280 pp->pstate_write();
281 gonzo_gstate_write();
282 commit();
283 lock_release();
284
285 //
286 // verbose success message
287 //
288 change_verbose(cp, 0, i18n("new branch undo complete"));
289 change_free(cp);
290 project_free(pp);
291 trace(("}\n"));
292 }
293
294
295 void
new_branch_undo(void)296 new_branch_undo(void)
297 {
298 static arglex_dispatch_ty dispatch[] =
299 {
300 { arglex_token_help, new_branch_undo_help, 0 },
301 { arglex_token_list, new_branch_undo_list, 0 },
302 };
303
304 trace(("new_branch_undo()\n{\n"));
305 arglex_dispatch(dispatch, SIZEOF(dispatch), new_branch_undo_main);
306 trace(("}\n"));
307 }
308
309
310 // vim: set ts=8 sw=4 et :
311