Auto-generate man pages for all StGit commands
[stgit] / stgit / commands / pick.py
1 __copyright__ = """
2 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License version 2 as
6 published by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 """
17
18 import sys, os
19 from stgit.argparse import opt
20 from stgit.commands.common import *
21 from stgit.utils import *
22 from stgit.out import *
23 from stgit import stack, git
24 from stgit.stack import Series
25
26 help = 'Import a patch from a different branch or a commit object'
27 usage = ['[options] ([<patch1>] [<patch2>] [<patch3>..<patch4>])|<commit>']
28 description = """
29 Import one or more patches from a different branch or a commit object
30 into the current series. By default, the name of the imported patch is
31 used as the name of the current patch. It can be overridden with the
32 '--name' option. A commit object can be reverted with the '--reverse'
33 option. The log and author information are those of the commit
34 object."""
35
36 options = [
37 opt('-n', '--name',
38 short = 'Use NAME as the patch name'),
39 opt('-B', '--ref-branch',
40 short = 'Pick patches from BRANCH'),
41 opt('-r', '--reverse', action = 'store_true',
42 short = 'Reverse the commit object before importing'),
43 opt('-p', '--parent', metavar = 'COMMITID',
44 short = 'Use COMMITID as parent'),
45 opt('-x', '--expose', action = 'store_true',
46 short = 'Append the imported commit id to the patch log'),
47 opt('--fold', action = 'store_true',
48 short = 'Fold the commit object into the current patch'),
49 opt('--update', action = 'store_true',
50 short = 'Like fold but only update the current patch files'),
51 opt('--unapplied', action = 'store_true',
52 short = 'Keep the patch unapplied')]
53
54 directory = DirectoryGotoToplevel()
55
56 def __pick_commit(commit_id, patchname, options):
57 """Pick a commit id.
58 """
59 commit = git.Commit(commit_id)
60
61 if options.name:
62 patchname = options.name
63
64 if options.parent:
65 parent = git_id(crt_series, options.parent)
66 else:
67 parent = commit.get_parent()
68
69 if not options.reverse:
70 bottom = parent
71 top = commit_id
72 else:
73 bottom = commit_id
74 top = parent
75
76 if options.fold:
77 out.start('Folding commit %s' % commit_id)
78
79 # try a direct git apply first
80 if not git.apply_diff(bottom, top):
81 git.merge_recursive(bottom, git.get_head(), top)
82
83 out.done()
84 elif options.update:
85 rev1 = git_id(crt_series, 'HEAD^')
86 rev2 = git_id(crt_series, 'HEAD')
87 files = git.barefiles(rev1, rev2).split('\n')
88
89 out.start('Updating with commit %s' % commit_id)
90
91 if not git.apply_diff(bottom, top, files = files):
92 raise CmdException, 'Patch updating failed'
93
94 out.done()
95 else:
96 message = commit.get_log()
97 if options.expose:
98 message += '(imported from commit %s)\n' % commit.get_id_hash()
99 author_name, author_email, author_date = \
100 name_email_date(commit.get_author())
101
102 out.start('Importing commit %s' % commit_id)
103
104 newpatch = crt_series.new_patch(patchname, message = message, can_edit = False,
105 unapplied = True, bottom = bottom, top = top,
106 author_name = author_name,
107 author_email = author_email,
108 author_date = author_date)
109 # in case the patch name was automatically generated
110 patchname = newpatch.get_name()
111
112 # find a patchlog to fork from
113 refbranchname, refpatchname = parse_rev(patchname)
114 if refpatchname:
115 if refbranchname:
116 # assume the refseries is OK, since we already resolved
117 # commit_str to a git_id
118 refseries = Series(refbranchname)
119 else:
120 refseries = crt_series
121 patch = refseries.get_patch(refpatchname)
122 if patch.get_log():
123 out.info("Log was %s" % newpatch.get_log())
124 out.info("Setting log to %s\n" % patch.get_log())
125 newpatch.set_log(patch.get_log())
126 out.info("Log is now %s" % newpatch.get_log())
127 else:
128 out.info("No log for %s\n" % patchname)
129
130 if not options.unapplied:
131 modified = crt_series.push_patch(patchname)
132 else:
133 modified = False
134
135 if crt_series.empty_patch(patchname):
136 out.done('empty patch')
137 elif modified:
138 out.done('modified')
139 else:
140 out.done()
141
142
143 def func(parser, options, args):
144 """Import a commit object as a new patch
145 """
146 if not args:
147 parser.error('incorrect number of arguments')
148
149 if not options.unapplied:
150 check_local_changes()
151 check_conflicts()
152 check_head_top_equal(crt_series)
153
154 if options.ref_branch:
155 remote_series = Series(options.ref_branch)
156 else:
157 remote_series = crt_series
158
159 applied = remote_series.get_applied()
160 unapplied = remote_series.get_unapplied()
161 try:
162 patches = parse_patches(args, applied + unapplied, len(applied))
163 commit_id = None
164 except CmdException:
165 if len(args) > 1:
166 raise
167 # no patches found, try a commit id
168 commit_id = git_id(remote_series, args[0])
169
170 if not commit_id and len(patches) > 1:
171 if options.name:
172 raise CmdException, '--name can only be specified with one patch'
173 if options.parent:
174 raise CmdException, '--parent can only be specified with one patch'
175
176 if (options.fold or options.update) and not crt_series.get_current():
177 raise CmdException, 'No patches applied'
178
179 if commit_id:
180 __pick_commit(commit_id, None, options)
181 else:
182 if options.unapplied:
183 patches.reverse()
184 for patch in patches:
185 __pick_commit(git_id(remote_series, patch), patch, options)
186
187 print_crt_patch(crt_series)