{"thread":{"id":"6563","subject":"[PATCH 2/5] Allows extraction of information about remotes.","startedAt":"2007-01-27T11:21:11Z","lastAt":"2007-01-27T11:21:38Z","messageCount":6,"participants":["Yann Dirson"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"32827","messageId":"20070127104024.16475.81445.stgit@gandelf.nowhere.earth","threadId":"6563","inReplyTo":null,"subject":"[PATCH 0/5] [DRAFT] StGIT, remotes, and parent information","fromName":"Yann Dirson","fromEmail":"ydirson@altern.org","sentAt":"2007-01-27T11:21:11Z","receivedAt":"2007-01-27T11:21:11Z","isPatch":true,"sender":{"key":"ydirson@altern.org","avatar":"https://avatars.githubusercontent.com/u/1190950?v=4"},"body":"Although they are not finished, it may be useful to give some context\nto current threads to publish a snapshot of what I'm working on, so\nhere it is.\n\n1/5 Move identification of parent branch's remote def up into stack class.\n\nMostly code reorg.\n\n2/5 Allows extraction of information about remotes.\n\nIntroduces a handful of functions to get informations about remotes in\nthe repository.  Makes use of remote.<branch>.fetch multi-valued\nconfig entry.\n\n3/5 Basic support for keeping a ref to the parent branch.\n\nCurrently relies on the failed assumption that config.set() does what\nit sounds, so rely instead on a rewritten config.py.\n\nThat's why I'd rather rewrite it rapidly without a dictionnary, at\nleast as a first step - we can make the implementation better\nafterwards, but first make it work.  Indeed I was originally only\nworking on the 5th patch of this stack, and all this is already a bit\nbeyond of what I was envisonning...\n\n4/5 Have 'stg branch --create' record parent information.\n\nPutting this all together.  Would also need to do something similar in\n'stg clone' and possibly other places, so some code surely needs to be\nfactorized.\n\n5/5 Make 'stg pull' use git-fetch and not git-pull.\n\nUnfinished.  Git-fetch takes care of using the remote information.\nStill have to use the previous work to find out the parent branch name\nto rebase onto.\n\nBest regards,\n-- \nYann Dirson    <ydirson@altern.org> |\nDebian-related: <dirson@debian.org> |   Support Debian GNU/Linux:\n                                    |  Freedom, Power, Stability, Gratis\n     http://ydirson.free.fr/        | Check <http://www.debian.org/>\n"},{"id":"32826","messageId":"20070127112117.16475.79541.stgit@gandelf.nowhere.earth","threadId":"6563","inReplyTo":"20070127104024.16475.81445.stgit@gandelf.nowhere.earth","subject":"[PATCH 1/5] Move identification of parent branch's remote def up into stack class.","fromName":"Yann Dirson","fromEmail":"ydirson@altern.org","sentAt":"2007-01-27T11:21:17Z","receivedAt":"2007-01-27T11:21:17Z","isPatch":true,"sender":{"key":"ydirson@altern.org","avatar":"https://avatars.githubusercontent.com/u/1190950?v=4"},"body":"\nAlso adds the __set_parent_remote() counterpart method, but only\nprivate since it is expected to be called only through set_parent, to\nbe introduced in subsequent patch.\n\nSigned-off-by: Yann Dirson <ydirson@altern.org>\n---\n\n stgit/commands/pull.py |    6 +-----\n stgit/stack.py         |   13 +++++++++++++\n 2 files changed, 14 insertions(+), 5 deletions(-)\n\ndiff --git a/stgit/commands/pull.py b/stgit/commands/pull.py\nindex 7c5db22..1a948be 100644\n--- a/stgit/commands/pull.py\n+++ b/stgit/commands/pull.py\n@@ -53,11 +53,7 @@ def func(parser, options, args):\n     if len(args) >= 1:\n         repository = args[0]\n     else:\n-        section = 'branch \"%s\"' % git.get_head_file()\n-        if config.has_option(section, 'remote'):\n-            repository = config.get(section, 'remote')\n-        else:\n-            repository = 'origin'\n+        repository = crt_series.get_parent_remote()\n \n     refspec = None\n     if len(args) == 2:\ndiff --git a/stgit/stack.py b/stgit/stack.py\nindex 2ae4dd5..e801f42 100644\n--- a/stgit/stack.py\n+++ b/stgit/stack.py\n@@ -397,6 +397,19 @@ class Series(StgitObject):\n     def set_description(self, line):\n         self._set_field('description', line)\n \n+    def get_parent_remote(self):\n+        section = 'branch \"%s\"' % self.__name\n+        if config.has_option(section, 'remote'):\n+            return config.get(section, 'remote')\n+        else:\n+            return 'origin'\n+\n+    def __set_parent_remote(self, remote):\n+        section = 'branch \"%s\"' % self.__name\n+        if not config.has_section(section):\n+            config.add_section(section)\n+        config.set(section, 'remote', remote)\n+\n     def __patch_is_current(self, patch):\n         return patch.get_name() == self.get_current()\n \n"},{"id":"32822","messageId":"20070127112122.16475.82057.stgit@gandelf.nowhere.earth","threadId":"6563","inReplyTo":"20070127104024.16475.81445.stgit@gandelf.nowhere.earth","subject":"[PATCH 2/5] Allows extraction of information about remotes.","fromName":"Yann Dirson","fromEmail":"ydirson@altern.org","sentAt":"2007-01-27T11:21:22Z","receivedAt":"2007-01-27T11:21:22Z","isPatch":true,"sender":{"key":"ydirson@altern.org","avatar":"https://avatars.githubusercontent.com/u/1190950?v=4"},"body":"\nWe want to know the list of declared remotes, the local branches they\nhold, and which remotes holds a given branch.  All this regardless of\nwhere the information is stored.\n\nIf there are any git-1.5 remotes declared in .git/config, we suppose\nyou know what you're doing and they will take precedence on any\npre-1.5 remotes.\n\nThis does not use git-remote for now, since it is 1.5 material not\nreleased yet, does not support legacy branches/ remotes, and does not\nallow yet to query all of the information we need.\n\nSigned-off-by: Yann Dirson <ydirson@altern.org>\n---\n\n stgit/git.py |   75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 75 insertions(+), 0 deletions(-)\n\ndiff --git a/stgit/git.py b/stgit/git.py\nindex 4c5685a..3c2c237 100644\n--- a/stgit/git.py\n+++ b/stgit/git.py\n@@ -879,3 +879,78 @@ def modifying_revs(files, base_rev):\n     revs = [line.strip() for line in _output_lines(cmd + files)]\n \n     return revs\n+\n+\n+def __remotes_from_config():\n+    configremotes = []\n+    stream = os.popen('git repo-config --get-regexp \"^remote\\..*\\.url$\"', 'r')\n+    for line in stream:\n+        m = re.match('^remote\\.(.*)\\.url ', line)\n+        if m:\n+            configremotes.append(m.group(1))\n+    stream.close()\n+    return configremotes\n+\n+def __remotes_from_dir(dir):\n+    return os.listdir(os.path.join(basedir.get(), dir))\n+\n+def remotes_list():\n+    \"\"\"Return the list of remotes in the repository\n+    \"\"\"\n+\n+    return set(__remotes_from_config()) | \\\n+           set(__remotes_from_dir('remotes')) | \\\n+           set(__remotes_from_dir('branches'))\n+\n+def remotes_local_branches(remote):\n+    \"\"\"Returns the list of local branches fetched from given remote\n+    \"\"\"\n+\n+    branches = []\n+    if remote in __remotes_from_config():\n+        stream = os.popen('git repo-config --get-all \"remote.%s.fetch\"' % remote, 'r')\n+        for line in stream:\n+            # FIXME: should factorize refspec handling\n+            m = re.match('^[^:]*:([^:]*)\\n$', line)\n+            if m:\n+                branches.append(m.group(1))\n+            else:\n+                raise GitException, 'Cannot parse refspec \"%s\"' % line\n+        stream.close()\n+    elif remote in __remotes_from_dir('remotes'):\n+        stream = open(os.path.join(basedir.get(), 'remotes', remote), 'r')\n+        for line in stream:\n+            # Only consider Pull lines\n+            m = re.match('^Pull: (.*)\\n$', line)\n+            if m:\n+                refspec = m.group(1)\n+                m = re.match('^[^:]*:([^:]*)$', refspec)\n+                if m:\n+                    branches.append(m.group(1))\n+                else:\n+                    raise GitException, 'Cannot parse refspec \"%s\"' % refspec\n+        stream.close()\n+        pass\n+    elif remote in __remotes_from_dir('branches'):\n+        # old-style branches only declare one branch\n+        branches.append('refs/heads/'+remote);\n+        pass\n+    else:\n+        raise GitException, 'Unknown remote \"%s\"' % remote\n+\n+    return branches\n+\n+def identify_remote(branchname):\n+    \"\"\"Return the name for the remote to pull the given branchname\n+    from, or None if we believe it is a local branch.\n+    \"\"\"\n+\n+    for remote in remotes_list():\n+        if branchname in remotes_local_branches(remote):\n+            return remote\n+\n+    # FIXME: in the case of local branch we should maybe set remote to\n+    # \".\" but are we even sure it is the only case left ?\n+\n+    # if we get here we've found nothing\n+    return None\n"},{"id":"32824","messageId":"20070127112127.16475.31003.stgit@gandelf.nowhere.earth","threadId":"6563","inReplyTo":"20070127104024.16475.81445.stgit@gandelf.nowhere.earth","subject":"[PATCH 3/5] Basic support for keeping a ref to the parent branch.","fromName":"Yann Dirson","fromEmail":"ydirson@altern.org","sentAt":"2007-01-27T11:21:27Z","receivedAt":"2007-01-27T11:21:27Z","isPatch":true,"sender":{"key":"ydirson@altern.org","avatar":"https://avatars.githubusercontent.com/u/1190950?v=4"},"body":"\nThis adds a framework to handle the parent branch of a stack, in\naddition to the parent remote, and to set them when creating a stack.\n\nSigned-off-by: Yann Dirson <ydirson@altern.org>\n---\n\n TODO           |    2 --\n stgit/stack.py |   27 ++++++++++++++++++++++++++-\n 2 files changed, 26 insertions(+), 3 deletions(-)\n\ndiff --git a/TODO b/TODO\nindex 549bc9d..884b831 100644\n--- a/TODO\n+++ b/TODO\n@@ -20,8 +20,6 @@ The future, when time allows or if someone else does them:\n - multiple heads in a patch - useful for forking a patch,\n   synchronising with other patches (diff format or in other\n   repositories)\n-- \"pull\" argument should default to a sane value, \"origin\" is wrong in\n-  many cases\n - commit directly to a patch which is not top\n - patch synchronisation between between branches (as some people,\n   including me have the same patches based on different branches and\ndiff --git a/stgit/stack.py b/stgit/stack.py\nindex e801f42..9d4f881 100644\n--- a/stgit/stack.py\n+++ b/stgit/stack.py\n@@ -410,6 +410,29 @@ class Series(StgitObject):\n             config.add_section(section)\n         config.set(section, 'remote', remote)\n \n+    def get_parent_branch(self):\n+        section = 'branch \"%s\"' % self.__name\n+        if config.has_option(section, 'merge'):\n+            return config.get(section, 'merge')\n+        elif rev_parse('heads/origin'):\n+            return 'heads/origin'\n+        else:\n+            raise StackException, 'Cannot find a parent branch for \"%s\"' % self.__name\n+\n+    def __set_parent_branch(self, name):\n+        section = 'branch \"%s\"' % self.__name\n+        if not config.has_section(section):\n+            config.add_section(section)\n+        config.set(section, 'merge', name)\n+\n+    def set_parent(self, remote, localbranch):\n+        if localbranch:\n+            self.__set_parent_branch(localbranch)\n+            if remote:\n+                self.__set_parent_remote(remote)\n+        elif remote:\n+            raise StackException, 'Remote \"%s\" without a branch cannot be used as parent' % remote\n+\n     def __patch_is_current(self, patch):\n         return patch.get_name() == self.get_current()\n \n@@ -458,7 +481,7 @@ class Series(StgitObject):\n         \"\"\"\n         return os.path.isdir(self.__patch_dir)\n \n-    def init(self, create_at=False):\n+    def init(self, create_at=False, parent_remote=None, parent_branch=None):\n         \"\"\"Initialises the stgit series\n         \"\"\"\n         bases_dir = os.path.join(self.__base_dir, 'refs', 'bases')\n@@ -475,6 +498,8 @@ class Series(StgitObject):\n \n         os.makedirs(self.__patch_dir)\n \n+        self.set_parent(parent_remote, parent_branch)\n+        \n         create_dirs(bases_dir)\n \n         self.create_empty_field('applied')\n"},{"id":"32823","messageId":"20070127112133.16475.53497.stgit@gandelf.nowhere.earth","threadId":"6563","inReplyTo":"20070127104024.16475.81445.stgit@gandelf.nowhere.earth","subject":"[PATCH 4/5] Have 'stg branch --create' record parent information.","fromName":"Yann Dirson","fromEmail":"ydirson@altern.org","sentAt":"2007-01-27T11:21:33Z","receivedAt":"2007-01-27T11:21:33Z","isPatch":true,"sender":{"key":"ydirson@altern.org","avatar":"https://avatars.githubusercontent.com/u/1190950?v=4"},"body":"\n\n\nSigned-off-by: Yann Dirson <ydirson@altern.org>\n---\n\n stgit/commands/branch.py |   37 ++++++++++++++++++++++++++++++++++---\n 1 files changed, 34 insertions(+), 3 deletions(-)\n\ndiff --git a/stgit/commands/branch.py b/stgit/commands/branch.py\nindex d348409..f074d47 100644\n--- a/stgit/commands/branch.py\n+++ b/stgit/commands/branch.py\n@@ -123,10 +123,41 @@ def func(parser, options, args):\n         check_head_top_equal()\n \n         tree_id = None\n-        if len(args) == 2:\n+        if len(args) >= 2:\n+            try:\n+                if git.rev_parse(args[1]) == git.rev_parse('refs/heads/' + args[1]):\n+                    # we are for sure refering to a branch\n+                    parentbranch = 'refs/heads/' + args[1]\n+                    print 'Recording \"%s\" as parent branch.' % parentbranch\n+                elif git.rev_parse(args[1]) and re.search('/', args[1]):\n+                    # FIXME: should the test be more strict ?\n+                    parentbranch = args[1]\n+                else:\n+                    # Note: this includes refs to StGIT patches\n+                    print 'Don\\'t know how to determine parent branch from \"%s\".' % args[1]\n+                    parentbranch = None\n+            except git.GitException:\n+                # should use a more specific exception to catch only non-git refs ?\n+                print 'Don\\'t know how to determine parent branch from \"%s\".' % args[1]\n+                parentbranch = None\n+\n             tree_id = git_id(args[1])\n-        \n-        stack.Series(args[0]).init(create_at = tree_id)\n+        else:\n+            # branch stack off current branch\n+            parentbranch = git.get_head_file()\n+\n+        if parentbranch:\n+            parentremote = git.identify_remote(parentbranch)\n+            if parentremote:\n+                print 'Using \"%s\" remote to pull parent from.' % parentremote\n+            else:\n+                print 'Not identified a remote to pull parent from.'\n+        else:\n+            parentremote = None\n+\n+        stack.Series(args[0]).init(create_at = tree_id,\n+                                   parent_remote = parentremote,\n+                                   parent_branch = parentbranch)\n \n         print 'Branch \"%s\" created.' % args[0]\n         return\n"},{"id":"32825","messageId":"20070127112138.16475.92752.stgit@gandelf.nowhere.earth","threadId":"6563","inReplyTo":"20070127104024.16475.81445.stgit@gandelf.nowhere.earth","subject":"[PATCH 5/5] Make 'stg pull' use git-fetch and not git-pull.","fromName":"Yann Dirson","fromEmail":"ydirson@altern.org","sentAt":"2007-01-27T11:21:38Z","receivedAt":"2007-01-27T11:21:38Z","isPatch":true,"sender":{"key":"ydirson@altern.org","avatar":"https://avatars.githubusercontent.com/u/1190950?v=4"},"body":"\nWe introduce a new pull-does-rebase setting, as companion to pullcmd.\nThis allows to use both pullcmd's that need a rebase (like git-fetch)\nand pullcmd's that do not (like git-pull).\n\nTo be able to rebase, we rely on parent information being available\nfor the stack.\n\nSigned-off-by: Yann Dirson <ydirson@altern.org>\n---\n\n examples/gitconfig |    8 +++++++-\n stgit/config.py    |    3 ++-\n stgit/git.py       |   15 ++++++++++-----\n 3 files changed, 19 insertions(+), 7 deletions(-)\n\ndiff --git a/examples/gitconfig b/examples/gitconfig\nindex 5e7b240..e3d9889 100644\n--- a/examples/gitconfig\n+++ b/examples/gitconfig\n@@ -33,8 +33,14 @@\n \t#pager = ~/share/stgit/contrib/diffcol.sh\n \t#pager = filterdiff --annotate | colordiff | less -FRX\n \n-\t# GIT pull command (should take the same arguments as git-pull)\n+\t# GIT pull command (should take the same arguments as\n+\t# git-fetch or git-pull).  By default:\n+\t#pullcmd = git-fetch\n+\t#pull-does-rebase = yes\n+\t# Alternative (old behaviour), less intuitive but maybe useful\n+\t# for some workflows:\n \t#pullcmd = git-pull\n+\t#pull-does-rebase = no\n \n \t# The three-way merge tool. Note that the 'output' file contains the\n \t# same data as 'branch1'. This is useful for tools that do not take an\ndiff --git a/stgit/config.py b/stgit/config.py\nindex f5fbdab..0d4da06 100644\n--- a/stgit/config.py\n+++ b/stgit/config.py\n@@ -65,7 +65,8 @@ def config_setup():\n     config.set('stgit', 'autoresolved', 'no')\n     config.set('stgit', 'smtpserver', 'localhost:25')\n     config.set('stgit', 'smtpdelay', '5')\n-    config.set('stgit', 'pullcmd', 'git-pull')\n+    config.set('stgit', 'pullcmd', 'git-fetch')\n+    config.set('stgit', 'pull-does-rebase', 'yes')\n     config.set('stgit', 'merger',\n                'diff3 -L current -L ancestor -L patched -m -E ' \\\n                '\"%(branch1)s\" \"%(ancestor)s\" \"%(branch2)s\" > \"%(output)s\"')\ndiff --git a/stgit/git.py b/stgit/git.py\nindex 3c2c237..82b5bf5 100644\n--- a/stgit/git.py\n+++ b/stgit/git.py\n@@ -806,18 +806,23 @@ def reset(files = None, tree_id = None, check_out = True):\n         __set_head(tree_id)\n \n def pull(repository = 'origin', refspec = None):\n-    \"\"\"Pull changes from the remote repository. At the moment, just\n-    use the 'git-pull' command\n+    \"\"\"Pull changes from the remote repository. Uses 'git-fetch'\n+    and moves the stack base.\n     \"\"\"\n-    # 'git-pull' updates the HEAD\n+    # we update the HEAD\n     __clear_head_cache()\n \n     args = [repository]\n     if refspec:\n         args.append(refspec)\n \n-    if __run(config.get('stgit', 'pullcmd'), args) != 0:\n-        raise GitException, 'Failed \"git-pull %s\"' % repository\n+    command = config.get('stgit', 'pullcmd')\n+    if __run(command, args) != 0:\n+        raise GitException, 'Failed \"%s %s\"' % (command, repository)\n+\n+    if (config.get('stgit', 'pull-does-rebase')):\n+        # FIXME!\n+        reset(tree_id = rev_parse(repository))\n \n def repack():\n     \"\"\"Repack all objects into a single pack\n"}]}