Commit | Line | Data |
---|---|---|
06848fab CM |
1 | __copyright__ = """ |
2 | Copyright (C) 2006, 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 optparse import OptionParser, make_option | |
20 | ||
21 | import stgit.commands.common | |
22 | from stgit.commands.common import * | |
23 | from stgit.utils import * | |
24 | from stgit import stack, git | |
25 | ||
26 | ||
27 | help = 'synchronise patches with a branch or a series' | |
28 | usage = """%prog [options] [<patch1>] [<patch2>] [<patch3>..<patch4>] | |
29 | ||
30 | For each of the specified patches perform a three-way merge with the | |
31 | same patch in the specified branch or series. The command can be used | |
32 | for keeping patches on several branches in sync. Note that the | |
33 | operation may fail for some patches because of conflicts. The patches | |
34 | in the series must apply cleanly. | |
35 | ||
36 | The sync operation can be reverted for individual patches with --undo.""" | |
37 | ||
38 | options = [make_option('-a', '--all', | |
39 | help = 'synchronise all the patches', | |
40 | action = 'store_true'), | |
41 | make_option('-b', '--branch', | |
42 | help = 'syncronise patches with BRANCH'), | |
43 | make_option('-s', '--series', | |
44 | help = 'syncronise patches with SERIES'), | |
45 | make_option('--undo', | |
46 | help = 'undo the synchronisation of the current patch', | |
47 | action = 'store_true')] | |
48 | ||
49 | def __check_all(): | |
50 | check_local_changes() | |
51 | check_conflicts() | |
52 | check_head_top_equal() | |
53 | ||
54 | def __branch_merge_patch(remote_series, pname): | |
55 | """Merge a patch from a remote branch into the current tree. | |
56 | """ | |
57 | patch = remote_series.get_patch(pname) | |
58 | git.merge(patch.get_bottom(), git.get_head(), patch.get_top()) | |
59 | ||
60 | def __series_merge_patch(base, patchdir, pname): | |
61 | """Merge a patch file with the given StGIT patch. | |
62 | """ | |
63 | patchfile = os.path.join(patchdir, pname) | |
64 | git.apply_patch(filename = patchfile, base = base) | |
65 | ||
66 | def func(parser, options, args): | |
67 | """Synchronise a range of patches | |
68 | """ | |
69 | global crt_series | |
70 | ||
71 | if options.undo: | |
72 | if options.branch or options.series: | |
73 | raise CmdException, \ | |
74 | '--undo cannot be specified with --branch or --series' | |
75 | __check_all() | |
76 | ||
77 | print 'Undoing the "%s" sync...' % crt_series.get_current(), | |
78 | sys.stdout.flush() | |
79 | ||
80 | crt_series.undo_refresh() | |
81 | git.reset() | |
82 | ||
83 | print 'done' | |
84 | return | |
85 | ||
86 | if options.branch: | |
87 | # the main function already made crt_series to be the remote | |
88 | # branch | |
89 | remote_series = crt_series | |
90 | stgit.commands.common.crt_series = crt_series = stack.Series() | |
91 | if options.branch == crt_series.get_branch(): | |
92 | raise CmdException, 'Cannot synchronise with the current branch' | |
93 | remote_patches = remote_series.get_applied() | |
94 | ||
95 | # the merge function merge_patch(patch, pname) | |
96 | merge_patch = lambda patch, pname: \ | |
97 | __branch_merge_patch(remote_series, pname) | |
98 | elif options.series: | |
99 | patchdir = os.path.dirname(options.series) | |
100 | ||
101 | remote_patches = [] | |
102 | f = file(options.series) | |
103 | for line in f: | |
104 | p = re.sub('#.*$', '', line).strip() | |
105 | if not p: | |
106 | continue | |
107 | remote_patches.append(p) | |
108 | f.close() | |
109 | ||
110 | # the merge function merge_patch(patch, pname) | |
111 | merge_patch = lambda patch, pname: \ | |
112 | __series_merge_patch(patch.get_bottom(), patchdir, pname) | |
113 | else: | |
114 | raise CmdException, 'No remote branch or series specified' | |
115 | ||
116 | applied = crt_series.get_applied() | |
117 | ||
118 | if options.all: | |
119 | patches = applied | |
120 | elif len(args) != 0: | |
121 | patches = parse_patches(args, applied, ordered = True) | |
00d468f5 CM |
122 | elif applied: |
123 | patches = [crt_series.get_current()] | |
06848fab | 124 | else: |
00d468f5 | 125 | parser.error('no patches applied') |
06848fab CM |
126 | |
127 | if not patches: | |
128 | raise CmdException, 'No patches to synchronise' | |
129 | ||
130 | __check_all() | |
131 | ||
132 | # only keep the patches to be synchronised | |
133 | sync_patches = [p for p in patches if p in remote_patches] | |
134 | if not sync_patches: | |
135 | raise CmdException, 'No common patches to be synchronised' | |
136 | ||
137 | # pop to the one before the first patch to be synchronised | |
138 | popped = applied[applied.index(sync_patches[0]) + 1:] | |
139 | if popped: | |
140 | pop_patches(popped[::-1]) | |
141 | ||
142 | for p in sync_patches: | |
143 | if p in popped: | |
144 | # push to this patch | |
145 | idx = popped.index(p) + 1 | |
146 | push_patches(popped[:idx]) | |
147 | del popped[:idx] | |
148 | ||
149 | # the actual sync | |
150 | print 'Synchronising "%s"...' % p, | |
151 | sys.stdout.flush() | |
152 | ||
153 | patch = crt_series.get_patch(p) | |
154 | bottom = patch.get_bottom() | |
155 | top = patch.get_top() | |
156 | ||
157 | # reset the patch backup information. That's needed in case we | |
158 | # undo the sync but there were no changes made | |
159 | patch.set_bottom(bottom, backup = True) | |
160 | patch.set_top(top, backup = True) | |
161 | ||
162 | # the actual merging (either from a branch or an external file) | |
163 | merge_patch(patch, p) | |
164 | ||
165 | if git.local_changes(verbose = False): | |
166 | # index (cache) already updated by the git merge. The | |
167 | # backup information was already reset above | |
168 | crt_series.refresh_patch(cache_update = False, backup = False, | |
169 | log = 'sync') | |
170 | print 'done (updated)' | |
171 | else: | |
172 | print 'done' | |
173 | ||
174 | # push the remaining patches | |
175 | if popped: | |
176 | push_patches(popped) |