77from syrinx .exceptions import UnknownBranchError
88if TYPE_CHECKING :
99 from syrinx .config import BuildMetaInfo
10+ from syrinx .node import ContentNode
1011logger = logging .getLogger (__name__ )
1112
1213def read_branches (root_dir : str ) -> Branches :
@@ -60,6 +61,8 @@ class Branches:
6061
6162 def __init__ (self , inner : Dict [str , datetime ]) -> None :
6263 self .inner = inner
64+ self .active = None
65+ self .changed_files = set ()
6366
6467 def get_lastmodified (self , branch_name : str ) -> datetime :
6568 """Get the last modified datetime for a specific branch.
@@ -87,6 +90,7 @@ def update(self, meta: BuildMetaInfo, root_dir: str) -> None:
8790 Uses git to determine the current branch name and records the build timestamp
8891 for that branch. If no branch is found, does nothing.
8992 Updates the internal branch dictionary and persists changes to branches.toml.
93+ Also caches the list of changed files for use in warnIfModifiedNodeHasOutdatedBranch.
9094
9195 Args:
9296 meta: BuildMetaInfo object containing timestamp and other build metadata
@@ -99,9 +103,20 @@ def update(self, meta: BuildMetaInfo, root_dir: str) -> None:
99103 # Detached HEAD state, no branch name available
100104 return
101105 branch_name = repo .active_branch .name
106+
107+ # Cache changed files (both staged and unstaged, plus untracked)
108+ try :
109+ self .changed_files = set (repo .untracked_files )
110+ self .changed_files .update (item .a_path for item in repo .index .diff (None ) if item .a_path )
111+ self .changed_files .update (item .a_path for item in repo .index .diff ('HEAD' ) if item .a_path )
112+ except (AttributeError , TypeError ):
113+ # Unable to get changed files (e.g., in tests with mocked repo)
114+ self .changed_files = set ()
115+
102116 except (InvalidGitRepositoryError , ValueError , TypeError ):
103117 # Not a git repository or unable to determine branch
104118 return
119+ self .active = branch_name
105120
106121 # Update the branch with the current build timestamp
107122 self .inner [branch_name ] = meta .timestamp
@@ -111,3 +126,35 @@ def update(self, meta: BuildMetaInfo, root_dir: str) -> None:
111126 # Write the updated branches to file if we have any entries
112127 if self .inner :
113128 write_branches (self .inner , root_dir )
129+
130+ def warnIfModifiedNodeHasOutdatedBranch (self , node : ContentNode , branch : str ):
131+ """Check if a modified node's file has uncommitted changes on a different branch.
132+
133+ If the node's file has uncommitted changes and the specified branch doesn't
134+ match the currently active branch, logs a warning.
135+ Uses cached changed_files populated by update() for efficiency.
136+
137+ Args:
138+ node: The ContentNode to check
139+ branch: The branch name specified in the node's LastModifiedBranch
140+ """
141+ # Only check if we have an active branch to compare against
142+ if self .active is None :
143+ return
144+
145+ # If the branch matches the active branch, no warning needed
146+ if branch == self .active :
147+ return
148+
149+ # Construct the file path relative to the repo root
150+ # node.path is the directory path, node.name is the filename
151+ file_path = join (node .path .lstrip ('/' ), node .name )
152+ if file_path .startswith ('./' ):
153+ file_path = file_path [2 :]
154+
155+ # Check if the file is in the cached changed files
156+ if file_path in self .changed_files :
157+ logger .warning (
158+ f"File '{ node .name } ' at '{ node .path } ' has uncommitted changes "
159+ f"but references LastModifiedBranch '{ branch } ' while currently on branch '{ self .active } '"
160+ )
0 commit comments