diff --git a/src/applications/phragment/controller/PhragmentBrowseController.php b/src/applications/phragment/controller/PhragmentBrowseController.php
index adb4c0d79..852f1e864 100644
--- a/src/applications/phragment/controller/PhragmentBrowseController.php
+++ b/src/applications/phragment/controller/PhragmentBrowseController.php
@@ -1,85 +1,89 @@
 <?php
 
 final class PhragmentBrowseController extends PhragmentController {
 
   private $dblob;
 
   public function willProcessRequest(array $data) {
     $this->dblob = idx($data, "dblob", "");
   }
 
   public function processRequest() {
     $request = $this->getRequest();
     $viewer = $request->getUser();
 
     $parents = $this->loadParentFragments($this->dblob);
     if ($parents === null) {
       return new Aphront404Response();
     }
     $current = nonempty(last($parents), null);
 
     $path = '';
     if ($current !== null) {
       $path = $current->getPath();
     }
 
     $crumbs = $this->buildApplicationCrumbsWithPath($parents);
     $crumbs->addAction(
       id(new PHUIListItemView())
         ->setName(pht('Create Fragment'))
         ->setHref($this->getApplicationURI('/create/'.$path))
         ->setIcon('create'));
 
     $current_box = $this->createCurrentFragmentView($current, false);
 
     $list = id(new PHUIObjectItemListView())
       ->setUser($viewer);
 
     $fragments = null;
     if ($current === null) {
       // Find all root fragments.
       $fragments = id(new PhragmentFragmentQuery())
         ->setViewer($this->getRequest()->getUser())
         ->needLatestVersion(true)
         ->withDepths(array(1))
         ->execute();
     } else {
       // Find all child fragments.
       $fragments = id(new PhragmentFragmentQuery())
         ->setViewer($this->getRequest()->getUser())
         ->needLatestVersion(true)
         ->withLeadingPath($current->getPath().'/')
         ->withDepths(array($current->getDepth() + 1))
         ->execute();
     }
 
     foreach ($fragments as $fragment) {
       $item = id(new PHUIObjectItemView());
       $item->setHeader($fragment->getName());
       $item->setHref($this->getApplicationURI('/browse/'.$fragment->getPath()));
       if (!$fragment->isDirectory()) {
         $item->addAttribute(pht(
           'Last Updated %s',
           phabricator_datetime(
             $fragment->getLatestVersion()->getDateCreated(),
             $viewer)));
         $item->addAttribute(pht(
           'Latest Version %s',
           $fragment->getLatestVersion()->getSequence()));
+        if ($fragment->isDeleted()) {
+          $item->setDisabled(true);
+          $item->addAttribute(pht('Deleted'));
+        }
       } else {
         $item->addAttribute('Directory');
       }
       $list->addItem($item);
     }
 
     return $this->buildApplicationPage(
       array(
         $crumbs,
         $current_box,
         $list),
       array(
         'title' => pht('Browse Fragments'),
         'device' => true));
   }
 
 }
diff --git a/src/applications/phragment/controller/PhragmentController.php b/src/applications/phragment/controller/PhragmentController.php
index d156745f4..ac5d43c03 100644
--- a/src/applications/phragment/controller/PhragmentController.php
+++ b/src/applications/phragment/controller/PhragmentController.php
@@ -1,154 +1,160 @@
 <?php
 
 abstract class PhragmentController extends PhabricatorController {
 
   protected function loadParentFragments($path) {
     $components = explode('/', $path);
 
     $combinations = array();
     $current = '';
     foreach ($components as $component) {
       $current .= '/'.$component;
       $current = trim($current, '/');
       if (trim($current) === '') {
         continue;
       }
 
       $combinations[] = $current;
     }
 
     $fragments = array();
     $results = id(new PhragmentFragmentQuery())
       ->setViewer($this->getRequest()->getUser())
       ->needLatestVersion(true)
       ->withPaths($combinations)
       ->execute();
     foreach ($combinations as $combination) {
       $found = false;
       foreach ($results as $fragment) {
         if ($fragment->getPath() === $combination) {
           $fragments[] = $fragment;
           $found = true;
           break;
         }
       }
       if (!$found) {
         return null;
       }
     }
     return $fragments;
   }
 
   protected function buildApplicationCrumbsWithPath(array $fragments) {
     $crumbs = $this->buildApplicationCrumbs();
     $crumbs->addCrumb(
       id(new PhabricatorCrumbView())
         ->setName('/')
         ->setHref('/phragment/'));
     foreach ($fragments as $parent) {
       $crumbs->addCrumb(
         id(new PhabricatorCrumbView())
           ->setName($parent->getName())
           ->setHref('/phragment/browse/'.$parent->getPath()));
     }
     return $crumbs;
   }
 
   protected function createCurrentFragmentView($fragment, $is_history_view) {
     if ($fragment === null) {
       return null;
     }
 
     $viewer = $this->getRequest()->getUser();
 
     $phids = array();
     $phids[] = $fragment->getLatestVersionPHID();
 
     $this->loadHandles($phids);
 
     $file = null;
     $file_uri = null;
     if (!$fragment->isDirectory()) {
       $file = id(new PhabricatorFileQuery())
         ->setViewer($viewer)
         ->withPHIDs(array($fragment->getLatestVersion()->getFilePHID()))
         ->executeOne();
       if ($file !== null) {
         $file_uri = $file->getBestURI();
       }
     }
 
     $header = id(new PHUIHeaderView())
       ->setHeader($fragment->getName())
       ->setPolicyObject($fragment)
       ->setUser($viewer);
 
     $actions = id(new PhabricatorActionListView())
       ->setUser($viewer)
       ->setObject($fragment)
       ->setObjectURI($fragment->getURI());
     $actions->addAction(
       id(new PhabricatorActionView())
         ->setName(pht('Download Fragment'))
         ->setHref($file_uri)
         ->setDisabled($file === null)
         ->setIcon('download'));
     $actions->addAction(
       id(new PhabricatorActionView())
         ->setName(pht('Download Contents as ZIP'))
         ->setHref($this->getApplicationURI("zip/".$fragment->getPath()))
         ->setDisabled(false) // TODO: Policy
         ->setIcon('zip'));
     if (!$fragment->isDirectory()) {
       $actions->addAction(
         id(new PhabricatorActionView())
           ->setName(pht('Update Fragment'))
           ->setHref($this->getApplicationURI("update/".$fragment->getPath()))
           ->setDisabled(false) // TODO: Policy
           ->setIcon('edit'));
     } else {
       $actions->addAction(
         id(new PhabricatorActionView())
           ->setName(pht('Convert to File'))
           ->setHref($this->getApplicationURI("update/".$fragment->getPath()))
           ->setDisabled(false) // TODO: Policy
           ->setIcon('edit'));
     }
     if ($is_history_view) {
       $actions->addAction(
         id(new PhabricatorActionView())
           ->setName(pht('View Child Fragments'))
           ->setHref($this->getApplicationURI("browse/".$fragment->getPath()))
           ->setIcon('browse'));
     } else {
       $actions->addAction(
         id(new PhabricatorActionView())
           ->setName(pht('View History'))
           ->setHref($this->getApplicationURI("history/".$fragment->getPath()))
           ->setIcon('history'));
     }
 
     $properties = id(new PHUIPropertyListView())
       ->setUser($viewer)
       ->setObject($fragment)
       ->setActionList($actions);
 
     if (!$fragment->isDirectory()) {
-      $properties->addProperty(
-        pht('Type'),
-        pht('File'));
+      if ($fragment->isDeleted()) {
+        $properties->addProperty(
+          pht('Type'),
+          pht('File (Deleted)'));
+      } else {
+        $properties->addProperty(
+          pht('Type'),
+          pht('File'));
+      }
       $properties->addProperty(
         pht('Latest Version'),
         $this->renderHandlesForPHIDs(array($fragment->getLatestVersionPHID())));
     } else {
       $properties->addProperty(
         pht('Type'),
         pht('Directory'));
     }
 
     return id(new PHUIObjectBoxView())
       ->setHeader($header)
       ->addPropertyList($properties);
   }
 
 }
diff --git a/src/applications/phragment/controller/PhragmentHistoryController.php b/src/applications/phragment/controller/PhragmentHistoryController.php
index 4ca50c0f3..53cac0c36 100644
--- a/src/applications/phragment/controller/PhragmentHistoryController.php
+++ b/src/applications/phragment/controller/PhragmentHistoryController.php
@@ -1,78 +1,83 @@
 <?php
 
 final class PhragmentHistoryController extends PhragmentController {
 
   private $dblob;
 
   public function willProcessRequest(array $data) {
     $this->dblob = idx($data, "dblob", "");
   }
 
   public function processRequest() {
     $request = $this->getRequest();
     $viewer = $request->getUser();
 
     $parents = $this->loadParentFragments($this->dblob);
     if ($parents === null) {
       return new Aphront404Response();
     }
     $current = idx($parents, count($parents) - 1, null);
 
     $path = $current->getPath();
 
     $crumbs = $this->buildApplicationCrumbsWithPath($parents);
     $crumbs->addAction(
       id(new PHUIListItemView())
         ->setName(pht('Create Fragment'))
         ->setHref($this->getApplicationURI('/create/'.$path))
         ->setIcon('create'));
 
     $current_box = $this->createCurrentFragmentView($current, true);
 
     $versions = id(new PhragmentFragmentVersionQuery())
       ->setViewer($viewer)
       ->withFragmentPHIDs(array($current->getPHID()))
       ->execute();
 
     $list = id(new PHUIObjectItemListView())
       ->setUser($viewer);
 
     $file_phids = mpull($versions, 'getFilePHID');
     $files = id(new PhabricatorFileQuery())
       ->setViewer($viewer)
       ->withPHIDs($file_phids)
       ->execute();
     $files = mpull($files, null, 'getPHID');
 
     foreach ($versions as $version) {
       $item = id(new PHUIObjectItemView());
       $item->setHeader('Version '.$version->getSequence());
       $item->setHref($version->getURI());
       $item->addAttribute(phabricator_datetime(
         $version->getDateCreated(),
         $viewer));
 
+      if ($version->getFilePHID() === null) {
+        $item->setDisabled(true);
+        $item->addAttribute('Deletion');
+      }
+
       $disabled = !isset($files[$version->getFilePHID()]);
       $action = id(new PHUIListItemView())
         ->setIcon('download')
         ->setDisabled($disabled)
         ->setRenderNameAsTooltip(true)
         ->setName(pht("Download"));
       if (!$disabled) {
         $action->setHref($files[$version->getFilePHID()]->getBestURI());
       }
       $item->addAction($action);
       $list->addItem($item);
     }
 
     return $this->buildApplicationPage(
       array(
         $crumbs,
         $current_box,
         $list),
       array(
         'title' => pht('Fragment History'),
         'device' => true));
   }
 
 }
diff --git a/src/applications/phragment/storage/PhragmentFragment.php b/src/applications/phragment/storage/PhragmentFragment.php
index 87caf483e..446c306ac 100644
--- a/src/applications/phragment/storage/PhragmentFragment.php
+++ b/src/applications/phragment/storage/PhragmentFragment.php
@@ -1,302 +1,306 @@
 <?php
 
 final class PhragmentFragment extends PhragmentDAO
   implements PhabricatorPolicyInterface {
 
   protected $path;
   protected $depth;
   protected $latestVersionPHID;
   protected $viewPolicy;
   protected $editPolicy;
 
   private $latestVersion = self::ATTACHABLE;
 
   public function getConfiguration() {
     return array(
       self::CONFIG_AUX_PHID => true,
     ) + parent::getConfiguration();
   }
 
   public function generatePHID() {
     return PhabricatorPHID::generateNewPHID(
       PhragmentPHIDTypeFragment::TYPECONST);
   }
 
   public function getURI() {
     return '/phragment/fragment/'.$this->getID().'/';
   }
 
   public function getName() {
     return basename($this->path);
   }
 
   public function getFile() {
     return $this->assertAttached($this->file);
   }
 
   public function attachFile(PhabricatorFile $file) {
     return $this->file = $file;
   }
 
   public function isDirectory() {
     return $this->latestVersionPHID === null;
   }
 
+  public function isDeleted() {
+    return $this->getLatestVersion()->getFilePHID() === null;
+  }
+
   public function getLatestVersion() {
     if ($this->latestVersionPHID === null) {
       return null;
     }
     return $this->assertAttached($this->latestVersion);
   }
 
   public function attachLatestVersion(PhragmentFragmentVersion $version) {
     return $this->latestVersion = $version;
   }
 
 
 /* -(  Updating  )  --------------------------------------------------------- */
 
 
   /**
    * Create a new fragment from a file.
    */
   public static function createFromFile(
     PhabricatorUser $viewer,
     PhabricatorFile $file = null,
     $path,
     $view_policy,
     $edit_policy) {
 
     $fragment = id(new PhragmentFragment());
     $fragment->setPath($path);
     $fragment->setDepth(count(explode('/', $path)));
     $fragment->setLatestVersionPHID(null);
     $fragment->setViewPolicy($view_policy);
     $fragment->setEditPolicy($edit_policy);
     $fragment->save();
 
     // Directory fragments have no versions associated with them, so we
     // just return the fragment at this point.
     if ($file === null) {
       return $fragment;
     }
 
     if ($file->getMimeType() === "application/zip") {
       $fragment->updateFromZIP($viewer, $file);
     } else {
       $fragment->updateFromFile($viewer, $file);
     }
 
     return $fragment;
   }
 
 
   /**
    * Set the specified file as the next version for the fragment.
    */
   public function updateFromFile(
     PhabricatorUser $viewer,
     PhabricatorFile $file) {
 
     $existing = id(new PhragmentFragmentVersionQuery())
       ->setViewer($viewer)
       ->withFragmentPHIDs(array($this->getPHID()))
       ->execute();
     $sequence = count($existing);
 
     $this->openTransaction();
       $version = id(new PhragmentFragmentVersion());
       $version->setSequence($sequence);
       $version->setFragmentPHID($this->getPHID());
       $version->setFilePHID($file->getPHID());
       $version->save();
 
       $this->setLatestVersionPHID($version->getPHID());
       $this->save();
     $this->saveTransaction();
   }
 
   /**
    * Apply the specified ZIP archive onto the fragment, removing
    * and creating fragments as needed.
    */
   public function updateFromZIP(
     PhabricatorUser $viewer,
     PhabricatorFile $file) {
 
     if ($file->getMimeType() !== "application/zip") {
       throw new Exception("File must have mimetype 'application/zip'");
     }
 
     // First apply the ZIP as normal.
     $this->updateFromFile($viewer, $file);
 
     // Ensure we have ZIP support.
     $zip = null;
     try {
       $zip = new ZipArchive();
     } catch (Exception $e) {
       // The server doesn't have php5-zip, so we can't do recursive updates.
       return;
     }
 
     $temp = new TempFile();
     Filesystem::writeFile($temp, $file->loadFileData());
     if (!$zip->open($temp)) {
       throw new Exception("Unable to open ZIP");
     }
 
     // Get all of the paths and their data from the ZIP.
     $mappings = array();
     for ($i = 0; $i < $zip->numFiles; $i++) {
       $path = trim($zip->getNameIndex($i), '/');
       $stream = $zip->getStream($path);
       $data = null;
       // If the stream is false, then it is a directory entry.  We leave
       // $data set to null for directories so we know not to create a
       // version entry for them.
       if ($stream !== false) {
         $data = stream_get_contents($stream);
         fclose($stream);
       }
       $mappings[$path] = $data;
     }
 
     // We need to detect any directories that are in the ZIP folder that
     // aren't explicitly noted in the ZIP.  This can happen if the file
     // entries in the ZIP look like:
     //
     //  * something/blah.png
     //  * something/other.png
     //  * test.png
     //
     // Where there is no explicit "something/" entry.
     foreach ($mappings as $path_key => $data) {
       if ($data === null) {
         continue;
       }
       $directory = dirname($path_key);
       while ($directory !== ".") {
         if (!array_key_exists($directory, $mappings)) {
           $mappings[$directory] = null;
         }
         if (dirname($directory) === $directory) {
           // dirname() will not reduce this directory any further; to
           // prevent infinite loop we just break out here.
           break;
         }
         $directory = dirname($directory);
       }
     }
 
     // Adjust the paths relative to this fragment so we can look existing
     // fragments up in the DB.
     $base_path = $this->getPath();
     $paths = array();
     foreach ($mappings as $p => $data) {
       $paths[] = $base_path.'/'.$p;
     }
 
     // FIXME: What happens when a child exists, but the current user
     // can't see it.  We're going to create a new child with the exact
     // same path and then bad things will happen.
     $children = id(new PhragmentFragmentQuery())
       ->setViewer($viewer)
       ->needLatestVersion(true)
       ->withLeadingPath($this->getPath().'/')
       ->execute();
     $children = mpull($children, null, 'getPath');
 
     // Iterate over the existing fragments.
     foreach ($children as $full_path => $child) {
       $path = substr($full_path, strlen($base_path) + 1);
       if (array_key_exists($path, $mappings)) {
         if ($child->isDirectory() && $mappings[$path] === null) {
           // Don't create a version entry for a directory
           // (unless it's been converted into a file).
           continue;
         }
 
         // The file is being updated.
         $file = PhabricatorFile::newFromFileData(
           $mappings[$path],
           array('name' => basename($path)));
         $child->updateFromFile($viewer, $file);
       } else {
         // The file is being deleted.
         $child->deleteFile($viewer);
       }
     }
 
     // Iterate over the mappings to find new files.
     foreach ($mappings as $path => $data) {
       if (!array_key_exists($base_path.'/'.$path, $children)) {
         // The file is being created.  If the data is null,
         // then this is explicitly a directory being created.
         $file = null;
         if ($mappings[$path] !== null) {
           $file = PhabricatorFile::newFromFileData(
             $mappings[$path],
             array('name' => basename($path)));
         }
         PhragmentFragment::createFromFile(
           $viewer,
           $file,
           $base_path.'/'.$path,
           $this->getViewPolicy(),
           $this->getEditPolicy());
       }
     }
   }
 
   /**
    * Delete the contents of the specified fragment.
    */
   public function deleteFile(PhabricatorUser $viewer) {
     $existing = id(new PhragmentFragmentVersionQuery())
       ->setViewer($viewer)
       ->withFragmentPHIDs(array($this->getPHID()))
       ->execute();
     $sequence = count($existing);
 
     $this->openTransaction();
       $version = id(new PhragmentFragmentVersion());
       $version->setSequence($sequence);
       $version->setFragmentPHID($this->getPHID());
       $version->setFilePHID(null);
       $version->save();
 
       $this->setLatestVersionPHID($version->getPHID());
       $this->save();
     $this->saveTransaction();
   }
 
 
 /* -(  Policy Interface  )--------------------------------------------------- */
 
 
   public function getCapabilities() {
     return array(
       PhabricatorPolicyCapability::CAN_VIEW,
       PhabricatorPolicyCapability::CAN_EDIT,
     );
   }
 
   public function getPolicy($capability) {
     switch ($capability) {
       case PhabricatorPolicyCapability::CAN_VIEW:
         return $this->getViewPolicy();
       case PhabricatorPolicyCapability::CAN_EDIT:
         return $this->getEditPolicy();
     }
   }
 
   public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
     return false;
   }
 
   public function describeAutomaticCapability($capability) {
     return null;
   }
 
 }