@@ -151,6 +151,14 @@ def __init__(self, submodule: str, files: List[str], error: str = None):
151151 self .error = error
152152
153153
154+ class AmendCommitDetectedEvent (QEvent ):
155+ Type = QEvent .User + 9
156+
157+ def __init__ (self , commitInfo : AmendCommitInfo ):
158+ super ().__init__ (QEvent .Type (AmendCommitDetectedEvent .Type ))
159+ self .commitInfo = commitInfo
160+
161+
154162class CommitWindow (StateWindow ):
155163
156164 def __init__ (self , parent = None ):
@@ -281,6 +289,10 @@ def __init__(self, parent=None):
281289
282290 self .ui .btnCommit .clicked .connect (self ._onCommitClicked )
283291
292+ self ._amendDetectExecutor = SubmoduleExecutor (self )
293+ self ._amendDetectExecutor .finished .connect (self ._onAmendDetectFinished )
294+ self ._amendDetectionResults : List [AmendCommitInfo ] = []
295+
284296 # no UI tasks
285297 self ._submoduleExecutor = SubmoduleExecutor (self )
286298 self ._submoduleExecutor .finished .connect (
@@ -888,6 +900,32 @@ def _onNonUITaskFinished(self):
888900 self .ui .spinnerUnstaged .stop ()
889901 self ._updateAmendCommitsIfNeeded ()
890902
903+ def _onAmendDetectFinished (self ):
904+ """Called when amend commit detection completes"""
905+ commits = self ._amendDetectionResults
906+ mainCommit = None
907+ if commits :
908+ for commit in commits :
909+ if not commit .repoDir or commit .repoDir == "." :
910+ mainCommit = commit
911+ break
912+ if not mainCommit :
913+ mainCommit = commits [0 ]
914+
915+ ApplicationBase .instance ().postEvent (
916+ self , TemplateReadyEvent (mainCommit .body , True ))
917+
918+ hasStagedFiles = self ._stagedModel .rowCount () > 0
919+ if not hasStagedFiles and commits :
920+ commits = [c for c in commits if c .subject == mainCommit .subject ]
921+
922+ # Determine if unchecking is allowed
923+ allowUncheck = not hasStagedFiles and len (commits ) > 1
924+
925+ # Update model with commits
926+ self ._amendCommitsModel .setAllowUncheck (allowUncheck )
927+ self ._amendCommitsModel .setCommits (commits )
928+
891929 def _blockUI (self , blocked = True ):
892930 self .ui .tbUnstage .setEnabled (not blocked )
893931 self .ui .tbUnstageAll .setEnabled (not blocked )
@@ -945,6 +983,10 @@ def event(self, evt: QEvent):
945983 evt .ntpDateTime , evt .localDateTime )
946984 return True
947985
986+ if evt .type () == AmendCommitDetectedEvent .Type :
987+ self ._handleAmendCommitDetectedEvent (evt .commitInfo )
988+ return True
989+
948990 return super ().event (evt )
949991
950992 def _addBlock (self , key : str , title : str ):
@@ -1014,6 +1056,11 @@ def _handleUpdateCommitProgress(self, submodule: str, out: str, error: str, upda
10141056 # add title only
10151057 self ._updateBlockOutput (repoName , "" , False , action )
10161058
1059+ def _handleAmendCommitDetectedEvent (self , commitInfo : AmendCommitInfo ):
1060+ """Handle single commit detected from worker thread"""
1061+ if commitInfo :
1062+ self ._amendDetectionResults .append (commitInfo )
1063+
10171064 def reloadLocalChanges (self ):
10181065 self ._statusFetcher .cancel ()
10191066 self .clear ()
@@ -1639,6 +1686,7 @@ def _onCodeReviewClicked(self):
16391686
16401687 def cancel (self , force = False ):
16411688 self ._aiMessage .cancel (force )
1689+ self ._amendDetectExecutor .cancel (force )
16421690 self ._submoduleExecutor .cancel (force )
16431691 self ._commitExecutor .cancel (force )
16441692 self ._statusFetcher .cancel (force )
@@ -2018,56 +2066,35 @@ def _onAmendToggled(self, checked: bool):
20182066
20192067 def _detectAmendCommits (self ):
20202068 """Detect which commits will be amended based on staged files or HEAD"""
2021- commits = []
2069+ # Cancel any running detection
2070+ if self ._amendDetectExecutor .isRunning ():
2071+ self ._amendDetectExecutor .cancel ()
20222072
20232073 # Collect repos that have staged files
20242074 submoduleFiles = self ._collectModelFiles (self ._stagedModel )
2025- hasStagedFiles = bool (submoduleFiles )
2075+
2076+ # Clear previous results
2077+ self ._amendDetectionResults .clear ()
20262078
20272079 if submoduleFiles :
20282080 # If there are staged files, detect commits from those repos
2029- for repoDir in submoduleFiles .keys ():
2030- fullPath = fullRepoDir (repoDir )
2031- commitInfo = self ._getCommitInfo ("HEAD" , fullPath , repoDir )
2032- if commitInfo :
2033- commits .append (commitInfo )
2081+ repoDirs = list (submoduleFiles .keys ())
20342082 else :
2035- # No staged files - detect from HEAD of main repo
2036- mainCommitInfo = self ._getCommitInfo ("HEAD" , Git .REPO_DIR , None )
2037- if not mainCommitInfo :
2038- return
2039-
2040- commits .append (mainCommitInfo )
2041- repoDirs = {mainCommitInfo .repoDir }
2042-
2043- # Check submodules for commits with the same message
2083+ # No staged files - detect from HEAD of main repo + matching submodules
20442084 app = ApplicationBase .instance ()
2045- if app .submodules :
2046- for submodule in app .submodules :
2047- if not submodule or submodule == "." :
2048- continue
2049- if submodule in repoDirs :
2050- continue
2051- subRepoDir = fullRepoDir (submodule )
2052- subCommitInfo = self ._getCommitInfo (
2053- "HEAD" , subRepoDir , submodule )
2054-
2055- # Include if commit message matches
2056- if subCommitInfo and subCommitInfo .subject == mainCommitInfo .subject :
2057- repoDirs .add (subCommitInfo .repoDir )
2058- commits .append (subCommitInfo )
2085+ repoDirs = app .submodules
20592086
2060- allowUncheck = not hasStagedFiles and len (commits ) > 1
2061- self ._amendCommitsModel .setAllowUncheck (allowUncheck )
2062- self ._amendCommitsModel .setCommits (commits )
2087+ self ._amendDetectExecutor .submit (repoDirs , self ._doDetectAmendCommits )
20632088
2064- # Get the last commit message (from the first repo) when allowed
2065- if (commits and self ._canUpdateMessage () and self ._submoduleExecutor
2066- and not self ._submoduleExecutor .isRunning ()):
2067- self ._blockUI ()
2068- self .ui .spinnerUnstaged .start ()
2069- self ._submoduleExecutor .submit (
2070- [commits [0 ].repoDir ], self ._doGetMessage )
2089+ def _doDetectAmendCommits (self , submodule : str , userData : any , cancelEvent : CancelEvent ):
2090+ if cancelEvent .isSet ():
2091+ return
2092+
2093+ fullPath = fullRepoDir (submodule )
2094+ commitInfo = self ._getCommitInfo ("HEAD" , fullPath , submodule )
2095+ if commitInfo and not cancelEvent .isSet ():
2096+ ApplicationBase .instance ().postEvent (
2097+ self , AmendCommitDetectedEvent (commitInfo ))
20712098
20722099 def _normalizeRepoDirDisplay (self , repoDir : str ):
20732100 if not repoDir or repoDir == "." :
@@ -2076,14 +2103,15 @@ def _normalizeRepoDirDisplay(self, repoDir: str):
20762103
20772104 def _getCommitInfo (self , sha1 : str , repoDir : str , repoDirDisplay : str ) -> AmendCommitInfo :
20782105 """Get commit information for a specific commit"""
2079- summary = Git .commitSummary (sha1 , repoDir )
2106+ summary = Git .commitSummary (sha1 , repoDir , includeFullMessage = True )
20802107 if not summary :
20812108 return None
20822109
20832110 return AmendCommitInfo (
20842111 repoDir = self ._normalizeRepoDirDisplay (repoDirDisplay ),
20852112 sha1 = summary ["sha1" ],
20862113 subject = summary ["subject" ],
2114+ body = summary ["body" ],
20872115 author = summary ["author" ],
20882116 date = summary ["date" ],
20892117 willAmend = True
@@ -2102,16 +2130,6 @@ def _canUpdateMessage(self):
21022130
21032131 return False
21042132
2105- def _doGetMessage (self , submodule : str , userData , cancelEvent : CancelEvent ):
2106- if cancelEvent .isSet ():
2107- return
2108-
2109- repoDir = fullRepoDir (submodule )
2110- message = Git .commitMessage ("HEAD" , repoDir )
2111- if cancelEvent .isSet ():
2112- return
2113- ApplicationBase .instance ().postEvent (self , TemplateReadyEvent (message , True ))
2114-
21152133 def _onExternalDiff (self ):
21162134 ApplicationBase .instance ().trackFeatureUsage ("commit.external_diff" )
21172135 listView : QListView = self ._acRestoreFiles .data ()
0 commit comments