def manifest_diff(a, b, base_path=''): base_path = base_path.rstrip('/') start = len(base_path) + bool(base_path) for line in Git.diff_tree(a, b, base_path, recursive=True): mode_before, mode_after, sha1_before, sha1_after, status, path = line if sha1_before != sha1_after: yield path[start:], sha1_after, sha1_before
def manifest_diff(a, b, base_path=''): base_path = base_path.rstrip('/') start = len(base_path) + bool(base_path) for line in Git.diff_tree(a, b, base_path): mode_before, mode_after, sha1_before, sha1_after, status, path = line if sha1_before != sha1_after: yield path[start:], sha1_after, sha1_before
def manifest_diff(a, b, base_path=''): for line in Git.diff_tree(a, b, base_path): mode_before, mode_after, sha1_before, sha1_after, status, path = line if sha1_before != sha1_after: yield path, sha1_after, sha1_before
def create_hg_manifest(self, commit, parents): manifest = GeneratedManifestInfo(NULL_NODE_ID) changeset_files = [] if parents: parent_changeset = self.changeset(self.hg_changeset(parents[0])) parent_manifest = self.manifest(parent_changeset.manifest) parent_node = parent_manifest.node if len(parents) == 2: parent2_changeset = self.changeset(self.hg_changeset(parents[1])) parent2_manifest = self.manifest(parent2_changeset.manifest) parent2_node = parent2_manifest.node if parent_node == parent2_node: parents = parents[:1] if not parents: for line in Git.ls_tree(commit, recursive=True): mode, typ, sha1, path = line node = self.create_file(sha1, git_manifest_parents=(), path=path) manifest.append_line(ManifestLine(path, node, self.ATTR[mode]), modified=True) changeset_files.append(path) manifest.set_parents(NULL_NODE_ID) manifest.delta_node = NULL_NODE_ID return manifest, changeset_files elif len(parents) == 2: if not experiment('merge'): raise Exception('Pushing merges is not supported yet') if not self._merge_warn: logging.warning('Pushing merges is experimental.') logging.warning('This may irremediably push bad state to the ' 'mercurial server!') self._merge_warn = 1 git_manifests = (self.manifest_ref(parent_node), self.manifest_ref(parent2_node)) # TODO: this would benefit from less git queries changes = list(get_changes(commit, parents)) files = [(path, mode, sha1) for mode, _, sha1, path in Git.ls_tree(commit, recursive=True)] manifests = sorted_merge(parent_manifest._lines, parent2_manifest._lines, key=lambda i: i.name, non_key=lambda i: i) for line in sorted_merge(files, sorted_merge(changes, manifests)): path, f, (change, (manifest_line_p1, manifest_line_p2)) = line if not f: # File was removed if manifest_line_p1: manifest.removed.add(path) changeset_files.append(path) continue mode, sha1 = f attr = self.ATTR[mode] if manifest_line_p1 and not manifest_line_p2: file_parents = (manifest_line_p1.node,) elif manifest_line_p2 and not manifest_line_p1: file_parents = (manifest_line_p2.node,) elif not manifest_line_p1 and not manifest_line_p2: file_parents = () elif manifest_line_p1.node == manifest_line_p2.node: file_parents = (manifest_line_p1.node,) else: if self._merge_warn == 1: logging.warning('This may take a while...') self._merge_warn = 2 file_parents = (manifest_line_p1.node, manifest_line_p2.node) assert file_parents is not None f = self._create_file_internal( sha1, *file_parents, git_manifest_parents=git_manifests, path=path ) file_parents = tuple(p for p in (f.parent1, f.parent2) if p != NULL_NODE_ID) merged = len(file_parents) == 2 if not merged and file_parents: if self.git_file_ref(file_parents[0]) == sha1: node = file_parents[0] else: merged = True if merged: node = self._store_file_internal(f) else: node = file_parents[0] attr_change = (manifest_line_p1 and manifest_line_p1.attr != attr) manifest.append_line(ManifestLine(path, node, attr), modified=merged or attr_change) if merged or attr_change: changeset_files.append(path) if manifest.data == parent_manifest.data: return parent_manifest, [] manifest.set_parents(parent_node, parent2_node) return manifest, changeset_files def process_diff(diff): for (mode_before, mode_after, sha1_before, sha1_after, status, path) in diff: if status[0] == 'R': yield status[1:], ( '000000', sha1_before, NULL_NODE_ID, 'D') yield path, (mode_after, sha1_before, sha1_after, status) git_diff = sorted( l for l in process_diff(Git.diff_tree( parents[0], commit, detect_copy=True)) ) if not git_diff: return parent_manifest, [] parent_lines = OrderedDict((l.name, l) for l in parent_manifest._lines) for line in sorted_merge(parent_lines.iteritems(), git_diff, non_key=lambda i: i[1]): path, manifest_line, change = line if not change: manifest.append_line(manifest_line) continue mode_after, sha1_before, sha1_after, status = change path2 = status[1:] status = status[0] attr = self.ATTR.get(mode_after) if status == 'D': manifest.removed.add(path) changeset_files.append(path) continue if status in 'MT': if sha1_before == sha1_after: node = manifest_line.node else: node = self.create_file( sha1_after, str(manifest_line.node), git_manifest_parents=( self.manifest_ref(parent_node),), path=path) elif status in 'RC': if sha1_after != EMPTY_BLOB: node = self.create_copy( (path2, parent_lines[path2].node), sha1_after, git_manifest_parents=( self.manifest_ref(parent_node),), path=path) else: node = self.create_file( sha1_after, git_manifest_parents=( self.manifest_ref(parent_node),), path=path) else: assert status == 'A' node = self.create_file( sha1_after, git_manifest_parents=( self.manifest_ref(parent_node),), path=path) manifest.append_line(ManifestLine(path, node, attr), modified=True) changeset_files.append(path) manifest.set_parents(parent_node) manifest.delta_node = parent_node return manifest, changeset_files
def create_hg_metadata(self, commit, parents): if len(parents) > 1: raise Exception('Pushing merges is not supported yet') manifest = GeneratedManifestInfo(NULL_NODE_ID) # TODO: share code with GitHgStore.manifest removed = set() modified = {} copies = {} created = OrderedDict() if parents: parent_changeset_data = self.read_changeset_data(parents[0]) parent_manifest = self.manifest(parent_changeset_data['manifest']) parent_node = parent_manifest.node parent_lines = list(parent_manifest._lines) branch = parent_changeset_data.get('extra', {}).get('branch') line = None for line in Git.diff_tree(parents[0], commit, detect_copy=True, recursive=True): mode_before, mode_after, sha1_before, sha1_after, status, \ path = line status = status[0] if status == 'D': removed.add(path) elif status in 'MT': if sha1_before == sha1_after: modified[path] = (None, self.ATTR[mode_after]) else: modified[path] = (sha1_after, self.ATTR[mode_after]) elif status in 'RC': path1, path2 = path.split('\t', 1) if status == 'R': removed.add(path1) if sha1_after != EMPTY_BLOB: copies[path2] = path1 created[path2] = (sha1_after, self.ATTR[mode_after]) else: assert status == 'A' created[path] = (sha1_after, self.ATTR[mode_after]) if line is None: manifest = parent_manifest parent_lines = [] else: parent_node = NULL_NODE_ID parent_lines = [] branch = None for line in Git.ls_tree(commit, recursive=True): mode, typ, sha1, path = line created[path] = (sha1, self.ATTR[mode]) if copies: copied = {k: () for k in copies.values()} for line in parent_lines: name = str(line.name) if name in copied: copied[name] = line.node iter_created = created.iteritems() next_created = next(iter_created) modified_lines = [] for line in parent_lines: if line.name in removed and line.name not in created: continue mod = modified.get(line.name) if mod: node, attr = mod if attr is None: attr = line.attr if node is None: node = PseudoString(line.node) else: node = self.create_file(node, str(line.node)) line = ManifestLine(line.name, node, attr) modified_lines.append(line) while next_created and next_created[0] < line.name: node, attr = next_created[1] if next_created[0] in copies: copied_name = copies[next_created[0]] node = self.create_copy((copied_name, copied[copied_name]), node) else: node = self.create_file(node) created_line = ManifestLine(next_created[0], node, attr) modified_lines.append(created_line) manifest.append_line(created_line) next_created = next(iter_created) manifest.append_line(line) while next_created: node, attr = next_created[1] if next_created[0] in copies: copied_name = copies[next_created[0]] node = self.create_copy((copied_name, copied[copied_name]), node) else: node = self.create_file(node) created_line = ManifestLine(next_created[0], node, attr) modified_lines.append(created_line) manifest.append_line(created_line) next_created = next(iter_created) commit_data = GitCommit(commit) if manifest.node == NULL_NODE_ID: manifest.set_parents(parent_node) manifest.node = manifest.sha1 manifest.removed = removed manifest.modified = {l.name: (l.node, l.attr) for l in modified_lines} manifest.delta_node = parent_node self._push_manifests[manifest.node] = manifest self.manifest_ref(manifest.node, hg2git=False, create=True) self._manifest_git_tree[manifest.node] = commit_data.tree extra = {} if commit_data.author != commit_data.committer: committer = self.hg_author_info(commit_data.committer) extra['committer'] = '%s %d %d' % committer if branch: extra['branch'] = branch changeset_data = self._changeset_data_cache[commit] = { 'files': sorted(chain(removed, modified, created)), 'manifest': manifest.node, } if extra: changeset_data['extra'] = extra changeset = self._changeset(commit, include_parents=True) if self._graft is True and parents and changeset.data[-1] == '\n': parent_cs = self._changeset(parents[0], skip_patch=True) if 'patch' not in self._changeset_data_cache[parents[0]]: self._graft = False else: patch = self._changeset_data_cache[parents[0]]['patch'][-1] self._graft = (patch[1] == len(parent_cs.data) and parent_cs.data[-1] == '\n') if self._graft: self._graft = 'true' if self._graft == 'true' and changeset.data[-1] == '\n': changeset.data = changeset.data[:-1] changeset_data['patch'] = ( (len(changeset.data), len(changeset.data) + 1, ''), ) changeset_data['changeset'] = changeset.changeset = changeset.node = \ changeset.sha1 self._push_changesets[changeset.node] = changeset # This is a horrible way to do this, but this method is not doing much # better overall anyways. if extra: if 'committer' in extra: del extra['committer'] if not extra: del changeset_data['extra'] self._changesets[changeset.node] = PseudoString(commit)