Skip to content

Commit 81613be

Browse files
newrenchriscool
authored andcommitted
replay: make it a minimal server side command
We want this command to be a minimal command that just does server side picking of commits, displaying the results on stdout for higher level scripts to consume. So let's simplify it: * remove the worktree and index reading/writing, * remove the ref (and reflog) updating, * remove the assumptions tying us to HEAD, since (a) this is not a rebase and (b) we want to be able to pick commits in a bare repo, i.e. to/from branches that are not checked out and not the main branch, * remove unneeded includes, * handle rebasing multiple branches by printing on stdout the update ref commands that should be performed. The output can be piped into `git update-ref --stdin` for the ref updates to happen. In the future to make it easier for users to use this command directly maybe an option can be added to automatically pipe its output into `git update-ref`. Co-authored-by: Christian Couder <[email protected]> Signed-off-by: Elijah Newren <[email protected]> Signed-off-by: Christian Couder <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent fda7dea commit 81613be

File tree

4 files changed

+72
-69
lines changed

4 files changed

+72
-69
lines changed

Documentation/git-replay.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ DESCRIPTION
1515
-----------
1616

1717
Takes a range of commits, specified by <oldbase> and <branch>, and
18-
replays them onto a new location (see `--onto` option below).
18+
replays them onto a new location (see `--onto` option below). Leaves
19+
the working tree and the index untouched, and updates no references.
20+
The output of this command is meant to be used as input to
21+
`git update-ref --stdin`, which would update the relevant branches.
1922

2023
THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.
2124

builtin/replay.c

Lines changed: 26 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,14 @@
66
#include "git-compat-util.h"
77

88
#include "builtin.h"
9-
#include "cache-tree.h"
10-
#include "commit.h"
119
#include "environment.h"
12-
#include "gettext.h"
13-
#include "hash.h"
1410
#include "hex.h"
1511
#include "lockfile.h"
1612
#include "merge-ort.h"
1713
#include "object-name.h"
1814
#include "parse-options.h"
1915
#include "refs.h"
2016
#include "revision.h"
21-
#include "sequencer.h"
22-
#include "setup.h"
2317
#include "strvec.h"
2418
#include <oidset.h>
2519
#include <tree.h>
@@ -102,6 +96,7 @@ static struct commit *pick_regular_commit(struct commit *pickme,
10296
pickme_tree = repo_get_commit_tree(the_repository, pickme);
10397
base_tree = repo_get_commit_tree(the_repository, base);
10498

99+
merge_opt->branch1 = short_commit_name(last_commit);
105100
merge_opt->branch2 = short_commit_name(pickme);
106101
merge_opt->ancestor = xstrfmt("parent of %s", merge_opt->branch2);
107102

@@ -122,15 +117,12 @@ int cmd_replay(int argc, const char **argv, const char *prefix)
122117
{
123118
struct commit *onto;
124119
const char *onto_name = NULL;
125-
struct commit *last_commit = NULL, *last_picked_commit = NULL;
126-
struct lock_file lock = LOCK_INIT;
120+
struct commit *last_commit = NULL;
127121
struct strvec rev_walk_args = STRVEC_INIT;
128122
struct rev_info revs;
129123
struct commit *commit;
130124
struct merge_options merge_opt;
131-
struct tree *head_tree;
132125
struct merge_result result;
133-
struct strbuf reflog_msg = STRBUF_INIT;
134126
struct strbuf branch_name = STRBUF_INIT;
135127
int ret = 0;
136128

@@ -161,10 +153,6 @@ int cmd_replay(int argc, const char **argv, const char *prefix)
161153
onto = peel_committish(onto_name);
162154
strbuf_addf(&branch_name, "refs/heads/%s", argv[2]);
163155

164-
repo_hold_locked_index(the_repository, &lock, LOCK_DIE_ON_ERROR);
165-
if (repo_read_index(the_repository) < 0)
166-
BUG("Could not read index");
167-
168156
repo_init_revisions(the_repository, &revs, prefix);
169157

170158
strvec_pushl(&rev_walk_args, "", argv[2], "--not", argv[1], NULL);
@@ -227,58 +215,44 @@ int cmd_replay(int argc, const char **argv, const char *prefix)
227215
init_merge_options(&merge_opt, the_repository);
228216
memset(&result, 0, sizeof(result));
229217
merge_opt.show_rename_progress = 0;
230-
merge_opt.branch1 = "HEAD";
231-
head_tree = repo_get_commit_tree(the_repository, onto);
232-
result.tree = head_tree;
218+
result.tree = repo_get_commit_tree(the_repository, onto);
233219
last_commit = onto;
234220
while ((commit = get_revision(&revs))) {
235-
struct commit *pick;
221+
const struct name_decoration *decoration;
236222

237223
if (!commit->parents)
238224
die(_("replaying down to root commit is not supported yet!"));
239225
if (commit->parents->next)
240226
die(_("replaying merge commits is not supported yet!"));
241227

242-
pick = pick_regular_commit(commit, last_commit, &merge_opt, &result);
243-
if (!pick)
228+
last_commit = pick_regular_commit(commit, last_commit, &merge_opt, &result);
229+
if (!last_commit)
244230
break;
245-
last_commit = pick;
246-
last_picked_commit = commit;
231+
232+
decoration = get_name_decoration(&commit->object);
233+
if (!decoration)
234+
continue;
235+
236+
while (decoration) {
237+
if (decoration->type == DECORATION_REF_LOCAL) {
238+
printf("update %s %s %s\n",
239+
decoration->name,
240+
oid_to_hex(&last_commit->object.oid),
241+
oid_to_hex(&commit->object.oid));
242+
}
243+
decoration = decoration->next;
244+
}
247245
}
248246

249247
merge_finalize(&merge_opt, &result);
248+
ret = result.clean;
250249

251-
if (result.clean < 0)
252-
exit(128);
253-
254-
if (result.clean) {
255-
strbuf_addf(&reflog_msg, "finish rebase %s onto %s",
256-
oid_to_hex(&last_picked_commit->object.oid),
257-
oid_to_hex(&last_commit->object.oid));
258-
if (update_ref(reflog_msg.buf, branch_name.buf,
259-
&last_commit->object.oid,
260-
&last_picked_commit->object.oid,
261-
REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
262-
error(_("could not update %s"), argv[2]);
263-
die("Failed to update %s", argv[2]);
264-
}
265-
if (create_symref("HEAD", branch_name.buf, reflog_msg.buf) < 0)
266-
die(_("unable to update HEAD"));
267-
} else {
268-
strbuf_addf(&reflog_msg, "rebase progress up to %s",
269-
oid_to_hex(&last_picked_commit->object.oid));
270-
if (update_ref(reflog_msg.buf, "HEAD",
271-
&last_commit->object.oid,
272-
&onto->object.oid,
273-
REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
274-
error(_("could not update %s"), argv[2]);
275-
die("Failed to update %s", argv[2]);
276-
}
277-
}
278-
ret = (result.clean == 0);
279250
cleanup:
280-
strbuf_release(&reflog_msg);
281251
strbuf_release(&branch_name);
282252
release_revisions(&revs);
283-
return ret;
253+
254+
/* Return */
255+
if (ret < 0)
256+
exit(128);
257+
return ret ? 0 : 1;
284258
}

t/t3650-replay-basics.sh

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,29 @@ test_expect_success 'setup' '
4747
test_commit C.conflict C.t conflict
4848
'
4949

50+
test_expect_success 'setup bare' '
51+
git clone --bare . bare
52+
'
53+
5054
test_expect_success 'using replay to rebase two branches, one on top of other' '
5155
git replay --onto main topic1 topic2 >result &&
5256
57+
test_line_count = 1 result &&
58+
5359
git log --format=%s $(cut -f 3 -d " " result) >actual &&
5460
test_write_lines E D M L B A >expect &&
55-
test_cmp expect actual
61+
test_cmp expect actual &&
62+
63+
printf "update refs/heads/topic2 " >expect &&
64+
printf "%s " $(cut -f 3 -d " " result) >>expect &&
65+
git rev-parse topic2 >>expect &&
66+
67+
test_cmp expect result
68+
'
69+
70+
test_expect_success 'using replay on bare repo to rebase two branches, one on top of other' '
71+
git -C bare replay --onto main topic1 topic2 >result-bare &&
72+
test_cmp expect result-bare
5673
'
5774

5875
test_done

t/t6429-merge-sequence-rename-caching.sh

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,9 @@ test_expect_success 'caching renames does not preclude finding new ones' '
7171
7272
git switch upstream &&
7373
74-
git replay --onto HEAD upstream~1 topic &&
75-
git reset --hard topic &&
74+
git replay --onto HEAD upstream~1 topic >out &&
75+
git update-ref --stdin <out &&
76+
git checkout topic &&
7677
7778
git ls-files >tracked-files &&
7879
test_line_count = 2 tracked-files &&
@@ -140,7 +141,9 @@ test_expect_success 'cherry-pick both a commit and its immediate revert' '
140141
GIT_TRACE2_PERF="$(pwd)/trace.output" &&
141142
export GIT_TRACE2_PERF &&
142143
143-
git replay --onto HEAD upstream~1 topic &&
144+
git replay --onto HEAD upstream~1 topic >out &&
145+
git update-ref --stdin <out &&
146+
git checkout topic &&
144147
145148
grep region_enter.*diffcore_rename trace.output >calls &&
146149
test_line_count = 1 calls
@@ -198,8 +201,9 @@ test_expect_success 'rename same file identically, then reintroduce it' '
198201
GIT_TRACE2_PERF="$(pwd)/trace.output" &&
199202
export GIT_TRACE2_PERF &&
200203
201-
git replay --onto HEAD upstream~1 topic &&
202-
git reset --hard topic &&
204+
git replay --onto HEAD upstream~1 topic >out &&
205+
git update-ref --stdin <out &&
206+
git checkout topic &&
203207
204208
git ls-files >tracked &&
205209
test_line_count = 2 tracked &&
@@ -275,8 +279,9 @@ test_expect_success 'rename same file identically, then add file to old dir' '
275279
GIT_TRACE2_PERF="$(pwd)/trace.output" &&
276280
export GIT_TRACE2_PERF &&
277281
278-
git replay --onto HEAD upstream~1 topic &&
279-
git reset --hard topic &&
282+
git replay --onto HEAD upstream~1 topic >out &&
283+
git update-ref --stdin <out &&
284+
git checkout topic &&
280285
281286
git ls-files >tracked &&
282287
test_line_count = 4 tracked &&
@@ -451,8 +456,9 @@ test_expect_success 'dir rename unneeded, then add new file to old dir' '
451456
GIT_TRACE2_PERF="$(pwd)/trace.output" &&
452457
export GIT_TRACE2_PERF &&
453458
454-
git replay --onto HEAD upstream~1 topic &&
455-
git reset --hard topic &&
459+
git replay --onto HEAD upstream~1 topic >out &&
460+
git update-ref --stdin <out &&
461+
git checkout topic &&
456462
457463
grep region_enter.*diffcore_rename trace.output >calls &&
458464
test_line_count = 2 calls &&
@@ -517,8 +523,9 @@ test_expect_success 'dir rename unneeded, then rename existing file into old dir
517523
GIT_TRACE2_PERF="$(pwd)/trace.output" &&
518524
export GIT_TRACE2_PERF &&
519525
520-
git replay --onto HEAD upstream~1 topic &&
521-
git reset --hard topic &&
526+
git replay --onto HEAD upstream~1 topic >out &&
527+
git update-ref --stdin <out &&
528+
git checkout topic &&
522529
523530
grep region_enter.*diffcore_rename trace.output >calls &&
524531
test_line_count = 3 calls &&
@@ -619,8 +626,9 @@ test_expect_success 'caching renames only on upstream side, part 1' '
619626
GIT_TRACE2_PERF="$(pwd)/trace.output" &&
620627
export GIT_TRACE2_PERF &&
621628
622-
git replay --onto HEAD upstream~1 topic &&
623-
git reset --hard topic &&
629+
git replay --onto HEAD upstream~1 topic >out &&
630+
git update-ref --stdin <out &&
631+
git checkout topic &&
624632
625633
grep region_enter.*diffcore_rename trace.output >calls &&
626634
test_line_count = 1 calls &&
@@ -677,8 +685,9 @@ test_expect_success 'caching renames only on upstream side, part 2' '
677685
GIT_TRACE2_PERF="$(pwd)/trace.output" &&
678686
export GIT_TRACE2_PERF &&
679687
680-
git replay --onto HEAD upstream~1 topic &&
681-
git reset --hard topic &&
688+
git replay --onto HEAD upstream~1 topic >out &&
689+
git update-ref --stdin <out &&
690+
git checkout topic &&
682691
683692
grep region_enter.*diffcore_rename trace.output >calls &&
684693
test_line_count = 2 calls &&

0 commit comments

Comments
 (0)