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