Skip to content

Commit 02230b7

Browse files
committed
Merge branch 'cc/git-replay'
Introduce "git replay", a tool meant on the server side without working tree to recreate a history. * cc/git-replay: replay: stop assuming replayed branches do not diverge replay: add --contained to rebase contained branches replay: add --advance or 'cherry-pick' mode replay: use standard revision ranges replay: make it a minimal server side command replay: remove HEAD related sanity check replay: remove progress and info output replay: add an important FIXME comment about gpg signing replay: change rev walking options replay: introduce pick_regular_commit() replay: die() instead of failing assert() replay: start using parse_options API replay: introduce new builtin t6429: remove switching aspects of fast-rebase
2 parents 3335365 + e928c11 commit 02230b7

File tree

12 files changed

+801
-264
lines changed

12 files changed

+801
-264
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@
135135
/git-remote-ext
136136
/git-repack
137137
/git-replace
138+
/git-replay
138139
/git-request-pull
139140
/git-rerere
140141
/git-reset

Documentation/git-replay.txt

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
git-replay(1)
2+
=============
3+
4+
NAME
5+
----
6+
git-replay - EXPERIMENTAL: Replay commits on a new base, works with bare repos too
7+
8+
9+
SYNOPSIS
10+
--------
11+
[verse]
12+
(EXPERIMENTAL!) 'git replay' ([--contained] --onto <newbase> | --advance <branch>) <revision-range>...
13+
14+
DESCRIPTION
15+
-----------
16+
17+
Takes ranges of commits and replays them onto a new location. Leaves
18+
the working tree and the index untouched, and updates no references.
19+
The output of this command is meant to be used as input to
20+
`git update-ref --stdin`, which would update the relevant branches
21+
(see the OUTPUT section below).
22+
23+
THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.
24+
25+
OPTIONS
26+
-------
27+
28+
--onto <newbase>::
29+
Starting point at which to create the new commits. May be any
30+
valid commit, and not just an existing branch name.
31+
+
32+
When `--onto` is specified, the update-ref command(s) in the output will
33+
update the branch(es) in the revision range to point at the new
34+
commits, similar to the way how `git rebase --update-refs` updates
35+
multiple branches in the affected range.
36+
37+
--advance <branch>::
38+
Starting point at which to create the new commits; must be a
39+
branch name.
40+
+
41+
When `--advance` is specified, the update-ref command(s) in the output
42+
will update the branch passed as an argument to `--advance` to point at
43+
the new commits (in other words, this mimics a cherry-pick operation).
44+
45+
<revision-range>::
46+
Range of commits to replay. More than one <revision-range> can
47+
be passed, but in `--advance <branch>` mode, they should have
48+
a single tip, so that it's clear where <branch> should point
49+
to. See "Specifying Ranges" in linkgit:git-rev-parse and the
50+
"Commit Limiting" options below.
51+
52+
include::rev-list-options.txt[]
53+
54+
OUTPUT
55+
------
56+
57+
When there are no conflicts, the output of this command is usable as
58+
input to `git update-ref --stdin`. It is of the form:
59+
60+
update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
61+
update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
62+
update refs/heads/branch3 ${NEW_branch3_HASH} ${OLD_branch3_HASH}
63+
64+
where the number of refs updated depends on the arguments passed and
65+
the shape of the history being replayed. When using `--advance`, the
66+
number of refs updated is always one, but for `--onto`, it can be one
67+
or more (rebasing multiple branches simultaneously is supported).
68+
69+
EXIT STATUS
70+
-----------
71+
72+
For a successful, non-conflicted replay, the exit status is 0. When
73+
the replay has conflicts, the exit status is 1. If the replay is not
74+
able to complete (or start) due to some kind of error, the exit status
75+
is something other than 0 or 1.
76+
77+
EXAMPLES
78+
--------
79+
80+
To simply rebase `mybranch` onto `target`:
81+
82+
------------
83+
$ git replay --onto target origin/main..mybranch
84+
update refs/heads/mybranch ${NEW_mybranch_HASH} ${OLD_mybranch_HASH}
85+
------------
86+
87+
To cherry-pick the commits from mybranch onto target:
88+
89+
------------
90+
$ git replay --advance target origin/main..mybranch
91+
update refs/heads/target ${NEW_target_HASH} ${OLD_target_HASH}
92+
------------
93+
94+
Note that the first two examples replay the exact same commits and on
95+
top of the exact same new base, they only differ in that the first
96+
provides instructions to make mybranch point at the new commits and
97+
the second provides instructions to make target point at them.
98+
99+
What if you have a stack of branches, one depending upon another, and
100+
you'd really like to rebase the whole set?
101+
102+
------------
103+
$ git replay --contained --onto origin/main origin/main..tipbranch
104+
update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
105+
update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
106+
update refs/heads/tipbranch ${NEW_tipbranch_HASH} ${OLD_tipbranch_HASH}
107+
------------
108+
109+
When calling `git replay`, one does not need to specify a range of
110+
commits to replay using the syntax `A..B`; any range expression will
111+
do:
112+
113+
------------
114+
$ git replay --onto origin/main ^base branch1 branch2 branch3
115+
update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
116+
update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
117+
update refs/heads/branch3 ${NEW_branch3_HASH} ${OLD_branch3_HASH}
118+
------------
119+
120+
This will simultaneously rebase `branch1`, `branch2`, and `branch3`,
121+
all commits they have since `base`, playing them on top of
122+
`origin/main`. These three branches may have commits on top of `base`
123+
that they have in common, but that does not need to be the case.
124+
125+
GIT
126+
---
127+
Part of the linkgit:git[1] suite

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,6 @@ TEST_BUILTINS_OBJS += test-dump-split-index.o
803803
TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
804804
TEST_BUILTINS_OBJS += test-env-helper.o
805805
TEST_BUILTINS_OBJS += test-example-decorate.o
806-
TEST_BUILTINS_OBJS += test-fast-rebase.o
807806
TEST_BUILTINS_OBJS += test-find-pack.o
808807
TEST_BUILTINS_OBJS += test-fsmonitor-client.o
809808
TEST_BUILTINS_OBJS += test-genrandom.o
@@ -1294,6 +1293,7 @@ BUILTIN_OBJS += builtin/remote-fd.o
12941293
BUILTIN_OBJS += builtin/remote.o
12951294
BUILTIN_OBJS += builtin/repack.o
12961295
BUILTIN_OBJS += builtin/replace.o
1296+
BUILTIN_OBJS += builtin/replay.o
12971297
BUILTIN_OBJS += builtin/rerere.o
12981298
BUILTIN_OBJS += builtin/reset.o
12991299
BUILTIN_OBJS += builtin/rev-list.o

builtin.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ int cmd_remote(int argc, const char **argv, const char *prefix);
211211
int cmd_remote_ext(int argc, const char **argv, const char *prefix);
212212
int cmd_remote_fd(int argc, const char **argv, const char *prefix);
213213
int cmd_repack(int argc, const char **argv, const char *prefix);
214+
int cmd_replay(int argc, const char **argv, const char *prefix);
214215
int cmd_rerere(int argc, const char **argv, const char *prefix);
215216
int cmd_reset(int argc, const char **argv, const char *prefix);
216217
int cmd_restore(int argc, const char **argv, const char *prefix);

0 commit comments

Comments
 (0)