1 # -*- coding: utf-8 -*-
4 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
5 Copyright (C) 2008, Karl Hasselström <kha@treskal.com>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License version 2 as
9 published by the Free Software Foundation.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 from stgit.argparse import opt
22 from stgit.commands import common
23 from stgit.lib import git, transaction
24 from stgit.out import out
25 from stgit import utils
27 help = 'Generate a new commit for the current patch'
29 usage = ['[options] [<files or dirs>]']
31 Include the latest work tree and index changes in the current patch.
32 This command generates a new git commit object for the patch; the old
33 commit is no longer visible.
35 You may optionally list one or more files or directories relative to
36 the current working directory; if you do, only matching files will be
39 Behind the scenes, stg refresh first creates a new temporary patch
40 with your updates, and then merges that patch into the patch you asked
41 to have refreshed. If you asked to refresh a patch other than the
42 topmost patch, there can be conflicts; in that case, the temporary
43 patch will be left for you to take care of, for example with stg
46 The creation of the temporary patch is recorded in a separate entry in
47 the patch stack log; this means that one undo step will undo the merge
48 between the other patch and the temp patch, and two undo steps will
49 additionally get rid of the temp patch."""
52 opt('-u', '--update', action = 'store_true',
53 short = 'Only update the current patch files'),
54 opt('-i', '--index', action = 'store_true',
55 short = 'Refresh from index instead of worktree', long = """
56 Instead of setting the patch top to the current contents of
57 the worktree, set it to the current contents of the index."""),
59 short = 'Refresh (applied) PATCH instead of the top patch')]
61 directory = common.DirectoryHasRepositoryLib()
63 def get_patch(stack, given_patch):
64 """Get the name of the patch we are to refresh."""
66 patch_name = given_patch
67 if not stack.patches.exists(patch_name):
68 raise common.CmdException('%s: no such patch' % patch_name)
71 if not stack.patchorder.applied:
72 raise common.CmdException(
73 'Cannot refresh top patch, because no patches are applied')
74 return stack.patchorder.applied[-1]
76 def list_files(stack, patch_name, args, index, update):
77 """Figure out which files to update."""
79 # --index: Don't update the index.
81 paths = stack.repository.default_iw.changed_files(
82 stack.head.data.tree, args or [])
84 # --update: Restrict update to the paths that were already
86 paths &= stack.patches.get(patch_name).files()
89 def write_tree(stack, paths, temp_index):
90 """Possibly update the index, and then write its tree.
91 @return: The written tree.
92 @rtype: L{Tree<stgit.git.Tree>}"""
95 iw = git.IndexAndWorktree(index, stack.repository.default_worktree)
96 iw.update_index(paths)
97 return index.write_tree()
99 index = stack.repository.temp_index()
101 index.read_tree(stack.head)
105 stack.repository.default_iw.update_index(paths)
107 return go(stack.repository.default_index)
109 def make_temp_patch(stack, patch_name, paths, temp_index):
110 """Commit index to temp patch, in a complete transaction. If any path
111 limiting is in effect, use a temp index."""
112 tree = write_tree(stack, paths, temp_index)
113 commit = stack.repository.commit(git.CommitData(
114 tree = tree, parents = [stack.head],
115 message = 'Refresh of %s' % patch_name))
116 temp_name = utils.make_patch_name('refresh-temp', stack.patches.exists)
117 trans = transaction.StackTransaction(stack,
118 'refresh (create temporary patch)')
119 trans.patches[temp_name] = commit
120 trans.applied.append(temp_name)
121 return trans.run(stack.repository.default_iw,
122 print_current_patch = False), temp_name
124 def absorb_applied(trans, iw, patch_name, temp_name):
125 """Absorb the temp patch (C{temp_name}) into the given patch
126 (C{patch_name}), which must be applied.
128 @return: C{True} if we managed to absorb the temp patch, C{False}
129 if we had to leave it for the user to deal with."""
130 temp_absorbed = False
132 # Pop any patch on top of the patch we're refreshing.
133 to_pop = trans.applied[trans.applied.index(patch_name)+1:]
135 popped = trans.pop_patches(lambda pn: pn in to_pop)
136 assert not popped # no other patches were popped
137 trans.push_patch(temp_name, iw)
138 assert to_pop.pop() == temp_name
140 # Absorb the temp patch.
141 temp_cd = trans.patches[temp_name].data
142 assert trans.patches[patch_name] == temp_cd.parent
143 trans.patches[patch_name] = trans.stack.repository.commit(
144 trans.patches[patch_name].data.set_tree(temp_cd.tree))
145 popped = trans.delete_patches(lambda pn: pn == temp_name, quiet = True)
146 assert not popped # the temp patch was topmost
149 # Push back any patch we were forced to pop earlier.
151 trans.push_patch(pn, iw)
152 except transaction.TransactionHalted:
156 def absorb_unapplied(trans, iw, patch_name, temp_name):
157 """Absorb the temp patch (C{temp_name}) into the given patch
158 (C{patch_name}), which must be unapplied.
161 @return: C{True} if we managed to absorb the temp patch, C{False}
162 if we had to leave it for the user to deal with."""
164 # Pop the temp patch.
165 popped = trans.pop_patches(lambda pn: pn == temp_name)
166 assert not popped # the temp patch was topmost
168 # Try to create the new tree of the refreshed patch. (This is the
169 # same operation as pushing the temp patch onto the patch we're
170 # trying to refresh -- but we don't have a worktree to spill
171 # conflicts to, so if the simple merge doesn't succeed, we have to
173 patch_cd = trans.patches[patch_name].data
174 temp_cd = trans.patches[temp_name].data
175 new_tree = trans.stack.repository.simple_merge(
176 base = temp_cd.parent.data.tree,
177 ours = patch_cd.tree, theirs = temp_cd.tree)
179 # It worked. Refresh the patch with the new tree, and delete
181 trans.patches[patch_name] = trans.stack.repository.commit(
182 patch_cd.set_tree(new_tree))
183 popped = trans.delete_patches(lambda pn: pn == temp_name, quiet = True)
184 assert not popped # the temp patch was not applied
187 # Nope, we couldn't create the new tree, so we'll just have to
188 # leave the temp patch for the user.
191 def absorb(stack, patch_name, temp_name):
192 """Absorb the temp patch into the target patch."""
193 trans = transaction.StackTransaction(stack, 'refresh')
194 iw = stack.repository.default_iw
195 f = { True: absorb_applied, False: absorb_unapplied
196 }[patch_name in trans.applied]
197 if f(trans, iw, patch_name, temp_name):
201 out.warn('The new changes did not apply cleanly to %s.'
202 % patch_name, 'They were saved in %s.' % temp_name)
207 def func(parser, options, args):
208 """Generate a new commit for the current or given patch."""
210 # Catch illegal argument combinations.
211 path_limiting = bool(args or options.update)
212 if options.index and path_limiting:
213 raise common.CmdException(
214 'Only full refresh is available with the --index option')
216 stack = directory.repository.current_stack
217 patch_name = get_patch(stack, options.patch)
218 paths = list_files(stack, patch_name, args, options.index, options.update)
220 # Make sure there are no conflicts in the files we want to
222 if stack.repository.default_index.conflicts() & paths:
223 raise common.CmdException(
224 'Cannot refresh -- resolve conflicts first')
226 # Commit index to temp patch, and absorb it into the target patch.
227 retval, temp_name = make_temp_patch(
228 stack, patch_name, paths, temp_index = path_limiting)
229 if retval != utils.STGIT_SUCCESS:
231 return absorb(stack, patch_name, temp_name)