[PATCH 1 of 2] histedit: replace pickle with custom serialization

Ryan McElroy rm at fb.com
Tue Apr 14 12:50:12 CDT 2015


On 4/14/2015 10:06 AM, Durham Goode wrote:
> # HG changeset patch
> # User Durham Goode <durham at fb.com>
> # Date 1428938637 25200
> #      Mon Apr 13 08:23:57 2015 -0700
> # Node ID 6289acbbd36ec81eab91ed3b8e32b3354bf8cf84
> # Parent  b2fb1403994e033584aed8a487ab162a9d75fa80
> histedit: replace pickle with custom serialization
>
> Pickle is undesirable, so let's serialize it ourselves. We keep the ability to
> parse existing pickle blobs for now.
>
> diff --git a/hgext/histedit.py b/hgext/histedit.py
> --- a/hgext/histedit.py
> +++ b/hgext/histedit.py
> @@ -220,7 +220,12 @@ class histeditstate(object):
>                   raise
>               raise util.Abort(_('no histedit in progress'))
>   
> -        parentctxnode, rules, keep, topmost, replacements = pickle.load(fp)
> +        try:
> +            data = pickle.load(fp)
> +            parentctxnode, rules, keep, topmost, replacements = data
> +        except pickle.UnpicklingError:
> +            data = self._load()
> +            parentctxnode, rules, keep, topmost, replacements = data
>   
>           self.parentctxnode = parentctxnode
>           self.rules = rules
> @@ -230,10 +235,63 @@ class histeditstate(object):
>   
>       def write(self):
>           fp = self.repo.vfs('histedit-state', 'w')
> -        pickle.dump((self.parentctxnode, self.rules, self.keep,
> -                     self.topmost, self.replacements), fp)
> +        fp.write('v1\n')
> +        fp.write('%s\n' % node.hex(self.parentctxnode))
> +        fp.write('%s\n' % node.hex(self.topmost))
> +        fp.write('%s\n' % self.keep)
> +        fp.write('%d\n' % len(self.rules))
> +        for rule in self.rules:
> +            fp.write('%s%s\n' % (rule[1], rule[0]))
I'd prefer rules to be in their own file, so it's easy to edit even 
without --edit-todo.

I'm fine having that in a later change though.

That would also eliminate the need for printing the number of lines into 
the file.
> +        fp.write('%d\n' % len(self.replacements))
> +        for replacement in self.replacements:
> +            fp.write('%s%s\n' % (node.hex(replacement[0]), ''.join(node.hex(r)
> +                for r in replacement[1])))
>           fp.close()
>   
> +    def _load(self):
> +        fp = self.repo.vfs('histedit-state', 'r')
> +        lines = [l[:-1] for l in fp.readlines()]
> +
> +        index = 0
> +        lines[index] # version number

Why have this line if you're not reading it or using it? Perhaps just 
comment it out for documentation?

Or even better, make this forwards-compatible. Look for a version that's 
not v1 and abort with a useful error message like "this histedit was 
started with a newer version of mercurial that uses a file format this 
version of mercurial does not know how to read. Please continue this 
histedit with the newer version of mercurial that started this histedit".

Also, bonus points if you can include in the message version of 
mercurial that knows how to to parse the history. Consider, for example, 
a version line that looks like this:

v1 # 3.4.1+123

That way, the message can show what version of mercurial might be used 
here to make things better.

> +        index += 1
> +
> +        parentctxnode = node.bin(lines[index])
> +        index += 1
> +
> +        topmost = node.bin(lines[index])
> +        index += 1
> +
> +        keep = lines[index] == 'True'
> +        index += 1
> +
> +        # Rules
> +        rules = []
> +        rulelen = int(lines[index])
> +        index += 1
> +        for i in xrange(rulelen):
> +            rule = lines[index]
> +            rulehash = rule[:40]
> +            ruleaction = rule[40:]
> +            rules.append((ruleaction, rulehash))
> +            index += 1
> +
> +        # Replacements
> +        replacements = []
> +        replacementlen = int(lines[index])
> +        index += 1
> +        for i in xrange(replacementlen):
> +            replacement = lines[index]
> +            original = node.bin(replacement[:40])
> +            succ = [node.bin(replacement[i:i + 40]) for i in
> +                    range(40, len(replacement), 40)]
> +            replacements.append((original, succ))
> +            index += 1
> +
> +        fp.close()
> +
> +        return parentctxnode, rules, keep, topmost, replacements
> +
>       def clear(self):
>           self.repo.vfs.unlink('histedit-state')
>   
>



More information about the Mercurial-devel mailing list