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