def get_tree_changes(repo): """Return add/delete/modify changes to tree by comparing index to HEAD. :param repo: repo path or object :return: dict with lists for each type of change """ r = open_repo(repo) index = r.open_index() # Compares the Index to the HEAD & determines changes # Iterate through the changes and report add/delete/modify # TODO: call out to dulwich.diff_tree somehow. tracked_changes = { 'add': [], 'delete': [], 'modify': [], } for change in index.changes_from_tree(r.object_store, r['HEAD'].tree): if not change[0][0]: tracked_changes['add'].append(change[0][1]) elif not change[0][1]: tracked_changes['delete'].append(change[0][0]) elif change[0][0] == change[0][1]: tracked_changes['modify'].append(change[0][0]) else: raise AssertionError('git mv ops not yet supported') return tracked_changes
def get_tree_changes(repo): """Return add/delete/modify changes to tree by comparing index to HEAD. :param repo: repo path or object :return: dict with lists for each type of change """ r = open_repo(repo) index = r.open_index() # Compares the Index to the HEAD & determines changes # Iterate through the changes and report add/delete/modify # TODO: call out to dulwich.diff_tree somehow. tracked_changes = { 'add': [], 'delete': [], 'modify': [], } for change in index.changes_from_tree(r.object_store, r['HEAD'].tree): if not change[0][0]: tracked_changes['add'].append(change[0][1]) elif not change[0][1]: tracked_changes['delete'].append(change[0][0]) elif change[0][0] == change[0][1]: tracked_changes['modify'].append(change[0][0]) else: raise AssertionError('git mv ops not yet supported') return tracked_changes
def checkout(self, sha=None): self._fetch() print "# Checkout repo %s @ %s => %s.."%(self.remote_uri,sha,self.path) self._init_subrepos(sha) target = self['HEAD'] if sha: target = self[sha] target_tree = target.tree index = Index(self.index_path()) for e_path,e_mode,e_sha in index.changes_from_tree(self.object_store, target_tree, want_unchanged=True): e_source = (e_path[1],e_mode[1],e_sha[1]) e_target = (e_path[0],e_mode[0],e_sha[0]) e_to_checkout = None if e_source[0] and e_target[0]: if not os.path.exists(os.path.join(self.path, e_target[0])): e_to_checkout = ("A",e_target) elif e_source[2] != e_target[2]: e_to_checkout = ("U",e_target) elif not e_target[0]: print "D %s"%(os.path.join(self.path, e_source[0])) if stat.S_ISREG(e_source[1]): os.unlink(os.path.join(self.path, e_source[0])) del index[e_source[0]] else: e_to_checkout = ("A",e_target) if e_to_checkout: print "%s %s"%(e_to_checkout[0],os.path.join(self.path,e_to_checkout[1][0])) self._checkout_entry(index, *e_to_checkout[1]) self._init_subrepos(sha) for subrepo in self.subrepo.values(): subrepo[1].checkout(subrepo[0])
def _changed_entries(self, ref=None): ref = ref or self.DEFAULT_COMMIT if not self.has_commits: return [] obj_sto = self.repo.object_store tree_id = self[ref].tree names = self.trackable_files lookup_func = partial(self.lookup_entry, trackable_files=names) # Format = [((old_name, new_name), (old_mode, new_mode), (old_sha, new_sha)), ...] tree_diff = changes_from_tree(names, lookup_func, obj_sto, tree_id, want_unchanged=False) return list(tree_diff)
def _changed_entries(self, ref=None): ref = ref or self.DEFAULT_COMMIT if not self.has_commits: return [] obj_sto = self.repo.object_store tree_id = self[ref].tree names = self.trackable_files lookup_func = partial(self.lookup_entry, trackable_files=names) # Format = [((old_name, new_name), (old_mode, new_mode), (old_sha, new_sha)), ...] tree_diff = changes_from_tree(names, lookup_func, obj_sto, tree_id, want_unchanged=False) return list(tree_diff)
def write_tree_workingdir_diff(f, store, tree, names, filter_callback=None, diff_binary=False): """Write diff of tree against current working dir Args: f: File-like object to write to. tree: tree id for base of comparison names: list of working directory relative file paths (bytes only) diff_binary: Whether to diff files even if they are considered binary files by is_binary(). """ entry_info = {} def lookup_entry(name): if name in entry_info: blob, fmode = entry_info[name] return (blob.id, fmode) return (None, None) # convert tree_paths that represent files in working dir # to an equivalent temp blob mode and store it # This should properly handle checkin normalization # which is required to make diffs work properly for name in names: filepath = name if os_sep_bytes != b'/': filepath = name.replace(b'/', os_sep_bytes) stat = os.stat(filepath) fmode = stat.st_mode blob = blob_from_path_and_stat(filepath, stat) if filter_callback: blob = filter_callback(blob, name) entry_info[name] = (blob, fmode) for change_entry in changes_from_tree(names, lookup_entry, store, tree): (name1, name2), (mode1, mode2), (sha1, sha2) = change_entry content1 = b'' content2 = b'' if name2: if name2 in entry_info: blob, fmode = entry_info[name2] content2 = blob.as_raw_string() if name1: content1 = store[sha1].as_raw_string() _write_diff(f, (name1, mode1, sha1, content1), (name2, mode2, sha2, content2))
def status(self, sha=None): print "# Status.." target = self['HEAD'] if sha: target = self[sha] target_tree = target.tree index = Index(self.index_path()) for e_path,e_mode,e_sha in index.changes_from_tree(self.object_store, target_tree, want_unchanged=True): e_source = (e_path[0],e_mode[0],e_sha[0]) e_target = (e_path[1],e_mode[1],e_sha[1]) if e_source[0] and e_target[0]: if not os.path.exists(os.path.join(self.path, e_source[0])): print "D %s"%(os.path.join(self.path, e_source[0])) elif e_source[2] != e_target[2]: print "M %s"%(os.path.join(self.path, e_source[0])) elif not e_target[0]: print "D %s"%(os.path.join(self.path, e_source[0])) else: print "A %s"%(os.path.join(self.path, e_target[0]))
def emit_repo_as_xdot(repo, options): '''Emits xdot for the given repo on stdout.''' global graph # TODO: globals are bad mmmmkay global vertices vertices = {} graph = pydot.Graph(verbose=True) graph.set_bgcolor('#00000000') # transparent background objstore = repo.object_store seen = set() # walk everything in the object store. (this means orphaned nodes will show.) for sha in objstore: if not options.blobs and objstore[sha].type_name in ('blob', 'tree'): continue walk_node(objstore, seen, sha, options) for ref in repo.refs.keys(): if ref == 'HEAD': continue # TODO: let this loop handle symbolic refs too branch_node = add_branch_node(ref) graph.add_edge( pydot.Edge(branch_node, repo.refs[ref], **edge_opts(style='dotted'))) # do HEAD as a special case ref = 'HEAD' nopts = node_opts(label=ref, shape='diamond', style='filled', fillcolor='#ff3333', fontcolor='white', tooltip='Symbolic Ref: HEAD') head_node = pydot.Node(ref, **nopts) graph.add_node(head_node) symref = repo.refs.read_ref(ref) if symref.startswith('ref: '): symref = symref[5:] points_to = add_branch_node(symref) graph.add_node(points_to) graph.add_edge( pydot.Edge(head_node, add_branch_node(symref), **edge_opts(style='dotted'))) # index if options.index: try: head_tree = repo['HEAD'].tree except KeyError: head_tree = None index = repo.open_index() try: changes = list(index.changes_from_tree(objstore, head_tree)) except TypeError: # the official dulwich repo throws a TypeError changes_from_tree is # called against an empty tree (None) if head_tree is not None: raise changes = [] if changes: index_node = pydot.Node('index', shape='invtriangle', style='filled', fillcolor='#33ff33', fontname=DEFAULT_FONTNAME, fontsize=DEFAULT_FONTSIZE) graph.add_node(index_node) for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes: graph.add_edge( pydot.Edge(index_node, vert_for_sha(objstore, newsha), label=q(' ' + newpath), fontname=DEFAULT_FONTNAME, fontsize=DEFAULT_FONTSIZE)) # invoke dot -Txdot to turn out DOT file into an xdot file, which canviz is expecting subprocess.Popen(['dot', '-Txdot'], stdin=subprocess.PIPE).communicate(graph.to_string())
def emit_repo_as_xdot(repo, options): """Emits xdot for the given repo on stdout.""" global graph # TODO: globals are bad mmmmkay global vertices vertices = {} graph = pydot.Graph(verbose=True) graph.set_bgcolor("#00000000") # transparent background objstore = repo.object_store seen = set() # walk everything in the object store. (this means orphaned nodes will show.) for sha in objstore: if not options.blobs and objstore[sha].type_name in ("blob", "tree"): continue walk_node(objstore, seen, sha, options) for ref in repo.refs.keys(): if ref == "HEAD": continue # TODO: let this loop handle symbolic refs too branch_node = add_branch_node(ref) graph.add_edge(pydot.Edge(branch_node, repo.refs[ref], **edge_opts(style="dotted"))) # do HEAD as a special case ref = "HEAD" nopts = node_opts( label=ref, shape="diamond", style="filled", fillcolor="#ff3333", fontcolor="white", tooltip="Symbolic Ref: HEAD" ) head_node = pydot.Node(ref, **nopts) graph.add_node(head_node) symref = repo.refs.read_ref(ref) if symref.startswith("ref: "): symref = symref[5:] points_to = add_branch_node(symref) graph.add_node(points_to) graph.add_edge(pydot.Edge(head_node, add_branch_node(symref), **edge_opts(style="dotted"))) # index if options.index: try: head_tree = repo["HEAD"].tree except KeyError: head_tree = None index = repo.open_index() try: changes = list(index.changes_from_tree(objstore, head_tree)) except TypeError: # the official dulwich repo throws a TypeError changes_from_tree is # called against an empty tree (None) if head_tree is not None: raise changes = [] if changes: index_node = pydot.Node( "index", shape="invtriangle", style="filled", fillcolor="#33ff33", fontname=DEFAULT_FONTNAME, fontsize=DEFAULT_FONTSIZE, ) graph.add_node(index_node) for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes: graph.add_edge( pydot.Edge( index_node, vert_for_sha(objstore, newsha), label=q(" " + newpath), fontname=DEFAULT_FONTNAME, fontsize=DEFAULT_FONTSIZE, ) ) # invoke dot -Txdot to turn out DOT file into an xdot file, which canviz is expecting subprocess.Popen(["dot", "-Txdot"], stdin=subprocess.PIPE).communicate(graph.to_string())