Synchronisieren Sie Pakete zwischen verschiedenen Computern

57

Ich verwende Emacs an verschiedenen Orten und möchte, dass überall ein ähnliches Setup und ähnliche Pakete installiert werden. Ich denke, ich kann ein Versionskontroll-Repository für die Setup-Dateien verwenden. Da ich Prelude benutze , wäre das ~/.emacs.d/personal/.

Ich weiß nicht, wie ich mit Paketen umgehen soll. Gibt es irgendwo eine Datei .emacs.d/mit der Liste der installierten Pakete, mit denen ich Emacs auf anderen Computern erstellen kann, um auch die dort aufgelisteten zu installieren?

El Diego Efe
quelle
2
Für mich sind die installierten Pakete nur ein Teil meines Emacs-Versionskontroll-Repositorys. Ich kompiliere die Pakete mit der ältesten Version von Emacs, die ich verwenden möchte / muss, und füge die .elc-Dateien in das Repository ein. Dies bietet maximale Kontrolle und Konsistenz. Der Kompromiss ist die (relativ) große Größe des Repository. Mein 6 Jahre altes Git-Repository hat eine Größe von 120 MB. Während ich wahrscheinlich mit 1/10 davon durchkommen könnte, wenn ich keine Pakete einbinden würde, machen mir diese wenigen "verschwendeten" Megabytes wirklich keine Sorgen.
Paprika
1
Ich sollte hinzufügen, dass, obwohl ELPA / MELPA / ... heutzutage ziemlich beliebt sind, immer noch nicht alle Pakete über sie erhältlich sind. Wenn Sie also ein Paket verwenden, das manuell installiert werden muss, möchten Sie möglicherweise nicht den Aufwand auf jedem neuen Computer, den Sie verwenden (plus für jedes Paket-Upgrade), wiederholen. Eine einfache Lösung ist das Hinzufügen des Pakets zu Ihrem Emacs-Versionskontroll-Repository.
Paprika
Können Sie nicht einfach vor Ort eine Byte-Kompilierung durchführen und dabei die .elc-Dateien in git ignorieren?
Vamsi
@Vamsi: Ich habe die bytekompilierten Dateien in das Repository gestellt, da einige (nicht ELPA-) Pakete aufgrund ihrer Abhängigkeiten etwas schwierig zu kompilieren sind. Bei diesen wiederhole ich den Vorgang nicht gerne, wenn es nicht nötig ist.
Paprika
@paprika Könnten Sie einige Details angeben, wie Sie Ihr Setup erhalten? Fügen Sie alles in .emacs.d plus .emacs zum Repository hinzu und fertig?
Anfänger

Antworten:

50

Es gibt keine automatisch generierte Manifestdatei, die Sie synchronisieren können, um den gewünschten Effekt zu erzielen.

Sie können jedoch Anrufe zu package-installIhrer emacs-Konfiguration hinzufügen .

(package-install 'auctex)

Die Idee ist, dass dies package-installidempotent ist. Wenn das Paket also bereits vorhanden ist, passiert eigentlich nichts. Angenommen, Sie haben einen solchen Aufruf für jedes von Ihnen verwendete Paket (oder zumindest für die Blätter im Abhängigkeitsdiagramm), der Ihre Pakete effektiv maschinenübergreifend synchronisiert.


Für mehrere Pakete können Sie Folgendes verwenden:

(setq my-package-list '(package1 package2 packageN))
(mapc #'package-install my-package-list)
Sigma
quelle
2
Ich bin überrascht, wie viel einfacher dieser Ansatz im Vergleich zu der hier vorgeschlagenen Lösung ist . Gibt es da einen Unterschied? Das Snippet verwendet auch package-install.
Stenskjaer
1
package.elSeit dieser verknüpften Antwort wurden Änderungen vorgenommen . Es ist möglich, dass zu diesem Zeitpunkt package-installVorgänge an vorhandenen Paketen durchgeführt wurden, nicht nur an deinstallierten Paketen.
Jonathan Leech-Pepin
3
Diese Technik ist leider problematisch - auch wenn das Paketarchiv-Repository zwischen Maschinen einheitlich und in SCM spezifiziert ist. Es wird nicht sichergestellt, dass die Paketversionen zwischen den Maschinen identisch sind. Das Problem ist, dass keine Paketversionen angegeben sind. Diese einzelnen Pakete können im Laufe der Zeit voneinander abweichen und ihre Abhängigkeiten können inkompatibel werden. Dies kann bei aktiven Paketarchiven wie melpa recht einfach passieren.
CTPENROSE
@ctpenrose: Hast du einen Vorschlag, um dieses Problem zu vermeiden?
Student
@student Ich habe das Problem durch die Verwendung von melpa-stable und die seltenere Aktualisierung von Paketen minimiert.
CTPENROSE
34

Ich behalte mein .emacs.d-Verzeichnis in der Versionskontrolle. Dann benutze ich in meinem init.el und nachfolgenden Dateien use-package , um das Paket-Setup zu definieren. Use-package lädt Ihre Pakete nicht nur träge, sondern lädt sie auch bei Bedarf herunter, wenn sie nicht aus den von Ihnen eingerichteten Paket-Repos stammen.

Zum Beispiel verwende ich den Go-Modus, aber nicht auf jedem Computer. In meinem Init.el habe ich folgendes:

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

Dies fügt einen Modus-Hook hinzu, aber was noch wichtiger ist: Durch Angabe :ensure tdieses Hooks wird das Paket bei Bedarf heruntergeladen.

Um eine Maschine synchron zu halten, können Sie einfach aus dem Repo auschecken oder ziehen und Emacs starten. Alle neuen Pakete werden heruntergeladen und installiert.

Elarson
quelle
Dies ist die Lösung, die ich jetzt anstelle von Cask verwende, vor allem, weil Cask (wie T. Verron bemerkte) unter Windows nicht (gut) funktioniert und eine weitere Abhängigkeit darstellt.
Andy
1
Dies ist der Weg, den ich auch benutze, aber anstatt :ensure go-modeden Paketnamen zu wiederholen, können Sie einfach angeben:ensure t
Pedro Luz
Guter Punkt! Dies ist eine alte Antwort. Ich werde es aktualisieren.
Elarson
Sie können auch das :hookSchlüsselwort verwenden, um den Code zu vereinfachen.
Guilherme Salomé
17

In Emacs-25 gibt es die Variable package-selected-packages, sodass Sie diese Variable anpassen und verwenden können package-install-selected-packages, um sicherzustellen, dass sie installiert ist.

Stefan
quelle
Beachten Sie, dass ich es sehe, der Name dieses Befehls ist ein wenig verwirrend. Kann ich es in package-install-selected-packages ändern?
Malabarba,
Angenommen, Sie meinen "Jetzt" anstelle von "Notiz", ja.
Stefan
9

Was Sie verwenden möchten, ist Cask , mit dem Sie eine Cask-Datei erstellen können, die angibt, auf welchen Paketen installiert werden soll cask install. Es kann verwendet werden, um Abhängigkeiten eines Pakets und "Abhängigkeiten" Ihrer Emacs-Konfiguration mühelos zu verwalten. Versetzen Sie Ihre Cask-Datei in den Versionskontrollmodus und installieren / aktualisieren Sie die Pakete pro Computer.

Andy
quelle
4
Es ist erwähnenswert, dass diese Lösung (ab heute) nicht für Windows-Maschinen funktioniert.
T. Verron
1
Ich verwende diese Lösung aus Ihrem eigenen Grund nicht mehr (und dieses Fass ist noch eine weitere Abhängigkeit). Ideal für die Herstellung von Paketen; schrecklich für das Konfigurationsmanagement.
Andy
6

Ein alternativer Ansatz wäre die folgend sein: da ich nur will nicht , dass meine Emacs - Pakete synchronisieren, sondern auch andere Dateien (zB .emacs, .bashrcaber auch andere Verzeichnisse) zwischen meinem Server und meinem Laptop, ich begann zu verwenden unison, um die Dateien zu synchronisieren und verzeichnisse. Wenn ich also an meinem Laptop arbeite, starte ich einfach unison laptopvor allem anderen. Meine ~/.unison/laptop.prfDatei enthält den folgenden Abschnitt für Emacs-bezogene Dateien:

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

Da meine Emacs-Pakete (und auch meine Emacs-Backups und -Lesezeichen) in ~/.emacs.ddiesem Ordner gespeichert sind, ist sichergestellt, dass auf allen meinen Computern alles vorhanden ist.

Ein alternativer Ansatz wäre, das .emacs.dVerzeichnis in ein Verzeichnis zu legen, das mit OwnCloud, DropBox oder einem anderen Dateisynchronisierungsdienst synchronisiert ist, und dann Symlinks von ~/.emacs.ddiesem freigegebenen Verzeichnis zu erstellen .

ph0t0nix
quelle
5

Während dies package.eldie Standardmethode zum Installieren von Paketen ist, möchten Sie möglicherweise auch versuchen, el-getwelche sehr nützlich ist , um Pakete zu installieren, die nicht auf elpa sind (oder nicht sein können). Diese Antwort behandelt das Synchronisieren solcher Pakete.

Wenn Sie mit el-get sicherstellen, dass bestimmte Pakete installiert sind, müssen Sie Ihrer init-Datei Folgendes hinzufügen

(el-get 'sync '(packages))

Dabei sind Pakete die Liste der Pakete, die installiert werden sollen. Diese Funktion ähnelt der package-installInstallation von Paketen nur, wenn diese noch nicht installiert sind. Andernfalls werden die Pakete lediglich initialisiert.

Iqbal Ansari
quelle
5

Ich benutze einen kleinen Trick, der aus dem Emacs-Starter-Kit "gestohlen" wurde (glaube ich):

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

Wenn ich also ein Paket benötige, benutze ich einfach:

(maybe-install-and-require 'magit)

Bei Emacs-Starts, die meine Konfiguration auswerten, package.elwird Magit installiert, wenn es nicht installiert ist.

Meine Konfiguration finden Sie hier:

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

mdallastella
quelle
1
Nach der gleichen Philosophie könnten Sie 'paradox-require' von paradox verwenden
csantosb
3

Ich habe das ~ / emacs-Verzeichnis, das von der Mercurial-Version gesteuert wird und alles enthält, woraus mein Emacs-Setup besteht (~ / emacs / site-lisp für manuell heruntergeladene Bibliotheken, ~ / emacs / elpa für von elpa installierte Bibliotheken, ~ / emacs / etc / für geteilte .emacs ~ / emacs / dot-emacs.el die ich als ~ / .emacs symlinke). Es erforderte einige Anpassungen an einigen Paketen, um alle wichtigen Dateien in diesem Baum zu haben, aber es funktioniert gut. Die wenigen Bits, die maschinenspezifisch sind, habe ich durch Bedingungen für den Systemnamen implementiert.

Nachdem ich etwas installiert / neu konfiguriert / geändert habe, lege ich einfach fest, dass alle Änderungen zwischen allen von mir verwendeten Computern gezogen / verschoben werden sollen.

Ein zusätzlicher Vorteil ist, dass ich eine vollständige Historie meiner Konfiguration habe und zurück / halbieren / zurücksetzen kann, falls etwas schief geht.

PS mercurial scheint besonders geeignet zu sein, da es ein natürliches zweiseitiges Ziehen / Drücken hat, aber ein ähnliches Setup sollte mit Git oder anderen DVDs nicht schwer zu erreichen sein.

Mekk
quelle
3

Ich habe dies setup-packages.elin meinem Emacs-Setup, das eine Mischung aus Code aus Prelude und Tomorokoshis Blog über Paketverwaltung ist .

setup-packages.el macht folgendes:

  • Erstellen Sie ein Verzeichnis für elpaPakete, falls noch keines vorhanden ist, und fügen Sie es und seine Unterverzeichnisse dem hinzu load-path.
  • Update - package-archivesListe mit Melpa.
  • Überprüfen Sie, ob alle in der my-packagesListe aufgeführten Pakete installiert sind. Wenn ein Paket nicht installiert ist, installieren Sie es.

Wie implementieren

  • Speichern Sie das setup-packages.elFolgende in Ihrem ~/.emacs.d/Verzeichnis.
  • Set user-emacs-directory, setup-packages-fileund my-packagesVariablen in Ihrem init.elund tun (load setup-packages-file).

Wenn Sie emacs zum ersten Mal auf einem Computer starten, auf dem diese Pakete nicht installiert sind, werden alle in aufgeführten Pakete my-packagesautomatisch installiert.

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

Sie benötigen Folgendes in Ihrem 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
quelle
2

Um meine Konfiguration zu spiegeln, entschied ich mich für einen anderen Ansatz mit Syncthing . Jede Änderung in einer meiner Konfigurationsdateien wird auf einen anderen PC übertragen, ohne dass ich mich darum kümmern muss. Wenn ich also Pakete aktualisiere, muss ich dies nur auf einem der PCs tun.

csantosb
quelle
2

RSYNC : Synchronisieren Sie ausgewählte Ordner / Dateien rsyncentweder über ein Heimnetzwerk oder über ssheinen Remote-Server.

rsyncist eine Synchronisation Utility Einweg , die in der Lage ist zu löschen Dateien auf dem Ziel, so seien Sie sicher Backup- Daten auf den Quell- und Zielpositionen, und gründlich mit der Test - --dry-runOption, bevor die reale Sache zu tun.

Informationen zum ordnungsgemäßen Konfigurieren der .authinfoDatei finden Sie unter https://www.gnu.org/software/emacs/manual/auth.html. Ein Beispiel für den .authinfoDateiinhalt (der mehrere unterschiedliche Einträge enthalten kann) lautet wie folgt:

machine mymachine login myloginname password mypassword port myport

Konfigurieren und verwenden Sie die Funktion, rsync-remoteum eine Synchronisierung sshmit einem Remoteserver durchzuführen. Oder verwenden Sie die Funktion rsync-local, um auf demselben Computer oder über ein vertrauenswürdiges Heimnetzwerk zu synchronisieren.

(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!"))))))
Gesetzesliste
quelle
0

https://github.com/redguardtoo/elpa-mirror erstellt ein lokales Repository aller installierten Pakete.

Die Bedienung ist einfach, einfach ausführen M-x elpamr-create-mirror-for-installed.

Fügen Sie auf anderen Computern (setq package-archives '(("myelpa" . "~/myelpa/")))in Ihren .emacsEmacs ein und starten Sie ihn neu.

Jetzt erhalten Sie auf allen Maschinen genau dieselbe Version von Paketen.

chen bin
quelle