Improve the "publish" merge message to give slightly more information
[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
24
25help = 'Publish the stack changes to a merge-friendly head'
26kind = 'stack'
27usage = ['[options] [branch]']
28description = """
29This command commits a set of changes on a separate (called public) branch
30based on the modifications of the given or current stack. The history of the
31public branch is not re-written, making it merge-friendly and feasible for
32publishing. The heads of the stack and public branch may be different but the
33corresponding tree objects are always the same.
34
35If the trees of the stack and public branch are different (otherwise the
36command has no effect), StGit first checks for a rebase of the stack since the
37last publishing. If a rebase is detected, StGit creates a commit on the public
38branch corresponding to a merge between the new stack base and the latest
39public head.
40
41If no rebasing was detected, StGit checks for new patches that may have been
42created on top of the stack since the last publishing. If new patches are
43found and are not empty, they are checked into the public branch keeping the
44same commit information (e.g. log message, author, committer, date).
45
46If the above tests fail (e.g. patches modified or removed), StGit creates a
47new commit on the public branch having the same tree as the stack but the
48public head as its parent. The editor will be invoked if no "--message" option
49is given.
50
51It is recommended that stack modifications falling in different categories as
52described above are separated by a publish command in order to keep the public
53branch history cleaner (otherwise StGit would generate a big commit including
54several stack modifications).
55
56The public branch name can be set via the branch.<branch>.public configuration
57variable (defaulting to "<branch>.public").
58"""
59
60args = [argparse.all_branches]
61options = [
62 opt('-b', '--branch', args = [argparse.stg_branches],
63 short = 'Use BRANCH instead of the default branch')
64] + (argparse.author_options()
65 + argparse.message_options(save_template = False)
66 + argparse.sign_options())
67
68directory = common.DirectoryHasRepositoryLib()
69
70def __create_commit(repository, tree, parents, options, message = ''):
71 """Return a new Commit object."""
72 cd = git.CommitData(
73 tree = tree, parents = parents, message = message,
74 author = git.Person.author(), committer = git.Person.committer())
75 cd = common.update_commit_data(cd, options, allow_edit = True)
76
77 return repository.commit(cd)
78
79def func(parser, options, args):
80 """Publish the stack changes."""
81 repository = directory.repository
82 stack = repository.get_stack(options.branch)
83
84 if not args:
5660771c 85 public_ref = common.get_public_ref(stack.name)
e58f264a
CM
86 elif len(args) == 1:
87 public_ref = args[0]
88 else:
89 parser.error('incorrect number of arguments')
90
91 # just clone the stack if the public ref does not exist
92 if not repository.refs.exists(public_ref):
93 repository.refs.set(public_ref, stack.head, 'publish')
94 out.info('Created "%s"' % public_ref)
95 return
96
97 public_head = repository.refs.get(public_ref)
98 public_tree = public_head.data.tree
99
100 # check for same tree (already up to date)
101 if public_tree.sha1 == stack.head.data.tree.sha1:
102 out.info('"%s" already up to date' % public_ref)
103 return
104
105 # check for rebased stack. In this case we emulate a merge with the stack
106 # base by setting two parents.
107 merge_bases = repository.get_merge_bases(public_head, stack.base)
108 if not stack.base in merge_bases:
11125113
CM
109 message = 'Merge %s into %s' % (repository.describe(stack.base),
110 public_ref)
e58f264a
CM
111 public_head = __create_commit(repository, stack.head.data.tree,
112 [public_head, stack.base], options,
113 message)
114 repository.refs.set(public_ref, public_head, 'publish')
115 out.info('Merged the stack base into "%s"' % public_ref)
116 return
117
118 # check for new patches from the last publishing. This is done by checking
119 # whether the public tree is the same as the bottom of the checked patch.
120 # If older patches were modified, new patches cannot be detected. The new
121 # patches and their metadata are pushed directly to the published head.
122 for p in stack.patchorder.applied:
123 pc = stack.patches.get(p).commit
124 if public_tree.sha1 == pc.data.parent.data.tree.sha1:
125 if pc.data.is_nochange():
126 out.info('Ignored new empty patch "%s"' % p)
127 continue
128 cd = pc.data.set_parent(public_head)
129 public_head = repository.commit(cd)
130 public_tree = public_head.data.tree
131 out.start('Published new patch "%s"' % p)
132
133 # create a new commit (only happens if no new patches are detected)
134 if public_tree.sha1 != stack.head.data.tree.sha1:
135 public_head = __create_commit(repository, stack.head.data.tree,
136 [public_head], options)
137
138 # update the public head
139 repository.refs.set(public_ref, public_head, 'publish')
140 out.info('Updated "%s"' % public_ref)