mirror of
https://github.com/facebook/sapling.git
synced 2024-10-08 07:49:11 +03:00
merge with crew.
This commit is contained in:
commit
74c20c0bae
@ -2,36 +2,36 @@ shopt -s extglob
|
||||
|
||||
_hg_command_list()
|
||||
{
|
||||
hg --debug help 2>/dev/null | \
|
||||
"$hg" --debug help 2>/dev/null | \
|
||||
awk 'function command_line(line) {
|
||||
gsub(/,/, "", line)
|
||||
gsub(/:.*/, "", line)
|
||||
split(line, aliases)
|
||||
command = aliases[1]
|
||||
delete aliases[1]
|
||||
print command
|
||||
for (i in aliases)
|
||||
if (index(command, aliases[i]) != 1)
|
||||
print aliases[i]
|
||||
}
|
||||
/^list of commands:/ {commands=1}
|
||||
commands && /^ debug/ {a[i++] = $0; next;}
|
||||
commands && /^ [^ ]/ {command_line($0)}
|
||||
/^global options:/ {exit 0}
|
||||
END {for (i in a) command_line(a[i])}'
|
||||
gsub(/,/, "", line)
|
||||
gsub(/:.*/, "", line)
|
||||
split(line, aliases)
|
||||
command = aliases[1]
|
||||
delete aliases[1]
|
||||
print command
|
||||
for (i in aliases)
|
||||
if (index(command, aliases[i]) != 1)
|
||||
print aliases[i]
|
||||
}
|
||||
/^list of commands:/ {commands=1}
|
||||
commands && /^ debug/ {a[i++] = $0; next;}
|
||||
commands && /^ [^ ]/ {command_line($0)}
|
||||
/^global options:/ {exit 0}
|
||||
END {for (i in a) command_line(a[i])}'
|
||||
|
||||
}
|
||||
|
||||
_hg_option_list()
|
||||
{
|
||||
hg -v help $1 2> /dev/null | \
|
||||
awk '/^ *-/ {
|
||||
for (i = 1; i <= NF; i ++) {
|
||||
"$hg" -v help $1 2>/dev/null | \
|
||||
awk '/^ *-/ {
|
||||
for (i = 1; i <= NF; i ++) {
|
||||
if (index($i, "-") != 1)
|
||||
break;
|
||||
break;
|
||||
print $i;
|
||||
}
|
||||
}'
|
||||
}
|
||||
}'
|
||||
}
|
||||
|
||||
|
||||
@ -56,29 +56,29 @@ _hg_commands()
|
||||
|
||||
_hg_paths()
|
||||
{
|
||||
local paths="$(hg paths 2> /dev/null | sed -e 's/ = .*$//')"
|
||||
COMPREPLY=(${COMPREPLY[@]:-} $( compgen -W '$paths' -- "$cur" ))
|
||||
local paths="$("$hg" paths 2>/dev/null | sed -e 's/ = .*$//')"
|
||||
COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$paths' -- "$cur"))
|
||||
}
|
||||
|
||||
_hg_repos()
|
||||
{
|
||||
local i
|
||||
for i in $( compgen -d -- "$cur" ); do
|
||||
test ! -d "$i"/.hg || COMPREPLY=(${COMPREPLY[@]:-} "$i")
|
||||
for i in $(compgen -d -- "$cur"); do
|
||||
test ! -d "$i"/.hg || COMPREPLY=(${COMPREPLY[@]:-} "$i")
|
||||
done
|
||||
}
|
||||
|
||||
_hg_status()
|
||||
{
|
||||
local files="$( hg status -n$1 . 2> /dev/null)"
|
||||
COMPREPLY=(${COMPREPLY[@]:-} $( compgen -W '$files' -- "$cur" ))
|
||||
local files="$("$hg" status -n$1 . 2>/dev/null)"
|
||||
COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur"))
|
||||
}
|
||||
|
||||
_hg_tags()
|
||||
{
|
||||
local tags="$(hg tags 2> /dev/null |
|
||||
sed -e 's/[0-9]*:[a-f0-9]\{40\}$//; s/ *$//')"
|
||||
COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -W '$tags' -- "$cur") )
|
||||
local tags="$("$hg" tags 2>/dev/null |
|
||||
sed -e 's/[0-9]*:[a-f0-9]\{40\}$//; s/ *$//')"
|
||||
COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$tags' -- "$cur"))
|
||||
}
|
||||
|
||||
# this is "kind of" ugly...
|
||||
@ -87,7 +87,7 @@ _hg_count_non_option()
|
||||
local i count=0
|
||||
local filters="$1"
|
||||
|
||||
for (( i=1; $i<=$COMP_CWORD; i++ )); do
|
||||
for ((i=1; $i<=$COMP_CWORD; i++)); do
|
||||
if [[ "${COMP_WORDS[i]}" != -* ]]; then
|
||||
if [[ ${COMP_WORDS[i-1]} == @($filters|$global_args) ]]; then
|
||||
continue
|
||||
@ -104,6 +104,7 @@ _hg()
|
||||
local cur prev cmd opts i
|
||||
# global options that receive an argument
|
||||
local global_args='--cwd|-R|--repository'
|
||||
local hg="$1"
|
||||
|
||||
COMPREPLY=()
|
||||
cur="$2"
|
||||
@ -112,7 +113,7 @@ _hg()
|
||||
# searching for the command
|
||||
# (first non-option argument that doesn't follow a global option that
|
||||
# receives an argument)
|
||||
for (( i=1; $i<=$COMP_CWORD; i++ )); do
|
||||
for ((i=1; $i<=$COMP_CWORD; i++)); do
|
||||
if [[ ${COMP_WORDS[i]} != -* ]]; then
|
||||
if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then
|
||||
cmd="${COMP_WORDS[i]}"
|
||||
@ -124,7 +125,7 @@ _hg()
|
||||
if [[ "$cur" == -* ]]; then
|
||||
opts=$(_hg_option_list $cmd)
|
||||
|
||||
COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -W '$opts' -- "$cur") )
|
||||
COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$opts' -- "$cur"))
|
||||
return
|
||||
fi
|
||||
|
||||
@ -146,7 +147,7 @@ _hg()
|
||||
fi
|
||||
|
||||
# canonicalize command name
|
||||
cmd=$(hg -q help "$cmd" 2> /dev/null | sed -e 's/^hg //; s/ .*//; 1q')
|
||||
cmd=$("$hg" -q help "$cmd" 2>/dev/null | sed -e 's/^hg //; s/ .*//; 1q')
|
||||
|
||||
if [ "$cmd" != status ] && [ "$prev" = -r ] || [ "$prev" = --rev ]; then
|
||||
_hg_tags
|
||||
@ -190,17 +191,17 @@ _hg()
|
||||
if [ $count = 1 ]; then
|
||||
_hg_paths
|
||||
fi
|
||||
_hg_repos
|
||||
_hg_repos
|
||||
;;
|
||||
debugindex|debugindexdot)
|
||||
COMPREPLY=(${COMPREPLY[@]:-} $( compgen -f -X "!*.i" -- "$cur" ))
|
||||
COMPREPLY=(${COMPREPLY[@]:-} $(compgen -f -X "!*.i" -- "$cur"))
|
||||
;;
|
||||
debugdata)
|
||||
COMPREPLY=(${COMPREPLY[@]:-} $( compgen -f -X "!*.d" -- "$cur" ))
|
||||
COMPREPLY=(${COMPREPLY[@]:-} $(compgen -f -X "!*.d" -- "$cur"))
|
||||
;;
|
||||
esac
|
||||
|
||||
}
|
||||
|
||||
complete -o bashdefault -o default -F _hg hg 2> /dev/null \
|
||||
complete -o bashdefault -o default -F _hg hg 2>/dev/null \
|
||||
|| complete -o default -F _hg hg
|
||||
|
203
hgext/gpg.py
203
hgext/gpg.py
@ -1,6 +1,14 @@
|
||||
import os, tempfile, binascii, errno
|
||||
# GnuPG signing extension for Mercurial
|
||||
#
|
||||
# Copyright 2005, 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
|
||||
#
|
||||
# This software may be used and distributed according to the terms
|
||||
# of the GNU General Public License, incorporated herein by reference.
|
||||
|
||||
import os, tempfile, binascii
|
||||
from mercurial import util
|
||||
from mercurial import node as hgnode
|
||||
from mercurial.i18n import gettext as _
|
||||
|
||||
class gpg:
|
||||
def __init__(self, path, key=None):
|
||||
@ -14,6 +22,7 @@ class gpg:
|
||||
def verify(self, data, sig):
|
||||
""" returns of the good and bad signatures"""
|
||||
try:
|
||||
# create temporary files
|
||||
fd, sigfile = tempfile.mkstemp(prefix="hggpgsig")
|
||||
fp = os.fdopen(fd, 'wb')
|
||||
fp.write(sig)
|
||||
@ -22,8 +31,8 @@ class gpg:
|
||||
fp = os.fdopen(fd, 'wb')
|
||||
fp.write(data)
|
||||
fp.close()
|
||||
gpgcmd = "%s --logger-fd 1 --status-fd 1 --verify \"%s\" \"%s\"" % (self.path, sigfile, datafile)
|
||||
#gpgcmd = "%s --status-fd 1 --verify \"%s\" \"%s\"" % (self.path, sigfile, datafile)
|
||||
gpgcmd = ("%s --logger-fd 1 --status-fd 1 --verify "
|
||||
"\"%s\" \"%s\"" % (self.path, sigfile, datafile))
|
||||
ret = util.filter("", gpgcmd)
|
||||
except:
|
||||
for f in (sigfile, datafile):
|
||||
@ -41,7 +50,7 @@ class gpg:
|
||||
continue
|
||||
l = l[9:]
|
||||
if l.startswith("ERRSIG"):
|
||||
err = "error while verifying signature"
|
||||
err = _("error while verifying signature")
|
||||
break
|
||||
elif l.startswith("VALIDSIG"):
|
||||
# fingerprint of the primary key
|
||||
@ -61,12 +70,97 @@ class gpg:
|
||||
return err, keys
|
||||
|
||||
def newgpg(ui, **opts):
|
||||
"""create a new gpg instance"""
|
||||
gpgpath = ui.config("gpg", "cmd", "gpg")
|
||||
gpgkey = opts.get('key')
|
||||
if not gpgkey:
|
||||
gpgkey = ui.config("gpg", "key", None)
|
||||
return gpg(gpgpath, gpgkey)
|
||||
|
||||
def sigwalk(repo):
|
||||
"""
|
||||
walk over every sigs, yields a couple
|
||||
((node, version, sig), (filename, linenumber))
|
||||
"""
|
||||
def parsefile(fileiter, context):
|
||||
ln = 1
|
||||
for l in fileiter:
|
||||
if not l:
|
||||
continue
|
||||
yield (l.split(" ", 2), (context, ln))
|
||||
ln +=1
|
||||
|
||||
fl = repo.file(".hgsigs")
|
||||
h = fl.heads()
|
||||
h.reverse()
|
||||
# read the heads
|
||||
for r in h:
|
||||
fn = ".hgsigs|%s" % hgnode.short(r)
|
||||
for item in parsefile(fl.read(r).splitlines(), fn):
|
||||
yield item
|
||||
try:
|
||||
# read local signatures
|
||||
fn = "localsigs"
|
||||
for item in parsefile(repo.opener(fn), fn):
|
||||
yield item
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
def getkeys(ui, repo, mygpg, sigdata, context):
|
||||
"""get the keys who signed a data"""
|
||||
fn, ln = context
|
||||
node, version, sig = sigdata
|
||||
prefix = "%s:%d" % (fn, ln)
|
||||
node = hgnode.bin(node)
|
||||
|
||||
data = node2txt(repo, node, version)
|
||||
sig = binascii.a2b_base64(sig)
|
||||
err, keys = mygpg.verify(data, sig)
|
||||
if err:
|
||||
ui.warn("%s:%d %s\n" % (fn, ln , err))
|
||||
return None
|
||||
|
||||
validkeys = []
|
||||
# warn for expired key and/or sigs
|
||||
for key in keys:
|
||||
if key[0] == "BADSIG":
|
||||
ui.write(_("%s Bad signature from \"%s\"\n") % (prefix, key[2]))
|
||||
continue
|
||||
if key[0] == "EXPSIG":
|
||||
ui.write(_("%s Note: Signature has expired"
|
||||
" (signed by: \"%s\")\n") % (prefix, key[2]))
|
||||
elif key[0] == "EXPKEYSIG":
|
||||
ui.write(_("%s Note: This key has expired"
|
||||
" (signed by: \"%s\")\n") % (prefix, key[2]))
|
||||
validkeys.append((key[1], key[2], key[3]))
|
||||
return validkeys
|
||||
|
||||
def sigs(ui, repo):
|
||||
"""list signed changesets"""
|
||||
mygpg = newgpg(ui)
|
||||
revs = {}
|
||||
|
||||
for data, context in sigwalk(repo):
|
||||
node, version, sig = data
|
||||
fn, ln = context
|
||||
try:
|
||||
n = repo.lookup(node)
|
||||
except KeyError:
|
||||
ui.warn(_("%s:%d node does not exist\n") % (fn, ln))
|
||||
continue
|
||||
r = repo.changelog.rev(n)
|
||||
keys = getkeys(ui, repo, mygpg, data, context)
|
||||
if not keys:
|
||||
continue
|
||||
revs.setdefault(r, [])
|
||||
revs[r].extend(keys)
|
||||
nodes = list(revs)
|
||||
nodes.reverse()
|
||||
for rev in nodes:
|
||||
for k in revs[rev]:
|
||||
r = "%5d:%s" % (rev, hgnode.hex(repo.changelog.node(rev)))
|
||||
ui.write("%-30s %s\n" % (keystr(ui, k), r))
|
||||
|
||||
def check(ui, repo, rev):
|
||||
"""verify all the signatures there may be for a particular revision"""
|
||||
mygpg = newgpg(ui)
|
||||
@ -74,63 +168,30 @@ def check(ui, repo, rev):
|
||||
hexrev = hgnode.hex(rev)
|
||||
keys = []
|
||||
|
||||
def addsig(fn, ln, l):
|
||||
if not l: return
|
||||
n, v, sig = l.split(" ", 2)
|
||||
if n == hexrev:
|
||||
data = node2txt(repo, rev, v)
|
||||
sig = binascii.a2b_base64(sig)
|
||||
err, k = mygpg.verify(data, sig)
|
||||
if not err:
|
||||
keys.append((k, fn, ln))
|
||||
else:
|
||||
ui.warn("%s:%d %s\n" % (fn, ln , err))
|
||||
|
||||
fl = repo.file(".hgsigs")
|
||||
h = fl.heads()
|
||||
h.reverse()
|
||||
# read the heads
|
||||
for r in h:
|
||||
ln = 1
|
||||
for l in fl.read(r).splitlines():
|
||||
addsig(".hgsigs|%s" % hgnode.short(r), ln, l)
|
||||
ln +=1
|
||||
try:
|
||||
# read local signatures
|
||||
ln = 1
|
||||
f = repo.opener("localsigs")
|
||||
for l in f:
|
||||
addsig("localsigs", ln, l)
|
||||
ln +=1
|
||||
except IOError:
|
||||
pass
|
||||
for data, context in sigwalk(repo):
|
||||
node, version, sig = data
|
||||
if node == hexrev:
|
||||
k = getkeys(ui, repo, mygpg, data, context)
|
||||
if k:
|
||||
keys.extend(k)
|
||||
|
||||
if not keys:
|
||||
ui.write("%s not signed\n" % hgnode.short(rev))
|
||||
ui.write(_("No valid signature for %s\n") % hgnode.short(rev))
|
||||
return
|
||||
valid = []
|
||||
# warn for expired key and/or sigs
|
||||
for k, fn, ln in keys:
|
||||
prefix = "%s:%d" % (fn, ln)
|
||||
for key in k:
|
||||
if key[0] == "BADSIG":
|
||||
ui.write("%s Bad signature from \"%s\"\n" % (prefix, key[2]))
|
||||
continue
|
||||
if key[0] == "EXPSIG":
|
||||
ui.write("%s Note: Signature has expired"
|
||||
" (signed by: \"%s\")\n" % (prefix, key[2]))
|
||||
elif key[0] == "EXPKEYSIG":
|
||||
ui.write("%s Note: This key has expired"
|
||||
" (signed by: \"%s\")\n" % (prefix, key[2]))
|
||||
valid.append((key[1], key[2], key[3]))
|
||||
|
||||
# print summary
|
||||
ui.write("%s is signed by:\n" % hgnode.short(rev))
|
||||
for keyid, user, fingerprint in valid:
|
||||
role = getrole(ui, fingerprint)
|
||||
ui.write(" %s (%s)\n" % (user, role))
|
||||
for key in keys:
|
||||
ui.write(" %s\n" % keystr(ui, key))
|
||||
|
||||
def getrole(ui, fingerprint):
|
||||
return ui.config("gpg", fingerprint, "no role defined")
|
||||
def keystr(ui, key):
|
||||
"""associate a string to a key (username, comment)"""
|
||||
keyid, user, fingerprint = key
|
||||
comment = ui.config("gpg", fingerprint, None)
|
||||
if comment:
|
||||
return "%s (%s)" % (user, comment)
|
||||
else:
|
||||
return user
|
||||
|
||||
def sign(ui, repo, *revs, **opts):
|
||||
"""add a signature for the current tip or a given revision"""
|
||||
@ -150,7 +211,7 @@ def sign(ui, repo, *revs, **opts):
|
||||
data = node2txt(repo, n, sigver)
|
||||
sig = mygpg.sign(data)
|
||||
if not sig:
|
||||
raise util.Abort("Error while signing")
|
||||
raise util.Abort(_("Error while signing"))
|
||||
sig = binascii.b2a_base64(sig)
|
||||
sig = sig.replace("\n", "")
|
||||
sigmessage += "%s %s %s\n" % (hexnode, sigver, sig)
|
||||
@ -162,9 +223,9 @@ def sign(ui, repo, *revs, **opts):
|
||||
|
||||
for x in repo.changes():
|
||||
if ".hgsigs" in x and not opts["force"]:
|
||||
raise util.Abort("working copy of .hgsigs is changed "
|
||||
"(please commit .hgsigs manually"
|
||||
"or use --force)")
|
||||
raise util.Abort(_("working copy of .hgsigs is changed "
|
||||
"(please commit .hgsigs manually "
|
||||
"or use --force)"))
|
||||
|
||||
repo.wfile(".hgsigs", "ab").write(sigmessage)
|
||||
|
||||
@ -176,7 +237,8 @@ def sign(ui, repo, *revs, **opts):
|
||||
|
||||
message = opts['message']
|
||||
if not message:
|
||||
message = "\n".join(["Added signature for changeset %s" % hgnode.hex(n)
|
||||
message = "\n".join([_("Added signature for changeset %s")
|
||||
% hgnode.hex(n)
|
||||
for n in nodes])
|
||||
try:
|
||||
repo.commit([".hgsigs"], message, opts['user'], opts['date'])
|
||||
@ -188,19 +250,20 @@ def node2txt(repo, node, ver):
|
||||
if ver == "0":
|
||||
return "%s\n" % hgnode.hex(node)
|
||||
else:
|
||||
util.Abort("unknown signature version")
|
||||
raise util.Abort(_("unknown signature version"))
|
||||
|
||||
cmdtable = {
|
||||
"sign":
|
||||
(sign,
|
||||
[('l', 'local', None, "make the signature local"),
|
||||
('f', 'force', None, "sign even if the sigfile is modified"),
|
||||
('', 'no-commit', None, "do not commit the sigfile after signing"),
|
||||
('m', 'message', "", "commit message"),
|
||||
('d', 'date', "", "date code"),
|
||||
('u', 'user', "", "user"),
|
||||
('k', 'key', "", "the key id to sign with")],
|
||||
"hg sign [OPTION]... REVISIONS"),
|
||||
"sigcheck": (check, [], 'hg sigcheck REVISION')
|
||||
[('l', 'local', None, _("make the signature local")),
|
||||
('f', 'force', None, _("sign even if the sigfile is modified")),
|
||||
('', 'no-commit', None, _("do not commit the sigfile after signing")),
|
||||
('m', 'message', "", _("commit message")),
|
||||
('d', 'date', "", _("date code")),
|
||||
('u', 'user', "", _("user")),
|
||||
('k', 'key', "", _("the key id to sign with"))],
|
||||
_("hg sign [OPTION]... [REVISION]...")),
|
||||
"sigcheck": (check, [], _('hg sigcheck REVISION')),
|
||||
"sigs": (sigs, [], _('hg sigs')),
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Interactive script for sending a collection of Mercurial changesets
|
||||
# as a series of patch emails.
|
||||
# Command for sending a collection of Mercurial changesets as a series
|
||||
# of patch emails.
|
||||
#
|
||||
# The series is started off with a "[PATCH 0 of N]" introduction,
|
||||
# which describes the series as a whole.
|
||||
@ -50,9 +48,9 @@
|
||||
from email.MIMEMultipart import MIMEMultipart
|
||||
from email.MIMEText import MIMEText
|
||||
from mercurial import commands
|
||||
from mercurial import fancyopts
|
||||
from mercurial import hg
|
||||
from mercurial import ui
|
||||
from mercurial.i18n import gettext as _
|
||||
import os
|
||||
import popen2
|
||||
import smtplib
|
||||
@ -89,6 +87,17 @@ def diffstat(patch):
|
||||
except: pass
|
||||
|
||||
def patchbomb(ui, repo, *revs, **opts):
|
||||
'''send changesets as a series of patch emails
|
||||
|
||||
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".'''
|
||||
def prompt(prompt, default = None, rest = ': ', empty_ok = False):
|
||||
if default: prompt += ' [%s]' % default
|
||||
prompt += rest
|
||||
@ -97,7 +106,7 @@ def patchbomb(ui, repo, *revs, **opts):
|
||||
if r: return r
|
||||
if default is not None: return default
|
||||
if empty_ok: return r
|
||||
ui.warn('Please enter a valid value.\n')
|
||||
ui.warn(_('Please enter a valid value.\n'))
|
||||
|
||||
def confirm(s):
|
||||
if not prompt(s, default = 'y', rest = '? ').lower().startswith('y'):
|
||||
@ -109,7 +118,7 @@ def patchbomb(ui, repo, *revs, **opts):
|
||||
if summary:
|
||||
ui.write(summary, '\n')
|
||||
ui.write(s, '\n')
|
||||
confirm('Does the diffstat above look okay')
|
||||
confirm(_('Does the diffstat above look okay'))
|
||||
return s
|
||||
|
||||
def makepatch(patch, idx, total):
|
||||
@ -162,20 +171,20 @@ def patchbomb(ui, repo, *revs, **opts):
|
||||
self.container.append(''.join(self.lines).split('\n'))
|
||||
self.lines = []
|
||||
|
||||
commands.export(ui, repo, *args, **{'output': exportee(patches),
|
||||
commands.export(ui, repo, *revs, **{'output': exportee(patches),
|
||||
'switch_parent': False,
|
||||
'text': None})
|
||||
|
||||
jumbo = []
|
||||
msgs = []
|
||||
|
||||
ui.write('This patch series consists of %d patches.\n\n' % len(patches))
|
||||
ui.write(_('This patch series consists of %d patches.\n\n') % len(patches))
|
||||
|
||||
for p, i in zip(patches, range(len(patches))):
|
||||
jumbo.extend(p)
|
||||
msgs.append(makepatch(p, i + 1, len(patches)))
|
||||
|
||||
ui.write('\nWrite the introductory message for the patch series.\n\n')
|
||||
ui.write(_('\nWrite the introductory message for the patch series.\n\n'))
|
||||
|
||||
sender = (opts['from'] or ui.config('patchbomb', 'from') or
|
||||
prompt('From', ui.username()))
|
||||
@ -193,7 +202,7 @@ def patchbomb(ui, repo, *revs, **opts):
|
||||
to = getaddrs('to', 'To')
|
||||
cc = getaddrs('cc', 'Cc', '')
|
||||
|
||||
ui.write('Finish with ^D or a dot on a line by itself.\n\n')
|
||||
ui.write(_('Finish with ^D or a dot on a line by itself.\n\n'))
|
||||
|
||||
body = []
|
||||
|
||||
@ -208,7 +217,7 @@ def patchbomb(ui, repo, *revs, **opts):
|
||||
ui.write('\n')
|
||||
|
||||
if opts['diffstat']:
|
||||
d = cdiffstat('Final summary:\n', jumbo)
|
||||
d = cdiffstat(_('Final summary:\n'), jumbo)
|
||||
if d: msg.attach(MIMEText(d))
|
||||
|
||||
msgs.insert(0, msg)
|
||||
@ -252,25 +261,15 @@ def patchbomb(ui, repo, *revs, **opts):
|
||||
if not opts['test']:
|
||||
s.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
optspec = [('c', 'cc', [], 'email addresses of copy recipients'),
|
||||
('d', 'diffstat', None, 'add diffstat output to messages'),
|
||||
('f', 'from', '', 'email address of sender'),
|
||||
('', 'plain', None, 'omit hg patch header'),
|
||||
('n', 'test', None, 'print messages that would be sent'),
|
||||
('s', 'subject', '', 'subject of introductory message'),
|
||||
('t', 'to', [], 'email addresses of recipients')]
|
||||
options = {}
|
||||
try:
|
||||
args = fancyopts.fancyopts(sys.argv[1:], commands.globalopts + optspec,
|
||||
options)
|
||||
except fancyopts.getopt.GetoptError, inst:
|
||||
u = ui.ui()
|
||||
u.warn('error: %s' % inst)
|
||||
sys.exit(1)
|
||||
|
||||
u = ui.ui(options["verbose"], options["debug"], options["quiet"],
|
||||
not options["noninteractive"])
|
||||
repo = hg.repository(ui = u)
|
||||
|
||||
patchbomb(u, repo, *args, **options)
|
||||
cmdtable = {
|
||||
'email':
|
||||
(patchbomb,
|
||||
[('c', 'cc', [], 'email addresses of copy recipients'),
|
||||
('d', 'diffstat', None, 'add diffstat output to messages'),
|
||||
('f', 'from', '', 'email address of sender'),
|
||||
('', 'plain', None, 'omit hg patch header'),
|
||||
('n', 'test', None, 'print messages that would be sent'),
|
||||
('s', 'subject', '', 'subject of introductory message'),
|
||||
('t', 'to', [], 'email addresses of recipients')],
|
||||
"hg email [OPTION]... [REV]...")
|
||||
}
|
@ -1399,6 +1399,13 @@ class localrepository(object):
|
||||
|
||||
modified, added, removed, deleted, unknown = self.changes()
|
||||
|
||||
# is this a jump, or a merge? i.e. is there a linear path
|
||||
# from p1 to p2?
|
||||
linear_path = (pa == p1 or pa == p2)
|
||||
|
||||
if allow and linear_path:
|
||||
raise util.Abort(_("there is nothing to merge, "
|
||||
"just use 'hg update'"))
|
||||
if allow and not forcemerge:
|
||||
if modified or added or removed:
|
||||
raise util.Abort(_("outstanding uncommited changes"))
|
||||
@ -1411,10 +1418,6 @@ class localrepository(object):
|
||||
raise util.Abort(_("'%s' already exists in the working"
|
||||
" dir and differs from remote") % f)
|
||||
|
||||
# is this a jump, or a merge? i.e. is there a linear path
|
||||
# from p1 to p2?
|
||||
linear_path = (pa == p1 or pa == p2)
|
||||
|
||||
# resolve the manifest to determine which files
|
||||
# we care about merging
|
||||
self.ui.note(_("resolving manifests\n"))
|
||||
|
@ -24,11 +24,34 @@ hg commit -m "2" -d "0 0"
|
||||
cd ../r2
|
||||
hg -q pull ../r1
|
||||
hg status
|
||||
hg parents
|
||||
hg --debug up
|
||||
hg parents
|
||||
hg --debug up 0
|
||||
hg parents
|
||||
hg --debug up -m || echo failed
|
||||
hg --debug up -f -m
|
||||
hg parents
|
||||
hg --debug up
|
||||
hg parents
|
||||
hg -v history
|
||||
hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
|
||||
-e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
|
||||
|
||||
# create a second head
|
||||
cd ../r1
|
||||
hg up 0
|
||||
echo b2 > b
|
||||
echo a3 > a
|
||||
hg addremove
|
||||
hg commit -m "3" -d "0 0"
|
||||
|
||||
cd ../r2
|
||||
hg -q pull ../r1
|
||||
hg status
|
||||
hg parents
|
||||
hg --debug up || echo failed
|
||||
hg --debug up -m || echo failed
|
||||
hg --debug up -f -m
|
||||
hg parents
|
||||
hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
|
||||
-e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
|
||||
|
@ -7,6 +7,11 @@ diff -r c19d34741b0a a
|
||||
+abc
|
||||
adding b
|
||||
M a
|
||||
changeset: 0:c19d34741b0a
|
||||
user: test
|
||||
date: Thu Jan 1 00:00:00 1970 +0000
|
||||
summary: 1
|
||||
|
||||
resolving manifests
|
||||
force None allow None moddirstate True linear True
|
||||
ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
|
||||
@ -16,11 +21,38 @@ getting b
|
||||
merging a
|
||||
resolving a
|
||||
file a: my b789fdd96dc2 other d730145abbf9 ancestor b789fdd96dc2
|
||||
abort: outstanding uncommited changes
|
||||
failed
|
||||
changeset: 1:1e71731e6fbb
|
||||
tag: tip
|
||||
user: test
|
||||
date: Thu Jan 1 00:00:00 1970 +0000
|
||||
summary: 2
|
||||
|
||||
resolving manifests
|
||||
force None allow 1 moddirstate True linear True
|
||||
ancestor 1165e8bd193e local 1165e8bd193e remote 1165e8bd193e
|
||||
force None allow None moddirstate True linear True
|
||||
ancestor a0c8bcbbb45c local 1165e8bd193e remote a0c8bcbbb45c
|
||||
remote deleted b
|
||||
removing b
|
||||
changeset: 0:c19d34741b0a
|
||||
user: test
|
||||
date: Thu Jan 1 00:00:00 1970 +0000
|
||||
summary: 1
|
||||
|
||||
abort: there is nothing to merge, just use 'hg update'
|
||||
failed
|
||||
changeset: 0:c19d34741b0a
|
||||
user: test
|
||||
date: Thu Jan 1 00:00:00 1970 +0000
|
||||
summary: 1
|
||||
|
||||
resolving manifests
|
||||
force None allow None moddirstate True linear True
|
||||
ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
|
||||
a versions differ, resolve
|
||||
remote created b
|
||||
getting b
|
||||
merging a
|
||||
resolving a
|
||||
file a: my b789fdd96dc2 other d730145abbf9 ancestor b789fdd96dc2
|
||||
changeset: 1:1e71731e6fbb
|
||||
tag: tip
|
||||
user: test
|
||||
@ -44,6 +76,55 @@ description:
|
||||
1
|
||||
|
||||
|
||||
diff -r 1e71731e6fbb a
|
||||
--- a/a
|
||||
+++ b/a
|
||||
@@ -1,1 +1,1 @@ a2
|
||||
-a2
|
||||
+abc
|
||||
adding b
|
||||
M a
|
||||
changeset: 1:1e71731e6fbb
|
||||
user: test
|
||||
date: Thu Jan 1 00:00:00 1970 +0000
|
||||
summary: 2
|
||||
|
||||
resolving manifests
|
||||
force None allow None moddirstate True linear False
|
||||
ancestor a0c8bcbbb45c local 1165e8bd193e remote 4096f2872392
|
||||
a versions differ, resolve
|
||||
b versions differ, resolve
|
||||
this update spans a branch affecting the following files:
|
||||
a (resolve)
|
||||
b (resolve)
|
||||
aborting update spanning branches!
|
||||
(use update -m to merge across branches or -C to lose changes)
|
||||
failed
|
||||
abort: outstanding uncommited changes
|
||||
failed
|
||||
resolving manifests
|
||||
force None allow 1 moddirstate True linear False
|
||||
ancestor a0c8bcbbb45c local 1165e8bd193e remote 4096f2872392
|
||||
a versions differ, resolve
|
||||
b versions differ, resolve
|
||||
merging a
|
||||
resolving a
|
||||
file a: my d730145abbf9 other 13e0d5f949fa ancestor b789fdd96dc2
|
||||
merging b
|
||||
resolving b
|
||||
file b: my 1e88685f5dde other 61de8c7723ca ancestor 000000000000
|
||||
changeset: 1:1e71731e6fbb
|
||||
user: test
|
||||
date: Thu Jan 1 00:00:00 1970 +0000
|
||||
summary: 2
|
||||
|
||||
changeset: 2:83c51d0caff4
|
||||
tag: tip
|
||||
parent: 0:c19d34741b0a
|
||||
user: test
|
||||
date: Thu Jan 1 00:00:00 1970 +0000
|
||||
summary: 3
|
||||
|
||||
diff -r 1e71731e6fbb a
|
||||
--- a/a
|
||||
+++ b/a
|
||||
|
Loading…
Reference in New Issue
Block a user