[Pkg-bazaar-commits] ./bzr/unstable r850: - Merge merge updates from aaron

Martin Pool mbp at sourcefrog.net
Fri Apr 10 08:21:14 UTC 2009


------------------------------------------------------------
revno: 850
committer: Martin Pool <mbp at sourcefrog.net>
timestamp: Thu 2005-07-07 18:02:16 +1000
message:
  - Merge merge updates from aaron
  
    aaron.bentley at utoronto.ca-20050706170931-9d2551019af3578d
modified:
  TODO
  bzrlib/changeset.py
  bzrlib/merge.py
  bzrlib/merge_core.py
-------------- next part --------------
=== modified file 'TODO'
--- a/TODO	2005-07-04 12:22:34 +0000
+++ b/TODO	2005-07-07 08:02:16 +0000
@@ -17,6 +17,10 @@
 Small things
 ------------
 
+* Merge should ignore the destination's working directory, otherwise
+  we get an error about the statcache when pulling from a remote
+  branch.
+
 * Add of a file that was present in the base revision should put back
   the previous file-id.
 

=== modified file 'bzrlib/changeset.py'
--- a/bzrlib/changeset.py	2005-06-06 04:17:53 +0000
+++ b/bzrlib/changeset.py	2005-07-07 08:02:16 +0000
@@ -24,7 +24,10 @@
 
 NULL_ID = "!NULL"
 
-
+class OldFailedTreeOp(Exception):
+    def __init__(self):
+        Exception.__init__(self, "bzr-tree-change contains files from a"
+                           " previous failed merge operation.")
 def invert_dict(dict):
     newdict = {}
     for (key,value) in dict.iteritems():
@@ -826,10 +829,10 @@
     my_sort(target_entries, shortest_to_longest)
     return (source_entries, target_entries)
 
-def rename_to_temp_delete(source_entries, inventory, dir, conflict_handler,
-                          reverse):
+def rename_to_temp_delete(source_entries, inventory, dir, temp_dir, 
+                          conflict_handler, reverse):
     """Delete and rename entries as appropriate.  Entries are renamed to temp
-    names.  A map of id -> temp name is returned.
+    names.  A map of id -> temp name (or None, for deletions) is returned.
 
     :param source_entries: The entries to rename and delete
     :type source_entries: List of `ChangesetEntry`
@@ -842,16 +845,16 @@
     :return: a mapping of id to temporary name
     :rtype: Dictionary
     """
-    temp_dir = os.path.join(dir, "temp")
     temp_name = {}
     for i in range(len(source_entries)):
         entry = source_entries[i]
         if entry.is_deletion(reverse):
             path = os.path.join(dir, inventory[entry.id])
             entry.apply(path, conflict_handler, reverse)
+            temp_name[entry.id] = None
 
         else:
-            to_name = temp_dir+"/"+str(i)
+            to_name = os.path.join(temp_dir, str(i))
             src_path = inventory.get(entry.id)
             if src_path is not None:
                 src_path = os.path.join(dir, src_path)
@@ -867,12 +870,12 @@
     return temp_name
 
 
-def rename_to_new_create(temp_name, target_entries, inventory, changeset, dir,
-                         conflict_handler, reverse):
+def rename_to_new_create(changed_inventory, target_entries, inventory, 
+                         changeset, dir, conflict_handler, reverse):
     """Rename entries with temp names to their final names, create new files.
 
-    :param temp_name: A mapping of id to temporary name
-    :type temp_name: Dictionary
+    :param changed_inventory: A mapping of id to temporary name
+    :type changed_inventory: Dictionary
     :param target_entries: The entries to apply changes to
     :type target_entries: List of `ChangesetEntry`
     :param changeset: The changeset to apply
@@ -883,22 +886,24 @@
     :type reverse: bool
     """
     for entry in target_entries:
-        new_path = entry.get_new_path(inventory, changeset, reverse)
-        if new_path is None:
+        new_tree_path = entry.get_new_path(inventory, changeset, reverse)
+        if new_tree_path is None:
             continue
-        new_path = os.path.join(dir, new_path)
-        old_path = temp_name.get(entry.id)
+        new_path = os.path.join(dir, new_tree_path)
+        old_path = changed_inventory.get(entry.id)
         if os.path.exists(new_path):
             if conflict_handler.target_exists(entry, new_path, old_path) == \
                 "skip":
                 continue
         if entry.is_creation(reverse):
             entry.apply(new_path, conflict_handler, reverse)
+            changed_inventory[entry.id] = new_tree_path
         else:
             if old_path is None:
                 continue
             try:
                 os.rename(old_path, new_path)
+                changed_inventory[entry.id] = new_tree_path
             except OSError, e:
                 raise Exception ("%s is missing" % new_path)
 
@@ -1006,6 +1011,19 @@
         Exception.__init__(self, msg)
         self.filename = filename
 
+class NewContentsConflict(Exception):
+    def __init__(self, filename):
+        msg = "Conflicting contents for new file %s" % (filename)
+        Exception.__init__(self, msg)
+
+
+class MissingForMerge(Exception):
+    def __init__(self, filename):
+        msg = "The file %s was modified, but does not exist in this tree"\
+            % (filename)
+        Exception.__init__(self, msg)
+
+
 class ExceptionConflictHandler(object):
     def __init__(self, dir):
         self.dir = dir
@@ -1066,6 +1084,12 @@
     def missing_for_rename(self, filename):
         raise MissingForRename(filename)
 
+    def missing_for_merge(self, file_id, inventory):
+        raise MissingForMerge(inventory.other.get_path(file_id))
+
+    def new_contents_conflict(self, filename, other_contents):
+        raise NewContentsConflict(filename)
+
     def finalize():
         pass
 
@@ -1086,8 +1110,19 @@
     """
     if conflict_handler is None:
         conflict_handler = ExceptionConflictHandler(dir)
-    temp_dir = dir+"/temp"
-    os.mkdir(temp_dir)
+    temp_dir = os.path.join(dir, "bzr-tree-change")
+    try:
+        os.mkdir(temp_dir)
+    except OSError, e:
+        if e.errno == errno.EEXIST:
+            try:
+                os.rmdir(temp_dir)
+            except OSError, e:
+                if e.errno == errno.ENOTEMPTY:
+                    raise OldFailedTreeOp()
+            os.mkdir(temp_dir)
+        else:
+            raise
     
     #apply changes that don't affect filenames
     for entry in changeset.entries.itervalues():
@@ -1102,21 +1137,14 @@
     (source_entries, target_entries) = get_rename_entries(changeset, inventory,
                                                           reverse)
 
-    temp_name = rename_to_temp_delete(source_entries, inventory, dir,
-                                      conflict_handler, reverse)
+    changed_inventory = rename_to_temp_delete(source_entries, inventory, dir,
+                                              temp_dir, conflict_handler,
+                                              reverse)
 
-    rename_to_new_create(temp_name, target_entries, inventory, changeset, dir,
-                         conflict_handler, reverse)
+    rename_to_new_create(changed_inventory, target_entries, inventory,
+                         changeset, dir, conflict_handler, reverse)
     os.rmdir(temp_dir)
-    r_inventory = invert_dict(inventory)
-    new_entries, removed_entries = get_inventory_change(inventory,
-    r_inventory, changeset, reverse)
-    new_inventory = {}
-    for path, file_id in new_entries.iteritems():
-        new_inventory[file_id] = path
-    for file_id in removed_entries:
-        new_inventory[file_id] = None
-    return new_inventory
+    return changed_inventory
 
 
 def apply_changeset_tree(cset, tree, reverse=False):
@@ -1133,17 +1161,13 @@
 def get_inventory_change(inventory, new_inventory, cset, reverse=False):
     new_entries = {}
     remove_entries = []
-    r_inventory = invert_dict(inventory)
-    r_new_inventory = invert_dict(new_inventory)
     for entry in cset.entries.itervalues():
         if entry.needs_rename():
-            old_path = r_inventory.get(entry.id)
-            if old_path is not None:
-                remove_entries.append(old_path)
+            new_path = entry.get_new_path(inventory, cset)
+            if new_path is None:
+                remove_entries.append(entry.id)
             else:
-                new_path = entry.get_new_path(inventory, cset)
-                if new_path is not None:
-                    new_entries[new_path] = entry.id
+                new_entries[new_path] = entry.id
     return new_entries, remove_entries
 
 
@@ -1517,15 +1541,23 @@
         return self.inventory.get(id)
 
     def get_name(self, id):
-        return os.path.basename(self.get_path(id))
+        path = self.get_path(id)
+        if path is None:
+            return None
+        else:
+            return os.path.basename(path)
 
     def get_dir(self, id):
         path = self.get_path(id)
         if path == "":
             return None
+        if path is None:
+            return None
         return os.path.dirname(path)
 
     def get_parent(self, id):
+        if self.get_path(id) is None:
+            return None
         directory = self.get_dir(id)
         if directory == '.':
             directory = './.'

=== modified file 'bzrlib/merge.py'
--- a/bzrlib/merge.py	2005-06-06 11:53:29 +0000
+++ b/bzrlib/merge.py	2005-07-07 08:02:16 +0000
@@ -80,6 +80,12 @@
         moved_path = self.add_suffix(target, ".moved")
         self.conflict("Moved existing %s to %s" % (target, moved_path))
 
+    def rmdir_non_empty(self, filename):
+        """Handle the case where the dir to be removed still has contents"""
+        self.conflict("Directory %s not removed because it is not empty"\
+            % filename)
+        return "skip"
+
     def finalize(self):
         if not self.ignore_zero:
             print "%d conflicts encountered.\n" % self.conflicts
@@ -121,8 +127,6 @@
 def inventory_map(tree):
     inventory = {}
     for file_id in tree.inventory:
-        if not file_exists(tree, file_id):
-            continue
         path = abspath(tree, file_id)
         inventory[path] = SourceFile(path, file_id)
     return inventory
@@ -142,6 +146,8 @@
         self.cached = {}
 
     def readonly_path(self, id):
+        if id not in self.tree:
+            return None
         if self.root is not None:
             return self.tree.abspath(self.tree.id2path(id))
         else:

=== modified file 'bzrlib/merge_core.py'
--- a/bzrlib/merge_core.py	2005-06-06 04:17:53 +0000
+++ b/bzrlib/merge_core.py	2005-07-07 08:02:16 +0000
@@ -48,55 +48,22 @@
     for entry in cset.entries.itervalues():
         if entry.is_boring():
             new_cset.add_entry(entry)
-        elif entry.is_creation(False):
-            if inventory.this.get_path(entry.id) is None:
-                new_cset.add_entry(entry)
-            else:
-                this_contents = get_this_contents(entry.id)
-                other_contents = entry.contents_change.new_contents
-                if other_contents == this_contents:
-                    boring_entry = changeset.ChangesetEntry(entry.id, 
-                                                            entry.new_parent, 
-                                                            entry.new_path)
-                    new_cset.add_entry(boring_entry)
-                else:
-                    conflict_handler.contents_conflict(this_contents, 
-                                                       other_contents)
-
-        elif entry.is_deletion(False):
-            if inventory.this.get_path(entry.id) is None:
-                boring_entry = changeset.ChangesetEntry(entry.id, entry.parent, 
-                                                        entry.path)
-                new_cset.add_entry(boring_entry)
-            elif entry.contents_change is not None:
-                this_contents = get_this_contents(entry.id) 
-                base_contents = entry.contents_change.old_contents
-                if base_contents == this_contents:
-                    new_cset.add_entry(entry)
-                else:
-                    entry_path = inventory.this.get_path(entry.id)
-                    conflict_handler.rem_contents_conflict(entry_path,
-                                                           this_contents, 
-                                                           base_contents)
-
-            else:
-                new_cset.add_entry(entry)
         else:
-            entry = get_merge_entry(entry, inventory, base, other, 
-                                    conflict_handler)
-            if entry is not None:
-                new_cset.add_entry(entry)
+            new_entry = make_merged_entry(entry, inventory, conflict_handler)
+            new_contents = make_merged_contents(entry, this, base, other,
+                                                conflict_handler)
+            new_entry.contents_change = new_contents
+            new_entry.metadata_change = make_merged_metadata(entry, base, other)
+            new_cset.add_entry(new_entry)
+
     return new_cset
 
-
-def get_merge_entry(entry, inventory, base, other, conflict_handler):
+def make_merged_entry(entry, inventory, conflict_handler):
     this_name = inventory.this.get_name(entry.id)
     this_parent = inventory.this.get_parent(entry.id)
     this_dir = inventory.this.get_dir(entry.id)
     if this_dir is None:
         this_dir = ""
-    if this_name is None:
-        return conflict_handler.merge_missing(entry.id, inventory)
 
     base_name = inventory.base.get_name(entry.id)
     base_parent = inventory.base.get_parent(entry.id)
@@ -133,17 +100,65 @@
             old_dir = this_dir
             new_parent = other_parent
             new_dir = other_dir
-    old_path = os.path.join(old_dir, old_name)
+    if old_name is not None and old_parent is not None:
+        old_path = os.path.join(old_dir, old_name)
+    else:
+        old_path = None
     new_entry = changeset.ChangesetEntry(entry.id, old_parent, old_name)
-    if new_name is not None or new_parent is not None:
+    if new_name is not None and new_parent is not None:
         new_entry.new_path = os.path.join(new_dir, new_name)
     else:
         new_entry.new_path = None
     new_entry.new_parent = new_parent
-
-    base_path = base.readonly_path(entry.id)
-    other_path = other.readonly_path(entry.id)
+    return new_entry
+
+
+def make_merged_contents(entry, this, base, other, conflict_handler):
+    contents = entry.contents_change
+    if contents is None:
+        return None
+    this_path = this.readonly_path(entry.id)
+    def make_diff3():
+        if this_path is None:
+            return conflict_handler.missing_for_merge(entry.id, inventory)
+        base_path = base.readonly_path(entry.id)
+        other_path = other.readonly_path(entry.id)    
+        return changeset.Diff3Merge(base_path, other_path)
+
+    if isinstance(contents, changeset.PatchApply):
+        return make_diff3()
+    if isinstance(contents, changeset.ReplaceContents):
+        if contents.old_contents is None and contents.new_contents is None:
+            return None
+        if contents.new_contents is None:
+            if this_path is not None and os.path.exists(this_path):
+                return contents
+            else:
+                return None
+        elif contents.old_contents is None:
+            if this_path is None or not os.path.exists(this_path):
+                return contents
+            else:
+                this_contents = file(this_path, "rb").read()
+                if this_contents == contents.new_contents:
+                    return None
+                else:
+                    other_path = other.readonly_path(entry.id)    
+                    conflict_handler.new_contents_conflict(this_path, 
+                                                           other_path)
+        elif isinstance(contents.old_contents, changeset.FileCreate) and \
+            isinstance(contents.new_contents, changeset.FileCreate):
+            return make_diff3()
+        else:
+            raise Exception("Unhandled merge scenario")
+
+def make_merged_metadata(entry, base, other):
+    if entry.metadata_change is not None:
+        base_path = base.readonly_path(entry.id)
+        other_path = other.readonly_path(entry.id)    
+        return PermissionsMerge(base_path, other_path)
     
+def get_merge_entry(entry, inventory, base, other, conflict_handler):
     if entry.contents_change is not None:
         new_entry.contents_change = changeset.Diff3Merge(base_path, other_path)
     if entry.metadata_change is not None:
@@ -210,6 +225,9 @@
     def full_path(self, id):
         return self.abs_path(self.inventory[id])
 
+    def readonly_path(self, id):
+        return self.full_path(id)
+
     def change_path(self, id, path):
         new = os.path.join(self.dir, self.inventory[id])
         os.rename(self.abs_path(self.inventory[id]), self.abs_path(path))
@@ -329,14 +347,60 @@
                                           Inventory(self.base.inventory), 
                                           Inventory(self.other.inventory))
         conflict_handler = changeset.ExceptionConflictHandler(self.this.dir)
-        return make_merge_changeset(self.cset, all_inventory, self.this.dir,
-                                    self.base.dir, self.other.dir, 
-                                    conflict_handler)
+        return make_merge_changeset(self.cset, all_inventory, self.this,
+                                    self.base, self.other, conflict_handler)
+
+    def apply_inv_change(self, inventory_change, orig_inventory):
+        orig_inventory_by_path = {}
+        for file_id, path in orig_inventory.iteritems():
+            orig_inventory_by_path[path] = file_id
+
+        def parent_id(file_id):
+            try:
+                parent_dir = os.path.dirname(orig_inventory[file_id])
+            except:
+                print file_id
+                raise
+            if parent_dir == "":
+                return None
+            return orig_inventory_by_path[parent_dir]
+        
+        def new_path(file_id):
+            if inventory_change.has_key(file_id):
+                return inventory_change[file_id]
+            else:
+                parent = parent_id(file_id)
+                if parent is None:
+                    return orig_inventory[file_id]
+                dirname = new_path(parent)
+                return os.path.join(dirname, orig_inventory[file_id])
+
+        new_inventory = {}
+        for file_id in orig_inventory.iterkeys():
+            path = new_path(file_id)
+            if path is None:
+                continue
+            new_inventory[file_id] = path
+
+        for file_id, path in inventory_change.iteritems():
+            if orig_inventory.has_key(file_id):
+                continue
+            new_inventory[file_id] = path
+        return new_inventory
+
+        
+
     def apply_changeset(self, cset, conflict_handler=None, reverse=False):
-        self.this.inventory = \
-            changeset.apply_changeset(cset, self.this.inventory,
-                                      self.this.dir, conflict_handler,
-                                      reverse)
+        inventory_change = changeset.apply_changeset(cset,
+                                                     self.this.inventory,
+                                                     self.this.dir,
+                                                     conflict_handler, reverse)
+        self.this.inventory =  self.apply_inv_change(inventory_change, 
+                                                     self.this.inventory)
+
+                    
+        
+
         
     def cleanup(self):
         shutil.rmtree(self.dir)



More information about the Pkg-bazaar-commits mailing list