* kha/safe and kha/experimental updated @ 2008-01-29 2:58 Karl Hasselström 2008-01-29 3:02 ` [StGit PATCH 0/5] Various cleanups Karl Hasselström ` (2 more replies) 0 siblings, 3 replies; 25+ messages in thread From: Karl Hasselström @ 2008-01-29 2:58 UTC (permalink / raw) To: Catalin Marinas; +Cc: git I'll follow up with the three patch series in here that are new. The following changes since commit cd885e085a697d5377956d5beb30e6030f224ccd: Peter Oberndorfer (1): replace "git repo-config" usage by "git config" are available in the git repository at: git://repo.or.cz/stgit/kha.git safe Karl Hasselström (10): Don't keep old committer when rewriting a commit Homogenize buffer names Remove unused default values Refactor --diff-opts handling Create index and worktree objects just once Wrap excessively long line Eliminate temp variable that's used just once Simplify editor selection logic Let the caller supply the diff text to diffstat() Don't clean away patches with conflicts Pavel Roskin (1): Add test to ensure that "stg clean" preserves conflicting patches contrib/stgit.el | 6 +++--- stgit/commands/clean.py | 7 +++++++ stgit/commands/coalesce.py | 2 +- stgit/commands/commit.py | 2 +- stgit/commands/common.py | 3 ++- stgit/commands/diff.py | 19 ++++++------------- stgit/commands/edit.py | 16 ++++------------ stgit/commands/export.py | 20 +++++++------------- stgit/commands/files.py | 15 +++++---------- stgit/commands/goto.py | 2 +- stgit/commands/mail.py | 26 +++++++++----------------- stgit/commands/status.py | 17 +++++------------ stgit/git.py | 9 +++------ stgit/lib/git.py | 42 ++++++++++++++++++++++++++++++++---------- stgit/lib/transaction.py | 4 ++-- stgit/utils.py | 20 ++++++++++++++------ t/t2500-clean.sh | 17 +++++++++++++++++ 17 files changed, 119 insertions(+), 108 deletions(-) -+- The following changes since commit 149ad73c6b1639981b1064a9e8f3699b08928621: Karl Hasselström (1): Don't clean away patches with conflicts are available in the git repository at: git://repo.or.cz/stgit/kha.git experimental Karl Hasselström (6): Let "stg show" use the unified --diff-opts handling Read default diff options from the user's config Teach new infrastructure about the default author and committer Teach new infrastructure to apply patches Teach new infrastructure to diff two trees Convert "stg edit" to the new infrastructure Peter Oberndorfer (1): Add an --index option to "stg refresh" examples/gitconfig | 4 + stgit/commands/edit.py | 309 +++++++++++++++++++++------------------------ stgit/commands/refresh.py | 25 +++- stgit/commands/show.py | 13 +-- stgit/lib/git.py | 54 ++++++++ stgit/utils.py | 3 +- t/t2700-refresh.sh | 57 ++++++++- 7 files changed, 283 insertions(+), 182 deletions(-) -- Karl Hasselström, kha@treskal.com www.treskal.com/kalle ^ permalink raw reply [flat|nested] 25+ messages in thread
* [StGit PATCH 0/5] Various cleanups 2008-01-29 2:58 kha/safe and kha/experimental updated Karl Hasselström @ 2008-01-29 3:02 ` Karl Hasselström 2008-01-29 3:02 ` [StGit PATCH 1/5] Create index and worktree objects just once Karl Hasselström ` (4 more replies) 2008-01-29 3:10 ` [StGit PATCH 0/2] "stg clean" test+bugfix Karl Hasselström 2008-01-29 3:15 ` [StGit PATCH 0/4] Rewrite "stg edit" to use new infrastructure Karl Hasselström 2 siblings, 5 replies; 25+ messages in thread From: Karl Hasselström @ 2008-01-29 3:02 UTC (permalink / raw) To: Catalin Marinas; +Cc: git Some of these are very tiny, others are a bit bigger. They're all in kha/safe, since they should be perfectly safe. --- Karl Hasselström (5): Let the caller supply the diff text to diffstat() Simplify editor selection logic Eliminate temp variable that's used just once Wrap excessively long line Create index and worktree objects just once stgit/commands/coalesce.py | 2 +- stgit/commands/commit.py | 2 +- stgit/commands/common.py | 3 ++- stgit/commands/diff.py | 9 ++++----- stgit/commands/edit.py | 2 +- stgit/commands/export.py | 10 +++++----- stgit/commands/files.py | 2 +- stgit/commands/goto.py | 2 +- stgit/commands/mail.py | 16 +++++++--------- stgit/git.py | 9 +++------ stgit/lib/git.py | 34 ++++++++++++++++++++++++---------- stgit/lib/transaction.py | 3 +-- stgit/utils.py | 8 ++------ 13 files changed, 53 insertions(+), 49 deletions(-) -- Karl Hasselström, kha@treskal.com www.treskal.com/kalle ^ permalink raw reply [flat|nested] 25+ messages in thread
* [StGit PATCH 1/5] Create index and worktree objects just once 2008-01-29 3:02 ` [StGit PATCH 0/5] Various cleanups Karl Hasselström @ 2008-01-29 3:02 ` Karl Hasselström 2008-01-29 3:03 ` [StGit PATCH 2/5] Wrap excessively long line Karl Hasselström ` (3 subsequent siblings) 4 siblings, 0 replies; 25+ messages in thread From: Karl Hasselström @ 2008-01-29 3:02 UTC (permalink / raw) To: Catalin Marinas; +Cc: git Create the objects for a repository's default index, worktree, and index+worktree just once. Both for performance (though the gain is probably negligible), and for future-proofing if we ever add mutable state to those objects. And make them properties while we're at it. Signed-off-by: Karl Hasselström <kha@treskal.com> --- stgit/commands/coalesce.py | 2 +- stgit/commands/commit.py | 2 +- stgit/commands/goto.py | 2 +- stgit/lib/git.py | 34 ++++++++++++++++++++++++---------- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/stgit/commands/coalesce.py b/stgit/commands/coalesce.py index d2cba3e..291a537 100644 --- a/stgit/commands/coalesce.py +++ b/stgit/commands/coalesce.py @@ -118,5 +118,5 @@ def func(parser, options, args): + list(stack.patchorder.unapplied))) if len(patches) < 2: raise common.CmdException('Need at least two patches') - return _coalesce(stack, stack.repository.default_iw(), options.name, + return _coalesce(stack, stack.repository.default_iw, options.name, options.message, options.save_template, patches) diff --git a/stgit/commands/commit.py b/stgit/commands/commit.py index 1d741b3..bff94ce 100644 --- a/stgit/commands/commit.py +++ b/stgit/commands/commit.py @@ -68,7 +68,7 @@ def func(parser, options, args): if not patches: raise common.CmdException('No patches to commit') - iw = stack.repository.default_iw() + iw = stack.repository.default_iw trans = transaction.StackTransaction(stack, 'stg commit') try: common_prefix = 0 diff --git a/stgit/commands/goto.py b/stgit/commands/goto.py index 763a8af..fe13e49 100644 --- a/stgit/commands/goto.py +++ b/stgit/commands/goto.py @@ -35,7 +35,7 @@ def func(parser, options, args): patch = args[0] stack = directory.repository.current_stack - iw = stack.repository.default_iw() + iw = stack.repository.default_iw trans = transaction.StackTransaction(stack, 'stg goto') if patch in trans.applied: to_pop = set(trans.applied[trans.applied.index(patch)+1:]) diff --git a/stgit/lib/git.py b/stgit/lib/git.py index 118c9b2..2af1844 100644 --- a/stgit/lib/git.py +++ b/stgit/lib/git.py @@ -211,6 +211,9 @@ class Repository(RunWithEnv): self.__refs = Refs(self) self.__trees = ObjectCache(lambda sha1: Tree(sha1)) self.__commits = ObjectCache(lambda sha1: Commit(self, sha1)) + self.__default_index = None + self.__default_worktree = None + self.__default_iw = None env = property(lambda self: { 'GIT_DIR': self.__git_dir }) @classmethod def default(cls): @@ -220,21 +223,32 @@ class Repository(RunWithEnv): ).output_one_line()) except run.RunException: raise RepositoryException('Cannot find git repository') + @property def default_index(self): - return Index(self, (os.environ.get('GIT_INDEX_FILE', None) - or os.path.join(self.__git_dir, 'index'))) + if self.__default_index == None: + self.__default_index = Index( + self, (os.environ.get('GIT_INDEX_FILE', None) + or os.path.join(self.__git_dir, 'index'))) + return self.__default_index def temp_index(self): return Index(self, self.__git_dir) + @property def default_worktree(self): - path = os.environ.get('GIT_WORK_TREE', None) - if not path: - o = run.Run('git', 'rev-parse', '--show-cdup').output_lines() - o = o or ['.'] - assert len(o) == 1 - path = o[0] - return Worktree(path) + if self.__default_worktree == None: + path = os.environ.get('GIT_WORK_TREE', None) + if not path: + o = run.Run('git', 'rev-parse', '--show-cdup').output_lines() + o = o or ['.'] + assert len(o) == 1 + path = o[0] + self.__default_worktree = Worktree(path) + return self.__default_worktree + @property def default_iw(self): - return IndexAndWorktree(self.default_index(), self.default_worktree()) + if self.__default_iw == None: + self.__default_iw = IndexAndWorktree(self.default_index, + self.default_worktree) + return self.__default_iw directory = property(lambda self: self.__git_dir) refs = property(lambda self: self.__refs) def cat_object(self, sha1): ^ permalink raw reply related [flat|nested] 25+ messages in thread
* [StGit PATCH 2/5] Wrap excessively long line 2008-01-29 3:02 ` [StGit PATCH 0/5] Various cleanups Karl Hasselström 2008-01-29 3:02 ` [StGit PATCH 1/5] Create index and worktree objects just once Karl Hasselström @ 2008-01-29 3:03 ` Karl Hasselström 2008-01-29 3:03 ` [StGit PATCH 3/5] Eliminate temp variable that's used just once Karl Hasselström ` (2 subsequent siblings) 4 siblings, 0 replies; 25+ messages in thread From: Karl Hasselström @ 2008-01-29 3:03 UTC (permalink / raw) To: Catalin Marinas; +Cc: git Signed-off-by: Karl Hasselström <kha@treskal.com> --- I try to kill those whenever I can get away with it ... stgit/commands/common.py | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/stgit/commands/common.py b/stgit/commands/common.py index 7d9df02..7a7cb80 100644 --- a/stgit/commands/common.py +++ b/stgit/commands/common.py @@ -274,7 +274,8 @@ def name_email(address): if not str_list: str_list = re.findall('^(.*)\s*\((.*)\)\s*$', address) if not str_list: - raise CmdException, 'Incorrect "name <email>"/"email (name)" string: %s' % address + raise CmdException('Incorrect "name <email>"/"email (name)"' + ' string: %s' % address) return ( str_list[0][1], str_list[0][0] ) return str_list[0] ^ permalink raw reply related [flat|nested] 25+ messages in thread
* [StGit PATCH 3/5] Eliminate temp variable that's used just once 2008-01-29 3:02 ` [StGit PATCH 0/5] Various cleanups Karl Hasselström 2008-01-29 3:02 ` [StGit PATCH 1/5] Create index and worktree objects just once Karl Hasselström 2008-01-29 3:03 ` [StGit PATCH 2/5] Wrap excessively long line Karl Hasselström @ 2008-01-29 3:03 ` Karl Hasselström 2008-01-29 3:04 ` [StGit PATCH 4/5] Simplify editor selection logic Karl Hasselström 2008-01-29 3:06 ` [StGit PATCH 5/5] Let the caller supply the diff text to diffstat() Karl Hasselström 4 siblings, 0 replies; 25+ messages in thread From: Karl Hasselström @ 2008-01-29 3:03 UTC (permalink / raw) To: Catalin Marinas; +Cc: git Signed-off-by: Karl Hasselström <kha@treskal.com> --- stgit/lib/transaction.py | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/stgit/lib/transaction.py b/stgit/lib/transaction.py index 809eabf..6a2ed81 100644 --- a/stgit/lib/transaction.py +++ b/stgit/lib/transaction.py @@ -174,7 +174,6 @@ class StackTransaction(object): """Attempt to push the named patch. If this results in conflicts, halts the transaction. If index+worktree are given, spill any conflicts to them.""" - i = self.unapplied.index(pn) cd = self.patches[pn].data cd = cd.set_committer(None) s = ['', ' (empty)'][cd.is_nochange()] @@ -203,7 +202,7 @@ class StackTransaction(object): s = ' (conflict)' cd = cd.set_tree(tree) self.patches[pn] = self.__stack.repository.commit(cd) - del self.unapplied[i] + del self.unapplied[self.unapplied.index(pn)] self.applied.append(pn) out.info('Pushed %s%s' % (pn, s)) if merge_conflict: ^ permalink raw reply related [flat|nested] 25+ messages in thread
* [StGit PATCH 4/5] Simplify editor selection logic 2008-01-29 3:02 ` [StGit PATCH 0/5] Various cleanups Karl Hasselström ` (2 preceding siblings ...) 2008-01-29 3:03 ` [StGit PATCH 3/5] Eliminate temp variable that's used just once Karl Hasselström @ 2008-01-29 3:04 ` Karl Hasselström 2008-01-29 20:09 ` Peter Oberndorfer 2008-01-29 3:06 ` [StGit PATCH 5/5] Let the caller supply the diff text to diffstat() Karl Hasselström 4 siblings, 1 reply; 25+ messages in thread From: Karl Hasselström @ 2008-01-29 3:04 UTC (permalink / raw) To: Catalin Marinas; +Cc: git Signed-off-by: Karl Hasselström <kha@treskal.com> --- There are a few other env variables we might like to look at, like VISUAL. And isn't there a git-specific variable too? stgit/utils.py | 8 ++------ 1 files changed, 2 insertions(+), 6 deletions(-) diff --git a/stgit/utils.py b/stgit/utils.py index 00776b0..43366c9 100644 --- a/stgit/utils.py +++ b/stgit/utils.py @@ -175,12 +175,8 @@ def call_editor(filename): # the editor editor = config.get('stgit.editor') - if editor: - pass - elif 'EDITOR' in os.environ: - editor = os.environ['EDITOR'] - else: - editor = 'vi' + if not editor: + editor = os.environ.get('EDITOR', 'vi') editor += ' %s' % filename out.start('Invoking the editor: "%s"' % editor) ^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [StGit PATCH 4/5] Simplify editor selection logic 2008-01-29 3:04 ` [StGit PATCH 4/5] Simplify editor selection logic Karl Hasselström @ 2008-01-29 20:09 ` Peter Oberndorfer 2008-01-30 7:28 ` Karl Hasselström 0 siblings, 1 reply; 25+ messages in thread From: Peter Oberndorfer @ 2008-01-29 20:09 UTC (permalink / raw) To: Karl Hasselström; +Cc: Catalin Marinas, git Subject: [PATCH] use the same search order for picking a editor as git Signed-off-by: Peter Oberndorfer <kumbayo84@arcor.de> --- On Dienstag 29 Januar 2008, Karl Hasselström wrote: > Signed-off-by: Karl Hasselström <kha@treskal.com> > > --- > > There are a few other env variables we might like to look at, like > VISUAL. And isn't there a git-specific variable too? > > stgit/utils.py | 8 ++------ > 1 files changed, 2 insertions(+), 6 deletions(-) > > > diff --git a/stgit/utils.py b/stgit/utils.py > index 00776b0..43366c9 100644 > --- a/stgit/utils.py > +++ b/stgit/utils.py > @@ -175,12 +175,8 @@ def call_editor(filename): > > # the editor > editor = config.get('stgit.editor') > - if editor: > - pass > - elif 'EDITOR' in os.environ: > - editor = os.environ['EDITOR'] > - else: > - editor = 'vi' > + if not editor: > + editor = os.environ.get('EDITOR', 'vi') > editor += ' %s' % filename > > out.start('Invoking the editor: "%s"' % editor) Since i personally dislike having a separate config for the editor in git/stgit i locally use this patch. unfortunately it makes the whole editor searching thing more complex :-( But i am sure it is possible to rewrite the code to something easier with some more python knowledge :-/ So it is not meant for direct applying, just for discussion... TODO: update sample gitconfig? TODO: do the same for pager? Documentation/stg-new.txt | 6 ++++++ stgit/utils.py | 10 +++++++++- 2 files changed, 15 insertions(+), 1 deletions(-) diff --git a/Documentation/stg-new.txt b/Documentation/stg-new.txt index fbf2f67..a728d8e 100644 --- a/Documentation/stg-new.txt +++ b/Documentation/stg-new.txt @@ -31,7 +31,10 @@ the patch, unless the '--message' flag already specified one. The editor. The editor to use is taken from the first of the following sources of information, and defaults to 'vi': +. the 'GIT_EDITOR' environment variable . the 'stgit.editor' GIT configuration variable +. the 'core.editor' GIT configuration variable +. the 'VISUAL' environment variable . the 'EDITOR' environment variable The message and other GIT commit attributes can be modified later @@ -101,13 +104,16 @@ ENVIRONMENT VARIABLES GIT_AUTHOR_DATE GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL + GIT_EDITOR EDITOR + VISUAL GIT CONFIGURATION VARIABLES --------------------------- user.name user.email + core.editor stgit.editor StGIT diff --git a/stgit/utils.py b/stgit/utils.py index 2ff1d74..6b1d196 100644 --- a/stgit/utils.py +++ b/stgit/utils.py @@ -174,9 +174,17 @@ def call_editor(filename): """Run the editor on the specified filename.""" # the editor - editor = config.get('stgit.editor') + editor = None + if 'GIT_EDITOR' in os.environ: + editor = os.environ['GIT_EDITOR'] + if not editor: + editor = config.get('stgit.editor') + if not editor: + editor = config.get('core.editor') if editor: pass + elif 'VISUAL' in os.environ: + editor = os.environ['VISUAL'] elif 'EDITOR' in os.environ: editor = os.environ['EDITOR'] else: -- 1.5.4.rc3 ^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [StGit PATCH 4/5] Simplify editor selection logic 2008-01-29 20:09 ` Peter Oberndorfer @ 2008-01-30 7:28 ` Karl Hasselström 2008-01-30 14:55 ` Jay Soffian 0 siblings, 1 reply; 25+ messages in thread From: Karl Hasselström @ 2008-01-30 7:28 UTC (permalink / raw) To: Peter Oberndorfer; +Cc: Catalin Marinas, git On 2008-01-29 21:09:37 +0100, Peter Oberndorfer wrote: > Since i personally dislike having a separate config for the editor > in git/stgit i locally use this patch. unfortunately it makes the > whole editor searching thing more complex :-( But i am sure it is > possible to rewrite the code to something easier with some more > python knowledge :-/ So it is not meant for direct applying, just > for discussion... I like the intention. What I'd like to do is to let the code _use_ the stgit.editor variable, but not advertise it in the docs except for noting that "this is deprecated and will go away in the future, but for now it's still effective". > +. the 'GIT_EDITOR' environment variable > . the 'stgit.editor' GIT configuration variable > +. the 'core.editor' GIT configuration variable > +. the 'VISUAL' environment variable > . the 'EDITOR' environment variable That's a very nice order. (For completeness, the list should end with "vi", though. And I'd like that deprecation note for stgit.editor, unless someone has strong objections.) > # the editor > - editor = config.get('stgit.editor') > + editor = None > + if 'GIT_EDITOR' in os.environ: > + editor = os.environ['GIT_EDITOR'] > + if not editor: > + editor = config.get('stgit.editor') > + if not editor: > + editor = config.get('core.editor') > if editor: > pass > + elif 'VISUAL' in os.environ: > + editor = os.environ['VISUAL'] > elif 'EDITOR' in os.environ: > editor = os.environ['EDITOR'] > else: You could write it kind of like this: def e(key): return os.environ.get(key, None) def c(key): return config.get(key) editor = filter(None, [e('GIT_EDITOR'), c('stgit.editor'), c('core.editor'), e('VISUAL'), e('EDITOR'), 'vi'])[0] Of course, if we're going to have code like this in several places (you already mentioned the pager), we could build a function like this: editor = get_config(['GIT_EDITOR', 'stgit.editor', 'core.editor', 'VISUAL', 'EDITOR'], default = 'vi') that would differentiate between env variables and conf keys by looking for dots in the name or something. -- Karl Hasselström, kha@treskal.com www.treskal.com/kalle ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [StGit PATCH 4/5] Simplify editor selection logic 2008-01-30 7:28 ` Karl Hasselström @ 2008-01-30 14:55 ` Jay Soffian 2008-01-30 17:57 ` Karl Hasselström 0 siblings, 1 reply; 25+ messages in thread From: Jay Soffian @ 2008-01-30 14:55 UTC (permalink / raw) To: Karl Hasselström; +Cc: Peter Oberndorfer, Catalin Marinas, git On Jan 30, 2008 2:28 AM, Karl Hasselström <kha@treskal.com> wrote: > You could write it kind of like this: > > def e(key): return os.environ.get(key, None) > def c(key): return config.get(key) > editor = filter(None, [e('GIT_EDITOR'), c('stgit.editor'), c('core.editor'), > e('VISUAL'), e('EDITOR'), 'vi'])[0] Too clever by half if you ask me. Why not just: editor = (os.environ.get('GIT_EDITOR') or config.get('stgit.editor') or config.get('core.editor') or os.environ.get('VISUAL') or os.environ.get('EDITOR') or 'vi') And be done with it? > Of course, if we're going to have code like this in several places > (you already mentioned the pager), we could build a function like > this: > > editor = get_config(['GIT_EDITOR', 'stgit.editor', 'core.editor', > 'VISUAL', 'EDITOR'], default = 'vi') > > that would differentiate between env variables and conf keys by > looking for dots in the name or something. def get_config(keys, default=None): rv = default for k in keys: if '.' in k: d = config else: d = os.environ if k in d: rv = d[k] break return rv j. ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [StGit PATCH 4/5] Simplify editor selection logic 2008-01-30 14:55 ` Jay Soffian @ 2008-01-30 17:57 ` Karl Hasselström 0 siblings, 0 replies; 25+ messages in thread From: Karl Hasselström @ 2008-01-30 17:57 UTC (permalink / raw) To: Jay Soffian; +Cc: Peter Oberndorfer, Catalin Marinas, git On 2008-01-30 09:55:18 -0500, Jay Soffian wrote: > On Jan 30, 2008 2:28 AM, Karl Hasselström <kha@treskal.com> wrote: > > You could write it kind of like this: > > > > def e(key): return os.environ.get(key, None) > > def c(key): return config.get(key) > > editor = filter(None, [e('GIT_EDITOR'), c('stgit.editor'), c('core.editor'), > > e('VISUAL'), e('EDITOR'), 'vi'])[0] > > Too clever by half if you ask me. Why not just: > > editor = (os.environ.get('GIT_EDITOR') or > config.get('stgit.editor') or > config.get('core.editor') or > os.environ.get('VISUAL') or > os.environ.get('EDITOR') or > 'vi') > > And be done with it? Yes. It's more repetitive, but not much longer. With only five options and one default -- if there were more, my version would be nicer (IMHO). > > Of course, if we're going to have code like this in several places > > (you already mentioned the pager), we could build a function like > > this: > > > > editor = get_config(['GIT_EDITOR', 'stgit.editor', 'core.editor', > > 'VISUAL', 'EDITOR'], default = 'vi') > > > > that would differentiate between env variables and conf keys by > > looking for dots in the name or something. > > def get_config(keys, default=None): > rv = default > for k in keys: > if '.' in k: > d = config > else: > d = os.environ > if k in d: > rv = d[k] > break > return rv 'config' isn't a dict, so you have to use config.get, and you can't use 'k in config'. But otherwise yes. So something like this maybe: def get_config(keys, default = None): rv = None for k in keys: if '.' in k: rv = config.get(k) else: rv = os.environ.get(k, None) if rv != None: return rv return default -- Karl Hasselström, kha@treskal.com www.treskal.com/kalle ^ permalink raw reply [flat|nested] 25+ messages in thread
* [StGit PATCH 5/5] Let the caller supply the diff text to diffstat() 2008-01-29 3:02 ` [StGit PATCH 0/5] Various cleanups Karl Hasselström ` (3 preceding siblings ...) 2008-01-29 3:04 ` [StGit PATCH 4/5] Simplify editor selection logic Karl Hasselström @ 2008-01-29 3:06 ` Karl Hasselström 4 siblings, 0 replies; 25+ messages in thread From: Karl Hasselström @ 2008-01-29 3:06 UTC (permalink / raw) To: Catalin Marinas; +Cc: git Almost all diffstat() callers already have the diff text, so they might as well pass it to diffstat() instead of letting it recompute it. In some cases this even makes for a code simplification since the diff (and thus diffstat) parameters were nontrivial. Also, diffstat() wasn't as versatile as diff(); for example, it didn't accept any extra diff options. This patch solves all of those problems as a side-effect. Signed-off-by: Karl Hasselström <kha@treskal.com> --- I wanted this for the "stg edit" rewrite. It took some time before I saw the TODO comment describing exactly the change I was in the process of making! stgit/commands/diff.py | 9 ++++----- stgit/commands/edit.py | 2 +- stgit/commands/export.py | 10 +++++----- stgit/commands/files.py | 2 +- stgit/commands/mail.py | 16 +++++++--------- stgit/git.py | 9 +++------ 6 files changed, 21 insertions(+), 27 deletions(-) diff --git a/stgit/commands/diff.py b/stgit/commands/diff.py index 7c213d1..fd6be34 100644 --- a/stgit/commands/diff.py +++ b/stgit/commands/diff.py @@ -81,12 +81,11 @@ def func(parser, options, args): rev1 = 'HEAD' rev2 = None + diff_str = git.diff(args, git_id(crt_series, rev1), + git_id(crt_series, rev2), + diff_flags = options.diff_flags) if options.stat: - out.stdout_raw(git.diffstat(args, git_id(crt_series, rev1), - git_id(crt_series, rev2)) + '\n') + out.stdout_raw(git.diffstat(diff_str) + '\n') else: - diff_str = git.diff(args, git_id(crt_series, rev1), - git_id(crt_series, rev2), - diff_flags = options.diff_flags) if diff_str: pager(diff_str) diff --git a/stgit/commands/edit.py b/stgit/commands/edit.py index da67275..9915e49 100644 --- a/stgit/commands/edit.py +++ b/stgit/commands/edit.py @@ -154,9 +154,9 @@ def __generate_file(pname, write_fn, options): '%(diffstat)s\n' \ '%(diff)s' - tmpl_dict['diffstat'] = git.diffstat(rev1 = bottom, rev2 = top) tmpl_dict['diff'] = git.diff(rev1 = bottom, rev2 = top, diff_flags = options.diff_flags) + tmpl_dict['diffstat'] = git.diffstat(tmpl_dict['diff']) for key in tmpl_dict: # make empty strings if key is not available diff --git a/stgit/commands/export.py b/stgit/commands/export.py index 16c64ba..50f6f67 100644 --- a/stgit/commands/export.py +++ b/stgit/commands/export.py @@ -138,11 +138,13 @@ def func(parser, options, args): long_descr = reduce(lambda x, y: x + '\n' + y, descr_lines[1:], '').strip() + diff = git.diff(rev1 = patch.get_bottom(), + rev2 = patch.get_top(), + diff_flags = options.diff_flags) tmpl_dict = {'description': patch.get_description().rstrip(), 'shortdescr': short_descr, 'longdescr': long_descr, - 'diffstat': git.diffstat(rev1 = patch.get_bottom(), - rev2 = patch.get_top()), + 'diffstat': git.diffstat(diff), 'authname': patch.get_authname(), 'authemail': patch.get_authemail(), 'authdate': patch.get_authdate(), @@ -172,9 +174,7 @@ def func(parser, options, args): print '-'*79 f.write(descr) - f.write(git.diff(rev1 = patch.get_bottom(), - rev2 = patch.get_top(), - diff_flags = options.diff_flags)) + f.write(diff) if not options.stdout: f.close() patch_no += 1 diff --git a/stgit/commands/files.py b/stgit/commands/files.py index ab1f6a3..b43b12f 100644 --- a/stgit/commands/files.py +++ b/stgit/commands/files.py @@ -60,7 +60,7 @@ def func(parser, options, args): rev2 = git_id(crt_series, '%s//top' % patch) if options.stat: - out.stdout_raw(git.diffstat(rev1 = rev1, rev2 = rev2) + '\n') + out.stdout_raw(git.diffstat(git.diff(rev1 = rev1, rev2 = rev2)) + '\n') elif options.bare: out.stdout_raw(git.barefiles(rev1, rev2) + '\n') else: diff --git a/stgit/commands/mail.py b/stgit/commands/mail.py index 4aa16fb..7d19eca 100644 --- a/stgit/commands/mail.py +++ b/stgit/commands/mail.py @@ -360,9 +360,9 @@ def __build_cover(tmpl, patches, msg_id, options): 'number': number_str, 'shortlog': stack.shortlog(crt_series.get_patch(p) for p in patches), - 'diffstat': git.diffstat( + 'diffstat': git.diffstat(git.diff( rev1 = git_id(crt_series, '%s//bottom' % patches[0]), - rev2 = git_id(crt_series, '%s//top' % patches[-1]))} + rev2 = git_id(crt_series, '%s//top' % patches[-1])))} try: msg_string = tmpl % tmpl_dict @@ -433,6 +433,9 @@ def __build_message(tmpl, patch, patch_nr, total_nr, msg_id, ref_id, options): else: number_str = '' + diff = git.diff(rev1 = git_id(crt_series, '%s//bottom' % patch), + rev2 = git_id(crt_series, '%s//top' % patch), + diff_flags = options.diff_flags) tmpl_dict = {'patch': patch, 'sender': sender, # for backward template compatibility @@ -441,13 +444,8 @@ def __build_message(tmpl, patch, patch_nr, total_nr, msg_id, ref_id, options): 'longdescr': long_descr, # for backward template compatibility 'endofheaders': '', - 'diff': git.diff( - rev1 = git_id(crt_series, '%s//bottom' % patch), - rev2 = git_id(crt_series, '%s//top' % patch), - diff_flags = options.diff_flags), - 'diffstat': git.diffstat( - rev1 = git_id(crt_series, '%s//bottom'%patch), - rev2 = git_id(crt_series, '%s//top' % patch)), + 'diff': diff, + 'diffstat': git.diffstat(diff), # for backward template compatibility 'date': '', 'version': version_str, diff --git a/stgit/git.py b/stgit/git.py index 85cceb0..4dc4dcf 100644 --- a/stgit/git.py +++ b/stgit/git.py @@ -640,12 +640,9 @@ def diff(files = None, rev1 = 'HEAD', rev2 = None, diff_flags = [], else: return '' -# TODO: take another parameter representing a diff string as we -# usually invoke git.diff() form the calling functions -def diffstat(files = None, rev1 = 'HEAD', rev2 = None): - """Return the diffstat between rev1 and rev2.""" - return GRun('apply', '--stat', '--summary' - ).raw_input(diff(files, rev1, rev2)).raw_output() +def diffstat(diff): + """Return the diffstat of the supplied diff.""" + return GRun('apply', '--stat', '--summary').raw_input(diff).raw_output() def files(rev1, rev2, diff_flags = []): """Return the files modified between rev1 and rev2 ^ permalink raw reply related [flat|nested] 25+ messages in thread
* [StGit PATCH 0/2] "stg clean" test+bugfix 2008-01-29 2:58 kha/safe and kha/experimental updated Karl Hasselström 2008-01-29 3:02 ` [StGit PATCH 0/5] Various cleanups Karl Hasselström @ 2008-01-29 3:10 ` Karl Hasselström 2008-01-29 3:11 ` [StGit PATCH 1/2] Add test to ensure that "stg clean" preserves conflicting patches Karl Hasselström 2008-01-29 3:12 ` [StGit PATCH 2/2] Don't clean away patches with conflicts Karl Hasselström 2008-01-29 3:15 ` [StGit PATCH 0/4] Rewrite "stg edit" to use new infrastructure Karl Hasselström 2 siblings, 2 replies; 25+ messages in thread From: Karl Hasselström @ 2008-01-29 3:10 UTC (permalink / raw) To: Catalin Marinas; +Cc: git This is the test by Pavel, followed by a fix by me. They're in kha/safe, since they should be low-risk, and because losing a conflicting patch (even though you still have all the changes in your worktree) is unpleasant. --- Karl Hasselström (1): Don't clean away patches with conflicts Pavel Roskin (1): Add test to ensure that "stg clean" preserves conflicting patches stgit/commands/clean.py | 7 +++++++ stgit/lib/git.py | 8 ++++++++ t/t2500-clean.sh | 17 +++++++++++++++++ 3 files changed, 32 insertions(+), 0 deletions(-) -- Karl Hasselström, kha@treskal.com www.treskal.com/kalle ^ permalink raw reply [flat|nested] 25+ messages in thread
* [StGit PATCH 1/2] Add test to ensure that "stg clean" preserves conflicting patches 2008-01-29 3:10 ` [StGit PATCH 0/2] "stg clean" test+bugfix Karl Hasselström @ 2008-01-29 3:11 ` Karl Hasselström 2008-01-29 3:12 ` [StGit PATCH 2/2] Don't clean away patches with conflicts Karl Hasselström 1 sibling, 0 replies; 25+ messages in thread From: Karl Hasselström @ 2008-01-29 3:11 UTC (permalink / raw) To: Catalin Marinas; +Cc: git From: Pavel Roskin <proski@gnu.org> If "stg push" fails, the subsequent "stg clean" will remove the patch that could not be applied. I think it's wrong. Especially when doing "stg pull", it can happen that I want to run "stg clean" to get rid of the patches applied upstream so I can concentrate on the conflict. Instead, the conflicting patch is removed too. The test added by this patch should pass once the bug is fixed. Signed-off-by: Pavel Roskin <proski@gnu.org> Signed-off-by: Karl Hasselström <kha@treskal.com> --- I doctored the commit message a bit, and made the failing test use test_expect_failure. t/t2500-clean.sh | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-) diff --git a/t/t2500-clean.sh b/t/t2500-clean.sh index 3364c18..b38d868 100755 --- a/t/t2500-clean.sh +++ b/t/t2500-clean.sh @@ -24,4 +24,21 @@ test_expect_success 'Clean empty patches' ' [ "$(echo $(stg unapplied))" = "" ] ' +test_expect_success 'Create a conflict' ' + stg new p1 -m p1 && + echo bar > foo.txt && + stg refresh && + stg pop && + stg new p2 -m p2 + echo quux > foo.txt && + stg refresh && + ! stg push +' + +test_expect_failure 'Make sure conflicting patches are preserved' ' + stg clean && + [ "$(echo $(stg applied))" = "p0 p2 p1" ] && + [ "$(echo $(stg unapplied))" = "" ] +' + test_done ^ permalink raw reply related [flat|nested] 25+ messages in thread
* [StGit PATCH 2/2] Don't clean away patches with conflicts 2008-01-29 3:10 ` [StGit PATCH 0/2] "stg clean" test+bugfix Karl Hasselström 2008-01-29 3:11 ` [StGit PATCH 1/2] Add test to ensure that "stg clean" preserves conflicting patches Karl Hasselström @ 2008-01-29 3:12 ` Karl Hasselström 1 sibling, 0 replies; 25+ messages in thread From: Karl Hasselström @ 2008-01-29 3:12 UTC (permalink / raw) To: Catalin Marinas; +Cc: git If we have conflicts, it means that the topmost patch is empty because of those conflicts (since StGit explicitly makes a conflicting patch empty), so don't let "stg clean" touch it. Signed-off-by: Karl Hasselström <kha@treskal.com> --- I considered fixing this by way of a check in the shared new-infrastructure code, but came to the conclusion that the check really does belong in "stg clean". stgit/commands/clean.py | 7 +++++++ stgit/lib/git.py | 8 ++++++++ t/t2500-clean.sh | 2 +- 3 files changed, 16 insertions(+), 1 deletions(-) diff --git a/stgit/commands/clean.py b/stgit/commands/clean.py index a0a0dca..889c1dc 100644 --- a/stgit/commands/clean.py +++ b/stgit/commands/clean.py @@ -40,6 +40,13 @@ def _clean(stack, clean_applied, clean_unapplied): trans = transaction.StackTransaction(stack, 'stg clean') def del_patch(pn): if pn in stack.patchorder.applied: + if pn == stack.patchorder.applied[-1]: + # We're about to clean away the topmost patch. Don't + # do that if we have conflicts, since that means the + # patch is only empty because the conflicts have made + # us dump its contents into the index and worktree. + if stack.repository.default_index.conflicts(): + return False return clean_applied and trans.patches[pn].data.is_nochange() elif pn in stack.patchorder.unapplied: return clean_unapplied and trans.patches[pn].data.is_nochange() diff --git a/stgit/lib/git.py b/stgit/lib/git.py index 2af1844..6cd7450 100644 --- a/stgit/lib/git.py +++ b/stgit/lib/git.py @@ -353,6 +353,14 @@ class Index(RunWithEnv): def delete(self): if os.path.isfile(self.__filename): os.remove(self.__filename) + def conflicts(self): + """The set of conflicting paths.""" + paths = set() + for line in self.run(['git', 'ls-files', '-z', '--unmerged'] + ).raw_output().split('\0')[:-1]: + stat, path = line.split('\t', 1) + paths.add(path) + return paths class Worktree(object): def __init__(self, directory): diff --git a/t/t2500-clean.sh b/t/t2500-clean.sh index b38d868..ad8f892 100755 --- a/t/t2500-clean.sh +++ b/t/t2500-clean.sh @@ -35,7 +35,7 @@ test_expect_success 'Create a conflict' ' ! stg push ' -test_expect_failure 'Make sure conflicting patches are preserved' ' +test_expect_success 'Make sure conflicting patches are preserved' ' stg clean && [ "$(echo $(stg applied))" = "p0 p2 p1" ] && [ "$(echo $(stg unapplied))" = "" ] ^ permalink raw reply related [flat|nested] 25+ messages in thread
* [StGit PATCH 0/4] Rewrite "stg edit" to use new infrastructure 2008-01-29 2:58 kha/safe and kha/experimental updated Karl Hasselström 2008-01-29 3:02 ` [StGit PATCH 0/5] Various cleanups Karl Hasselström 2008-01-29 3:10 ` [StGit PATCH 0/2] "stg clean" test+bugfix Karl Hasselström @ 2008-01-29 3:15 ` Karl Hasselström 2008-01-29 3:15 ` [StGit PATCH 1/4] Teach new infrastructure about the default author and committer Karl Hasselström ` (4 more replies) 2 siblings, 5 replies; 25+ messages in thread From: Karl Hasselström @ 2008-01-29 3:15 UTC (permalink / raw) To: Catalin Marinas; +Cc: git This is in kha/experimental. We hardly have any tests for "stg edit", and the control flow is kind of complicated. --- Karl Hasselström (4): Convert "stg edit" to the new infrastructure Teach new infrastructure to diff two trees Teach new infrastructure to apply patches Teach new infrastructure about the default author and committer stgit/commands/edit.py | 309 ++++++++++++++++++++++-------------------------- stgit/lib/git.py | 54 ++++++++ 2 files changed, 196 insertions(+), 167 deletions(-) -- Karl Hasselström, kha@treskal.com www.treskal.com/kalle ^ permalink raw reply [flat|nested] 25+ messages in thread
* [StGit PATCH 1/4] Teach new infrastructure about the default author and committer 2008-01-29 3:15 ` [StGit PATCH 0/4] Rewrite "stg edit" to use new infrastructure Karl Hasselström @ 2008-01-29 3:15 ` Karl Hasselström 2008-01-29 3:15 ` [StGit PATCH 2/4] Teach new infrastructure to apply patches Karl Hasselström ` (3 subsequent siblings) 4 siblings, 0 replies; 25+ messages in thread From: Karl Hasselström @ 2008-01-29 3:15 UTC (permalink / raw) To: Catalin Marinas; +Cc: git As specified by the config options user.name and user.email, and the environment variables GIT_{AUTHOR,COMMITTER}_{NAME,EMAIL,DATE} (the latter overriding the former). Nothing uses this yet, but "stg edit" will soon. Signed-off-by: Karl Hasselström <kha@treskal.com> --- stgit/lib/git.py | 24 ++++++++++++++++++++++++ 1 files changed, 24 insertions(+), 0 deletions(-) diff --git a/stgit/lib/git.py b/stgit/lib/git.py index 6cd7450..8678979 100644 --- a/stgit/lib/git.py +++ b/stgit/lib/git.py @@ -52,6 +52,30 @@ class Person(Repr): email = m.group(2) date = m.group(3) return cls(name, email, date) + @classmethod + def user(cls): + if not hasattr(cls, '__user'): + cls.__user = cls(name = config.get('user.name'), + email = config.get('user.email')) + return cls.__user + @classmethod + def author(cls): + if not hasattr(cls, '__author'): + cls.__author = cls( + name = os.environ.get('GIT_AUTHOR_NAME', NoValue), + email = os.environ.get('GIT_AUTHOR_EMAIL', NoValue), + date = os.environ.get('GIT_AUTHOR_DATE', NoValue), + defaults = cls.user()) + return cls.__author + @classmethod + def committer(cls): + if not hasattr(cls, '__committer'): + cls.__committer = cls( + name = os.environ.get('GIT_COMMITTER_NAME', NoValue), + email = os.environ.get('GIT_COMMITTER_EMAIL', NoValue), + date = os.environ.get('GIT_COMMITTER_DATE', NoValue), + defaults = cls.user()) + return cls.__committer class Tree(Repr): """Immutable.""" ^ permalink raw reply related [flat|nested] 25+ messages in thread
* [StGit PATCH 2/4] Teach new infrastructure to apply patches 2008-01-29 3:15 ` [StGit PATCH 0/4] Rewrite "stg edit" to use new infrastructure Karl Hasselström 2008-01-29 3:15 ` [StGit PATCH 1/4] Teach new infrastructure about the default author and committer Karl Hasselström @ 2008-01-29 3:15 ` Karl Hasselström 2008-01-29 3:16 ` [StGit PATCH 3/4] Teach new infrastructure to diff two trees Karl Hasselström ` (2 subsequent siblings) 4 siblings, 0 replies; 25+ messages in thread From: Karl Hasselström @ 2008-01-29 3:15 UTC (permalink / raw) To: Catalin Marinas; +Cc: git Two new methods: one index method that applies a patch to that index or fails without side-effects (without touching a worktree in either case); and one repository method that uses a temp index to apply a patch to a tree and returning the new tree (or None if the application failed), entirely side-effect free. Nothing uses this yet, but "stg edit" will soon. Signed-off-by: Karl Hasselström <kha@treskal.com> --- stgit/lib/git.py | 24 ++++++++++++++++++++++++ 1 files changed, 24 insertions(+), 0 deletions(-) diff --git a/stgit/lib/git.py b/stgit/lib/git.py index 8678979..9cb2521 100644 --- a/stgit/lib/git.py +++ b/stgit/lib/git.py @@ -338,6 +338,23 @@ class Repository(RunWithEnv): return None finally: index.delete() + def apply(self, tree, patch_text): + """Given a tree and a patch, will either return the new tree that + results when the patch is applied, or None if the patch + couldn't be applied.""" + assert isinstance(tree, Tree) + if not patch_text: + return tree + index = self.temp_index() + try: + index.read_tree(tree) + try: + index.apply(patch_text) + return index.write_tree() + except MergeException: + return None + finally: + index.delete() class MergeException(exception.StgException): pass @@ -374,6 +391,13 @@ class Index(RunWithEnv): """In-index merge, no worktree involved.""" self.run(['git', 'read-tree', '-m', '-i', '--aggressive', base.sha1, ours.sha1, theirs.sha1]).no_output() + def apply(self, patch_text): + """In-index patch application, no worktree involved.""" + try: + self.run(['git', 'apply', '--cached'] + ).raw_input(patch_text).no_output() + except run.RunException: + raise MergeException('Patch does not apply cleanly') def delete(self): if os.path.isfile(self.__filename): os.remove(self.__filename) ^ permalink raw reply related [flat|nested] 25+ messages in thread
* [StGit PATCH 3/4] Teach new infrastructure to diff two trees 2008-01-29 3:15 ` [StGit PATCH 0/4] Rewrite "stg edit" to use new infrastructure Karl Hasselström 2008-01-29 3:15 ` [StGit PATCH 1/4] Teach new infrastructure about the default author and committer Karl Hasselström 2008-01-29 3:15 ` [StGit PATCH 2/4] Teach new infrastructure to apply patches Karl Hasselström @ 2008-01-29 3:16 ` Karl Hasselström 2008-01-29 14:40 ` David Kågedal 2008-01-29 3:17 ` [StGit PATCH 4/4] Convert "stg edit" to the new infrastructure Karl Hasselström 2008-02-01 7:49 ` [StGit PATCH 0/3] "stg edit" fixups Karl Hasselström 4 siblings, 1 reply; 25+ messages in thread From: Karl Hasselström @ 2008-01-29 3:16 UTC (permalink / raw) To: Catalin Marinas; +Cc: git Nothing uses this yet, but "stg edit" will soon. Signed-off-by: Karl Hasselström <kha@treskal.com> --- stgit/lib/git.py | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) diff --git a/stgit/lib/git.py b/stgit/lib/git.py index 9cb2521..d75f724 100644 --- a/stgit/lib/git.py +++ b/stgit/lib/git.py @@ -1,5 +1,6 @@ import os, os.path, re from stgit import exception, run, utils +from stgit.config import config class RepositoryException(exception.StgException): pass @@ -355,6 +356,11 @@ class Repository(RunWithEnv): return None finally: index.delete() + def diff_tree(self, t1, t2, diff_opts): + assert isinstance(t1, Tree) + assert isinstance(t2, Tree) + return self.run(['git', 'diff-tree', '-p'] + list(diff_opts) + + [t1.sha1, t2.sha1]).raw_output() class MergeException(exception.StgException): pass ^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [StGit PATCH 3/4] Teach new infrastructure to diff two trees 2008-01-29 3:16 ` [StGit PATCH 3/4] Teach new infrastructure to diff two trees Karl Hasselström @ 2008-01-29 14:40 ` David Kågedal 2008-01-29 15:57 ` Karl Hasselström 0 siblings, 1 reply; 25+ messages in thread From: David Kågedal @ 2008-01-29 14:40 UTC (permalink / raw) To: Karl Hasselström; +Cc: git Karl Hasselström <kha@treskal.com> writes: > Nothing uses this yet, but "stg edit" will soon. > > Signed-off-by: Karl Hasselström <kha@treskal.com> > > --- > > stgit/lib/git.py | 6 ++++++ > 1 files changed, 6 insertions(+), 0 deletions(-) > > > diff --git a/stgit/lib/git.py b/stgit/lib/git.py > index 9cb2521..d75f724 100644 > --- a/stgit/lib/git.py > +++ b/stgit/lib/git.py > @@ -1,5 +1,6 @@ > import os, os.path, re > from stgit import exception, run, utils > +from stgit.config import config But "config" isn't used in this patch. > class RepositoryException(exception.StgException): > pass > @@ -355,6 +356,11 @@ class Repository(RunWithEnv): > return None > finally: > index.delete() > + def diff_tree(self, t1, t2, diff_opts): > + assert isinstance(t1, Tree) > + assert isinstance(t2, Tree) > + return self.run(['git', 'diff-tree', '-p'] + list(diff_opts) > + + [t1.sha1, t2.sha1]).raw_output() > > class MergeException(exception.StgException): > pass > -- David Kågedal ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [StGit PATCH 3/4] Teach new infrastructure to diff two trees 2008-01-29 14:40 ` David Kågedal @ 2008-01-29 15:57 ` Karl Hasselström 0 siblings, 0 replies; 25+ messages in thread From: Karl Hasselström @ 2008-01-29 15:57 UTC (permalink / raw) To: David Kågedal; +Cc: git On 2008-01-29 15:40:55 +0100, David Kågedal wrote: > Karl Hasselström <kha@treskal.com> writes: > > +from stgit.config import config > > But "config" isn't used in this patch. You're right, it's not. It's actually used in patch 1/4, but because that code is never called until patch 4/4, the interpreter didn't catch the error. Thanks, will fix. -- Karl Hasselström, kha@treskal.com www.treskal.com/kalle ^ permalink raw reply [flat|nested] 25+ messages in thread
* [StGit PATCH 4/4] Convert "stg edit" to the new infrastructure 2008-01-29 3:15 ` [StGit PATCH 0/4] Rewrite "stg edit" to use new infrastructure Karl Hasselström ` (2 preceding siblings ...) 2008-01-29 3:16 ` [StGit PATCH 3/4] Teach new infrastructure to diff two trees Karl Hasselström @ 2008-01-29 3:17 ` Karl Hasselström 2008-02-01 7:49 ` [StGit PATCH 0/3] "stg edit" fixups Karl Hasselström 4 siblings, 0 replies; 25+ messages in thread From: Karl Hasselström @ 2008-01-29 3:17 UTC (permalink / raw) To: Catalin Marinas; +Cc: git The --annotate and --undo switches were dropped in the conversion. --annotate could be re-added, but --undo is more problematic since the command will now rewrite any applied patches on top of the edited patch. It seems best to leave this job to the fabled general undo command, expected Real Soon Now. In addition to the usual improvements from the new infrastructure, this patch has some additional benefits: * There's a new -e/--edit flag, which forces interactive editing even if options such as --sign or --author are given. (Normally, interactive editing is skipped if the patch is modified with a commandline option.) * It's now possible to edit any patch, including unapplied patches. Even diff editing works for all patches, including unapplied patches. (In fact, editing unapplied patches is slightly safer, since they don't mind a dirty index/worktree.) Signed-off-by: Karl Hasselström <kha@treskal.com> --- Testers welcome! stgit/commands/edit.py | 309 ++++++++++++++++++++++-------------------------- 1 files changed, 142 insertions(+), 167 deletions(-) diff --git a/stgit/commands/edit.py b/stgit/commands/edit.py index 9915e49..b42728f 100644 --- a/stgit/commands/edit.py +++ b/stgit/commands/edit.py @@ -18,14 +18,12 @@ 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 OptionParser, make_option -from email.Utils import formatdate +from optparse import make_option -from stgit.commands.common import * -from stgit.utils import * +from stgit import git, utils +from stgit.commands import common +from stgit.lib import git as gitlib, transaction from stgit.out import * -from stgit import stack, git - help = 'edit a patch description or diff' usage = """%prog [options] [<patch>] @@ -49,23 +47,19 @@ separator: Diff text Command-line options can be used to modify specific information -without invoking the editor. +without invoking the editor. (With the --edit option, the editor is +invoked even if such command-line options are given.) -If the patch diff is edited but the patch application fails, the -rejected patch is stored in the .stgit-failed.patch file (and also in -.stgit-edit.{diff,txt}). The edited patch can be replaced with one of -these files using the '--file' and '--diff' options. -""" +If the patch diff is edited but does not apply, no changes are made to +the patch at all. The edited patch is saved to a file which you can +feed to "stg edit --file", once you have made sure it does apply.""" -directory = DirectoryGotoToplevel() +directory = common.DirectoryHasRepositoryLib() options = [make_option('-d', '--diff', help = 'edit the patch diff', action = 'store_true'), - make_option('--undo', - help = 'revert the commit generated by the last edit', - action = 'store_true'), - make_option('-a', '--annotate', metavar = 'NOTE', - help = 'annotate the patch log entry'), + make_option('-e', '--edit', action = 'store_true', + help = 'invoke interactive editor'), make_option('--author', metavar = '"NAME <EMAIL>"', help = 'replae the author details with "NAME <EMAIL>"'), make_option('--authname', @@ -78,162 +72,143 @@ options = [make_option('-d', '--diff', help = 'replace the committer name with COMMNAME'), make_option('--commemail', help = 'replace the committer e-mail with COMMEMAIL') - ] + (make_sign_options() + make_message_options() - + make_diff_opts_option()) - -def __update_patch(pname, text, options): - """Update the current patch from the given text. - """ - patch = crt_series.get_patch(pname) - - bottom = patch.get_bottom() - top = patch.get_top() - - if text: - (message, author_name, author_email, author_date, diff - ) = parse_patch(text) - else: - message = author_name = author_email = author_date = diff = None - - out.start('Updating patch "%s"' % pname) - - if options.diff: - git.switch(bottom) - try: - git.apply_patch(diff = diff) - except: - # avoid inconsistent repository state - git.switch(top) - raise - - def c(a, b): - if a != None: - return a - return b - crt_series.refresh_patch(message = message, - author_name = c(options.authname, author_name), - author_email = c(options.authemail, author_email), - author_date = c(options.authdate, author_date), - committer_name = options.commname, - committer_email = options.commemail, - backup = True, sign_str = options.sign_str, - log = 'edit', notes = options.annotate) - - if crt_series.empty_patch(pname): - out.done('empty patch') - else: - out.done() - -def __generate_file(pname, write_fn, options): - """Generate a file containing the description to edit - """ - patch = crt_series.get_patch(pname) - - # generate the file to be edited - descr = patch.get_description().strip() - authdate = patch.get_authdate() - - tmpl = 'From: %(authname)s <%(authemail)s>\n' - if authdate: - tmpl += 'Date: %(authdate)s\n' - tmpl += '\n%(descr)s\n' - - tmpl_dict = { - 'descr': descr, - 'authname': patch.get_authname(), - 'authemail': patch.get_authemail(), - 'authdate': patch.get_authdate() - } - - if options.diff: - # add the patch diff to the edited file - bottom = patch.get_bottom() - top = patch.get_top() + ] + (utils.make_sign_options() + utils.make_message_options() + + utils.make_diff_opts_option()) - tmpl += '---\n\n' \ - '%(diffstat)s\n' \ - '%(diff)s' - - tmpl_dict['diff'] = git.diff(rev1 = bottom, rev2 = top, - diff_flags = options.diff_flags) - tmpl_dict['diffstat'] = git.diffstat(tmpl_dict['diff']) - - for key in tmpl_dict: - # make empty strings if key is not available - if tmpl_dict[key] is None: - tmpl_dict[key] = '' - - text = tmpl % tmpl_dict - - # write the file to be edited - write_fn(text) - -def __edit_update_patch(pname, options): - """Edit the given patch interactively. - """ - if options.diff: - fname = '.stgit-edit.diff' +def patch_diff(repository, cd, diff, diff_flags): + if diff: + diff = repository.diff_tree(cd.parent.data.tree, cd.tree, diff_flags) + return '\n'.join([git.diffstat(diff), diff]) else: - fname = '.stgit-edit.txt' - def write_fn(text): - f = file(fname, 'w') - f.write(text) - f.close() - - __generate_file(pname, write_fn, options) - - # invoke the editor - call_editor(fname) - - __update_patch(pname, file(fname).read(), options) + return None + +def patch_description(cd, diff): + """Generate a string containing the description to edit.""" + + desc = ['From: %s <%s>' % (cd.author.name, cd.author.email), + 'Date: %s' % cd.author.date, + '', + cd.message] + if diff: + desc += ['---', + '', + diff] + return '\n'.join(desc) + +def patch_desc(repository, cd, failed_diff, diff, diff_flags): + return patch_description(cd, failed_diff or patch_diff( + repository, cd, diff, diff_flags)) + +def update_patch_description(repository, cd, text): + message, authname, authemail, authdate, diff = common.parse_patch(text) + cd = (cd.set_message(message) + .set_author(cd.author.set_name(authname) + .set_email(authemail) + .set_date(authdate))) + failed_diff = None + if diff: + tree = repository.apply(cd.parent.data.tree, diff) + if tree == None: + failed_diff = diff + else: + cd = cd.set_tree(tree) + return cd, failed_diff def func(parser, options, args): """Edit the given patch or the current one. """ - crt_pname = crt_series.get_current() + stack = directory.repository.current_stack - if not args: - pname = crt_pname - if not pname: - raise CmdException, 'No patches applied' + if len(args) == 0: + if not stack.patchorder.applied: + raise CmdException( + 'Cannot edit top patch, because no patches are applied') + patchname = stack.patchorder.applied[-1] elif len(args) == 1: - pname = args[0] - if crt_series.patch_unapplied(pname) or crt_series.patch_hidden(pname): - raise CmdException, 'Cannot edit unapplied or hidden patches' - elif not crt_series.patch_applied(pname): - raise CmdException, 'Unknown patch "%s"' % pname + [patchname] = args + if not stack.patches.exists(patchname): + raise CmdException('%s: no such patch' % patchname) else: - parser.error('incorrect number of arguments') - - check_local_changes() - check_conflicts() - check_head_top_equal(crt_series) - - if pname != crt_pname: - # Go to the patch to be edited - applied = crt_series.get_applied() - between = applied[:applied.index(pname):-1] - pop_patches(crt_series, between) - - if options.author: - options.authname, options.authemail = name_email(options.author) - - if options.undo: - out.start('Undoing the editing of "%s"' % pname) - crt_series.undo_refresh() - out.done() - elif options.save_template: - __generate_file(pname, options.save_template, options) - elif any([options.message, options.authname, options.authemail, - options.authdate, options.commname, options.commemail, - options.sign_str]): - out.start('Updating patch "%s"' % pname) - __update_patch(pname, options.message, options) - out.done() - else: - __edit_update_patch(pname, options) + parser.error('Cannot edit more than one patch') + + cd = orig_cd = stack.patches.get(patchname).commit.data - if pname != crt_pname: - # Push the patches back - between.reverse() - push_patches(crt_series, between) + # Read patch from user-provided description. + if options.message == None: + failed_diff = None + else: + cd, failed_diff = update_patch_description(stack.repository, cd, + options.message) + + # Modify author and committer data. + if options.author != None: + options.authname, options.authemail = common.name_email(options.author) + for p, f, val in [('author', 'name', options.authname), + ('author', 'email', options.authemail), + ('author', 'date', options.authdate), + ('committer', 'name', options.commname), + ('committer', 'email', options.commemail)]: + if val != None: + cd = getattr(cd, 'set_' + p)( + getattr(getattr(cd, p), 'set_' + f)(val)) + + # Add Signed-off-by: or similar. + if options.sign_str != None: + cd = cd.set_message(utils.add_sign_line( + cd.message, options.sign_str, gitlib.Person.committer().name, + gitlib.Person.committer().email)) + + if options.save_template: + options.save_template( + patch_desc(stack.repository, cd, failed_diff, + options.diff, options.diff_flags)) + return utils.STGIT_SUCCESS + + # Let user edit the patch manually. + if cd == orig_cd or options.edit: + fn = '.stgit-edit.' + ['txt', 'patch'][bool(options.diff)] + cd, failed_diff = update_patch_description( + stack.repository, cd, utils.edit_string( + patch_desc(stack.repository, cd, failed_diff, + options.diff, options.diff_flags), + fn)) + + def failed(): + fn = '.stgit-failed.patch' + f = file(fn, 'w') + f.write(patch_desc(stack.repository, cd, failed_diff, + options.diff, options.diff_flags)) + f.close() + out.error('Edited patch did not apply.', + 'It has been saved to "%s".' % fn) + return utils.STGIT_COMMAND_ERROR + + # If we couldn't apply the patch, fail without even trying to + # effect any of the changes. + if failed_diff: + return failed() + + # The patch applied, so now we have to rewrite the StGit patch + # (and any patches on top of it). + iw = stack.repository.default_iw + trans = transaction.StackTransaction(stack, 'stg edit') + if patchname in trans.applied: + popped = trans.applied[trans.applied.index(patchname)+1:] + assert not trans.pop_patches(lambda pn: pn in popped) + else: + popped = [] + trans.patches[patchname] = stack.repository.commit(cd) + try: + for pn in popped: + trans.push_patch(pn, iw) + except transaction.TransactionHalted: + pass + try: + # Either a complete success, or a conflict during push. But in + # either case, we've successfully effected the edits the user + # asked us for. + return trans.run(iw) + except transaction.TransactionException: + # Transaction aborted -- we couldn't check out files due to + # dirty index/worktree. The edits were not carried out. + return failed() ^ permalink raw reply related [flat|nested] 25+ messages in thread
* [StGit PATCH 0/3] "stg edit" fixups 2008-01-29 3:15 ` [StGit PATCH 0/4] Rewrite "stg edit" to use new infrastructure Karl Hasselström ` (3 preceding siblings ...) 2008-01-29 3:17 ` [StGit PATCH 4/4] Convert "stg edit" to the new infrastructure Karl Hasselström @ 2008-02-01 7:49 ` Karl Hasselström 2008-02-01 7:50 ` [StGit PATCH 1/3] Parse the date instead of treating it as an opaque string Karl Hasselström ` (2 more replies) 4 siblings, 3 replies; 25+ messages in thread From: Karl Hasselström @ 2008-02-01 7:49 UTC (permalink / raw) To: Catalin Marinas; +Cc: git, David Kågedal I've fixed a few things with the "stg edit" series: * Took care of the import problem David pointed out. * Made the emacs mode not refuse to edit unaplied patches, now that "stg edit" can do it. * Made it so that the dates the user gets to edit are nicely human-readable, and not that seconds-since-1970 mess. Also pushed out to kha/experimental. --- Karl Hasselström (3): It's possible to edit unapplied patches now Convert "stg edit" to the new infrastructure Parse the date instead of treating it as an opaque string contrib/stgit.el | 4 - stgit/commands/edit.py | 309 ++++++++++++++++++++++-------------------------- stgit/lib/git.py | 76 +++++++++++- 3 files changed, 215 insertions(+), 174 deletions(-) -- Karl Hasselström, kha@treskal.com www.treskal.com/kalle ^ permalink raw reply [flat|nested] 25+ messages in thread
* [StGit PATCH 1/3] Parse the date instead of treating it as an opaque string 2008-02-01 7:49 ` [StGit PATCH 0/3] "stg edit" fixups Karl Hasselström @ 2008-02-01 7:50 ` Karl Hasselström 2008-02-01 7:50 ` [StGit PATCH 2/3] Convert "stg edit" to the new infrastructure Karl Hasselström 2008-02-01 7:50 ` [StGit PATCH 3/3] It's possible to edit unapplied patches now Karl Hasselström 2 siblings, 0 replies; 25+ messages in thread From: Karl Hasselström @ 2008-02-01 7:50 UTC (permalink / raw) To: Catalin Marinas; +Cc: git, David Kågedal This is needed for when we want to display it, since the seconds-since-the-epoch format isn't that human-friendly. Signed-off-by: Karl Hasselström <kha@treskal.com> --- stgit/lib/git.py | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 72 insertions(+), 4 deletions(-) diff --git a/stgit/lib/git.py b/stgit/lib/git.py index d75f724..50dc4f1 100644 --- a/stgit/lib/git.py +++ b/stgit/lib/git.py @@ -1,10 +1,17 @@ import os, os.path, re +from datetime import datetime, timedelta, tzinfo + from stgit import exception, run, utils from stgit.config import config class RepositoryException(exception.StgException): pass +class DateException(exception.StgException): + def __init__(self, string, type): + exception.StgException.__init__( + self, '"%s" is not a valid %s' % (string, type)) + class DetachedHeadException(RepositoryException): def __init__(self): RepositoryException.__init__(self, 'Not on any branch') @@ -26,6 +33,65 @@ def make_defaults(defaults): return None return d +class TimeZone(tzinfo, Repr): + def __init__(self, tzstring): + m = re.match(r'^([+-])(\d{2}):?(\d{2})$', tzstring) + if not m: + raise DateException(tzstring, 'time zone') + sign = int(m.group(1) + '1') + try: + self.__offset = timedelta(hours = sign*int(m.group(2)), + minutes = sign*int(m.group(3))) + except OverflowError: + raise DateException(tzstring, 'time zone') + self.__name = tzstring + def utcoffset(self, dt): + return self.__offset + def tzname(self, dt): + return self.__name + def dst(self, dt): + return timedelta(0) + def __str__(self): + return self.__name + +class Date(Repr): + """Immutable.""" + def __init__(self, datestring): + # Try git-formatted date. + m = re.match(r'^(\d+)\s+([+-]\d\d:?\d\d)$', datestring) + if m: + try: + self.__time = datetime.fromtimestamp(int(m.group(1)), + TimeZone(m.group(2))) + except ValueError: + raise DateException(datestring, 'date') + return + + # Try iso-formatted date. + m = re.match(r'^(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2}):(\d{2})\s+' + + r'([+-]\d\d:?\d\d)$', datestring) + if m: + try: + self.__time = datetime( + *[int(m.group(i + 1)) for i in xrange(6)], + **{'tzinfo': TimeZone(m.group(7))}) + except ValueError: + raise DateException(datestring, 'date') + return + + raise DateException(datestring, 'date') + def __str__(self): + return self.isoformat() + def isoformat(self): + """Human-friendly ISO 8601 format.""" + return '%s %s' % (self.__time.replace(tzinfo = None).isoformat(' '), + self.__time.tzinfo) + @classmethod + def maybe(cls, datestring): + if datestring in [None, NoValue]: + return datestring + return cls(datestring) + class Person(Repr): """Immutable.""" def __init__(self, name = NoValue, email = NoValue, @@ -34,6 +100,7 @@ class Person(Repr): self.__name = d(name, 'name') self.__email = d(email, 'email') self.__date = d(date, 'date') + assert isinstance(self.__date, Date) or self.__date in [None, NoValue] name = property(lambda self: self.__name) email = property(lambda self: self.__email) date = property(lambda self: self.__date) @@ -51,7 +118,7 @@ class Person(Repr): assert m name = m.group(1).strip() email = m.group(2) - date = m.group(3) + date = Date(m.group(3)) return cls(name, email, date) @classmethod def user(cls): @@ -65,7 +132,7 @@ class Person(Repr): cls.__author = cls( name = os.environ.get('GIT_AUTHOR_NAME', NoValue), email = os.environ.get('GIT_AUTHOR_EMAIL', NoValue), - date = os.environ.get('GIT_AUTHOR_DATE', NoValue), + date = Date.maybe(os.environ.get('GIT_AUTHOR_DATE', NoValue)), defaults = cls.user()) return cls.__author @classmethod @@ -74,7 +141,8 @@ class Person(Repr): cls.__committer = cls( name = os.environ.get('GIT_COMMITTER_NAME', NoValue), email = os.environ.get('GIT_COMMITTER_EMAIL', NoValue), - date = os.environ.get('GIT_COMMITTER_DATE', NoValue), + date = Date.maybe( + os.environ.get('GIT_COMMITTER_DATE', NoValue)), defaults = cls.user()) return cls.__committer @@ -301,7 +369,7 @@ class Repository(RunWithEnv): for attr, v2 in (('name', 'NAME'), ('email', 'EMAIL'), ('date', 'DATE')): if getattr(p, attr) != None: - env['GIT_%s_%s' % (v1, v2)] = getattr(p, attr) + env['GIT_%s_%s' % (v1, v2)] = str(getattr(p, attr)) sha1 = self.run(c, env = env).raw_input(commitdata.message ).output_one_line() return self.get_commit(sha1) ^ permalink raw reply related [flat|nested] 25+ messages in thread
* [StGit PATCH 2/3] Convert "stg edit" to the new infrastructure 2008-02-01 7:49 ` [StGit PATCH 0/3] "stg edit" fixups Karl Hasselström 2008-02-01 7:50 ` [StGit PATCH 1/3] Parse the date instead of treating it as an opaque string Karl Hasselström @ 2008-02-01 7:50 ` Karl Hasselström 2008-02-01 7:50 ` [StGit PATCH 3/3] It's possible to edit unapplied patches now Karl Hasselström 2 siblings, 0 replies; 25+ messages in thread From: Karl Hasselström @ 2008-02-01 7:50 UTC (permalink / raw) To: Catalin Marinas; +Cc: git, David Kågedal The --annotate and --undo switches were dropped in the conversion. --annotate could be re-added, but --undo is more problematic since the command will now rewrite any applied patches on top of the edited patch. It seems best to leave this job to the fabled general undo command, expected Real Soon Now. In addition to the usual improvements from the new infrastructure, this patch has some additional benefits: * There's a new -e/--edit flag, which forces interactive editing even if options such as --sign or --author are given. (Normally, interactive editing is skipped if the patch is modified with a commandline option.) * It's now possible to edit any patch, including unapplied patches. Even diff editing works for all patches, including unapplied patches. (In fact, editing unapplied patches is slightly safer, since they don't mind a dirty index/worktree.) Signed-off-by: Karl Hasselström <kha@treskal.com> --- stgit/commands/edit.py | 309 ++++++++++++++++++++++-------------------------- 1 files changed, 142 insertions(+), 167 deletions(-) diff --git a/stgit/commands/edit.py b/stgit/commands/edit.py index 9915e49..7daf156 100644 --- a/stgit/commands/edit.py +++ b/stgit/commands/edit.py @@ -18,14 +18,12 @@ 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 OptionParser, make_option -from email.Utils import formatdate +from optparse import make_option -from stgit.commands.common import * -from stgit.utils import * +from stgit import git, utils +from stgit.commands import common +from stgit.lib import git as gitlib, transaction from stgit.out import * -from stgit import stack, git - help = 'edit a patch description or diff' usage = """%prog [options] [<patch>] @@ -49,23 +47,19 @@ separator: Diff text Command-line options can be used to modify specific information -without invoking the editor. +without invoking the editor. (With the --edit option, the editor is +invoked even if such command-line options are given.) -If the patch diff is edited but the patch application fails, the -rejected patch is stored in the .stgit-failed.patch file (and also in -.stgit-edit.{diff,txt}). The edited patch can be replaced with one of -these files using the '--file' and '--diff' options. -""" +If the patch diff is edited but does not apply, no changes are made to +the patch at all. The edited patch is saved to a file which you can +feed to "stg edit --file", once you have made sure it does apply.""" -directory = DirectoryGotoToplevel() +directory = common.DirectoryHasRepositoryLib() options = [make_option('-d', '--diff', help = 'edit the patch diff', action = 'store_true'), - make_option('--undo', - help = 'revert the commit generated by the last edit', - action = 'store_true'), - make_option('-a', '--annotate', metavar = 'NOTE', - help = 'annotate the patch log entry'), + make_option('-e', '--edit', action = 'store_true', + help = 'invoke interactive editor'), make_option('--author', metavar = '"NAME <EMAIL>"', help = 'replae the author details with "NAME <EMAIL>"'), make_option('--authname', @@ -78,162 +72,143 @@ options = [make_option('-d', '--diff', help = 'replace the committer name with COMMNAME'), make_option('--commemail', help = 'replace the committer e-mail with COMMEMAIL') - ] + (make_sign_options() + make_message_options() - + make_diff_opts_option()) - -def __update_patch(pname, text, options): - """Update the current patch from the given text. - """ - patch = crt_series.get_patch(pname) - - bottom = patch.get_bottom() - top = patch.get_top() - - if text: - (message, author_name, author_email, author_date, diff - ) = parse_patch(text) - else: - message = author_name = author_email = author_date = diff = None - - out.start('Updating patch "%s"' % pname) - - if options.diff: - git.switch(bottom) - try: - git.apply_patch(diff = diff) - except: - # avoid inconsistent repository state - git.switch(top) - raise - - def c(a, b): - if a != None: - return a - return b - crt_series.refresh_patch(message = message, - author_name = c(options.authname, author_name), - author_email = c(options.authemail, author_email), - author_date = c(options.authdate, author_date), - committer_name = options.commname, - committer_email = options.commemail, - backup = True, sign_str = options.sign_str, - log = 'edit', notes = options.annotate) - - if crt_series.empty_patch(pname): - out.done('empty patch') - else: - out.done() - -def __generate_file(pname, write_fn, options): - """Generate a file containing the description to edit - """ - patch = crt_series.get_patch(pname) - - # generate the file to be edited - descr = patch.get_description().strip() - authdate = patch.get_authdate() - - tmpl = 'From: %(authname)s <%(authemail)s>\n' - if authdate: - tmpl += 'Date: %(authdate)s\n' - tmpl += '\n%(descr)s\n' - - tmpl_dict = { - 'descr': descr, - 'authname': patch.get_authname(), - 'authemail': patch.get_authemail(), - 'authdate': patch.get_authdate() - } - - if options.diff: - # add the patch diff to the edited file - bottom = patch.get_bottom() - top = patch.get_top() + ] + (utils.make_sign_options() + utils.make_message_options() + + utils.make_diff_opts_option()) - tmpl += '---\n\n' \ - '%(diffstat)s\n' \ - '%(diff)s' - - tmpl_dict['diff'] = git.diff(rev1 = bottom, rev2 = top, - diff_flags = options.diff_flags) - tmpl_dict['diffstat'] = git.diffstat(tmpl_dict['diff']) - - for key in tmpl_dict: - # make empty strings if key is not available - if tmpl_dict[key] is None: - tmpl_dict[key] = '' - - text = tmpl % tmpl_dict - - # write the file to be edited - write_fn(text) - -def __edit_update_patch(pname, options): - """Edit the given patch interactively. - """ - if options.diff: - fname = '.stgit-edit.diff' +def patch_diff(repository, cd, diff, diff_flags): + if diff: + diff = repository.diff_tree(cd.parent.data.tree, cd.tree, diff_flags) + return '\n'.join([git.diffstat(diff), diff]) else: - fname = '.stgit-edit.txt' - def write_fn(text): - f = file(fname, 'w') - f.write(text) - f.close() - - __generate_file(pname, write_fn, options) - - # invoke the editor - call_editor(fname) - - __update_patch(pname, file(fname).read(), options) + return None + +def patch_description(cd, diff): + """Generate a string containing the description to edit.""" + + desc = ['From: %s <%s>' % (cd.author.name, cd.author.email), + 'Date: %s' % cd.author.date.isoformat(), + '', + cd.message] + if diff: + desc += ['---', + '', + diff] + return '\n'.join(desc) + +def patch_desc(repository, cd, failed_diff, diff, diff_flags): + return patch_description(cd, failed_diff or patch_diff( + repository, cd, diff, diff_flags)) + +def update_patch_description(repository, cd, text): + message, authname, authemail, authdate, diff = common.parse_patch(text) + cd = (cd.set_message(message) + .set_author(cd.author.set_name(authname) + .set_email(authemail) + .set_date(gitlib.Date.maybe(authdate)))) + failed_diff = None + if diff: + tree = repository.apply(cd.parent.data.tree, diff) + if tree == None: + failed_diff = diff + else: + cd = cd.set_tree(tree) + return cd, failed_diff def func(parser, options, args): """Edit the given patch or the current one. """ - crt_pname = crt_series.get_current() + stack = directory.repository.current_stack - if not args: - pname = crt_pname - if not pname: - raise CmdException, 'No patches applied' + if len(args) == 0: + if not stack.patchorder.applied: + raise common.CmdException( + 'Cannot edit top patch, because no patches are applied') + patchname = stack.patchorder.applied[-1] elif len(args) == 1: - pname = args[0] - if crt_series.patch_unapplied(pname) or crt_series.patch_hidden(pname): - raise CmdException, 'Cannot edit unapplied or hidden patches' - elif not crt_series.patch_applied(pname): - raise CmdException, 'Unknown patch "%s"' % pname + [patchname] = args + if not stack.patches.exists(patchname): + raise common.CmdException('%s: no such patch' % patchname) else: - parser.error('incorrect number of arguments') - - check_local_changes() - check_conflicts() - check_head_top_equal(crt_series) - - if pname != crt_pname: - # Go to the patch to be edited - applied = crt_series.get_applied() - between = applied[:applied.index(pname):-1] - pop_patches(crt_series, between) - - if options.author: - options.authname, options.authemail = name_email(options.author) - - if options.undo: - out.start('Undoing the editing of "%s"' % pname) - crt_series.undo_refresh() - out.done() - elif options.save_template: - __generate_file(pname, options.save_template, options) - elif any([options.message, options.authname, options.authemail, - options.authdate, options.commname, options.commemail, - options.sign_str]): - out.start('Updating patch "%s"' % pname) - __update_patch(pname, options.message, options) - out.done() - else: - __edit_update_patch(pname, options) + parser.error('Cannot edit more than one patch') + + cd = orig_cd = stack.patches.get(patchname).commit.data - if pname != crt_pname: - # Push the patches back - between.reverse() - push_patches(crt_series, between) + # Read patch from user-provided description. + if options.message == None: + failed_diff = None + else: + cd, failed_diff = update_patch_description(stack.repository, cd, + options.message) + + # Modify author and committer data. + if options.author != None: + options.authname, options.authemail = common.name_email(options.author) + for p, f, val in [('author', 'name', options.authname), + ('author', 'email', options.authemail), + ('author', 'date', gitlib.Date.maybe(options.authdate)), + ('committer', 'name', options.commname), + ('committer', 'email', options.commemail)]: + if val != None: + cd = getattr(cd, 'set_' + p)( + getattr(getattr(cd, p), 'set_' + f)(val)) + + # Add Signed-off-by: or similar. + if options.sign_str != None: + cd = cd.set_message(utils.add_sign_line( + cd.message, options.sign_str, gitlib.Person.committer().name, + gitlib.Person.committer().email)) + + if options.save_template: + options.save_template( + patch_desc(stack.repository, cd, failed_diff, + options.diff, options.diff_flags)) + return utils.STGIT_SUCCESS + + # Let user edit the patch manually. + if cd == orig_cd or options.edit: + fn = '.stgit-edit.' + ['txt', 'patch'][bool(options.diff)] + cd, failed_diff = update_patch_description( + stack.repository, cd, utils.edit_string( + patch_desc(stack.repository, cd, failed_diff, + options.diff, options.diff_flags), + fn)) + + def failed(): + fn = '.stgit-failed.patch' + f = file(fn, 'w') + f.write(patch_desc(stack.repository, cd, failed_diff, + options.diff, options.diff_flags)) + f.close() + out.error('Edited patch did not apply.', + 'It has been saved to "%s".' % fn) + return utils.STGIT_COMMAND_ERROR + + # If we couldn't apply the patch, fail without even trying to + # effect any of the changes. + if failed_diff: + return failed() + + # The patch applied, so now we have to rewrite the StGit patch + # (and any patches on top of it). + iw = stack.repository.default_iw + trans = transaction.StackTransaction(stack, 'stg edit') + if patchname in trans.applied: + popped = trans.applied[trans.applied.index(patchname)+1:] + assert not trans.pop_patches(lambda pn: pn in popped) + else: + popped = [] + trans.patches[patchname] = stack.repository.commit(cd) + try: + for pn in popped: + trans.push_patch(pn, iw) + except transaction.TransactionHalted: + pass + try: + # Either a complete success, or a conflict during push. But in + # either case, we've successfully effected the edits the user + # asked us for. + return trans.run(iw) + except transaction.TransactionException: + # Transaction aborted -- we couldn't check out files due to + # dirty index/worktree. The edits were not carried out. + return failed() ^ permalink raw reply related [flat|nested] 25+ messages in thread
* [StGit PATCH 3/3] It's possible to edit unapplied patches now 2008-02-01 7:49 ` [StGit PATCH 0/3] "stg edit" fixups Karl Hasselström 2008-02-01 7:50 ` [StGit PATCH 1/3] Parse the date instead of treating it as an opaque string Karl Hasselström 2008-02-01 7:50 ` [StGit PATCH 2/3] Convert "stg edit" to the new infrastructure Karl Hasselström @ 2008-02-01 7:50 ` Karl Hasselström 2 siblings, 0 replies; 25+ messages in thread From: Karl Hasselström @ 2008-02-01 7:50 UTC (permalink / raw) To: Catalin Marinas; +Cc: git, David Kågedal With the rewrite, "stg edit" gained the ability to edit unapplied patches, so the emacs mode no longer has to check that a patch is applied before trying to edit it. Signed-off-by: Karl Hasselström <kha@treskal.com> --- contrib/stgit.el | 4 +--- 1 files changed, 1 insertions(+), 3 deletions(-) diff --git a/contrib/stgit.el b/contrib/stgit.el index e8bbb2c..bef41c7 100644 --- a/contrib/stgit.el +++ b/contrib/stgit.el @@ -290,9 +290,7 @@ Commands: (defun stgit-edit () "Edit the patch on the current line" (interactive) - (let ((patch (if (stgit-applied-at-point) - (stgit-patch-at-point) - (error "This patch is not applied"))) + (let ((patch (stgit-patch-at-point)) (edit-buf (get-buffer-create "*StGit edit*")) (dir default-directory)) (log-edit 'stgit-confirm-edit t nil edit-buf) ^ permalink raw reply related [flat|nested] 25+ messages in thread
end of thread, other threads:[~2008-02-01 8:23 UTC | newest] Thread overview: 25+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2008-01-29 2:58 kha/safe and kha/experimental updated Karl Hasselström 2008-01-29 3:02 ` [StGit PATCH 0/5] Various cleanups Karl Hasselström 2008-01-29 3:02 ` [StGit PATCH 1/5] Create index and worktree objects just once Karl Hasselström 2008-01-29 3:03 ` [StGit PATCH 2/5] Wrap excessively long line Karl Hasselström 2008-01-29 3:03 ` [StGit PATCH 3/5] Eliminate temp variable that's used just once Karl Hasselström 2008-01-29 3:04 ` [StGit PATCH 4/5] Simplify editor selection logic Karl Hasselström 2008-01-29 20:09 ` Peter Oberndorfer 2008-01-30 7:28 ` Karl Hasselström 2008-01-30 14:55 ` Jay Soffian 2008-01-30 17:57 ` Karl Hasselström 2008-01-29 3:06 ` [StGit PATCH 5/5] Let the caller supply the diff text to diffstat() Karl Hasselström 2008-01-29 3:10 ` [StGit PATCH 0/2] "stg clean" test+bugfix Karl Hasselström 2008-01-29 3:11 ` [StGit PATCH 1/2] Add test to ensure that "stg clean" preserves conflicting patches Karl Hasselström 2008-01-29 3:12 ` [StGit PATCH 2/2] Don't clean away patches with conflicts Karl Hasselström 2008-01-29 3:15 ` [StGit PATCH 0/4] Rewrite "stg edit" to use new infrastructure Karl Hasselström 2008-01-29 3:15 ` [StGit PATCH 1/4] Teach new infrastructure about the default author and committer Karl Hasselström 2008-01-29 3:15 ` [StGit PATCH 2/4] Teach new infrastructure to apply patches Karl Hasselström 2008-01-29 3:16 ` [StGit PATCH 3/4] Teach new infrastructure to diff two trees Karl Hasselström 2008-01-29 14:40 ` David Kågedal 2008-01-29 15:57 ` Karl Hasselström 2008-01-29 3:17 ` [StGit PATCH 4/4] Convert "stg edit" to the new infrastructure Karl Hasselström 2008-02-01 7:49 ` [StGit PATCH 0/3] "stg edit" fixups Karl Hasselström 2008-02-01 7:50 ` [StGit PATCH 1/3] Parse the date instead of treating it as an opaque string Karl Hasselström 2008-02-01 7:50 ` [StGit PATCH 2/3] Convert "stg edit" to the new infrastructure Karl Hasselström 2008-02-01 7:50 ` [StGit PATCH 3/3] It's possible to edit unapplied patches now Karl Hasselström
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).