11
11
12
12
13
13
class Rebase (GitSimBaseCommand ):
14
- def __init__ (self , branch : str , rebase_merges : bool , onto : bool , oldparent : str , until : str ):
14
+ def __init__ (self , newbase : str , rebase_merges : bool , onto : bool , oldbase : str , until : str ):
15
15
super ().__init__ ()
16
- self .branch = branch
16
+ self .newbase = newbase
17
17
self .rebase_merges = rebase_merges
18
18
self .onto = onto
19
- self .oldparent = oldparent
19
+ self .oldbase = oldbase
20
20
self .until = until
21
21
22
+ self .non_merge_reached = False
23
+
22
24
try :
23
- git .repo .fun .rev_parse (self .repo , self .branch )
25
+ git .repo .fun .rev_parse (self .repo , self .newbase )
24
26
except git .exc .BadName :
25
27
print (
26
28
"git-sim error: '"
27
- + self .branch
29
+ + self .newbase
28
30
+ "' is not a valid Git ref or identifier."
29
31
)
30
32
sys .exit (1 )
31
33
32
34
if self .onto :
33
- if not self .oldparent :
35
+ if not self .oldbase :
36
+ print (
37
+ "git-sim error: Please specify <oldbase> as the parent of the commit to rebase"
38
+ )
39
+ sys .exit (1 )
40
+ elif not self .is_on_mainline (self .oldbase , "HEAD" ):
34
41
print (
35
- "git-sim error: Please specify the parent of the commit to rebase ('oldparent') "
42
+ "git-sim error: Currently only mainline commit paths (i.e. paths traced following _first parents_) along <oldbase> to HEAD are supported for rebase simulations "
36
43
)
37
44
sys .exit (1 )
38
- self .n = max (self .get_mainline_distance (self .oldparent , "HEAD" ), self .n )
45
+ self .n = max (self .get_shortest_distance (self .oldbase , "HEAD" ), self .n )
39
46
40
47
if self .until :
41
- self .until_n = self .get_mainline_distance (self .oldparent , self .until )
48
+ self .until_n = self .get_shortest_distance (self .oldbase , self .until )
42
49
else :
43
- if self .oldparent or self .until :
50
+ if self .oldbase or self .until :
44
51
print (
45
- "git-sim error: Please use --onto flag when specifying <oldparent > and <until>"
52
+ "git-sim error: Please use --onto flag when specifying <oldbase > and <until>"
46
53
)
47
54
sys .exit (1 )
48
55
49
- if self .branch in [branch .name for branch in self .repo .heads ]:
50
- self .selected_branches .append (self .branch )
56
+ if self .newbase in [branch .name for branch in self .repo .heads ]:
57
+ self .selected_branches .append (self .newbase )
51
58
52
59
try :
53
60
self .selected_branches .append (self .repo .active_branch .name )
54
61
except TypeError :
55
62
pass
56
63
57
- self .cmd += f"{ type (self ).__name__ .lower ()} { ' --rebase-merges' if self .rebase_merges else '' } { ' --onto' if self .onto else '' } { self .branch } { ' ' + self .oldparent if self .onto and self .oldparent else '' } { ' ' + self .until if self .onto and self .until else '' } "
64
+ self .cmd += f"{ type (self ).__name__ .lower ()} { ' --rebase-merges' if self .rebase_merges else '' } { ' --onto' if self .onto else '' } { self .newbase } { ' ' + self .oldbase if self .onto and self .oldbase else '' } { ' ' + self .until if self .onto and self .until else '' } "
58
65
59
66
self .alt_colors = {
60
67
0 : [m .BLUE_B , m .BLUE_E ],
@@ -69,58 +76,61 @@ def construct(self):
69
76
if not settings .stdout and not settings .output_only_path and not settings .quiet :
70
77
print (f"{ settings .INFO_STRING } { self .cmd } " )
71
78
72
- if self .branch in self .repo .git .branch (
73
- "--contains" , self .repo .active_branch .name
74
- ):
79
+ if self .repo .is_ancestor (self .repo .head .commit .hexsha , self .newbase ):
75
80
print (
76
- "git-sim error: Branch '"
77
- + self .repo .active_branch . name
78
- + "' is already included in the history of active branch '"
79
- + self .branch
81
+ "git-sim error: Current HEAD '"
82
+ + self .repo .head . commit . hexsha
83
+ + "' is already included in the history of '"
84
+ + self .newbase
80
85
+ "'."
81
86
)
82
87
sys .exit (1 )
83
88
84
- if self .repo .active_branch .name in self .repo .git .branch (
85
- "--contains" , self .branch
86
- ):
89
+ if self .repo .is_ancestor (self .newbase , self .repo .head .commit .hexsha ):
87
90
print (
88
- "git-sim error: Branch '"
89
- + self .branch
90
- + "' is already based on active branch '"
91
- + self .repo .active_branch . name
91
+ "git-sim error: New base '"
92
+ + self .newbase
93
+ + "' is already based on current HEAD '"
94
+ + self .repo .head . commit . hexsha
92
95
+ "'."
93
96
)
94
97
sys .exit (1 )
95
98
96
99
self .show_intro ()
97
- branch_commit = self .get_commit (self .branch )
98
- self .parse_commits (branch_commit )
100
+ newbase_commit = self .get_commit (self .newbase )
101
+ self .parse_commits (newbase_commit )
99
102
head_commit = self .get_commit ()
100
103
default_commits = {}
101
104
self .get_default_commits (self .get_commit (), default_commits )
102
105
flat_default_commits = self .sort_and_flatten (default_commits )
103
106
104
107
self .parse_commits (head_commit , shift = 4 * m .DOWN )
105
108
self .parse_all ()
106
- self .center_frame_on_commit (branch_commit )
109
+ self .center_frame_on_commit (newbase_commit )
107
110
108
111
self .to_rebase = []
109
112
for c in flat_default_commits :
110
- if self . branch not in self .repo .git . branch ( "--contains" , c ):
113
+ if not self .repo .is_ancestor ( c , self . newbase ):
111
114
if self .onto and self .until :
112
- range_commits = list (self .repo .iter_commits (f"{ self .oldparent } ...{ self .until } " ))
115
+ range_commits = list (self .repo .iter_commits (f"{ self .oldbase } ...{ self .until } " ))
113
116
if c in range_commits :
114
117
self .to_rebase .append (c )
115
118
else :
116
119
self .to_rebase .append (c )
120
+ if self .rebase_merges :
121
+ if len (self .to_rebase [- 1 ].parents ) > 1 :
122
+ self .cleaned_to_rebase = []
123
+ for j , tr in enumerate (self .to_rebase ):
124
+ if self .repo .is_ancestor (self .to_rebase [- 1 ], tr ):
125
+ self .cleaned_to_rebase .append (tr )
126
+ self .to_rebase = self .cleaned_to_rebase
117
127
118
128
reached_base = False
119
- merge_base = self .repo .git .merge_base (self .branch , self .repo .active_branch . name )
129
+ merge_base = self .repo .git .merge_base (self .newbase , self .repo .head . commit . hexsha )
120
130
if merge_base in self .drawnCommits or (self .onto and self .to_rebase [- 1 ].hexsha in self .drawnCommits ):
121
131
reached_base = True
122
132
123
- parent = branch_commit .hexsha
133
+ parent = newbase_commit .hexsha
124
134
branch_counts = {}
125
135
rebased_shas = []
126
136
rebased_sha_map = {}
@@ -159,15 +169,20 @@ def construct(self):
159
169
arrow_color = self .alt_colors [color_index % len (self .alt_colors )][1 if branch_counts [color_index ] % 2 == 0 else 1 ]
160
170
self .draw_arrow_between_commits (tr .hexsha , rebased_shas [j - k ], color = arrow_color )
161
171
162
- if self .rebase_merges :
163
- if self .onto and self .until :
164
- until_sha = self .get_commit (self .until ).hexsha
165
- if until_sha == self .repo .head .commit .hexsha :
166
- self .reset_head_branch (rebased_sha_map [until_sha ])
167
- else :
168
- self .reset_head (rebased_sha_map [until_sha ])
172
+ if self .onto and self .until :
173
+ until_sha = self .get_commit (self .until ).hexsha
174
+ if until_sha == self .repo .head .commit .hexsha :
175
+ self .reset_head_branch (rebased_sha_map [until_sha ])
169
176
else :
170
- self .reset_head_branch (rebased_sha_map [default_commits [0 ][0 ].hexsha ])
177
+ try :
178
+ self .reset_head (rebased_sha_map [until_sha ])
179
+ except KeyError :
180
+ for sha in rebased_sha_map :
181
+ if len (self .get_commit (sha ).parents ) < 2 :
182
+ self .reset_head (rebased_sha_map [sha ])
183
+ break
184
+ elif self .rebase_merges :
185
+ self .reset_head_branch (rebased_sha_map [default_commits [0 ][0 ].hexsha ])
171
186
else :
172
187
self .reset_head_branch (parent )
173
188
self .color_by (offset = 2 * len (self .to_rebase ))
@@ -223,14 +238,21 @@ def setup_and_draw_parent(
223
238
end = tuple (self .drawnCommits [child ].get_center ())
224
239
arrow_start_ends .add ((start , end ))
225
240
if self .rebase_merges :
226
- for p in self .get_commit (orig ).parents :
227
- if self .branch in self .repo .git .branch (
228
- "--contains" , p
229
- ):
241
+ orig_commit = self .get_commit (orig )
242
+ if len (orig_commit .parents ) < 2 :
243
+ self .non_merge_reached = True
244
+ for p in orig_commit .parents :
245
+ if self .repo .is_ancestor (p , self .newbase ):
230
246
continue
231
247
try :
232
248
if p not in self .to_rebase :
233
- end = tuple (self .drawnCommits [self .get_commit (self .branch ).hexsha ].get_center ())
249
+ if branch_index > 0 :
250
+ end = tuple (self .drawnCommits [self .get_commit (self .newbase ).hexsha ].get_center ())
251
+ elif not self .non_merge_reached :
252
+ if p .hexsha == orig_commit .parents [1 ].hexsha :
253
+ end = tuple (self .drawnCommits [p .hexsha ].get_center ())
254
+ else :
255
+ continue
234
256
else :
235
257
end = tuple (self .drawnCommits [p .hexsha ].get_center () + m .UP * 4 + (m .LEFT if settings .reverse else m .RIGHT ) * len (default_commits [0 ]) * 2.5 + (m .LEFT * side_offset if settings .reverse else m .RIGHT * side_offset ) * 5 )
236
258
arrow_start_ends .add ((start , end ))
@@ -303,11 +325,9 @@ def get_default_commits(self, commit, default_commits, branch_index=0):
303
325
if branch_index not in default_commits :
304
326
default_commits [branch_index ] = []
305
327
if len (default_commits [branch_index ]) < self .n :
306
- if self .onto and commit .hexsha == self .get_commit (self .oldparent ).hexsha :
328
+ if self .onto and commit .hexsha == self .get_commit (self .oldbase ).hexsha :
307
329
return default_commits
308
- if commit not in self .sort_and_flatten (default_commits ) and self .branch not in self .repo .git .branch (
309
- "--contains" , commit
310
- ):
330
+ if commit not in self .sort_and_flatten (default_commits ) and not self .repo .is_ancestor (commit , self .newbase ):
311
331
default_commits [branch_index ].append (commit )
312
332
for i , parent in enumerate (commit .parents ):
313
333
self .get_default_commits (parent , default_commits , branch_index + i )
0 commit comments