1
+#define USE_THE_REPOSITORY_VARIABLE
2
+
3
#include "builtin.h"
4
+#include "commit.h"
5
+#include "commit-reach.h"
6
+#include "config.h"
7
+#include "editor.h"
8
+#include "environment.h"
9
#include "gettext.h"
10
+#include "hex.h"
11
#include "parse-options.h"
12
+#include "refs.h"
13
+#include "replay.h"
14
+#include "revision.h"
15
+#include "sequencer.h"
16
+#include "strvec.h"
17
+#include "tree.h"
18
+#include "wt-status.h"
19
+
20
+#define GIT_HISTORY_REWORD_USAGE \
21
+ N_("git history reword <commit> [--ref-action=(branches|head|print)]")
22
+
23
+static void change_data_free(void *util, const char *str UNUSED)
24
+{
25
+ struct wt_status_change_data *d = util;
26
+ free(d->rename_source);
27
+ free(d);
28
+}
29
+
30
+static int fill_commit_message(struct repository *repo,
31
+ const struct object_id *old_tree,
32
+ const struct object_id *new_tree,
33
+ const char *default_message,
34
+ const char *action,
35
+ struct strbuf *out)
36
+{
37
+ const char *path = git_path_commit_editmsg();
38
+ const char *hint =
39
+ _("Please enter the commit message for the %s changes."
40
+ " Lines starting\nwith '%s' will be ignored, and an"
41
+ " empty message aborts the commit.\n");
42
+ struct wt_status s;
43
+
44
+ strbuf_addstr(out, default_message);
45
+ strbuf_addch(out, '\n');
46
+ strbuf_commented_addf(out, comment_line_str, hint, action, comment_line_str);
47
+ write_file_buf(path, out->buf, out->len);
48
+
49
+ wt_status_prepare(repo, &s);
50
+ FREE_AND_NULL(s.branch);
51
+ s.ahead_behind_flags = AHEAD_BEHIND_QUICK;
52
+ s.commit_template = 1;
53
+ s.colopts = 0;
54
+ s.display_comment_prefix = 1;
55
+ s.hints = 0;
56
+ s.use_color = 0;
57
+ s.whence = FROM_COMMIT;
58
+ s.committable = 1;
59
+
60
+ s.fp = fopen(git_path_commit_editmsg(), "a");
61
+ if (!s.fp)
62
+ return error_errno(_("could not open '%s'"), git_path_commit_editmsg());
63
+
64
+ wt_status_collect_changes_trees(&s, old_tree, new_tree);
65
+ wt_status_print(&s);
66
+ wt_status_collect_free_buffers(&s);
67
+ string_list_clear_func(&s.change, change_data_free);
68
+
69
+ strbuf_reset(out);
70
+ if (launch_editor(path, out, NULL)) {
71
+ fprintf(stderr, _("Aborting commit as launching the editor failed.\n"));
72
+ return -1;
73
+ }
74
+ strbuf_stripspace(out, comment_line_str);
75
+
76
+ cleanup_message(out, COMMIT_MSG_CLEANUP_ALL, 0);
77
+
78
+ if (!out->len) {
79
+ fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
80
+ return -1;
81
+ }
82
+
83
+ return 0;
84
+}
85
+
86
+static int commit_tree_with_edited_message(struct repository *repo,
87
+ const char *action,
88
+ struct commit *original,
89
+ struct commit **out)
90
+{
91
+ const char *exclude_gpgsig[] = {
92
+ /* We reencode the message, so the encoding needs to be stripped. */
93
+ "encoding",
94
+ /* We need to strip signatures as those will become invalid. */
95
+ "gpgsig",
96
+ "gpgsig-sha256",
97
+ NULL,
98
+ };
99
+ const char *original_message, *original_body, *ptr;
100
+ struct commit_extra_header *original_extra_headers = NULL;
101
+ struct strbuf commit_message = STRBUF_INIT;
102
+ struct object_id rewritten_commit_oid;
103
+ struct object_id original_tree_oid;
104
+ struct object_id parent_tree_oid;
105
+ char *original_author = NULL;
106
+ struct commit *parent;
107
+ size_t len;
108
+ int ret;
109
+
110
+ original_tree_oid = repo_get_commit_tree(repo, original)->object.oid;
111
+
112
+ parent = original->parents ? original->parents->item : NULL;
113
+ if (parent) {
114
+ if (repo_parse_commit(repo, parent)) {
115
+ ret = error(_("unable to parse parent commit %s"),
116
+ oid_to_hex(&parent->object.oid));
117
+ goto out;
118
+ }
119
+
120
+ parent_tree_oid = repo_get_commit_tree(repo, parent)->object.oid;
121
+ } else {
122
+ oidcpy(&parent_tree_oid, repo->hash_algo->empty_tree);
123
+ }
124
+
125
+ /* We retain authorship of the original commit. */
126
+ original_message = repo_logmsg_reencode(repo, original, NULL, NULL);
127
+ ptr = find_commit_header(original_message, "author", &len);
128
+ if (ptr)
129
+ original_author = xmemdupz(ptr, len);
130
+ find_commit_subject(original_message, &original_body);
131
+
132
+ ret = fill_commit_message(repo, &parent_tree_oid, &original_tree_oid,
133
+ original_body, action, &commit_message);
134
+ if (ret < 0)
135
+ goto out;
136
+
137
+ original_extra_headers = read_commit_extra_headers(original, exclude_gpgsig);
138
+
139
+ ret = commit_tree_extended(commit_message.buf, commit_message.len, &original_tree_oid,
140
+ original->parents, &rewritten_commit_oid, original_author,
141
+ NULL, NULL, original_extra_headers);
142
+ if (ret < 0)
143
+ goto out;
144
+
145
+ *out = lookup_commit_or_die(&rewritten_commit_oid, "rewritten commit");
146
+
147
+out:
148
+ free_commit_extra_headers(original_extra_headers);
149
+ strbuf_release(&commit_message);
150
+ free(original_author);
151
+ return ret;
152
+}
153
+
154
+enum ref_action {
155
+ REF_ACTION_DEFAULT,
156
+ REF_ACTION_BRANCHES,
157
+ REF_ACTION_HEAD,
158
+ REF_ACTION_PRINT,
159
+};
160
+
161
+static int parse_ref_action(const struct option *opt, const char *value, int unset)
162
+{
163
+ enum ref_action *action = opt->value;
164
+
165
+ BUG_ON_OPT_NEG_NOARG(unset, value);
166
+ if (!strcmp(value, "branches")) {
167
+ *action = REF_ACTION_BRANCHES;
168
+ } else if (!strcmp(value, "head")) {
169
+ *action = REF_ACTION_HEAD;
170
+ } else if (!strcmp(value, "print")) {
171
+ *action = REF_ACTION_PRINT;
172
+ } else {
173
+ return error(_("%s expects one of 'branches', 'head' or 'print'"),
174
+ opt->long_name);
175
+ }
176
+
177
+ return 0;
178
+}
179
+
180
+static int handle_reference_updates(enum ref_action action,
181
+ struct repository *repo,
182
+ struct commit *original,
183
+ struct commit *rewritten,
184
+ const char *reflog_msg)
185
+{
186
+ const struct name_decoration *decoration;
187
+ struct replay_revisions_options opts = { 0 };
188
+ struct replay_result result = { 0 };
189
+ struct ref_transaction *transaction = NULL;
190
+ struct strvec args = STRVEC_INIT;
191
+ struct strbuf err = STRBUF_INIT;
192
+ struct commit *head = NULL;
193
+ struct rev_info revs;
194
+ char hex[GIT_MAX_HEXSZ + 1];
195
+ bool detached_head;
196
+ int head_flags = 0;
197
+ int ret;
198
+
199
+ refs_read_ref_full(get_main_ref_store(repo), "HEAD",
200
+ RESOLVE_REF_NO_RECURSE, NULL, &head_flags);
201
+ detached_head = !(head_flags & REF_ISSYMREF);
202
+
203
+ repo_init_revisions(repo, &revs, NULL);
204
+ strvec_push(&args, "ignored");
205
+ strvec_push(&args, "--reverse");
206
+ strvec_push(&args, "--topo-order");
207
+ strvec_push(&args, "--full-history");
208
+
209
+ /* We only want to see commits that are descendants of the old commit. */
210
+ strvec_pushf(&args, "--ancestry-path=%s",
211
+ oid_to_hex(&original->object.oid));
212
+
213
+ /*
214
+ * Ancestry path may also show ancestors of the old commit, but we
215
+ * don't want to see those, either.
216
+ */
217
+ strvec_pushf(&args, "^%s", oid_to_hex(&original->object.oid));
218
+
219
+ /*
220
+ * When we're asked to update HEAD we need to verify that the commit
221
+ * that we want to rewrite is actually an ancestor of it and, if so,
222
+ * update it. Otherwise we'll update (or print) all descendant
223
+ * branches.
224
+ */
225
+ if (action == REF_ACTION_HEAD) {
226
+ struct commit_list *from_list = NULL;
227
+
228
+ head = lookup_commit_reference_by_name("HEAD");
229
+ if (!head) {
230
+ ret = error(_("cannot look up HEAD"));
231
+ goto out;
232
+ }
233
+
234
+ commit_list_insert(original, &from_list);
235
+ ret = repo_is_descendant_of(repo, head, from_list);
236
+ free_commit_list(from_list);
237
+
238
+ if (ret < 0) {
239
+ ret = error(_("cannot determine descendance"));
240
+ goto out;
241
+ } else if (!ret) {
242
+ ret = error(_("rewritten commit must be an ancestor "
243
+ "of HEAD when using --ref-action=head"));
244
+ goto out;
245
+ }
246
+
247
+ strvec_push(&args, "HEAD");
248
+ } else {
249
+ strvec_push(&args, "--branches");
250
+ strvec_push(&args, "HEAD");
251
+ }
252
+
253
+ setup_revisions_from_strvec(&args, &revs, NULL);
254
+ if (args.nr != 1)
255
+ BUG("revisions were set up with invalid argument");
256
+
257
+ opts.onto = oid_to_hex_r(hex, &rewritten->object.oid);
258
+
259
+ ret = replay_revisions(&revs, &opts, &result);
260
+ if (ret)
261
+ goto out;
262
+
263
+ switch (action) {
264
+ case REF_ACTION_BRANCHES:
265
+ case REF_ACTION_HEAD:
266
+ transaction = ref_store_transaction_begin(get_main_ref_store(repo), 0, &err);
267
+ if (!transaction) {
268
+ ret = error(_("failed to begin ref transaction: %s"), err.buf);
269
+ goto out;
270
+ }
271
+
272
+ for (size_t i = 0; i < result.updates_nr; i++) {
273
+ ret = ref_transaction_update(transaction,
274
+ result.updates[i].refname,
275
+ &result.updates[i].new_oid,
276
+ &result.updates[i].old_oid,
277
+ NULL, NULL, 0, reflog_msg, &err);
278
+ if (ret) {
279
+ ret = error(_("failed to update ref '%s': %s"),
280
+ result.updates[i].refname, err.buf);
281
+ goto out;
282
+ }
283
+ }
284
+
285
+ /*
286
+ * `replay_revisions()` only updates references that are
287
+ * ancestors of `rewritten`, so we need to manually
288
+ * handle updating references that point to `original`.
289
+ */
290
+ for (decoration = get_name_decoration(&original->object);
291
+ decoration;
292
+ decoration = decoration->next)
293
+ {
294
+ if (decoration->type != DECORATION_REF_LOCAL &&
295
+ decoration->type != DECORATION_REF_HEAD)
296
+ continue;
297
+
298
+ if (action == REF_ACTION_HEAD &&
299
+ decoration->type != DECORATION_REF_HEAD)
300
+ continue;
301
+
302
+ /*
303
+ * We only need to update HEAD separately in case it's
304
+ * detached. If it's not we'd already update the branch
305
+ * it is pointing to.
306
+ */
307
+ if (action == REF_ACTION_BRANCHES &&
308
+ decoration->type == DECORATION_REF_HEAD &&
309
+ !detached_head)
310
+ continue;
311
+
312
+ ret = ref_transaction_update(transaction,
313
+ decoration->name,
314
+ &rewritten->object.oid,
315
+ &original->object.oid,
316
+ NULL, NULL, 0, reflog_msg, &err);
317
+ if (ret) {
318
+ ret = error(_("failed to update ref '%s': %s"),
319
+ decoration->name, err.buf);
320
+ goto out;
321
+ }
322
+ }
323
+
324
+ if (ref_transaction_commit(transaction, &err)) {
325
+ ret = error(_("failed to commit ref transaction: %s"), err.buf);
326
+ goto out;
327
+ }
328
+
329
+ break;
330
+ case REF_ACTION_PRINT:
331
+ for (size_t i = 0; i < result.updates_nr; i++)
332
+ printf("update %s %s %s\n",
333
+ result.updates[i].refname,
334
+ oid_to_hex(&result.updates[i].new_oid),
335
+ oid_to_hex(&result.updates[i].old_oid));
336
+ break;
337
+ default:
338
+ BUG("unsupported ref action %d", action);
339
+ }
340
+
341
+ ret = 0;
342
+
343
+out:
344
+ ref_transaction_free(transaction);
345
+ replay_result_release(&result);
346
+ release_revisions(&revs);
347
+ strbuf_release(&err);
348
+ strvec_clear(&args);
349
+ return ret;
350
+}
351
+
352
+static int cmd_history_reword(int argc,
353
+ const char **argv,
354
+ const char *prefix,
355
+ struct repository *repo)
356
+{
357
+ const char * const usage[] = {
358
+ GIT_HISTORY_REWORD_USAGE,
359
+ NULL,
360
+ };
361
+ enum ref_action action = REF_ACTION_DEFAULT;
362
+ struct option options[] = {
363
+ OPT_CALLBACK_F(0, "ref-action", &action, N_("<action>"),
364
+ N_("control ref update behavior (branches|head|print)"),
365
+ PARSE_OPT_NONEG, parse_ref_action),
366
+ OPT_END(),
367
+ };
368
+ struct strbuf reflog_msg = STRBUF_INIT;
369
+ struct commit *original, *rewritten;
370
+ int ret;
371
+
372
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
373
+ if (argc != 1) {
374
+ ret = error(_("command expects a single revision"));
375
+ goto out;
376
+ }
377
+ repo_config(repo, git_default_config, NULL);
378
+
379
+ if (action == REF_ACTION_DEFAULT)
380
+ action = REF_ACTION_BRANCHES;
381
+
382
+ original = lookup_commit_reference_by_name(argv[0]);
383
+ if (!original) {
384
+ ret = error(_("commit cannot be found: %s"), argv[0]);
385
+ goto out;
386
+ }
387
+
388
+ ret = commit_tree_with_edited_message(repo, "reworded", original, &rewritten);
389
+ if (ret < 0) {
390
+ ret = error(_("failed writing reworded commit"));
391
+ goto out;
392
+ }
393
+
394
+ strbuf_addf(&reflog_msg, "reword: updating %s", argv[0]);
395
+
396
+ ret = handle_reference_updates(action, repo, original, rewritten,
397
+ reflog_msg.buf);
398
+ if (ret < 0) {
399
+ ret = error(_("failed replaying descendants"));
400
+ goto out;
401
+ }
402
+
403
+ ret = 0;
404
+
405
+out:
406
+ strbuf_release(&reflog_msg);
407
+ return ret;
408
+}
409
410
int cmd_history(int argc,
411
const char **argv,
412
const char *prefix,
8
- struct repository *repo UNUSED)
413
+ struct repository *repo)
414
{
415
const char * const usage[] = {
11
- N_("git history [<options>]"),
416
+ GIT_HISTORY_REWORD_USAGE,
417
NULL,
418
};
419
+ parse_opt_subcommand_fn *fn = NULL;
420
struct option options[] = {
421
+ OPT_SUBCOMMAND("reword", &fn, cmd_history_reword),
422
OPT_END(),
423
};
424
425
argc = parse_options(argc, argv, prefix, options, usage, 0);
19
- if (argc)
20
- usagef("unrecognized argument: %s", argv[0]);
21
- return 0;
426
+ return fn(argc, argv, prefix, repo);
427
}