[PATCH 3 of 6 resolve-ux] merge: implement mergestate.__len__ and mergestate.__nonzero__
Mads Kiilerich
mads at kiilerich.com
Fri May 2 08:15:30 CDT 2014
On 05/02/2014 07:47 AM, Gregory Szorc wrote:
> # HG changeset patch
> # User Gregory Szorc <gregory.szorc at gmail.com>
> # Date 1397872888 25200
> # Fri Apr 18 19:01:28 2014 -0700
> # Branch stable
> # Node ID b4d95342be6de3e3a229e1536720d22d6de6eb13
> # Parent 812bb7bf59e8ff0e2e60c1d73857206c6efd8b32
> merge: implement mergestate.__len__ and mergestate.__nonzero__
>
> An upcoming patch will test if mergestate is present. Until this patch,
> there was no easy way to do this without looking at files on disk (which
> are part of mergestate's implementation details).
>
> This patch allows consumers to differentiate between no mergestate,
> empty mergestate, and mergestate with entries. It does this by
> implementing __nonzero__ and __len__. |if ms| will now be true if the
> mergestate has entries or it has records on disk. |if len(ms)| will now
> be true iff mergestate has entries.
>
> diff --git a/mercurial/merge.py b/mercurial/merge.py
> --- a/mercurial/merge.py
> +++ b/mercurial/merge.py
> @@ -46,16 +46,17 @@ class mergestate(object):
> F: a file to be merged entry
> '''
> statepathv1 = "merge/state"
> statepathv2 = "merge/state2"
>
> def __init__(self, repo):
> self._repo = repo
> self._dirty = False
> + self._recordpresent = False
This value seems a bit odd to me. The name do not seem to explain its
purpose to me. I assume it represents one of the 3 states you mention in
the description? After trying to grok the code, I think something like
_ongoing or _active would make more sense to me. But it also seems like
we already have this information in ._local and ._other being None?
Anyway, shouldn't it be initialized in .reset()?
> self._read()
>
> def reset(self, node=None, other=None):
> self._state = {}
> if node:
> self._local = node
> self._other = other
> shutil.rmtree(self._repo.join("merge"), True)
> @@ -136,16 +137,17 @@ class mergestate(object):
> try:
> f = self._repo.opener(self.statepathv1)
> for i, l in enumerate(f):
> if i == 0:
> records.append(('L', l[:-1]))
> else:
> records.append(('F', l[:-1]))
> f.close()
> + self._recordpresent = True
This is in a method that is a pretty pure function on the object and
returns a value. It do not seem nice that it modifies the state.
Instead, I would suggest returning None for 'no ongoing merge' and a
(possibly empty) list for ongoing merges.
> except IOError, err:
> if err.errno != errno.ENOENT:
> raise
> return records
>
> def _readrecordsv2(self):
> """read on disk merge state for version 2 file
>
> @@ -161,16 +163,17 @@ class mergestate(object):
> rtype = data[off]
> off += 1
> length = _unpack('>I', data[off:(off + 4)])[0]
> off += 4
> record = data[off:(off + length)]
> off += length
> records.append((rtype, record))
> f.close()
> + self._recordpresent = True
> except IOError, err:
> if err.errno != errno.ENOENT:
> raise
> return records
>
> def commit(self):
> """Write current state on disk (if necessary)"""
> if self._dirty:
> @@ -181,16 +184,17 @@ class mergestate(object):
> records.append(("F", "\0".join([d] + v)))
> self._writerecords(records)
> self._dirty = False
>
> def _writerecords(self, records):
> """Write current state on disk (both v1 and v2)"""
> self._writerecordsv1(records)
> self._writerecordsv2(records)
> + self._recordpresent = True
>
> def _writerecordsv1(self, records):
> """Write current state on disk in a version 1 file"""
> f = self._repo.opener(self.statepathv1, "w")
> irecords = iter(records)
> lrecords = irecords.next()
> assert lrecords[0] == 'L'
> f.write(hex(self._local) + "\n")
> @@ -220,16 +224,22 @@ class mergestate(object):
> hash = util.sha1(fcl.path()).hexdigest()
> self._repo.opener.write("merge/" + hash, fcl.data())
> self._state[fd] = ['u', hash, fcl.path(),
> fca.path(), hex(fca.filenode()),
> fco.path(), hex(fco.filenode()),
> fcl.flags()]
> self._dirty = True
>
> + def __nonzero__(self):
> + return self._recordpresent or bool(self._state)
and later:
ms = mergemod.mergestate(repo)
+
+ if not ms:
+ raise util.Abort(_('no merge in progress; '
+ 'resolve command not applicable'))
I do not think the mergestate.__nonzero__ helps here. The code here
would be more readable with an explict if not ms.ongoing() or
.active() or something like that.
> +
> + def __len__(self):
> + return len(self._state)
AFAICS, this is not used yet. If I saw len(ms) (or an iterator over
ms), I guess I would expect it to show the unresolved files. It would be
ambiguous and it would be better to be explicit with a .states() and a
.unresolved() or a .gimme(all=True).
/Mads
More information about the Mercurial-devel
mailing list