Synchronizuj pakiety między różnymi komputerami

57

Używam emacsa w różnych miejscach i chcę mieć wszędzie podobną konfigurację i pakiety. Chyba mogę użyć repozytorium kontroli wersji dla plików instalacyjnych. Skoro korzystam z Preludium , byłoby to możliwe ~/.emacs.d/personal/.

Nie wiem jak zrobić z pakietami. Czy jest gdzieś plik .emacs.d/z listą zainstalowanych pakietów, których mogę używać do tworzenia emacsa na innych komputerach i instalowania tam wymienionych?

El Diego Efe
źródło
2
Dla mnie zainstalowane pakiety są tylko częścią mojego repozytorium kontroli wersji Emacsa. Kompiluję bajtowo pakiety z najstarszą wersją Emacsa, której chcę / potrzebuję, a także umieszczam pliki .elc w repozytorium. Zapewnia to maksymalną kontrolę i spójność. Kompromis to (stosunkowo) duży rozmiar repozytorium. Moje 6-letnie repozytorium git ma rozmiar 120 MB. Chociaż prawdopodobnie mógłbym uciec z 1/10 tego, gdybym nie dodał pakietów, te kilka „zmarnowanych” megabajtów naprawdę mnie nie martwi.
papryka
1
Powinienem dodać, że chociaż ELPA / MELPA / ... są obecnie dość popularne, wciąż nie wszystkie pakiety są dostępne za ich pośrednictwem. Jeśli więc używasz pakietu wymagającego ręcznej instalacji, możesz nie chcieć replikować nakładów pracy na każdym nowym komputerze, z którego korzystasz (plus przy każdej aktualizacji pakietu). Ponownie łatwym rozwiązaniem jest dodanie pakietu do repozytorium kontroli wersji Emacsa.
papryka
Czy nie możesz po prostu bajtowo skompilować na stronie i uciec od ignorowania plików .elc w git?
Vamsi
@Vamsi: Umieściłem pliki skompilowane w bajtach w repozytorium, ponieważ niektóre pakiety (inne niż ELPA) są nieco skomplikowane w kompilacji ze względu na ich zależności. W przypadku tych nie lubię powtarzać procesu, jeśli nie jest to konieczne.
papryka
@paprika Czy możesz podać szczegółowe informacje na temat uzyskania konfiguracji? Czy dodajesz wszystko do .emacs.d plus .emacs do repozytorium i to wszystko?
Początkujący

Odpowiedzi:

50

Nie ma automatycznie generowanego pliku manifestu, który można zsynchronizować w celu osiągnięcia pożądanego efektu.

To powiedziawszy, możesz coś dodać do package-installsamej konfiguracji emacsa.

(package-install 'auctex)

Chodzi o to, że package-installjest idempotentny, więc jeśli pakiet jest już obecny, nic się nie wydarzy. Zakładając, że masz takie wywołanie dla każdego używanego pakietu (lub przynajmniej liści na wykresie zależności), które skutecznie synchronizowałyby twoje pakiety na różnych komputerach.


W przypadku wielu pakietów można użyć następujących opcji:

(setq my-package-list '(package1 package2 packageN))
(mapc #'package-install my-package-list)
Sigma
źródło
2
Dziwi mnie, o ile prostsze jest to podejście w porównaniu z zaproponowanym tutaj rozwiązaniem . Czy jest jakaś różnica? Fragment również używa package-install.
Stenskjaer
1
package.elOd tej połączonej odpowiedzi nastąpiły zmiany . Możliwe, że w tym czasie package-installwykonywał operacje na istniejących pakietach, a nie tylko odinstalowanych.
Jonathan Leech-Pepin
3
Ta technika jest niestety problematyczna - nawet jeśli repozytorium archiwów pakietów jest jednolite między komputerami i określone w SCM. Nie zapewnia to, że wersje pakietów są identyczne między komputerami. Problem polega na tym, że wersje pakietów nie są określone; te poszczególne pakiety mogą się z czasem zmieniać, a ich zależności mogą stać się niezgodne. Może się to zdarzyć dość łatwo w aktywnych archiwach pakietów, takich jak melpa.
ctpenrose,
@ctpenrose: Czy masz sugestię, aby uniknąć tego problemu?
student
@student Zminimalizowałem problem, używając stabilnej wersji i rzadziej aktualizując pakiety.
ctpenrose
34

Trzymam mój katalog .emacs.d w kontroli wersji. Następnie w moim pliku init.el i kolejnych plikach używam use-package, aby zdefiniować konfigurację pakietu. Pakiet use nie tylko leniwie ładuje twoje pakiety, ale pobiera je na żądanie, jeśli nie istnieją one z repozytoriów pakietów, które skonfigurowałeś.

Na przykład używam trybu go, ale nie na każdym komputerze. W moim init.el mam:

(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)))

Dodaje to tryb przechwytywania, ale co ważniejsze, podając, :ensure tże pobierze pakiet na żądanie.

Aby zsynchronizować maszynę, możesz po prostu pobrać kasę lub pobrać repozytorium i uruchomić Emacsa. Wszelkie nowe pakiety zostaną pobrane i zainstalowane.

Elarson
źródło
Jest to rozwiązanie, którego teraz używam, a nie Cask, przede wszystkim dlatego, że (jak zauważył T. Verron) Cask nie działa (dobrze) w systemie Windows i jest kolejną zależnością.
Andy,
1
Tego też używam, ale zamiast :ensure go-modepowtarzać nazwę pakietu, możesz tylko określić:ensure t
Pedro Luz,
Słuszna uwaga! To stara odpowiedź. Zaktualizuję to.
elarson
Możesz także użyć :hooksłowa kluczowego, aby uprościć kod.
Guilherme Salomé
17

W Emacs-25 jest zmienna package-selected-packages, więc możesz dostosować tę zmienną i użyć, package-install-selected-packagesaby upewnić się, że są one zainstalowane.

Stefan
źródło
Zauważ, że to widzę, nazwa tego polecenia jest nieco myląca. Czy mogę zmienić to na pakiet-zainstaluj-wybrane-pakiety?
Malabarba
Zakładając, że masz na myśli „Teraz” zamiast „Uwaga”, tak.
Stefan
9

To, czego chcesz użyć, to Cask , który pozwala utworzyć plik Cask, określający, na których pakietach zainstalować cask install. Może być używany do łatwego zarządzania zależnościami pakietu i „zależnościami” konfiguracji Emacsa. Umieść swój plik Cask pod kontrolą wersji i instaluj / aktualizuj pakiety dla poszczególnych komputerów.

Andy
źródło
4
Warto zauważyć, że (na dzień dzisiejszy) to rozwiązanie nie działa na komputerach z systemem Windows.
T. Verron
1
Z tego samego powodu nie używam już tego rozwiązania (i że Cask to kolejna zależność). Idealne do robienia paczek; straszne dla zarządzania konfiguracją.
Andy,
6

Alternatywnym rozwiązaniem byłoby następujące: ponieważ nie tylko chcesz synchronizować swoje pakiety Emacsa, ale również inne pliki (np .emacs, .bashrc, ale również inne katalogi) między mój serwer i moim laptopie, zacząłem używać unisondo synchronizacji plików i katalogi. Więc podczas pracy na moim laptopie po prostu biegam unison laptopprzed czymkolwiek innym. Mój ~/.unison/laptop.prfplik ma następującą sekcję dotyczącą plików powiązanych z Emacsem:

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

Ponieważ moje pakiety Emacsa (a także kopie zapasowe i zakładki Emacsa) są w nim przechowywane, ~/.emacs.dto upewnia się, że mam wszystko na wszystkich moich komputerach.

Alternatywnym podejściem byłoby umieszczenie .emacs.dkatalogu w katalogu zsynchronizowanym z OwnCloud, DropBox lub dowolną inną usługą synchronizacji plików, a następnie utworzenie dowiązań symbolicznych ~/.emacs.ddo tego wspólnego katalogu.

ph0t0nix
źródło
5

Chociaż package.eljest to standardowy sposób instalowania pakietów, możesz także spróbować wypróbować el-getto, co jest bardzo przydatne do instalowania pakietów, które nie są (lub nie mogą być) na elpa. Ta odpowiedź dotyczy synchronizacji takich pakietów.

Aby upewnić się, że podane pakiety są instalowane podczas korzystania z el-get, należy dodać do pliku init coś takiego jak poniżej

(el-get 'sync '(packages))

gdzie pakiety to lista pakietów, które chcesz zainstalować. Ta funkcja jest podobna do package-installinstalowania pakietów tylko wtedy, gdy nie są jeszcze zainstalowane, w przeciwnym razie po prostu inicjuje pakiety.

Iqbal Ansari
źródło
5

Używam małej sztuczki „skradzionej” z emacs-starter-kit (tak myślę):

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

Kiedy potrzebuję pakietu, po prostu używam:

(maybe-install-and-require 'magit)

Przy uruchamianiu emacsa, oceniając moją konfigurację, package.elzapewni zainstalowanie magit, jeśli nie zostanie zainstalowany.

Możesz znaleźć moją konfigurację tutaj:

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

mdallastella
źródło
1
Kierując się tą samą filozofią, możesz użyć paradoksu-wymagającego od paradoksu
csantosb
3

Mam katalog ~ / emacs, który jest kontrolowany w wersji mercurial i zawiera wszystko, z czego składa się moja konfiguracja emacs (~ / emacs / site-lisp dla ręcznie pobranych bibliotek, ~ / emacs / elpa dla bibliotek zainstalowanych na elpa, ~ / emacs / etc / dla podzielonych plików .emacs, ~ / emacs / dot-emacs.el, które symbolizuję jako ~ / .emacs). Wymagało to drobnych poprawek niektórych pakietów, aby wszystkie ważne pliki znajdowały się w tym drzewie, ale działa dobrze. Tych kilka bitów specyficznych dla maszyny zaimplementowałem przez warunkowe nazwy systemu.

Więc po zainstalowaniu / ponownej konfiguracji / zmianie czegokolwiek, po prostu zatwierdzam wszystkie zmiany pomiędzy wszystkimi używanymi maszynami.

Dodatkową korzyścią jest to, że mam pełną historię konfiguracji i mogę wrócić / podzielić na dwie części / przywrócić, jeśli coś pójdzie nie tak.

PS mercurial wydaje się szczególnie odpowiedni, ponieważ ma naturalne dwustronne pull / push, ale podobna konfiguracja nie powinna być trudna do osiągnięcia w przypadku git lub jakiegokolwiek innego dvcs.

Mekk
źródło
3

Mam to setup-packages.elw konfiguracji emacsa, która jest hybrydą kodu z Preludium i blogu Tomorokoshi na temat zarządzania pakietami .

setup-packages.el wykonuje następujące czynności:

  • Utwórz katalog dla elpapakietów, jeśli taki nie istnieje i dodaj go wraz z podkatalogami do load-path.
  • Zaktualizuj package-archiveslistę za pomocą Melpy.
  • Sprawdź, czy masz my-packageszainstalowane wszystkie pakiety wymienione na liście. Jeśli pakiet nie jest zainstalowany, zainstaluj go.

Jak wdrożyć

  • Zapisz setup-packages.elponiżej w swoim ~/.emacs.d/katalogu.
  • Ustaw user-emacs-directory, setup-packages-filea my-packageszmienne w twojej init.eli zrobić (load setup-packages-file).

Kiedy uruchomisz emacsa po raz pierwszy na maszynie, która nie ma zainstalowanych tych pakietów, wszystkie pakiety wymienione w my-packageszostaną automatycznie zainstalowane.

setup-packages.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

Potrzebne będą następujące elementy 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
Kaushal Modi
źródło
2

Aby odzwierciedlić moją konfigurację, zdecydowałem się na inne podejście, używając Syncthing ; każda zmiana w dowolnym z moich plików konfiguracyjnych rozprzestrzenia się na dowolny inny komputer bez konieczności dbania o to, więc kiedy aktualizuję pakiety, muszę to zrobić tylko w jednym z komputerów.

csantosb
źródło
2

RSYNC : Synchronizuj wybrane foldery / pliki za pomocą rsyncalbo w sieci domowej, albo za pośrednictwem sshzdalnego serwera.

rsyncto narzędzie do synchronizacji w jedną stronę, które jest w stanie usunąć pliki na obiekcie docelowym, dlatego należy wykonać kopię zapasową danych zarówno w lokalizacji źródłowej, jak i docelowej, i dokładnie przetestować przy użyciu tej --dry-runopcji, zanim zrobisz to w rzeczywistości.

Aby przeczytać o tym, jak poprawnie skonfigurować .authinfoplik, zobacz https://www.gnu.org/software/emacs/manual/auth.html Przykładowa .authinfozawartość pliku (która może zawierać wiele różnych wpisów):

machine mymachine login myloginname password mypassword port myport

Skonfiguruj i użyj tej funkcji rsync-remotedo synchronizacji sshze zdalnym serwerem. Lub użyj funkcji rsync-localdo synchronizacji na tym samym komputerze lub w zaufanej sieci domowej.

(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!"))))))
lista prawnicza
źródło
0

https://github.com/redguardtoo/elpa-mirror tworzy lokalne repozytorium wszystkich zainstalowanych pakietów.

Użycie jest proste, wystarczy uruchomić M-x elpamr-create-mirror-for-installed.

Na innych komputerach włóż (setq package-archives '(("myelpa" . "~/myelpa/")))do swojego .emacsi uruchom ponownie Emacsa.

Teraz na wszystkich komputerach otrzymujesz dokładnie taką samą wersję pakietów.

Chen Bin
źródło