A few more i18n fixes
[ugit.git] / ugitlibs / models.py
blob463ae44d0c01923677f0ea193001882f76f2fff7
1 import os
2 import re
3 import commands
4 import cmds
5 import utils
6 from model import Model
8 class GitModel(Model):
9 def __init__(self):
11 # chdir to the root of the git tree. This is critical
12 # to being able to properly use the git porcelain.
13 cdup = cmds.git_show_cdup()
14 if cdup: os.chdir(cdup)
16 Model.__init__(self, {
17 #####################################################
18 # Used in various places
19 'remotes': cmds.git_remote(),
20 'remote': '',
21 'local_branch': '',
22 'remote_branch': '',
24 #####################################################
25 # Used primarily by the main UI
26 'project': os.path.basename(os.getcwd()),
27 'name': cmds.git_config('user.name'),
28 'email': cmds.git_config('user.email'),
29 'commitmsg': '',
30 'staged': [],
31 'unstaged': [],
32 'untracked': [],
33 'all_unstaged': [], # unstaged+untracked
35 #####################################################
36 # Used by the create branch dialog
37 'revision': '',
38 'local_branches': cmds.git_branch(remote=False),
39 'remote_branches': cmds.git_branch(remote=True),
40 'tags': cmds.git_tag(),
42 #####################################################
43 # Used by the repo browser
44 'directory': '',
46 # These are parallel lists
47 'types': [],
48 'sha1s': [],
49 'names': [],
51 # All items below here are re-calculated in
52 # init_browser_data()
53 'directories': [],
54 'directory_entries': {},
56 # These are also parallel lists
57 'subtree_types': [],
58 'subtree_sha1s': [],
59 'subtree_names': [],
63 def all_branches(self):
64 return (self.get_local_branches()
65 + self.get_remote_branches())
67 def init_branch_data(self):
68 remotes = cmds.git_remote()
69 remote_branches = cmds.git_branch(remote=True)
70 local_branches = cmds.git_branch(remote=False)
71 tags = cmds.git_tag()
73 self.set_remotes(remotes)
74 self.set_remote_branches(remote_branches)
75 self.set_local_branches(local_branches)
76 self.set_tags(tags)
77 self.set_revision('')
78 self.set_local_branch('')
79 self.set_remote_branch('')
81 def set_remote(self,remote):
82 if not remote: return
83 self.set('remote',remote)
84 branches = utils.grep('%s/\S+$' % remote,
85 cmds.git_branch(remote=True))
86 self.set_remote_branches(branches)
88 def init_browser_data(self):
89 '''This scans over self.(names, sha1s, types) to generate
90 directories, directory_entries, and subtree_*'''
92 # Collect data for the model
93 if not self.get_branch(): return
95 self.subtree_types = []
96 self.subtree_sha1s = []
97 self.subtree_names = []
98 self.directories = []
99 self.directory_entries = {}
101 # Lookup the tree info
102 tree_info = cmds.git_ls_tree(self.get_branch())
104 self.set_types(map( lambda(x): x[1], tree_info ))
105 self.set_sha1s(map( lambda(x): x[2], tree_info ))
106 self.set_names(map( lambda(x): x[3], tree_info ))
108 if self.directory: self.directories.append('..')
110 dir_entries = self.directory_entries
111 dir_regex = re.compile('([^/]+)/')
112 dirs_seen = {}
113 subdirs_seen = {}
115 for idx, name in enumerate(self.names):
117 if not name.startswith(self.directory): continue
118 name = name[ len(self.directory): ]
120 if name.count('/'):
121 # This is a directory...
122 match = dir_regex.match(name)
123 if not match: continue
125 dirent = match.group(1) + '/'
126 if dirent not in self.directory_entries:
127 self.directory_entries[dirent] = []
129 if dirent not in dirs_seen:
130 dirs_seen[dirent] = True
131 self.directories.append(dirent)
133 entry = name.replace(dirent, '')
134 entry_match = dir_regex.match(entry)
135 if entry_match:
136 subdir = entry_match.group(1) + '/'
137 if subdir in subdirs_seen: continue
138 subdirs_seen[subdir] = True
139 dir_entries[dirent].append(subdir)
140 else:
141 dir_entries[dirent].append(entry)
142 else:
143 self.subtree_types.append(self.types[idx])
144 self.subtree_sha1s.append(self.sha1s[idx])
145 self.subtree_names.append(name)
147 def get_tree_node(self, idx):
148 return (self.get_types()[idx],
149 self.get_sha1s()[idx],
150 self.get_names()[idx] )
152 def get_subtree_node(self, idx):
153 return (self.get_subtree_types()[idx],
154 self.get_subtree_sha1s()[idx],
155 self.get_subtree_names()[idx] )
157 def add_signoff(self,*rest):
158 '''Adds a standard Signed-off by: tag to the end
159 of the current commit message.'''
161 msg = self.get_commitmsg()
162 signoff =('Signed-off by: %s <%s>'
163 %(self.get_name(), self.get_email()))
165 if signoff not in msg:
166 self.set_commitmsg(msg + '\n\n' + signoff)
168 def apply_diff(self, filename):
169 return cmds.git_apply(filename)
171 def get_uncommitted_item(self, row):
172 return(self.get_unstaged() + self.get_untracked())[row]
174 def __get_squash_msg_path(self):
175 return os.path.join(os.getcwd(), '.git', 'SQUASH_MSG')
177 def has_squash_msg(self):
178 squash_msg = self.__get_squash_msg_path()
179 return os.path.exists(squash_msg)
181 def get_squash_msg(self):
182 return utils.slurp(self.__get_squash_msg_path())
184 def get_prev_commitmsg(self,*rest):
185 '''Queries git for the latest commit message and sets it in
186 self.commitmsg.'''
187 commit_msg = []
188 commit_lines = cmds.git_show('HEAD').split('\n')
189 for idx, msg in enumerate(commit_lines):
190 if idx < 4: continue
191 msg = msg.lstrip()
192 if msg.startswith('diff --git'):
193 commit_msg.pop()
194 break
195 commit_msg.append(msg)
196 self.set_commitmsg('\n'.join(commit_msg).rstrip())
198 def update_status(self):
199 # This allows us to defer notification until the
200 # we finish processing data
201 notify_enabled = self.get_notify()
202 self.set_notify(False)
204 # Reset the staged and unstaged model lists
205 # NOTE: the model's unstaged list is used to
206 # hold both unstaged and untracked files.
207 self.staged = []
208 self.unstaged = []
209 self.untracked = []
211 # Read git status items
212 ( staged_items,
213 unstaged_items,
214 untracked_items ) = cmds.git_status()
216 # Gather items to be committed
217 for staged in staged_items:
218 if staged not in self.get_staged():
219 self.add_staged(staged)
221 # Gather unindexed items
222 for unstaged in unstaged_items:
223 if unstaged not in self.get_unstaged():
224 self.add_unstaged(unstaged)
226 # Gather untracked items
227 for untracked in untracked_items:
228 if untracked not in self.get_untracked():
229 self.add_untracked(untracked)
231 # Provide a convenient representation of the unstaged list
232 self.set_all_unstaged(self.get_unstaged() + self.get_untracked())
234 # Re-enable notifications and emit changes
235 self.set_notify(notify_enabled)
236 self.notify_observers('all_unstaged', 'staged')