Preserve picked patch name when possible
[stgit.git] / stgit / commands / refresh.py
blobbbfacc0c0a6c870747590c4e8c3ec59d4be0100d
1 from stgit import argparse, utils
2 from stgit.argparse import opt
3 from stgit.commands.common import (
4 CmdException,
5 DirectoryHasRepository,
6 check_head_top_equal,
7 run_commit_msg_hook,
9 from stgit.config import config
10 from stgit.lib.edit import auto_edit_patch, interactive_edit_patch
11 from stgit.lib.git import CommitData, IndexAndWorktree
12 from stgit.lib.transaction import (
13 StackTransaction,
14 TransactionException,
15 TransactionHalted,
17 from stgit.out import out
18 from stgit.run import RunException
19 from stgit.utils import get_hook
21 __copyright__ = """
22 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
23 Copyright (C) 2008, Karl Hasselström <kha@treskal.com>
25 This program is free software; you can redistribute it and/or modify
26 it under the terms of the GNU General Public License version 2 as
27 published by the Free Software Foundation.
29 This program is distributed in the hope that it will be useful,
30 but WITHOUT ANY WARRANTY; without even the implied warranty of
31 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 GNU General Public License for more details.
34 You should have received a copy of the GNU General Public License
35 along with this program; if not, see http://www.gnu.org/licenses/.
36 """
38 help = 'Generate a new commit for the current patch'
39 kind = 'patch'
40 usage = ['[options] [--] [<files or dirs>]']
41 description = """
42 Include the latest work tree and index changes in the current patch.
43 This command generates a new git commit object for the patch; the old
44 commit is no longer visible.
46 Refresh will warn if the index is dirty, and require use of either the
47 '--index' or '--force' options to override this check. This is to prevent
48 accidental full refresh when only some changes were staged using git add
49 interative mode.
51 You may optionally list one or more files or directories relative to
52 the current working directory; if you do, only matching files will be
53 updated.
55 Behind the scenes, stg refresh first creates a new temporary patch
56 with your updates, and then merges that patch into the patch you asked
57 to have refreshed. If you asked to refresh a patch other than the
58 topmost patch, there can be conflicts; in that case, the temporary
59 patch will be left for you to take care of, for example with stg
60 squash.
62 The creation of the temporary patch is recorded in a separate entry in
63 the patch stack log; this means that one undo step will undo the merge
64 between the other patch and the temp patch, and two undo steps will
65 additionally get rid of the temp patch.
67 Additionally, the '--spill' option resets the topmost patch, emptying
68 the patch while leaving the patch's changes intact in the worktree."""
70 args = ['dirty_files']
71 options = [
72 opt(
73 '-u',
74 '--update',
75 action='store_true',
76 short='Only update the current patch files',
78 opt(
79 '-i',
80 '--index',
81 action='store_true',
82 short='Refresh from index instead of worktree',
83 long="""
84 Instead of setting the patch top to the current contents of
85 the worktree, set it to the current contents of the index.""",
87 opt(
88 '-F',
89 '--force',
90 action='store_true',
91 short='Force refresh even if index is dirty',
92 long="""
93 Instead of warning the user when some work has already been staged
94 (such as with git add interactive mode) force a full refresh.""",
96 opt(
97 '-p',
98 '--patch',
99 args=['other_applied_patches', 'unapplied_patches'],
100 short='Refresh (applied) PATCH instead of the top patch',
102 opt(
103 '-e',
104 '--edit',
105 action='store_true',
106 short='Invoke an editor for the patch description',
108 opt(
109 '-d',
110 '--diff',
111 action='store_true',
112 short='Show diff when editing patch description',
114 opt(
115 '-a',
116 '--annotate',
117 metavar='NOTE',
118 short='Annotate the patch log entry',
120 opt(
121 '-s',
122 '--submodules',
123 action='store_true',
124 short='Include submodules when refreshing patch contents',
126 opt(
127 '--no-submodules',
128 action='store_false',
129 dest='submodules',
130 short='Exclude submodules when refreshing patch contents',
132 opt(
133 '--spill',
134 action='store_true',
135 dest='spill',
136 short='Spill patch content to worktree and index, erasing patch content.',
139 options.extend(argparse.message_options(save_template=False))
140 options.extend(argparse.hook_options())
141 options.extend(argparse.trailer_options())
142 options.extend(argparse.author_options())
143 options.extend(argparse.diff_opts_option())
145 directory = DirectoryHasRepository()
148 def get_patch(stack, given_patch):
149 """Get the name of the patch we are to refresh."""
150 if given_patch:
151 patch_name = given_patch
152 if patch_name not in stack.patches:
153 raise CmdException('%s: no such patch' % patch_name)
154 return patch_name
155 else:
156 if not stack.patchorder.applied:
157 raise CmdException(
158 'Cannot refresh top patch because no patches are applied'
160 return stack.patchorder.applied[-1]
163 def list_files(stack, patch_name, path_limits, update, submodules):
164 """Figure out which files to update."""
165 iw = stack.repository.default_iw
166 paths = iw.changed_files(stack.head.data.tree, path_limits or [])
167 if update:
168 # --update: Restrict update to the paths that were already part of the patch.
169 patch_paths = set()
170 commit = stack.patches[patch_name]
171 for dt in stack.repository.diff_tree_files(
172 commit.data.parent.data.tree, commit.data.tree
174 _, _, _, _, _, oldname, newname = dt
175 patch_paths.add(oldname)
176 patch_paths.add(newname)
177 paths &= patch_paths
178 else:
179 # Avoid including submodule files by default. This is to ensure that
180 # users in repositories with submodueles do not accidentally include
181 # submodule changes to patches just because they happen to have not
182 # run "git submodule update" prior to running stg refresh. We won't
183 # exclude them if we're explicitly told to include them, or if we're
184 # given explicit paths.
185 if not path_limits and not submodules:
186 paths -= stack.repository.submodules(stack.head.data.tree)
187 return paths
190 def write_tree(stack, paths, use_temp_index):
191 """Possibly update the index, and then write its tree.
193 If any path limiting is in effect, use a temp index.
195 :return: The written tree.
196 :rtype: :class:`Tree<stgit.git.Tree>`
199 if use_temp_index:
200 with stack.repository.temp_index() as index:
201 iw = IndexAndWorktree(index, stack.repository.default_worktree)
202 try:
203 index.read_tree(stack.head)
204 if paths:
205 iw.update_index(paths)
206 return index.write_tree()
207 finally:
208 stack.repository.default_iw.update_index(paths)
209 else:
210 iw = stack.repository.default_iw
211 if paths:
212 iw.update_index(paths)
213 return iw.index.write_tree()
216 def make_temp_patch(stack, patch_name, tree):
217 """Commit tree to temp patch, in a complete transaction."""
218 commit = stack.repository.commit(
219 CommitData(
220 tree=tree,
221 parents=[stack.head],
222 message='Refresh of %s' % patch_name,
225 temp_name = stack.patches.make_name('refresh-temp')
226 trans = StackTransaction(stack)
227 trans.patches[temp_name] = commit
228 trans.applied.append(temp_name)
229 return (
230 trans.execute(
231 'refresh (create temporary patch)',
232 stack.repository.default_iw,
233 print_current_patch=False,
235 temp_name,
239 def absorb_applied(trans, iw, patch_name, temp_name, edit_fun):
240 """Absorb temp patch into the given patch, which must be applied.
242 If the absorption succeeds, call ``edit_fun`` on the resulting
243 :class:`stgit.lib.git.CommitData` before committing it, then commit the return
244 value.
246 :returns: True if the temp patch is successfully absorbed or False if there remain
247 conflicts for the user to resolve
250 temp_absorbed = False
251 try:
252 # Pop any patch on top of the patch we're refreshing.
253 to_pop = trans.applied[trans.applied.index(patch_name) + 1 :]
254 if len(to_pop) > 1:
255 popped_extra = trans.pop_patches(lambda pn: pn in to_pop)
256 assert not popped_extra # no other patches were popped
257 trans.push_patch(temp_name, iw)
258 top_name = to_pop.pop()
259 assert top_name == temp_name
261 # Absorb the temp patch.
262 temp_cd = trans.patches[temp_name].data
263 assert trans.patches[patch_name] == temp_cd.parent
264 cd, new_patch_name = edit_fun(
265 trans.patches[patch_name].data.set_tree(temp_cd.tree)
267 trans.patches[patch_name] = trans.stack.repository.commit(cd)
268 if new_patch_name and patch_name != new_patch_name:
269 trans.rename_patch(patch_name, new_patch_name)
271 popped_extra = trans.delete_patches(lambda pn: pn == temp_name, quiet=True)
272 assert not popped_extra # the temp patch was topmost
273 temp_absorbed = True
275 # Push back any patch we were forced to pop earlier.
276 for pn in to_pop:
277 trans.push_patch(pn, iw)
278 except TransactionHalted:
279 pass
280 return temp_absorbed
283 def absorb_unapplied(trans, iw, patch_name, temp_name, edit_fun):
284 """Absorb the temp patch into the given patch, which must be unapplied.
286 If the absorption succeeds, call ``edit_fun`` on the resulting
287 :class:`stgit.lib.git.CommitData` before committing it, then commit the return
288 value.
290 :param iw: Not used.
291 :returns: True if the temp patch is absorbed successfully or False
292 if user resolvable conflicts remain
295 # Pop the temp patch.
296 popped_extra = trans.pop_patches(lambda pn: pn == temp_name)
297 assert not popped_extra # the temp patch was topmost
299 # Try to create the new tree of the refreshed patch. (This is the
300 # same operation as pushing the temp patch onto the patch we're
301 # trying to refresh -- but we don't have a worktree to spill
302 # conflicts to, so if the simple merge doesn't succeed, we have to
303 # give up.)
304 patch_cd = trans.patches[patch_name].data
305 temp_cd = trans.patches[temp_name].data
306 new_tree = trans.stack.repository.simple_merge(
307 base=temp_cd.parent.data.tree,
308 ours=patch_cd.tree,
309 theirs=temp_cd.tree,
311 if new_tree:
312 # It worked. Refresh the patch with the new tree, and delete
313 # the temp patch.
314 cd, new_patch_name = edit_fun(patch_cd.set_tree(new_tree))
315 trans.patches[patch_name] = trans.stack.repository.commit(cd)
316 if new_patch_name and patch_name != new_patch_name:
317 trans.rename_patch(patch_name, new_patch_name)
318 popped_extra = trans.delete_patches(lambda pn: pn == temp_name, quiet=True)
319 assert not popped_extra # the temp patch was not applied
320 return True
321 else:
322 # Nope, we couldn't create the new tree, so we'll just have to
323 # leave the temp patch for the user.
324 return False
327 def absorb(stack, patch_name, temp_name, edit_fun, annotate=None):
328 """Absorb the temp patch into the target patch."""
329 log_msg = 'refresh'
330 if annotate:
331 log_msg += '\n\n' + annotate
333 check_head_top_equal(stack)
334 trans = StackTransaction(stack)
335 iw = stack.repository.default_iw
336 if patch_name in trans.applied:
337 absorbed = absorb_applied(trans, iw, patch_name, temp_name, edit_fun)
338 else:
339 absorbed = absorb_unapplied(trans, iw, patch_name, temp_name, edit_fun)
341 r = trans.execute(log_msg, iw)
343 if not absorbed:
344 out.warn(
345 'The new changes did not apply cleanly to %s.' % patch_name,
346 'They were saved in %s.' % temp_name,
349 return r
352 def __refresh_spill(annotate):
353 stack = directory.repository.current_stack
355 # Fetch the topmost patch.
356 patchname = get_patch(stack, None)
358 cd = stack.patches[patchname].data
360 # Set the tree of the patch to the parent.
361 cd = cd.set_tree(cd.parent.data.tree)
363 log_msg = 'refresh (spill)'
364 if annotate:
365 log_msg += '\n\n' + annotate
367 check_head_top_equal(stack)
368 trans = StackTransaction(stack, allow_conflicts=True)
369 trans.patches[patchname] = stack.repository.commit(cd)
370 try:
371 # Either a complete success, or a conflict during push. But in
372 # either case, we've successfully effected the edits the user
373 # asked us for.
374 return trans.execute(log_msg)
375 except TransactionException:
376 # Transaction aborted -- we couldn't check out files due to
377 # dirty index/worktree. The edits were not carried out.
378 out.error('Unable to spill the topmost patch')
379 return utils.STGIT_COMMAND_ERROR
382 def __refresh(
383 args,
384 force=False,
385 target_patch=None,
386 message=None,
387 author=None,
388 trailers=None,
389 annotate=None,
390 use_temp_index=False,
391 refresh_from_index=False,
392 only_update_patchfiles=False,
393 include_submodules=False,
394 no_verify=False,
395 invoke_editor=False,
396 edit_diff=False,
397 diff_flags=(),
399 stack = directory.repository.current_stack
401 patch_name = get_patch(stack, target_patch)
403 if refresh_from_index:
404 paths = set()
405 else:
406 paths = list_files(
407 stack,
408 patch_name,
409 args,
410 only_update_patchfiles,
411 include_submodules,
414 # Make sure there are no conflicts in the files we want to
415 # refresh.
416 if stack.repository.default_index.conflicts() & paths:
417 raise CmdException('Cannot refresh -- resolve conflicts first')
419 # Make sure the index is clean before performing a full refresh
420 if not refresh_from_index and not force:
421 if not (
422 stack.repository.default_index.is_clean(stack.head)
423 or stack.repository.default_iw.worktree_clean()
425 raise CmdException(
426 'The index is dirty. Did you mean --index? '
427 'To force a full refresh use --force.'
430 check_head_top_equal(stack)
432 # Update index and write tree
433 tree = write_tree(stack, paths, use_temp_index=use_temp_index)
435 # Run pre-commit hook, if fails, abort refresh
436 if not no_verify:
437 pre_commit_hook = get_hook(
438 stack.repository,
439 'pre-commit',
440 extra_env={} if invoke_editor else {'GIT_EDITOR': ':'},
442 if pre_commit_hook:
443 try:
444 pre_commit_hook()
445 except RunException:
446 raise CmdException(
447 'pre-commit hook failed, review the changes using `stg diff`, '
448 'run `stg add` to add them to index and run `stg refresh` again'
450 else:
451 # Update index and rewrite tree if hook updated files in index
452 if not stack.repository.default_index.is_clean(tree):
453 tree = write_tree(stack, paths, use_temp_index=use_temp_index)
455 # Commit tree to temp patch, and absorb it into the target patch.
456 retval, temp_name = make_temp_patch(stack, patch_name, tree)
458 if retval != utils.STGIT_SUCCESS:
459 return retval
461 def edit_fun(cd):
462 orig_msg = cd.message
463 new_msg = None
464 if message is not None:
465 new_msg = message.encode(config.get('i18n.commitencoding'))
466 cd = auto_edit_patch(
467 stack.repository,
469 msg=new_msg,
470 author=author,
471 trailers=trailers,
473 new_patch_name = None
474 if invoke_editor:
475 cd, new_patch_name, failed_diff = interactive_edit_patch(
476 stack.repository, cd, patch_name, edit_diff, diff_flags
478 assert not failed_diff
479 if not no_verify and (invoke_editor or cd.message != orig_msg):
480 cd = run_commit_msg_hook(stack.repository, cd, invoke_editor)
481 # Refresh the committer information
482 return cd.set_committer(None), new_patch_name
484 return absorb(stack, patch_name, temp_name, edit_fun, annotate=annotate)
487 def func(parser, options, args):
488 """Generate a new commit for the current or given patch."""
490 if options.spill:
491 if len(args) > 0:
492 # TODO: would be nice if path limiting could be used with spill.
493 raise CmdException('Cannot use path limiting with --spill')
494 for opt_name, opt_value in [
495 ('--index', options.index),
496 ('--edit', options.edit),
497 ('--update', options.update),
498 ('--patch', options.patch),
499 ('--force', options.force),
500 ('--no-verify', options.no_verify),
501 ('--sign', options.trailers),
502 ('--ack', options.trailers),
503 ('--review', options.trailers),
505 if opt_value:
506 raise CmdException('Cannot combine --spill with %s' % opt_name)
507 return __refresh_spill(annotate=options.annotate)
508 else:
509 # Catch illegal argument combinations.
510 is_path_limiting = bool(args or options.update)
511 if options.index and is_path_limiting:
512 raise CmdException('Only full refresh is available with the --index option')
514 if options.index and options.force:
515 raise CmdException(
516 'You cannot --force a full refresh when using --index mode'
519 if options.update and options.submodules:
520 raise CmdException(
521 '--submodules is meaningless when only updating modified files'
524 if options.index and options.submodules:
525 raise CmdException(
526 '--submodules is meaningless when keeping the current index'
529 # If submodules was not specified on the command line, infer a default
530 # from configuration.
531 if options.submodules is None:
532 options.submodules = config.getbool('stgit.refreshsubmodules')
534 return __refresh(
535 args,
536 force=options.force,
537 target_patch=options.patch,
538 message=options.message,
539 author=options.author,
540 trailers=options.trailers,
541 annotate=options.annotate,
542 use_temp_index=is_path_limiting,
543 refresh_from_index=options.index,
544 only_update_patchfiles=options.update,
545 include_submodules=options.submodules,
546 no_verify=options.no_verify,
547 invoke_editor=options.edit,
548 edit_diff=options.diff,
549 diff_flags=options.diff_flags,