From: "Karl Hasselström" <kha@treskal.com>
To: Catalin Marinas <catalin.marinas@gmail.com>
Cc: git@vger.kernel.org
Subject: [StGit PATCH 04/10] Write to a stack log when stack is modified
Date: Mon, 21 Apr 2008 00:10:44 +0200 [thread overview]
Message-ID: <20080420221044.5837.25817.stgit@yoghurt> (raw)
In-Reply-To: <20080420215625.5837.82896.stgit@yoghurt>
Create a log branch (called <branchname>.stgit) for each StGit branch,
and write to it whenever the stack is modified.
Commands using the new infrastructure write to the log when they
commit a transaction. Commands using the old infrastructure get a log
entry write written for them when they exit, unless they explicitly
ask for this not to happen.
As of yet, nothing can be done with this log.
Signed-off-by: Karl Hasselström <kha@treskal.com>
---
stgit/commands/branch.py | 20 +++++--
stgit/commands/common.py | 8 ++-
stgit/commands/diff.py | 2 -
stgit/commands/export.py | 2 -
stgit/commands/files.py | 2 -
stgit/commands/id.py | 2 -
stgit/commands/log.py | 2 -
stgit/commands/mail.py | 2 -
stgit/commands/patches.py | 2 -
stgit/commands/series.py | 2 -
stgit/commands/show.py | 2 -
stgit/commands/status.py | 3 +
stgit/commands/top.py | 2 -
stgit/lib/log.py | 131 +++++++++++++++++++++++++++++++++++++++++++++
stgit/lib/transaction.py | 3 +
stgit/main.py | 2 +
16 files changed, 168 insertions(+), 19 deletions(-)
create mode 100644 stgit/lib/log.py
diff --git a/stgit/commands/branch.py b/stgit/commands/branch.py
index 50684bb..241b1ef 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]
@@ -40,7 +40,7 @@ When displaying the branches, the names can be prefixed with
If not given any options, switch to the named branch."""
-directory = DirectoryGotoToplevel()
+directory = DirectoryGotoToplevel(log = False)
options = [make_option('-c', '--create',
help = 'create a new development branch',
action = 'store_true'),
@@ -161,6 +161,7 @@ def func(parser, options, args):
parent_branch = parentbranch)
out.info('Branch "%s" created' % args[0])
+ log.compat_log_entry('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/common.py b/stgit/commands/common.py
index d6df813..1a45d9e 100644
--- a/stgit/commands/common.py
+++ b/stgit/commands/common.py
@@ -28,6 +28,7 @@ from stgit.run import *
from stgit import stack, git, basedir
from stgit.config import config, file_extensions
from stgit.lib import stack as libstack
+from stgit.lib import log
# Command exception class
class CmdException(StgException):
@@ -478,8 +479,9 @@ class DirectoryException(StgException):
pass
class _Directory(object):
- def __init__(self, needs_current_series = True):
+ def __init__(self, needs_current_series = True, log = True):
self.needs_current_series = needs_current_series
+ self.log = log
@readonly_constant_property
def git_dir(self):
try:
@@ -512,6 +514,9 @@ class _Directory(object):
).output_one_line()]
def cd_to_topdir(self):
os.chdir(self.__topdir_path)
+ def write_log(self, msg):
+ if self.log:
+ log.compat_log_entry(msg)
class DirectoryAnywhere(_Directory):
def setup(self):
@@ -536,6 +541,7 @@ class DirectoryHasRepositoryLib(_Directory):
"""For commands that use the new infrastructure in stgit.lib.*."""
def __init__(self):
self.needs_current_series = False
+ self.log = False # stgit.lib.transaction handles logging
def setup(self):
# This will throw an exception if we don't have a repository.
self.repository = libstack.Repository.default()
diff --git a/stgit/commands/diff.py b/stgit/commands/diff.py
index fd6be34..8966642 100644
--- a/stgit/commands/diff.py
+++ b/stgit/commands/diff.py
@@ -42,7 +42,7 @@ rev = '([patch][//[bottom | top]]) | <tree-ish> | base'
If neither bottom nor top are given but a '//' is present, the command
shows the specified patch (defaulting to the current one)."""
-directory = DirectoryHasRepository()
+directory = DirectoryHasRepository(log = False)
options = [make_option('-r', '--range',
metavar = 'rev1[..[rev2]]', dest = 'revs',
help = 'show the diff between revisions'),
diff --git a/stgit/commands/export.py b/stgit/commands/export.py
index 50f6f67..552fd44 100644
--- a/stgit/commands/export.py
+++ b/stgit/commands/export.py
@@ -49,7 +49,7 @@ file:
%(commemail)s - committer's e-mail
"""
-directory = DirectoryHasRepository()
+directory = DirectoryHasRepository(log = False)
options = [make_option('-d', '--dir',
help = 'export patches to DIR instead of the default'),
make_option('-p', '--patch',
diff --git a/stgit/commands/files.py b/stgit/commands/files.py
index b43b12f..7844f8d 100644
--- a/stgit/commands/files.py
+++ b/stgit/commands/files.py
@@ -34,7 +34,7 @@ given patch. Note that this command doesn't show the files modified in
the working tree and not yet included in the patch by a 'refresh'
command. Use the 'diff' or 'status' commands for these files."""
-directory = DirectoryHasRepository()
+directory = DirectoryHasRepository(log = False)
options = [make_option('-s', '--stat',
help = 'show the diff stat',
action = 'store_true'),
diff --git a/stgit/commands/id.py b/stgit/commands/id.py
index 94b0229..5bb1ad2 100644
--- a/stgit/commands/id.py
+++ b/stgit/commands/id.py
@@ -33,7 +33,7 @@ the standard GIT id's like heads and tags, this command also accepts
'top' or 'bottom' are passed and <patch> is a valid patch name, 'top'
will be used by default."""
-directory = DirectoryHasRepository()
+directory = DirectoryHasRepository(log = False)
options = [make_option('-b', '--branch',
help = 'use BRANCH instead of the default one')]
diff --git a/stgit/commands/log.py b/stgit/commands/log.py
index 52d55a5..13e0baa 100644
--- a/stgit/commands/log.py
+++ b/stgit/commands/log.py
@@ -44,7 +44,7 @@ represent the changes to the entire base of the current
patch. Conflicts reset the patch content and a subsequent 'refresh'
will show the entire patch."""
-directory = DirectoryHasRepository()
+directory = DirectoryHasRepository(log = False)
options = [make_option('-b', '--branch',
help = 'use BRANCH instead of the default one'),
make_option('-p', '--patch',
diff --git a/stgit/commands/mail.py b/stgit/commands/mail.py
index b4d4e18..4027170 100644
--- a/stgit/commands/mail.py
+++ b/stgit/commands/mail.py
@@ -90,7 +90,7 @@ the following:
%(prefix)s - 'prefix ' string passed on the command line
%(shortdescr)s - the first line of the patch description"""
-directory = DirectoryHasRepository()
+directory = DirectoryHasRepository(log = False)
options = [make_option('-a', '--all',
help = 'e-mail all the applied patches',
action = 'store_true'),
diff --git a/stgit/commands/patches.py b/stgit/commands/patches.py
index 140699d..c95c40f 100644
--- a/stgit/commands/patches.py
+++ b/stgit/commands/patches.py
@@ -33,7 +33,7 @@ it shows the patches affected by the local tree modifications. The
'--diff' option also lists the patch log and the diff for the given
files."""
-directory = DirectoryHasRepository()
+directory = DirectoryHasRepository(log = False)
options = [make_option('-d', '--diff',
help = 'show the diff for the given files',
action = 'store_true'),
diff --git a/stgit/commands/series.py b/stgit/commands/series.py
index e3467cc..8d19dd7 100644
--- a/stgit/commands/series.py
+++ b/stgit/commands/series.py
@@ -34,7 +34,7 @@ range. The applied patches are prefixed with a '+', the unapplied ones
with a '-' and the hidden ones with a '!'. The current patch is
prefixed with a '>'. Empty patches are prefixed with a '0'."""
-directory = DirectoryHasRepository()
+directory = DirectoryHasRepository(log = False)
options = [make_option('-b', '--branch',
help = 'use BRANCH instead of the default one'),
make_option('-a', '--all',
diff --git a/stgit/commands/show.py b/stgit/commands/show.py
index b77a9c8..dd2a3a3 100644
--- a/stgit/commands/show.py
+++ b/stgit/commands/show.py
@@ -30,7 +30,7 @@ Show the commit log and the diff corresponding to the given
patches. The output is similar to that generated by the 'git show'
command."""
-directory = DirectoryHasRepository()
+directory = DirectoryHasRepository(log = False)
options = [make_option('-b', '--branch',
help = 'use BRANCH instead of the default one'),
make_option('-a', '--applied',
diff --git a/stgit/commands/status.py b/stgit/commands/status.py
index 6da4516..37b79c0 100644
--- a/stgit/commands/status.py
+++ b/stgit/commands/status.py
@@ -40,7 +40,7 @@ under revision control. The files are prefixed as follows:
A 'refresh' command clears the status of the modified, new and deleted
files."""
-directory = DirectoryHasRepository(needs_current_series = False)
+directory = DirectoryHasRepository(needs_current_series = False, log = False)
options = [make_option('-m', '--modified',
help = 'show modified files only',
action = 'store_true'),
@@ -106,6 +106,7 @@ def func(parser, options, args):
directory.cd_to_topdir()
if options.reset:
+ directory.log = True
if args:
conflicts = git.get_conflicts()
git.resolved(fn for fn in args if fn in conflicts)
diff --git a/stgit/commands/top.py b/stgit/commands/top.py
index e7cb275..96d680e 100644
--- a/stgit/commands/top.py
+++ b/stgit/commands/top.py
@@ -30,7 +30,7 @@ usage = """%prog [options]
Print the name of the current (topmost) patch."""
-directory = DirectoryHasRepository()
+directory = DirectoryHasRepository(log = False)
options = [make_option('-b', '--branch',
help = 'use BRANCH instead of the default one')]
diff --git a/stgit/lib/log.py b/stgit/lib/log.py
new file mode 100644
index 0000000..fac050f
--- /dev/null
+++ b/stgit/lib/log.py
@@ -0,0 +1,131 @@
+from stgit.lib import git, stack
+from stgit import exception
+from stgit.out import out
+
+class LogException(exception.StgException):
+ pass
+
+_current_version = 1
+
+def commit_info(cd):
+ return 'Author: %s\n\n%s' % (cd.author, cd.message)
+
+def patch_tree(repository, cd):
+ return repository.commit(git.Treedata({
+ 'a': cd.parent.data.tree, 'b': cd.tree,
+ 'info': repository.commit(git.Blobdata(commit_info(cd))) }))
+
+def order_blob(repository, stack, kind):
+ return repository.commit(git.Blobdata(''.join(
+ '%s %s\n' % (stack.patches.get(pn).commit.sha1, 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(
+ str(_current_version))),
+ 'head': repository.commit(git.Blobdata(str(stack.head.sha1))),
+ '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):
+ """Write a new log entry for the stack."""
+ ref = log_ref(stack.name)
+ try:
+ last_log = [FullLog(stack.repository, ref,
+ stack.repository.refs.get(ref))]
+ except KeyError:
+ last_log = []
+ except LogException, e:
+ out.warn(str(e), 'No log entry written.')
+ return
+ log_tree = log_entry_tree(stack.repository, stack)
+ stack_log = stack.repository.commit(
+ git.Commitdata(tree = log_tree, message = msg,
+ parents = [ll.stack_log for ll in last_log]))
+ full_log = stack.repository.commit(
+ git.Commitdata(tree = log_tree, message = msg,
+ parents = ([stack_log] + [stack.head]
+ + [ll.full_log for ll in last_log])))
+ stack.repository.refs.set(ref, full_log, msg)
+
+def compat_log_entry(msg):
+ repo = default_repo()
+ stack = repo.get_stack(repo.current_branch)
+ log_entry(stack, msg)
+
+class Log(object):
+ """Read a log entry."""
+ def __init__(self, repo, ref, commit):
+ self.commit = commit
+ mode, vblob = self.commit.data.tree.data.entries.get(
+ 'version', (None, None))
+ if not isinstance(vblob, git.Blob):
+ raise LogException('%s does not contain a valid log' % ref)
+ try:
+ version = int(vblob.data.str)
+ except ValueError:
+ raise LogException('"%s": invalid version number' % vblob.data.str)
+ if version < _current_version:
+ raise LogException(
+ '%s contains a stack log older than version %d;'
+ ' please delete it' % (ref, _current_version))
+ elif version > _current_version:
+ raise LogException(
+ 'Log contains a stack log newer than version %d;'
+ ' this version of StGit cannot read it' % _current_version)
+
+ # TODO: read the rest of the log lazily.
+
+ def pl(name):
+ patches = [x.split() for x in
+ self.commit.data.tree.data.entries[name][1]
+ .data.str.strip().split('\n') if x]
+ # TODO: handle case where we don't have the commit object.
+ return ([pn for sha1, pn in patches],
+ dict((pn, repo.get_commit(sha1)) for sha1, pn in patches))
+ self.patches = {}
+ self.applied, patches = pl('applied')
+ self.patches.update(patches)
+ self.unapplied, patches = pl('unapplied')
+ self.patches.update(patches)
+ self.head = repo.get_commit(
+ self.commit.data.tree.data.entries['head'][1].data.str)
+ if self.applied:
+ self.base = self.patches[self.applied[0]].data.parent
+ else:
+ self.base = self.head
+
+class FullLog(Log):
+ full_log = property(lambda self: self.commit)
+ stack_log = property(lambda self: self.commit.data.parents[0])
+
+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()
diff --git a/stgit/lib/transaction.py b/stgit/lib/transaction.py
index 874f81b..4fb37ce 100644
--- a/stgit/lib/transaction.py
+++ b/stgit/lib/transaction.py
@@ -1,7 +1,7 @@
from stgit import exception, utils
from stgit.utils import any, all
from stgit.out import *
-from stgit.lib import git
+from stgit.lib import git, log
class TransactionException(exception.StgException):
pass
@@ -131,6 +131,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
diff --git a/stgit/main.py b/stgit/main.py
index aa1f8ef..bd1a187 100644
--- a/stgit/main.py
+++ b/stgit/main.py
@@ -277,6 +277,7 @@ def main():
ret = command.func(parser, options, args)
except (StgException, IOError, ParsingError, NoSectionError), err:
+ directory.write_log('%s %s' % (prog, cmd))
out.error(str(err), title = '%s %s' % (prog, cmd))
if debug_level > 0:
traceback.print_exc()
@@ -292,4 +293,5 @@ def main():
traceback.print_exc()
sys.exit(utils.STGIT_BUG_ERROR)
+ directory.write_log('%s %s' % (prog, cmd))
sys.exit(ret or utils.STGIT_SUCCESS)
next prev parent reply other threads:[~2008-04-20 22:12 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-04-20 22:10 [StGit PATCH 00/10] Updated "stg reset" series Karl Hasselström
2008-04-20 22:10 ` [StGit PATCH 01/10] Prevent most commands from running when there are conflicts Karl Hasselström
2008-04-20 22:10 ` [StGit PATCH 02/10] Add property with a list of all patch names Karl Hasselström
2008-04-20 22:10 ` [StGit PATCH 03/10] Library functions for tree and blob manipulation Karl Hasselström
2008-04-20 22:10 ` Karl Hasselström [this message]
2008-04-20 22:10 ` [StGit PATCH 05/10] Add utility function for reordering patches Karl Hasselström
2008-04-20 22:10 ` [StGit PATCH 06/10] New command: stg reset Karl Hasselström
2008-04-20 22:11 ` [StGit PATCH 07/10] Log conflicts separately Karl Hasselström
2008-04-20 22:11 ` [StGit PATCH 08/10] Log conflicts separately for all commands Karl Hasselström
2008-04-20 22:11 ` [StGit PATCH 09/10] Add a --hard flag to stg reset Karl Hasselström
2008-04-20 22:11 ` [StGit PATCH 10/10] Don't write a log entry if there were no changes Karl Hasselström
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20080420221044.5837.25817.stgit@yoghurt \
--to=kha@treskal.com \
--cc=catalin.marinas@gmail.com \
--cc=git@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox