diff options
| author | Craig Jennings <c@cjennings.net> | 2025-10-20 17:18:13 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2025-10-20 17:18:13 -0500 |
| commit | afa913e3fd4715141d4d750aef3f1130effe1109 (patch) | |
| tree | 2b4358446247b7e0f28141c760ddfc5a3a437c8c | |
| parent | ecb6bdf40349faba2e43918a06fce2ad8626fb44 (diff) | |
feat:reconcile-open-repos: enhance git repository reconciliation
Improve the workflow for synchronizing git repositories across
projects. Add detailed commentary and handle edge cases for stash,
pull, and stash pop operations. Implement checks for project
directories and dependencies defined in init.el, enhancing
robustness against undefined variables and failed git operations.
Ensure Magit opens for manual intervention when issues arise.
| -rw-r--r-- | modules/reconcile-open-repos.el | 78 |
1 files changed, 53 insertions, 25 deletions
diff --git a/modules/reconcile-open-repos.el b/modules/reconcile-open-repos.el index ea22dda8..648de222 100644 --- a/modules/reconcile-open-repos.el +++ b/modules/reconcile-open-repos.el @@ -1,29 +1,47 @@ ;;; reconcile-open-repos.el --- reconcile open repos -*- lexical-binding: t; coding: utf-8; -*- ;; author: Craig Jennings <c@cjennings.net> +;; ;;; Commentary: -;; I have tried to keep this Emacs config as general as possible, and move all -;; config related to my personal workflows here. - -;; I typically work with multiple git repositories the day, and can forget to -;; commit uncommitted work. Also, I often forget to start a session by pulling -;; changes. So, making it a habit to run cj/check-for-open-work when starting -;; and ending the work session allows me to know that I've iterated through all -;; git repositories in my projects and code directories and have reconciled all +;; +;; Git repository reconciliation workflow for multiple projects. Ensures all git +;; repositories in your projects/ and code/ directories are synchronized with +;; remotes and have no uncommitted work at the start and end of work sessions. +;; The workflow iterates through all git repositories in projects-dir and +;; code-dir, skips local-only repos and http/https remotes (reference clones), +;; silently pulls latest changes for clean repos, and for dirty repos stashes +;; changes, pulls, pops stash, and opens Magit for review. Also checks org-dir +;; and user-emacs-directory individually. +;; +;; Main function: cj/check-for-open-work (bound to M-P) +;; +;; Dependencies: Requires projects-dir, code-dir, and org-dir to be defined in +;; init.el. Uses Magit for manual intervention when repositories have uncommitted ;; changes. ;;; Code: +;; Forward declarations for variables defined in init.el +(eval-when-compile + (defvar projects-dir) + (defvar code-dir) + (defvar org-dir)) + +;; Forward declaration for magit +(declare-function magit-status "magit" (&optional directory cache)) + ;; -------------------------- Reconcile Git Directory -------------------------- (defun cj/reconcile-git-directory (directory) - "Reconcile unopened work in a git project directory leveraging magit." + "Reconcile unopened work in a git project DIRECTORY. +Skips local-only repos and http/https remotes. For clean repos, silently pulls +latest changes. For dirty repos, stashes changes, pulls, pops stash, and opens +Magit for review." (message "checking: %s" directory) (let ((default-directory directory)) ;; Check for the presence of the .git directory (if (file-directory-p (expand-file-name ".git" directory)) (progn - (let ((remote-url (shell-command-to-string "git config --get remote.origin.url"))) - (setq remote-url (string-trim remote-url)) + (let ((remote-url (string-trim (shell-command-to-string "git config --get remote.origin.url")))) ;; skip local git repos, or remote URLs that are http or https, ;; these are typically cloned for reference only @@ -33,40 +51,50 @@ ;; if git directory is clean, pulling generates no errors (if (string-empty-p (shell-command-to-string "git status --porcelain")) (progn - ;; (message "%s is a clean git repository" directory) - (shell-command "git pull --quiet")) + (let ((pull-result (shell-command "git pull --quiet"))) + (unless (= pull-result 0) + (message "Warning: git pull failed for %s (exit code: %d)" directory pull-result)))) ;; if directory not clean, pull latest changes and display Magit for manual intervention (progn (message "%s contains uncommitted work" directory) - (shell-command "git stash --quiet") - (shell-command "git pull --quiet") - (shell-command "git stash pop --quiet") - (call-interactively #'magit-status) - ;; pause until magit buffer is closed - (while (buffer-live-p (get-buffer (format "*magit: %s*" (file-name-nondirectory directory)))) - (sit-for 0.5)))))))))) + (let ((stash-result (shell-command "git stash --quiet"))) + (if (= stash-result 0) + (progn + (let ((pull-result (shell-command "git pull --quiet"))) + (if (= pull-result 0) + (let ((stash-pop-result (shell-command "git stash pop --quiet"))) + (unless (= stash-pop-result 0) + (message "Warning: git stash pop failed for %s - opening Magit" directory))) + (message "Warning: git pull failed for %s - opening Magit" directory))) + (magit-status directory)) + (message "Warning: git stash failed for %s - opening Magit" directory) + (magit-status directory))))))))))) ;; ---------------------------- Check For Open Work ---------------------------- +;;;###autoload (defun cj/check-for-open-work () "Check all project directories for open work." (interactive) ;; these are constants defined in init.el ;; children of these directories will be checked (dolist (base-dir (list projects-dir code-dir)) - (when (file-directory-p base-dir) + (when (and (boundp 'base-dir) base-dir (file-directory-p base-dir)) (dolist (child-dir (directory-files base-dir t "^[^.]+$" 'nosort)) (when (file-directory-p child-dir) (cj/reconcile-git-directory child-dir))))) ;; check these directories individually - (cj/reconcile-git-directory org-dir) - (cj/reconcile-git-directory user-emacs-directory) + (when (and (boundp 'org-dir) org-dir (file-directory-p org-dir)) + (cj/reconcile-git-directory org-dir)) + (when (and (boundp 'user-emacs-directory) user-emacs-directory (file-directory-p user-emacs-directory)) + (cj/reconcile-git-directory user-emacs-directory)) ;; communicate when finished. - (message "Complete. All project repositories checked for uncommitted work and code updated from remote repository")) -(global-set-key (kbd "M-P") 'cj/check-for-open-work) + (message "Complete. All repositories checked and updated")) + +;;;###autoload (keymap-global-set "M-P" #'cj/check-for-open-work) (provide 'reconcile-open-repos) ;;; reconcile-open-repos.el ends here. |
