Linux 有没有办法让Nautilus使用VCS'在版本控制下移动文件;移动命令?

Linux 有没有办法让Nautilus使用VCS'在版本控制下移动文件;移动命令?,linux,version-control,bazaar,nautilus,Linux,Version Control,Bazaar,Nautilus,例如,假设我将一个文件从/project/file.cs移动到/project/subdir/file.cs。如果nautilus能自动将其转换为bzr mv/project/file.cs/project/subdir/file.cs,那就太好了。有可能设置这个吗 如果我在版本控制文件上执行普通的mv时收到警告也会很好,但我想这是一个单独的问题。作为的开发人员之一,我很确定这样的事情目前是不可能的。可以提供上下文菜单、属性页和其他列,它们可以响应浏览器主窗口中显示的文件。他们不能与移动或删除等

例如,假设我将一个文件从
/project/file.cs
移动到
/project/subdir/file.cs
。如果nautilus能自动将其转换为
bzr mv/project/file.cs/project/subdir/file.cs
,那就太好了。有可能设置这个吗


如果我在版本控制文件上执行普通的
mv
时收到警告也会很好,但我想这是一个单独的问题。

作为的开发人员之一,我很确定这样的事情目前是不可能的。可以提供上下文菜单、属性页和其他列,它们可以响应浏览器主窗口中显示的文件。他们不能与移动或删除等任意事件挂钩。(如果是这样的话,我很乐意,但这不是我们现在的优先事项。)您应该自己修改Nautilus扩展API

如果你觉得可以,但不知道从哪里开始,你应该看看鹦鹉螺的来源,并在网上询问。如果你在这件事上找错了人,他们肯定会告诉你的


有可能鹦鹉螺的延伸是这种事情的错误地点。也许可以用GVFS而不是Nautilus扩展来做一些事情,但我对此一无所知。

就像你已经指出的那样,你基本上需要一些可以监听移动的东西,所以我想我应该编写一些代码,让你知道这是如何工作的

我尝试使用gio.FileMonitor,但最终还是使用了普通的pyinotify,因为后者内置了检测文件重命名/移动的支持

import pyinotify

import bzrlib
from bzrlib.workingtree import WorkingTree
from bzrlib.errors import NotBranchError, BzrRenameFailedError

directories_to_watch = [
    # Add the paths to your working copies / branches to watch here
]

wm = pyinotify.WatchManager()

# When you listen to both MOVED_FROM and MOVED_TO the event for MOVED_TO will include both 
# pathname (new path) and src_pathname (previous path).
mask = pyinotify.IN_MOVED_FROM | pyinotify.IN_MOVED_TO

class EventHandler(pyinotify.ProcessEvent):

    def process_IN_MOVED_TO(self, event):
        try:
            tree, path = WorkingTree.open_containing(event.src_pathname)
            root = event.src_pathname[:-len(path)] # Ugh, hackish

            if not path.startswith(".bzr"): # Also hackish (to exclude events for anything in the .bzr subdirectory)
                try:
                    tree.lock_tree_write()
                    source = event.src_pathname[len(root):] # Again hackish
                    target = event.pathname[len(root):] # Same
                    tree.rename_one(source, target)
                    print "Renamed %s to %s" % (source, target)
                except BzrRenameFailedError: # Same
                    pass
                finally:
                    tree.unlock()
        except NotBranchError:
            return

handler = EventHandler()
notifier = pyinotify.Notifier(wm, handler)

for path in directories_to_watch:
    wdd = wm.add_watch(path, mask, rec=True, auto_add=True)
    print "Recursively watching %s" % path

notifier.loop()
下面是它的工作原理:

$ mv afile bfile
$ bzr status
renamed:
  afile => bfile

$ mv bfile foobar/
$ bzr status
renamed:
  afile => foobar/bfile

$ mv foobar/ zoobar
$ bzr status
renamed:
  afile => zoobar/bfile
  foobar/ => zoobar/

$ mv zoobar/ foobar
$ bzr status
renamed:
  afile => foobar/bfile

$ mv foobar/bfile afile
我们又回到了起点;-)

[编辑]

如果您不想手动列出要查看的各种目录,那么编写Nautilus扩展可能是一个好主意,它可以跟踪在导航时遇到的各种工作副本。下面是一些让您开始学习的内容(这将进入
~/.nautilus/python extensions
):

我从RabbitVCS的扩展代码中借用了各种docstring;-)

在监视器中,您可能需要查看
workingcopies.db
文件中的添加内容,并在找到的任何新工作副本上注册手表

资源


有趣!你发的时候我没看到这个。我不认为你一定要钓鹦鹉螺。例如,Dropbox运行一个守护进程,监听删除、移动、重命名等事件。它不需要Nautilus,您可以在仅命令行环境中运行它。我不知道它是如何工作的,但这是可能的。。。我明白你的意思。我现在为RabbitVCS考虑的一件事是在工作副本的根目录中添加文件系统监视器,以便在更改时更新缓存。(例如gio.GileMonitor,或者在Linux上使用pyinotify接口。)不确定通过在“/”上添加递归事件监视器,您将如何在全局范围内完成此操作。。。我不知道是否会出现与此相关的问题。递归监视“/”我不寒而栗;-)@布鲁斯·范德库伊-嗨,布鲁斯!是的,告诉我:P但是pyinotify也没有那么有趣…你太棒了!当然,也有一些限制。必须手动列出您想要查看的目录是一件痛苦的事情,如果您将文件移动到尚未添加到bzr的目录中,则会失败。但是对于一个陌生人在互联网上提出的问题的答案来说,这太棒了!我将查看并查看是否可以在使用脚本时对其进行优化。/me脸红感谢您的好话:-)为了避免手动列出您要查看的目录,最好创建Nautilus扩展,在导航过程中跟踪工作副本的位置。我加了一个例子。我很想看看你的想法。
import os
import pickle

import nautilus 

import gio

from xdg import BaseDirectory as basedir

import bzrlib
from bzrlib.workingtree import WorkingTree
from bzrlib.errors import NotBranchError

class BzrMonitor(nautilus.InfoProvider, nautilus.MenuProvider):

    data_directory = basedir.save_data_path("bzrmonitor")
    data_filename = os.path.join(data_directory, "workingcopies.db")

    def __init__(self):
        print "Initializing BzrMonitor extension..."

        try:
            data_file = open(self.data_filename, "r")
            self.data = pickle.load(data_file)
        except IOError:
            self.data = []
            data_file = open(self.data_filename, "w")
            pickle.dump(self.data, data_file)
            data_file.close()

    def detect_and_save_branch(self, path):
        try:
            tree, rel_path = WorkingTree.open_containing(path)

            # TODO: Still can't figure out how to get the path from the tree itself
            if len(rel_path) > 0: 
                root = path[:-len(rel_path)]
            else:
                root = path

            root = root.rstrip(os.path.sep)

            if root not in self.data: 
                print "Added not seen before branch %s to cache..." % root
                self.data.append(root)
                data_file = open(self.data_filename, "w")
                pickle.dump(self.data, data_file)
                data_file.close()

        except NotBranchError:
            return

    def update_file_info(self, item):
        """
        This function is called when:

          - When you enter a directory (once for each item but only when the
            item was modified since the last time it was listed)
          - When you refresh (once for each item visible)
          - When an item viewable from the current window is created or modified
        """
        self.detect_and_save_branch(gio.File(item.get_uri()).get_path())

    def get_file_items(self, window, items):
        """
        Menu activated with items selected. Nautilus also calls this function
        when rendering submenus, even though this is not needed since the entire
        menu has already been returned.
        """

        pass

    def get_background_items(self, window, item):
        """
        Menu activated on entering a directory. Builds context menu for File
        menu and for window background.
        """

        self.detect_and_save_branch(gio.File(item.get_uri()).get_path())