--- dispatch-conf.orig 2003-02-21 02:53:23.000000000 -0800 +++ dispatch-conf 2003-02-21 22:47:11.000000000 -0800 @@ -20,6 +20,12 @@ DIFF_WSCOMMENTS = 'diff -Nau %s %s | grep "^[+-][^+-]" | grep -v "^[-+]#" | grep -v "^[-+][:space:]*$"' MERGE = 'sdiff --suppress-common-lines --output=%s %s %s' +RCS_BRANCH = '1.1.1' +RCS_LOCK = 'rcs -ko -M -l' +RCS_PUT = 'ci -t-"Archived config file." -m"dispatch-conf update."' +RCS_GET = 'co' +RCS_MERGE = 'rcsmerge -p -r' + RCS_BRANCH + ' %s >%s' + MANDATORY_OPTS = [ 'archive-dir', 'diff', 'pager', 'replace-cvs', 'replace-wscomments' ] class dispatch: @@ -50,19 +56,37 @@ # def f (conf): - same_file = len(commands.getoutput (DIFF_CONTENTS % (conf ['current'], conf ['new']))) == 0 - same_cvs = len(commands.getoutput (DIFF_CVS_INTERP % (conf ['current'], conf ['new']))) == 0 - same_wsc = len(commands.getoutput (DIFF_WSCOMMENTS % (conf ['current'], conf ['new']))) == 0 + mrgconf = re.sub (r'\._cfg', '._mrg', conf ['new']) + if os.path.exists (mrgconf): + newconf = mrgconf + unmodified = 0 + else: + newconf = self.do_rcs (conf ['new'], conf ['current'], mrgconf) + if newconf == mrgconf: + unmodified = len(commands.getoutput (DIFF_CONTENTS % (conf ['new'], mrgconf))) == 0 + else: + unmodified = 0 + + same_file = len(commands.getoutput (DIFF_CONTENTS % (conf ['current'], newconf))) == 0 + same_cvs = len(commands.getoutput (DIFF_CVS_INTERP % (conf ['current'], newconf))) == 0 + same_wsc = len(commands.getoutput (DIFF_WSCOMMENTS % (conf ['current'], newconf))) == 0 # Do options permit? same_cvs = same_cvs and self.options ['replace-cvs'] == 'yes' same_wsc = same_wsc and self.options ['replace-wscomments'] == 'yes' + unmodified = unmodified and self.options ['replace-unmodified'] == 'yes' - if same_file: + if same_file or unmodified: os.unlink (conf ['new']) + if os.path.exists (mrgconf): + os.unlink (mrgconf) return False elif same_cvs or same_wsc or conf ['dir'] in portage.settings ['CONFIG_PROTECT_MASK'].split (): - self.replace (conf ['new'], conf ['current']) + self.replace (newconf, conf ['current']) + if newconf == mrgconf: + os.unlink (conf ['new']) + elif os.path.exists (mrgconf): + os.unlink (mrgconf) return False else: return True @@ -76,69 +100,117 @@ for conf in confs: count = count + 1 + newconf = conf ['new'] + mrgconf = re.sub (r'\._cfg', '._mrg', newconf) + if os.path.exists (mrgconf): + newconf = mrgconf + show_new_diff = 0 + while 1: - os.system ((self.options ['diff'] + '| %s') % (conf ['current'], conf ['new'], self.options ['pager'])) + if show_new_diff: + os.system ((self.options ['diff'] + '| %s') % (conf ['new'], mrgconf, self.options ['pager'])) + show_new_diff = 0 + else: + os.system ((self.options ['diff'] + '| %s') % (conf ['current'], newconf, self.options ['pager'])) print print '>> (%i of %i) -- %s' % (count, len(confs), conf ['current']) - print '>> q quit, h help, n skip, f fuse/merge, k kill new, s supercede w/new', + print '>> q quit, h help, n next, e edit-new, z zap-new, u use-new\n m merge, t toggle-merge, l look-merge: ', c = getch () if c == 'q': sys.exit (0) if c == 'h': - do_help () + self.do_help () + continue + elif c == 't': + if newconf == mrgconf: + newconf = conf ['new'] + elif os.path.exists (mrgconf): + newconf = mrgconf continue elif c == 'n': break - elif c == 'f': + elif c == 'm': merged = '/tmp/dispatch-conf.merged.%i' % (os.getpid (),) print - os.system (MERGE % (merged, conf ['current'], conf ['new'])) - os.rename (merged, conf ['new']) + os.system (MERGE % (merged, conf ['current'], newconf)) + os.rename (merged, mrgconf) + newconf = mrgconf + continue + elif c == 'l': + show_new_diff = 1 + continue + elif c == 'e': + os.system ('vim %s' % (newconf)) continue - elif c == 'k': - os.remove (conf ['new']) + elif c == 'z': + os.unlink (conf ['new']) + if os.path.exists (mrgconf): + os.unlink (mrgconf) break - elif c == 's': - self.replace (conf ['new'], conf ['current']) + elif c == 'u': + self.replace (newconf, conf ['current']) + if newconf == mrgconf: + os.unlink (conf ['new']) + elif os.path.exists (mrgconf): + os.unlink (mrgconf) break else: continue - def replace (self, newconf, curconf): - """Archive existing config, then replace current with new""" - full = os.path.join (self.options ['archive-dir'], curconf.lstrip ('/')) - + def do_rcs (self, newconf, curconf, mrgconf): + """Archive existing config in rcs (on trunk). Then, if an old branch + version exists, merge the user's changes and the distributed changes + and put the result into mrgconf. Next, archive the new distributed + version on the 1.1.1 branch of the rcs file. We return the mrgconf + name if a merge happened, else newconf.""" + archive = os.path.join (self.options ['archive-dir'], curconf.lstrip ('/')) try: - os.makedirs (os.path.dirname (full)) + os.makedirs (os.path.dirname (archive)) except: pass - count = 1 - while count < 1000: - archive = full + '.' + str(count) + try: + shutil.copy2 (curconf, archive) + except (IOError, os.error), why: + print >> sys.stderr, 'dispatch-conf: Error copying %s to %s: %s; fatal' % \ + (curconf, archive, str(why)) + if (os.path.exists (archive + ',v')): + os.system (RCS_LOCK + ' ' + archive) + os.system (RCS_PUT + ' ' + archive) + + # There is probably a better way of checking for the branch... + os.system (RCS_GET + ' -r' + RCS_BRANCH + ' ' + archive) + has_branch = os.path.exists (archive) - if os.path.exists (archive): - count = count + 1 - continue - else: - try: - shutil.copy2 (curconf, archive) - except (IOError, os.error), why: - print >> sys.stderr, 'dispatch-conf: Error copying %s to %s: %s; fatal' % \ - (curconf, archive, str(why)) - try: - os.rename (newconf, curconf) - except (IOError, os.error), why: - print >> sys.stderr, 'dispatch-conf: Error renaming %s to %s: %s; fatal' % \ - (newconf, curconf, str(why)) - break - else: - print >> sys.stderr, 'dispatch-conf: Error archiving files -- exhausted slots???; fatal' - sys.exit (1) + try: + shutil.copy2 (newconf, archive) + except (IOError, os.error), why: + print >> sys.stderr, 'dispatch-conf: Error copying %s to %s: %s; fatal' % \ + (newconf, archive, str(why)) + if has_branch: + # This puts the results of the merge into mrgconf. + os.system (RCS_MERGE % (archive, mrgconf)) + # Commit the last-distributed version onto the branch. + os.system (RCS_LOCK + RCS_BRANCH + ' ' + archive) + os.system (RCS_PUT + ' -r' + RCS_BRANCH + ' ' + archive) + return mrgconf + + # Forcefully commit the last-distributed version onto the branch. + os.system (RCS_PUT + ' -f -r' + RCS_BRANCH + ' ' + archive) + return newconf + + + def replace (self, newconf, curconf): + """Replace current config with the new/merged version""" + try: + os.rename (newconf, curconf) + except (IOError, os.error), why: + print >> sys.stderr, 'dispatch-conf: Error renaming %s to %s: %s; fatal' % \ + (newconf, curconf, str(why)) def massage (self, newconfigs): @@ -172,12 +244,15 @@ def do_help (self): print; print - print ' q -- quit' + print ' u -- update current config with new config and continue' + print ' z -- zap (delete) new config and continue' + print ' n -- skip to next config, leave all intact' + print ' e -- edit new config' + print ' m -- interactively merge current and new configs' + print ' l -- look at diff between pre-merged and merged configs' + print ' t -- toggle new config between merged and pre-merged state' print ' h -- this screen' - print ' n -- next/skip to next config, leave all intact' - print ' f -- interactively fuse/merge current and new configs' - print ' k -- kill/remove new config and continue' - print ' s -- supercede current config with new and continue' + print ' q -- quit' print; print 'press any key to return to diff...',