Fix test-patchbomb for crew

This commit is contained in:
Brendan Cully 2007-05-08 13:08:20 -07:00
commit 6fae8c6b65
5 changed files with 324 additions and 23 deletions

View File

@ -250,6 +250,33 @@ enum cmdline {
};
/*
* attempt to verify that a directory is really a hg repo, by testing
* for the existence of a subdirectory.
*/
static int validate_repo(const char *repo_root, const char *subdir)
{
char *abs_path;
struct stat st;
int ret;
if (asprintf(&abs_path, "%s.hg/%s", repo_root, subdir) == -1) {
ret = -1;
goto bail;
}
/* verify that we really are looking at valid repo. */
if (stat(abs_path, &st) == -1) {
ret = 0;
} else {
ret = 1;
}
bail:
return ret;
}
/*
* paranoid wrapper, runs hg executable in server mode.
*/
@ -259,7 +286,6 @@ static void serve_data(int argc, char **argv)
char *repo, *repo_root;
enum cmdline cmd;
char *nargv[6];
struct stat st;
size_t repolen;
int i;
@ -315,15 +341,23 @@ static void serve_data(int argc, char **argv)
/* only hg init expects no repo. */
if (cmd != hg_init) {
char *abs_path;
int valid;
if (asprintf(&abs_path, "%s.hg/data", repo_root) == -1) {
valid = validate_repo(repo_root, "data");
if (valid == -1) {
goto badargs;
}
if (valid == 0) {
valid = validate_repo(repo_root, "store");
/* verify that we really are looking at valid repo. */
if (stat(abs_path, &st) == -1) {
if (valid == -1) {
goto badargs;
}
}
if (valid == 0) {
perror(repo);
exit(EX_DATAERR);
}

View File

@ -36,6 +36,16 @@
:type 'sexp
:group 'mercurial)
(defcustom mq-edit-finish-hook nil
"Hook run before a patch description is finished up with."
:type 'sexp
:group 'mercurial)
(defcustom mq-signoff-address nil
"Address with which to sign off on a patch."
:type 'string
:group 'mercurial)
;;; Internal variables.
@ -62,10 +72,14 @@
(define-key mq-global-map ">" 'mq-push-all)
(define-key mq-global-map "," 'mq-pop)
(define-key mq-global-map "<" 'mq-pop-all)
(define-key mq-global-map "=" 'mq-diff)
(define-key mq-global-map "r" 'mq-refresh)
(define-key mq-global-map "e" 'mq-refresh-edit)
(define-key mq-global-map "i" 'mq-new)
(define-key mq-global-map "n" 'mq-next)
(define-key mq-global-map "o" 'mq-signoff)
(define-key mq-global-map "p" 'mq-previous)
(define-key mq-global-map "s" 'mq-edit-series)
(define-key mq-global-map "t" 'mq-top)
(add-minor-mode 'mq-mode 'mq-mode)
@ -76,16 +90,17 @@
(defvar mq-edit-mode-map (make-sparse-keymap))
(define-key mq-edit-mode-map "\C-c\C-c" 'mq-edit-finish)
(define-key mq-edit-mode-map "\C-c\C-k" 'mq-edit-kill)
(define-key mq-edit-mode-map "\C-c\C-s" 'mq-signoff)
;;; Helper functions.
(defun mq-read-patch-name (&optional source prompt)
(defun mq-read-patch-name (&optional source prompt force)
"Read a patch name to use with a command.
May return nil, meaning \"use the default\"."
(let ((patches (split-string
(hg-chomp (hg-run0 (or source "qseries"))) "\n")))
(when current-prefix-arg
(when force
(completing-read (format "Patch%s: " (or prompt ""))
(map 'list 'cons patches patches)
nil
@ -120,7 +135,8 @@ May return nil, meaning \"use the default\"."
(defun mq-push (&optional patch)
"Push patches until PATCH is reached.
If PATCH is nil, push at most one patch."
(interactive (list (mq-read-patch-name "qunapplied" " to push")))
(interactive (list (mq-read-patch-name "qunapplied" " to push"
current-prefix-arg)))
(let ((root (hg-root))
(prev-buf (current-buffer))
last-line ok)
@ -138,7 +154,8 @@ If PATCH is nil, push at most one patch."
(if patch (list patch))))
last-line (mq-last-line))
(let ((lines (count-lines (point-min) (point-max))))
(if (and (equal lines 2) (string-match "Now at:" last-line))
(if (or (<= lines 1)
(and (equal lines 2) (string-match "Now at:" last-line)))
(progn
(kill-buffer (current-buffer))
(delete-window))
@ -158,7 +175,8 @@ If PATCH is nil, push at most one patch."
(defun mq-pop (&optional patch)
"Pop patches until PATCH is reached.
If PATCH is nil, pop at most one patch."
(interactive (list (mq-read-patch-name "qapplied" " to pop to")))
(interactive (list (mq-read-patch-name "qapplied" " to pop to"
current-prefix-arg)))
(let ((root (hg-root))
last-line ok)
(unless root
@ -192,13 +210,14 @@ If PATCH is nil, pop at most one patch."
(message "Refreshing %s... done." patch)
(error "Refreshing %s... %s" patch (hg-chomp (cdr ret)))))))
(defun mq-refresh ()
"Refresh the topmost applied patch."
(interactive)
(defun mq-refresh (&optional git)
"Refresh the topmost applied patch.
With a prefix argument, generate a git-compatible patch."
(interactive "P")
(let ((root (hg-root)))
(unless root
(error "Cannot refresh outside of a repository!"))
(mq-refresh-internal root)))
(apply 'mq-refresh-internal root (if git '("--git")))))
(defun mq-patch-info (cmd &optional msg)
(let* ((ret (hg-run cmd))
@ -231,6 +250,7 @@ This would become the active patch if popped to."
(unless (equal (mq-patch-info "qtop") mq-top)
(error "Topmost patch has changed!"))
(hg-sync-buffers hg-root)
(run-hooks 'mq-edit-finish-hook)
(mq-refresh-internal hg-root "-m" (buffer-substring (point-min) (point-max)))
(let ((buf mq-prev-buffer))
(kill-buffer nil)
@ -318,6 +338,69 @@ Key bindings
(cd root)))
(message "Type `C-c C-c' to finish editing and refresh the patch."))
(defun mq-new (name)
"Create a new empty patch named NAME.
The patch is applied on top of the current topmost patch.
With a prefix argument, forcibly create the patch even if the working
directory is modified."
(interactive (list (mq-read-patch-name "qseries" " to create" t)))
(message "Creating patch...")
(let ((ret (if current-prefix-arg
(hg-run "qnew" "-f" name)
(hg-run "qnew" name))))
(if (equal (car ret) 0)
(progn
(hg-update-mode-lines (buffer-file-name))
(message "Creating patch... done."))
(error "Creating patch... %s" (hg-chomp (cdr ret))))))
(defun mq-edit-series ()
"Edit the MQ series file directly."
(interactive)
(let ((root (hg-root)))
(unless root
(error "Not in an MQ repository!"))
(find-file (concat root ".hg/patches/series"))))
(defun mq-diff (&optional git)
"Display a diff of the topmost applied patch.
With a prefix argument, display a git-compatible diff."
(interactive "P")
(hg-view-output ((format "MQ: Diff of %s" (mq-patch-info "qtop")))
(if git
(call-process (hg-binary) nil t nil "qdiff" "--git")
(call-process (hg-binary) nil t nil "qdiff"))
(diff-mode)
(font-lock-fontify-buffer)))
(defun mq-signoff ()
"Sign off on the current patch, in the style used by the Linux kernel.
If the variable mq-signoff-address is non-nil, it will be used, otherwise
the value of the ui.username item from your hgrc will be used."
(interactive)
(let ((was-editing (eq major-mode 'mq-edit-mode))
signed)
(unless was-editing
(mq-refresh-edit))
(save-excursion
(let* ((user (or mq-signoff-address
(hg-run0 "debugconfig" "ui.username")))
(signoff (concat "Signed-off-by: " user)))
(if (search-forward signoff nil t)
(message "You have already signed off on this patch.")
(goto-char (point-max))
(let ((case-fold-search t))
(if (re-search-backward "^Signed-off-by: " nil t)
(forward-line 1)
(insert "\n")))
(insert signoff)
(message "%s" signoff)
(setq signed t))))
(unless was-editing
(if signed
(mq-edit-finish)
(mq-edit-kill)))))
(provide 'mq)

View File

@ -435,7 +435,27 @@ class queue:
return (True, files, fuzz)
def apply(self, repo, series, list=False, update_status=True,
strict=False, patchdir=None, merge=None, wlock=None):
strict=False, patchdir=None, merge=None, wlock=None,
all_files={}):
tr = repo.transaction()
try:
ret = self._apply(tr, repo, series, list, update_status,
strict, patchdir, merge, wlock,
all_files=all_files)
tr.close()
self.save_dirty()
return ret
except:
try:
tr.abort()
finally:
repo.reload()
repo.wreload()
raise
def _apply(self, tr, repo, series, list=False, update_status=True,
strict=False, patchdir=None, merge=None, wlock=None,
all_files={}):
# TODO unify with commands.py
if not patchdir:
patchdir = self.path
@ -443,7 +463,6 @@ class queue:
if not wlock:
wlock = repo.wlock()
lock = repo.lock()
tr = repo.transaction()
n = None
for patchname in series:
pushable, reason = self.pushable(patchname)
@ -468,6 +487,7 @@ class queue:
message = '\n'.join(message)
(patcherr, files, fuzz) = self.patch(repo, pf)
all_files.update(files)
patcherr = not patcherr
if merge and files:
@ -506,7 +526,6 @@ class queue:
self.ui.warn("fuzz found when applying patch, stopping\n")
err = 1
break
tr.close()
self.removeundo(repo)
return (err, n)
@ -860,10 +879,25 @@ class queue:
else:
end = self.series.index(patch, start) + 1
s = self.series[start:end]
if mergeq:
ret = self.mergepatch(repo, mergeq, s, wlock)
else:
ret = self.apply(repo, s, list, wlock=wlock)
all_files = {}
try:
if mergeq:
ret = self.mergepatch(repo, mergeq, s, wlock)
else:
ret = self.apply(repo, s, list, wlock=wlock,
all_files=all_files)
except:
self.ui.warn(_('cleaning up working directory...'))
node = repo.dirstate.parents()[0]
hg.revert(repo, node, None, wlock)
unknown = repo.status(wlock=wlock)[4]
# only remove unknown files that we know we touched or
# created while patching
for f in unknown:
if f in all_files:
util.unlink(repo.wjoin(f))
self.ui.warn(_('done\n'))
raise
top = self.applied[-1].name
if ret[0]:
self.ui.write("Errors during apply, please fix and refresh %s\n" %
@ -1837,7 +1871,6 @@ def push(ui, repo, patch=None, **opts):
ui.warn("merging with queue at: %s\n" % mergeq.path)
ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
mergeq=mergeq)
q.save_dirty()
return ret
def pop(ui, repo, patch=None, **opts):

17
tests/test-patchbomb Executable file
View File

@ -0,0 +1,17 @@
#!/bin/sh
echo "[extensions]" >> $HGRCPATH
echo "patchbomb=" >> $HGRCPATH
hg init
echo a > a
hg commit -Ama -d '1 0'
hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar tip | \
sed -e 's/\(Message-Id:.*@\).*/\1/'
echo b > b
hg commit -Amb -d '2 0'
hg email --date '1970-1-1 0:2' -n -f quux -t foo -c bar -s test 0:tip | \
sed -e 's/\(Message-Id:.*@\|In-Reply-To:.*@\).*/\1/'

134
tests/test-patchbomb.out Normal file
View File

@ -0,0 +1,134 @@
adding a
hg email: option --date not recognized
hg email [OPTION]... [DEST]...
send changesets by email
By default, diffs are sent in the format generated by hg export,
one per message. The series starts with a "[PATCH 0 of N]"
introduction, which describes the series as a whole.
Each patch email has a Subject line of "[PATCH M of N] ...", using
the first line of the changeset description as the subject text.
The message contains two or three body parts. First, the rest of
the changeset description. Next, (optionally) if the diffstat
program is installed, the result of running diffstat on the patch.
Finally, the patch itself, as generated by "hg export".
With --outgoing, emails will be generated for patches not
found in the destination repository (or only those which are
ancestors of the specified revisions if any are provided)
With --bundle, changesets are selected as for --outgoing,
but a single email containing a binary Mercurial bundle as an
attachment will be sent.
Examples:
hg email -r 3000 # send patch 3000 only
hg email -r 3000 -r 3001 # send patches 3000 and 3001
hg email -r 3000:3005 # send patches 3000 through 3005
hg email 3000 # send patch 3000 (deprecated)
hg email -o # send all patches not in default
hg email -o DEST # send all patches not in DEST
hg email -o -r 3000 # send all ancestors of 3000 not in default
hg email -o -r 3000 DEST # send all ancestors of 3000 not in DEST
hg email -b # send bundle of all patches not in default
hg email -b DEST # send bundle of all patches not in DEST
hg email -b -r 3000 # bundle of all ancestors of 3000 not in default
hg email -b -r 3000 DEST # bundle of all ancestors of 3000 not in DEST
Before using this command, you will need to enable email in your hgrc.
See the [email] section in hgrc(5) for details.
options:
-a --attach send patches as inline attachments
--bcc email addresses of blind copy recipients
-c --cc email addresses of copy recipients
-d --diffstat add diffstat output to messages
-g --git use git extended diff format
-f --from email address of sender
--plain omit hg patch header
-n --test print messages that would be sent
-m --mbox write messages to mbox file instead of sending them
-o --outgoing send changes not found in the target repository
-b --bundle send changes not in target as a binary bundle
-r --rev a revision to send
-s --subject subject of first message (intro or single patch)
-t --to email addresses of recipients
--force run even when remote repository is unrelated (with -b)
--base a base changeset to specify instead of a destination (with -b)
-e --ssh specify ssh command to use
--remotecmd specify hg command to run on the remote side
use "hg -v help email" to show global options
adding b
hg email: option --date not recognized
hg email [OPTION]... [DEST]...
send changesets by email
By default, diffs are sent in the format generated by hg export,
one per message. The series starts with a "[PATCH 0 of N]"
introduction, which describes the series as a whole.
Each patch email has a Subject line of "[PATCH M of N] ...", using
the first line of the changeset description as the subject text.
The message contains two or three body parts. First, the rest of
the changeset description. Next, (optionally) if the diffstat
program is installed, the result of running diffstat on the patch.
Finally, the patch itself, as generated by "hg export".
With --outgoing, emails will be generated for patches not
found in the destination repository (or only those which are
ancestors of the specified revisions if any are provided)
With --bundle, changesets are selected as for --outgoing,
but a single email containing a binary Mercurial bundle as an
attachment will be sent.
Examples:
hg email -r 3000 # send patch 3000 only
hg email -r 3000 -r 3001 # send patches 3000 and 3001
hg email -r 3000:3005 # send patches 3000 through 3005
hg email 3000 # send patch 3000 (deprecated)
hg email -o # send all patches not in default
hg email -o DEST # send all patches not in DEST
hg email -o -r 3000 # send all ancestors of 3000 not in default
hg email -o -r 3000 DEST # send all ancestors of 3000 not in DEST
hg email -b # send bundle of all patches not in default
hg email -b DEST # send bundle of all patches not in DEST
hg email -b -r 3000 # bundle of all ancestors of 3000 not in default
hg email -b -r 3000 DEST # bundle of all ancestors of 3000 not in DEST
Before using this command, you will need to enable email in your hgrc.
See the [email] section in hgrc(5) for details.
options:
-a --attach send patches as inline attachments
--bcc email addresses of blind copy recipients
-c --cc email addresses of copy recipients
-d --diffstat add diffstat output to messages
-g --git use git extended diff format
-f --from email address of sender
--plain omit hg patch header
-n --test print messages that would be sent
-m --mbox write messages to mbox file instead of sending them
-o --outgoing send changes not found in the target repository
-b --bundle send changes not in target as a binary bundle
-r --rev a revision to send
-s --subject subject of first message (intro or single patch)
-t --to email addresses of recipients
--force run even when remote repository is unrelated (with -b)
--base a base changeset to specify instead of a destination (with -b)
-e --ssh specify ssh command to use
--remotecmd specify hg command to run on the remote side
use "hg -v help email" to show global options