Git development
 help / color / mirror / Atom feed
* [StGit PATCH 5/5] Add "stg coalesce"
From: Karl Hasselström @ 2007-11-05  3:15 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <20071105030847.6108.44653.stgit@yoghurt>

It coalesces two or more consecutive applied patches, with no need to
touch index/worktree, and no possibiliy of conflicts.

Future improvements could relax the "consecutive" and "applied"
restrictions, by building a new chain of commits just like "stg push"
will do once it's been converted to the new infrastructure.

Signed-off-by: Karl Hasselström <kha@treskal.com>

---

 stgit/commands/coalesce.py |   87 ++++++++++++++++++++++++++++++++++++++++++++
 stgit/main.py              |    2 +
 stgit/utils.py             |   11 ++++++
 t/t2600-coalesce.sh        |   31 ++++++++++++++++
 4 files changed, 131 insertions(+), 0 deletions(-)
 create mode 100644 stgit/commands/coalesce.py
 create mode 100755 t/t2600-coalesce.sh


diff --git a/stgit/commands/coalesce.py b/stgit/commands/coalesce.py
new file mode 100644
index 0000000..11b7b52
--- /dev/null
+++ b/stgit/commands/coalesce.py
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+
+__copyright__ = """
+Copyright (C) 2007, Karl Hasselström <kha@treskal.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+from optparse import make_option
+from stgit.out import *
+from stgit import utils
+from stgit.commands import common
+from stgit.lib import git, transaction
+
+help = 'coalesce two or more patches into one'
+usage = """%prog [options] <patches>
+
+Coalesce two or more patches, creating one big patch that contains all
+their changes. The patches must all be applied, and must be
+consecutive."""
+
+directory = common.DirectoryHasRepositoryLib()
+options = [make_option('-n', '--name', help = 'name of coalesced patch'),
+           make_option('-m', '--message',
+                       help = 'commit message of coalesced patch')]
+
+def _coalesce(stack, name, msg, patches):
+    applied = stack.patchorder.applied
+
+    # Make sure the patches are consecutive.
+    applied_ix = dict((applied[i], i) for i in xrange(len(applied)))
+    ixes = list(sorted(applied_ix[p] for p in patches))
+    i0, i1 = ixes[0], ixes[-1]
+    if i1 - i0 + 1 != len(patches):
+        raise common.CmdException('The patches must be consecutive')
+
+    # Make a commit for the coalesced patch.
+    def bad_name(pn):
+        return pn not in patches and stack.patches.exists(pn)
+    if name and bad_name(name):
+        raise common.CmdException('Patch name "%s" already taken')
+    ps = [stack.patches.get(pn) for pn in applied[i0:i1+1]]
+    if msg == None:
+        msg = '\n\n'.join('%s\n\n%s' % (p.name.ljust(70, '-'),
+                                        p.commit.data.message)
+                          for p in ps)
+        msg = utils.edit_string(msg, '.stgit-coalesce.txt').strip()
+    if not name:
+        name = utils.make_patch_name(msg, bad_name)
+    cd = git.Commitdata(tree = ps[-1].commit.data.tree,
+                        parents = ps[0].commit.data.parents, message = msg)
+
+    # Rewrite refs.
+    trans = transaction.StackTransaction(stack, 'coalesce')
+    parent = trans.patches[name] = stack.repository.commit(cd)
+    trans.applied = applied[:i0]
+    trans.applied.append(name)
+    for pn in applied[i0:i1+1]:
+        trans.patches[pn] = None
+    for pn in applied[i1+1:]:
+        p = stack.patches.get(pn)
+        parent = trans.patches[pn] = stack.repository.commit(
+            p.commit.data.set_parent(parent))
+        trans.applied.append(pn)
+    trans.run()
+
+def func(parser, options, args):
+    stack = directory.repository.current_stack
+    applied = set(stack.patchorder.applied)
+    for pn in args:
+        if not pn in applied:
+            raise common.CmdException('%s is not applied' % pn)
+    patches = set(args)
+    if len(patches) < 2:
+        raise common.CmdException('Need at least two patches')
+    _coalesce(stack, options.name, options.message, args)
diff --git a/stgit/main.py b/stgit/main.py
index 9ef6d44..ac46cde 100644
--- a/stgit/main.py
+++ b/stgit/main.py
@@ -65,6 +65,7 @@ commands = Commands({
     'diff':             'diff',
     'clean':            'clean',
     'clone':            'clone',
+    'coalesce':         'coalesce',
     'commit':           'commit',
     'edit':             'edit',
     'export':           'export',
@@ -109,6 +110,7 @@ stackcommands = (
     'assimilate',
     'branch',
     'clean',
+    'coalesce',
     'commit',
     'float',
     'goto',
diff --git a/stgit/utils.py b/stgit/utils.py
index b3f6232..688276c 100644
--- a/stgit/utils.py
+++ b/stgit/utils.py
@@ -189,6 +189,17 @@ def call_editor(filename):
         raise EditorException, 'editor failed, exit code: %d' % err
     out.done()
 
+def edit_string(s, filename):
+    f = file(filename, 'w')
+    f.write(s)
+    f.close()
+    call_editor(filename)
+    f = file(filename)
+    s = f.read()
+    f.close()
+    os.remove(filename)
+    return s
+
 def patch_name_from_msg(msg):
     """Return a string to be used as a patch name. This is generated
     from the top line of the string passed as argument."""
diff --git a/t/t2600-coalesce.sh b/t/t2600-coalesce.sh
new file mode 100755
index 0000000..f13a309
--- /dev/null
+++ b/t/t2600-coalesce.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='Run "stg coalesce"'
+
+. ./test-lib.sh
+
+test_expect_success 'Initialize StGit stack' '
+    stg init &&
+    for i in 0 1 2 3; do
+        stg new p$i -m "foo $i" &&
+        echo "foo $i" >> foo.txt &&
+        git add foo.txt &&
+        stg refresh
+    done
+'
+
+test_expect_success 'Coalesce some patches' '
+    [ "$(echo $(stg applied))" = "p0 p1 p2 p3" ] &&
+    [ "$(echo $(stg unapplied))" = "" ] &&
+    stg coalesce --name=q0 --message="wee woo" p1 p2 &&
+    [ "$(echo $(stg applied))" = "p0 q0 p3" ] &&
+    [ "$(echo $(stg unapplied))" = "" ]
+'
+
+test_expect_success 'Coalesce at stack top' '
+    stg coalesce --name=q1 --message="wee woo wham" q0 p3 &&
+    [ "$(echo $(stg applied))" = "p0 q1" ] &&
+    [ "$(echo $(stg unapplied))" = "" ]
+'
+
+test_done

^ permalink raw reply related

* [StGit PATCH 3/5] Simple test for "stg clean"
From: Karl Hasselström @ 2007-11-05  3:14 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <20071105030847.6108.44653.stgit@yoghurt>

Signed-off-by: Karl Hasselström <kha@treskal.com>

---

 t/t2500-clean.sh |   27 +++++++++++++++++++++++++++
 1 files changed, 27 insertions(+), 0 deletions(-)
 create mode 100755 t/t2500-clean.sh


diff --git a/t/t2500-clean.sh b/t/t2500-clean.sh
new file mode 100755
index 0000000..3364c18
--- /dev/null
+++ b/t/t2500-clean.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+test_description='Run "stg clean"'
+
+. ./test-lib.sh
+
+test_expect_success 'Initialize StGit stack' '
+    stg init &&
+    stg new e0 -m e0 &&
+    stg new p0 -m p0 &&
+    echo foo > foo.txt &&
+    git add foo.txt &&
+    stg refresh &&
+    stg new e1 -m e1 &&
+    stg new e2 -m e2 &&
+    stg pop
+'
+
+test_expect_success 'Clean empty patches' '
+    [ "$(echo $(stg applied))" = "e0 p0 e1" ] &&
+    [ "$(echo $(stg unapplied))" = "e2" ] &&
+    stg clean &&
+    [ "$(echo $(stg applied))" = "p0" ] &&
+    [ "$(echo $(stg unapplied))" = "" ]
+'
+
+test_done

^ permalink raw reply related

* [StGit PATCH 4/5] Let "stg clean" use the new infrastructure
From: Karl Hasselström @ 2007-11-05  3:14 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <20071105030847.6108.44653.stgit@yoghurt>

TODO:

  * detect uninitialized stack

  * upgrade stack

Signed-off-by: Karl Hasselström <kha@treskal.com>

---

 stgit/commands/clean.py  |   68 ++++++++++++++++++++++++----------------------
 stgit/commands/common.py |   10 ++++++-
 2 files changed, 44 insertions(+), 34 deletions(-)


diff --git a/stgit/commands/clean.py b/stgit/commands/clean.py
index 4484ecd..ad0b920 100644
--- a/stgit/commands/clean.py
+++ b/stgit/commands/clean.py
@@ -15,14 +15,10 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 """
 
-import sys, os
-from optparse import OptionParser, make_option
-
-from stgit.commands.common import *
-from stgit.utils import *
+from optparse import make_option
 from stgit.out import *
-from stgit import stack, git
-
+from stgit.commands import common
+from stgit.lib import transaction
 
 help = 'delete the empty patches in the series'
 usage = """%prog [options]
@@ -31,7 +27,7 @@ Delete the empty patches in the whole series or only those applied or
 unapplied. A patch is considered empty if the two commit objects
 representing its boundaries refer to the same tree object."""
 
-directory = DirectoryHasRepository()
+directory = common.DirectoryHasRepositoryLib()
 options = [make_option('-a', '--applied',
                        help = 'delete the empty applied patches',
                        action = 'store_true'),
@@ -40,18 +36,35 @@ options = [make_option('-a', '--applied',
                        action = 'store_true')]
 
 
-def __delete_empty(patches, applied):
-    """Delete the empty patches
-    """
-    for p in patches:
-        if crt_series.empty_patch(p):
-            out.start('Deleting patch "%s"' % p)
-            if applied and crt_series.patch_applied(p):
-                crt_series.pop_patch(p)
-            crt_series.delete_patch(p)
-            out.done()
-        elif applied and crt_series.patch_unapplied(p):
-            crt_series.push_patch(p)
+def _clean(stack, clean_applied, clean_unapplied):
+    def deleting(pn):
+        out.info('Deleting empty patch %s' % pn)
+    trans = transaction.StackTransaction(stack, 'clean')
+    if clean_unapplied:
+        trans.unapplied = []
+        for pn in stack.patchorder.unapplied:
+            p = stack.patches.get(pn)
+            if p.is_empty():
+                trans.patches[pn] = None
+                deleting(pn)
+            else:
+                trans.unapplied.append[pn]
+    if clean_applied:
+        trans.applied = []
+        parent = stack.base
+        for pn in stack.patchorder.applied:
+            p = stack.patches.get(pn)
+            if p.is_empty():
+                trans.patches[pn] = None
+                deleting(pn)
+            else:
+                if parent != p.commit.data.parent:
+                    parent = trans.patches[pn] = stack.repository.commit(
+                        p.commit.data.set_parent(parent))
+                else:
+                    parent = p.commit
+                trans.applied.append(pn)
+    trans.run()
 
 def func(parser, options, args):
     """Delete the empty patches in the series
@@ -59,19 +72,8 @@ def func(parser, options, args):
     if len(args) != 0:
         parser.error('incorrect number of arguments')
 
-    check_local_changes()
-    check_conflicts()
-    check_head_top_equal(crt_series)
-
     if not (options.applied or options.unapplied):
         options.applied = options.unapplied = True
 
-    if options.applied:
-        applied = crt_series.get_applied()
-        __delete_empty(applied, True)
-
-    if options.unapplied:
-        unapplied = crt_series.get_unapplied()
-        __delete_empty(unapplied, False)
-
-    print_crt_patch(crt_series)
+    _clean(directory.repository.current_stack,
+           options.applied, options.unapplied)
diff --git a/stgit/commands/common.py b/stgit/commands/common.py
index ceeebbc..0a017a8 100644
--- a/stgit/commands/common.py
+++ b/stgit/commands/common.py
@@ -27,7 +27,7 @@ from stgit.out import *
 from stgit.run import *
 from stgit import stack, git, basedir
 from stgit.config import config, file_extensions
-
+from stgit.lib import stack as libstack
 
 # Command exception class
 class CmdException(StgException):
@@ -537,3 +537,11 @@ class DirectoryGotoToplevel(DirectoryInWorktree):
     def setup(self):
         DirectoryInWorktree.setup(self)
         self.cd_to_topdir()
+
+class DirectoryHasRepositoryLib(_Directory):
+    """For commands that use the new infrastructure in stgit.lib.*."""
+    def __init__(self):
+        self.needs_current_series = False
+    def setup(self):
+        # This will throw an exception if we don't have a repository.
+        self.repository = libstack.Repository.default()

^ permalink raw reply related

* [StGit PATCH 2/5] Write metadata files used by the old infrastructure
From: Karl Hasselström @ 2007-11-05  3:14 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <20071105030847.6108.44653.stgit@yoghurt>

The new infrastructure doesn't use them, but they're needed to support
the old infrastructure during the transition when both of them are in
use.

Signed-off-by: Karl Hasselström <kha@treskal.com>

---

 stgit/lib/stack.py |   35 +++++++++++++++++++++++++++++++++++
 1 files changed, 35 insertions(+), 0 deletions(-)


diff --git a/stgit/lib/stack.py b/stgit/lib/stack.py
index b4d99ba..ce514d6 100644
--- a/stgit/lib/stack.py
+++ b/stgit/lib/stack.py
@@ -9,10 +9,45 @@ class Patch(object):
     name = property(lambda self: self.__name)
     def __ref(self):
         return 'refs/patches/%s/%s' % (self.__stack.name, self.__name)
+    def __log_ref(self):
+        return self.__ref() + '.log'
     @property
     def commit(self):
         return self.__stack.repository.refs.get(self.__ref())
+    def __write_compat_files(self, new_commit, msg):
+        """Write files used by the old infrastructure."""
+        fdir = os.path.join(self.__stack.directory, 'patches', self.__name)
+        def write(name, val, multiline = False):
+            fn = os.path.join(fdir, name)
+            if val:
+                utils.write_string(fn, val, multiline)
+            elif os.path.isfile(fn):
+                os.remove(fn)
+        def write_patchlog():
+            try:
+                old_log = [self.__stack.repository.refs.get(self.__log_ref())]
+            except KeyError:
+                old_log = []
+            cd = git.Commitdata(tree = new_commit.data.tree, parents = old_log,
+                                message = '%s\t%s' % (msg, new_commit.sha1))
+            c = self.__stack.repository.commit(cd)
+            self.__stack.repository.refs.set(self.__log_ref(), c, msg)
+            return c
+        d = new_commit.data
+        write('authname', d.author.name)
+        write('authemail', d.author.email)
+        write('authdate', d.author.date)
+        write('commname', d.committer.name)
+        write('commemail', d.committer.email)
+        write('description', d.message)
+        write('log', write_patchlog().sha1)
+        try:
+            old_commit_sha1 = self.commit
+        except KeyError:
+            old_commit_sha1 = None
+        write('top.old', old_commit_sha1)
     def set_commit(self, commit, msg):
+        self.__write_compat_files(commit, msg)
         self.__stack.repository.refs.set(self.__ref(), commit, msg)
     def delete(self):
         self.__stack.repository.refs.delete(self.__ref())

^ permalink raw reply related

* [StGit PATCH 1/5] New StGit core infrastructure: repository operations
From: Karl Hasselström @ 2007-11-05  3:14 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <20071105030847.6108.44653.stgit@yoghurt>

This is the first part of the New and Improved StGit core
infrastructure. It has functions for manipulating the git repository
(commits, refs, and so on), but doesn't yet touch the index or
worktree.

Currently not used by anything.

Signed-off-by: Karl Hasselström <kha@treskal.com>

---

 stgit/lib/__init__.py    |   18 +++
 stgit/lib/git.py         |  245 ++++++++++++++++++++++++++++++++++++++++++++++
 stgit/lib/stack.py       |  121 +++++++++++++++++++++++
 stgit/lib/transaction.py |   79 +++++++++++++++
 stgit/utils.py           |   13 ++
 5 files changed, 476 insertions(+), 0 deletions(-)
 create mode 100644 stgit/lib/__init__.py
 create mode 100644 stgit/lib/git.py
 create mode 100644 stgit/lib/stack.py
 create mode 100644 stgit/lib/transaction.py


diff --git a/stgit/lib/__init__.py b/stgit/lib/__init__.py
new file mode 100644
index 0000000..45eb307
--- /dev/null
+++ b/stgit/lib/__init__.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+
+__copyright__ = """
+Copyright (C) 2007, Karl Hasselström <kha@treskal.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
diff --git a/stgit/lib/git.py b/stgit/lib/git.py
new file mode 100644
index 0000000..1b873e3
--- /dev/null
+++ b/stgit/lib/git.py
@@ -0,0 +1,245 @@
+import os, os.path, re
+from stgit import exception, run, utils
+
+class RepositoryException(exception.StgException):
+    pass
+
+class DetachedHeadException(RepositoryException):
+    pass
+
+class Repr(object):
+    def __repr__(self):
+        return str(self)
+
+class NoValue(object):
+    pass
+
+def make_defaults(defaults):
+    def d(val, attr):
+        if val != NoValue:
+            return val
+        elif defaults != NoValue:
+            return getattr(defaults, attr)
+        else:
+            return None
+    return d
+
+class Person(Repr):
+    """Immutable."""
+    def __init__(self, name = NoValue, email = NoValue,
+                 date = NoValue, defaults = NoValue):
+        d = make_defaults(defaults)
+        self.__name = d(name, 'name')
+        self.__email = d(email, 'email')
+        self.__date = d(date, 'date')
+    name = property(lambda self: self.__name)
+    email = property(lambda self: self.__email)
+    date = property(lambda self: self.__date)
+    def set_name(self, name):
+        return type(self)(name = name, defaults = self)
+    def set_email(self, email):
+        return type(self)(email = email, defaults = self)
+    def set_date(self, date):
+        return type(self)(date = date, defaults = self)
+    def __str__(self):
+        return '%s <%s> %s' % (self.name, self.email, self.date)
+    @classmethod
+    def parse(cls, s):
+        m = re.match(r'^([^<]*)<([^>]*)>\s+(\d+\s+[+-]\d{4})$', s)
+        assert m
+        name = m.group(1).strip()
+        email = m.group(2)
+        date = m.group(3)
+        return cls(name, email, date)
+
+class Tree(Repr):
+    """Immutable."""
+    def __init__(self, sha1):
+        self.__sha1 = sha1
+    sha1 = property(lambda self: self.__sha1)
+    def __str__(self):
+        return 'Tree<%s>' % self.sha1
+
+class Commitdata(Repr):
+    """Immutable."""
+    def __init__(self, tree = NoValue, parents = NoValue, author = NoValue,
+                 committer = NoValue, message = NoValue, defaults = NoValue):
+        d = make_defaults(defaults)
+        self.__tree = d(tree, 'tree')
+        self.__parents = d(parents, 'parents')
+        self.__author = d(author, 'author')
+        self.__committer = d(committer, 'committer')
+        self.__message = d(message, 'message')
+    tree = property(lambda self: self.__tree)
+    parents = property(lambda self: self.__parents)
+    @property
+    def parent(self):
+        assert len(self.__parents) == 1
+        return self.__parents[0]
+    author = property(lambda self: self.__author)
+    committer = property(lambda self: self.__committer)
+    message = property(lambda self: self.__message)
+    def set_tree(self, tree):
+        return type(self)(tree = tree, defaults = self)
+    def set_parents(self, parents):
+        return type(self)(parents = parents, defaults = self)
+    def add_parent(self, parent):
+        return type(self)(parents = list(self.parents or []) + [parent],
+                          defaults = self)
+    def set_parent(self, parent):
+        return self.set_parents([parent])
+    def set_author(self, author):
+        return type(self)(author = author, defaults = self)
+    def set_committer(self, committer):
+        return type(self)(committer = committer, defaults = self)
+    def set_message(self, message):
+        return type(self)(message = message, defaults = self)
+    def __str__(self):
+        if self.tree == None:
+            tree = None
+        else:
+            tree = self.tree.sha1
+        if self.parents == None:
+            parents = None
+        else:
+            parents = [p.sha1 for p in self.parents]
+        return ('Commitdata<tree: %s, parents: %s, author: %s,'
+                ' committer: %s, message: "%s">'
+                ) % (tree, parents, self.author, self.committer, self.message)
+    @classmethod
+    def parse(cls, repository, s):
+        cd = cls()
+        lines = list(s.splitlines(True))
+        for i in xrange(len(lines)):
+            line = lines[i].strip()
+            if not line:
+                return cd.set_message(''.join(lines[i+1:]))
+            key, value = line.split(None, 1)
+            if key == 'tree':
+                cd = cd.set_tree(repository.get_tree(value))
+            elif key == 'parent':
+                cd = cd.add_parent(repository.get_commit(value))
+            elif key == 'author':
+                cd = cd.set_author(Person.parse(value))
+            elif key == 'committer':
+                cd = cd.set_committer(Person.parse(value))
+            else:
+                assert False
+        assert False
+
+class Commit(Repr):
+    """Immutable."""
+    def __init__(self, repository, sha1):
+        self.__sha1 = sha1
+        self.__repository = repository
+        self.__data = None
+    sha1 = property(lambda self: self.__sha1)
+    @property
+    def data(self):
+        if self.__data == None:
+            self.__data = Commitdata.parse(
+                self.__repository,
+                self.__repository.cat_object(self.sha1))
+        return self.__data
+    def __str__(self):
+        return 'Commit<sha1: %s, data: %s>' % (self.sha1, self.__data)
+
+class Refs(object):
+    def __init__(self, repository):
+        self.__repository = repository
+        self.__refs = None
+    def __cache_refs(self):
+        self.__refs = {}
+        for line in self.__repository.run(['git', 'show-ref']).output_lines():
+            m = re.match(r'^([0-9a-f]{40})\s+(\S+)$', line)
+            sha1, ref = m.groups()
+            self.__refs[ref] = sha1
+    def get(self, ref):
+        """Throws KeyError if ref doesn't exist."""
+        if self.__refs == None:
+            self.__cache_refs()
+        return self.__repository.get_commit(self.__refs[ref])
+    def set(self, ref, commit, msg):
+        if self.__refs == None:
+            self.__cache_refs()
+        old_sha1 = self.__refs.get(ref, '0'*40)
+        new_sha1 = commit.sha1
+        if old_sha1 != new_sha1:
+            self.__repository.run(['git', 'update-ref', '-m', msg,
+                                   ref, new_sha1, old_sha1]).no_output()
+            self.__refs[ref] = new_sha1
+    def delete(self, ref):
+        if self.__refs == None:
+            self.__cache_refs()
+        self.__repository.run(['git', 'update-ref',
+                               '-d', ref, self.__refs[ref]]).no_output()
+        del self.__refs[ref]
+
+class ObjectCache(object):
+    """Cache for Python objects, for making sure that we create only one
+    Python object per git object."""
+    def __init__(self, create):
+        self.__objects = {}
+        self.__create = create
+    def __getitem__(self, name):
+        if not name in self.__objects:
+            self.__objects[name] = self.__create(name)
+        return self.__objects[name]
+    def __contains__(self, name):
+        return name in self.__objects
+    def __setitem__(self, name, val):
+        assert not name in self.__objects
+        self.__objects[name] = val
+
+class RunWithEnv(object):
+    def run(self, args, env = {}):
+        return run.Run(*args).env(utils.add_dict(self.env, env))
+
+class Repository(RunWithEnv):
+    def __init__(self, directory):
+        self.__git_dir = directory
+        self.__refs = Refs(self)
+        self.__trees = ObjectCache(lambda sha1: Tree(sha1))
+        self.__commits = ObjectCache(lambda sha1: Commit(self, sha1))
+    env = property(lambda self: { 'GIT_DIR': self.__git_dir })
+    @classmethod
+    def default(cls):
+        """Return the default repository."""
+        try:
+            return cls(run.Run('git', 'rev-parse', '--git-dir'
+                               ).output_one_line())
+        except run.RunException:
+            raise RepositoryException('Cannot find git repository')
+    directory = property(lambda self: self.__git_dir)
+    refs = property(lambda self: self.__refs)
+    def cat_object(self, sha1):
+        return self.run(['git', 'cat-file', '-p', sha1]).raw_output()
+    def get_tree(self, sha1):
+        return self.__trees[sha1]
+    def get_commit(self, sha1):
+        return self.__commits[sha1]
+    def commit(self, commitdata):
+        c = ['git', 'commit-tree', commitdata.tree.sha1]
+        for p in commitdata.parents:
+            c.append('-p')
+            c.append(p.sha1)
+        env = {}
+        for p, v1 in ((commitdata.author, 'AUTHOR'),
+                       (commitdata.committer, 'COMMITTER')):
+            if p != None:
+                for attr, v2 in (('name', 'NAME'), ('email', 'EMAIL'),
+                                 ('date', 'DATE')):
+                    if getattr(p, attr) != None:
+                        env['GIT_%s_%s' % (v1, v2)] = getattr(p, attr)
+        sha1 = self.run(c, env = env).raw_input(commitdata.message
+                                                ).output_one_line()
+        return self.get_commit(sha1)
+    @property
+    def head(self):
+        try:
+            return self.run(['git', 'symbolic-ref', '-q', 'HEAD']
+                            ).output_one_line()
+        except run.RunException:
+            raise DetachedHeadException()
+    def set_head(self, ref, msg):
+        self.run(['git', 'symbolic-ref', '-m', msg, 'HEAD', ref]).no_output()
diff --git a/stgit/lib/stack.py b/stgit/lib/stack.py
new file mode 100644
index 0000000..b4d99ba
--- /dev/null
+++ b/stgit/lib/stack.py
@@ -0,0 +1,121 @@
+import os.path
+from stgit import utils
+from stgit.lib import git
+
+class Patch(object):
+    def __init__(self, stack, name):
+        self.__stack = stack
+        self.__name = name
+    name = property(lambda self: self.__name)
+    def __ref(self):
+        return 'refs/patches/%s/%s' % (self.__stack.name, self.__name)
+    @property
+    def commit(self):
+        return self.__stack.repository.refs.get(self.__ref())
+    def set_commit(self, commit, msg):
+        self.__stack.repository.refs.set(self.__ref(), commit, msg)
+    def delete(self):
+        self.__stack.repository.refs.delete(self.__ref())
+    def is_applied(self):
+        return self.name in self.__stack.patchorder.applied
+    def is_empty(self):
+        c = self.commit
+        return c.data.tree == c.data.parent.data.tree
+
+class PatchOrder(object):
+    """Keeps track of patch order, and which patches are applied.
+    Works with patch names, not actual patches."""
+    __list_order = [ 'applied', 'unapplied' ]
+    def __init__(self, stack):
+        self.__stack = stack
+        self.__lists = {}
+    def __read_file(self, fn):
+        return tuple(utils.read_strings(
+            os.path.join(self.__stack.directory, fn)))
+    def __write_file(self, fn, val):
+        utils.write_strings(os.path.join(self.__stack.directory, fn), val)
+    def __get_list(self, name):
+        if not name in self.__lists:
+            self.__lists[name] = self.__read_file(name)
+        return self.__lists[name]
+    def __set_list(self, name, val):
+        val = tuple(val)
+        if val != self.__lists.get(name, None):
+            self.__lists[name] = val
+            self.__write_file(name, val)
+    applied = property(lambda self: self.__get_list('applied'),
+                       lambda self, val: self.__set_list('applied', val))
+    unapplied = property(lambda self: self.__get_list('unapplied'),
+                         lambda self, val: self.__set_list('unapplied', val))
+
+class Patches(object):
+    """Creates Patch objects."""
+    def __init__(self, stack):
+        self.__stack = stack
+        def create_patch(name):
+            p = Patch(self.__stack, name)
+            p.commit # raise exception if the patch doesn't exist
+            return p
+        self.__patches = git.ObjectCache(create_patch) # name -> Patch
+    def exists(self, name):
+        try:
+            self.get(name)
+            return True
+        except KeyError:
+            return False
+    def get(self, name):
+        return self.__patches[name]
+    def new(self, name, commit, msg):
+        assert not name in self.__patches
+        p = Patch(self.__stack, name)
+        p.set_commit(commit, msg)
+        self.__patches[name] = p
+        return p
+
+class Stack(object):
+    def __init__(self, repository, name):
+        self.__repository = repository
+        self.__name = name
+        self.__patchorder = PatchOrder(self)
+        self.__patches = Patches(self)
+    name = property(lambda self: self.__name)
+    repository = property(lambda self: self.__repository)
+    patchorder = property(lambda self: self.__patchorder)
+    patches = property(lambda self: self.__patches)
+    @property
+    def directory(self):
+        return os.path.join(self.__repository.directory, 'patches', self.__name)
+    def __ref(self):
+        return 'refs/heads/%s' % self.__name
+    @property
+    def head(self):
+        return self.__repository.refs.get(self.__ref())
+    def set_head(self, commit, msg):
+        self.__repository.refs.set(self.__ref(), commit, msg)
+    @property
+    def base(self):
+        if self.patchorder.applied:
+            return self.patches.get(self.patchorder.applied[0]
+                                    ).commit.data.parent
+        else:
+            return self.head
+
+class Repository(git.Repository):
+    def __init__(self, *args, **kwargs):
+        git.Repository.__init__(self, *args, **kwargs)
+        self.__stacks = {} # name -> Stack
+    @property
+    def current_branch(self):
+        return utils.strip_leading('refs/heads/', self.head)
+    @property
+    def current_stack(self):
+        return self.get_stack(self.current_branch)
+    def get_stack(self, name):
+        if not name in self.__stacks:
+            if name == None:
+                s = None # detached HEAD
+            else:
+                # TODO: verify that the branch exists
+                s = Stack(self, name)
+            self.__stacks[name] = s
+        return self.__stacks[name]
diff --git a/stgit/lib/transaction.py b/stgit/lib/transaction.py
new file mode 100644
index 0000000..991e64e
--- /dev/null
+++ b/stgit/lib/transaction.py
@@ -0,0 +1,79 @@
+from stgit import exception
+from stgit.out import *
+
+class TransactionException(exception.StgException):
+    pass
+
+def print_current_patch(old_applied, new_applied):
+    def now_at(pn):
+        out.info('Now at patch "%s"' % pn)
+    if not old_applied and not new_applied:
+        pass
+    elif not old_applied:
+        now_at(new_applied[-1])
+    elif not new_applied:
+        out.info('No patch applied')
+    elif old_applied[-1] == new_applied[-1]:
+        pass
+    else:
+        now_at(new_applied[-1])
+
+class StackTransaction(object):
+    def __init__(self, stack, msg):
+        self.__stack = stack
+        self.__msg = msg
+        self.__patches = {}
+        self.__applied = list(self.__stack.patchorder.applied)
+        self.__unapplied = list(self.__stack.patchorder.unapplied)
+    def __set_patches(self, val):
+        self.__patches = dict(val)
+    patches = property(lambda self: self.__patches, __set_patches)
+    def __set_applied(self, val):
+        self.__applied = list(val)
+    applied = property(lambda self: self.__applied, __set_applied)
+    def __set_unapplied(self, val):
+        self.__unapplied = list(val)
+    unapplied = property(lambda self: self.__unapplied, __set_unapplied)
+    def __check_consistency(self):
+        remaining = set(self.__applied + self.__unapplied)
+        for pn, commit in self.__patches.iteritems():
+            if commit == None:
+                assert self.__stack.patches.exists(pn)
+            else:
+                assert pn in remaining
+    def run(self):
+        self.__check_consistency()
+
+        # Get new head commit.
+        if self.__applied:
+            top_patch = self.__applied[-1]
+            try:
+                new_head = self.__patches[top_patch]
+            except KeyError:
+                new_head = self.__stack.patches.get(top_patch).commit
+        else:
+            new_head = self.__stack.base
+
+        # Set branch head.
+        if new_head == self.__stack.head:
+            pass # same commit: OK
+        elif new_head.data.tree == self.__stack.head.data.tree:
+            pass # same tree: OK
+        else:
+            # We can't handle this case yet.
+            raise TransactionException('Error: HEAD tree changed')
+        self.__stack.set_head(new_head, self.__msg)
+
+        # Write patches.
+        for pn, commit in self.__patches.iteritems():
+            if self.__stack.patches.exists(pn):
+                p = self.__stack.patches.get(pn)
+                if commit == None:
+                    p.delete()
+                else:
+                    p.set_commit(commit, self.__msg)
+            else:
+                self.__stack.patches.new(pn, commit, self.__msg)
+        print_current_patch(self.__stack.patchorder.applied, self.__applied)
+        self.__stack.patchorder.applied = self.__applied
+        self.__stack.patchorder.unapplied = self.__unapplied
diff --git a/stgit/utils.py b/stgit/utils.py
index 3a480c0..b3f6232 100644
--- a/stgit/utils.py
+++ b/stgit/utils.py
@@ -256,3 +256,16 @@ def add_sign_line(desc, sign_str, name, email):
     if not any(s in desc for s in ['\nSigned-off-by:', '\nAcked-by:']):
         desc = desc + '\n'
     return '%s\n%s\n' % (desc, sign_str)
+
+def strip_leading(prefix, s):
+    """Strip leading prefix from a string. Blow up if the prefix isn't
+    there."""
+    assert s.startswith(prefix)
+    return s[len(prefix):]
+
+def add_dict(d1, d2):
+    """Return a new dict with the contents of both d1 and d2. In case of
+    conflicting mappings, d2 takes precedence."""
+    d = dict(d1)
+    d.update(d2)
+    return d

^ permalink raw reply related

* [StGit PATCH 0/5] Start the refactoring
From: Karl Hasselström @ 2007-11-05  3:14 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git

This series introduces parts of the proposed new infrastructure, and
uses it to implement "stg clean" and the new "stg coalesce".

There are undoubtedly more bugs to be found, but I'm pushing it to

  git://repo.or.cz/stgit/kha.git experimental

to make it easy for people to try it out. It does pass the test suite
after all.

---

Karl Hasselström (5):
      Add "stg coalesce"
      Let "stg clean" use the new infrastructure
      Simple test for "stg clean"
      Write metadata files used by the old infrastructure
      New StGit core infrastructure: repository operations


 stgit/commands/clean.py    |   68 ++++++------
 stgit/commands/coalesce.py |   87 ++++++++++++++++
 stgit/commands/common.py   |   10 ++
 stgit/lib/__init__.py      |   18 +++
 stgit/lib/git.py           |  245 ++++++++++++++++++++++++++++++++++++++++++++
 stgit/lib/stack.py         |  156 ++++++++++++++++++++++++++++
 stgit/lib/transaction.py   |   79 ++++++++++++++
 stgit/main.py              |    2 
 stgit/utils.py             |   24 ++++
 t/t2500-clean.sh           |   27 +++++
 t/t2600-coalesce.sh        |   31 ++++++
 11 files changed, 713 insertions(+), 34 deletions(-)
 create mode 100644 stgit/commands/coalesce.py
 create mode 100644 stgit/lib/__init__.py
 create mode 100644 stgit/lib/git.py
 create mode 100644 stgit/lib/stack.py
 create mode 100644 stgit/lib/transaction.py
 create mode 100755 t/t2500-clean.sh
 create mode 100755 t/t2600-coalesce.sh

-- 
Karl Hasselström, kha@treskal.com
      www.treskal.com/kalle

^ permalink raw reply

* [StGit PATCH] Cogito is deprecated, so don't point to it
From: Karl Hasselström @ 2007-11-05  3:07 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git

Signed-off-by: Karl Hasselström <kha@treskal.com>

---

Also available from

  git://repo.or.cz/stgit/kha.git safe

 README |    4 +---
 1 files changed, 1 insertions(+), 3 deletions(-)


diff --git a/README b/README
index 0e648f5..4f20154 100644
--- a/README
+++ b/README
@@ -6,9 +6,7 @@ other repositories using standard GIT functionality.
 
 Note that StGIT is not an SCM interface on top of GIT and it expects a
 previously initialised GIT repository (unless it is cloned using StGIT
-directly). For standard SCM operations, either use plain GIT commands
-or the Cogito tool but it is not recommended to mix them with the
-StGIT commands.
+directly). For standard SCM operations, use plain GIT commands.
 
 For the latest version see http://www.procode.org/stgit/
 For a tutorial see http://wiki.procode.org/cgi-bin/wiki.cgi/StGIT_Tutorial

^ permalink raw reply related

* [PATCH] t7501-commit.sh: Add test case for fixing author in amend commit.
From: Kristian Høgsberg @ 2007-11-05  3:42 UTC (permalink / raw)
  To: gitster; +Cc: git, Kristian Høgsberg

Signed-off-by: Kristian Høgsberg <krh@redhat.com>
---
 t/t7501-commit.sh |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)

diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index b151b51..7a37c13 100644
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -151,6 +151,7 @@ test_expect_success 'partial commit that involves removal (2)' '
 	diff expected current
 
 '
+test_tick
 
 test_expect_success 'partial commit that involves removal (3)' '
 
@@ -163,4 +164,19 @@ test_expect_success 'partial commit that involves removal (3)' '
 
 '
 
+oldtick=$GIT_AUTHOR_DATE
+test_tick
+
+author="The Real Author <someguy@his.email.org>"
+committer="$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE"
+
+test_expect_success 'amend commit to fix author' '
+
+	git reset --hard
+	git cat-file -p HEAD | sed -e "s/author.*/author $author $oldtick/" -e "s/committer.*/committer $committer/" > expected &&
+	git commit --amend --author="$author" &&
+	git cat-file -p HEAD > current &&
+	diff expected current
+	
+'
 test_done
-- 
1.5.3.5.1527.g6161-dirty

^ permalink raw reply related

* Re: [PATCH] upload-pack: Use finish_{command,async}() instead of waitpid().
From: Michael J. Cohen @ 2007-11-05  1:42 UTC (permalink / raw)
  To: Git Mailing List
In-Reply-To: <200711042046.48257.johannes.sixt@telecom.at>

On Nov 4, 2007, at 2:46 PM, Johannes Sixt wrote:

> The change is smaller than it looks because most of it only reduces  
> the
> indentation of a large part of the inner loop.


mac-pro:git mjc$ git diff -u -b HEAD | diffstat
t/t5530-upload-pack-error.sh |   49 ++++++++++++++++++++++++++++
upload-pack.c                |   74 +++++++++ 
+---------------------------------
2 files changed, 67 insertions(+), 56 deletions(-)

agreed. :P

-mjc

^ permalink raw reply

* Re: [PATCH 5/5] pretty describe: add %ds, %dn, %dd placeholders
From: René Scharfe @ 2007-11-05  1:20 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Junio C Hamano, Git Mailing List
In-Reply-To: <472E010B.5070602@lsrfire.ath.cx>

René Scharfe schrieb:
> In any case, interpolate.c needs some attention, with or without my
> patch.  I agree that a native strbuf version would be nice.  How about
> an interface like that:
> 
> 	typedef const char *(*expand_fn_t)
> 		(const char *placeholder, void *context);
> 	void strbuf_addexpand(struct strbuf *sb, const char *format,
> 	                      const char **placeholders,
> 	                      expand_fn_t fn, void *context);
> 
> strbuf_addexpand() would call fn() when it recognizes a placeholder,
> avoiding unneeded setup code.  It could cache the result, so that fn()
> gets called at most a single time for each given placeholder.  context
> would be passed through to fn(), e.g. a struct commit in case of
> format_commit_message().  Makes sense?

OK, for discussion, I've experimented a bit, and here's what I could
come up with.  This version doesn't do any caching.  Is it even needed?

Anyway, strbuf_expand() is really simple, and the callback is expected
to add it's output directly to the strbuf.  That means that the number
of strdups and memdupz is reduced significantly, speeding up the whole
thing.  Converting code to use strbuf_expand is easy, but not trivial
(i.e. not doable by a script).

René


 commit.c |  275 ++++++++++++++++++++++++++++++++++---------------------------
 strbuf.c |   22 +++++
 strbuf.h |    3 +
 3 files changed, 178 insertions(+), 122 deletions(-)

diff --git a/commit.c b/commit.c
index 8262f6a..4a47b0e 100644
--- a/commit.c
+++ b/commit.c
@@ -698,7 +698,8 @@ static char *logmsg_reencode(const struct commit *commit,
 	return out;
 }
 
-static void fill_person(struct interp *table, const char *msg, int len)
+static void format_person_part(struct strbuf *sb, char part,
+                               const char *msg, int len)
 {
 	int start, end, tz = 0;
 	unsigned long date;
@@ -710,7 +711,10 @@ static void fill_person(struct interp *table, const char *msg, int len)
 	start = end + 1;
 	while (end > 0 && isspace(msg[end - 1]))
 		end--;
-	table[0].value = xmemdupz(msg, end);
+	if (part == 'n') {	/* name */
+		strbuf_add(sb, msg, end);
+		return;
+	}
 
 	if (start >= len)
 		return;
@@ -722,7 +726,10 @@ static void fill_person(struct interp *table, const char *msg, int len)
 	if (end >= len)
 		return;
 
-	table[1].value = xmemdupz(msg + start, end - start);
+	if (part == 'e') {	/* email */
+		strbuf_add(sb, msg + start, end - start);
+		return;
+	}
 
 	/* parse date */
 	for (start = end + 1; start < len && isspace(msg[start]); start++)
@@ -733,7 +740,10 @@ static void fill_person(struct interp *table, const char *msg, int len)
 	if (msg + start == ep)
 		return;
 
-	table[5].value = xmemdupz(msg + start, ep - (msg + start));
+	if (part == 't') {	/* date, UNIX timestamp */
+		strbuf_add(sb, msg + start, ep - (msg + start));
+		return;
+	}
 
 	/* parse tz */
 	for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
@@ -744,123 +754,107 @@ static void fill_person(struct interp *table, const char *msg, int len)
 			tz = -tz;
 	}
 
-	interp_set_entry(table, 2, show_date(date, tz, DATE_NORMAL));
-	interp_set_entry(table, 3, show_date(date, tz, DATE_RFC2822));
-	interp_set_entry(table, 4, show_date(date, tz, DATE_RELATIVE));
-	interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601));
+	switch (part) {
+	case 'd':	/* date */
+		strbuf_addstr(sb, show_date(date, tz, DATE_NORMAL));
+		return;
+	case 'D':	/* date, RFC2822 style */
+		strbuf_addstr(sb, show_date(date, tz, DATE_RFC2822));
+		return;
+	case 'r':	/* date, relative */
+		strbuf_addstr(sb, show_date(date, tz, DATE_RELATIVE));
+		return;
+	case 'i':	/* date, ISO 8601 */
+		strbuf_addstr(sb, show_date(date, tz, DATE_ISO8601));
+		return;
+	}
 }
 
-void format_commit_message(const struct commit *commit,
-                           const void *format, struct strbuf *sb)
+static void format_commit_item(struct strbuf *sb, const char *placeholder,
+                               void *context)
 {
-	struct interp table[] = {
-		{ "%H" },	/* commit hash */
-		{ "%h" },	/* abbreviated commit hash */
-		{ "%T" },	/* tree hash */
-		{ "%t" },	/* abbreviated tree hash */
-		{ "%P" },	/* parent hashes */
-		{ "%p" },	/* abbreviated parent hashes */
-		{ "%an" },	/* author name */
-		{ "%ae" },	/* author email */
-		{ "%ad" },	/* author date */
-		{ "%aD" },	/* author date, RFC2822 style */
-		{ "%ar" },	/* author date, relative */
-		{ "%at" },	/* author date, UNIX timestamp */
-		{ "%ai" },	/* author date, ISO 8601 */
-		{ "%cn" },	/* committer name */
-		{ "%ce" },	/* committer email */
-		{ "%cd" },	/* committer date */
-		{ "%cD" },	/* committer date, RFC2822 style */
-		{ "%cr" },	/* committer date, relative */
-		{ "%ct" },	/* committer date, UNIX timestamp */
-		{ "%ci" },	/* committer date, ISO 8601 */
-		{ "%e" },	/* encoding */
-		{ "%s" },	/* subject */
-		{ "%b" },	/* body */
-		{ "%Cred" },	/* red */
-		{ "%Cgreen" },	/* green */
-		{ "%Cblue" },	/* blue */
-		{ "%Creset" },	/* reset color */
-		{ "%n" },	/* newline */
-		{ "%m" },	/* left/right/bottom */
-	};
-	enum interp_index {
-		IHASH = 0, IHASH_ABBREV,
-		ITREE, ITREE_ABBREV,
-		IPARENTS, IPARENTS_ABBREV,
-		IAUTHOR_NAME, IAUTHOR_EMAIL,
-		IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE,
-		IAUTHOR_TIMESTAMP, IAUTHOR_ISO8601,
-		ICOMMITTER_NAME, ICOMMITTER_EMAIL,
-		ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822,
-		ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP,
-		ICOMMITTER_ISO8601,
-		IENCODING,
-		ISUBJECT,
-		IBODY,
-		IRED, IGREEN, IBLUE, IRESET_COLOR,
-		INEWLINE,
-		ILEFT_RIGHT,
-	};
+	struct commit *commit = context;
 	struct commit_list *p;
-	char parents[1024];
-	unsigned long len;
 	int i;
 	enum { HEADER, SUBJECT, BODY } state;
 	const char *msg = commit->buffer;
 
-	if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table))
-		die("invalid interp table!");
-
 	/* these are independent of the commit */
-	interp_set_entry(table, IRED, "\033[31m");
-	interp_set_entry(table, IGREEN, "\033[32m");
-	interp_set_entry(table, IBLUE, "\033[34m");
-	interp_set_entry(table, IRESET_COLOR, "\033[m");
-	interp_set_entry(table, INEWLINE, "\n");
+	switch (placeholder[0]) {
+	case 'C':
+		switch (placeholder[3]) {
+		case 'd':	/* red */
+			strbuf_addstr(sb, "\033[31m");
+			return;
+		case 'e':	/* green */
+			strbuf_addstr(sb, "\033[32m");
+			return;
+		case 'u':	/* blue */
+			strbuf_addstr(sb, "\033[34m");
+			return;
+		case 's':	/* reset color */
+			strbuf_addstr(sb, "\033[m");
+			return;
+		}
+	case 'n':		/* newline */
+		strbuf_addch(sb, '\n');
+		return;
+	}
 
 	/* these depend on the commit */
 	if (!commit->object.parsed)
 		parse_object(commit->object.sha1);
-	interp_set_entry(table, IHASH, sha1_to_hex(commit->object.sha1));
-	interp_set_entry(table, IHASH_ABBREV,
-			find_unique_abbrev(commit->object.sha1,
-				DEFAULT_ABBREV));
-	interp_set_entry(table, ITREE, sha1_to_hex(commit->tree->object.sha1));
-	interp_set_entry(table, ITREE_ABBREV,
-			find_unique_abbrev(commit->tree->object.sha1,
-				DEFAULT_ABBREV));
-	interp_set_entry(table, ILEFT_RIGHT,
-			 (commit->object.flags & BOUNDARY)
-			 ? "-"
-			 : (commit->object.flags & SYMMETRIC_LEFT)
-			 ? "<"
-			 : ">");
-
-	parents[1] = 0;
-	for (i = 0, p = commit->parents;
-			p && i < sizeof(parents) - 1;
-			p = p->next)
-		i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
-			sha1_to_hex(p->item->object.sha1));
-	interp_set_entry(table, IPARENTS, parents + 1);
-
-	parents[1] = 0;
-	for (i = 0, p = commit->parents;
-			p && i < sizeof(parents) - 1;
-			p = p->next)
-		i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
-			find_unique_abbrev(p->item->object.sha1,
-				DEFAULT_ABBREV));
-	interp_set_entry(table, IPARENTS_ABBREV, parents + 1);
 
+	switch (placeholder[0]) {
+	case 'H':		/* commit hash */
+		strbuf_addstr(sb, sha1_to_hex(commit->object.sha1));
+		return;
+	case 'h':		/* abbreviated commit hash */
+		strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,
+		                                     DEFAULT_ABBREV));
+		return;
+	case 'T':		/* tree hash */
+		strbuf_addstr(sb, sha1_to_hex(commit->tree->object.sha1));
+		return;
+	case 't':		/* abbreviated tree hash */
+		strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1,
+		                                     DEFAULT_ABBREV));
+		return;
+	case 'P':		/* parent hashes */
+		for (p = commit->parents; p; p = p->next) {
+			if (p != commit->parents)
+				strbuf_addch(sb, ' ');
+			strbuf_addstr(sb, sha1_to_hex(p->item->object.sha1));
+		}
+		return;
+	case 'p':		/* abbreviated parent hashes */
+		for (p = commit->parents; p; p = p->next) {
+			if (p != commit->parents)
+				strbuf_addch(sb, ' ');
+			strbuf_addstr(sb, find_unique_abbrev(
+					p->item->object.sha1, DEFAULT_ABBREV));
+		}
+		return;
+	case 'm':		/* left/right/bottom */
+		strbuf_addch(sb, (commit->object.flags & BOUNDARY)
+		                 ? '-'
+		                 : (commit->object.flags & SYMMETRIC_LEFT)
+		                 ? '<'
+		                 : '>');
+		return;
+	}
+
+	/* For the rest we have to parse the commit header. */
 	for (i = 0, state = HEADER; msg[i] && state < BODY; i++) {
 		int eol;
 		for (eol = i; msg[eol] && msg[eol] != '\n'; eol++)
 			; /* do nothing */
 
 		if (state == SUBJECT) {
-			table[ISUBJECT].value = xmemdupz(msg + i, eol - i);
+			if (placeholder[0] == 's') {
+				strbuf_add(sb, msg + i, eol - i);
+				return;
+			}
 			i = eol;
 		}
 		if (i == eol) {
@@ -868,29 +862,66 @@ void format_commit_message(const struct commit *commit,
 			/* strip empty lines */
 			while (msg[eol + 1] == '\n')
 				eol++;
-		} else if (!prefixcmp(msg + i, "author "))
-			fill_person(table + IAUTHOR_NAME,
-					msg + i + 7, eol - i - 7);
-		else if (!prefixcmp(msg + i, "committer "))
-			fill_person(table + ICOMMITTER_NAME,
-					msg + i + 10, eol - i - 10);
-		else if (!prefixcmp(msg + i, "encoding "))
-			table[IENCODING].value =
-				xmemdupz(msg + i + 9, eol - i - 9);
+		} else if (!prefixcmp(msg + i, "author ")) {
+			if (placeholder[0] == 'a') {
+				format_person_part(sb, placeholder[1],
+				                   msg + i + 7, eol - i - 7);
+				return;
+			}
+		} else if (!prefixcmp(msg + i, "committer ")) {
+			if (placeholder[0] == 'c') {
+				format_person_part(sb, placeholder[1],
+				                   msg + i + 10, eol - i - 10);
+				return;
+			}
+		} else if (!prefixcmp(msg + i, "encoding ")) {
+			if (placeholder[0] == 'e') {
+				strbuf_add(sb, msg + i + 9, eol - i - 9);
+				return;
+			}
+		}
 		i = eol;
 	}
-	if (msg[i])
-		table[IBODY].value = xstrdup(msg + i);
-
-	len = interpolate(sb->buf + sb->len, strbuf_avail(sb),
-				format, table, ARRAY_SIZE(table));
-	if (len > strbuf_avail(sb)) {
-		strbuf_grow(sb, len);
-		interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1,
-					format, table, ARRAY_SIZE(table));
-	}
-	strbuf_setlen(sb, sb->len + len);
-	interp_clear_table(table, ARRAY_SIZE(table));
+	if (msg[i] && placeholder[0] == 'b')	/* body */
+		strbuf_addstr(sb, msg + i);
+}
+
+void format_commit_message(const struct commit *commit,
+                           const void *format, struct strbuf *sb)
+{
+	const char *placeholders[] = {
+		"H",		/* commit hash */
+		"h",		/* abbreviated commit hash */
+		"T",		/* tree hash */
+		"t",		/* abbreviated tree hash */
+		"P",		/* parent hashes */
+		"p",		/* abbreviated parent hashes */
+		"an",		/* author name */
+		"ae",		/* author email */
+		"ad",		/* author date */
+		"aD",		/* author date, RFC2822 style */
+		"ar",		/* author date, relative */
+		"at",		/* author date, UNIX timestamp */
+		"ai",		/* author date, ISO 8601 */
+		"cn",		/* committer name */
+		"ce",		/* committer email */
+		"cd",		/* committer date */
+		"cD",		/* committer date, RFC2822 style */
+		"cr",		/* committer date, relative */
+		"ct",		/* committer date, UNIX timestamp */
+		"ci",		/* committer date, ISO 8601 */
+		"e",		/* encoding */
+		"s",		/* subject */
+		"b",		/* body */
+		"Cred",		/* red */
+		"Cgreen",	/* green */
+		"Cblue",	/* blue */
+		"Creset",	/* reset color */
+		"n",		/* newline */
+		"m",		/* left/right/bottom */
+		NULL
+	};
+	strbuf_expand(sb, format, placeholders, format_commit_item, (void *)commit);
 }
 
 static void pp_header(enum cmit_fmt fmt,
diff --git a/strbuf.c b/strbuf.c
index f4201e1..b71da99 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -129,6 +129,28 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
 	strbuf_setlen(sb, sb->len + len);
 }
 
+void strbuf_expand(struct strbuf *sb, const char *fmt,
+                   const char **placeholders, expand_fn_t fn, void *context)
+{
+	char c;
+	const char **p;
+
+	while ((c = *fmt++)) {
+		if (c != '%') {
+			strbuf_addch(sb, c);
+			continue;
+		}
+
+		for (p = placeholders; *p; p++) {
+			if (!prefixcmp(fmt, *p)) {
+				fn(sb, *p, context);
+				fmt += strlen(*p);
+				break;
+			}
+		}
+	}
+}
+
 size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
 {
 	size_t res;
diff --git a/strbuf.h b/strbuf.h
index 9b9e861..b9c3e79 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -102,6 +102,9 @@ static inline void strbuf_addbuf(struct strbuf *sb, struct strbuf *sb2) {
 	strbuf_add(sb, sb2->buf, sb2->len);
 }
 
+typedef void (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
+extern void strbuf_expand(struct strbuf *sb, const char *fmt, const char **placeholders, expand_fn_t fn, void *context);
+
 __attribute__((format(printf,2,3)))
 extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
 

^ permalink raw reply related

* Re: gitk graph routing problem
From: Alex Riesen @ 2007-11-05  1:19 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Paul Mackerras, git
In-Reply-To: <20071105011700.GB5355@steel.home>

Alex Riesen, Mon, Nov 05, 2007 02:17:00 +0100:
> Alex Riesen, Mon, Nov 05, 2007 01:53:49 +0100:
> > Junio C Hamano, Mon, Nov 05, 2007 00:14:29 +0100:
> > > Paul Mackerras <paulus@samba.org> writes:
> > > 
> > > > Alex Riesen writes:
> > > >
> > > >> To reproduce, try running in git repo:
> > > >> 
> > > >>     gitk 02f630448e5d48e..06ea6ba9cf46ef5
> > > >
> > > > I can't reproduce it here, as my clone of the git repo doesn't have
> > > > 02f630448e5d48e in it, even after updating...
> > > 
> > > Heh, me neither ;-).
> > 
> > Stupid me. Will follow up with a link to the archived repo in private mail.
> > 

and of course, I send it to everyone... Ach, well. Will keep it for a
week, or until it is reproduced somewhere else. Whichever comes first.

^ permalink raw reply

* Re: gitk graph routing problem
From: Alex Riesen @ 2007-11-05  1:17 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Paul Mackerras, git
In-Reply-To: <20071105005349.GA5355@steel.home>

Alex Riesen, Mon, Nov 05, 2007 01:53:49 +0100:
> Junio C Hamano, Mon, Nov 05, 2007 00:14:29 +0100:
> > Paul Mackerras <paulus@samba.org> writes:
> > 
> > > Alex Riesen writes:
> > >
> > >> To reproduce, try running in git repo:
> > >> 
> > >>     gitk 02f630448e5d48e..06ea6ba9cf46ef5
> > >
> > > I can't reproduce it here, as my clone of the git repo doesn't have
> > > 02f630448e5d48e in it, even after updating...
> > 
> > Heh, me neither ;-).
> 
> Stupid me. Will follow up with a link to the archived repo in private mail.
> 

http://ftp-tmp:ftp-tmp@h877881.stratoserver.net/~ftp-tmp/break-gitk.git.tar.bz2

Its around 60mb.

^ permalink raw reply

* Re: [PATCH] fix display overlap between remote and local progress
From: Nicolas Pitre @ 2007-11-05  1:07 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Johannes Schindelin, git
In-Reply-To: <7v4pg1sj1r.fsf@gitster.siamese.dyndns.org>

On Sun, 4 Nov 2007, Junio C Hamano wrote:

> The compiler at k.org complains of "\e" being non ISO-C, though.

Bummer.

...

Signed-off-by: Nicolas Pitre <nico@cam.org>
---
diff --git a/sideband.c b/sideband.c
index 58edea6..756bbc2 100644
--- a/sideband.c
+++ b/sideband.c
@@ -13,7 +13,7 @@
  */
 
 #define PREFIX "remote:"
-#define SUFFIX "\e[K"  /* change to "        " if ANSI sequences don't work */
+#define SUFFIX "\033[K"  /* change to "        " if ANSI sequences don't work */
 
 int recv_sideband(const char *me, int in_stream, int out, int err)
 {

^ permalink raw reply related

* Re: gitk graph routing problem
From: Alex Riesen @ 2007-11-05  0:53 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Paul Mackerras, git
In-Reply-To: <7vzlxtr4bu.fsf@gitster.siamese.dyndns.org>

Junio C Hamano, Mon, Nov 05, 2007 00:14:29 +0100:
> Paul Mackerras <paulus@samba.org> writes:
> 
> > Alex Riesen writes:
> >
> >> To reproduce, try running in git repo:
> >> 
> >>     gitk 02f630448e5d48e..06ea6ba9cf46ef5
> >
> > I can't reproduce it here, as my clone of the git repo doesn't have
> > 02f630448e5d48e in it, even after updating...
> 
> Heh, me neither ;-).

Stupid me. Will follow up with a link to the archived repo in private mail.

^ permalink raw reply

* Re: [ANNOUNCE] cgit v0.7
From: Jakub Narebski @ 2007-11-05  0:29 UTC (permalink / raw)
  To: Lars Hjemli; +Cc: git list, Jakub Narebski
In-Reply-To: <8c5c35580711030408n658eb11fk19d554f0fa3b17@mail.gmail.com>

Lars Hjemli wrote:

> cgit v0.7 (a fast webinterface for git) is now available at
> 
>      git://hjemli.net/pub/git/cgit
> 
> This release includes better search capabilities, better diff,
> filtered and sorted branch/tag lists on the summary page, a simple way
> to switch between branches and finally a much needed restructuring of
> the user interface. There is even a brand new logo, and a number of
> bugfixes.
> 
> The latest version can be seen in action on http://hjemli.net/git/
> (disclaimer: I'm not a web/user interface designer, and should not be
> held responsible for any eyeball-related damages).

A few comments about new cgit:

 * Very nice cgit logo, but no favicon. Perhaps pacman head and G,
   or pacman head (like in logo) and +/-...

 * Providing reference with full sha1 of referenced object for tags
   list is not IMVHO a good design: what is interesting is type of
   tag, if it is signed it's first line, and if it is lightweight
   pointing to tag then perhaps commit subject.

 * Nice diffstat in commit view; the diff view is better, although I
   wouldn't lump from-file / to-file diff header together with git
   diff header and extended git diff header.

 * I like the sidebar very much, although I'm not sure how it would
   work for larger projects (more branches, much more tags). Also the
   search textbox is not very visible; I'd rather it have "groove"
   view.

 * I like separate 'mirrors' section, although I think it rather
   clashes badly with notion of forks (alternates).

 * I'm not sure if it wouln't be beter to provide -n/+m lines changed
   instead of nn likes changed column.

 * Nice submodule support!

By the way, Freedesktop provides besides standard gitweb interface
also cgit interface at
  http://cgit.freedesktop.org/
Take a look at how such site looks like with large number of projects
(perhaps sidebar is noot such a good idea then?), and with large
projects.

-- 
Jakub Narebski
Poland

^ permalink raw reply

* Re: [PATCH] Add more tests for git-clean
From: Junio C Hamano @ 2007-11-05  0:17 UTC (permalink / raw)
  To: Pierre Habouzit; +Cc: Shawn Bohrer, git
In-Reply-To: <20071104234617.GG4207@artemis.corp>

Pierre Habouzit <madcoder@debian.org> writes:

> On Sun, Nov 04, 2007 at 11:35:42PM +0000, Junio C Hamano wrote:
>> Shawn Bohrer <shawn.bohrer@gmail.com> writes:
>> 
>> > +test_expect_success 'git-clean with prefix' '
>> > +
>> > +	mkdir -p build docs &&
>> > +	touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
>> > +	cd src/ &&
>> > +	git-clean &&
>> > +	cd - &&
>> 
>> This is wrong for two reasons.
>> 
>>  - Is "cd -" portable?
>
>   this is POSIX:

That actually doesn't matter.  What the real world shells do
matters more.

In addition, "cd -" is a nice shorthand for interactive use but
it is a bad discipline to use it in a script anyway.

	...
	( cd src && git-clean ) &&
	...

would be the best way to write this.

^ permalink raw reply

* Re: [PATCH] Add more tests for git-clean
From: Johannes Schindelin @ 2007-11-04 23:49 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Shawn Bohrer, git
In-Reply-To: <7vve8hr3ch.fsf@gitster.siamese.dyndns.org>

Hi,

On Sun, 4 Nov 2007, Junio C Hamano wrote:

> Shawn Bohrer <shawn.bohrer@gmail.com> writes:
> 
> > +test_expect_success 'git-clean with prefix' '
> > +
> > +	mkdir -p build docs &&
> > +	touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
> > +	cd src/ &&
> > +	git-clean &&
> > +	cd - &&
> 
> This is wrong for two reasons.
> 
>  - Is "cd -" portable?
> 
>  - What happens when git-clean fails?  This test fails, and then
>    it goes on to the next test without cd'ing back.

So it should be

	(cd src/ && git clean) &&

right?  (Note that I also removed the dash, since it will be a builtin 
after the next commit.)

Ciao,
Dscho

^ permalink raw reply

* Re: Warning: cvsexportcommit considered dangerous
From: Johannes Schindelin @ 2007-11-04 23:46 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: Steffen Prohaska, Git Mailing List, Alex Bennee
In-Reply-To: <200711050005.28561.robin.rosenberg.lists@dewire.com>

Hi,

On Mon, 5 Nov 2007, Robin Rosenberg wrote:

> BTW, wouldn't this err on the right side anyway, i.e. if an existing 
> file was not up to date and was wrongly thought to not exist or a new 
> file was thought to be up-to-date, I would get an error and would not be 
> able to commit. I've never seen it though and I always have a clean CVS 
> checkout so the potential bug seems unlikely to me.

The problem is that it can err.  For example when I have new files, it 
says that the files were already added by someone else.  And then it 
refuses to do anything.

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH] Add more tests for git-clean
From: Pierre Habouzit @ 2007-11-04 23:46 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Shawn Bohrer, git
In-Reply-To: <7vve8hr3ch.fsf@gitster.siamese.dyndns.org>

[-- Attachment #1: Type: text/plain, Size: 864 bytes --]

On Sun, Nov 04, 2007 at 11:35:42PM +0000, Junio C Hamano wrote:
> Shawn Bohrer <shawn.bohrer@gmail.com> writes:
> 
> > +test_expect_success 'git-clean with prefix' '
> > +
> > +	mkdir -p build docs &&
> > +	touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
> > +	cd src/ &&
> > +	git-clean &&
> > +	cd - &&
> 
> This is wrong for two reasons.
> 
>  - Is "cd -" portable?

  this is POSIX:

8910 − When a hyphen is used as the operand, this shall be equivalent to the command:
8911   cd "$OLDPWD" && pwd
8912   which changes to the previous working directory and then writes its name.

  Meaning that cd $OLDPWD should work, and won't print $OLDPWD.

-- 
·O·  Pierre Habouzit
··O                                                madcoder@debian.org
OOO                                                http://www.madism.org

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply

* Re: [PATCH] Add more tests for git-clean
From: Junio C Hamano @ 2007-11-04 23:35 UTC (permalink / raw)
  To: Shawn Bohrer; +Cc: git, gitster
In-Reply-To: <11942029442710-git-send-email-shawn.bohrer@gmail.com>

Shawn Bohrer <shawn.bohrer@gmail.com> writes:

> +test_expect_success 'git-clean with prefix' '
> +
> +	mkdir -p build docs &&
> +	touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
> +	cd src/ &&
> +	git-clean &&
> +	cd - &&

This is wrong for two reasons.

 - Is "cd -" portable?

 - What happens when git-clean fails?  This test fails, and then
   it goes on to the next test without cd'ing back.

^ permalink raw reply

* Re: gitk graph routing problem
From: Junio C Hamano @ 2007-11-04 23:14 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: Alex Riesen, git
In-Reply-To: <18222.15870.945694.238217@cargo.ozlabs.ibm.com>

Paul Mackerras <paulus@samba.org> writes:

> Alex Riesen writes:
>
>> To reproduce, try running in git repo:
>> 
>>     gitk 02f630448e5d48e..06ea6ba9cf46ef5
>
> I can't reproduce it here, as my clone of the git repo doesn't have
> 02f630448e5d48e in it, even after updating...

Heh, me neither ;-).

^ permalink raw reply

* Re: [PATCH 01/10] Add a parseopt mode to git-rev-parse to bring parse-options to shell scripts.
From: Pierre Habouzit @ 2007-11-04 23:14 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7v8x5dsjne.fsf@gitster.siamese.dyndns.org>

[-- Attachment #1: Type: text/plain, Size: 1289 bytes --]

On Sun, Nov 04, 2007 at 10:58:13PM +0000, Junio C Hamano wrote:
> Two comments.
> 
>  * I have updated 1/10 with typo and indentation fixes.
> 
>  * I see you changed 2/10 to use OPTIONS_KEEPDASHDASH instead of
>    PARSEOPT_OPTS, but the scripts that do not want the --keep
>    behaviour do not set OPTIONS_KEEPDASHDASH to empty, so I do
>    not see how this updatet would make _any_ difference.  The
>    user can still screw up by having OPTIONS_KEEPDASHDASH in
>    their environments by mistake, curiosity or just plain
>    stupidity.

  Hmmm right, I was worried by the fact that the old PARSEOPT_OPTS was
being possibly diverted to inject malicious commands. I tend to find the
forced `OPTIONS_KEEPDASHDASH=` thing quite painful, but indeed it is
probably the sole way to guard ourselves against user stupidity (or more
likely unclean environments).

  Do you mind adding: OPTIONS_KEEPDASHDASH= front to the 8 patches that
needs it, or should I send an updated series ? (actually it's more like
the 7 that needs it as git-clean has been rewritten as a builtin if I'm
correct).

-- 
·O·  Pierre Habouzit
··O                                                madcoder@debian.org
OOO                                                http://www.madism.org

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply

* Re: [PATCH] errors: "strict subset" -> "ancestor"
From: Steffen Prohaska @ 2007-11-04 23:13 UTC (permalink / raw)
  To: J. Bruce Fields; +Cc: Junio C Hamano, Git Mailing List
In-Reply-To: <20071104220810.GC22762@fieldses.org>


On Nov 4, 2007, at 11:08 PM, J. Bruce Fields wrote:

> On Sat, Nov 03, 2007 at 08:51:29AM +0100, Steffen Prohaska wrote:
>>
>> On Nov 3, 2007, at 3:39 AM, J. Bruce Fields wrote:
>>
>>> From: J. Bruce Fields <bfields@citi.umich.edu>
>>>
>>> The term "ancestor" is a bit more intuitive (and more consistent  
>>> with
>>> the documentation) than the term "strict subset".
>>>
>>> Also, remove superfluous "ref", capitalize, and add some carriage
>>> returns, changing:
>>>
>>> 	error: remote 'refs/heads/master' is not a strict subset of  
>>> local ref
>>> 'refs/heads/master'. maybe you are not up-to-date and need to  
>>> pull first?
>>> 	error: failed to push to 'ssh://linux-nfs.org/~bfields/exports/ 
>>> git.git'
>>>
>>> to:
>>>
>>> 	error: remote 'refs/heads/master' is not an ancestor of
>>> 	 local 'refs/heads/master'.
>>> 	 Maybe you are not up-to-date and need to pull first?
>>> 	error: failed to push to 'ssh://linux-nfs.org/~bfields/exports/ 
>>> git.git'
>>
>>
>> Junio suggested in [1] (see also earlier messages in that
>> thread) to replace the recommendation to pull with a hint
>> where to look in the user manual.
>>
>> [1] http://marc.info/?l=git&m=119398999317677&w=2
>>
>>
>> The point is, there are various ways to resolve the problem.
>> pull is not necessarily the right solution. At least, you should
>> consider to rebase. Or maybe just something else went wrong.
>
> Yeah, actually in my case I usually want to force....
>
> So I think it's a good suggestion, but I'm putting it off for now  
> as I'm
> not sure yet where to refer people to, and don't like making the  
> error a
> lot longer.

I agree. And it's probably a waste of time anyway, because
sooner or later the mega-terse fetch output will be extended
to push.


> Hm.  I wonder if extra "help" commandline flags would be a way to get
> people extra guidance on particular situations without cluttering  
> up the
> default messages ("not sure what to try next?  Try -h  
> notanancestor..."
> Maybe not.)

The first step would be to describe the error messages in the
manual (or the man pages), and provide hints how to resolve
them. Currently we have no place we could link to, even if we
had a mechanism to do so.

	Steffen

^ permalink raw reply

* Re: [PATCH] fix display overlap between remote and local progress
From: Junio C Hamano @ 2007-11-04 23:11 UTC (permalink / raw)
  To: Nicolas Pitre; +Cc: Johannes Schindelin, git
In-Reply-To: <alpine.LFD.0.9999.0711041610520.21255@xanadu.home>

Nicolas Pitre <nico@cam.org> writes:

> On Sun, 4 Nov 2007, Johannes Schindelin wrote:
>
>> On Sun, 4 Nov 2007, Nicolas Pitre wrote:
>> 
>> > +#define SUFFIX "\e[K"  /* change to "        " if ANSI sequences don't work */ 
>> 
>> I am almost certain (without even testing) that cmd.exe has problems with 
>> that.  It does not even understand colorisation.
>
> That's what I was expecting.  This is why I suggested an alternative in 
> the comment.

That's fine --- cmd.exe weenies can patch it away ;-).

The compiler at k.org complains of "\e" being non ISO-C, though.

^ permalink raw reply

* Re: Warning: cvsexportcommit considered dangerous
From: Robin Rosenberg @ 2007-11-04 23:05 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Steffen Prohaska, Git Mailing List, Alex Bennee
In-Reply-To: <Pine.LNX.4.64.0711042133330.4362@racer.site>

söndag 04 november 2007 skrev Johannes Schindelin:
> Hi,
> 
> On Sun, 4 Nov 2007, Steffen Prohaska wrote:
> 
> > On Nov 4, 2007, at 5:41 PM, Johannes Schindelin wrote:
> > 
> > > ever since the up-to-date check was changed to use just one call to 
> > > "cvs status", a bug was present.  Now cvsexportcommit expects "cvs 
> > > status" to return the results in the same order as the file names were 
> > > passed.
> > > 
> > > This is not true, as I had to realise with one of my projects on 
> > > sourceforge.
> > > 
> > > Since time is so scarce on my side, I will not have time to fix this 
> > > bug, but will instead return to my old "commit by hand" procedure.
> > 
> > I introduced this 'optimization', which turned out to be a bug. So, I 
> > feel responsible. Sorry for the trouble.
> > 
> > In August this was already recognized and a patch submitted:
> > 
> > http://marc.info/?t=118718458000004&r=1&w=2
> > 
> > I do not know why it wasn't applied. I forgot re-checking after my 
> > vacation.
> 
> It slipped by me, because of holiday, too.  (I was on my well needed 
> holiday then.)
> 
> But that patch really seems like a step back to me.  The line "File: ... 
> Status: ..." should be parsable enough to fix the bug properly, instead of 
> undoing the optimisation.
Unfortunately it's not that easy to parse. It *can* be done by looking at the
repository path, and the CVS/Root etc, but it's not nice. 

> 
> AFAICS Robin replied with a "let's see if a proper fix materialises", and 
> I kind of hope that it will materialise soon.

Still hoping :). BTW, wouldn't this err on the right side anyway, i.e. if an
existing file was not up to date and was wrongly thought to not exist or a new 
file was thought to be up-to-date, I would get an error and would not be able
to commit. I've never seen it though and I always have a clean CVS checkout
so the potential bug seems unlikely to me.

The command I always use is.

	git cvsexportcommit -u -c -w /my/cvs/checkout

Never bitten me yet (touch wood).

My real worry is on the other side, with bad conversion from CVS to git.

-- robin

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox