1414
1515import sys
1616import typing as t
17+ import logging
18+
1719from difflib import ndiff , unified_diff
1820from functools import cached_property
1921from sqlmesh .core import constants as c
3840
3941IGNORED_PACKAGES = {"sqlmesh" , "sqlglot" }
4042
43+ logger = logging .getLogger (__name__ )
44+
4145
4246class ContextDiff (PydanticModel ):
4347 """ContextDiff is an object representing the difference between two environments.
@@ -88,6 +92,8 @@ class ContextDiff(PydanticModel):
8892 """Environment statements."""
8993 diff_rendered : bool = False
9094 """Whether the diff should compare raw vs rendered models"""
95+ initial_environment : str = ""
96+ """The initial target environment (e.g 'dev'), if the plan option `always_compare_to_prod` is set"""
9197
9298 @classmethod
9399 def create (
@@ -103,6 +109,8 @@ def create(
103109 environment_statements : t .Optional [t .List [EnvironmentStatements ]] = [],
104110 gateway_managed_virtual_layer : bool = False ,
105111 infer_python_dependencies : bool = True ,
112+ initial_environment : t .Optional [str ] = None ,
113+ always_compare_against_prod : bool = False ,
106114 ) -> ContextDiff :
107115 """Create a ContextDiff object.
108116
@@ -127,8 +135,17 @@ def create(
127135 Returns:
128136 The ContextDiff object.
129137 """
130- environment = environment .lower ()
138+ initial_environment = environment
139+ environment = _get_target_environment (
140+ environment , state_reader , always_compare_against_prod
141+ )
142+
131143 env = state_reader .get_environment (environment )
144+ initial_env = (
145+ env
146+ if initial_environment == environment
147+ else state_reader .get_environment (initial_environment )
148+ )
132149
133150 create_from_env_exists = False
134151 if env is None or env .expired :
@@ -222,6 +239,7 @@ def create(
222239
223240 return ContextDiff (
224241 environment = environment ,
242+ initial_environment = initial_environment ,
225243 is_new_environment = is_new_environment ,
226244 is_unfinalized_environment = bool (env and not env .finalized_ts ),
227245 normalize_environment_name = is_new_environment or bool (env and env .normalize_name ),
@@ -232,7 +250,9 @@ def create(
232250 modified_snapshots = modified_snapshots ,
233251 snapshots = merged_snapshots ,
234252 new_snapshots = new_snapshots ,
235- previous_plan_id = env .plan_id if env and not is_new_environment else None ,
253+ previous_plan_id = initial_env .plan_id
254+ if initial_env and not is_new_environment
255+ else None ,
236256 previously_promoted_snapshot_ids = previously_promoted_snapshot_ids ,
237257 previous_finalized_snapshots = env .previous_finalized_snapshots if env else None ,
238258 previous_requirements = env .requirements if env else {},
@@ -261,8 +281,9 @@ def create_no_diff(cls, environment: str, state_reader: StateReader) -> ContextD
261281
262282 snapshots = state_reader .get_snapshots (env .snapshots )
263283
284+ environment = env .name
264285 return ContextDiff (
265- environment = env . name ,
286+ environment = environment ,
266287 is_new_environment = False ,
267288 is_unfinalized_environment = False ,
268289 normalize_environment_name = env .normalize_name ,
@@ -281,6 +302,7 @@ def create_no_diff(cls, environment: str, state_reader: StateReader) -> ContextD
281302 previous_environment_statements = [],
282303 previous_gateway_managed_virtual_layer = env .gateway_managed ,
283304 gateway_managed_virtual_layer = env .gateway_managed ,
305+ initial_environment = environment ,
284306 )
285307
286308 @property
@@ -479,6 +501,23 @@ def text_diff(self, name: str) -> str:
479501 return ""
480502
481503
504+ def _get_target_environment (
505+ environment : str , state_reader : StateReader , always_compare_against_prod : bool = False
506+ ) -> str :
507+ if always_compare_against_prod :
508+ prod = state_reader .get_environment (c .PROD )
509+ if prod :
510+ logger .warning (
511+ f"Comparing against production environment instead of { environment } . Note that this may lead to "
512+ "additional backfills as accumulated changes are still pushed to the target environment."
513+ )
514+ environment = c .PROD
515+ else :
516+ environment = environment or c .PROD
517+
518+ return environment .lower ()
519+
520+
482521def _build_requirements (
483522 provided_requirements : t .Dict [str , str ],
484523 excluded_requirements : t .Set [str ],
0 commit comments