* [PATCH 0/2] Resistance is futile; you _will_ be assimilated ... @ 2006-10-22 13:05 Karl Hasselström 2006-10-22 13:08 ` [PATCH 1/2] New stg command: assimilate Karl Hasselström 2006-10-22 13:08 ` [PATCH 2/2] Regression test for "stg assimilate" Karl Hasselström 0 siblings, 2 replies; 9+ messages in thread From: Karl Hasselström @ 2006-10-22 13:05 UTC (permalink / raw) To: Catalin Marinas; +Cc: git A new StGIT command that assimilates any GIT commits made on top of your StGIT patches, and a test to go with it. -- Karl Hasselström, kha@treskal.com www.treskal.com/kalle ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/2] New stg command: assimilate 2006-10-22 13:05 [PATCH 0/2] Resistance is futile; you _will_ be assimilated Karl Hasselström @ 2006-10-22 13:08 ` Karl Hasselström 2006-10-22 17:43 ` Petr Baudis 2006-10-25 16:32 ` Karl Hasselström 2006-10-22 13:08 ` [PATCH 2/2] Regression test for "stg assimilate" Karl Hasselström 1 sibling, 2 replies; 9+ messages in thread From: Karl Hasselström @ 2006-10-22 13:08 UTC (permalink / raw) To: Catalin Marinas; +Cc: git From: Karl Hasselström <kha@treskal.com> Introduce an "assimilate" command, with no options. It takes any GIT commits committed on top of your StGIT patch stack and converts them into StGIT patches. Also change the error message when an StGIT command can't do its job because there are GIT commits on top of the stack. Instead of recommending "refresh -f", which is a destructive operation, recommend "assimilate", which is not. NOTE: "assimilate" currently refuses to work its magic if it encounters a merge commit. This is reasonable, since merge commits can't (yet) be represented as StGIT patches. However, it would be possible (and well-defined) to replace the merge commit with a regular commit on the branch with the same end result (tree object), discarding all the parents that aren't on our branch. But this would take a substantial amount of code, and is of dubious value, so for now "assimilate" just cries bloody murder if it finds a merge. Signed-off-by: Karl Hasselström <kha@treskal.com> --- stgit/commands/assimilate.py | 96 ++++++++++++++++++++++++++++++++++++++++++ stgit/commands/common.py | 8 ++-- stgit/git.py | 3 + stgit/main.py | 3 + stgit/stack.py | 28 +++++++++--- 5 files changed, 126 insertions(+), 12 deletions(-) diff --git a/stgit/commands/assimilate.py b/stgit/commands/assimilate.py new file mode 100644 index 0000000..2c0ec56 --- /dev/null +++ b/stgit/commands/assimilate.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- + +__copyright__ = """ +Copyright (C) 2006, Karl Hasselström <kha@treskal.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" + +import sys, os +from optparse import OptionParser, make_option + +from stgit.commands.common import * +from stgit.utils import * +from stgit import stack, git + +help = 'StGIT-ify any GIT commits made on top of your StGIT stack' +usage = """%prog + +If you have made GIT commits on top of your stack of StGIT patches, +many StGIT commands will refuse to work. This command converts any +such commits to StGIT patches, preserving their contents. + +Only GIT commits with exactly one parent can be assimilated; in other +words, if you have committed a merge on top of your stack, this +command cannot help you.""" + +options = [] + +def func(parser, options, args): + """Assimilate a number of patches. + """ + + def nothing_to_do(): + print 'No commits to assimilate' + + top_patch = crt_series.get_current_patch() + if not top_patch: + return nothing_to_do() + + victims = [] + victim = git.get_commit(git.get_head()) + while victim.get_id_hash() != top_patch.get_top(): + victims.append(victim) + parents = victim.get_parents() + if not parents: + raise CmdException, 'Commit %s has no parents, aborting' % victim + elif len(parents) > 1: + raise CmdException, 'Commit %s is a merge, aborting' % victim + victim = git.get_commit(parents[0]) + + if not victims: + return nothing_to_do() + + if crt_series.get_protected(): + raise CmdException( + 'This branch is protected. Modification is not permitted') + + patch2name = {} + name2patch = {} + def name_taken(name): + return patchname in name2patch or crt_series.patch_exists(patchname) + for victim in victims: + patchname = make_patch_name(victim.get_log()) + if not patchname: + patchname = 'patch' + if name_taken(patchname): + suffix = 0 + while name_taken('%s-%d' % (patchname, suffix)): + suffix += 1 + patchname = '%s-%d' % (patchname, suffix) + patch2name[victim] = patchname + name2patch[patchname] = victim + + for victim in reversed(victims): + print ('Creating patch "%s" from commit %s' + % (patch2name[victim], victim)) + aname, amail, adate = name_email_date(victim.get_author()) + cname, cmail, cdate = name_email_date(victim.get_committer()) + crt_series.new_patch( + patch2name[victim], + can_edit = False, before_existing = False, refresh = False, + top = victim.get_id_hash(), bottom = victim.get_parent(), + message = victim.get_log(), + author_name = aname, author_email = amail, author_date = adate, + committer_name = cname, committer_email = cmail) diff --git a/stgit/commands/common.py b/stgit/commands/common.py index bf8481e..1ea6025 100644 --- a/stgit/commands/common.py +++ b/stgit/commands/common.py @@ -113,10 +113,10 @@ def check_local_changes(): def check_head_top_equal(): if not crt_series.head_top_equal(): - raise CmdException, \ - 'HEAD and top are not the same. You probably committed\n' \ - ' changes to the tree outside of StGIT. If you know what you\n' \ - ' are doing, use the "refresh -f" command' + raise CmdException( + 'HEAD and top are not the same. You probably committed\n' + ' changes to the tree outside of StGIT. To bring them\n' + ' into StGIT, use the "assimilate" command') def check_conflicts(): if os.path.exists(os.path.join(basedir.get(), 'conflicts')): diff --git a/stgit/git.py b/stgit/git.py index 43bdc7e..42b0d12 100644 --- a/stgit/git.py +++ b/stgit/git.py @@ -79,6 +79,9 @@ class Commit: def get_log(self): return self.__log + def __str__(self): + return self.get_id_hash() + # dictionary of Commit objects, used to avoid multiple calls to git __commits = dict() diff --git a/stgit/main.py b/stgit/main.py index e9cc6cd..de35ca8 100644 --- a/stgit/main.py +++ b/stgit/main.py @@ -30,6 +30,7 @@ from stgit.commands.common import * # The commands import stgit.commands.add import stgit.commands.applied +import stgit.commands.assimilate import stgit.commands.branch import stgit.commands.delete import stgit.commands.diff @@ -70,6 +71,7 @@ # commands = { 'add': stgit.commands.add, 'applied': stgit.commands.applied, + 'assimilate': stgit.commands.assimilate, 'branch': stgit.commands.branch, 'delete': stgit.commands.delete, 'diff': stgit.commands.diff, @@ -113,6 +115,7 @@ repocommands = ( ) stackcommands = ( 'applied', + 'assimilate', 'clean', 'commit', 'float', diff --git a/stgit/stack.py b/stgit/stack.py index 93a3d4e..e50f189 100644 --- a/stgit/stack.py +++ b/stgit/stack.py @@ -350,9 +350,17 @@ class Series: """ return Patch(name, self.__patch_dir, self.__refs_dir) + def get_current_patch(self): + """Return a Patch object representing the topmost patch, or + None if there is no such patch.""" + crt = self.get_current() + if not crt: + return None + return Patch(crt, self.__patch_dir, self.__refs_dir) + def get_current(self): - """Return a Patch object representing the topmost patch - """ + """Return the name of the topmost patch, or None if there is + no such patch.""" if os.path.isfile(self.__current_file): name = read_string(self.__current_file) else: @@ -414,6 +422,11 @@ class Series: """ return name in self.get_unapplied() + def patch_exists(self, name): + """Return true if there is a patch with the given name, false + otherwise.""" + return self.__patch_applied(name) or self.__patch_applied(name) + def __begin_stack_check(self): """Save the current HEAD into .git/refs/heads/base if the stack is empty @@ -433,12 +446,11 @@ class Series: def head_top_equal(self): """Return true if the head and the top are the same """ - crt = self.get_current() + crt = self.get_current_patch() if not crt: # we don't care, no patches applied return True - return git.get_head() == Patch(crt, self.__patch_dir, - self.__refs_dir).get_top() + return git.get_head() == crt.get_top() def is_initialised(self): """Checks if series is already initialised @@ -688,7 +700,7 @@ class Series: top = None, bottom = None, author_name = None, author_email = None, author_date = None, committer_name = None, committer_email = None, - before_existing = False): + before_existing = False, refresh = True): """Creates a new patch """ if self.__patch_applied(name) or self.__patch_unapplied(name): @@ -741,8 +753,8 @@ class Series: else: append_string(self.__applied_file, patch.get_name()) self.__set_current(name) - - self.refresh_patch(cache_update = False, log = 'new') + if refresh: + self.refresh_patch(cache_update = False, log = 'new') def delete_patch(self, name): """Deletes a patch ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] New stg command: assimilate 2006-10-22 13:08 ` [PATCH 1/2] New stg command: assimilate Karl Hasselström @ 2006-10-22 17:43 ` Petr Baudis 2006-10-22 18:12 ` Karl Hasselström 2006-10-25 16:32 ` Karl Hasselström 1 sibling, 1 reply; 9+ messages in thread From: Petr Baudis @ 2006-10-22 17:43 UTC (permalink / raw) To: Karl Hasselström; +Cc: Catalin Marinas, git Dear diary, on Sun, Oct 22, 2006 at 03:08:02PM CEST, I got a letter where Karl Hasselström <kha@treskal.com> said that... > From: Karl Hasselström <kha@treskal.com> > > Introduce an "assimilate" command, with no options. It takes any GIT > commits committed on top of your StGIT patch stack and converts them > into StGIT patches. Hmm, isn't this what stg uncommit does? Well, I'm not sure if what uncommit takes is from below the stack or above the stack, but if it's the former, it would still IMHO make more sense to just tell that StGIT with a switch or something. -- Petr "Pasky" Baudis Stuff: http://pasky.or.cz/ #!/bin/perl -sp0777i<X+d*lMLa^*lN%0]dsXx++lMlN/dsM0<j]dsj $/=unpack('H*',$_);$_=`echo 16dio\U$k"SK$/SM$n\EsN0p[lN*1 lK[d2%Sa2/d0$^Ixp"|dc`;s/\W//g;$_=pack('H*',/((..)*)$/) ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] New stg command: assimilate 2006-10-22 17:43 ` Petr Baudis @ 2006-10-22 18:12 ` Karl Hasselström 2006-10-23 11:52 ` Catalin Marinas 0 siblings, 1 reply; 9+ messages in thread From: Karl Hasselström @ 2006-10-22 18:12 UTC (permalink / raw) To: Petr Baudis; +Cc: Catalin Marinas, git On 2006-10-22 19:43:08 +0200, Petr Baudis wrote: > Dear diary, on Sun, Oct 22, 2006 at 03:08:02PM CEST, I got a letter > where Karl Hasselström <kha@treskal.com> said that... > > > Introduce an "assimilate" command, with no options. It takes any > > GIT commits committed on top of your StGIT patch stack and > > converts them into StGIT patches. > > Hmm, isn't this what stg uncommit does? > > Well, I'm not sure if what uncommit takes is from below the stack or > above the stack, but if it's the former, it would still IMHO make > more sense to just tell that StGIT with a switch or something. Yes, you're correct in that uncommit and assimilate add existing commits to the StGIT stack, but in different ends: uncommit grows the stack by incorporating commits that precede it, while assimilate grows the stack by incorporating commits that follows it. The reason I decided to make a separate command is that the two actions have different use cases: uncommit is for when you want to use StGIT to manipulate commits that are already made; whereas assimilate is for the case when you were happily using StGIT to manage a patch series, and for whatever reason some regular GIT commits were made on top of that stack, jamming any further StGIT manipulation. The UIs are slightly different as a consequence: for uncommit, you tell the command explicitly how many commits you want it to convert, while assimilate will simply eat as much as it can (if it didn't, there would still be git commits on top of your StGIT series, and it'd still be jammed). uncommit is really the opposite of commit. assimilate might just as well have been called "fsck" or "cleanup". Of course, my usability study had only one participant, and he was rather biased since he's also the patch author, so this need not be the final word on the matter. -- Karl Hasselström, kha@treskal.com www.treskal.com/kalle ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] New stg command: assimilate 2006-10-22 18:12 ` Karl Hasselström @ 2006-10-23 11:52 ` Catalin Marinas 0 siblings, 0 replies; 9+ messages in thread From: Catalin Marinas @ 2006-10-23 11:52 UTC (permalink / raw) To: Karl Hasselström; +Cc: Petr Baudis, git On 22/10/06, Karl Hasselström <kha@treskal.com> wrote: > On 2006-10-22 19:43:08 +0200, Petr Baudis wrote: > > > Dear diary, on Sun, Oct 22, 2006 at 03:08:02PM CEST, I got a letter > > where Karl Hasselström <kha@treskal.com> said that... > > > > > Introduce an "assimilate" command, with no options. It takes any > > > GIT commits committed on top of your StGIT patch stack and > > > converts them into StGIT patches. > > > > Hmm, isn't this what stg uncommit does? > > > > Well, I'm not sure if what uncommit takes is from below the stack or > > above the stack, but if it's the former, it would still IMHO make > > more sense to just tell that StGIT with a switch or something. > > Yes, you're correct in that uncommit and assimilate add existing > commits to the StGIT stack, but in different ends: uncommit grows the > stack by incorporating commits that precede it, while assimilate grows > the stack by incorporating commits that follows it. I think I also prefer a separate command for this as they have slightly different goals and users could easily confuse the options of a more powerful 'uncommit'. -- Catalin ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] New stg command: assimilate 2006-10-22 13:08 ` [PATCH 1/2] New stg command: assimilate Karl Hasselström 2006-10-22 17:43 ` Petr Baudis @ 2006-10-25 16:32 ` Karl Hasselström 2006-10-25 16:41 ` Catalin Marinas 1 sibling, 1 reply; 9+ messages in thread From: Karl Hasselström @ 2006-10-25 16:32 UTC (permalink / raw) To: Catalin Marinas; +Cc: git On 2006-10-22 15:08:02 +0200, Karl Hasselström wrote: > + def name_taken(name): > + return patchname in name2patch or crt_series.patch_exists(patchname) I just realized, by means of an infinite loop, that "patchname" should be replaced with just "name" in the body of this function. Would you like me to resend the patch? -- Karl Hasselström, kha@treskal.com ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] New stg command: assimilate 2006-10-25 16:32 ` Karl Hasselström @ 2006-10-25 16:41 ` Catalin Marinas 2006-10-26 8:32 ` Karl Hasselström 0 siblings, 1 reply; 9+ messages in thread From: Catalin Marinas @ 2006-10-25 16:41 UTC (permalink / raw) To: Karl Hasselström; +Cc: git On 25/10/06, Karl Hasselström <kha@treskal.com> wrote: > On 2006-10-22 15:08:02 +0200, Karl Hasselström wrote: > > > + def name_taken(name): > > + return patchname in name2patch or crt_series.patch_exists(patchname) > > I just realized, by means of an infinite loop, that "patchname" should > be replaced with just "name" in the body of this function. Would you > like me to resend the patch? I can do it, no need to resend. I'll push the patch tonight and you can check it (I also fixed the "reversed" call as it is not available in my Python implementation). -- ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] New stg command: assimilate 2006-10-25 16:41 ` Catalin Marinas @ 2006-10-26 8:32 ` Karl Hasselström 0 siblings, 0 replies; 9+ messages in thread From: Karl Hasselström @ 2006-10-26 8:32 UTC (permalink / raw) To: Catalin Marinas; +Cc: git On 2006-10-25 17:41:50 +0100, Catalin Marinas wrote: > On 25/10/06, Karl Hasselström <kha@treskal.com> wrote: > > > I just realized, by means of an infinite loop, that "patchname" > > should be replaced with just "name" in the body of this function. > > Would you like me to resend the patch? > > I can do it, no need to resend. I'll push the patch tonight and you > can check it (I also fixed the "reversed" call as it is not > available in my Python implementation). Aahh, I avoided using set() (and had to settle for a decidedly less elegant dict-with-arbitrary-values) precisely so that the code wouldn't require Python 2.4, but reversed() is also a new feature of 2.4. -- Karl Hasselström, kha@treskal.com ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 2/2] Regression test for "stg assimilate" 2006-10-22 13:05 [PATCH 0/2] Resistance is futile; you _will_ be assimilated Karl Hasselström 2006-10-22 13:08 ` [PATCH 1/2] New stg command: assimilate Karl Hasselström @ 2006-10-22 13:08 ` Karl Hasselström 1 sibling, 0 replies; 9+ messages in thread From: Karl Hasselström @ 2006-10-22 13:08 UTC (permalink / raw) To: Catalin Marinas; +Cc: git From: Karl Hasselström <kha@treskal.com> Signed-off-by: Karl Hasselström <kha@treskal.com> --- t/t1301-assimilate.sh | 86 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 86 insertions(+), 0 deletions(-) diff --git a/t/t1301-assimilate.sh b/t/t1301-assimilate.sh new file mode 100755 index 0000000..26b263c --- /dev/null +++ b/t/t1301-assimilate.sh @@ -0,0 +1,86 @@ +#!/bin/sh +# Copyright (c) 2006 Karl Hasselström +test_description='Test the assimilate command.' +. ./test-lib.sh + +test_expect_success \ + 'Assimilate in a non-initialized repository' \ + 'stg assimilate' + +test_expect_success \ + 'Initialize the StGIT repository' \ + 'stg init' + +test_expect_success \ + 'Assimilate in a repository without patches' \ + 'stg assimilate' + +test_expect_success \ + 'Create a patch' \ + ' + stg new foo -m foo && + echo foo > foo.txt && + stg add foo.txt && + stg refresh + ' + +test_expect_success \ + 'Assimilate when there is nothing to do' \ + 'stg assimilate' + +test_expect_success \ + 'Create a GIT commit' \ + ' + echo bar > bar.txt && + git add bar.txt && + git commit -a -m bar + ' + +test_expect_success \ + 'Assimilate one GIT commit' \ + ' + [ $(stg applied | wc -l) -eq 1 ] && + stg assimilate && + [ $(stg applied | wc -l) -eq 2 ] + ' + +test_expect_success \ + 'Create three more GIT commits' \ + ' + echo one > numbers.txt && + git add numbers.txt && + git commit -a -m one && + echo two >> numbers.txt && + git commit -a -m two && + echo three >> numbers.txt && + git commit -a -m three + ' + +test_expect_success \ + 'Assimilate three GIT commits' \ + ' + [ $(stg applied | wc -l) -eq 2 ] && + stg assimilate && + [ $(stg applied | wc -l) -eq 5 ] + ' + +test_expect_success \ + 'Create a mege commit' \ + ' + git checkout -b br master^^ && + echo woof > woof.txt && + git add woof.txt && + git commit -a -m woof && + git checkout master && + git pull . br + ' + +test_expect_success \ + 'Try (and fail) to assimilate the merge commit' \ + ' + [ $(stg applied | wc -l) -eq 5 ] && + ! stg assimilate && + [ $(stg applied | wc -l) -eq 5 ] + ' + +test_done ^ permalink raw reply related [flat|nested] 9+ messages in thread
end of thread, other threads:[~2006-10-26 8:32 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2006-10-22 13:05 [PATCH 0/2] Resistance is futile; you _will_ be assimilated Karl Hasselström 2006-10-22 13:08 ` [PATCH 1/2] New stg command: assimilate Karl Hasselström 2006-10-22 17:43 ` Petr Baudis 2006-10-22 18:12 ` Karl Hasselström 2006-10-23 11:52 ` Catalin Marinas 2006-10-25 16:32 ` Karl Hasselström 2006-10-25 16:41 ` Catalin Marinas 2006-10-26 8:32 ` Karl Hasselström 2006-10-22 13:08 ` [PATCH 2/2] Regression test for "stg assimilate" 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).