Rename out.start() to out.inf() in the publish command
[stgit] / stgit / commands / publish.py
CommitLineData
e58f264a
CM
1__copyright__ = """
2Copyright (C) 2009, Catalin Marinas <catalin.marinas@gmail.com>
3
4This program is free software; you can redistribute it and/or modify
5it under the terms of the GNU General Public License version 2 as
6published by the Free Software Foundation.
7
8This program is distributed in the hope that it will be useful,
9but WITHOUT ANY WARRANTY; without even the implied warranty of
10MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11GNU General Public License for more details.
12
13You should have received a copy of the GNU General Public License
14along with this program; if not, write to the Free Software
15Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16"""
17
18from stgit import argparse
19from stgit.argparse import opt
20from stgit.commands import common
21from stgit.config import config
22from stgit.lib import git, stack
23from stgit.out import out
df6e6d0c 24from stgit import utils
e58f264a
CM
25
26help = 'Publish the stack changes to a merge-friendly head'
27kind = 'stack'
28usage = ['[options] [branch]']
29description = """
30This command commits a set of changes on a separate (called public) branch
31based on the modifications of the given or current stack. The history of the
32public branch is not re-written, making it merge-friendly and feasible for
33publishing. The heads of the stack and public branch may be different but the
34corresponding tree objects are always the same.
35
36If the trees of the stack and public branch are different (otherwise the
37command has no effect), StGit first checks for a rebase of the stack since the
38last publishing. If a rebase is detected, StGit creates a commit on the public
39branch corresponding to a merge between the new stack base and the latest
40public head.
41
42If no rebasing was detected, StGit checks for new patches that may have been
43created on top of the stack since the last publishing. If new patches are
44found and are not empty, they are checked into the public branch keeping the
45same commit information (e.g. log message, author, committer, date).
46
47If the above tests fail (e.g. patches modified or removed), StGit creates a
48new commit on the public branch having the same tree as the stack but the
49public head as its parent. The editor will be invoked if no "--message" option
50is given.
51
52It is recommended that stack modifications falling in different categories as
53described above are separated by a publish command in order to keep the public
54branch history cleaner (otherwise StGit would generate a big commit including
55several stack modifications).
56
57The public branch name can be set via the branch.<branch>.public configuration
58variable (defaulting to "<branch>.public").
59"""
60
61args = [argparse.all_branches]
62options = [
63 opt('-b', '--branch', args = [argparse.stg_branches],
64 short = 'Use BRANCH instead of the default branch')
65] + (argparse.author_options()
66 + argparse.message_options(save_template = False)
67 + argparse.sign_options())
68
69directory = common.DirectoryHasRepositoryLib()
70
71def __create_commit(repository, tree, parents, options, message = ''):
72 """Return a new Commit object."""
73 cd = git.CommitData(
74 tree = tree, parents = parents, message = message,
75 author = git.Person.author(), committer = git.Person.committer())
76 cd = common.update_commit_data(cd, options, allow_edit = True)
77
78 return repository.commit(cd)
79
80def func(parser, options, args):
81 """Publish the stack changes."""
82 repository = directory.repository
83 stack = repository.get_stack(options.branch)
84
85 if not args:
5660771c 86 public_ref = common.get_public_ref(stack.name)
e58f264a
CM
87 elif len(args) == 1:
88 public_ref = args[0]
89 else:
90 parser.error('incorrect number of arguments')
91
df6e6d0c
CM
92 if not public_ref.startswith('refs/heads/'):
93 public_ref = 'refs/heads/' + public_ref
94
e58f264a
CM
95 # just clone the stack if the public ref does not exist
96 if not repository.refs.exists(public_ref):
97 repository.refs.set(public_ref, stack.head, 'publish')
98 out.info('Created "%s"' % public_ref)
99 return
100
101 public_head = repository.refs.get(public_ref)
102 public_tree = public_head.data.tree
103
104 # check for same tree (already up to date)
105 if public_tree.sha1 == stack.head.data.tree.sha1:
106 out.info('"%s" already up to date' % public_ref)
107 return
108
109 # check for rebased stack. In this case we emulate a merge with the stack
110 # base by setting two parents.
111 merge_bases = repository.get_merge_bases(public_head, stack.base)
112 if not stack.base in merge_bases:
11125113 113 message = 'Merge %s into %s' % (repository.describe(stack.base),
df6e6d0c
CM
114 utils.strip_prefix('refs/heads/',
115 public_ref))
e58f264a
CM
116 public_head = __create_commit(repository, stack.head.data.tree,
117 [public_head, stack.base], options,
118 message)
119 repository.refs.set(public_ref, public_head, 'publish')
120 out.info('Merged the stack base into "%s"' % public_ref)
121 return
122
123 # check for new patches from the last publishing. This is done by checking
124 # whether the public tree is the same as the bottom of the checked patch.
125 # If older patches were modified, new patches cannot be detected. The new
126 # patches and their metadata are pushed directly to the published head.
127 for p in stack.patchorder.applied:
128 pc = stack.patches.get(p).commit
129 if public_tree.sha1 == pc.data.parent.data.tree.sha1:
130 if pc.data.is_nochange():
131 out.info('Ignored new empty patch "%s"' % p)
132 continue
133 cd = pc.data.set_parent(public_head)
134 public_head = repository.commit(cd)
135 public_tree = public_head.data.tree
eb0c552f 136 out.info('Published new patch "%s"' % p)
e58f264a
CM
137
138 # create a new commit (only happens if no new patches are detected)
139 if public_tree.sha1 != stack.head.data.tree.sha1:
140 public_head = __create_commit(repository, stack.head.data.tree,
141 [public_head], options)
142
143 # update the public head
144 repository.refs.set(public_ref, public_head, 'publish')
145 out.info('Updated "%s"' % public_ref)