useBranchMergeDiff.ts 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. import { useMemo } from 'react'
  2. import { useEdgeFunctionsDiff, type EdgeFunctionsDiffResult } from './useEdgeFunctionsDiff'
  3. import { useIsPgDeltaDiffEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
  4. import { useBranchDiffQuery } from '@/data/branches/branch-diff-query'
  5. import { useMigrationsQuery } from '@/data/database/migrations-query'
  6. interface UseBranchMergeDiffProps {
  7. currentBranchRef?: string
  8. parentProjectRef?: string
  9. currentBranchConnectionString?: string
  10. parentBranchConnectionString?: string
  11. currentBranchCreatedAt?: string
  12. }
  13. export interface BranchMergeDiffResult {
  14. // Database diff
  15. diffContent: string | undefined
  16. isDatabaseDiffLoading: boolean
  17. isDatabaseDiffRefetching: boolean
  18. databaseDiffError: any
  19. refetchDatabaseDiff: () => void
  20. // Edge functions diff
  21. edgeFunctionsDiff: EdgeFunctionsDiffResult
  22. // Migrations
  23. currentBranchMigrations: any[] | undefined
  24. mainBranchMigrations: any[] | undefined
  25. refetchCurrentBranchMigrations: () => void
  26. refetchMainBranchMigrations: () => void
  27. // Branch state
  28. isBranchOutOfDateMigrations: boolean
  29. hasEdgeFunctionModifications: boolean
  30. missingFunctionsCount: number
  31. hasMissingFunctions: boolean
  32. outOfDateFunctionsCount: number
  33. hasOutOfDateFunctions: boolean
  34. isBranchOutOfDateOverall: boolean
  35. missingMigrationsCount: number
  36. modifiedFunctionsCount: number
  37. // Combined states
  38. isLoading: boolean
  39. hasChanges: boolean
  40. }
  41. export const useBranchMergeDiff = ({
  42. currentBranchRef,
  43. parentProjectRef,
  44. currentBranchConnectionString,
  45. parentBranchConnectionString,
  46. currentBranchCreatedAt,
  47. }: UseBranchMergeDiffProps): BranchMergeDiffResult => {
  48. const pgDeltaDiffEnabled = useIsPgDeltaDiffEnabled()
  49. // Get database diff
  50. const {
  51. data: diffContent,
  52. isPending: isDatabaseDiffLoading,
  53. isRefetching: isDatabaseDiffRefetching,
  54. error: databaseDiffError,
  55. refetch: refetchDatabaseDiff,
  56. } = useBranchDiffQuery(
  57. {
  58. branchRef: currentBranchRef || '',
  59. projectRef: parentProjectRef || '',
  60. pgdelta: pgDeltaDiffEnabled,
  61. },
  62. {
  63. enabled: !!currentBranchRef && !!parentProjectRef,
  64. refetchOnMount: 'always',
  65. refetchOnWindowFocus: false,
  66. staleTime: 0,
  67. }
  68. )
  69. // Get migrations for both current branch and main branch
  70. const { data: currentBranchMigrations, refetch: refetchCurrentBranchMigrations } =
  71. useMigrationsQuery(
  72. {
  73. projectRef: currentBranchRef,
  74. connectionString: currentBranchConnectionString,
  75. },
  76. {
  77. enabled: !!currentBranchRef,
  78. staleTime: 3000,
  79. }
  80. )
  81. const { data: mainBranchMigrations, refetch: refetchMainBranchMigrations } = useMigrationsQuery(
  82. {
  83. projectRef: parentProjectRef,
  84. connectionString: parentBranchConnectionString,
  85. },
  86. {
  87. enabled: !!parentProjectRef,
  88. staleTime: 3000,
  89. }
  90. )
  91. // Get edge functions diff
  92. const edgeFunctionsDiff = useEdgeFunctionsDiff({
  93. currentBranchRef,
  94. mainBranchRef: parentProjectRef,
  95. })
  96. // Check if current branch is out of date with main branch (migrations)
  97. const isBranchOutOfDateMigrations = useMemo(() => {
  98. if (!currentBranchMigrations || !mainBranchMigrations) return false
  99. // Get the latest migration version from main branch
  100. const latestMainMigration = mainBranchMigrations[0] // migrations are ordered by version desc
  101. if (!latestMainMigration) return false
  102. // Check if current branch has this latest migration
  103. const hasLatestMigration = currentBranchMigrations.some(
  104. (migration) => migration.version === latestMainMigration.version
  105. )
  106. return !hasLatestMigration
  107. }, [currentBranchMigrations, mainBranchMigrations])
  108. // Check if main branch has functions that are newer than the current branch versions
  109. const outOfDateFunctionsCount = useMemo(() => {
  110. if (!edgeFunctionsDiff.modifiedSlugs.length) return 0
  111. return edgeFunctionsDiff.modifiedSlugs.filter((slug) => {
  112. // Get both main and current branch function data
  113. const mainFunction = edgeFunctionsDiff.mainBranchFunctions?.find((func) => func.slug === slug)
  114. const currentFunction = edgeFunctionsDiff.currentBranchFunctions?.find(
  115. (func) => func.slug === slug
  116. )
  117. if (!mainFunction || !currentFunction) return false
  118. // Compare updated_at timestamps - if main branch function is newer, it was modified after current branch
  119. const mainUpdatedAt = mainFunction.updated_at * 1000 // Convert to milliseconds
  120. const currentUpdatedAt = currentFunction.updated_at * 1000 // Convert to milliseconds
  121. return mainUpdatedAt > currentUpdatedAt
  122. }).length
  123. }, [
  124. edgeFunctionsDiff.modifiedSlugs,
  125. edgeFunctionsDiff.mainBranchFunctions,
  126. edgeFunctionsDiff.currentBranchFunctions,
  127. ])
  128. // Check if main branch has functions that are newer than the current branch versions
  129. const hasOutOfDateFunctions = outOfDateFunctionsCount > 0
  130. // Check if current branch has any edge function modifications (not additions/removals)
  131. // This only includes functions where the current branch version is newer than the main branch version
  132. const hasEdgeFunctionModifications =
  133. edgeFunctionsDiff.modifiedSlugs.length > outOfDateFunctionsCount
  134. // Count of removed functions that were updated on main branch after this branch was created
  135. // Note: For removed functions, we use branch creation date since there's no current branch version to compare to
  136. const missingFunctionsCount = useMemo(() => {
  137. if (!currentBranchCreatedAt || !edgeFunctionsDiff.removedSlugs.length) return 0
  138. const branchCreatedAt = new Date(currentBranchCreatedAt).getTime()
  139. return edgeFunctionsDiff.removedSlugs.filter((slug) => {
  140. // Access main branch function data from the original functions list
  141. const mainFunction = edgeFunctionsDiff.mainBranchFunctions?.find((func) => func.slug === slug)
  142. if (!mainFunction) return false
  143. // Check if function was updated after branch creation
  144. const functionUpdatedAt = mainFunction.updated_at * 1000 // Convert to milliseconds
  145. return functionUpdatedAt > branchCreatedAt
  146. }).length
  147. }, [
  148. currentBranchCreatedAt,
  149. edgeFunctionsDiff.removedSlugs,
  150. edgeFunctionsDiff.mainBranchFunctions,
  151. ])
  152. // Check if main branch has functions removed from current branch that were updated after branch creation
  153. const hasMissingFunctions = missingFunctionsCount > 0
  154. // Update overall out-of-date check to include newer removed functions and newer modified functions
  155. const isBranchOutOfDateOverall =
  156. isBranchOutOfDateMigrations || hasMissingFunctions || hasOutOfDateFunctions
  157. // Get the count of migrations that the branch is missing
  158. const missingMigrationsCount = useMemo(() => {
  159. if (!currentBranchMigrations || !mainBranchMigrations || !isBranchOutOfDateMigrations) return 0
  160. const currentVersions = new Set(currentBranchMigrations.map((m) => m.version))
  161. return mainBranchMigrations.filter((m) => !currentVersions.has(m.version)).length
  162. }, [currentBranchMigrations, mainBranchMigrations, isBranchOutOfDateMigrations])
  163. // Get count of modified functions
  164. const modifiedFunctionsCount = edgeFunctionsDiff.modifiedSlugs.length
  165. // Check if there are any changes (database or edge functions)
  166. const hasChanges = useMemo(() => {
  167. // Check database changes
  168. const hasDatabaseChanges = diffContent && diffContent.trim() !== ''
  169. // Check edge function changes
  170. const hasEdgeFunctionChanges = edgeFunctionsDiff.hasChanges
  171. return hasDatabaseChanges || hasEdgeFunctionChanges
  172. }, [diffContent, edgeFunctionsDiff.hasChanges])
  173. const isLoading = isDatabaseDiffLoading || edgeFunctionsDiff.isLoading
  174. return {
  175. // Database diff
  176. diffContent,
  177. isDatabaseDiffLoading,
  178. isDatabaseDiffRefetching,
  179. databaseDiffError,
  180. refetchDatabaseDiff,
  181. // Edge functions diff
  182. edgeFunctionsDiff,
  183. // Migrations
  184. currentBranchMigrations,
  185. mainBranchMigrations,
  186. refetchCurrentBranchMigrations,
  187. refetchMainBranchMigrations,
  188. // Branch state
  189. isBranchOutOfDateMigrations,
  190. hasEdgeFunctionModifications,
  191. missingFunctionsCount,
  192. hasMissingFunctions,
  193. outOfDateFunctionsCount,
  194. hasOutOfDateFunctions,
  195. isBranchOutOfDateOverall,
  196. missingMigrationsCount,
  197. modifiedFunctionsCount,
  198. // Combined states
  199. isLoading,
  200. hasChanges,
  201. }
  202. }