git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[StGit PATCH 2/2] Write to a stack log when stack is modified

From
Karl Hasselström <kha@treskal.com>
Date
Feb 14, 2008, 01:32 UTC
Message-ID
<20080214012912.22365.83207.stgit@yoghurt>
In-Reply-To
<20080214012119.22365.54526.stgit@yoghurt>

Create a log branch (called <branchname>.stgit) for each StGit branch, and write to it whenever the stack is modified.

As of yet, nothing can be done with this log.
Signed-off-by: Karl Hasselström <kha@treskal.com>
---

Lots of noise here since all the old-infrastructure commands update the log manually. But note how few lines it takes to actually write the blobs, trees, and commits (in log.py, near the end).

 stgit/commands/branch.py   |   18 ++++++++++---
 stgit/commands/clone.py    |    3 +-
 stgit/commands/float.py    |    3 ++
 stgit/commands/fold.py     |    3 +-
 stgit/commands/imprt.py    |    3 +-
 stgit/commands/init.py     |    3 +-
 stgit/commands/pick.py     |    3 +-
 stgit/commands/pop.py      |    3 +-
 stgit/commands/pull.py     |    3 +-
 stgit/commands/push.py     |    6 ++--
 stgit/commands/rebase.py   |    4 +--
 stgit/commands/refresh.py  |    4 ++-
 stgit/commands/rename.py   |    3 +-
 stgit/commands/resolved.py |    4 ++-
 stgit/commands/sink.py     |    3 +-
 stgit/commands/sync.py     |    5 +++-
 stgit/lib/log.py           |   60 ++++++++++++++++++++++++++++++++++++++++++++
 stgit/lib/transaction.py   |    3 +-
 18 files changed, 111 insertions(+), 23 deletions(-)
 create mode 100644 stgit/lib/log.py
diff --git a/stgit/commands/branch.py b/stgit/commands/branch.py
index 50684bb..131dfea 100644
--- a/stgit/commands/branch.py
+++ b/stgit/commands/branch.py
@@ -25,7 +25,7 @@ from stgit.commands.common import *
 from stgit.utils import *
 from stgit.out import *
 from stgit import stack, git, basedir
-
+from stgit.lib import log
 
 help = 'manage patch stacks'
 usage = """%prog [options] branch-name [commit-id]
@@ -161,6 +161,7 @@ def func(parser, options, args):
                                    parent_branch = parentbranch)
 
         out.info('Branch "%s" created' % args[0])
+        log.log_entry(log.default_stack(), 'stg branch --create')
         return
 
     elif options.clone:
@@ -181,6 +182,8 @@ def func(parser, options, args):
         crt_series.clone(clone)
         out.done()
 
+        log.copy_log(log.default_repo(), crt_series.get_name(), clone,
+                     'stg branch --clone')
         return
 
     elif options.delete:
@@ -188,6 +191,7 @@ def func(parser, options, args):
         if len(args) != 1:
             parser.error('incorrect number of arguments')
         __delete_branch(args[0], options.force)
+        log.delete_log(log.default_repo(), args[0])
         return
 
     elif options.list:
@@ -195,13 +199,16 @@ def func(parser, options, args):
         if len(args) != 0:
             parser.error('incorrect number of arguments')
 
-        branches = git.get_heads()
-        branches.sort()
+        branches = set(git.get_heads())
+        for br in set(branches):
+            m = re.match(r'^(.*)\.stgit$', br)
+            if m and m.group(1) in branches:
+                branches.remove(br)
 
         if branches:
             out.info('Available branches:')
             max_len = max([len(i) for i in branches])
-            for i in branches:
+            for i in sorted(branches):
                 __print_branch(i, max_len)
         else:
             out.info('No branches')
@@ -238,7 +245,8 @@ def func(parser, options, args):
         stack.Series(args[0]).rename(args[1])
 
         out.info('Renamed branch "%s" to "%s"' % (args[0], args[1]))
-
+        log.rename_log(log.default_repo(), args[0], args[1],
+                       'stg branch --rename')
         return
 
     elif options.unprotect:
diff --git a/stgit/commands/clone.py b/stgit/commands/clone.py
index c3b0bbe..8c3cccd 100644
--- a/stgit/commands/clone.py
+++ b/stgit/commands/clone.py
@@ -21,7 +21,7 @@ from optparse import OptionParser, make_option
 from stgit.commands.common import *
 from stgit.utils import *
 from stgit import stack, git
-
+from stgit.lib import log
 
 help = 'make a local clone of a remote repository'
 usage = """%prog [options] <repository> <dir>
@@ -56,5 +56,6 @@ def func(parser, options, args):
     # to work on a brand new repository
     basedir.clear_cache()
     stack.Series().init()
+    log.log_entry(log.default_stack(), 'stg clone')
 
     print 'done'
diff --git a/stgit/commands/float.py b/stgit/commands/float.py
index 0ba4446..5fec87e 100644
--- a/stgit/commands/float.py
+++ b/stgit/commands/float.py
@@ -22,6 +22,7 @@ from optparse import OptionParser, make_option
 from stgit.commands.common import *
 from stgit.utils import *
 from stgit import stack, git
+from stgit.lib import log
 
 help = 'push patches to the top, even if applied'
 usage = """%prog [<patches> | -s [<series>] ]
@@ -89,3 +90,5 @@ def func(parser, options, args):
         if topush:
             topush.reverse()
             push_patches(crt_series, topush)
+
+    log.log_entry(log.default_stack(), 'stg float')
diff --git a/stgit/commands/fold.py b/stgit/commands/fold.py
index 3930a1f..105f54a 100644
--- a/stgit/commands/fold.py
+++ b/stgit/commands/fold.py
@@ -22,7 +22,7 @@ from stgit.commands.common import *
 from stgit.utils import *
 from stgit.out import *
 from stgit import stack, git
-
+from stgit.lib import log
 
 help = 'integrate a GNU diff patch into the current patch'
 usage = """%prog [options] [<file>]
@@ -80,3 +80,4 @@ def func(parser, options, args):
         git.apply_patch(filename = filename)
 
     out.done()
+    log.log_entry(log.default_stack(), 'stg fold')
diff --git a/stgit/commands/imprt.py b/stgit/commands/imprt.py
index 4a4b792..d90e45d 100644
--- a/stgit/commands/imprt.py
+++ b/stgit/commands/imprt.py
@@ -24,7 +24,7 @@ from stgit.commands.common import *
 from stgit.utils import *
 from stgit.out import *
 from stgit import stack, git
-
+from stgit.lib import log
 
 help = 'import a GNU diff file as a new patch'
 usage = """%prog [options] [<file>|<url>]
@@ -289,3 +289,4 @@ def func(parser, options, args):
         __import_file(filename, options)
 
     print_crt_patch(crt_series)
+    log.log_entry(log.default_stack(), 'stg import')
diff --git a/stgit/commands/init.py b/stgit/commands/init.py
index 475a4ce..b49b7b2 100644
--- a/stgit/commands/init.py
+++ b/stgit/commands/init.py
@@ -22,7 +22,7 @@ from optparse import OptionParser, make_option
 from stgit.commands.common import *
 from stgit.utils import *
 from stgit import stack, git
-
+from stgit.lib import log
 
 help = 'initialise the current branch for use with StGIT'
 usage = """%prog [options]
@@ -42,3 +42,4 @@ def func(parser, options, args):
         parser.error('incorrect number of arguments')
 
     crt_series.init()
+    log.log_entry(log.default_stack(), 'stg init')
diff --git a/stgit/commands/pick.py b/stgit/commands/pick.py
index e011a84..4cc8944 100644
--- a/stgit/commands/pick.py
+++ b/stgit/commands/pick.py
@@ -23,7 +23,7 @@ from stgit.utils import *
 from stgit.out import *
 from stgit import stack, git
 from stgit.stack import Series
-
+from stgit.lib import log
 
 help = 'import a patch from a different branch or a commit object'
 usage = """%prog [options] [<patch@branch>|<commit>]
@@ -163,4 +163,5 @@ def func(parser, options, args):
         else:
             out.done()
 
+    log.log_entry(log.default_stack(), 'stg pick')
     print_crt_patch(crt_series)
diff --git a/stgit/commands/pop.py b/stgit/commands/pop.py
index dedf41b..e60361e 100644
--- a/stgit/commands/pop.py
+++ b/stgit/commands/pop.py
@@ -22,7 +22,7 @@ from optparse import OptionParser, make_option
 from stgit.commands.common import *
 from stgit.utils import *
 from stgit import stack, git
-
+from stgit.lib import log
 
 help = 'pop one or more patches from the stack'
 usage = """%prog [options] [<patch1>] [<patch2>] [<patch3>..<patch4>]
@@ -87,4 +87,5 @@ def func(parser, options, args):
     if topush:
         push_patches(crt_series, topush)
 
+    log.log_entry(log.default_stack(), 'stg pop')
     print_crt_patch(crt_series)
diff --git a/stgit/commands/pull.py b/stgit/commands/pull.py
index bec7fd7..8be5a14 100644
--- a/stgit/commands/pull.py
+++ b/stgit/commands/pull.py
@@ -23,7 +23,7 @@ from stgit.utils import *
 from stgit.out import *
 from stgit.config import GitConfigException
 from stgit import stack, git
-
+from stgit.lib import log
 
 help = 'pull the changes from the remote repository'
 usage = """%prog [options] [<repository>]
@@ -106,4 +106,5 @@ def func(parser, options, args):
     if config.get('stgit.keepoptimized') == 'yes':
         git.repack()
 
+    log.log_entry(log.default_stack(), 'stg pull')
     print_crt_patch(crt_series)
diff --git a/stgit/commands/push.py b/stgit/commands/push.py
index 979835b..148a0b5 100644
--- a/stgit/commands/push.py
+++ b/stgit/commands/push.py
@@ -23,7 +23,7 @@ from stgit.commands.common import *
 from stgit.utils import *
 from stgit.out import *
 from stgit import stack, git
-
+from stgit.lib import log
 
 help = 'push one or more patches onto of the stack'
 usage = """%prog [options] [<patch1>] [<patch2>] [<patch3>..<patch4>]
@@ -69,11 +69,11 @@ def func(parser, options, args):
         out.start('Undoing push of "%s"' % patch)
         resolved_all()
         if crt_series.undo_push():
+            log.log_entry(log.default_stack(), 'stg push --undo')
             out.done()
         else:
             out.done('patch unchanged')
         print_crt_patch(crt_series)
-
         return
 
     check_local_changes()
@@ -100,5 +100,5 @@ def func(parser, options, args):
         patches.reverse()
 
     push_patches(crt_series, patches, options.merged)
-
+    log.log_entry(log.default_stack(), 'stg push')
     print_crt_patch(crt_series)
diff --git a/stgit/commands/rebase.py b/stgit/commands/rebase.py
index 12faaf8..41e93ce 100644
--- a/stgit/commands/rebase.py
+++ b/stgit/commands/rebase.py
@@ -21,7 +21,7 @@ from optparse import OptionParser, make_option
 from stgit.commands.common import *
 from stgit.utils import *
 from stgit import stack, git
-
+from stgit.lib import log
 
 help = 'move the stack base to another point in history'
 usage = """%prog [options] <new-base-id>
@@ -57,5 +57,5 @@ def func(parser, options, args):
     applied = prepare_rebase(crt_series)
     rebase(crt_series, args[0])
     post_rebase(crt_series, applied, options.nopush, options.merged)
-
+    log.log_entry(log.default_stack(), 'stg rebase')
     print_crt_patch(crt_series)
diff --git a/stgit/commands/refresh.py b/stgit/commands/refresh.py
index 4695c62..017ec5b 100644
--- a/stgit/commands/refresh.py
+++ b/stgit/commands/refresh.py
@@ -24,7 +24,7 @@ from stgit.utils import *
 from stgit.out import *
 from stgit import stack, git
 from stgit.config import config
-
+from stgit.lib import log
 
 help = 'generate a new commit for the current patch'
 usage = """%prog [options] [<files or dirs>]
@@ -91,6 +91,7 @@ def func(parser, options, args):
     if options.undo:
         out.start('Undoing the refresh of "%s"' % patch)
         crt_series.undo_refresh()
+        log.log_entry(log.default_stack(), 'stg refresh --undo')
         out.done()
         return
 
@@ -131,6 +132,7 @@ def func(parser, options, args):
         if options.patch:
             between.reverse()
             push_patches(crt_series, between)
+        log.log_entry(log.default_stack(), 'stg refresh')
     elif options.annotate:
         # only annotate the top log entry as there is no need to
         # refresh the patch and generate a full commit
diff --git a/stgit/commands/rename.py b/stgit/commands/rename.py
index e2b0fa4..4e9aac0 100644
--- a/stgit/commands/rename.py
+++ b/stgit/commands/rename.py
@@ -22,7 +22,7 @@ from stgit.commands.common import *
 from stgit.utils import *
 from stgit.out import *
 from stgit import stack, git
-
+from stgit.lib import log
 
 help = 'rename a patch in the series'
 usage = """%prog [options] <oldpatch> <newpatch>
@@ -42,4 +42,5 @@ def func(parser, options, args):
 
     out.start('Renaming patch "%s" to "%s"' % (args[0], args[1]))
     crt_series.rename_patch(args[0], args[1])
+    log.log_entry(log.default_stack(), 'stg rename')
     out.done()
diff --git a/stgit/commands/resolved.py b/stgit/commands/resolved.py
index 4ee75b8..cb21d84 100644
--- a/stgit/commands/resolved.py
+++ b/stgit/commands/resolved.py
@@ -24,7 +24,7 @@ from stgit.utils import *
 from stgit import stack, git, basedir
 from stgit.config import config, file_extensions
 from stgit.gitmergeonefile import interactive_merge
-
+from stgit.lib import log
 
 help = 'mark a file conflict as solved'
 usage = """%prog [options] [<files...>]
@@ -55,6 +55,7 @@ def func(parser, options, args):
 
     if options.all and not options.interactive:
         resolved_all(options.reset)
+        log.log_entry(log.default_stack(), 'stg resolved')
         return
 
     conflicts = git.get_conflicts()
@@ -82,3 +83,4 @@ def func(parser, options, args):
             git.resolved([filename])
     else:
         git.resolved(files, options.reset)
+    log.log_entry(log.default_stack(), 'stg resolved')
diff --git a/stgit/commands/sink.py b/stgit/commands/sink.py
index 2167d87..f3a9ada 100644
--- a/stgit/commands/sink.py
+++ b/stgit/commands/sink.py
@@ -22,7 +22,7 @@ from optparse import OptionParser, make_option
 from stgit.commands.common import *
 from stgit.utils import *
 from stgit import stack, git
-
+from stgit.lib import log
 
 help = 'send patches deeper down the stack'
 usage = """%prog [-t <target patch>] [-n] [<patches>]
@@ -68,3 +68,4 @@ def func(parser, options, args):
         def not_reapplied_yet(p):
             return not p in newapplied
         push_patches(crt_series, filter(not_reapplied_yet, oldapplied))
+    log.log_entry(log.default_stack(), 'stg sink')
diff --git a/stgit/commands/sync.py b/stgit/commands/sync.py
index 99ab7de..168199d 100644
--- a/stgit/commands/sync.py
+++ b/stgit/commands/sync.py
@@ -23,7 +23,7 @@ from stgit.commands.common import *
 from stgit.utils import *
 from stgit.out import *
 from stgit import stack, git
-
+from stgit.lib import log
 
 help = 'synchronise patches with a branch or a series'
 usage = """%prog [options] [<patch1>] [<patch2>] [<patch3>..<patch4>]
@@ -77,6 +77,7 @@ def func(parser, options, args):
         out.start('Undoing the sync of "%s"' % crt_series.get_current())
         crt_series.undo_refresh()
         git.reset()
+        log.log_entry(log.default_stack(), 'stg sync --undo')
         out.done()
         return
 
@@ -166,3 +167,5 @@ def func(parser, options, args):
     # push the remaining patches
     if popped:
         push_patches(crt_series, popped)
+
+    log.log_entry(log.default_stack(), 'stg sync')
diff --git a/stgit/lib/log.py b/stgit/lib/log.py
new file mode 100644
index 0000000..1a9a326
--- /dev/null
+++ b/stgit/lib/log.py
@@ -0,0 +1,60 @@
+from stgit.lib import git, stack
+
+def patch_tree(repository, cd):
+    return repository.commit(git.Treedata({ 'a': cd.parent.data.tree,
+                                            'b': cd.tree }))
+
+def order_blob(repository, stack, kind):
+    return repository.commit(git.Blobdata(''.join(
+                '%s\n' % pn for pn in getattr(stack.patchorder, kind))))
+
+def log_entry_tree(repository, stack):
+    patches = repository.commit(
+        git.Treedata(dict((pn, patch_tree(repository,
+                                          stack.patches.get(pn).commit.data))
+                          for pn in stack.patchorder.all)))
+    return repository.commit(git.Treedata({
+                'version': repository.commit(git.Blobdata('0')),
+                'patches': patches,
+                'applied': order_blob(repository, stack, 'applied'),
+                'unapplied': order_blob(repository, stack, 'unapplied'),
+                }))
+
+def log_ref(branch):
+    return 'refs/heads/%s.stgit' % branch
+
+def log_entry(stack, msg):
+    ref = log_ref(stack.name)
+    try:
+        last = [stack.repository.refs.get(ref)]
+    except KeyError:
+        last = []
+    commit = stack.repository.commit(
+        git.Commitdata(tree = log_entry_tree(stack.repository, stack),
+                       parents = last, message = msg))
+    stack.repository.refs.set(ref, commit, msg)
+
+def delete_log(repo, branch):
+    ref = log_ref(branch)
+    if repo.refs.exists(ref):
+        repo.refs.delete(ref)
+
+def rename_log(repo, old_branch, new_branch, msg):
+    old_ref = log_ref(old_branch)
+    new_ref = log_ref(new_branch)
+    if repo.refs.exists(old_ref):
+        repo.refs.set(new_ref, repo.refs.get(old_ref), msg)
+        repo.refs.delete(old_ref)
+
+def copy_log(repo, src_branch, dst_branch, msg):
+    src_ref = log_ref(src_branch)
+    dst_ref = log_ref(dst_branch)
+    if repo.refs.exists(src_ref):
+        repo.refs.set(dst_ref, repo.refs.get(src_ref), msg)
+
+def default_repo():
+    return stack.Repository.default()
+
+def default_stack(branch = None):
+    repo = default_repo()
+    return repo.get_stack(branch or repo.current_branch)
diff --git a/stgit/lib/transaction.py b/stgit/lib/transaction.py
index 3613b15..0b0b52d 100644
--- a/stgit/lib/transaction.py
+++ b/stgit/lib/transaction.py
@@ -1,6 +1,6 @@
 from stgit import exception, utils
 from stgit.out import *
-from stgit.lib import git
+from stgit.lib import git, log
 
 class TransactionException(exception.StgException):
     pass
@@ -117,6 +117,7 @@ class StackTransaction(object):
         _print_current_patch(self.__stack.patchorder.applied, self.__applied)
         self.__stack.patchorder.applied = self.__applied
         self.__stack.patchorder.unapplied = self.__unapplied
+        log.log_entry(self.__stack, self.__msg)
 
         if self.__error:
             return utils.STGIT_CONFLICT
Previous: Karl HasselströmNext: Catalin Marinas
Message 3 of 8 in “Patch stack logging”
  1. 0/2 Patch stack loggingKarl Hasselström, Feb 14, 2008
  2. 1/2 Library functions for tree and blob manipulationKarl Hasselström, Feb 14, 2008
  3. 2/2 Write to a stack log when stack is modifiedKarl Hasselström, Feb 14, 2008
  4. Catalin MarinasFeb 20, 2008
  5. Karl HasselströmFeb 21, 2008
  6. Catalin MarinasFeb 22, 2008
  7. Karl HasselströmFeb 22, 2008
  8. Catalin MarinasFeb 22, 2008

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.