1 """A Python class hierarchy wrapping the StGit on-disk metadata."""
4 from stgit
import exception
, utils
5 from stgit
.lib
import git
, stackupgrade
8 """Represents an StGit patch. This class is mainly concerned with
9 reading and writing the on-disk representation of a patch."""
10 def __init__(self
, stack
, name
):
13 name
= property(lambda self
: self
.__name
)
16 return 'refs/patches/%s/%s' %
(self
.__stack
.name
, self
.__name
)
19 return self
.__ref
+ '.log'
22 return self
.__stack
.repository
.refs
.get(self
.__ref
)
24 def __compat_dir(self
):
25 return os
.path
.join(self
.__stack
.directory
, 'patches', self
.__name
)
26 def __write_compat_files(self
, new_commit
, msg
):
27 """Write files used by the old infrastructure."""
28 def write(name
, val
, multiline
= False):
29 fn
= os
.path
.join(self
.__compat_dir
, name
)
31 utils
.write_string(fn
, val
, multiline
)
32 elif os
.path
.isfile(fn
):
36 old_log
= [self
.__stack
.repository
.refs
.get(self
.__log_ref
)]
39 cd
= git
.CommitData(tree
= new_commit
.data
.tree
, parents
= old_log
,
40 message
= '%s\t%s' %
(msg
, new_commit
.sha1
))
41 c
= self
.__stack
.repository
.commit(cd
)
42 self
.__stack
.repository
.refs
.set(self
.__log_ref
, c
, msg
)
45 write('authname', d
.author
.name
)
46 write('authemail', d
.author
.email
)
47 write('authdate', d
.author
.date
)
48 write('commname', d
.committer
.name
)
49 write('commemail', d
.committer
.email
)
50 write('description', d
.message
)
51 write('log', write_patchlog().sha1
)
52 write('top', new_commit
.sha1
)
53 write('bottom', d
.parent
.sha1
)
55 old_top_sha1
= self
.commit
.sha1
56 old_bottom_sha1
= self
.commit
.data
.parent
.sha1
59 old_bottom_sha1
= None
60 write('top.old', old_top_sha1
)
61 write('bottom.old', old_bottom_sha1
)
62 def __delete_compat_files(self
):
63 if os
.path
.isdir(self
.__compat_dir
):
64 for f
in os
.listdir(self
.__compat_dir
):
65 os
.remove(os
.path
.join(self
.__compat_dir
, f
))
66 os
.rmdir(self
.__compat_dir
)
67 self
.__stack
.repository
.refs
.delete(self
.__log_ref
)
68 def set_commit(self
, commit
, msg
):
69 self
.__write_compat_files(commit
, msg
)
70 self
.__stack
.repository
.refs
.set(self
.__ref
, commit
, msg
)
72 self
.__delete_compat_files()
73 self
.__stack
.repository
.refs
.delete(self
.__ref
)
75 return self
.name
in self
.__stack
.patchorder
.applied
77 return self
.commit
.data
.is_nochange()
79 class PatchOrder(object):
80 """Keeps track of patch order, and which patches are applied.
81 Works with patch names, not actual patches."""
82 def __init__(self
, stack
):
85 def __read_file(self
, fn
):
86 return tuple(utils
.read_strings(
87 os
.path
.join(self
.__stack
.directory
, fn
)))
88 def __write_file(self
, fn
, val
):
89 utils
.write_strings(os
.path
.join(self
.__stack
.directory
, fn
), val
)
90 def __get_list(self
, name
):
91 if not name
in self
.__lists
:
92 self
.__lists
[name
] = self
.__read_file(name
)
93 return self
.__lists
[name
]
94 def __set_list(self
, name
, val
):
96 if val
!= self
.__lists
.get(name
, None):
97 self
.__lists
[name
] = val
98 self
.__write_file(name
, val
)
99 applied
= property(lambda self
: self
.__get_list('applied'),
100 lambda self
, val
: self
.__set_list('applied', val
))
101 unapplied
= property(lambda self
: self
.__get_list('unapplied'),
102 lambda self
, val
: self
.__set_list('unapplied', val
))
103 hidden
= property(lambda self
: self
.__get_list('hidden'),
104 lambda self
, val
: self
.__set_list('hidden', val
))
105 all
= property(lambda self
: self
.applied
+ self
.unapplied
+ self
.hidden
)
106 all_visible
= property(lambda self
: self
.applied
+ self
.unapplied
)
108 class Patches(object):
109 """Creates L{Patch} objects. Makes sure there is only one such object
111 def __init__(self
, stack
):
113 def create_patch(name
):
114 p
= Patch(self
.__stack
, name
)
115 p
.commit
# raise exception if the patch doesn't exist
117 self
.__patches
= git
.ObjectCache(create_patch
) # name -> Patch
118 def exists(self
, name
):
125 return self
.__patches
[name
]
126 def new(self
, name
, commit
, msg
):
127 assert not name
in self
.__patches
128 p
= Patch(self
.__stack
, name
)
129 p
.set_commit(commit
, msg
)
130 self
.__patches
[name
] = p
133 class Stack(git
.Branch
):
134 """Represents an StGit stack (that is, a git branch with some extra
136 __repo_subdir
= 'patches'
138 def __init__(self
, repository
, name
):
139 git
.Branch
.__init__(self
, repository
, name
)
140 self
.__patchorder
= PatchOrder(self
)
141 self
.__patches
= Patches(self
)
142 if not stackupgrade
.update_to_current_format_version(repository
, name
):
143 raise exception
.StgException('%s: branch not initialized' % name
)
144 patchorder
= property(lambda self
: self
.__patchorder
)
145 patches
= property(lambda self
: self
.__patches
)
148 return os
.path
.join(self
.repository
.directory
, self
.__repo_subdir
, self
.name
)
151 if self
.patchorder
.applied
:
152 return self
.patches
.get(self
.patchorder
.applied
[0]
156 def head_top_equal(self
):
157 if not self
.patchorder
.applied
:
159 return self
.head
== self
.patches
.get(self
.patchorder
.applied
[-1]).commit
161 class Repository(git
.Repository
):
162 """A git L{Repository<git.Repository>} with some added StGit-specific
164 def __init__(self
, *args
, **kwargs
):
165 git
.Repository
.__init__(self
, *args
, **kwargs
)
166 self
.__stacks
= {} # name -> Stack
168 def current_stack(self
):
169 return self
.get_stack()
170 def get_stack(self
, name
= None):
172 name
= self
.current_branch_name
173 if not name
in self
.__stacks
:
174 self
.__stacks
[name
] = Stack(self
, name
)
175 return self
.__stacks
[name
]