Raw
1 #define USE_THE_REPOSITORY_VARIABLE
2
3 #include "git-compat-util.h"
4 #include "environment.h"
5 #include "hex.h"
6 #include "merge-ort.h"
7 #include "object-name.h"
8 #include "refs.h"
9 #include "replay.h"
10 #include "revision.h"
11 #include "sequencer.h"
12 #include "strmap.h"
13 #include "tree.h"
14
15 /*
16 * We technically need USE_THE_REPOSITORY_VARIABLE for DEFAULT_ABBREV, but
17 * do not want to use the_repository.
18 */
19 #define the_repository DO_NOT_USE_THE_REPOSITORY
20
21 enum replay_mode {
22 REPLAY_MODE_PICK,
23 REPLAY_MODE_REVERT,
24 };
25
26 static const char *short_commit_name(struct repository *repo,
27 struct commit *commit)
28 {
29 return repo_find_unique_abbrev(repo, &commit->object.oid,
30 DEFAULT_ABBREV);
31 }
32
33 static struct commit *peel_committish(struct repository *repo,
34 const char *name,
35 const char *mode)
36 {
37 struct object *obj;
38 struct object_id oid;
39 struct commit *commit;
40
41 if (repo_get_oid(repo, name, &oid))
42 die(_("'%s' is not a valid commit-ish for %s"), name, mode);
43 obj = parse_object_or_die(repo, &oid, name);
44 commit = (struct commit *)repo_peel_to_type(repo, name, 0, obj,
45 OBJ_COMMIT);
46 if (!commit)
47 die(_("'%s' does not point to a commit for %s"), name, mode);
48 return commit;
49 }
50
51 static char *get_author(const char *message)
52 {
53 size_t len;
54 const char *a;
55
56 a = find_commit_header(message, "author", &len);
57 if (a)
58 return xmemdupz(a, len);
59
60 return NULL;
61 }
62
63 static void generate_revert_message(struct strbuf *msg,
64 struct commit *commit,
65 struct repository *repo)
66 {
67 const char *out_enc = get_commit_output_encoding();
68 const char *message = repo_logmsg_reencode(repo, commit, NULL, out_enc);
69 const char *subject_start;
70 int subject_len;
71 char *subject;
72
73 subject_len = find_commit_subject(message, &subject_start);
74 subject = xmemdupz(subject_start, subject_len);
75
76 sequencer_format_revert_message(repo, subject, commit,
77 commit->parents ? commit->parents->item : NULL,
78 false, msg);
79
80 free(subject);
81 repo_unuse_commit_buffer(repo, commit, message);
82 }
83
84 static struct commit *create_commit(struct repository *repo,
85 struct tree *tree,
86 struct commit *based_on,
87 struct commit *parent,
88 enum replay_mode mode)
89 {
90 struct object_id ret;
91 struct object *obj = NULL;
92 struct commit_list *parents = NULL;
93 char *author = NULL;
94 char *sign_commit = NULL; /* FIXME: cli users might want to sign again */
95 struct commit_extra_header *extra = NULL;
96 struct strbuf msg = STRBUF_INIT;
97 const char *out_enc = get_commit_output_encoding();
98 const char *message = repo_logmsg_reencode(repo, based_on,
99 NULL, out_enc);
100 const char *orig_message = NULL;
101 const char *exclude_gpgsig[] = { "gpgsig", "gpgsig-sha256", NULL };
102
103 commit_list_insert(parent, &parents);
104 extra = read_commit_extra_headers(based_on, exclude_gpgsig);
105 if (mode == REPLAY_MODE_REVERT) {
106 generate_revert_message(&msg, based_on, repo);
107 /* For revert, use current user as author (NULL = use default) */
108 } else if (mode == REPLAY_MODE_PICK) {
109 find_commit_subject(message, &orig_message);
110 strbuf_addstr(&msg, orig_message);
111 author = get_author(message);
112 } else {
113 BUG("unexpected replay mode %d", mode);
114 }
115 reset_ident_date();
116 if (commit_tree_extended(msg.buf, msg.len, &tree->object.oid, parents,
117 &ret, author, NULL, sign_commit, extra)) {
118 error(_("failed to write commit object"));
119 goto out;
120 }
121
122 obj = parse_object(repo, &ret);
123
124 out:
125 repo_unuse_commit_buffer(repo, based_on, message);
126 free_commit_extra_headers(extra);
127 commit_list_free(parents);
128 strbuf_release(&msg);
129 free(author);
130 return (struct commit *)obj;
131 }
132
133 struct ref_info {
134 struct commit *onto;
135 struct strset positive_refs;
136 struct strset negative_refs;
137 size_t positive_refexprs;
138 size_t negative_refexprs;
139 };
140
141 static void get_ref_information(struct repository *repo,
142 struct rev_cmdline_info *cmd_info,
143 struct ref_info *ref_info)
144 {
145 ref_info->onto = NULL;
146 strset_init(&ref_info->positive_refs);
147 strset_init(&ref_info->negative_refs);
148 ref_info->positive_refexprs = 0;
149 ref_info->negative_refexprs = 0;
150
151 /*
152 * When the user specifies e.g.
153 * git replay origin/main..mybranch
154 * git replay ^origin/next mybranch1 mybranch2
155 * we want to be able to determine where to replay the commits. In
156 * these examples, the branches are probably based on an old version
157 * of either origin/main or origin/next, so we want to replay on the
158 * newest version of that branch. In contrast we would want to error
159 * out if they ran
160 * git replay ^origin/master ^origin/next mybranch
161 * git replay mybranch~2..mybranch
162 * the first of those because there's no unique base to choose, and
163 * the second because they'd likely just be replaying commits on top
164 * of the same commit and not making any difference.
165 */
166 for (size_t i = 0; i < cmd_info->nr; i++) {
167 struct rev_cmdline_entry *e = cmd_info->rev + i;
168 struct object_id oid;
169 const char *refexpr = e->name;
170 char *fullname = NULL;
171 int can_uniquely_dwim = 1;
172
173 if (*refexpr == '^')
174 refexpr++;
175 if (repo_dwim_ref(repo, refexpr, strlen(refexpr), &oid, &fullname, 0) != 1)
176 can_uniquely_dwim = 0;
177
178 if (e->flags & BOTTOM) {
179 if (can_uniquely_dwim)
180 strset_add(&ref_info->negative_refs, fullname);
181 if (!ref_info->negative_refexprs)
182 ref_info->onto = lookup_commit_reference_gently(repo,
183 &e->item->oid, 1);
184 ref_info->negative_refexprs++;
185 } else {
186 if (can_uniquely_dwim)
187 strset_add(&ref_info->positive_refs, fullname);
188 ref_info->positive_refexprs++;
189 }
190
191 free(fullname);
192 }
193 }
194
195 static void set_up_branch_mode(struct repository *repo,
196 char **branch_name,
197 const char *option_name,
198 struct ref_info *rinfo,
199 struct commit **onto)
200 {
201 struct object_id oid;
202 char *fullname = NULL;
203
204 if (repo_dwim_ref(repo, *branch_name, strlen(*branch_name),
205 &oid, &fullname, 0) == 1) {
206 free(*branch_name);
207 *branch_name = fullname;
208 } else {
209 die(_("argument to %s must be a reference"), option_name);
210 }
211 *onto = peel_committish(repo, *branch_name, option_name);
212 if (rinfo->positive_refexprs > 1)
213 die(_("'%s' cannot be used with multiple revision ranges "
214 "because the ordering would be ill-defined"),
215 option_name);
216 }
217
218 static void set_up_replay_mode(struct repository *repo,
219 struct rev_cmdline_info *cmd_info,
220 const char *onto_name,
221 bool *detached_head,
222 char **advance_name,
223 char **revert_name,
224 struct commit **onto,
225 struct strset **update_refs)
226 {
227 struct ref_info rinfo;
228 int head_flags = 0;
229
230 refs_read_ref_full(get_main_ref_store(repo), "HEAD",
231 RESOLVE_REF_NO_RECURSE, NULL, &head_flags);
232 *detached_head = !(head_flags & REF_ISSYMREF);
233
234 get_ref_information(repo, cmd_info, &rinfo);
235 if (!rinfo.positive_refexprs)
236 die(_("need some commits to replay"));
237
238 if (onto_name) {
239 *onto = peel_committish(repo, onto_name, "--onto");
240 if (rinfo.positive_refexprs <
241 strset_get_size(&rinfo.positive_refs))
242 die(_("all positive revisions given must be references"));
243 *update_refs = xcalloc(1, sizeof(**update_refs));
244 **update_refs = rinfo.positive_refs;
245 memset(&rinfo.positive_refs, 0, sizeof(**update_refs));
246 } else if (*advance_name) {
247 set_up_branch_mode(repo, advance_name, "--advance", &rinfo, onto);
248 } else if (*revert_name) {
249 set_up_branch_mode(repo, revert_name, "--revert", &rinfo, onto);
250 } else {
251 BUG("expected one of onto_name, *advance_name, or *revert_name");
252 }
253 strset_clear(&rinfo.negative_refs);
254 strset_clear(&rinfo.positive_refs);
255 }
256
257 static struct commit *get_mapped_commit(kh_oid_map_t *replayed_commits,
258 struct commit *commit,
259 struct commit *fallback)
260 {
261 khint_t pos;
262 if (!commit)
263 return fallback;
264 pos = kh_get_oid_map(replayed_commits, commit->object.oid);
265 if (pos == kh_end(replayed_commits))
266 return fallback;
267 return kh_value(replayed_commits, pos);
268 }
269
270 static void put_mapped_commit(kh_oid_map_t *replayed_commits,
271 struct commit *commit,
272 struct commit *new_commit)
273 {
274 khint_t pos;
275 int ret;
276
277 pos = kh_put_oid_map(replayed_commits, commit->object.oid, &ret);
278 if (ret == 0)
279 BUG("Duplicate rewritten commit: %s",
280 oid_to_hex(&commit->object.oid));
281
282 kh_value(replayed_commits, pos) = new_commit;
283 }
284
285 static struct commit *pick_regular_commit(struct repository *repo,
286 struct commit *pickme,
287 struct commit *replayed_base,
288 struct merge_options *merge_opt,
289 struct merge_result *result,
290 enum replay_mode mode,
291 enum replay_empty_commit_action empty)
292 {
293 struct tree *pickme_tree, *base_tree, *replayed_base_tree;
294
295 if (pickme->parents)
296 base_tree = repo_get_commit_tree(repo, pickme->parents->item);
297 else
298 base_tree = lookup_tree(repo, repo->hash_algo->empty_tree);
299
300 replayed_base_tree = repo_get_commit_tree(repo, replayed_base);
301 pickme_tree = repo_get_commit_tree(repo, pickme);
302
303 if (mode == REPLAY_MODE_PICK) {
304 /* Cherry-pick: normal order */
305 merge_opt->branch1 = short_commit_name(repo, replayed_base);
306 merge_opt->branch2 = short_commit_name(repo, pickme);
307 if (pickme->parents)
308 merge_opt->ancestor = xstrfmt("parent of %s", merge_opt->branch2);
309 else
310 merge_opt->ancestor = xstrdup("empty tree");
311
312 merge_incore_nonrecursive(merge_opt,
313 base_tree,
314 replayed_base_tree,
315 pickme_tree,
316 result);
317
318 free((char *)merge_opt->ancestor);
319 } else if (mode == REPLAY_MODE_REVERT) {
320 /* Revert: swap base and pickme to reverse the diff */
321 const char *pickme_name = short_commit_name(repo, pickme);
322 merge_opt->branch1 = short_commit_name(repo, replayed_base);
323 merge_opt->branch2 = xstrfmt("parent of %s", pickme_name);
324 merge_opt->ancestor = pickme_name;
325
326 merge_incore_nonrecursive(merge_opt,
327 pickme_tree,
328 replayed_base_tree,
329 base_tree,
330 result);
331
332 free((char *)merge_opt->branch2);
333 } else {
334 BUG("unexpected replay mode %d", mode);
335 }
336 merge_opt->ancestor = NULL;
337 merge_opt->branch2 = NULL;
338
339 if (!result->clean)
340 return NULL;
341
342 /* Handle commits that become empty */
343 if (oideq(&replayed_base_tree->object.oid, &result->tree->object.oid) &&
344 !oideq(&pickme_tree->object.oid, &base_tree->object.oid)) {
345 switch (empty) {
346 case REPLAY_EMPTY_COMMIT_DROP:
347 return replayed_base;
348 case REPLAY_EMPTY_COMMIT_KEEP:
349 break;
350 case REPLAY_EMPTY_COMMIT_ABORT:
351 result->clean = error(_("commit %s became empty after replay"),
352 oid_to_hex(&pickme->object.oid));
353 return NULL;
354 }
355 }
356
357 return create_commit(repo, result->tree, pickme, replayed_base, mode);
358 }
359
360 void replay_result_release(struct replay_result *result)
361 {
362 for (size_t i = 0; i < result->updates_nr; i++)
363 free(result->updates[i].refname);
364 free(result->updates);
365 }
366
367 void replay_result_queue_update(struct replay_result *result,
368 const char *refname,
369 const struct object_id *old_oid,
370 const struct object_id *new_oid)
371 {
372 ALLOC_GROW(result->updates, result->updates_nr + 1, result->updates_alloc);
373 result->updates[result->updates_nr].refname = xstrdup(refname);
374 result->updates[result->updates_nr].old_oid = *old_oid;
375 result->updates[result->updates_nr].new_oid = *new_oid;
376 result->updates_nr++;
377 }
378
379 int replay_revisions(struct rev_info *revs,
380 struct replay_revisions_options *opts,
381 struct replay_result *out)
382 {
383 kh_oid_map_t *replayed_commits = NULL;
384 struct strset *update_refs = NULL;
385 struct commit *last_commit = NULL;
386 struct commit *commit;
387 struct commit *onto = NULL;
388 struct merge_options merge_opt = { 0 };
389 struct merge_result result = {
390 .clean = 1,
391 };
392 bool detached_head;
393 char *advance;
394 char *revert;
395 const char *ref;
396 struct object_id old_oid;
397 enum replay_mode mode = REPLAY_MODE_PICK;
398 int ret;
399
400 advance = xstrdup_or_null(opts->advance);
401 revert = xstrdup_or_null(opts->revert);
402 if (revert)
403 mode = REPLAY_MODE_REVERT;
404 set_up_replay_mode(revs->repo, &revs->cmdline, opts->onto,
405 &detached_head, &advance, &revert, &onto, &update_refs);
406
407 if (opts->ref) {
408 struct object_id oid;
409
410 if (update_refs && strset_get_size(update_refs) > 1) {
411 ret = error(_("'--ref' cannot be used with multiple revision ranges"));
412 goto out;
413 }
414 if (check_refname_format(opts->ref, 0) || !starts_with(opts->ref, "refs/")) {
415 ret = error(_("'%s' is not a valid refname"), opts->ref);
416 goto out;
417 }
418 ref = opts->ref;
419 if (!refs_read_ref(get_main_ref_store(revs->repo), opts->ref, &oid))
420 oidcpy(&old_oid, &oid);
421 else
422 oidclr(&old_oid, revs->repo->hash_algo);
423 } else {
424 ref = advance ? advance : revert;
425 oidcpy(&old_oid, &onto->object.oid);
426 }
427
428 if (prepare_revision_walk(revs) < 0) {
429 ret = error(_("error preparing revisions"));
430 goto out;
431 }
432
433 init_basic_merge_options(&merge_opt, revs->repo);
434 merge_opt.show_rename_progress = 0;
435 last_commit = onto;
436 replayed_commits = kh_init_oid_map();
437 while ((commit = get_revision(revs))) {
438 const struct name_decoration *decoration;
439
440 if (commit->parents && commit->parents->next) {
441 if (!opts->linearize)
442 die(_("replaying merge commits is not supported yet!"));
443 /*
444 * Drop the merge commit: do not pick it, leave
445 * `last_commit` unchanged, and fall through to the
446 * rest of the loop. As a result:
447 * - refs pointing to the merge commit will be updated
448 * to `last_commit`.
449 * - the next replayed commit uses `last_commit` as its
450 * `base`.
451 */
452 } else {
453 /*
454 * Decide where to replay this commit onto.
455 * If the parent commit was replayed already, the replayed result
456 * can be found in `replayed_commits`. Otherwise fall back to `onto`.
457 * When reverting, commits are replayed in reverse order and thus
458 * its parent isn't replayed yet. Therefore revert commits are
459 * always replayed onto `last_commit`.
460 * Also when opts->linearize is true, set the base to
461 * `last_commit` to create a single linear history.
462 */
463 struct commit *parent = commit->parents ? commit->parents->item : NULL;
464 struct commit *base = get_mapped_commit(replayed_commits, parent, onto);
465
466 if (opts->linearize || mode == REPLAY_MODE_REVERT)
467 base = last_commit;
468
469 last_commit = pick_regular_commit(revs->repo, commit, base,
470 &merge_opt, &result,
471 mode, opts->empty);
472 }
473
474 if (!last_commit)
475 break;
476
477 /* Record commit -> last_commit mapping */
478 put_mapped_commit(replayed_commits, commit, last_commit);
479
480 /* Update any necessary branches */
481 if (ref)
482 continue;
483
484 for (decoration = get_name_decoration(&commit->object);
485 decoration;
486 decoration = decoration->next)
487 {
488 if (decoration->type != DECORATION_REF_LOCAL &&
489 decoration->type != DECORATION_REF_HEAD)
490 continue;
491
492 /*
493 * We only need to update HEAD separately in case it's
494 * detached. If it's not we'd already update the branch
495 * it is pointing to.
496 */
497 if (decoration->type == DECORATION_REF_HEAD && !detached_head)
498 continue;
499
500 if (!opts->contained &&
501 !strset_contains(update_refs, decoration->name))
502 continue;
503
504 replay_result_queue_update(out, decoration->name,
505 &commit->object.oid,
506 &last_commit->object.oid);
507 }
508 }
509
510 if (result.clean < 0) {
511 ret = -1;
512 goto out;
513 }
514
515 if (!result.clean) {
516 ret = 1;
517 goto out;
518 }
519
520 if (ref)
521 replay_result_queue_update(out, ref, &old_oid,
522 &last_commit->object.oid);
523
524 ret = 0;
525
526 out:
527 if (update_refs) {
528 strset_clear(update_refs);
529 free(update_refs);
530 }
531 kh_destroy_oid_map(replayed_commits);
532 merge_finalize(&merge_opt, &result);
533 free(advance);
534 free(revert);
535 return ret;
536 }