From 24c2c90a2f166febcce9e79981cec0eb18fcf922 Mon Sep 17 00:00:00 2001
From: Jon Bernard <jbernard@tuxion.com>
Date: Wed, 29 Jun 2011 13:50:59 -0400
Subject: Add support for an arbitrary prefix

By default, dotfiles are stored with no prefix, so ~/.bashrc will link
to ~/Dotfiles/bashrc. Some folks, however, are already storing their
dotfiles in a repository where they have a prefix. '.' is common, but
I've seen one using '_' as well. This feature will allow those people to
specify their prefix in the configuration file without renaming all the
files in their repository. To do this, a configuration like:

    [dotfiles]
    prefix = .

in ~/.dotfilesrc is all that is needed.
---
 dotfiles/cli.py  | 49 ++++++++++++++++++++++++++++++++++---------------
 dotfiles/core.py | 14 ++++++--------
 2 files changed, 40 insertions(+), 23 deletions(-)

diff --git a/dotfiles/cli.py b/dotfiles/cli.py
index 2ce55bb..95e3b82 100644
--- a/dotfiles/cli.py
+++ b/dotfiles/cli.py
@@ -15,8 +15,6 @@ def parse_args():
     parser = OptionParser(usage="Usage: %prog ACTION [OPTION...] [FILE...]")
 
     parser.set_defaults(config=os.path.expanduser("~/.dotfilesrc"))
-    parser.set_defaults(repo=os.path.expanduser("~/Dotfiles"))
-    parser.set_defaults(prefix='')
     parser.set_defaults(ignore=[])
     parser.set_defaults(externals={})
 
@@ -26,6 +24,9 @@ def parse_args():
     parser.add_option("-R", "--repo", type="string", dest="repo",
             help="set repository location (default is ~/Dotfiles)")
 
+    parser.add_option("-p", "--prefix", type="string", dest="prefix",
+            help="set prefix character (default is None)")
+
     parser.add_option("-C", "--config", type="string", dest="config",
             help="set configuration file location (default is ~/.dotfilesrc)")
 
@@ -52,14 +53,9 @@ def parse_args():
 
     (opts, args) = parser.parse_args()
 
-    if not os.path.exists(opts.repo):
-        parser.error("Could not find dotfiles repository \"%s\"" % opts.repo)
-
-    if not opts.action:
-        parser.error("An action is required.")
-
-    if opts.action not in method_list(core.Dotfiles):
-        parser.error("No such action \"%s\"" % opts.action)
+    # Skip checking if the repository exists here. The user may have specified
+    # a command line argument or a configuration file, which will be examined
+    # next.
 
     return (opts, args)
 
@@ -77,14 +73,37 @@ def main():
     parser = ConfigParser.SafeConfigParser(config_defaults)
 
     if opts.config:
-
         parser.read(opts.config)
 
         if 'dotfiles' in parser.sections():
-            opts.repo = os.path.expanduser(parser.get('dotfiles', 'repository'))
-            opts.prefix = parser.get('dotfiles', 'prefix')
-            opts.ignore = eval(parser.get('dotfiles', 'ignore'))
-            opts.externals = eval(parser.get('dotfiles', 'externals'))
+
+            if not opts.repo:
+                if parser.get('dotfiles', 'repository'):
+                    opts.repo = os.path.expanduser(parser.get('dotfiles', 'repository'))
+                else:
+                    opts.repo = os.path.expanduser("~/Dotfiles")
+
+            if not opts.prefix:
+                if parser.get('dotfiles', 'prefix'):
+                    opts.prefix = parser.get('dotfiles', 'prefix')
+                else:
+                    opts.prefix = ''
+
+            if not opts.ignore and parser.get('dotfiles', 'ignore'):
+                opts.ignore = eval(parser.get('dotfiles', 'ignore'))
+
+            if not opts.externals and parser.get('dotfiles', 'externals'):
+                opts.externals = eval(parser.get('dotfiles', 'externals'))
+
+    if not os.path.exists(opts.repo):
+        print "%s\n" % USAGE
+        print NO_REPO_MESSAGE % opts.repo
+        exit(-1)
+
+    if not opts.action:
+        print "%s\n" % USAGE
+        print "Error: An action is required."
+        exit(-1)
 
     getattr(core.Dotfiles(location=opts.repo,
                           prefix=opts.prefix,
diff --git a/dotfiles/core.py b/dotfiles/core.py
index 3a13fd3..843f90f 100644
--- a/dotfiles/core.py
+++ b/dotfiles/core.py
@@ -36,7 +36,6 @@ class Dotfile(object):
         if self.status == '':
             print "Skipping \"%s\", already managed" % self.basename
             return
-        print "Adding \"%s\"" % self.basename
         shutil.move(self.name, self.target)
         os.symlink(self.target, self.name)
 
@@ -55,19 +54,18 @@ class Dotfiles(object):
 
     def __init__(self, location, prefix, ignore, externals, force):
         self.location = location
+        self.prefix = prefix
         self.force = force
         self.dotfiles = []
-        contents = [x for x in os.listdir(self.location)
-                    if x not in ignore]
+        contents = [x for x in os.listdir(self.location) if x not in ignore]
         for file in contents:
-            self.dotfiles.append(Dotfile(file,
+            self.dotfiles.append(Dotfile(file[len(prefix):],
                 os.path.join(self.location, file)))
         for file in externals.keys():
             self.dotfiles.append(Dotfile(file, externals[file]))
 
     def list(self, **kwargs):
-        for dotfile in sorted(self.dotfiles,
-                key=lambda dotfile: dotfile.name):
+        for dotfile in sorted(self.dotfiles, key=lambda dotfile: dotfile.name):
             if dotfile.status or kwargs.get('verbose', True):
                 print dotfile
 
@@ -83,7 +81,7 @@ class Dotfiles(object):
             if os.path.basename(file).startswith('.'):
                 Dotfile(file,
                         os.path.join(self.location,
-                                    os.path.basename(file).strip('.'))).add()
+                            self.prefix + os.path.basename(file).strip('.'))).add()
             else:
                 print "Skipping \"%s\", not a dotfile" % file
 
@@ -92,6 +90,6 @@ class Dotfiles(object):
             if os.path.basename(file).startswith('.'):
                 Dotfile(file,
                         os.path.join(self.location,
-                            os.path.basename(file).strip('.'))).remove()
+                            self.prefix + os.path.basename(file).strip('.'))).remove()
             else:
                 print "Skipping \"%s\", not a dotfile" % file
-- 
cgit v1.2.3


From cd9eb8fafc237ba8bd64549a2cecba7db613611b Mon Sep 17 00:00:00 2001
From: Jon Bernard <jbernard@tuxion.com>
Date: Wed, 29 Jun 2011 14:02:08 -0400
Subject: Show a helpful message to first-time users

---
 dotfiles/cli.py | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/dotfiles/cli.py b/dotfiles/cli.py
index 95e3b82..fda0a2e 100644
--- a/dotfiles/cli.py
+++ b/dotfiles/cli.py
@@ -5,6 +5,27 @@ from . import core
 import ConfigParser
 from optparse import OptionParser, OptionGroup
 
+USAGE = "Usage: %prog ACTION [OPTION...] [FILE...]"
+
+NO_REPO_MESSAGE = """Could not find dotfiles repository \"%s\"
+
+If this is your first time running dotfiles, you must first create a
+repository.  By default, dotfiles will look for '~/Dotfiles'. Something like:
+
+    $ mkdir ~/Dotfiles
+
+is all you need to do. If you don't like the default, you can put your
+repository wherever you like.  You have two choices once you've created your
+repository.  You can specify the path to the repository on the command line
+using the '-R' flag.  Alternatively, you can create a configuration file at
+'~/.dotfilesrc' and place the path to your repository in there.  The contents
+would look like:
+
+    [dotfiles]
+    repository = ~/.my-dotfiles-repo
+
+You can see more information by typing 'dotfiles -h'"""
+
 
 def method_list(object):
     return [method for method in dir(object)
@@ -12,7 +33,7 @@ def method_list(object):
 
 
 def parse_args():
-    parser = OptionParser(usage="Usage: %prog ACTION [OPTION...] [FILE...]")
+    parser = OptionParser(usage=USAGE)
 
     parser.set_defaults(config=os.path.expanduser("~/.dotfilesrc"))
     parser.set_defaults(ignore=[])
-- 
cgit v1.2.3