:files files
:update-function update-function)))
+(defun vc-git--current-branch ()
+ (vc-git--out-match '("symbolic-ref" "HEAD")
+ "^\\(refs/heads/\\)?\\(.+\\)$" 2))
+
(defun vc-git-dir--branch-headers ()
"Return headers for branch-related information."
- (let ((branch (vc-git--out-match
- '("symbolic-ref" "HEAD")
- "^\\(refs/heads/\\)?\\(.+\\)$" 2))
+ (let ((branch (vc-git--current-branch))
tracking remote-url)
(if branch
(when-let ((branch-merge
(autoload 'vc-switches "vc")
+(defun vc-git--log-edit-extract-headers (comment)
+ (cl-flet ((boolean-arg-fn (argument)
+ (lambda (v) (and (equal v "yes") (list argument)))))
+ (log-edit-extract-headers
+ `(("Author" . "--author")
+ ("Date" . "--date")
+ ("Amend" . ,(boolean-arg-fn "--amend"))
+ ("No-Verify" . ,(boolean-arg-fn "--no-verify"))
+ ("Sign-Off" . ,(boolean-arg-fn "--signoff")))
+ comment)))
+
(defun vc-git-checkin (files comment &optional _rev)
(let* ((file1 (or (car files) default-directory))
(root (vc-git-root file1))
(vc-git-command nil 0 patch-file "apply" "--cached")
(delete-file patch-file))))
(when to-stash (vc-git--stash-staged-changes files)))
- (cl-flet ((boolean-arg-fn
- (argument)
- (lambda (value) (when (equal value "yes") (list argument)))))
- ;; When operating on the whole tree, better pass "-a" than ".", since "."
- ;; fails when we're committing a merge.
- (apply #'vc-git-command nil 0 (if (and only (not vc-git-patch-string)) files)
- (nconc (if msg-file (list "commit" "-F"
- (file-local-name msg-file))
- (list "commit" "-m"))
- (let ((args
- (log-edit-extract-headers
- `(("Author" . "--author")
- ("Date" . "--date")
- ("Amend" . ,(boolean-arg-fn "--amend"))
- ("No-Verify" . ,(boolean-arg-fn "--no-verify"))
- ("Sign-Off" . ,(boolean-arg-fn "--signoff")))
- comment)))
- (when msg-file
- (let ((coding-system-for-write
- (or pcsw vc-git-commits-coding-system)))
- (write-region (car args) nil msg-file))
- (setq args (cdr args)))
- args)
- (unless vc-git-patch-string
- (if only (list "--only" "--") '("-a"))))))
+ ;; When operating on the whole tree, better pass "-a" than ".",
+ ;; since "." fails when we're committing a merge.
+ (apply #'vc-git-command nil 0
+ (if (and only (not vc-git-patch-string)) files)
+ (nconc (if msg-file (list "commit" "-F"
+ (file-local-name msg-file))
+ (list "commit" "-m"))
+ (let ((args
+ (vc-git--log-edit-extract-headers comment)))
+ (when msg-file
+ (let ((coding-system-for-write
+ (or pcsw vc-git-commits-coding-system)))
+ (write-region (car args) nil msg-file))
+ (setq args (cdr args)))
+ args)
+ (unless vc-git-patch-string
+ (if only (list "--only" "--") '("-a")))))
(if (and msg-file (file-exists-p msg-file)) (delete-file msg-file))
(when to-stash
(let ((cached (make-nearby-temp-file "git-cached")))
(vc-git-command standard-output 1 nil
"log" "--max-count=1" "--pretty=format:%B" rev)))
+(defun vc-git--assert-allowed-rewrite (rev)
+ (when (and (not (eq vc-allow-rewriting-published-history 'no-ask))
+ ;; Check there is an upstream.
+ (with-temp-buffer
+ (vc-git--out-ok "config" "--get"
+ (format "branch.%s.merge"
+ (vc-git--current-branch)))))
+ (let ((outgoing (split-string
+ (with-output-to-string
+ (vc-git-command standard-output 0 nil "log"
+ "--pretty=format:%H"
+ "@{upstream}..HEAD")))))
+ (unless (or (cl-member rev outgoing :test #'string-prefix-p)
+ (and vc-allow-rewriting-published-history
+ (yes-or-no-p
+ (format "Commit %s appears published; allow rewriting history?"
+ rev))))
+ (user-error "Will not rewrite likely-public history; see option `vc-allow-rewriting-published-history'")))))
+
+(defun vc-git-modify-change-comment (files rev comment)
+ (vc-git--assert-allowed-rewrite rev)
+ (let* ((args (vc-git--log-edit-extract-headers comment))
+ (message (format "amend! %s\n\n%s" rev (pop args)))
+ (msg-file
+ ;; On MS-Windows, pass the message through a file, to work
+ ;; around how command line arguments must be in the system
+ ;; codepage, and therefore might not support non-ASCII.
+ ;;
+ ;; As our other arguments are static, we need not be concerned
+ ;; about the encoding of command line arguments in general.
+ ;; See `vc-git-checkin' for the more complex case.
+ (and (eq system-type 'windows-nt)
+ (let ((default-directory
+ (or (file-name-directory (or (car files)
+ default-directory))
+ default-directory)))
+ (make-nearby-temp-file "git-msg")))))
+ (unwind-protect
+ (progn
+ (when (cl-intersection '("--author" "--date") args
+ :test #'string=)
+ ;; 'git rebase --autosquash' cannot alter authorship.
+ ;; See the description of --fixup in git-commit(1).
+ (error
+"Author: and Date: not supported when modifying existing commits"))
+ (when msg-file
+ (let ((coding-system-for-write
+ (or coding-system-for-write
+ vc-git-commits-coding-system)))
+ (write-region message nil msg-file)))
+ ;; Regardless of the state of the index and working tree, this
+ ;; will always create an empty commit, thanks to --only.
+ (apply #'vc-git-command nil 0 nil
+ "commit" "--only" "--allow-empty"
+ (nconc (if msg-file
+ (list "-F" (file-local-name msg-file))
+ (list "-m" message))
+ args)))
+ (when (and msg-file (file-exists-p msg-file))
+ (delete-file msg-file))))
+ (with-environment-variables (("GIT_SEQUENCE_EDITOR" "true"))
+ (vc-git-command nil 0 nil "rebase" "--autostash" "--autosquash" "-i"
+ (format "%s~1" rev))))
+
(defvar vc-git-extra-menu-map
(let ((map (make-sparse-keymap)))
(define-key map [git-grep]
:type 'boolean
:version "27.1")
+;; The default is nil because only a VC user who also possesses a lot of
+;; knowledge specific to the VCS in use can know when it is okay to
+;; rewrite history, and we can't convey to a user who is relatively
+;; naïve regarding the VCS in use the potential risks in only the space
+;; of a minibuffer yes/no prompt.
+;;
+;; See `vc-git--assert-allowed-rewrite' for an example of how to use
+;; this variable in VCS backend code.
+(defcustom vc-allow-rewriting-published-history nil
+ "When non-nil, permit VCS operations that may rewrite published history.
+
+Many VCS commands can change your copy of published change history
+without warning. If this occurs, you won't be able to pull and push in
+the ordinary way until you take special action. For example, for Git,
+see \"Recovering from Upstream Rebase\" in the Man page git-rebase(1).
+
+Normally, Emacs refuses to run VCS commands that it thinks will rewrite
+published history. If you customize this variable to a non-nil value,
+Emacs will instead prompt you to confirm that you really want to perform
+the rewrite. A value of `no-ask' means to proceed with no prompting."
+ :type '(choice (const :tag "Don't allow" nil)
+ (const :tag "Prompt to allow" t)
+ (const :tag "Allow without prompting" no-ask))
+ :version "31.1")
+
\f
;; File property caching