/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to send.py

  • Committer: John Arbash Meinel
  • Date: 2010-01-12 22:51:31 UTC
  • mto: This revision was merged to the branch mainline in revision 4955.
  • Revision ID: john@arbash-meinel.com-20100112225131-he8h411p6aeeb947
Delay grabbing an output stream until we actually go to show a diff.

This makes the test suite happy, but it also seems to be reasonable.
If we aren't going to write anything, we don't need to hold an
output stream open.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2009 Jelmer Vernooij <jelmer@samba.org>
2
 
 
3
 
# Based on the original from bzr-svn:
4
 
# Copyright (C) 2009 Lukas Lalinsky <lalinsky@gmail.com>
5
 
# Copyright (C) 2009 Jelmer Vernooij <jelmer@samba.org>
6
 
 
7
 
# This program is free software; you can redistribute it and/or modify
8
 
# it under the terms of the GNU General Public License as published by
9
 
# the Free Software Foundation; either version 2 of the License, or
10
 
# (at your option) any later version.
11
 
 
12
 
# This program is distributed in the hope that it will be useful,
13
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 
# GNU General Public License for more details.
16
 
 
17
 
# You should have received a copy of the GNU General Public License
18
 
# along with this program; if not, write to the Free Software
19
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
 
 
21
 
"""Support in "bzr send" for git-am style patches."""
22
 
 
23
 
import time
24
 
import bzrlib
25
 
from bzrlib import (
26
 
    branch as _mod_branch,
27
 
    diff as _mod_diff,
28
 
    errors,
29
 
    osutils,
30
 
    revision as _mod_revision,
31
 
    )
32
 
 
33
 
from bzrlib.merge_directive import BaseMergeDirective
34
 
 
35
 
from bzrlib.plugins.git import (
36
 
    version_info as bzr_git_version_info,
37
 
    )
38
 
from bzrlib.plugins.git.mapping import (
39
 
    object_mode,
40
 
    )
41
 
from bzrlib.plugins.git.object_store import (
42
 
    get_object_store,
43
 
    )
44
 
 
45
 
from cStringIO import StringIO
46
 
from dulwich import (
47
 
    __version__ as dulwich_version,
48
 
    )
49
 
from dulwich.objects import (
50
 
    Blob,
51
 
    )
52
 
 
53
 
 
54
 
version_tail = "bzr %s, bzr-git %d.%d.%d, dulwich %d.%d.%d" % (
55
 
    (bzrlib.__version__, ) + bzr_git_version_info[:3] + dulwich_version[:3])
56
 
 
57
 
 
58
 
class GitDiffTree(_mod_diff.DiffTree):
59
 
    """Provides a text representation between two trees, formatted for svn."""
60
 
 
61
 
    def _show_diff(self, specific_files, extra_trees):
62
 
        from dulwich.patch import write_blob_diff
63
 
        iterator = self.new_tree.iter_changes(self.old_tree,
64
 
            specific_files=specific_files, extra_trees=extra_trees,
65
 
            require_versioned=True)
66
 
        has_changes = 0
67
 
        def get_encoded_path(path):
68
 
            if path is not None:
69
 
                return path.encode(self.path_encoding, "replace")
70
 
        def get_file_mode(tree, path, kind, executable):
71
 
            if path is None:
72
 
                return 0
73
 
            return object_mode(kind, executable)
74
 
        def get_blob(present, tree, file_id):
75
 
            if present:
76
 
                return Blob.from_string(tree.get_file(file_id).read())
77
 
            else:
78
 
                return None
79
 
        trees = (self.old_tree, self.new_tree)
80
 
        for (file_id, paths, changed_content, versioned, parent, name, kind,
81
 
             executable) in iterator:
82
 
            # The root does not get diffed, and items with no known kind (that
83
 
            # is, missing) in both trees are skipped as well.
84
 
            if parent == (None, None) or kind == (None, None):
85
 
                continue
86
 
            path_encoded = (get_encoded_path(paths[0]),
87
 
                            get_encoded_path(paths[1]))
88
 
            present = ((kind[0] not in (None, 'directory')),
89
 
                       (kind[1] not in (None, 'directory')))
90
 
            if not present[0] and not present[1]:
91
 
                continue
92
 
            contents = (get_blob(present[0], trees[0], file_id),
93
 
                        get_blob(present[1], trees[1], file_id))
94
 
            renamed = (parent[0], name[0]) != (parent[1], name[1])
95
 
            mode = (get_file_mode(trees[0], path_encoded[0],
96
 
                                  kind[0], executable[0]),
97
 
                    get_file_mode(trees[1], path_encoded[1],
98
 
                                  kind[1], executable[1]))
99
 
            write_blob_diff(self.to_file,
100
 
                (path_encoded[0], mode[0], contents[0]),
101
 
                (path_encoded[1], mode[1], contents[1]))
102
 
            has_changes |= (changed_content or renamed)
103
 
        return has_changes
104
 
 
105
 
 
106
 
class GitMergeDirective(BaseMergeDirective):
107
 
 
108
 
    multiple_output_files = True
109
 
 
110
 
    def __init__(self, revision_id, testament_sha1, time, timezone,
111
 
                 target_branch, source_branch=None, message=None,
112
 
                 patches=None):
113
 
        super(GitMergeDirective, self).__init__(revision_id=revision_id,
114
 
            testament_sha1=testament_sha1, time=time, timezone=timezone,
115
 
            target_branch=target_branch, patch=None,
116
 
            source_branch=source_branch, message=message, bundle=None)
117
 
        self.patches = patches
118
 
 
119
 
    def to_lines(self):
120
 
        return self.patch.splitlines(True)
121
 
 
122
 
    def to_files(self):
123
 
        return self.patches
124
 
 
125
 
    @classmethod
126
 
    def _generate_commit(cls, repository, revision_id, num, total):
127
 
        s = StringIO()
128
 
        store = get_object_store(repository)
129
 
        store.lock_read()
130
 
        try:
131
 
            commit = store[store._lookup_revision_sha1(revision_id)]
132
 
        finally:
133
 
            store.unlock()
134
 
        from dulwich.patch import write_commit_patch, get_summary
135
 
        try:
136
 
            lhs_parent = repository.get_revision(revision_id).parent_ids[0]
137
 
        except IndexError:
138
 
            lhs_parent = _mod_revision.NULL_REVISION
139
 
        tree_1 = repository.revision_tree(lhs_parent)
140
 
        tree_2 = repository.revision_tree(revision_id)
141
 
        contents = StringIO()
142
 
        differ = GitDiffTree.from_trees_options(tree_1, tree_2,
143
 
                contents, 'utf8', None, 'a/', 'b/', None)
144
 
        differ.show_diff(None, None)
145
 
        write_commit_patch(s, commit, contents.getvalue(), (num, total),
146
 
                           version_tail)
147
 
        summary = "%04d-%s.patch" % (num, get_summary(commit).rstrip("."))
148
 
        return summary, s.getvalue()
149
 
 
150
 
    @classmethod
151
 
    def from_objects(cls, repository, revision_id, time, timezone,
152
 
                     target_branch, local_target_branch=None,
153
 
                     public_branch=None, message=None):
154
 
        patches = []
155
 
        submit_branch = _mod_branch.Branch.open(target_branch)
156
 
        submit_branch.lock_read()
157
 
        try:
158
 
            submit_revision_id = submit_branch.last_revision()
159
 
            repository.fetch(submit_branch.repository, submit_revision_id)
160
 
            graph = repository.get_graph()
161
 
            todo = graph.find_difference(submit_revision_id, revision_id)[1]
162
 
            total = len(todo)
163
 
            for i, revid in enumerate(graph.iter_topo_order(todo)):
164
 
                patches.append(cls._generate_commit(repository, revid, i+1,
165
 
                    total))
166
 
        finally:
167
 
            submit_branch.unlock()
168
 
        return cls(revision_id, None, time, timezone,
169
 
            target_branch=target_branch, source_branch=public_branch,
170
 
            message=message, patches=patches)
171
 
 
172
 
 
173
 
def send_git(branch, revision_id, submit_branch, public_branch, no_patch,
174
 
             no_bundle, message, base_revision_id):
175
 
    if no_patch:
176
 
        raise errors.BzrCommandError("no patch not supported for git-am style patches")
177
 
    if no_bundle:
178
 
        raise errors.BzrCommandError("no bundle not supported for git-am style patches")
179
 
    return GitMergeDirective.from_objects(
180
 
        branch.repository, revision_id, time.time(),
181
 
        osutils.local_time_offset(), submit_branch,
182
 
        public_branch=public_branch, message=message)