threads / patch / 6575

patch, 7 partsAllows extraction of information about remotes.

Subject: [PATCH 4/7] Allows extraction of information about remotes.

## tl;dr

10 messages between Jan 29, 2007 and Feb 1, 2007. Diffs are folded; open one to read it.

replies: 9people: 2as markdown or json

Yann Dirson· Jan 29, 2007, 23:05 UTC · lore

[PATCH 0/7] StGIT, remotes, and parent

This new series should bring the first 5 patches into sufficiently reasonable shape, so I'm presenting them for inclusion. The last 2 ones are still there for context only.

This new revision of the series brings:
- Better use of stgit.config in subsequent patches, notably deported
the remaining call to repo-config into stgit.config:
config.sections_matching(regexp)
- Caching in stgit.config.  I implemented this as on-demand caching,
initially because it was straightforward, but it turns out that
sections_matching() naturally fits in this model, and i'm not sure how
we could have implemented this in the pre-caching model.
- The usual bunch of misc cleanups.
Best regards,
-- 
Yann Dirson    <ydirson@altern.org> |
Debian-related: <dirson@debian.org> |   Support Debian GNU/Linux:
                                    |  Freedom, Power, Stability, Gratis
     http://ydirson.free.fr/        | Check <http://www.debian.org/>
Yann Dirson· Jan 29, 2007, 23:05 UTC · re: Yann Dirson · lore

[PATCH 1/7] Make stgit.config use git-repo-config.

This is an initial implementation without any sort of caching. If we want that, we could do it with this new API. We could even just simply generalize the use of ConfigOption instead.

I just copypasted __run from stgit.git to avoid too much work, but it's definitely something to cleanup.

Signed-off-by: Yann Dirson <ydirson@altern.org>
---
 stgit/commands/common.py  |    5 +
 stgit/commands/mail.py    |   36 +++-------
 stgit/commands/pull.py    |    9 +-
 stgit/commands/refresh.py |    2 -
 stgit/config.py           |  166 +++++++++++++++++++++++----------------------
 stgit/git.py              |   10 +--
 stgit/gitmergeonefile.py  |    7 +-
 stgit/stack.py            |    5 +
 8 files changed, 113 insertions(+), 127 deletions(-)
Show changes to 8 files +113 −127

stgit/commands/common.py, stgit/commands/mail.py, stgit/commands/pull.py, stgit/commands/refresh.py, stgit/config.py, stgit/git.py, stgit/gitmergeonefile.py, stgit/stack.py

diff --git a/stgit/commands/common.py b/stgit/commands/common.py
index 2e57824..432edce 100644
--- a/stgit/commands/common.py
+++ b/stgit/commands/common.py
@@ -311,9 +311,10 @@ def address_or_alias(addr_str):
         if addr.find('@') >= 0:
             # it's an e-mail address
             return addr
-        if config.has_option('mail "alias"', addr):
+        alias = config.get('mail.alias.'+addr)
+        if alias:
             # it's an alias
-            return config.get('mail "alias"', addr)
+            return alias
 
         raise CmdException, 'unknown e-mail alias: %s' % addr
 
diff --git a/stgit/commands/mail.py b/stgit/commands/mail.py
index 6f61b83..7f20f13 100644
--- a/stgit/commands/mail.py
+++ b/stgit/commands/mail.py
@@ -129,9 +129,8 @@ def __get_sender():
     """Return the 'authname <authemail>' string as read from the
     configuration file
     """
-    if config.has_option('stgit', 'sender'):
-        sender = config.get('stgit', 'sender')
-    else:
+    sender=config.get('stgit.sender')
+    if not sender:
         try:
             sender = str(git.user())
         except git.GitException:
@@ -207,10 +206,7 @@ def __build_address_headers(msg, options, extra_cc = []):
     cc_addr = ''
     bcc_addr = ''
 
-    if config.has_option('stgit', 'autobcc'):
-        autobcc = config.get('stgit', 'autobcc')
-    else:
-        autobcc = ''
+    autobcc = config.get('stgit.autobcc') or ''
 
     if options.to:
         to_addr = ', '.join(options.to)
@@ -283,8 +279,9 @@ def __edit_message(msg):
     f.close()
 
     # the editor
-    if config.has_option('stgit', 'editor'):
-        editor = config.get('stgit', 'editor')
+    editor = config.get('stgit.editor')
+    if editor:
+        pass
     elif 'EDITOR' in os.environ:
         editor = os.environ['EDITOR']
     else:
@@ -469,14 +466,7 @@ def func(parser, options, args):
     """Send the patches by e-mail using the patchmail.tmpl file as
     a template
     """
-    smtpserver = config.get('stgit', 'smtpserver')
-
-    smtpuser = None
-    smtppassword = None
-    if config.has_option('stgit', 'smtpuser'):
-        smtpuser = config.get('stgit', 'smtpuser')
-    if config.has_option('stgit', 'smtppassword'):
-        smtppassword = config.get('stgit', 'smtppassword')
+    smtpserver = config.get('stgit.smtpserver')
 
     applied = crt_series.get_applied()
 
@@ -488,11 +478,8 @@ def func(parser, options, args):
     else:
         raise CmdException, 'Incorrect options. Unknown patches to send'
 
-    if options.smtp_password:
-        smtppassword = options.smtp_password
-
-    if options.smtp_user:
-        smtpuser = options.smtp_user
+    smtppassword = options.smtp_password or config.get('stgit.smtppassword')
+    smtpuser = options.smtp_user or config.get('stgit.smtpuser')
 
     if (smtppassword and not smtpuser):
         raise CmdException, 'SMTP password supplied, username needed'
@@ -508,10 +495,7 @@ def func(parser, options, args):
     else:
         ref_id = options.refid
 
-    if options.sleep != None:
-        sleep = options.sleep
-    else:
-        sleep = config.getint('stgit', 'smtpdelay')
+    sleep = options.sleep or config.getint('stgit.smtpdelay')
 
     # send the cover message (if any)
     if options.cover or options.edit_cover:
diff --git a/stgit/commands/pull.py b/stgit/commands/pull.py
index 7c5db22..e47858a 100644
--- a/stgit/commands/pull.py
+++ b/stgit/commands/pull.py
@@ -53,11 +53,7 @@ def func(parser, options, args):
     if len(args) >= 1:
         repository = args[0]
     else:
-        section = 'branch "%s"' % git.get_head_file()
-        if config.has_option(section, 'remote'):
-            repository = config.get(section, 'remote')
-        else:
-            repository = 'origin'
+        repository = config.get('branch.%s.remote' % git.get_head_file()) or 'origin'
 
     refspec = None
     if len(args) == 2:
@@ -88,8 +84,7 @@ def func(parser, options, args):
         push_patches(applied, options.merged)
 
     # maybe tidy up
-    repack = config.get('stgit', 'keepoptimized')
-    if repack == 'yes':
+    if config.get('stgit.keepoptimized') == 'yes':
         git.repack()
 
     print_crt_patch()
diff --git a/stgit/commands/refresh.py b/stgit/commands/refresh.py
index 0efe1df..7a9bf7d 100644
--- a/stgit/commands/refresh.py
+++ b/stgit/commands/refresh.py
@@ -77,7 +77,7 @@ options = [make_option('-f', '--force',
 
 
 def func(parser, options, args):
-    autoresolved = config.get('stgit', 'autoresolved')
+    autoresolved = config.get('stgit.autoresolved')
 
     if autoresolved != 'yes':
         check_conflicts()
diff --git a/stgit/config.py b/stgit/config.py
index a6afbfd..d42754b 100644
--- a/stgit/config.py
+++ b/stgit/config.py
@@ -18,91 +18,97 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 """
 
-import os, ConfigParser
-from StringIO import StringIO
+import os, re
 from stgit import basedir
 
-config = ConfigParser.RawConfigParser()
-
-def git_config(filename):
-    """Open a git config file and convert it to be understood by
-    Python."""
-    try:
-        f = file(filename)
-        cont = False
-        lines = []
-        for line in f:
-            line = line.strip()
-
-            if cont:
-                # continued line, add a space at the beginning
-                line = ' ' + line
-
-            if line and line[-1] == '\\':
-                line = line[:-1].rstrip()
-                cont = True
-            else:
-                line = line + '\n'
-                cont = False
-
-            lines.append(line)
-
-        f.close()
-        cfg_str = ''.join(lines)
-    except IOError:
-        cfg_str = ''
-
-    strio = StringIO(cfg_str)
-    strio.name = filename
-
-    return strio
+class GitConfigException(Exception):
+    pass
+
+class GitConfig:
+    __defaults={
+        'stgit.autoresolved':	'no',
+        'stgit.smtpserver':	'localhost:25',
+        'stgit.smtpdelay':	'5',
+        'stgit.pullcmd':	'git-pull',
+        'stgit.merger':		'diff3 -L current -L ancestor -L patched -m -E ' \
+				'"%(branch1)s" "%(ancestor)s" "%(branch2)s" > "%(output)s"',
+        'stgit.autoimerge':	'no',
+        'stgit.keeporig':	'yes',
+        'stgit.keepoptimized':	'no',
+        'stgit.extensions':	'.ancestor .current .patched',
+        'stgit.shortnr':	 '5'
+        }
+
+    def __run(self, cmd, args=None):
+        """__run: runs cmd using spawnvp.
+    
+        Runs cmd using spawnvp.  The shell is avoided so it won't mess up
+        our arguments.  If args is very large, the command is run multiple
+        times; args is split xargs style: cmd is passed on each
+        invocation.  Unlike xargs, returns immediately if any non-zero
+        return code is received.  
+        """
+        
+        args_l=cmd.split()
+        if args is None:
+            args = []
+        for i in range(0, len(args)+1, 100):
+            r=os.spawnvp(os.P_WAIT, args_l[0], args_l + args[i:min(i+100, len(args))])
+        if r:
+            return r
+        return 0
+    
+    def get(self, name):
+        stream = os.popen('git repo-config --get %s' % name, 'r')
+        value = stream.readline().strip()
+        stream.close()
+        if len(value) > 0:
+            return value
+        elif (self.__defaults.has_key(name)):
+            return self.__defaults[name]
+        else:
+            return None
+
+    def getall(self, name):
+        stream = os.popen('git repo-config --get-all %s' % name, 'r')
+        values = [line.strip() for line in stream]
+        stream.close()
+        return values
+
+    def getint(self, name):
+        value = self.get(name)
+        if value.isdigit():
+            return int(value)
+        else:
+            raise GitConfigException, 'Value for "%s" is not an integer: "%s"' % (name, value)
+
+    def set(self, name, value):
+        self.__run('git-repo-config', [name, value])
+
+    def sections_matching(self, regexp):
+        """Takes a regexp with a single group, matches it against all
+        config variables, and returns a list whose members are the
+        group contents, for all variable names matching the regexp.
+        """
+        result = []
+        stream = os.popen('git repo-config --get-regexp "^%s$"' % regexp, 'r')
+        for line in stream:
+            m = re.match('^%s ' % regexp, line)
+            if m:
+                result.append(m.group(1))
+        stream.close()
+        return result
+        
+config=GitConfig()
 
 def config_setup():
     global config
 
-    # Set the defaults
-    config.add_section('stgit')
-    config.set('stgit', 'autoresolved', 'no')
-    config.set('stgit', 'smtpserver', 'localhost:25')
-    config.set('stgit', 'smtpdelay', '5')
-    config.set('stgit', 'pullcmd', 'git-pull')
-    config.set('stgit', 'merger',
-               'diff3 -L current -L ancestor -L patched -m -E ' \
-               '"%(branch1)s" "%(ancestor)s" "%(branch2)s" > "%(output)s"')
-    config.set('stgit', 'autoimerge', 'no')
-    config.set('stgit', 'keeporig', 'yes')
-    config.set('stgit', 'keepoptimized', 'no')
-    config.set('stgit', 'extensions', '.ancestor .current .patched')
-    config.set('stgit', 'shortnr', '5')
-
-    # Read the configuration files (if any) and override the default settings
-    # stgitrc are read for backward compatibility
-    config.read('/etc/stgitrc')
-    config.read(os.path.expanduser('~/.stgitrc'))
-    config.read(os.path.join(basedir.get(), 'stgitrc'))
-
-    # GIT configuration files can have a [stgit] section
-    try:
-        global_config = os.environ['GIT_CONFIG']
-    except KeyError:
-        global_config = os.path.expanduser('~/.gitconfig')
-    try:
-        local_config = os.environ['GIT_CONFIG_LOCAL']
-    except KeyError:
-        local_config = os.path.join(basedir.get(), 'config')
-    config.readfp(git_config(global_config))
-    config.readfp(git_config(local_config))
-
     # Set the PAGER environment to the config value (if any)
-    if config.has_option('stgit', 'pager'):
-        os.environ['PAGER'] = config.get('stgit', 'pager')
-
-    # [gitmergeonefile] section is deprecated. In case it exists copy the
-    # options/values to the [stgit] one
-    if config.has_section('gitmergeonefile'):
-        for option, value in config.items('gitmergeonefile'):
-            config.set('stgit', option, value)
-
+    pager = config.get('stgit.pager')
+    if pager:
+        os.environ['PAGER'] = pager
+    # FIXME: handle EDITOR the same way ?
 
 class ConfigOption:
     """Delayed cached reading of a configuration option.
@@ -114,7 +120,7 @@ class ConfigOption:
 
     def __str__(self):
         if not self.__value:
-            self.__value = config.get(self.__section, self.__option)
+            self.__value = config.get(self.__section + '.' + self.__option)
         return self.__value
 
 
@@ -127,7 +133,7 @@ def file_extensions():
     global __extensions
 
     if not __extensions:
-        cfg_ext = config.get('stgit', 'extensions').split()
+        cfg_ext = config.get('stgit.extensions').split()
         if len(cfg_ext) != 3:
             raise CmdException, '"extensions" configuration error'
 
diff --git a/stgit/git.py b/stgit/git.py
index 7d99338..249080e 100644
--- a/stgit/git.py
+++ b/stgit/git.py
@@ -449,10 +449,10 @@ def user():
     """
     global __user
     if not __user:
-        if config.has_option('user', 'name') \
-               and config.has_option('user', 'email'):
-            __user = Person(config.get('user', 'name'),
-                            config.get('user', 'email'))
+        name=config.get('user.name')
+        email=config.get('user.email')
+        if name and email:
+            __user = Person(name, email)
         else:
             raise GitException, 'unknown user details'
     return __user;
@@ -818,7 +818,7 @@ def pull(repository = 'origin', refspec = None):
     if refspec:
         args.append(refspec)
 
-    if __run(config.get('stgit', 'pullcmd'), args) != 0:
+    if __run(config.get('stgit.pullcmd'), args) != 0:
         raise GitException, 'Failed "git-pull %s"' % repository
 
 def repack():
diff --git a/stgit/gitmergeonefile.py b/stgit/gitmergeonefile.py
index 587c8c4..b9d02aa 100644
--- a/stgit/gitmergeonefile.py
+++ b/stgit/gitmergeonefile.py
@@ -102,9 +102,8 @@ def interactive_merge(filename):
     """Run the interactive merger on the given file. Note that the
     index should not have any conflicts.
     """
-    try:
-        imerger = config.get('stgit', 'imerger')
-    except Exception, err:
+    imerger = config.get('stgit.imerger')
+    if not imerger:
         raise GitMergeException, 'Configuration error: %s' % err
 
     extensions = file_extensions()
@@ -185,7 +184,7 @@ def merge(orig_hash, file1_hash, file2_hash,
                     os.system('git-update-index --cacheinfo %s %s %s'
                               % (file1_mode, file1_hash, path))
 
-                    if config.get('stgit', 'autoimerge') == 'yes':
+                    if config.get('stgit.autoimerge') == 'yes':
                         print >> sys.stderr, \
                               'Trying the interactive merge'
                         try:
diff --git a/stgit/stack.py b/stgit/stack.py
index 5237084..6281f36 100644
--- a/stgit/stack.py
+++ b/stgit/stack.py
@@ -92,8 +92,9 @@ def edit_file(series, line, comment, show_patch = True):
     f.close()
 
     # the editor
-    if config.has_option('stgit', 'editor'):
-        editor = config.get('stgit', 'editor')
+    editor = config.get('stgit.editor')
+    if editor:
+        pass
     elif 'EDITOR' in os.environ:
         editor = os.environ['EDITOR']
     else:
Yann Dirson· Jan 29, 2007, 23:05 UTC · re: Yann Dirson · lore

[PATCH 2/7] Add caching to the new config class.

Signed-off-by: Yann Dirson <ydirson@altern.org>
---
 stgit/config.py |   19 ++++++++++++++++---
 1 files changed, 16 insertions(+), 3 deletions(-)
Show changes to stgit/config.py +16 −3
diff --git a/stgit/config.py b/stgit/config.py
index d42754b..901f600 100644
--- a/stgit/config.py
+++ b/stgit/config.py
@@ -39,6 +39,8 @@ class GitConfig:
         'stgit.shortnr':	 '5'
         }
 
+    __cache={}
+
     def __run(self, cmd, args=None):
         """__run: runs cmd using spawnvp.
     
@@ -59,20 +61,31 @@ class GitConfig:
         return 0
     
     def get(self, name):
+        if self.__cache.has_key(name):
+            return self.__cache[name]
+
         stream = os.popen('git repo-config --get %s' % name, 'r')
         value = stream.readline().strip()
         stream.close()
         if len(value) > 0:
-            return value
+            pass
         elif (self.__defaults.has_key(name)):
-            return self.__defaults[name]
+            value = self.__defaults[name]
         else:
-            return None
+            value = None
+
+        self.__cache[name] = value
+        return value
 
     def getall(self, name):
+        if self.__cache.has_key(name):
+            return self.__cache[name]
+
         stream = os.popen('git repo-config --get-all %s' % name, 'r')
         values = [line.strip() for line in stream]
         stream.close()
+
+        self.__cache[name] = values
         return values
 
     def getint(self, name):
Yann Dirson· Jan 29, 2007, 23:05 UTC · re: Yann Dirson · lore

[PATCH 3/7] Move identification of parent branch's remote def up into stack class.

Also adds the __set_parent_remote() counterpart method, but only private since it is expected to be called only through set_parent, to be introduced in subsequent patch.

Signed-off-by: Yann Dirson <ydirson@altern.org>
---
 stgit/commands/pull.py |    2 +-
 stgit/stack.py         |    6 ++++++
 2 files changed, 7 insertions(+), 1 deletions(-)
Show changes to 2 files +7 −1

stgit/commands/pull.py, stgit/stack.py

diff --git a/stgit/commands/pull.py b/stgit/commands/pull.py
index e47858a..0608fb6 100644
--- a/stgit/commands/pull.py
+++ b/stgit/commands/pull.py
@@ -53,7 +53,7 @@ def func(parser, options, args):
     if len(args) >= 1:
         repository = args[0]
     else:
-        repository = config.get('branch.%s.remote' % git.get_head_file()) or 'origin'
+        repository = crt_series.get_parent_remote()
 
     refspec = None
     if len(args) == 2:
diff --git a/stgit/stack.py b/stgit/stack.py
index 6281f36..947b416 100644
--- a/stgit/stack.py
+++ b/stgit/stack.py
@@ -407,6 +407,12 @@ class Series(StgitObject):
     def set_description(self, line):
         self._set_field('description', line)
 
+    def get_parent_remote(self):
+        return config.get('branch.%s.remote' % self.__name) or 'origin'
+
+    def __set_parent_remote(self, remote):
+        value = config.set('branch.%s.remote' % self.__name, remote)
+
     def __patch_is_current(self, patch):
         return patch.get_name() == self.get_current()
 
Yann Dirson· Jan 29, 2007, 23:05 UTC · re: Yann Dirson · lore

We want to know the list of declared remotes, the local branches they hold, and which remotes holds a given branch. All this regardless of where the information is stored.

If there are any git-1.5 remotes declared in .git/config, we suppose you know what you're doing and they will take precedence on any pre-1.5 remotes.

This does not use git-remote for now, since it is 1.5 material not released yet, does not support legacy branches/ remotes, and does not allow yet to query all of the information we need.

Signed-off-by: Yann Dirson <ydirson@altern.org>
---
 stgit/git.py |   68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 68 insertions(+), 0 deletions(-)
Show changes to stgit/git.py +68 −0
diff --git a/stgit/git.py b/stgit/git.py
index 249080e..038aaac 100644
--- a/stgit/git.py
+++ b/stgit/git.py
@@ -881,3 +881,71 @@ def modifying_revs(files, base_rev):
     revs = [line.strip() for line in _output_lines(cmd + files)]
 
     return revs
+
+
+def refspec_localpart(refspec):
+    m = re.match('^[^:]*:([^:]*)$', refspec)
+    if m:
+        return m.group(1)
+    else:
+        raise GitException, 'Cannot parse refspec "%s"' % line
+
+def refspec_remotepart(refspec):
+    m = re.match('^([^:]*):[^:]*$', refspec)
+    if m:
+        return m.group(1)
+    else:
+        raise GitException, 'Cannot parse refspec "%s"' % line
+    
+
+def __remotes_from_config():
+    return config.sections_matching(r'remote\.(.*)\.url')
+
+def __remotes_from_dir(dir):
+    return os.listdir(os.path.join(basedir.get(), dir))
+
+def remotes_list():
+    """Return the list of remotes in the repository
+    """
+
+    return set(__remotes_from_config()) | \
+           set(__remotes_from_dir('remotes')) | \
+           set(__remotes_from_dir('branches'))
+
+def remotes_local_branches(remote):
+    """Returns the list of local branches fetched from given remote
+    """
+
+    branches = []
+    if remote in __remotes_from_config():
+        for line in config.getall('remote.%s.fetch' % remote):
+            branches.append(refspec_localpart(line))
+    elif remote in __remotes_from_dir('remotes'):
+        stream = open(os.path.join(basedir.get(), 'remotes', remote), 'r')
+        for line in stream:
+            # Only consider Pull lines
+            m = re.match('^Pull: (.*)\n$', line)
+            branches.append(refspec_localpart(m.group(1)))
+        stream.close()
+    elif remote in __remotes_from_dir('branches'):
+        # old-style branches only declare one branch
+        branches.append('refs/heads/'+remote);
+    else:
+        raise GitException, 'Unknown remote "%s"' % remote
+
+    return branches
+
+def identify_remote(branchname):
+    """Return the name for the remote to pull the given branchname
+    from, or None if we believe it is a local branch.
+    """
+
+    for remote in remotes_list():
+        if branchname in remotes_local_branches(remote):
+            return remote
+
+    # FIXME: in the case of local branch we should maybe set remote to
+    # "." but are we even sure it is the only case left ?
+
+    # if we get here we've found nothing
+    return None
Yann Dirson· Jan 29, 2007, 23:05 UTC · re: Yann Dirson · lore

[PATCH 5/7] Basic support for keeping a ref to the parent branch.

This adds a framework to handle the parent branch of a stack, in addition to the parent remote, and to set them when creating a stack.

Signed-off-by: Yann Dirson <ydirson@altern.org>
---
 TODO           |    2 --
 stgit/stack.py |   24 +++++++++++++++++++++++-
 2 files changed, 23 insertions(+), 3 deletions(-)
Show changes to 2 files +23 −3

TODO, stgit/stack.py

diff --git a/TODO b/TODO
index 549bc9d..884b831 100644
--- a/TODO
+++ b/TODO
@@ -20,8 +20,6 @@ The future, when time allows or if someone else does them:
 - multiple heads in a patch - useful for forking a patch,
   synchronising with other patches (diff format or in other
   repositories)
-- "pull" argument should default to a sane value, "origin" is wrong in
-  many cases
 - commit directly to a patch which is not top
 - patch synchronisation between between branches (as some people,
   including me have the same patches based on different branches and
diff --git a/stgit/stack.py b/stgit/stack.py
index 947b416..9640eb5 100644
--- a/stgit/stack.py
+++ b/stgit/stack.py
@@ -413,6 +413,26 @@ class Series(StgitObject):
     def __set_parent_remote(self, remote):
         value = config.set('branch.%s.remote' % self.__name, remote)
 
+    def get_parent_branch(self):
+        value = config.get('branch.%s.merge' % self.__name)
+        if value:
+            return value
+        elif rev_parse('heads/origin'):
+            return 'heads/origin'
+        else:
+            raise StackException, 'Cannot find a parent branch for "%s"' % self.__name
+
+    def __set_parent_branch(self, name):
+        config.set('branch.%s.merge' % self.__name, name)
+
+    def set_parent(self, remote, localbranch):
+        if localbranch:
+            self.__set_parent_branch(localbranch)
+            if remote:
+                self.__set_parent_remote(remote)
+        elif remote:
+            raise StackException, 'Remote "%s" without a branch cannot be used as parent' % remote
+
     def __patch_is_current(self, patch):
         return patch.get_name() == self.get_current()
 
@@ -466,7 +486,7 @@ class Series(StgitObject):
         """
         return os.path.isdir(self.__patch_dir)
 
-    def init(self, create_at=False):
+    def init(self, create_at=False, parent_remote=None, parent_branch=None):
         """Initialises the stgit series
         """
         bases_dir = os.path.join(self.__base_dir, 'refs', 'bases')
@@ -483,6 +503,8 @@ class Series(StgitObject):
 
         os.makedirs(self.__patch_dir)
 
+        self.set_parent(parent_remote, parent_branch)
+        
         create_dirs(bases_dir)
 
         self.create_empty_field('applied')
Yann Dirson· Jan 29, 2007, 23:05 UTC · re: Yann Dirson · lore

[PATCH 6/7] Have 'stg branch --create' record parent information.

Signed-off-by: Yann Dirson <ydirson@altern.org>
---
 stgit/commands/branch.py |   37 ++++++++++++++++++++++++++++++++++---
 1 files changed, 34 insertions(+), 3 deletions(-)
Show changes to stgit/commands/branch.py +34 −3
diff --git a/stgit/commands/branch.py b/stgit/commands/branch.py
index d348409..f074d47 100644
--- a/stgit/commands/branch.py
+++ b/stgit/commands/branch.py
@@ -123,10 +123,41 @@ def func(parser, options, args):
         check_head_top_equal()
 
         tree_id = None
-        if len(args) == 2:
+        if len(args) >= 2:
+            try:
+                if git.rev_parse(args[1]) == git.rev_parse('refs/heads/' + args[1]):
+                    # we are for sure refering to a branch
+                    parentbranch = 'refs/heads/' + args[1]
+                    print 'Recording "%s" as parent branch.' % parentbranch
+                elif git.rev_parse(args[1]) and re.search('/', args[1]):
+                    # FIXME: should the test be more strict ?
+                    parentbranch = args[1]
+                else:
+                    # Note: this includes refs to StGIT patches
+                    print 'Don\'t know how to determine parent branch from "%s".' % args[1]
+                    parentbranch = None
+            except git.GitException:
+                # should use a more specific exception to catch only non-git refs ?
+                print 'Don\'t know how to determine parent branch from "%s".' % args[1]
+                parentbranch = None
+
             tree_id = git_id(args[1])
-        
-        stack.Series(args[0]).init(create_at = tree_id)
+        else:
+            # branch stack off current branch
+            parentbranch = git.get_head_file()
+
+        if parentbranch:
+            parentremote = git.identify_remote(parentbranch)
+            if parentremote:
+                print 'Using "%s" remote to pull parent from.' % parentremote
+            else:
+                print 'Not identified a remote to pull parent from.'
+        else:
+            parentremote = None
+
+        stack.Series(args[0]).init(create_at = tree_id,
+                                   parent_remote = parentremote,
+                                   parent_branch = parentbranch)
 
         print 'Branch "%s" created.' % args[0]
         return
Yann Dirson· Jan 29, 2007, 23:05 UTC · re: Yann Dirson · lore

[PATCH 7/7] Make 'stg pull' use git-fetch and not git-pull.

We introduce a new pull-does-rebase setting, as companion to pullcmd. This allows to use both pullcmd's that need a rebase (like git-fetch) and pullcmd's that do not (like git-pull).

To be able to rebase, we rely on parent information being available for the stack.

Signed-off-by: Yann Dirson <ydirson@altern.org>
---
 examples/gitconfig |    8 +++++++-
 stgit/config.py    |    3 ++-
 stgit/git.py       |   15 ++++++++++-----
 3 files changed, 19 insertions(+), 7 deletions(-)
Show changes to 3 files +19 −7

examples/gitconfig, stgit/config.py, stgit/git.py

diff --git a/examples/gitconfig b/examples/gitconfig
index ee68d07..60f6e85 100644
--- a/examples/gitconfig
+++ b/examples/gitconfig
@@ -33,8 +33,14 @@
 	#pager = ~/share/stgit/contrib/diffcol.sh
 	#pager = filterdiff --annotate | colordiff | less -FRX
 
-	# GIT pull command (should take the same arguments as git-pull)
+	# GIT pull command (should take the same arguments as
+	# git-fetch or git-pull).  By default:
+	#pullcmd = git-fetch
+	#pull-does-rebase = yes
+	# Alternative (old behaviour), less intuitive but maybe useful
+	# for some workflows:
 	#pullcmd = git-pull
+	#pull-does-rebase = no
 
 	# The three-way merge tool. Note that the 'output' file contains the
 	# same data as 'branch1'. This is useful for tools that do not take an
diff --git a/stgit/config.py b/stgit/config.py
index 901f600..48b4e2d 100644
--- a/stgit/config.py
+++ b/stgit/config.py
@@ -29,7 +29,8 @@ class GitConfig:
         'stgit.autoresolved':	'no',
         'stgit.smtpserver':	'localhost:25',
         'stgit.smtpdelay':	'5',
-        'stgit.pullcmd':	'git-pull',
+        'stgit.pullcmd':	'git-fetch',
+        'stgit.pull-does-rebase': 'yes',
         'stgit.merger':		'diff3 -L current -L ancestor -L patched -m -E ' \
 				'"%(branch1)s" "%(ancestor)s" "%(branch2)s" > "%(output)s"',
         'stgit.autoimerge':	'no',
diff --git a/stgit/git.py b/stgit/git.py
index 038aaac..e30b959 100644
--- a/stgit/git.py
+++ b/stgit/git.py
@@ -808,18 +808,23 @@ def reset(files = None, tree_id = None, check_out = True):
         __set_head(tree_id)
 
 def pull(repository = 'origin', refspec = None):
-    """Pull changes from the remote repository. At the moment, just
-    use the 'git-pull' command
+    """Pull changes from the remote repository. Uses 'git-fetch'
+    and moves the stack base.
     """
-    # 'git-pull' updates the HEAD
+    # we update the HEAD
     __clear_head_cache()
 
     args = [repository]
     if refspec:
         args.append(refspec)
 
-    if __run(config.get('stgit.pullcmd'), args) != 0:
-        raise GitException, 'Failed "git-pull %s"' % repository
+    command = config.get('stgit.pullcmd')
+    if __run(command, args) != 0:
+        raise GitException, 'Failed "%s %s"' % (command, repository)
+
+    if (config.get('stgit.pull-does-rebase')):
+        # FIXME!
+        reset(tree_id = rev_parse(repository))
 
 def repack():
     """Repack all objects into a single pack
Catalin Marinas· Jan 31, 2007, 23:44 UTC · re: Yann Dirson · lore

Re: [PATCH 7/7] Make 'stg pull' use git-fetch and not git-pull.

On 29/01/07, Yann Dirson <ydirson@altern.org> wrote:
> We introduce a new pull-does-rebase setting, as companion to pullcmd.
> This allows to use both pullcmd's that need a rebase (like git-fetch)
> and pullcmd's that do not (like git-pull).
Thanks for the patches. I applied all of them but I have a question on this one:
> +    if (config.get('stgit.pull-does-rebase')):
> +        # FIXME!
> +        reset(tree_id = rev_parse(repository))
Why the "FIXME" here? The alternative is switch() and it might be safer.
-- 
Catalin
Yann Dirson· Feb 1, 2007, 18:47 UTC · re: Catalin Marinas · lore

Re: [PATCH 7/7] Make 'stg pull' use git-fetch and not git-pull.

On Wed, Jan 31, 2007 at 11:44:07PM +0000, Catalin Marinas wrote:
Show 13 quoted lines
> On 29/01/07, Yann Dirson <ydirson@altern.org> wrote:
> >We introduce a new pull-does-rebase setting, as companion to pullcmd.
> >This allows to use both pullcmd's that need a rebase (like git-fetch)
> >and pullcmd's that do not (like git-pull).
> 
> Thanks for the patches. I applied all of them but I have a question on this 
> one:
> 
> >+    if (config.get('stgit.pull-does-rebase')):
> >+        # FIXME!
> >+        reset(tree_id = rev_parse(repository))
> 
> Why the "FIXME" here? The alternative is switch() and it might be safer.

The last 2 patches, including the one with this FIXME, will need some work.

In this case, we should reset to the parent branch. It currently just works because traditionnaly we use refs/heads/origin from remote "origin", and "rev-parse origin" resolves to that branch.

Best regards,
-- 
Yann

← back to recent threads