summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorSerge Broslavsky <serge.broslavsky@gmail.com>2012-02-14 11:55:18 +0200
committerhut <hut@lavabit.com>2012-02-14 12:50:37 +0100
commitec18cfafc2f85e4dbc4ab2d61588395a59352443 (patch)
treeee1d97287fba65e09453434484380c8a57cd59cf
parent824104ae627f276eee8ec88522c873a6b76c5ecc (diff)
downloadranger-ec18cfafc2f85e4dbc4ab2d61588395a59352443.tar.gz
Feature: pasting hardlinked trees
This feature is useful when there is a need to create a directory tree,
which is similar to an existing one while keeping all the files in this
new tree hardlinked to those existing ones. Such functionality might be
useful when rearranging a huge directory tree (or a set of directory
trees) into some new layout.

This operation is done synchronously (i.e. it doesn't use CommandLoader
to put the ongoing operation into the task queue).

Target end of the operation is handled the following way:

1. If target directory with the same name exists - it gets used.

2. If target folder does not exist - it is created with the same mode
   bits as corresponding source one.

3. If target file with the same name exists and is a hardlink of the
   source file already - it is getting left as it is. If it's not a
   hardlink of the source file - a new hardlink is created in a form of
   <original>_, <original>_1, ..., etc.

4. If target file does not exist - a hardlink is created.
-rw-r--r--ranger/core/actions.py26
-rw-r--r--ranger/defaults/rc.conf1
2 files changed, 25 insertions, 2 deletions
diff --git a/ranger/core/actions.py b/ranger/core/actions.py
index 8106a89b..4e72de77 100644
--- a/ranger/core/actions.py
+++ b/ranger/core/actions.py
@@ -19,8 +19,8 @@ import re
 import shutil
 import string
 import tempfile
-from os.path import join, isdir, realpath
-from os import link, symlink, getcwd
+from os.path import join, isdir, realpath, exists
+from os import link, symlink, getcwd, listdir, stat
 from inspect import cleandoc
 
 import ranger
@@ -1003,6 +1003,28 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 			except Exception as x:
 				self.notify(x)
 
+	def paste_hardlinked_subtree(self):
+		for f in self.env.copy:
+			try:
+				target_path = join(getcwd(), f.basename)
+				self._recurse_hardlinked_tree(f.path, target_path)
+			except Exception as x:
+				self.notify(x)
+
+	def _recurse_hardlinked_tree(self, source_path, target_path):
+		if isdir(source_path):
+			if not exists(target_path):
+				os.mkdir(target_path, stat(source_path).st_mode)
+			for item in listdir(source_path):
+				self._recurse_hardlinked_tree(
+					join(source_path, item),
+					join(target_path, item))
+		else:
+			if not exists(target_path) \
+			or stat(source_path).st_ino != stat(target_path).st_ino:
+				link(source_path,
+					next_available_filename(target_path))
+
 	def paste(self, overwrite=False):
 		"""Paste the selected items into the current directory"""
 		copied_files = tuple(self.env.copy)
diff --git a/ranger/defaults/rc.conf b/ranger/defaults/rc.conf
index 1ecbc398..77ffa5c3 100644
--- a/ranger/defaults/rc.conf
+++ b/ranger/defaults/rc.conf
@@ -147,6 +147,7 @@ map po paste overwrite=True
 map pl paste_symlink relative=False
 map pL paste_symlink relative=True
 map phl paste_hardlink
+map pht paste_hardlinked_subtree
 
 map dd cut
 map ud uncut