File: View.hs

package info (click to toggle)
git-annex 10.20251029-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 75,300 kB
  • sloc: haskell: 91,492; javascript: 9,103; sh: 1,593; makefile: 216; perl: 137; ansic: 44
file content (160 lines) | stat: -rw-r--r-- 4,838 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
{- git-annex recent views log
 -
 - The most recently accessed view comes first.
 -
 - This file is stored locally in .git/annex/, not in the git-annex branch.
 -
 - Copyright 2014-2023 Joey Hess <id@joeyh.name>
 -
 - Licensed under the GNU AGPL version 3 or higher.
 -}

{-# LANGUAGE OverloadedStrings #-}

module Logs.View (
	currentView,
	setView,
	removeView,
	recentViews,
	branchView,
	fromViewBranch,
	is_branchView,
	branchViewPrefix,
	prop_branchView_legal,
) where

import Annex.Common
import Types.View
import Types.MetaData
import Types.AdjustedBranch
import Annex.AdjustedBranch.Name
import qualified Git
import qualified Git.Branch
import qualified Git.Ref
import Git.Types
import Logs.File

import qualified Data.Text as T
import qualified Data.Set as S
import Data.Char
import qualified Data.ByteString as B

setView :: View -> Annex ()
setView v = do
	old <- take 99 . filter (/= v) <$> recentViews
	writeViews (v : old)

writeViews :: [View] -> Annex ()
writeViews l = do
	f <- fromRepo gitAnnexViewLog
	writeLogFile f $ unlines $ map show l

removeView :: View -> Annex ()
removeView v = writeViews =<< filter (/= v) <$> recentViews

recentViews :: Annex [View]
recentViews = do
	f <- fromRepo gitAnnexViewLog
	liftIO $ mapMaybe readish . lines <$> catchDefaultIO [] (readFileString f)

{- Gets the currently checked out view, if there is one. 
 -
 - The view may also have an adjustment applied to it.
 -}
currentView :: Annex (Maybe (View, Maybe Adjustment))
currentView = go =<< inRepo Git.Branch.current
  where
	go (Just b) = case adjustedToOriginal b of
		Nothing -> getvb b Nothing
		Just (adj, b') -> getvb b' (Just adj)
	go Nothing = return Nothing

	getvb b madj
		| branchViewPrefix `B.isPrefixOf` fromRef' b = do
			vb <- headMaybe 
				. filter (\v -> branchView v Nothing == b || branchViewOld v == b) 
				<$> recentViews
			case vb of
				Just vb' -> return (Just (vb', madj))
				Nothing -> return Nothing
		| otherwise = return Nothing

{- Note that this is not the prefix used when an adjustment is applied to a
 - view branch. -}
branchViewPrefix :: B.ByteString
branchViewPrefix = "refs/heads/views"

{- Generates a git branch name for a View, which may also have an
 - adjustment applied to it.
 - 
 - There is no guarantee that each view gets a unique branch name,
 - but the branch name is used to express the view as well as possible
 - given the constraints on git branch names. It includes the name of the
 - parent branch, and what metadata is used.
 -}
branchView :: View -> Maybe Adjustment -> Git.Branch
branchView view madj = case madj of
	Nothing -> vb
	Just adj -> adjBranch $ originalToAdjusted vb adj
  where
  	basebranch = fromRef' (Git.Ref.base (viewParentBranch view))
	vb = Git.Ref $ branchViewPrefix <> "/" <> basebranch
		<> "(" <> branchViewDesc view False <> ")"

{- Old name used for a view did not include the name of the parent branch. -}
branchViewOld :: View -> Git.Branch
branchViewOld view = Git.Ref $
	 branchViewPrefix <> "/" <> branchViewDesc view True

branchViewDesc :: View -> Bool -> B.ByteString
branchViewDesc view pareninvisibles = encodeBS $
	intercalate ";" $ map branchcomp (viewComponents view)
  where
	branchcomp c
		| viewVisible c || not pareninvisibles = branchcomp' c
		| otherwise = "(" ++ branchcomp' c ++ ")"
	branchcomp' (ViewComponent metafield viewfilter _) = concat
		[ forcelegal (T.unpack (fromMetaField metafield))
		, branchvals viewfilter
		]
	branchvals (FilterValues set) = '=' : branchset set
	branchvals (FilterGlob glob) = '=' : forcelegal glob
	branchvals (ExcludeValues set) = "!=" ++ branchset set
	branchvals (FilterValuesOrUnset set _) = '=' : branchset set
	branchvals (FilterGlobOrUnset glob _) = '=' : forcelegal glob
	branchset = intercalate ","
		. map (forcelegal . decodeBS . fromMetaValue)
		. S.toList
	forcelegal s
		| Git.Ref.legal True s = s
		| otherwise = map (\c -> if isAlphaNum c then c else '_') s

is_branchView :: Git.Branch -> Bool
is_branchView b = case adjustedToOriginal b of
	Nothing -> hasprefix b
	Just (_adj, b') -> hasprefix b'
  where
	hasprefix (Ref b') = (branchViewPrefix <> "/") `B.isPrefixOf` b'

{- Converts a view branch as generated by branchView (but not by
 - branchViewOld) back to the parent branch.
 - Has no effect on other branches. -}
fromViewBranch :: Git.Branch -> Git.Branch
fromViewBranch b = case adjustedToOriginal b of
	Nothing -> go b
	Just (_adj, b') -> go b'
  where
	go b' =
		let bs = fromRef' b'
		in if (branchViewPrefix <> "/") `B.isPrefixOf` bs
			then 
				let (branch, _desc) = separate' (== openparen) (B.drop prefixlen bs)
				in Ref branch
			else b'
	
	prefixlen = B.length branchViewPrefix + 1
	openparen = fromIntegral (ord '(')

prop_branchView_legal :: View -> Bool
prop_branchView_legal = Git.Ref.legal False 
	. fromRef . (\v -> branchView v Nothing)