8cd7646ab70e — Alain Leufroy 11 years ago
[state] do not change state of unmodified guest

The idea is that users can fill the ``.hgguestrepo`` with
a clever chengeid than a hex (a tag for example).

In that case, maintaining the previous changeid:

- reduces the .hgguestrepo diff
- maintains the clever changeid


(Merged by pnathan, fixed to run correctly).
M guestrepo/__init__.py +4 -2
@@ 139,7 139,8 @@ cmdtable = {
                 "hg grupdate [REPO [+]] [options]"),
    "grfreeze": (freeze,
                 [('f', 'file', '', "output state to a .hggrfrozen file",  'FILE'),
-                 ('p', 'publish', False, "publish all record changeset"),]
+                 ('p', 'publish', False, "publish all record changeset"),
+                 ('k', 'keep', False, "keep previous changeid of unmodified guests"),]
                 + threadopt,
                 "hg grfreeze"),
    "grsummary": (summary,

          
@@ 148,7 149,8 @@ cmdtable = {
                  + jsonopt + localopt,
                  "hg grsummary"),
    "grstate": (state,
-               jsonopt + localopt,
+               [('k', 'keep', False, "keep previous changeid of unmodified guests"),]
+               + jsonopt + localopt,
                "hg grstate"),
    "grout" : (grout,
               localopt + threadopt + jsonopt,

          
M guestrepo/grconfig.py +11 -0
@@ 218,11 218,16 @@ class Configuration(_Configuration):
         hgconfig.parse(self.mappingpath, ctx[self.mappingpath].data())
         return hgconfig['']
 
+    def load_conf_as_kv_pair(self, ctx):
+        hgconfig = config.config()
+        hgconfig.parse(self.configpath, ctx[self.configpath].data())
+        return hgconfig['']
 
 class NewConfiguration(_Configuration):
     snappath = '.hggrsnap'
     confpath = '.hggrconf'
 
+
     def get_files(self):
         return self.confpath, self.snappath
 

          
@@ 259,6 264,12 @@ class NewConfiguration(_Configuration):
             guest.csid = csid
         return guests
 
+    def load_conf_as_kv_pair(self, ctx):
+        hgconfig = config.config()
+        hgconfig.parse(self.confpath, ctx[self.confpath].data())
+        return hgconfig['']
+
+
     def load_guests(self, ctx, **opts):
         rootpath = self.repo.root
         pathauditor = pathutil.pathauditor(rootpath)

          
M guestrepo/guestrepo.py +123 -12
@@ 1,4 1,6 @@ 
 # Guestrepo: A Mercurial Extension for the managing of components
+# Copyright (C) 2013-2015 Paul Nathan
+# Other copyrights as found in hg.
 # Copyright (C) 2012  Schweitzer Engineering Laboratories, Inc.
 
 # This program is free software; you can redistribute it and/or modify

          
@@ 118,7 120,7 @@ def grupdate(ui, repo, *args, **opts):
     if opts.get('file'):
         guests = getguests(ui, repo, opts.get('file'), local=opts.get('local'))
     else:
-        guests = getguests(ui, repo, local=opts.get('local'))
+        guests = getguests(ui, repo, local=opts.get('local'), check=True)
 
     if args:
         guests = matchguests(repo.root,

          
@@ 247,7 249,7 @@ def pull(ui, repo, *args, **opts):
     see help for the guestrepo extension for more information on the files used
     '''
 
-    guests = getguests(ui, repo, local=opts.get('local'))
+    guests = getguests(ui, repo, local=opts.get('local'), check=True)
 
     if args:
         guests = matchguests(repo.root,

          
@@ 404,13 406,8 @@ def state(ui, repo, **opts):
                             'id' : dict_rep['id']})
         ui.write(json.dumps(json_list) + "\n")
     else:
-        # '%-10s' : left justifier at least 10 chars (space completed)
-        # => 'guest      = myguest    default'
-        lines = ("%-10s = %-10s %s\n" % (guest.configpath,
-                                         guest.name,
-                                         guest.get_cset(ui, '.').hex())
-                 for guest in filterwarnguests(ui, guests))
-        ui.write(''.join(lines))
+        statestr = makestatestr(getstate(ui, repo, guests, opts.get('keep')))
+        ui.write(statestr)
 
 @warn_uncomitted_config
 def freeze(ui, repo, **opts):

          
@@ 425,12 422,15 @@ def freeze(ui, repo, **opts):
     ``.hgguestrepo`` file.
     '''
     guests = getguests(ui, repo)
+    state = getstate(ui, repo, guests, opts.get('keep'))
+    statestr = makestatestr(state)
     if opts.get('publish', False):
+        state = dict(state)
         if not SUPPORTS_PHASES:
             msg = 'your version of Mercurial does not support phases'
             raise util.Abort(msg)
         def publishaction(ui, repo, guest):
-            commands.phase(ui, repo, rev=[guest.get_cset(ui, rev='.').hex()],
+            commands.phase(ui, repo, rev=[state[guest]],
                            public=True, draft=None, secret=None, force=False)
 
         workers = makeworkers(ui, opts)

          
@@ 615,13 615,13 @@ def getmapping(ui, repo, useworkingmap=F
         # detect case of uncommitted branch
         currentbranch = currentctx.branch()
         if currentbranch not in repo.branchmap():
-            raise util.Abort("unmanaged mapping file: "
-                             "no commits on current branch")
+            raise util.Abort("unmanaged mapping file; no commits on current branch")
         # Use the current changeset as a base for the mapping
         ctx = repo['.']
 
     return conf.load_mapping(ctx)
 
+
 def getguests(ui, repo, frozen_file=None, ctx=None, **kwargs):
     ''' Get the guest repos by parsing the .hgguestrepo.
 

          
@@ 631,6 631,8 @@ def getguests(ui, repo, frozen_file=None
 
 
     local = kwargs.get('local')
+    check = kwargs.get('check')
+    skip = False
 
     # Two choices: we can either mooch the local (uncommitted)
     # changes (using --local), or we go for the local ctx

          
@@ 841,6 843,60 @@ def applyguests(ui, repo, guests, worker
         if work:
             workers.run(work, guest)
 
+def readconfig(filename, ctx):
+    ''' read a config file at a context
+
+        throws IOError when config file missing from the working directory,
+        error. LookupError if config is missing from a changeset
+    '''
+    p = config.config()
+    p.parse(filename, ctx[filename].data())
+    return p
+
+def makestatestr(states):
+    '''
+    Create a description string for a list of state.
+
+    It can also return the same string in JSON.
+    '''
+    lines = []
+    for guest, hex in states:
+        lines.append("%s = %s %s\n" %
+                     (guest.configpath.ljust(10),
+                      guest.name.ljust(10),
+                      hex))
+    return ''.join(lines)
+
+
+def getstate(ui, repo, guests, keep=False):
+    '''
+    Return a list of repo's guest's state.
+
+    :keep: do not change previous state if guest remains unchanged
+    '''
+    states = []
+    conf = get_conf(ui, repo)
+    # in legacy config (.hgguestrepo): path = <identifier> changeset
+    # in current config (.hggrsnap): path = changeset
+    config = conf.load_conf_as_kv_pair(repo['.'])
+    for guest in filterwarnguests(ui, guests):
+        guestrepo = hg.repository(ui, guest.root, create=False)
+        guestctx = guestrepo['.']
+        gueststate = guestctx.hex()
+        # we try to see if we can keep the previous saved state
+        if keep:
+            guestconfig = config.get(guest.configpath, None)
+            if guestconfig is not None:
+                # pick off the changeset...
+                initialstate = guestconfig.split()[-1]
+                try:
+                    if guestrepo[initialstate] == guestrepo['.']:
+                        gueststate = initialstate
+                except error.RepoLookupError:
+                    pass # initial state not found
+        states.append((guest, gueststate))
+    return states
+
 def template_output_to_list(template_output):
     '''
     Take the output of hg.incoming or hg.incoming formatted using the

          
@@ 878,3 934,58 @@ def template_output_to_list(template_out
         in_records.append(loaded_object)
 
     return in_records
+
+
+
+# N.b., might be dead code.
+class guestrepo(object):
+    '''An aggregate representing a guest repository'''
+    def __init__(self, name, configpath, canonpath, uri, csid, root):
+        self.name = name
+        self.configpath = configpath
+        self.canonpath = canonpath
+        self.uri = uri
+        self.csid = csid
+        self.root = root
+
+    def isrepo(self, warn=False):
+        return os.path.exists(os.path.join(self.root, '.hg'))
+
+    def to_dict(self, ui):
+        '''
+        Output all of the metadata associated with a Guestrepo as a dict
+        '''
+        guestrepo = hg.repository(ui, self.root, create=False)
+        guestctx = guestrepo[None]
+
+        # Set up GR attributes in JSON
+        json_dict = {'path' : self.canonpath}
+        json_dict['branch'] = guestctx.branch()
+        json_dict['remote_name'] = self.name
+
+        parents = guestctx.parents()
+        json_dict['id'] = (' '.join(['+'.join([node.hex(p.node()) for p in parents]), ]))[:12]
+
+        if guestctx.tags():
+            json_dict['tags'] = '/'.join(guestctx.tags())
+
+        if guestctx.bookmarks():
+            json_dict['bookmarks'] = '/'.join(guestctx.bookmarks())
+
+        current_bookmark = bookmarks.readcurrent(guestrepo)
+
+        if current_bookmark:
+            json_dict['current_bookmark'] = current_bookmark
+
+        if changed(guestrepo):
+            json_dict['changed'] = True
+
+        return json_dict
+
+    def to_gr_entry(self, ui):
+        guestrepo = hg.repository(ui, self.root, create=False)
+        changeset_id = node.hex(guestrepo[None].parents()[0].node())
+        return "%s = %s %s\n" % self.configpath.ljust(10), self.name.ljust(10), changeset_id
+
+    def to_mapping_entry(self, ui):
+        return "%s = %s" % self.configpath.ljust(10), self.name

          
M tests/test-freeze.t +11 -0
@@ 45,6 45,17 @@ Make a host and guest repo
   updating to branch default
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
+Test --keep and verify that it doesn't break normal behavior.
+
+  $ hg grfreeze --keep
+  $ cat .hggrsnap
+  mygr       = ???????????????????????????????????????? (glob)
+
+Test --keep - nothing should change
+  $ hg grfreeze --keep
+  $ cat .hggrsnap
+  mygr       = ???????????????????????????????????????? (glob)
+
 Run freeze and test that it rewrote and commited
   $ hg grfreeze
   $ cat .hggrsnap

          
M tests/test-uncommitedbranch-legacy.t +1 -1
@@ 31,5 31,5 @@ Test the case where the host repo is on 
   $ hg commit -m 0
   $ hg branch newbranch >> /dev/null 2>&1
   $ hg grpull
-  abort: unmanaged mapping file: no commits on current branch
+  abort: unmanaged mapping file; no commits on current branch
   [255]

          
M tests/test-uncommitedbranch.t +1 -1
@@ 33,5 33,5 @@ Test the case where the host repo is on 
   $ hg commit -m 0
   $ hg branch newbranch >> /dev/null 2>&1
   $ hg grpull
-  abort: unmanaged mapping file: no commits on current branch
+  abort: unmanaged mapping file; no commits on current branch
   [255]