8 class Model(model
.Model
):
9 def __init__(self
, init
=True):
10 model
.Model
.__init
__(self
)
12 # These methods are best left implemented in git.py
32 for attr
in git_attrs
:
33 setattr(self
, attr
, getattr(git
,attr
))
35 # chdir to the root of the git tree. This is critical
36 # to being able to properly use the git porcelain.
37 cdup
= git
.show_cdup()
38 if cdup
: os
.chdir(cdup
)
40 self
.__config
_types
= {
41 'merge.verbosity':'int',
42 'gui.diffcontext':'int',
43 'gui.pruneduringfetch':'bool',
44 'merge.summary':'bool',
51 #####################################################
52 # Used in various places
53 branch
= git
.current_branch(),
54 remotes
= git
.remote(),
59 #####################################################
60 # Used primarily by the main UI
61 window_geom
= utils
.parse_geom(git
.config('ugit.geometry')),
62 project
= os
.path
.basename(os
.getcwd()),
67 all_unstaged
= [], # unstaged+untracked
69 #####################################################
70 # Used by the create branch dialog
72 local_branches
= git
.branch(remote
=False),
73 remote_branches
= git
.branch(remote
=True),
76 #####################################################
77 # Used by the commit/repo browser
82 # These are parallel lists
87 # All items below here are re-calculated in
90 directory_entries
= {},
92 # These are also parallel lists
98 def get_config(self
,key
,local
=True):
100 config
= self
.get_local_config()
102 config
= self
.get_global_config()
108 def read_configs(self
):
109 def config_to_dict(config
):
111 for line
in config
.splitlines():
112 k
, v
= line
.split('=')
114 linetype
= self
.__config
_types
[k
]
115 if linetype
== 'int':
117 elif linetype
== 'bool':
118 v
= bool(eval(v
[0].upper()+v
[1:]))
123 local_config
= git
.git('config','--global','--list')
124 global_config
= git
.git('config','--list')
126 self
.set('local_config', config_to_dict(local_config
))
127 self
.set('global_config', config_to_dict(global_config
))
129 def init_browser_data(self
):
130 '''This scans over self.(names, sha1s, types) to generate
131 directories, directory_entries, and subtree_*'''
133 # Collect data for the model
134 if not self
.get_branch(): return
136 self
.subtree_types
= []
137 self
.subtree_sha1s
= []
138 self
.subtree_names
= []
139 self
.directories
= []
140 self
.directory_entries
= {}
142 # Lookup the tree info
143 tree_info
= git
.ls_tree(self
.get_branch())
145 self
.set_types(map( lambda(x
): x
[1], tree_info
))
146 self
.set_sha1s(map( lambda(x
): x
[2], tree_info
))
147 self
.set_names(map( lambda(x
): x
[3], tree_info
))
149 if self
.directory
: self
.directories
.append('..')
151 dir_entries
= self
.directory_entries
152 dir_regex
= re
.compile('([^/]+)/')
156 for idx
, name
in enumerate(self
.names
):
158 if not name
.startswith(self
.directory
): continue
159 name
= name
[ len(self
.directory
): ]
162 # This is a directory...
163 match
= dir_regex
.match(name
)
164 if not match
: continue
166 dirent
= match
.group(1) + '/'
167 if dirent
not in self
.directory_entries
:
168 self
.directory_entries
[dirent
] = []
170 if dirent
not in dirs_seen
:
171 dirs_seen
[dirent
] = True
172 self
.directories
.append(dirent
)
174 entry
= name
.replace(dirent
, '')
175 entry_match
= dir_regex
.match(entry
)
177 subdir
= entry_match
.group(1) + '/'
178 if subdir
in subdirs_seen
: continue
179 subdirs_seen
[subdir
] = True
180 dir_entries
[dirent
].append(subdir
)
182 dir_entries
[dirent
].append(entry
)
184 self
.subtree_types
.append(self
.types
[idx
])
185 self
.subtree_sha1s
.append(self
.sha1s
[idx
])
186 self
.subtree_names
.append(name
)
188 def get_tree_node(self
, idx
):
189 return (self
.get_types()[idx
],
190 self
.get_sha1s()[idx
],
191 self
.get_names()[idx
] )
193 def get_subtree_node(self
, idx
):
194 return (self
.get_subtree_types()[idx
],
195 self
.get_subtree_sha1s()[idx
],
196 self
.get_subtree_names()[idx
] )
198 def get_all_branches(self
):
199 return (self
.get_local_branches() + self
.get_remote_branches())
201 def set_remote(self
,remote
):
202 if not remote
: return
203 self
.set('remote',remote
)
204 branches
= utils
.grep( '%s/\S+$' % remote
, git
.branch(remote
=True))
205 self
.set_remote_branches(branches
)
207 def add_signoff(self
,*rest
):
208 '''Adds a standard Signed-off by: tag to the end
209 of the current commit message.'''
211 msg
= self
.get_commitmsg()
212 signoff
=('Signed-off by: %s <%s>' % (
213 self
.get_config('user.name'),
214 self
.get_config('user.email')))
216 if signoff
not in msg
:
217 self
.set_commitmsg(msg
+ os
.linesep
*2 + signoff
)
219 def apply_diff(self
, filename
):
220 return git
.apply(filename
)
222 def __get_squash_msg_path(self
):
223 return os
.path
.join(os
.getcwd(), '.git', 'SQUASH_MSG')
225 def has_squash_msg(self
):
226 squash_msg
= self
.__get
_squash
_msg
_path
()
227 return os
.path
.exists(squash_msg
)
229 def get_squash_msg(self
):
230 return utils
.slurp(self
.__get
_squash
_msg
_path
())
232 def set_squash_msg(self
):
233 self
.model
.set_commitmsg(self
.model
.get_squash_msg())
235 def get_prev_commitmsg(self
,*rest
):
236 '''Queries git for the latest commit message and sets it in
239 commit_lines
= git
.show('HEAD').split('\n')
240 for idx
, msg
in enumerate(commit_lines
):
243 if msg
.startswith('diff --git'):
246 commit_msg
.append(msg
)
247 self
.set_commitmsg(os
.linesep
.join(commit_msg
).rstrip())
249 def update_status(self
):
250 # This allows us to defer notification until the
251 # we finish processing data
252 notify_enabled
= self
.get_notify()
253 self
.set_notify(False)
255 # Reset the staged and unstaged model lists
256 # NOTE: the model's unstaged list is used to
257 # hold both unstaged and untracked files.
262 # Read git status items
265 untracked_items
) = git
.status()
267 # Gather items to be committed
268 for staged
in staged_items
:
269 if staged
not in self
.get_staged():
270 self
.add_staged(staged
)
272 # Gather unindexed items
273 for unstaged
in unstaged_items
:
274 if unstaged
not in self
.get_unstaged():
275 self
.add_unstaged(unstaged
)
277 # Gather untracked items
278 for untracked
in untracked_items
:
279 if untracked
not in self
.get_untracked():
280 self
.add_untracked(untracked
)
282 self
.set_branch(git
.current_branch())
283 self
.set_all_unstaged(self
.get_unstaged() + self
.get_untracked())
284 self
.set_remotes(git
.remote())
285 self
.set_remote_branches(git
.branch(remote
=True))
286 self
.set_local_branches(git
.branch(remote
=False))
287 self
.set_tags(git
.tag())
288 self
.set_revision('')
289 self
.set_local_branch('')
290 self
.set_remote_branch('')
294 # Re-enable notifications and emit changes
295 self
.set_notify(notify_enabled
)
296 self
.notify_observers(
297 'branch', 'all_unstaged', 'staged',
298 'revision', 'remote', 'remotes',
299 'local_branches','remote_branches', 'tags')
301 def delete_branch(self
, branch
):
302 return git
.branch(name
=branch
, delete
=True)
304 def get_revision_sha1(self
, idx
):
305 return self
.get_revisions()[idx
]
307 def get_commit_diff(self
, sha1
):
308 commit
= self
.show(sha1
)
309 first_newline
= commit
.index(os
.linesep
)
310 merge
= commit
[first_newline
+1:].startswith('Merge:')
312 return (commit
+ os
.linesep
*2
313 + self
.diff(commit
=sha1
, cached
=False,
314 suppress_header
=False))
318 def get_unstaged_item(self
, idx
):
319 return self
.get_all_unstaged()[idx
]
321 def get_diff_and_status(self
, idx
, staged
=True):
323 filename
= self
.get_staged()[idx
]
324 if os
.path
.exists(filename
):
325 status
= 'Staged for commit'
327 status
= 'Staged for removal'
328 diff
= self
.diff(filename
=filename
, cached
=True)
330 filename
= self
.get_all_unstaged()[idx
]
331 if os
.path
.isdir(filename
):
332 status
= 'Untracked directory'
333 diff
= os
.linesep
.join(os
.listdir(filename
))
334 elif filename
in self
.get_unstaged():
335 status
= 'Modified, not staged'
336 diff
= self
.diff(filename
=filename
, cached
=False)
338 status
= 'Untracked, not staged'
340 file_type
= utils
.run_cmd('file','-b',filename
)
341 if 'binary' in file_type
or 'data' in file_type
:
342 diff
= utils
.run_cmd('hexdump','-C',filename
)
344 if os
.path
.exists(filename
):
345 file = open(filename
, 'r')
352 def stage_changed(self
):
353 git
.add(self
.get_unstaged())
356 def stage_untracked(self
):
357 git
.add(self
.get_untracked())
360 def reset(self
, items
):
364 def unstage_all(self
):
365 git
.reset(self
.get_staged())
368 def save_window_geom(self
):
369 git
.config('ugit.geometry', utils
.get_geom())