diff options
Diffstat (limited to 'admin/gitmerge.el')
-rw-r--r-- | admin/gitmerge.el | 132 |
1 files changed, 101 insertions, 31 deletions
diff --git a/admin/gitmerge.el b/admin/gitmerge.el index 1058088cce9..71b1761970c 100644 --- a/admin/gitmerge.el +++ b/admin/gitmerge.el @@ -50,11 +50,22 @@ (defvar gitmerge-skip-regexp ;; We used to include "sync" in there, but in my experience it only ;; caused false positives. --Stef - "back[- ]?port\\|cherry picked from commit\\|\\(do\\( no\\|n['’]\\)t\\|no need to\\) merge\\|\ -re-?generate\\|bump version\\|from trunk\\|Auto-commit" + (let ((skip "back[- ]?port\\|cherry picked from commit\\|\ +\\(do\\( no\\|n['’]\\)t\\|no need to\\) merge\\|\ +bump version\\|Auto-commit")) + (if noninteractive skip + ;; "Regenerate" is quite prone to false positives. + ;; We only want to skip merging things like AUTHORS and ldefs-boot. + ;; These should be covered by "bump version" and "auto-commit". + ;; It doesn't do much harm if we merge one of those files by mistake. + ;; So it's better to err on the side of false negatives. + (concat skip "\\|re-?generate\\|from trunk"))) "Regexp matching logs of revisions that might be skipped. `gitmerge-missing' will ask you if it should skip any matches.") +(defvar gitmerge-minimum-missing 10 + "Minimum number of missing commits to consider merging in batch mode.") + (defvar gitmerge-status-file (expand-file-name "gitmerge-status" user-emacs-directory) "File where missing commits will be saved between sessions.") @@ -67,8 +78,9 @@ re-?generate\\|bump version\\|from trunk\\|Auto-commit" '((t (:strike-through t))) "Face for skipped commits.") -(defconst gitmerge-default-branch "origin/emacs-25" - "Default for branch that should be merged.") +(defvar gitmerge-default-branch nil + "Default for branch that should be merged. +If nil, the function `gitmerge-default-branch' guesses.") (defconst gitmerge-buffer "*gitmerge*" "Working buffer for gitmerge.") @@ -103,6 +115,21 @@ re-?generate\\|bump version\\|from trunk\\|Auto-commit" (defvar gitmerge--commits nil) (defvar gitmerge--from nil) +(defun gitmerge-emacs-version (&optional branch) + "Return the major version of Emacs, optionally in BRANCH." + (with-temp-buffer + (if (not branch) + (insert-file-contents "configure.ac") + (call-process "git" nil t nil "show" (format "%s:configure.ac" branch)) + (goto-char (point-min))) + (re-search-forward "^AC_INIT([^,]+, \\([0-9]+\\)\\.") + (string-to-number (match-string 1)))) + +(defun gitmerge-default-branch () + "Default for branch that should be merged; eg \"origin/emacs-26\"." + (or gitmerge-default-branch + (format "origin/emacs-%s" (1- (gitmerge-emacs-version))))) + (defun gitmerge-get-sha1 () "Get SHA1 from commit at point." (save-excursion @@ -182,11 +209,13 @@ Will detect a default set of skipped revision by looking at cherry mark and search for `gitmerge-skip-regexp'. The result is a list with entries of the form (SHA1 . SKIP), where SKIP denotes if and why this commit should be skipped." + (message "Finding missing commits...") (let (commits) ;; Go through the log and remember all commits that match ;; `gitmerge-skip-regexp' or are marked by --cherry-mark. (with-temp-buffer (call-process "git" nil t nil "log" "--cherry-mark" "--left-only" + "--no-decorate" (concat from "..." (car (vc-git-branches)))) (goto-char (point-max)) (while (re-search-backward "^commit \\(.+\\) \\([0-9a-f]+\\).*" nil t) @@ -203,6 +232,7 @@ if and why this commit should be skipped." (when (re-search-forward gitmerge-skip-regexp nil t) (setcdr (car commits) "R")))))) (delete-region (point) (point-max)))) + (message "Finding missing commits...done") (nreverse commits))) (defun gitmerge-setup-log-buffer (commits from) @@ -291,23 +321,47 @@ Returns non-nil if conflicts remain." ;; (pop-to-buffer (current-buffer)) (debug 'before-resolve) )) ;; Try to resolve the conflicts. - (cond - ((member file '("configure" "lisp/ldefs-boot.el" - "lisp/emacs-lisp/cl-loaddefs.el")) - ;; We are in the file's buffer, so names are relative. - (call-process "git" nil t nil "checkout" "--" - (file-name-nondirectory file)) - (revert-buffer nil 'noconfirm)) - (t - (goto-char (point-max)) - (while (re-search-backward smerge-begin-re nil t) - (save-excursion - (ignore-errors - (smerge-match-conflict) - (smerge-resolve)))) - ;; (when (derived-mode-p 'change-log-mode) - ;; (pop-to-buffer (current-buffer)) (debug 'after-resolve)) - (save-buffer))) + (let (temp) + (cond + ((and (equal file "etc/NEWS") + (ignore-errors + (setq temp + (format "NEWS.%s" + (gitmerge-emacs-version gitmerge--from)))) + (file-exists-p temp) + (or noninteractive + (y-or-n-p "Try to fix NEWS conflict? "))) + (let ((relfile (file-name-nondirectory file)) + (tempfile (make-temp-file "gitmerge"))) + (unwind-protect + (progn + (call-process "git" nil `(:file ,tempfile) nil "diff" + (format ":1:%s" file) + (format ":3:%s" file)) + (call-process "git" nil t nil "reset" "--" relfile) + (call-process "git" nil t nil "checkout" "--" relfile) + (revert-buffer nil 'noconfirm) + (call-process "patch" tempfile nil nil temp) + (call-process "git" nil t nil "add" "--" temp)) + (delete-file tempfile)))) + ;; Generated files. + ((member file '("lisp/ldefs-boot.el")) + ;; We are in the file's buffer, so names are relative. + (call-process "git" nil t nil "reset" "--" + (file-name-nondirectory file)) + (call-process "git" nil t nil "checkout" "--" + (file-name-nondirectory file)) + (revert-buffer nil 'noconfirm)) + (t + (goto-char (point-max)) + (while (re-search-backward smerge-begin-re nil t) + (save-excursion + (ignore-errors + (smerge-match-conflict) + (smerge-resolve)))) + ;; (when (derived-mode-p 'change-log-mode) + ;; (pop-to-buffer (current-buffer)) (debug 'after-resolve)) + (save-buffer)))) (goto-char (point-min)) (prog1 (re-search-forward smerge-begin-re nil t) (unless exists (kill-buffer)))))))) @@ -387,7 +441,9 @@ Throw an user-error if we cannot resolve automatically." (setq conflicted t) ;; Mark as resolved (call-process "git" nil t nil "add" file))) - (when conflicted + (if (not conflicted) + (and files (not (gitmerge-commit)) + (error "Error committing resolution - fix it manually")) (with-current-buffer (get-buffer-create gitmerge-warning-buffer) (erase-buffer) (insert "For the following files, conflicts could\n" @@ -413,6 +469,12 @@ Throw an user-error if we cannot resolve automatically." "diff" "--name-only") (zerop (buffer-size)))) +(defun gitmerge-commit () + "Commit, and return non-nil if it succeeds." + (with-current-buffer (get-buffer-create gitmerge-output-buffer) + (erase-buffer) + (eq 0 (call-process "git" nil t nil "commit" "--no-edit")))) + (defun gitmerge-maybe-resume () "Check if we have to resume a merge. If so, add no longer conflicted files and commit." @@ -425,7 +487,7 @@ If so, add no longer conflicted files and commit." (not (gitmerge-repo-clean))) (user-error "Repository is not clean")) (when statusexist - (if (not (y-or-n-p "Resume merge? ")) + (if (or noninteractive (not (y-or-n-p "Resume merge? "))) (progn (delete-file gitmerge-status-file) ;; No resume. @@ -434,11 +496,8 @@ If so, add no longer conflicted files and commit." (gitmerge-resolve-unmerged) ;; Commit the merge. (when mergehead - (with-current-buffer (get-buffer-create gitmerge-output-buffer) - (erase-buffer) - (unless (zerop (call-process "git" nil t nil - "commit" "--no-edit")) - (error "Git error during merge - fix it manually")))) + (or (gitmerge-commit) + (error "Git error during merge - fix it manually"))) ;; Successfully resumed. t)))) @@ -494,8 +553,12 @@ Branch FROM will be prepended to the list." (list (if (gitmerge-maybe-resume) 'resume - (completing-read "Merge branch: " (gitmerge-get-all-branches) - nil t gitmerge-default-branch)))))) + (if noninteractive + (or (pop command-line-args-left) + (gitmerge-default-branch)) + (completing-read "Merge branch: " + (gitmerge-get-all-branches) + nil t (gitmerge-default-branch)))))))) (let ((default-directory (vc-git-root default-directory))) (if (eq from 'resume) (progn @@ -507,6 +570,12 @@ Branch FROM will be prepended to the list." (setq gitmerge--from from) (when (null gitmerge--commits) (user-error "Nothing to merge")) + (and noninteractive + gitmerge-minimum-missing + (< (length gitmerge--commits) gitmerge-minimum-missing) + (user-error "Number of missing commits (%s) is less than %s" + (length gitmerge--commits) + gitmerge-minimum-missing)) (with-current-buffer (gitmerge-setup-log-buffer gitmerge--commits gitmerge--from) (goto-char (point-min)) @@ -517,7 +586,8 @@ Branch FROM will be prepended to the list." "(C) Detected backport (cherry-mark), (R) Log matches " "regexp, (M) Manually picked\n\n") (gitmerge-mode) - (pop-to-buffer (current-buffer)))))) + (pop-to-buffer (current-buffer)) + (if noninteractive (gitmerge-start-merge)))))) (defun gitmerge-start-merge () (interactive) |