Đồng bộ hóa các gói giữa các máy khác nhau


57

Tôi sử dụng emacs ở những nơi khác nhau và tôi muốn có một thiết lập và gói tương tự được cài đặt ở mọi nơi. Tôi đoán tôi có thể sử dụng kho lưu trữ kiểm soát phiên bản cho các tệp cài đặt. Vì tôi sử dụng Prelude , nên sẽ như vậy ~/.emacs.d/personal/.

Tôi không biết làm thế nào với các gói. Có một tập tin nào đó trong .emacs.d/danh sách các gói đã cài đặt mà tôi có thể sử dụng để tạo emacs trong các máy khác để cài đặt những gói được liệt kê ở đó không?


2
Đối với tôi các gói đã cài đặt chỉ là một phần của kho điều khiển phiên bản Emacs của tôi. Tôi biên dịch byte các gói với phiên bản Emacs cũ nhất mà tôi muốn / cần sử dụng và cũng đặt các tệp .elc vào kho lưu trữ. Điều này cung cấp kiểm soát tối đa và nhất quán. Sự đánh đổi là kích thước lớn (tương đối) của kho lưu trữ. Kho git 6 tuổi của tôi có kích thước 120MB. Mặc dù tôi có thể thoát khỏi 1/10 nếu tôi không bao gồm các gói, nhưng vài megabyte "lãng phí" đó thực sự không làm tôi lo lắng.
ớt bột

1
Tôi nên nói thêm rằng mặc dù ELPA / MELPA / ... khá phổ biến hiện nay, nhưng vẫn không phải tất cả các gói đều có sẵn thông qua chúng. Vì vậy, nếu bạn sử dụng gói cần cài đặt thủ công, bạn có thể không muốn sao chép nỗ lực trên mỗi máy mới mà bạn sử dụng (cộng với mỗi lần nâng cấp gói). Một lần nữa, một giải pháp dễ dàng là thêm gói vào kho điều khiển phiên bản Emacs của bạn.
ớt bột

Bạn có thể chỉ biên dịch byte trên trang web và thoát khỏi việc bỏ qua các tệp .elc trong git không?
Vamsi

@Vamsi: Tôi đặt các tệp được biên dịch byte vào kho lưu trữ vì một số gói (không phải ELPA) hơi khó biên dịch do phụ thuộc vào chúng. Đối với những điều này tôi không muốn lặp lại quá trình nếu không cần thiết.
ớt bột

@paprika Bạn có thể cung cấp một số chi tiết làm thế nào để có được thiết lập của bạn? Bạn có thêm mọi thứ trong .emacs.d plus .emacs vào kho lưu trữ không?
Người mới bắt đầu

Câu trả lời:


50

Không có tệp kê khai được tạo tự động mà bạn có thể đồng bộ hóa để đạt được hiệu quả mong muốn.

Điều đó nói rằng, một cái gì đó bạn có thể làm là thêm các cuộc gọi package-installvào chính cấu hình emacs của bạn.

(package-install 'auctex)

Ý tưởng là package-installidempotent, vì vậy nếu gói đã có sẵn, sẽ không có gì thực sự xảy ra. Giả sử bạn có một cuộc gọi như vậy cho mọi gói bạn sử dụng (hoặc ít nhất là các lá trong biểu đồ phụ thuộc), điều đó sẽ đồng bộ hóa hiệu quả các gói của bạn trên các máy.


Đối với nhiều gói bạn có thể sử dụng như sau:

(setq my-package-list '(package1 package2 packageN))
(mapc #'package-install my-package-list)

2
Tôi ngạc nhiên khi cách tiếp cận này đơn giản hơn nhiều so với giải pháp được đề xuất ở đây . Có sự khác biệt nào không? Đoạn trích cũng sử dụng package-install.
Stenskjaer

1
Đã có những thay đổi package.elkể từ câu trả lời được liên kết đó. Có thể tại thời điểm package-installthực hiện các thao tác trên các gói hiện có, không chỉ gỡ cài đặt.
Jonathan Leech-Pepin

3
Kỹ thuật này không may gặp sự cố - ngay cả khi kho lưu trữ gói lưu trữ đồng nhất giữa các máy và được chỉ định trong SCM. Nó không đảm bảo rằng các phiên bản gói giống hệt nhau giữa các máy. Vấn đề là các phiên bản gói không được chỉ định; các gói riêng lẻ này có thể phân kỳ theo thời gian và các phụ thuộc của chúng có thể trở nên không tương thích. Điều này có thể xảy ra khá dễ dàng trên kho lưu trữ gói hoạt động như melpa.
ctpenrose

@ctpenrose: Bạn có gợi ý để tránh vấn đề này không?
học sinh

@student Tôi đã giảm thiểu vấn đề bằng cách sử dụng melpa ổn định và cập nhật các gói ít thường xuyên hơn.
ctpenrose

34

Tôi giữ thư mục .emacs.d của mình trong kiểm soát phiên bản. Sau đó, trong init.el và các tệp tiếp theo, tôi sử dụng gói use để xác định thiết lập gói. Gói không chỉ tải gói của bạn một cách lười biếng, nó sẽ tải chúng theo yêu cầu nếu chúng không tồn tại từ bất kỳ gói repos nào bạn đã thiết lập.

Ví dụ: tôi sử dụng chế độ đi, nhưng không phải trên mọi máy. Trong init.el của tôi, tôi có những điều sau đây:

(use-package go-mode
  :ensure t
  :config
  (progn
    (defun my-go-mode-hook ()
      (linum-mode t)
      (setq tab-width 4)
      (add-hook 'before-save-hook 'gofmt-before-save))
    (add-hook 'go-mode-hook 'my-go-mode-hook)))

Điều này thêm một móc chế độ, nhưng quan trọng hơn, bằng cách chỉ định :ensure tnó sẽ tải xuống gói theo yêu cầu.

Để giữ máy đồng bộ, bạn chỉ cần kiểm tra hoặc kéo từ repo và khởi động Emacs. Bất kỳ gói mới sẽ được tải xuống và cài đặt.


Đây cũng là giải pháp tôi sử dụng, thay vì Cask, chủ yếu vì (như T. Verron đã lưu ý) Cask không hoạt động (tốt) trên Windows, và vẫn là một sự phụ thuộc khác.
Andy

1
Đây cũng là cách tôi sử dụng, nhưng thay vì làm điều :ensure go-modeđó là lặp lại tên gói, bạn chỉ có thể chỉ định:ensure t
Pedro Luz

Điểm tốt! Đây là một câu trả lời cũ. Tôi sẽ cập nhật nó.
elarson

Bạn cũng có thể sử dụng :hooktừ khóa để đơn giản hóa mã của bạn.
Guilherme Salomé

17

Trong Emacs-25, có biến package-selected-packages, vì vậy bạn có thể tùy chỉnh biến này và sử dụng package-install-selected-packagesđể đảm bảo chúng được cài đặt.


Lưu ý rằng tôi thấy nó, tên của lệnh đó hơi khó hiểu. Tôi có thể thay đổi nó thành gói-install-chọn-gói không?
Malabarba

Giả sử bạn có nghĩa là "Bây giờ" thay vì "Lưu ý", vâng.
Stefan

9

Những gì bạn muốn sử dụng là Cask , cho phép bạn tạo một tệp Cask chỉ định gói nào sẽ được cài đặt cask install. Nó có thể được sử dụng để quản lý các phụ thuộc của gói và "phụ thuộc" cấu hình Emacs của bạn một cách dễ dàng. Đặt tệp Cask của bạn dưới sự kiểm soát phiên bản và cài đặt / cập nhật các gói trên cơ sở từng máy.


4
Điều đáng chú ý là (tính đến ngày hôm nay) giải pháp này không hoạt động đối với các máy tính windows.
T. Verron

1
Tôi không còn sử dụng giải pháp này nữa, vì lý do của bạn (và Cask vẫn là một phụ thuộc khác). Tuyệt vời để làm gói; kinh khủng cho việc quản lý cấu hình.
Andy

6

Một cách tiếp cận khác sẽ là như sau: vì tôi không chỉ muốn đồng bộ hóa các gói Emacs của mình, mà cả các tệp khác (ví dụ .emacs, .bashrccũng như các thư mục khác) giữa máy chủ và máy tính xách tay của tôi, tôi bắt đầu sử dụng unison, để đồng bộ hóa các tệp và thư mục. Vì vậy, khi làm việc trên máy tính xách tay của tôi, tôi đơn giản chạy unison laptoptrước mọi thứ khác. ~/.unison/laptop.prfTệp của tôi có phần sau cho các tệp liên quan đến Emacs:

path = .emacs
path = .emacs.d
ignore = Path {.emacs.d/semanticdb}

Vì các gói Emacs của tôi (và cả các bản sao lưu và dấu trang Emacs của tôi) được lưu trữ trong ~/.emacs.dnày nên đảm bảo tôi đã có mọi thứ trên tất cả các máy của mình.

Một cách tiếp cận khác sẽ được đặt .emacs.dthư mục trong một thư mục được đồng bộ hóa với ownCloud, DropBox hoặc bất kỳ dịch vụ đồng bộ hóa tệp nào khác và sau đó tạo liên kết tượng trưng từ ~/.emacs.dthư mục dùng chung đó.


5

Mặc dù package.ellà cách tiêu chuẩn để cài đặt các gói, bạn cũng có thể muốn thử el-get, rất hữu ích cho việc cài đặt các gói không (hoặc không thể) trên elpa. Câu trả lời này liên quan đến việc đồng bộ hóa các gói như vậy.

Cách bạn đảm bảo rằng các gói đã cho được cài đặt khi sử dụng el-get là thêm một cái gì đó giống như sau vào tệp init của bạn

(el-get 'sync '(packages))

trong đó các gói là danh sách các gói bạn muốn được cài đặt. Chức năng này tương tự như package-installnó chỉ cài đặt các gói nếu chúng chưa được cài đặt, nếu không, nó chỉ đơn giản là khởi tạo các gói.


5

Tôi sử dụng một mẹo nhỏ "bị đánh cắp" từ emacs-starter-kit (tôi nghĩ):

(defun maybe-install-and-require (p)
  (when (not (package-installed-p p))
   (package-install p))
  (require p))

Vì vậy, khi tôi yêu cầu một gói, tôi chỉ cần sử dụng:

(maybe-install-and-require 'magit)

Trên các phần khởi động emacs, đánh giá cấu hình của tôi, package.elsẽ cung cấp để cài đặt magit nếu nó không được cài đặt.

Bạn có thể tìm thấy cấu hình của tôi ở đây:

https://github.com/mdallastella/emacs-config/


1
Theo cùng một triết lý, bạn có thể sử dụng 'yêu cầu nghịch lý
csantosb

3

Tôi có thư mục ~ / emacs được kiểm soát phiên bản thủy ngân và chứa mọi thứ mà thiết lập emacs của tôi bao gồm (~ / emacs / site-lisp cho các thư viện được tải xuống thủ công, ~ / emacs / elpa cho các thư viện được cài đặt elpa, ~ / emacs / etc / đối với .emacs đã tách, ~ / emacs / dot-emacs.el mà tôi symlink là ~ / .emacs). Nó đòi hỏi một số điều chỉnh của một số gói để có tất cả các tệp quan trọng bên trong cây này, nhưng nó hoạt động tốt. Một vài bit cụ thể mà tôi đã thực hiện bởi các điều kiện trên tên hệ thống.

Vì vậy, sau khi tôi cài đặt / cấu hình lại / thay đổi bất cứ điều gì, tôi chỉ cần cam kết kéo / đẩy tất cả các thay đổi giữa tất cả các máy tôi sử dụng.

Lợi ích bổ sung là tôi có lịch sử đầy đủ về cấu hình của mình và có thể quay lại / bisect / Revert trong trường hợp có lỗi xảy ra.

PS mercurial có vẻ đặc biệt phù hợp vì nó có lực kéo / đẩy hai bên tự nhiên, nhưng thiết lập tương tự không nên khó thực hiện với git hoặc bất kỳ dvcs nào khác.


3

Tôi có cái này setup-packages.eltrong thiết lập emacs của tôi, một mã lai từ blog của PreludeTomorokoshi về Quản lý gói .

setup-packages.el làm như sau:

  • Tạo một thư mục cho elpacác gói nếu không tồn tại và thêm nó và các thư mục con của nó vào load-path.
  • Cập nhật package-archivesdanh sách với Melpa.
  • Kiểm tra xem bạn đã my-packagescài đặt tất cả các gói trong danh sách chưa. Nếu một gói không được cài đặt, cài đặt nó.

Cách thực hiện

  • Lưu setup-packages.eldưới đây vào ~/.emacs.d/thư mục của bạn .
  • Đặt user-emacs-directory, setup-packages-filemy-packagescác biến trong của bạn init.elvà làm (load setup-packages-file).

Khi bạn khởi động emacs lần đầu tiên trên một máy chưa cài đặt các gói này, tất cả các nhịp được liệt kê trong my-packagessẽ được cài đặt tự động.

thiết lập-gói.el

;; setup-packages.el - Package management

(require 'cl)
(require 'package)

;; Set the directory where you want to install the packages
(setq package-user-dir (concat user-emacs-directory "elpa/"))

;; Add melpa package source when using package list
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/") t)

;; Load emacs packages and activate them
;; This must come before configurations of installed packages.
;; Don't delete this line.
(package-initialize)
;; `package-initialize' call is required before any of the below
;; can happen

;; Auto install the required packages
;; Method to check if all packages are installed
(defun packages-installed-p ()
  (loop for p in my-packages
        when (not (package-installed-p p)) do (return nil)
        finally (return t)))

;; if not all packages are installed, check one by one and install the missing ones.
(unless (packages-installed-p)
  ;; check for new packages (package versions)
  (message "%s" "Emacs is now refreshing its package database...")
  (package-refresh-contents)
  (message "%s" " done.")
  ;; install the missing packages
  (dolist (p my-packages)
    (when (not (package-installed-p p))
      (package-install p))))

(provide 'setup-packages)

init.el

Bạn sẽ cần những điều sau đây trong init.el:

(setq user-home-directory  (getenv "HOME"))
(setq user-emacs-directory (concat user-home-directory ".emacs.d/"))
(setq setup-packages-file  (expand-file-name "setup-packages.el" user-emacs-directory))

;; A list of packages to ensure are installed at launch
(setq my-packages
      '(
        ;; package1
        ;; package2
       ))

(load setup-packages-file nil :nomessage) ; Load the packages

2

Để phản ánh cấu hình của mình, tôi quyết định thực hiện một cách tiếp cận khác, sử dụng Syncthing ; mọi thay đổi trong bất kỳ tệp cấu hình nào của tôi đều lan truyền sang bất kỳ máy tính nào khác của tôi mà không phải quan tâm đến nó, vì vậy khi tôi nâng cấp các gói, tôi chỉ phải thực hiện một trong các máy tính.


2

RSYNC : Đồng bộ hóa các thư mục / tệp được chọn bằng cách sử dụng rsyncqua mạng gia đình hoặc qua sshmáy chủ từ xa.

rsynclà tiện ích đồng bộ hóa một chiều có thể xóa các tệp trên mục tiêu, vì vậy hãy đảm bảo sao lưu dữ liệu của bạn trên cả vị trí nguồn và đích kiểm tra kỹ lưỡng bằng cách sử dụng --dry-runtùy chọn trước khi thực hiện.

Để đọc về cách định cấu hình đúng .authinfotệp, hãy xem https://www.gnu.org/software/emacs/manual/auth.html Một .authinfonội dung tệp ví dụ (có thể chứa nhiều mục khác nhau) như sau:

machine mymachine login myloginname password mypassword port myport

Định cấu hình và sử dụng chức năng rsync-remoteđể đồng bộ hóa với sshmáy chủ từ xa. Hoặc, sử dụng chức năng rsync-localđể đồng bộ hóa trên cùng một máy tính hoặc qua mạng gia đình đáng tin cậy.

(require 'auth-source)

;;; EXAMPLE:
;;;   (get-auth-info "12.34.567.89" "username")
;;;   (get-auth-info "localhost" "root")
(defun get-auth-info (host user &optional port)
  (let ((info (nth 0 (auth-source-search
                      :host host
                      :user user
                      :port port
                      :require '(:user :secret)
                      :create t))))
    (if info
      (let* ((port (plist-get info :port))
             (secret-maybe (plist-get info :secret))
             (secret
               (if (functionp secret-maybe)
                 (funcall secret-maybe)
                 secret-maybe)))
          (list port secret))
    nil)))

(defun rsync-filter (proc string)
  (cond
    ((string-match
       "^\\([a-zA-Z0-9_\\-\\.]+\\)@\\([a-zA-Z0-9_\\-\\.]+\\)'s password: "
       string)
      (let* ((user (substring string (match-beginning 1) (match-end 1)))
             (host (substring string (match-beginning 2) (match-end 2)))
             (password (car (cdr (get-auth-info host user)))))
        (process-send-string proc (concat password "\n"))))
    ((not (or (string-match "files\\.\\.\\.\r" string)
              (string-match "files to consider\n" string)))
      (with-current-buffer (messages-buffer)
        (let ((inhibit-read-only t))
          (goto-char (point-max))
          (when (not (bolp))
            (insert "\n"))
          (insert string)
          (when (not (bolp))
            (insert "\n")))))))

(defun rsync-remote ()
"Use rsync to a remote server via ssh.  Back-up your data first!!!"
(interactive)
  (let* (
      (host "localhost")
      (username "root")
      (port (or (car (get-auth-info host username))
                (number-to-string (read-number "Port:  "))))
      (source
        (let ((dir (expand-file-name (locate-user-emacs-file "elpa/"))))
          (if (file-directory-p dir)
            dir
            (let ((debug-on-quit nil)
                  (msg (format "`%s` is not a valid directory." dir)))
              (signal 'quit `(,msg))))))
      (target "/private/var/mobile/elpa/")
      (ssh "/usr/bin/ssh")
      (rsync "/usr/bin/rsync")
      (rsync-include-file "/path/to/include-file.txt")
      (rsync-exclude-file "/path/to/exclude-file.txt")
      (rsh (concat "--rsh=ssh -p " port " -l " username))
      (host+target (concat host ":" target)))
    (start-process
        "rsync-process"
        nil
        rsync
        "-avr" ;; must specify the `-r` argument when using `--files-from`
        "--delete"
        ;; The paths inside the exclusion file must be relative, NOT absolute.
        ;;; (concat "--files-from=" rsync-include-file)
        ;;; (concat "--exclude-from=" rsync-exclude-file)
        rsh
        source
        host+target)
    (set-process-filter (get-process "rsync-process") 'rsync-filter)
    (set-process-sentinel
      (get-process "rsync-process")
      (lambda (p e) (when (= 0 (process-exit-status p))
        (message "rsync-remote:  synchronizing ... done."))))))

(defun rsync-local ()
"Use rsync locally -- e.g., over a trusted home network.
 Back-up your data first!!!"
  (interactive)
  (let (
      (rsync-program "/usr/bin/rsync")
      (source
        (let ((dir (expand-file-name
                     (file-name-as-directory
                       (read-directory-name "Source Directory: " nil nil nil nil)))))
          (if (file-directory-p dir)
            dir
            (let ((debug-on-quit nil)
                  (msg (format "`%s` is not a valid directory." dir)))
              (signal 'quit `(,msg))))))
      (target (expand-file-name
                (file-name-as-directory
                  (read-directory-name "Target Directory: " nil nil nil nil)))))
    (unless (y-or-n-p (format "SOURCE:  %s | TARGET:  %s" source target))
      (let ((debug-on-quit nil))
        (signal 'quit `("You have exited the function."))))
    (start-process "rsync-process"
      nil
      rsync-program
      "--delete"
      "-arzhv"
      source
      target)
    (set-process-filter (get-process "rsync-process") #'rsync-process-filter)
    (set-process-sentinel
      (get-process "rsync-process")
      (lambda (p e)
        (when (= 0 (process-exit-status p))
        (message "Done!"))))))

0

https://github.com/redguardtoo/elpa-mirror tạo một kho lưu trữ cục bộ của tất cả các gói đã cài đặt.

Cách sử dụng rất đơn giản, chỉ cần chạy M-x elpamr-create-mirror-for-installed.

Trên các máy khác, chèn (setq package-archives '(("myelpa" . "~/myelpa/")))vào .emacsvà khởi động lại Emacs.

Bây giờ trên tất cả các máy, bạn có được phiên bản chính xác của các gói.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.