13
13
# You should have received a copy of the GNU General Public License
14
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
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
"""Display what revisions are missing in 'other' from 'this' and vice versa."""
19
from bzrlib.log import (
21
repository as _mod_repository,
24
import bzrlib.revision as _mod_revision
24
27
def iter_log_revisions(revisions, revision_source, verbose):
25
last_tree = revision_source.revision_tree(None)
28
last_tree = revision_source.revision_tree(_mod_revision.NULL_REVISION)
27
for revno, rev_id in revisions:
31
# We need the following for backward compatibilty (hopefully
32
# this will be deprecated soon :-/) -- vila 080911
37
revno, rev_id, merge_depth = rev
28
38
rev = revision_source.get_revision(rev_id)
30
remote_tree = revision_source.revision_tree(rev_id)
31
parent_rev_id = rev.parent_ids[0]
32
if last_rev_id == parent_rev_id:
33
parent_tree = last_tree
35
parent_tree = revision_source.revision_tree(parent_rev_id)
36
revision_tree = revision_source.revision_tree(rev_id)
38
last_tree = revision_tree
39
delta = revision_tree.changes_from(parent_tree)
40
delta = revision_source.get_revision_delta(rev_id)
42
yield LogRevision(rev, revno, delta=delta)
45
def find_unmerged(local_branch, remote_branch, restrict='all'):
43
yield log.LogRevision(rev, revno, merge_depth, delta=delta)
46
def find_unmerged(local_branch, remote_branch, restrict='all',
47
include_merges=False, backward=False,
48
local_revid_range=None, remote_revid_range=None):
46
49
"""Find revisions from each side that have not been merged.
48
51
:param local_branch: Compare the history of local_branch
52
55
unique revisions from both sides. If 'local', we will return None
53
56
for the remote revisions, similarly if 'remote' we will return None for
54
57
the local revisions.
58
:param include_merges: Show mainline revisions only if False,
59
all revisions otherwise.
60
:param backward: Show oldest versions first when True, newest versions
62
:param local_revid_range: Revision-id range for filtering local_branch
63
revisions (lower bound, upper bound)
64
:param remote_revid_range: Revision-id range for filtering remote_branch
65
revisions (lower bound, upper bound)
56
67
:return: A list of [(revno, revision_id)] for the mainline revisions on
61
72
remote_branch.lock_read()
63
return _find_unmerged(local_branch,
64
remote_branch, restrict=restrict)
74
return _find_unmerged(
75
local_branch, remote_branch, restrict=restrict,
76
include_merges=include_merges, backward=backward,
77
local_revid_range=local_revid_range,
78
remote_revid_range=remote_revid_range)
66
80
remote_branch.unlock()
68
82
local_branch.unlock()
71
def _enumerate_mainline(ancestry, graph, tip_revno, tip):
85
def _enumerate_mainline(ancestry, graph, tip_revno, tip, backward=True):
72
86
"""Enumerate the mainline revisions for these revisions.
74
88
:param ancestry: A set of revisions that we care about
75
89
:param graph: A Graph which lets us find the parents for a revision
76
90
:param tip_revno: The revision number for the tip revision
77
91
:param tip: The tip of mainline
92
:param backward: Show oldest versions first when True, newest versions
78
94
:return: [(revno, revision_id)] for all revisions in ancestry that
79
95
are left-hand parents from tip, or None if ancestry is None.
95
111
parents = parent_map.get(cur)
97
113
break # Ghost, we are done
98
mainline.append((cur_revno, cur))
114
mainline.append((str(cur_revno), cur))
105
def _find_unmerged(local_branch, remote_branch, restrict):
122
def _enumerate_with_merges(branch, ancestry, graph, tip_revno, tip,
124
"""Enumerate the revisions for the ancestry.
126
:param branch: The branch we care about
127
:param ancestry: A set of revisions that we care about
128
:param graph: A Graph which lets us find the parents for a revision
129
:param tip_revno: The revision number for the tip revision
130
:param tip: The tip of the ancsetry
131
:param backward: Show oldest versions first when True, newest versions
133
:return: [(revno, revision_id)] for all revisions in ancestry that
134
are parents from tip, or None if ancestry is None.
138
if not ancestry: #Empty ancestry, no need to do any work
141
mainline_revs, rev_nos, start_rev_id, end_rev_id = log._get_mainline_revs(
142
branch, None, tip_revno)
143
if not mainline_revs:
146
# This asks for all mainline revisions, which is size-of-history and
147
# should be addressed (but currently the only way to get correct
150
# mainline_revisions always includes an extra revision at the
151
# beginning, so don't request it.
152
parent_map = dict(((key, value) for key, value
153
in graph.iter_ancestry(mainline_revs[1:])
154
if value is not None))
155
# filter out ghosts; merge_sort errors on ghosts.
156
# XXX: is this needed here ? -- vila080910
157
rev_graph = _mod_repository._strip_NULL_ghosts(parent_map)
158
# XXX: what if rev_graph is empty now ? -- vila080910
159
merge_sorted_revisions = tsort.merge_sort(rev_graph, tip,
162
# Now that we got the correct revnos, keep only the relevant
164
merge_sorted_revisions = [
165
(s, revid, n, d, e) for s, revid, n, d, e in merge_sorted_revisions
166
if revid in ancestry]
168
merge_sorted_revisions = log.reverse_by_depth(merge_sorted_revisions)
170
for seq, rev_id, merge_depth, revno, end_of_merge in merge_sorted_revisions:
171
revline.append(('.'.join(map(str, revno)), rev_id, merge_depth))
175
def _filter_revs(graph, revs, revid_range):
176
if revid_range is None or revs is None:
178
return [rev for rev in revs
179
if graph.is_between(rev[1], revid_range[0], revid_range[1])]
182
def _find_unmerged(local_branch, remote_branch, restrict,
183
include_merges, backward,
184
local_revid_range=None, remote_revid_range=None):
106
185
"""See find_unmerged.
108
187
The branches should already be locked before entering.
112
191
if local_revno == remote_revno and local_revision_id == remote_revision_id:
113
192
# A simple shortcut when the tips are at the same point
115
graph = local_branch.repository.get_graph(
116
remote_branch.repository)
194
graph = local_branch.repository.get_graph(remote_branch.repository)
117
195
if restrict == 'remote':
118
196
local_extra = None
119
remote_extra = graph.find_unique_ancestors(
120
remote_revision_id, [local_revision_id])
197
remote_extra = graph.find_unique_ancestors(remote_revision_id,
121
199
elif restrict == 'local':
122
200
remote_extra = None
123
local_extra = graph.find_unique_ancestors(
124
local_revision_id, [remote_revision_id])
201
local_extra = graph.find_unique_ancestors(local_revision_id,
202
[remote_revision_id])
126
204
if restrict != 'all':
127
205
raise ValueError('param restrict not one of "all", "local",'
128
206
' "remote": %r' % (restrict,))
129
local_extra, remote_extra = graph.find_difference(
130
local_revision_id, remote_revision_id)
131
# Now that we have unique ancestors, compute just the mainline, and
132
# generate revnos for them.
133
local_mainline = _enumerate_mainline(local_extra, graph, local_revno,
135
remote_mainline = _enumerate_mainline(remote_extra, graph, remote_revno,
137
return local_mainline, remote_mainline
207
local_extra, remote_extra = graph.find_difference(local_revision_id,
210
locals = _enumerate_with_merges(local_branch, local_extra,
212
local_revision_id, backward)
213
remotes = _enumerate_with_merges(remote_branch, remote_extra,
215
remote_revision_id, backward)
217
# Now that we have unique ancestors, compute just the mainline, and
218
# generate revnos for them.
219
locals = _enumerate_mainline(local_extra, graph, local_revno,
220
local_revision_id, backward)
221
remotes = _enumerate_mainline(remote_extra, graph, remote_revno,
222
remote_revision_id, backward)
223
return _filter_revs(graph, locals, local_revid_range), _filter_revs(graph,
224
remotes, remote_revid_range)
140
227
def sorted_revisions(revisions, history_map):