1111
1212
1313class 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 ):
1515 super ().__init__ ()
16- self .branch = branch
16+ self .newbase = newbase
1717 self .rebase_merges = rebase_merges
1818 self .onto = onto
19- self .oldparent = oldparent
19+ self .oldbase = oldbase
2020 self .until = until
2121
22+ self .non_merge_reached = False
23+
2224 try :
23- git .repo .fun .rev_parse (self .repo , self .branch )
25+ git .repo .fun .rev_parse (self .repo , self .newbase )
2426 except git .exc .BadName :
2527 print (
2628 "git-sim error: '"
27- + self .branch
29+ + self .newbase
2830 + "' is not a valid Git ref or identifier."
2931 )
3032 sys .exit (1 )
3133
3234 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" ):
3441 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 "
3643 )
3744 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 )
3946
4047 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 )
4249 else :
43- if self .oldparent or self .until :
50+ if self .oldbase or self .until :
4451 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>"
4653 )
4754 sys .exit (1 )
4855
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 )
5158
5259 try :
5360 self .selected_branches .append (self .repo .active_branch .name )
5461 except TypeError :
5562 pass
5663
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 '' } "
5865
5966 self .alt_colors = {
6067 0 : [m .BLUE_B , m .BLUE_E ],
@@ -69,58 +76,61 @@ def construct(self):
6976 if not settings .stdout and not settings .output_only_path and not settings .quiet :
7077 print (f"{ settings .INFO_STRING } { self .cmd } " )
7178
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 ):
7580 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
8085 + "'."
8186 )
8287 sys .exit (1 )
8388
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 ):
8790 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
9295 + "'."
9396 )
9497 sys .exit (1 )
9598
9699 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 )
99102 head_commit = self .get_commit ()
100103 default_commits = {}
101104 self .get_default_commits (self .get_commit (), default_commits )
102105 flat_default_commits = self .sort_and_flatten (default_commits )
103106
104107 self .parse_commits (head_commit , shift = 4 * m .DOWN )
105108 self .parse_all ()
106- self .center_frame_on_commit (branch_commit )
109+ self .center_frame_on_commit (newbase_commit )
107110
108111 self .to_rebase = []
109112 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 ):
111114 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 } " ))
113116 if c in range_commits :
114117 self .to_rebase .append (c )
115118 else :
116119 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
117127
118128 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 )
120130 if merge_base in self .drawnCommits or (self .onto and self .to_rebase [- 1 ].hexsha in self .drawnCommits ):
121131 reached_base = True
122132
123- parent = branch_commit .hexsha
133+ parent = newbase_commit .hexsha
124134 branch_counts = {}
125135 rebased_shas = []
126136 rebased_sha_map = {}
@@ -159,15 +169,20 @@ def construct(self):
159169 arrow_color = self .alt_colors [color_index % len (self .alt_colors )][1 if branch_counts [color_index ] % 2 == 0 else 1 ]
160170 self .draw_arrow_between_commits (tr .hexsha , rebased_shas [j - k ], color = arrow_color )
161171
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 ])
169176 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 ])
171186 else :
172187 self .reset_head_branch (parent )
173188 self .color_by (offset = 2 * len (self .to_rebase ))
@@ -223,14 +238,21 @@ def setup_and_draw_parent(
223238 end = tuple (self .drawnCommits [child ].get_center ())
224239 arrow_start_ends .add ((start , end ))
225240 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 ):
230246 continue
231247 try :
232248 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
234256 else :
235257 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 )
236258 arrow_start_ends .add ((start , end ))
@@ -303,11 +325,9 @@ def get_default_commits(self, commit, default_commits, branch_index=0):
303325 if branch_index not in default_commits :
304326 default_commits [branch_index ] = []
305327 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 :
307329 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 ):
311331 default_commits [branch_index ].append (commit )
312332 for i , parent in enumerate (commit .parents ):
313333 self .get_default_commits (parent , default_commits , branch_index + i )
0 commit comments