From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:08
The name of the current patch, if any, is always the last line of
patches/<branch>/applied (and there is no current patch if and only if
the "applied" file is empty). So use that instead, and stop having to
worry about keeping the redundant "current" file up-to-date.
Signed-off-by: Karl Hasselström <redacted>
---
This is another remove-redundant-metadata cleanup patch. Not only does
it remove more code than it adds, the removed code (mostly calls to
__set_current) is the kind that one easily forgets to insert in the
proper places when writing new code.
stgit/stack.py | 35 +++++++++--------------------------
1 files changed, 9 insertions(+), 26 deletions(-)
@@ -295,7 +295,6 @@ class Series(StgitObject):self.__applied_file=os.path.join(self._dir(),'applied')self.__unapplied_file=os.path.join(self._dir(),'unapplied')self.__hidden_file=os.path.join(self._dir(),'hidden')-self.__current_file=os.path.join(self._dir(),'current')self.__descr_file=os.path.join(self._dir(),'description')# where this series keeps its patches
@@ -325,11 +324,6 @@ class Series(StgitObject):"""returnself.__name-def__set_current(self,name):-"""Sets the topmost patch-"""-self._set_field('current',name)-defget_patch(self,name):"""Return a Patch object for the given name"""
@@ -346,11 +340,16 @@ class Series(StgitObject):defget_current(self):"""Return the name of the topmost patch, or None if there isnosuchpatch."""-name=self._get_field('current')-ifname=='':+try:+applied=self.get_applied()+exceptStackException:+# No "applied" file: branch is not initialized.+returnNone+try:+returnapplied[-1]+exceptIndexError:+# No patches applied.returnNone-else:-returnnamedefget_applied(self):ifnotos.path.isfile(self.__applied_file):
@@ -650,8 +649,6 @@ class Series(StgitObject):os.remove(self.__unapplied_file)ifos.path.exists(self.__hidden_file):os.remove(self.__hidden_file)-ifos.path.exists(self.__current_file):-os.remove(self.__current_file)ifos.path.exists(self.__descr_file):os.remove(self.__descr_file)ifos.path.exists(self._dir()+'/orig-base'):
@@ -825,11 +822,8 @@ class Series(StgitObject):self.log_patch(patch,'new')insert_string(self.__applied_file,patch.get_name())-ifnotself.get_current():-self.__set_current(name)else:append_string(self.__applied_file,patch.get_name())-self.__set_current(name)ifrefresh:self.refresh_patch(cache_update=False,log='new')
@@ -936,8 +930,6 @@ class Series(StgitObject):f.writelines([line+'\n'forlineinunapplied])f.close()-self.__set_current(name)-returnforwardeddefmerged_patches(self,names):
@@ -1019,8 +1011,6 @@ class Series(StgitObject):f.writelines([line+'\n'forlineinunapplied])f.close()-self.__set_current(name)-# head == bottom case doesn't need to refresh the patchifemptyorhead!=bottom:ifnotex:
@@ -1098,11 +1088,6 @@ class Series(StgitObject):f.writelines([line+'\n'forlineinapplied])f.close()-ifapplied==[]:-self.__set_current(None)-else:-self.__set_current(applied[-1])-defempty_patch(self,name):"""Returns True if the patch is empty"""
@@ -1144,8 +1129,6 @@ class Series(StgitObject):f.close()elifoldnameinapplied:Patch(oldname,self.__patch_dir,self.__refs_dir).rename(newname)-ifoldname==self.get_current():-self.__set_current(newname)applied[applied.index(oldname)]=newname
On 06/05/07, Karl Hasselström [off-list ref] wrote:
The name of the current patch, if any, is always the last line of
patches/<branch>/applied (and there is no current patch if and only if
the "applied" file is empty). So use that instead, and stop having to
worry about keeping the redundant "current" file up-to-date.
I applied this patch. Could you also send me a patch for the
bash-completion script as it uses this file?
I think the self.__current_file (same for the base file removed in a
different patch) should still be available in the Series object and
removed when deleting a branch, otherwise you get a "Series directory
... is not empty" exception.
Thanks.
--
Catalin
From: Peter Oberndorfer <hidden> Date: 2016-06-15 22:43:10
On Tuesday 15 May 2007 17:56, Catalin Marinas wrote:
On 06/05/07, Karl Hasselström [off-list ref] wrote:
quoted
The name of the current patch, if any, is always the last line of
patches/<branch>/applied (and there is no current patch if and only if
the "applied" file is empty). So use that instead, and stop having to
worry about keeping the redundant "current" file up-to-date.
I applied this patch. Could you also send me a patch for the
bash-completion script as it uses this file?
I think the self.__current_file (same for the base file removed in a
different patch) should still be available in the Series object and
removed when deleting a branch, otherwise you get a "Series directory
... is not empty" exception.
Thanks.
Hi,
this is a bit OT,
but when i wanted to try out this changes i found that 2 unrelated patches in you repo[1] are empty.
* Store branch description in the config file
* Make the "name" argument to "stg new" optional
Is that a problem on my side, or are they really empty?
Greetings Peter
[1] http://homepage.ntlworld.com/cmarinas/stgit.git
which is mirrored at
http://repo.or.cz/w/stgit.git
On 15/05/07, Peter Oberndorfer [off-list ref] wrote:
this is a bit OT,
but when i wanted to try out this changes i found that 2 unrelated patches in you repo[1] are empty.
* Store branch description in the config file
* Make the "name" argument to "stg new" optional
Thanks for pointing out. They failed to apply cleanly last night and
forgot to delete the empty patches created. I fixed the conflicts and
added them today (I'll push them tonight).
--
Catalin
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:10
On 2007-05-15 16:56:33 +0100, Catalin Marinas wrote:
On 06/05/07, Karl Hasselström [off-list ref] wrote:
quoted
The name of the current patch, if any, is always the last line of
patches/<branch>/applied (and there is no current patch if and
only if the "applied" file is empty). So use that instead, and
stop having to worry about keeping the redundant "current" file
up-to-date.
I applied this patch. Could you also send me a patch for the
bash-completion script as it uses this file?
I realized this myself yesterday or so, and patched it to not need the
current, applied, and unapplied files. Are you OK with that patch, or
would you like one that keeps using {,un}applied?
I think the self.__current_file (same for the base file removed in a
different patch) should still be available in the Series object and
removed when deleting a branch, otherwise you get a "Series
directory ... is not empty" exception.
Ah, very true. I'll whip up a fix.
Same question there: are you OK with a single fix for base, current,
applied, and unapplied, or do you want them separate?
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
@@ -660,6 +660,16 @@ class Series(StgitObject):ifos.path.exists(self._dir()+'/orig-base'):os.remove(self._dir()+'/orig-base')+# Remove obsolete files that StGIT no longer uses, but+# that might still be around if this is an old repository.+forobsoletein([os.path.join(self._dir(),fn)+forfnin['current','description',+'applied','unapplied']]++[os.path.join(self.__base_dir,+'refs','bases',self.__name)]):+ifos.path.exists(obsolete):+os.remove(obsolete)+ifnotos.listdir(self.__patch_dir):os.rmdir(self.__patch_dir)else:
On 15/05/07, Karl Hasselström [off-list ref] wrote:
On 2007-05-15 16:56:33 +0100, Catalin Marinas wrote:
quoted
On 06/05/07, Karl Hasselström [off-list ref] wrote:
quoted
The name of the current patch, if any, is always the last line of
patches/<branch>/applied (and there is no current patch if and
only if the "applied" file is empty). So use that instead, and
stop having to worry about keeping the redundant "current" file
up-to-date.
I applied this patch. Could you also send me a patch for the
bash-completion script as it uses this file?
I realized this myself yesterday or so, and patched it to not need the
current, applied, and unapplied files. Are you OK with that patch, or
would you like one that keeps using {,un}applied?
What is the impact on the bash completion for calling StGIT rather
than reading those files? Is it visible? If I integrate the DAG
patches, there probably isn't other way anyway.
quoted
I think the self.__current_file (same for the base file removed in a
different patch) should still be available in the Series object and
removed when deleting a branch, otherwise you get a "Series
directory ... is not empty" exception.
Ah, very true. I'll whip up a fix.
Same question there: are you OK with a single fix for base, current,
applied, and unapplied, or do you want them separate?
Whatever is easier for you :-), I don't have any preference.
I'll push the patches I integrated in the next hour or so and you can
base your changes on them.
Thanks.
--
Catalin
On Tue, May 15, 2007 at 04:56:33PM +0100, Catalin Marinas wrote:
On 06/05/07, Karl Hasselström [off-list ref] wrote:
quoted
The name of the current patch, if any, is always the last line of
patches/<branch>/applied (and there is no current patch if and only if
the "applied" file is empty). So use that instead, and stop having to
worry about keeping the redundant "current" file up-to-date.
I applied this patch. Could you also send me a patch for the
bash-completion script as it uses this file?
I think the self.__current_file (same for the base file removed in a
different patch) should still be available in the Series object and
removed when deleting a branch, otherwise you get a "Series directory
.. is not empty" exception.
Shouldn't we also migrate to new format as soon as we need to touch a
data - in this case, whenever we push/pop ?
Or maybe declare a new "stgit stack format version" ? Currently we
have "stg branch --convert", which switches between a "new" and an
"old" format which noone probably uses any more. What about
versionning the on-disk format, and possibly provide the "convert"
functionnality back and forth between one format and the next, with
formal documentation about which version works with which stack
format ?
Best regards,
--
Yann.
On Tue, May 15, 2007 at 04:56:33PM +0100, Catalin Marinas wrote:
quoted
I think the self.__current_file (same for the base file removed in a
different patch) should still be available in the Series object and
removed when deleting a branch, otherwise you get a "Series directory
.. is not empty" exception.
Shouldn't we also migrate to new format as soon as we need to touch a
data - in this case, whenever we push/pop ?
Or maybe declare a new "stgit stack format version" ? Currently we
have "stg branch --convert", which switches between a "new" and an
"old" format which noone probably uses any more. What about
versionning the on-disk format, and possibly provide the "convert"
functionnality back and forth between one format and the next, with
formal documentation about which version works with which stack
format ?
I think it would be useful to have a version file (probably per
branch) and just upgrade when a mismatch is detected (in the __init__
function). The other option is to keep ignoring the unused files until
the branch is deleted but we might make a change at some point that
would break things.
We should write successive convert() functions and keep all of them in
case one skips an intermediate version.
--
Catalin
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:10
On 2007-05-15 23:49:52 +0200, Yann Dirson wrote:
On Tue, May 15, 2007 at 10:36:05PM +0100, Catalin Marinas wrote:
quoted
I think it would be useful to have a version file (probably per
branch) and just upgrade when a mismatch is detected (in the
__init__ function).
Sounds reasonable, but I'd rather keep that in the config file (eg.
branch.<name>.stgit.formatversion).
I agree that explicit versioning would be a good idea -- doing
explicit upgrades at well-defined points is good headache prevention.
And I agree that the config file is a good place to put it.
I'll probably have time to whip up a patch later today. I think I'll
call the old "old" format 0, the old "new" format 1, and then use
successive integers from then on. I'll make a single version bump for
the format changes you've alreay applied, and re-do the
format-changing patches you haven't applied yet so that they have
version bumping integrated.
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:10
On 2007-05-15 21:01:43 +0100, Catalin Marinas wrote:
What is the impact on the bash completion for calling StGIT rather
than reading those files? Is it visible?
Yes, it's visible, but not annoying (to me anyway). The overhead is
akin to the overhead we used to have when "stg help" generated the
command names -- on the order of 100-200 ms, when StGIT is in the
cache. The expensive part is to start stgit; the git calls are cheap.
So theoretically the completion script could duplicate the logic in
StGIT and avoid most of the overhead, if someone wanted it badly
enough.
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
On 16/05/07, Karl Hasselström [off-list ref] wrote:
On 2007-05-15 21:01:43 +0100, Catalin Marinas wrote:
quoted
What is the impact on the bash completion for calling StGIT rather
than reading those files? Is it visible?
Yes, it's visible, but not annoying (to me anyway). The overhead is
akin to the overhead we used to have when "stg help" generated the
command names -- on the order of 100-200 ms, when StGIT is in the
cache. The expensive part is to start stgit; the git calls are cheap.
So theoretically the completion script could duplicate the logic in
StGIT and avoid most of the overhead, if someone wanted it badly
enough.
I did a quick test of 'stg series' with the DAG patches applied, on a
Linux kernel repository ('du -sh .git' is 285M) with 42 patches (only
25 applied). It constantly takes over 2 seconds to complete (compared
to < 200ms without the DAG patches). The problem is that this delay
will happen for bash completion as well.
It seems that most of the time is spent in git._output_lines() called
from stack.read_refs() (for git-show-ref). I attach the profiling
output generated by stg-prof.
--
Catalin
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:10
On 2007-05-16 13:07:14 +0100, Catalin Marinas wrote:
I did a quick test of 'stg series' with the DAG patches applied, on
a Linux kernel repository ('du -sh .git' is 285M) with 42 patches
(only 25 applied). It constantly takes over 2 seconds to complete
(compared to < 200ms without the DAG patches). The problem is that
this delay will happen for bash completion as well.
This is more than ten times as expensive as in my measurement.
Curious. And the algorithm is designed so that it shouldn't take time
proportional to the repository size, just proportional to the number
of patches.
There are three git calls involved:
* List the references. There can't be more than a few hundred of
them.
* rev-list all patches, subtracting everything that's reachable from
the branch head. This set of commits should not be much larger
than the number of unapplied patches.
* rev-list the branch head, but stop walking as soon as all applied
patches have been seen. This set of commits should not be much
larger than the number of applied patches.
None of the calls should be expensive.
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:10
On 2007-05-16 21:40:02 +0200, Karl Hasselström wrote:
On 2007-05-16 13:07:14 +0100, Catalin Marinas wrote:
quoted
I did a quick test of 'stg series' with the DAG patches applied,
on a Linux kernel repository ('du -sh .git' is 285M) with 42
patches (only 25 applied). It constantly takes over 2 seconds to
complete (compared to < 200ms without the DAG patches). The
problem is that this delay will happen for bash completion as
well.
This is more than ten times as expensive as in my measurement.
Curious. And the algorithm is designed so that it shouldn't take
time proportional to the repository size, just proportional to the
number of patches.
I set up a kernel repository with 100 applied and 100 unapplied
patches:
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
$ cd linux-2.6/
$ stg init
$ for ((i=0;i<200;i++)); do stg new p$(printf '%03d' $i) -m "Patch $i"; done
$ stg goto p099
Then repeatedly:
$ time stg series >/dev/null
This gives times such as
real 0m0.247s
user 0m0.156s
sys 0m0.060s
real 0m0.147s
user 0m0.088s
sys 0m0.036s
real 0m0.153s
user 0m0.088s
sys 0m0.032s
real 0m0.144s
user 0m0.104s
sys 0m0.024s
Tab completion also feels like it takes 0.1-0.2 seconds -- which it
should, since it's implemented with series, applied, and unapplied,
allow which do the same amount of work.
But my kernel repository is _much_ smaller than yours:
$ du -sh .git
183M .git
Do you perchance have a bunch of loose objects in there?
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
On 16/05/07, Karl Hasselström [off-list ref] wrote:
I set up a kernel repository with 100 applied and 100 unapplied
patches:
[...]
Then repeatedly:
$ time stg series >/dev/null
I ran 'git repack -a -d' and 'git prune'. There are no other objects
apart from the generated pack:
$ du -sh .git
211M .git
And then repeatedly 'time stg series > /dev/null':
real 0m1.638s
user 0m1.422s
sys 0m0.088s
real 0m2.542s
user 0m1.436s
sys 0m0.078s
real 0m2.916s
user 0m1.424s
sys 0m0.083s
real 0m2.940s
user 0m1.425s
sys 0m0.081s
real 0m1.614s
user 0m1.421s
sys 0m0.081s
real 0m1.587s
user 0m1.423s
sys 0m0.081s
real 0m2.653s
user 0m1.427s
sys 0m0.075s
But my kernel repository is _much_ smaller than yours:
$ du -sh .git
183M .git
Do you perchance have a bunch of loose objects in there?
It got smaller after repacking but it is still bigger than yours.
Maybe the reason is that I have 14 branches with various patches, some
of them just for historical reasons but going back to 2.6.12. There
are also several commits generated for the patch logs.
The CPU is a P4 at 2.5GHz and the 'stg series' operation seems to be
CPU bound rather than IO. I'm also using Python 2.3 on this PC and for
this reason I changed 2 generator constructs (x for x in ...) with
list comprehension (see the attached patch).
--
Catalin
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:10
On 2007-05-17 13:43:35 +0100, Catalin Marinas wrote:
I ran 'git repack -a -d' and 'git prune'. There are no other objects
apart from the generated pack:
$ du -sh .git
211M .git
And then repeatedly 'time stg series > /dev/null':
Hmm, it seems there is a problem then. :-(
It got smaller after repacking but it is still bigger than yours.
Maybe the reason is that I have 14 branches with various patches,
some of them just for historical reasons but going back to 2.6.12.
There are also several commits generated for the patch logs.
OK. That shouldn't matter, though, since that extra history shouldn't
be examined anyway.
The CPU is a P4 at 2.5GHz and the 'stg series' operation seems to be
CPU bound rather than IO. I'm also using Python 2.3 on this PC and
for this reason I changed 2 generator constructs (x for x in ...)
with list comprehension (see the attached patch).
I don't think that's the problem, since those lists are both small.
The only possibility I can think of that might explain this is that
some of your unapplied patches are attached to a place in the commit
DAG that's far away from the branch head (e.g. you have rebased to
some entirely different place since you last had them applied), so
that "git-rev-list patch ^branch" outputs a large part of the commit
DAG.
Could you put counters in unapplied_patches() and
sort_applied_patches() to see how many lines each of them reads from
git-rev-list? The expected number (if it had taken just a little time,
like it did for me) is a small constant times the number of patches in
both cases.
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
On 17/05/07, Karl Hasselström [off-list ref] wrote:
The only possibility I can think of that might explain this is that
some of your unapplied patches are attached to a place in the commit
DAG that's far away from the branch head (e.g. you have rebased to
some entirely different place since you last had them applied), so
that "git-rev-list patch ^branch" outputs a large part of the commit
DAG.
That's probably the case. I have patches that I haven't rebased for
months but I keep them in case they might be needed in the future.
That's the reason for the hide/unhide commands. Anyway, I'm not yet
prepared to give up my current workflow.
I haven't tried to understand your patch yet but the unapplied patches
will never be in a linear DAG similar to the applied patches. Because
of this, we need to keep their order in a file anyway and we might not
need to run git-rev-list (BTW, how do you preserve the unapplied
patches order with the DAG implementation?).
Could you put counters in unapplied_patches() and
sort_applied_patches() to see how many lines each of them reads from
git-rev-list? The expected number (if it had taken just a little time,
like it did for me) is a small constant times the number of patches in
both cases.
I'll do this tomorrow to confirm but that's probably the cause of the slow-down.
--
Catalin
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:10
On 2007-05-17 21:51:19 +0100, Catalin Marinas wrote:
On 17/05/07, Karl Hasselström [off-list ref] wrote:
quoted
The only possibility I can think of that might explain this is
that some of your unapplied patches are attached to a place in the
commit DAG that's far away from the branch head (e.g. you have
rebased to some entirely different place since you last had them
applied), so that "git-rev-list patch ^branch" outputs a large
part of the commit DAG.
That's probably the case. I have patches that I haven't rebased for
months but I keep them in case they might be needed in the future.
That's probably it, then. I'll need to come up with a clever scheme to
make that case cheap again.
That's the reason for the hide/unhide commands.
Ah.
Anyway, I'm not yet prepared to give up my current workflow.
I didn't intend to make that workflow painful, so don't despair yet --
there might be a way to make it work.
I haven't tried to understand your patch yet but the unapplied
patches will never be in a linear DAG similar to the applied
patches.
I know.
Because of this, we need to keep their order in a file anyway and we
might not need to run git-rev-list (BTW, how do you preserve the
unapplied patches order with the DAG implementation?).
The order of _all_ patches is kept in the "patchorder" file. This file
is consulted only when the relative order of two unapplied patches
need to be established. (Applied patches are sorted by the DAG, and
applied patches always precede unapplied patches.)
I would like to avoid keeping a record of known-to-be-unapplied
patches; while this is nowhere near as important as not keeping a
record of applied patches, it would still provide an opening for the
user to confuse StGIT.
But I believe I have a cunning plan ...
quoted
Could you put counters in unapplied_patches() and
sort_applied_patches() to see how many lines each of them reads
from git-rev-list? The expected number (if it had taken just a
little time, like it did for me) is a small constant times the
number of patches in both cases.
I'll do this tomorrow to confirm but that's probably the cause of
the slow-down.
Thanks for hanging in there.
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:11
On 2007-05-16 08:27:11 +0200, Karl Hasselström wrote:
I'll probably have time to whip up a patch later today.
OK, so this turned out to be a slight miscalculation. But here it is,
with a test! The test contains two small tarballs, so I had to whip up
binary patch support as well.
I'll make a single version bump for the format changes you've alreay
applied, and re-do the format-changing patches you haven't applied
yet so that they have version bumping integrated.
This contains a single version bump (1 -> 2) for the applied changes,
but i haven't updated the DAG patch to use this yet. That's for
another day.
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:11
The config caching was never invalidated or updated, which caused the
two gets to always return the same value regardless of the value
passed to set.
Signed-off-by: Karl Hasselström <redacted>
---
stgit/config.py | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
@@ -99,12 +99,15 @@ class GitConfig:defrename_section(self,from_name,to_name):self.__run('git-repo-config --rename-section',[from_name,to_name])+self.__cache.clear()defset(self,name,value):self.__run('git-repo-config',[name,value])+self.__cache[name]=valuedefunset(self,name):self.__run('git-repo-config --unset',[name])+self.__cache[name]=Nonedefsections_matching(self,regexp):"""Takes a regexp with a single group, matches it against all
@@ -12,57 +12,72 @@ Exercises the "stg branch" commands. stginit+test_expect_success\+'Create a spurious refs/patches/ entry''+find.git-namefoo|xargsrm-rf&&+touch.git/refs/patches/foo+'+ test_expect_failure\-'Try to create an stgit branch with a spurious refs/patches/ entry'\-'find.git-namefoo|xargsrm-rf&&-touch.git/refs/patches/foo&&-stgbranch-cfoo+'Try to create an stgit branch with a spurious refs/patches/ entry''+stgbranch-cfoo+'++test_expect_success\+'Check that no part of the branch was created''+test"`find .git -name foo | tee /dev/stderr`"=".git/refs/patches/foo"&&+(grepfoo.git/HEAD;test$?=1)' test_expect_success\-'Check no part of the branch was created'\-'test"`find .git -name foo | tee /dev/stderr`"=".git/refs/patches/foo"&&-(grepfoo.git/HEAD;test$?=1)+'Create a spurious patches/ entry''+find.git-namefoo|xargsrm-rf&&+touch.git/patches/foo' test_expect_failure\-'Try to create an stgit branch with a spurious patches/ entry'\-'find.git-namefoo|xargsrm-rf&&-touch.git/patches/foo&&-stgbranch-cfoo+'Try to create an stgit branch with a spurious patches/ entry''+stgbranch-cfoo' test_expect_success\-'Check no part of the branch was created'\-'test"`find .git -name foo | tee /dev/stderr`"=".git/patches/foo"&&-(grepfoo.git/HEAD;test$?=1)+'Check that no part of the branch was created''+test"`find .git -name foo | tee /dev/stderr`"=".git/patches/foo"&&+(grepfoo.git/HEAD;test$?=1)+'++test_expect_success\+'Create a git branch''+find.git-namefoo|xargsrm-rf&&+cp.git/refs/heads/master.git/refs/heads/foo' test_expect_failure\-'Try to create an stgit branch with an existing git branch by that name'\-'find.git-namefoo|xargsrm-rf&&-cp.git/refs/heads/master.git/refs/heads/foo&&-stgbranch-cfoo+'Try to create an stgit branch with an existing git branch by that name''+stgbranch-cfoo' test_expect_success\-'Check no part of the branch was created'\-'test"`find .git -name foo | tee /dev/stderr`"=".git/refs/heads/foo"&&-(grepfoo.git/HEAD;test$?=1)+'Check that no part of the branch was created''+test"`find .git -name foo | tee /dev/stderr`"=".git/refs/heads/foo"&&+(grepfoo.git/HEAD;test$?=1)'+test_expect_success\+'Create an invalid refs/heads/ entry''+find.git-namefoo|xargsrm-rf&&+touch.git/refs/heads/foo+' test_expect_failure\-'Try to create an stgit branch with an invalid refs/heads/ entry'\-'find.git-namefoo|xargsrm-rf&&-touch.git/refs/heads/foo&&-stgbranch-cfoo+'Try to create an stgit branch with an invalid refs/heads/ entry''+stgbranch-cfoo' test_expect_success\-'Check no part of the branch was created'\-'test"`find .git -name foo | tee /dev/stderr`"=".git/refs/heads/foo"&&-(grepfoo.git/HEAD;test$?=1)+'Check that no part of the branch was created''+test"`find .git -name foo | tee /dev/stderr`"=".git/refs/heads/foo"&&+(grepfoo.git/HEAD;test$?=1)' test_done
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:11
There used to be a "stg branch --convert" command that switched
between "old" and "new" format metadata. But my recent metadata
cleanup patches introduced a "new new" format, and more are hopefully
on the way, so it was time to start versioning the metadata format
explicitly in order to avoid future headaches.
This patch removes the "stg branch --convert" command, and makes StGIT
automatically upgrade older formats to the latest format. It stores
the format (as an integer) in the config file. The current metadata
format version number is 2 (the "old" format is 0, and the "new"
format is 1).
Signed-off-by: Karl Hasselström <redacted>
---
Documentation/stg-branch.txt | 4 -
stgit/commands/branch.py | 11 ---
stgit/stack.py | 153 ++++++++++++++++++++++++------------------
3 files changed, 86 insertions(+), 82 deletions(-)
@@ -91,9 +90,6 @@ the "master" branch if it exists. Branch "master" is treated specially (see bug #8732), in that only the StGIT metadata are removed, the GIT branch itself is not destroyed.-'stg' branch --convert::- Switch current stack between old and new format.- OPTIONS -------
@@ -45,9 +45,6 @@ options = [make_option('-c', '--create',make_option('--clone',help='clone the contents of the current branch',action='store_true'),-make_option('--convert',-help='switch between old and new format branches',-action='store_true'),make_option('--delete',help='delete an existing development branch',action='store_true'),
@@ -186,14 +183,6 @@ def func(parser, options, args):return-elifoptions.convert:--iflen(args)!=0:-parser.error('incorrect number of arguments')--crt_series.convert()-return-elifoptions.delete:iflen(args)!=1:
@@ -273,6 +273,79 @@ class Patch(StgitObject):self._set_field('log',value)self.__update_log_ref(value)+# The current StGIT metadata format version.+FORMAT_VERSION=2++defformat_version_key(branch):+return'branch.%s.stgitformatversion'%branch++defupdate_to_current_format_version(branch,git_dir):+"""Update a potentially older StGIT directory structure to the+latestversion.Note:Thisfunctionshoulddependaslittleas+possibleonexternalfunctionsthatmaychangeduringaformat+versionbump,sinceitmustremainabletoprocessolderformats."""++branch_dir=os.path.join(git_dir,'patches',branch)+defget_format_version():+"""Return the integer format version number, or None if the+branchdoesn't have any StGIT metadata at all, of any version."""+fv=config.get(format_version_key(branch))+iffv:+# Great, there's an explicitly recorded format version+# number, which means that the branch is initialized and+# of that exact version.+returnint(fv)+elifos.path.isdir(os.path.join(branch_dir,'patches')):+# There's a .git/patches/<branch>/patches dirctory, which+# means this is an initialized version 1 branch.+return1+elifos.path.isdir(branch_dir):+# There's a .git/patches/<branch> directory, which means+# this is an initialized version 0 branch.+return0+else:+# The branch doesn't seem to be initialized at all.+returnNone+defset_format_version(v):+config.set(format_version_key(branch),'%d'%v)+defmkdir(d):+ifnotos.path.isdir(d):+os.makedirs(d)+defrm(f):+ifos.path.exists(f):+os.remove(f)++# Update 0 -> 1.+ifget_format_version()==0:+mkdir(os.path.join(branch_dir,'trash'))+patch_dir=os.path.join(branch_dir,'patches')+mkdir(patch_dir)+refs_dir=os.path.join(git_dir,'refs','patches',branch)+mkdir(refs_dir)+forpatchin(file(os.path.join(branch_dir,'unapplied')).readlines()++file(os.path.join(branch_dir,'applied')).readlines()):+patch=patch.strip()+os.rename(os.path.join(branch_dir,patch),+os.path.join(patch_dir,patch))+Patch(patch,patch_dir,refs_dir).update_top_ref()+set_format_version(1)++# Update 1 -> 2.+ifget_format_version()==1:+desc_file=os.path.join(branch_dir,'description')+ifos.path.isfile(desc_file):+desc=read_string(desc_file)+ifdesc:+config.set('branch.%s.description'%branch,desc)+rm(desc_file)+rm(os.path.join(branch_dir,'current'))+rm(os.path.join(git_dir,'refs','bases',branch))+set_format_version(2)++# Make sure we're at the latest version.+ifnotget_format_version()in[None,FORMAT_VERSION]:+raiseStackException('Branch %s is at format version %d, expected %d'+%(branch,get_format_version(),FORMAT_VERSION))classSeries(StgitObject):"""Class including the operations on series
@@ -290,6 +363,11 @@ class Series(StgitObject):raiseStackException,'GIT tree not initialised: %s'%exself._set_dir(os.path.join(self.__base_dir,'patches',self.__name))++# Update the branch to the latest format version if it is+# initialized, but don't touch it if it isn't.+update_to_current_format_version(self.__name,self.__base_dir)+self.__refs_dir=os.path.join(self.__base_dir,'refs','patches',self.__name)
@@ -299,19 +377,9 @@ class Series(StgitObject):# where this series keeps its patchesself.__patch_dir=os.path.join(self._dir(),'patches')-ifnotos.path.isdir(self.__patch_dir):-self.__patch_dir=self._dir()--# if no __refs_dir, create and populate it (upgrade old repositories)-ifself.is_initialised()andnotos.path.isdir(self.__refs_dir):-os.makedirs(self.__refs_dir)-forpatchinself.get_applied()+self.get_unapplied():-self.get_patch(patch).update_top_ref()# trash directoryself.__trash_dir=os.path.join(self._dir(),'trash')-ifself.is_initialised()andnotos.path.isdir(self.__trash_dir):-os.makedirs(self.__trash_dir)def__patch_name_valid(self,name):"""Raise an exception if the patch name is not valid.
@@ -410,19 +478,13 @@ class Series(StgitObject):return'branch.%s.description'%self.get_branch()defget_description(self):-# Fall back to the .git/patches/<branch>/description file if-# the config variable is unset.-return(config.get(self.__branch_descr())-orself._get_field('description')or'')+returnconfig.get(self.__branch_descr())defset_description(self,line):ifline:config.set(self.__branch_descr(),line)else:config.unset(self.__branch_descr())-# Delete the old .git/patches/<branch>/description file if it-# exists.-self._set_field('description',None)defget_parent_remote(self):value=config.get('branch.%s.remote'%self.__name)
@@ -503,15 +565,16 @@ class Series(StgitObject):defis_initialised(self):"""Checks if series is already initialised"""-returnos.path.isdir(self.__patch_dir)+returnbool(config.get(format_version_key(self.get_branch())))definit(self,create_at=False,parent_remote=None,parent_branch=None):"""Initialises the stgit series"""-ifos.path.exists(self.__patch_dir):-raiseStackException,self.__patch_dir+' already exists'-ifos.path.exists(self.__refs_dir):-raiseStackException,self.__refs_dir+' already exists'+ifself.is_initialised():+raiseStackException,'%s already initialized'%self.get_branch()+fordin[self._dir(),self.__refs_dir]:+ifos.path.exists(d):+raiseStackException,'%s already exists'%dif(create_at!=False):git.create_branch(self.__name,create_at)
@@ -522,45 +585,10 @@ class Series(StgitObject):self.create_empty_field('applied')self.create_empty_field('unapplied')-os.makedirs(os.path.join(self._dir(),'patches'))os.makedirs(self.__refs_dir)self._set_field('orig-base',git.get_head())-defconvert(self):-"""Either convert to use a separate patch directory, or-unconverttoplacethepatchesinthesamedirectorywith-seriescontrolfiles-"""-ifself.__patch_dir==self._dir():-print'Converting old-style to new-style...',-sys.stdout.flush()--self.__patch_dir=os.path.join(self._dir(),'patches')-os.makedirs(self.__patch_dir)--forpinself.get_applied()+self.get_unapplied():-src=os.path.join(self._dir(),p)-dest=os.path.join(self.__patch_dir,p)-os.rename(src,dest)--print'done'--else:-print'Converting new-style to old-style...',-sys.stdout.flush()--forpinself.get_applied()+self.get_unapplied():-src=os.path.join(self.__patch_dir,p)-dest=os.path.join(self._dir(),p)-os.rename(src,dest)--ifnotos.listdir(self.__patch_dir):-os.rmdir(self.__patch_dir)-print'done'-else:-print'Patch directory %s is not empty.'%self.__patch_dir--self.__patch_dir=self._dir()+config.set(format_version_key(self.get_branch()),str(FORMAT_VERSION))defrename(self,to_name):"""Renames a series
@@ -666,15 +694,6 @@ class Series(StgitObject):ifos.path.exists(self._dir()+'/orig-base'):os.remove(self._dir()+'/orig-base')-# Remove obsolete files that StGIT no longer uses, but-# that might still be around if this is an old repository.-forobsoletein([os.path.join(self._dir(),fn)-forfnin['current','description']]-+[os.path.join(self.__base_dir,-'refs','bases',self.__name)]):-ifos.path.exists(obsolete):-os.remove(obsolete)-ifnotos.listdir(self.__patch_dir):os.rmdir(self.__patch_dir)else:
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:11
This test contains tarballs of repositories created with older
versions of StGIT. It also contains the script used to generate them,
but at some point we will lose the ability to easily run old versions
-- for example, if git changes incompatibly -- so tarballs will be the
only practical option for sufficiently old versions.
Signed-off-by: Karl Hasselström <redacted>
---
t/t4000-upgrade.sh | 40 ++++++++++++++++++++++++
t/t4000-upgrade/.gitignore | 4 ++
t/t4000-upgrade/0.12.tar.gz | Bin
t/t4000-upgrade/0.8.tar.gz | Bin
t/t4000-upgrade/make-repo.sh | 71 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 115 insertions(+), 0 deletions(-)
@@ -0,0 +1,40 @@+#!/bin/sh+#+# Copyright (c) 2007 Karl Hasselström+#++test_description='Make sure that we can use old StGIT repositories'++../test-lib.sh++forverin0.120.8;do++tarzxf../t4000-upgrade/$ver.tar.gz+cd$ver++test_expect_success\+"v$ver: Check the list of applied and unapplied patches"'+["$(echo$(stgapplied))"="p0 p1 p2"]&&+["$(echo$(stgunapplied))"="p3 p4"]+'++test_expect_success\+"v$ver: Make sure the 'description' file is no longer there"'+[!-e.git/patches/master/description]&&+["$(echo$(gitconfigbranch.master.description))"="cool branch"]+'++test_expect_success\+"v$ver: Make sure the 'current' file is no longer there"'+[!-e.git/patches/master/current]+'++test_expect_success\+"v$ver: Make sure the base ref is no longer there"'+!gitshow-ref--verify--quietrefs/bases/master+'++cd..+done++test_done
@@ -0,0 +1,71 @@+# This script makes several versions of a small test repository that+# can be used for testing the format version upgrade code.++LANG=C+LC_ALL=C+PAGER=cat+TZ=UTC+exportLANGLC_ALLPAGERTZ+unsetAUTHOR_DATE+unsetAUTHOR_EMAIL+unsetAUTHOR_NAME+unsetCOMMIT_AUTHOR_EMAIL+unsetCOMMIT_AUTHOR_NAME+unsetGIT_ALTERNATE_OBJECT_DIRECTORIES+unsetGIT_AUTHOR_DATE+GIT_AUTHOR_EMAIL=author@example.com+GIT_AUTHOR_NAME='A U Thor'+unsetGIT_COMMITTER_DATE+GIT_COMMITTER_EMAIL=committer@example.com+GIT_COMMITTER_NAME='C O Mitter'+unsetGIT_DIFF_OPTS+unsetGIT_DIR+unsetGIT_EXTERNAL_DIFF+unsetGIT_INDEX_FILE+unsetGIT_OBJECT_DIRECTORY+unsetSHA1_FILE_DIRECTORIES+unsetSHA1_FILE_DIRECTORY+exportGIT_AUTHOR_EMAILGIT_AUTHOR_NAME+exportGIT_COMMITTER_EMAILGIT_COMMITTER_NAME++forverin0.120.8;do+if[-e$ver.tar.gz];thencontinue;fi++# Get the required stgit version.+(+cd../..+gitarchive--format=tar--prefix=stgit-$ver/v$ver+)|tarxf-++# Set up a repository.+mkdir$ver+cd$ver+gitinit+touchfoo+gitaddfoo+gitcommit-m'Initial commit'++# Use the old stgit.+(+pwd+PATH=../stgit-$ver:$PATH++stg--version+stginit+echo'cool branch'>.git/patches/master/description++foriin01234;do+stgnewp$i-m"Patch $i"+echo"Line $i">>foo+stgrefresh+done+stgpop-n2+)++# Reduce the number of small files.+gitgc++# Make a tarball.+cd..+tarzcf$ver.tar.gz$ver+done
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:11
This just passes the --binary option to git-diff-*, which causes the
generated diffs to contain an applyable diff even when binary files
differ. It's necessary to do this if you want to mail patches to
binary files.
Signed-off-by: Karl Hasselström <redacted>
---
stgit/commands/diff.py | 6 +++++-
stgit/commands/export.py | 5 ++++-
stgit/commands/mail.py | 6 +++++-
stgit/git.py | 16 ++++++++++++----
4 files changed, 26 insertions(+), 7 deletions(-)
@@ -44,6 +44,9 @@ shows the specified patch (defaulting to the current one)."""options=[make_option('-r','--range',metavar='rev1[..[rev2]]',dest='revs',help='show the diff between revisions'),+make_option('--binary',+help='output a diff even for binary files',+action='store_true'),make_option('-s','--stat',help='show the stat instead of the diff',action='store_true')]
@@ -62,6 +62,9 @@ options = [make_option('-d', '--dir',help='Use FILE as a template'),make_option('-b','--branch',help='use BRANCH instead of the default one'),+make_option('--binary',+help='output a diff even for binary files',+action='store_true'),make_option('-s','--stdout',help='dump the patches to the standard output',action='store_true')]
@@ -120,6 +120,9 @@ options = [make_option('-a', '--all',help='username for SMTP authentication'),make_option('-b','--branch',help='use BRANCH instead of the default one'),+make_option('--binary',+help='output a diff even for binary files',+action='store_true'),make_option('-m','--mbox',help='generate an mbox file instead of sending',action='store_true')]
@@ -390,7 +393,8 @@ def __build_message(tmpl, patch, patch_nr, total_nr, msg_id, ref_id, options):# for backward template compatibility'endofheaders':'','diff':git.diff(rev1=git_id('%s//bottom'%patch),-rev2=git_id('%s//top'%patch)),+rev2=git_id('%s//top'%patch),+binary=options.binary),'diffstat':git.diffstat(rev1=git_id('%s//bottom'%patch),rev2=git_id('%s//top'%patch)),# for backward template compatibility
From: Robin Rosenberg <hidden> Date: 2016-06-15 22:43:11
Here is a fix to update the bash prompt so it does not
use the obsolete current file anymore.
Part 2 is my version which uses a different format, mostly
because '/' can be part of the branch name.
-- robin
Note that "tail -1" gives a warning with newer versions, "tail -n 1"
should be the proper call.
Also I'm not sure it is a good way to look at "applied" file, since
Karl IIRC has plans to change this. Better call "stg top" and not
touch that again :)
On Sun, May 20, 2007 at 10:04:03PM +0200, Robin Rosenberg wrote:
@@ -8,8 +8,8 @@ if [ "$PS1" ]; thengit_dir=$(git-rev-parse--git-dir2>/dev/null)||returnref=$(git-symbolic-refHEAD2>/dev/null)||returnbr=${ref#refs/heads/}-top=$(cat$git_dir/patches/$br/current2>/dev/null)\-&&top="/$top"+top=$(tail-1$git_dir/patches/$br/applied2>/dev/null)\+&&top="/$top";echo"[$br$top]"}PS1='\u@\h:$(__prompt_git)\W\$ '-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
!DSPAM:4650aff673931961316905!
From: Robin Rosenberg <hidden> Date: 2016-06-15 22:43:11
Signed-off-by: Robin Rosenberg <redacted>
s�ndag 20 maj 2007 skrev Yann Dirson:
Note that "tail -1" gives a warning with newer versions, "tail -n 1"
should be the proper call.
My man page doesn't mention -N being deprecated, but ok since -n 1 seems
to work here too.
Also I'm not sure it is a good way to look at "applied" file, since
Karl IIRC has plans to change this. Better call "stg top" and not
touch that again :)
Calling stg is too slow to be be used here. I that command in my first draft
for this function and people complained (see the thread named "Bash snippet
to show branch and patch in bash prompt"). It takes ~ 0.15s on here which is
very noticable, barely below my pain threshold.
We'll update the prompt when and if Karl breaks this.
It'd probably drain my battery too :/
-- robin
contrib/stgbashprompt.sh | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:11
On 2007-05-20 23:22:00 +0200, Robin Rosenberg wrote:
söndag 20 maj 2007 skrev Yann Dirson:
quoted
Also I'm not sure it is a good way to look at "applied" file,
since Karl IIRC has plans to change this. Better call "stg top"
and not touch that again :)
Calling stg is too slow to be be used here. I that command in my
first draft for this function and people complained (see the thread
named "Bash snippet to show branch and patch in bash prompt"). It
takes ~ 0.15s on here which is very noticable, barely below my pain
threshold.
We'll update the prompt when and if Karl breaks this.
Yes, I can confirm that I'm hard at work breaking this. :-) I'm trying
out a way to get around the performance bug Catalin found, but I
didn't have time to finish it yesterday.
If that work is included, you could simply find the top patch by doing
git-show-ref and figuring out which patch has the same sha1 as HEAD.
But it sucks that stg starts so slowly. It has gotten better, I
believe (I think Catalin did some work here?), but 150 ms doesn't
really qualify as "instantaneous".
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
On 21/05/07, Karl Hasselström [off-list ref] wrote:
On 2007-05-20 23:22:00 +0200, Robin Rosenberg wrote:
quoted
Calling stg is too slow to be be used here. I that command in my
first draft for this function and people complained (see the thread
named "Bash snippet to show branch and patch in bash prompt"). It
takes ~ 0.15s on here which is very noticable, barely below my pain
threshold.
We'll update the prompt when and if Karl breaks this.
Yes, I can confirm that I'm hard at work breaking this. :-) I'm trying
out a way to get around the performance bug Catalin found, but I
didn't have time to finish it yesterday.
My plan is to release a 0.13 version pretty soon but without the DAG
patches as we might have to test them a bit more. The release after
0.13 I'd like to be a 1.0-rc1 (including the DAG patches) unless we
have some other major changes pending.
But it sucks that stg starts so slowly. It has gotten better, I
believe (I think Catalin did some work here?), but 150 ms doesn't
really qualify as "instantaneous".
I don't think we can get much slower than this. I modified stg to only
load the modules needed for a given command but it still takes around
150ms for a command like 'top'. I don't know any other python tricks
to make it start faster.
BTW, any of you would like to get added as a member to
gna.org/projects/stgit (there are no advantages, only e-mail updates
for filed bug reports)?
Regards.
--
Catalin
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:11
On 2007-05-21 10:31:09 +0100, Catalin Marinas wrote:
My plan is to release a 0.13 version pretty soon but without the DAG
patches as we might have to test them a bit more.
That's reasonable.
The release after 0.13 I'd like to be a 1.0-rc1 (including the DAG
patches) unless we have some other major changes pending.
It'd be great to do away with the need to "stg init", but that
shouldn't really be a major change (but I haven't started looking at
it yet). It would be nice if 1.0 had documentation that didn't have to
mention "stg init".
I don't think we can get much slower than this.
Oh yes we can ... :-)
I modified stg to only load the modules needed for a given command
but it still takes around 150ms for a command like 'top'. I don't
know any other python tricks to make it start faster.
I don't either. We might consider having plumbing written in C or
something, and make sure that the plumbing can be called directly if
there's need, but it's going to complicate things greatly compared to
pure Python.
BTW, any of you would like to get added as a member to
gna.org/projects/stgit (there are no advantages, only e-mail updates
for filed bug reports)?
Free bug reports? Sure, I'm in! (I just created a Gna! account: kha)
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:11
On 2007-05-21 12:15:40 +0200, Karl Hasselström wrote:
We might consider having plumbing written in C or something, and
make sure that the plumbing can be called directly if there's need,
but it's going to complicate things greatly compared to pure Python.
What I'm (foggily) envisioning here is to rewrite parts of StGIT (as
little as possible) as a C library (libstgit.so?), and call the
library both from the Python code, and from a "plumbing" C program
(stgit-helper?). We should not try to make the library API stable,
just like the current git library.
There are two kinds of things we'd want to have in the library: (1)
things that are too slow to do in Python, and (2) things that need to
be available from stgit-helper in order to avoid Python's startup
cost, such as top/applied/unapplied for the bash completion and bash
prompt.
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
On 21/05/07, Karl Hasselström [off-list ref] wrote:
On 2007-05-21 12:15:40 +0200, Karl Hasselström wrote:
quoted
We might consider having plumbing written in C or something, and
make sure that the plumbing can be called directly if there's need,
but it's going to complicate things greatly compared to pure Python.
Yes, it will complicate things. I think StGIT would have progressed at
a much slower pace if written in C :-).
What I'm (foggily) envisioning here is to rewrite parts of StGIT (as
little as possible) as a C library (libstgit.so?), and call the
library both from the Python code, and from a "plumbing" C program
(stgit-helper?). We should not try to make the library API stable,
just like the current git library.
Apart from the start-up time, I don't see other major slowdowns caused
by Python. It would be useful to use a git library directly without
invoking external applications (I'm not sure what's the state of a
"libgit.a" or what improvement we would get).
As for the start-up time, unless you write most of the commands in C,
we would still have to load Python modules. If you run stg-prof
instead of stg for a simple command like 'top', you can see that the
main function takes about 60-70ms, the rest to 150ms reported by the
external 'time' is Python start-up and module loading.
I had a quick try at using "freeze.py" to generate a binary (well, it
includes python bytecodes but it might save time on module look-up)
but it got confused by my optimisation to only load module commands
based on the stg arguments. Maybe we should try this first.
There are two kinds of things we'd want to have in the library: (1)
things that are too slow to do in Python, and (2) things that need to
be available from stgit-helper in order to avoid Python's startup
cost, such as top/applied/unapplied for the bash completion and bash
prompt.
As you probably guessed, I'm not really in favour of re-writing parts
of StGIT in C, at least not in the near future, though anyone can fork
and re-implement it :-).
--
Catalin
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:11
On 2007-05-21 16:17:22 +0100, Catalin Marinas wrote:
On 21/05/07, Karl Hasselström [off-list ref] wrote:
quoted
On 2007-05-21 12:15:40 +0200, Karl Hasselström wrote:
quoted
We might consider having plumbing written in C or something, and
make sure that the plumbing can be called directly if there's
need, but it's going to complicate things greatly compared to
pure Python.
Yes, it will complicate things. I think StGIT would have progressed at
a much slower pace if written in C :-).
I agree. C is not a good language to write a whole application in. But
for the hot spots, C is a reasonable choice.
quoted
What I'm (foggily) envisioning here is to rewrite parts of StGIT
(as little as possible) as a C library (libstgit.so?), and call
the library both from the Python code, and from a "plumbing" C
program (stgit-helper?). We should not try to make the library API
stable, just like the current git library.
Apart from the start-up time, I don't see other major slowdowns
caused by Python.
I haven't, either.
It would be useful to use a git library directly without invoking
external applications (I'm not sure what's the state of a "libgit.a"
or what improvement we would get).
There is no usable git library yet. But once there is, I agree we
should use it.
As for the start-up time, unless you write most of the commands in C,
we would still have to load Python modules. If you run stg-prof
instead of stg for a simple command like 'top', you can see that the
main function takes about 60-70ms, the rest to 150ms reported by the
external 'time' is Python start-up and module loading.
My suggestion was to have a small stand-alone C program that could do
some operations that need to be really fast, such as
top/applied/unapplied. It need not have a nice user interface since
it's only going to be called by scripts (bash-completion and the
like), and it should only handle those operations that _must- avoid
the Python startup penalty. And for sanity reasons, it should share
code with stgit.
I had a quick try at using "freeze.py" to generate a binary (well,
it includes python bytecodes but it might save time on module
look-up) but it got confused by my optimisation to only load module
commands based on the stg arguments. Maybe we should try this first.
I agree that we should try pure-Python optimizations first.
quoted
There are two kinds of things we'd want to have in the library:
(1) things that are too slow to do in Python, and (2) things that
need to be available from stgit-helper in order to avoid Python's
startup cost, such as top/applied/unapplied for the bash
completion and bash prompt.
As you probably guessed, I'm not really in favour of re-writing
parts of StGIT in C, at least not in the near future, though anyone
can fork and re-implement it :-).
I wouldn't do it just for fun, either. But if it's a prerequisite to
get good enough performance for something we really want, and all else
has failed, I'd be willing to argue for the introduction of a C
library and helper application.
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
On 21/05/07, Karl Hasselström [off-list ref] wrote:
My suggestion was to have a small stand-alone C program that could do
some operations that need to be really fast, such as
top/applied/unapplied. It need not have a nice user interface since
it's only going to be called by scripts (bash-completion and the
like), and it should only handle those operations that _must- avoid
the Python startup penalty. And for sanity reasons, it should share
code with stgit.
There is one more case to consider - people using NFS-mounted
directories. The applied/unapplied commands would be even slower and
the language overhead be negligible.
Another workaround would be to always generate the applied/unapplied
files when the stack structure changes.
--
Catalin
On 19/05/07, Karl Hasselström [off-list ref] wrote:
This just passes the --binary option to git-diff-*, which causes the
generated diffs to contain an applyable diff even when binary files
differ. It's necessary to do this if you want to mail patches to
binary files.
I applied this patch but is there anything wrong if we have this
option on by default, at least for some commands? Maybe we don't need
it for 'show' and 'diff' but we definitely need it for 'mail' and
'export'.
There is also git.apply_diff() which calls git.diff(). This is first
tried when pushing a patch and followed by a three-way merged if it
fails. I think we should always have the --binary option in this case.
--
Catalin
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:12
On 2007-05-22 13:15:13 +0100, Catalin Marinas wrote:
On 19/05/07, Karl Hasselström [off-list ref] wrote:
quoted
This just passes the --binary option to git-diff-*, which causes
the generated diffs to contain an applyable diff even when binary
files differ. It's necessary to do this if you want to mail
patches to binary files.
I applied this patch but is there anything wrong if we have this
option on by default, at least for some commands? Maybe we don't
need it for 'show' and 'diff' but we definitely need it for 'mail'
and 'export'.
I'd be fine with that.
There is also git.apply_diff() which calls git.diff(). This is first
tried when pushing a patch and followed by a three-way merged if it
fails. I think we should always have the --binary option in this
case.
Yes, that sounds good.
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:12
On 2007-05-22 13:11:13 +0100, Catalin Marinas wrote:
On 21/05/07, Karl Hasselström [off-list ref] wrote:
quoted
My suggestion was to have a small stand-alone C program that could
do some operations that need to be really fast, such as
top/applied/unapplied. It need not have a nice user interface
since it's only going to be called by scripts (bash-completion and
the like), and it should only handle those operations that _must-
avoid the Python startup penalty. And for sanity reasons, it
should share code with stgit.
There is one more case to consider - people using NFS-mounted
directories. The applied/unapplied commands would be even slower and
the language overhead be negligible.
Another workaround would be to always generate the applied/unapplied
files when the stack structure changes.
Yes, we could do that. These files would only be accurate when the
stack was last modified with StGIT and not plain git, but that might
be acceptable.
Hmm. Since the only way plain git modifies the stack is by changing
HEAD (we assume the user doesn't manually mess with the patch refs),
we might also write down the value of HEAD for which the
applied/unapplied files are valid, so that the caller could call "stg
applied" if the applied file was out of date. But that's quite a
hassle to have to reimplement every time.
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:15
This took a while, but here it is. (Actually, I finished this about a
week ago, but had no Internet connection so I couldn't send it.)
The series starts with basically the same DAG appliedness test as
before, with the same known performance bug. Later on in the series,
the mechanism is changed to one that doesn't have the bug. I kept the
intermediate, slow state because the changesets read better that way,
and because the new mechanism is more complicated than the old so it
might be useful to be able to compare their output in case some bug
turns up further down the road.
To test the performance, I used a script (which I've unfortunately
misplaced) that (in the kernel repository) reset to one point 10000
commits in the past and one 5000 commits in the past, pushed a few
patches at each spot, and then created 100 applied and 100 unapplied
patches on top of upstream HEAD. This triggers the performance bug
with the first algorithm since we have unapplied commits very far from
HEAD.
Both algorithms are documented in the patches that introduce them.
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
@@ -19,23 +19,26 @@ specify --merged, then rollback and retry with the correct flag.' test_create_repofoo test_expect_success\-'Clone tree and setup changes'\-"stg clone foo bar &&-(cdbar&&stgnewp1-mp1-printf'a\nc\n'>file&&stgaddfile&&stgrefresh&&-stgnewp2-mp2-printf'a\nb\nc\n'>file&&stgrefresh-)-"+'Clone tree and setup changes''+stgclonefoobar&&+(+cdbar&&stgnewp1-mp1+printf"a\nc\n">file&&stgaddfile&&stgrefresh&&+stgnewp2-mp2&&+printf"a\nb\nc\n">file&&stgrefresh&&+["$(echo$(stgapplied))"="p1 p2"]&&+["$(echo$(stgunapplied))"=""]+)+' test_expect_success\-'Port those patches to orig tree'\-'(cdfoo&&-GIT_DIR=../bar/.gitgit-format-patch--stdout\-$(cd../bar&&stgidbase@master)..HEAD|-git-am-3-k-)-'+'Port those patches to orig tree''+(+cdfoo&&+GIT_DIR=../bar/.gitgit-format-patch--stdout\+$(cd../bar&&stgidbase@master)..HEAD|git-am-3-k+)+' test_expect_success\'Pull to sync with parent, preparing for the problem'\
@@ -51,15 +54,21 @@ test_expect_failure \" test_expect_success\-'Rollback the push'\-"(cd bar && stg push --undo-)-"+'Rollback the push''+(+cdbar&&stgpush--undo&&+["$(echo$(stgapplied))"=""]&&+["$(echo$(stgunapplied))"="p1 p2"]+)+' test_expect_success\-'Push those patches while checking they were merged upstream'\-"(cd bar && stg push --merged --all-)-"+'Push those patches while checking they were merged upstream''+(+cdbar&&stgpush--merged--all+["$(echo$(stgapplied))"="p1 p2"]&&+["$(echo$(stgunapplied))"=""]+)+' test_done
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:15
We already had it, but no one was using it
Signed-off-by: Karl Hasselström <redacted>
---
stgit/stack.py | 27 +++++++++++++--------------
1 files changed, 13 insertions(+), 14 deletions(-)
@@ -404,7 +404,7 @@ class Series(StgitObject):crt=self.get_current()ifnotcrt:returnNone-returnPatch(crt,self.__patch_dir,self.__refs_dir)+returnself.get_patch(crt)defget_current(self):"""Return the name of the topmost patch, or None if there is
@@ -678,7 +678,7 @@ class Series(StgitObject):raiseStackException, \'Cannot delete: the series still contains patches'forpinpatches:-Patch(p,self.__patch_dir,self.__refs_dir).delete()+self.get_patch(p).delete()# remove the trash directoryforfnameinos.listdir(self.__trash_dir):
@@ -732,7 +732,7 @@ class Series(StgitObject):ifnotname:raiseStackException,'No patches applied'-patch=Patch(name,self.__patch_dir,self.__refs_dir)+patch=self.get_patch(name)descr=patch.get_description()ifnot(messageordescr):
@@ -798,7 +798,7 @@ class Series(StgitObject):name=self.get_current()assert(name)-patch=Patch(name,self.__patch_dir,self.__refs_dir)+patch=self.get_patch(name)old_bottom=patch.get_old_bottom()old_top=patch.get_old_top()
@@ -839,7 +839,7 @@ class Series(StgitObject):ifname==None:name=make_patch_name(descr,self.patch_exists)-patch=Patch(name,self.__patch_dir,self.__refs_dir)+patch=self.get_patch(name)patch.create()ifbottom:
@@ -919,7 +919,7 @@ class Series(StgitObject):fornameinnames:assert(nameinunapplied)-patch=Patch(name,self.__patch_dir,self.__refs_dir)+patch=self.get_patch(name)head=topbottom=patch.get_bottom()
@@ -988,8 +988,7 @@ class Series(StgitObject):patchesdetectedtohavebeenapplied.Thestateofthetreeisrestoredtotheoriginalone"""-patches=[Patch(name,self.__patch_dir,self.__refs_dir)-fornameinnames]+patches=[self.get_patch(name)fornameinnames]patches.reverse()merged=[]
@@ -1008,7 +1007,7 @@ class Series(StgitObject):unapplied=self.get_unapplied()assert(nameinunapplied)-patch=Patch(name,self.__patch_dir,self.__refs_dir)+patch=self.get_patch(name)head=git.get_head()bottom=patch.get_bottom()
@@ -1084,7 +1083,7 @@ class Series(StgitObject):name=self.get_current()assert(name)-patch=Patch(name,self.__patch_dir,self.__refs_dir)+patch=self.get_patch(name)old_bottom=patch.get_old_bottom()old_top=patch.get_old_top()
@@ -1110,7 +1109,7 @@ class Series(StgitObject):applied.reverse()assert(nameinapplied)-patch=Patch(name,self.__patch_dir,self.__refs_dir)+patch=self.get_patch(name)ifgit.get_head_file()==self.get_branch():ifkeepandnotgit.apply_diff(git.get_head(),patch.get_bottom()):
@@ -1142,7 +1141,7 @@ class Series(StgitObject):"""Returns True if the patch is empty"""self.__patch_name_valid(name)-patch=Patch(name,self.__patch_dir,self.__refs_dir)+patch=self.get_patch(name)bottom=patch.get_bottom()top=patch.get_top()
@@ -1171,14 +1170,14 @@ class Series(StgitObject):self.hide_patch(newname)ifoldnameinunapplied:-Patch(oldname,self.__patch_dir,self.__refs_dir).rename(newname)+self.get_patch(oldname).rename(newname)unapplied[unapplied.index(oldname)]=newnamef=file(self.__unapplied_file,'w+')f.writelines([line+'\n'forlineinunapplied])f.close()elifoldnameinapplied:-Patch(oldname,self.__patch_dir,self.__refs_dir).rename(newname)+self.get_patch(oldname).rename(newname)applied[applied.index(oldname)]=newname
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:15
Don't rely on cached metadata in the "applied" and "unapplied" files
to tell which patches are applied. Instead, consider the patches
reachable from the branch head to be applied, and the rest unapplied.
The order of the applied patches is also taken from the DAG, but we
can't do that for the unapplied patches. So the patch order is saved
to a file whenever it changes, and that file is consulted whenever we
need to compute the order of the unapplied patches.
The point of this excercise is to let users do things such as "git
reset" without confusing stgit. This gives incrased flexibility to
power users, and increased safety to other users. The advantages come
from the removal of redundant metadata: it is no longer possible for
StGIT's appliedness status to get out of sync with the underlying git
commit DAG.
This is how the appliedness and order is computed:
* First, a single call to git-show-ref gives the hashes of all
patches and the branch head.
* Then, "git-rev-list patch1 patch2 patch3 ^branch" lists a small
set of hashes that contains all the unapplied patches and none of
the applied patches.
* Last, "git-rev-list head" lists all commits in the branch. The
applied patches are listed in the correct order.
This is efficient because none of the two rev-list calls need to look
at more than a small part of the DAG. The first call returns a small
set of commits, and the last call is abandoned before it has time to
look far back in the DAG.
Signed-off-by: Karl Hasselström <redacted>
---
stgit/commands/commit.py | 8 -
stgit/commands/float.py | 2
stgit/commands/imprt.py | 2
stgit/commands/refresh.py | 2
stgit/commands/sync.py | 2
stgit/git.py | 5 +
stgit/stack.py | 333 +++++++++++++++++++++++++++++----------------
t/t4000-upgrade.sh | 6 +
8 files changed, 227 insertions(+), 133 deletions(-)
@@ -161,7 +161,7 @@ def func(parser, options, args):ifgit.local_changes(verbose=False):# index (cache) already updated by the git merge. The# backup information was already reset above-crt_series.refresh_patch(cache_update=False,backup=False,+crt_series.refresh_patch(p,cache_update=False,backup=False,log='sync')out.done('updated')else:
@@ -18,12 +18,13 @@ along with this program; if not, write to the Free SoftwareFoundation,Inc.,59TemplePlace,Suite330,Boston,MA02111-1307USA"""-importsys,os,re+importsys,os,popen2,refromstgit.utilsimport*fromstgitimportgit,basedir,templatesfromstgit.configimportconfigfromshutilimportcopyfile+fromsetsimportSet# stack exception class
@@ -274,7 +275,7 @@ class Patch(StgitObject):self.__update_log_ref(value)# The current StGIT metadata format version.-FORMAT_VERSION=2+FORMAT_VERSION=3defformat_version_key(branch):return'branch.%s.stgitformatversion'%branch
@@ -343,11 +344,173 @@ def update_to_current_format_version(branch, git_dir):rm(os.path.join(git_dir,'refs','bases',branch))set_format_version(2)+# Update 2 -> 3.+ifget_format_version()==2:+patchorder=file(os.path.join(branch_dir,'patchorder'),'w')+forpfin['applied','unapplied']:+pfn=os.path.join(branch_dir,pf)+ifnotos.path.isfile(pfn):+continue+forlineinfile(pfn):+line=line.strip()+ifline:+patchorder.write(line+'\n')+rm(pfn)+patchorder.close()+set_format_version(3)+# Make sure we're at the latest version.ifnotget_format_version()in[None,FORMAT_VERSION]:raiseStackException('Branch %s is at format version %d, expected %d'%(branch,get_format_version(),FORMAT_VERSION))+classPatchorderCache:+"""An object that keeps track of the patch order for a series, as+savedinitspatchorderfile."""+def__init__(self,series):+self.__series=series+self.__invalidate()+def__invalidate(self):+self.__patchnames=None+self.__position=None+def__cache(self):+ifself.__patchnames!=None:+return# already cached++self.__patchnames=[]+self.__position={}+pof=os.path.join(self.__series._dir(),'patchorder')+ifos.path.isfile(pof):+forlineinfile(pof):+name=line.strip()+assertnotnameinself.__position+self.__position[name]=len(self.__patchnames)+self.__patchnames.append(name)+defset_patchorder(self,new_order):+self.__invalidate()+f=file(os.path.join(self.__series._dir(),'patchorder'),'w')+fornameinnew_order:+f.write('%s\n'%name)+f.close()+defcmp(self,name1,name2):+"""Compare two patch names to see which patch comes first. If+bothpatchesarelistedinthepatchorderfile,sortthemby+theordertheyappearthere;ifoneislistedandtheother+not,thelistedpatchgoesfirst;andifneitherislisted,+sortthembytheirnames."""+self.__cache()+largepos=len(self.__patchnames)+pos1=self.__position.get(name1,largepos)+pos2=self.__position.get(name2,largepos)+returncmp((pos1,name1),(pos2,name2))++defread_refs(branch):+"""Return a mapping from patches and branch head to hashes for a+givenbranch.Thepatchesarelistedbyname;thebranchheadis+None."""+refs={}+patchpat=re.compile(r'^refs/patches/%s/([^\.]+)$'%branch)+forlineingit._output_lines('git-show-ref'):+sha1,ref=line.split()+m=re.match(patchpat,ref)+ifm:+refs[m.group(1)]=sha1+elifref=='refs/heads/%s'%branch:+refs[None]=sha1+returnrefs++defunapplied_patches(ref2hash):+"""Given a map of patch names (and the branch head, keyed by None)+tohashes,returnthesetofunappliedpatches."""+hash2refs={}+forr,hinref2hash.iteritems():+hash2refs.setdefault(h,Set()).add(r)++unapplied=Set()+forlineingit._output_lines(+'git-rev-list --stdin',+('%s%s\n'%(['','^'][ref==None],sha1)+forref,sha1inref2hash.iteritems())):+forrefinhash2refs.get(line.strip(),[]):+unapplied.add(ref)+returnunapplied++defsort_applied_patches(ref2hash):+"""Given a map of patch names (and the branch head, keyed by None)+tohashes,returnalistwiththeappliedpatchesinstackorder.+Allpatchesinthemapmustbeapplied."""+hash2refs={}+forr,hinref2hash.iteritems():+ifr!=None:+hash2refs.setdefault(h,Set()).add(r)++missing=Set(refforrefinref2hash.iterkeys()ifref!=None)+ifnotmissing:+return[]+applied=[]+grl=popen2.Popen3('git-rev-list %s'%ref2hash[None],True)+forlineingrl.fromchild:+forrefinhash2refs.get(line.strip(),[]):+applied.append(ref)+missing.remove(ref)+ifnotmissing:+applied.reverse()+returnapplied++raiseStackException,'Could not find patches: %s'%', '.join(missing)++classAppliedCache:+"""An object that keeps track of the appliedness and order of the+patchesinapatchseries."""+def__init__(self,series):+self.__series=series+self.__order=PatchorderCache(series)+self.__invalidate()+defget_applied(self):+self.__cache()+returnself.__applied+defget_unapplied(self):+self.__cache()+returnself.__unapplied+defrename(self,oldname,newname):+"""Rename a patch."""+self.__cache()+forlstin(self.__applied,self.__unapplied):+try:+lst[lst.index(oldname)]=newname+exceptValueError:+pass# lst.index() couldn't find the index+else:+self.__write_patchorder()+return+raiseStackException,'Unknown patch "%s"'%oldname+def__write_patchorder(self):+self.__order.set_patchorder(self.get_applied()+self.get_unapplied())+defset_patchorder(self,new_order):+self.__order.set_patchorder(new_order)+self.refresh()+defrefresh(self):+"""Re-read patch appliedness info, and write patch order to+disk."""+self.__invalidate()+self.__write_patchorder()+def__invalidate(self):+self.__applied=None+self.__unapplied=None+def__cached(self):+return(self.__applied!=None)+def__cache(self):+ifself.__cached():+return+patches=read_refs(self.__series.get_branch())+unapplied=unapplied_patches(patches)+forpatchinunapplied:+delpatches[patch]+self.__applied=sort_applied_patches(patches)+self.__unapplied=list(unapplied)+self.__unapplied.sort(self.__order.cmp)++classSeries(StgitObject):"""Class including the operations on series"""
@@ -372,8 +535,6 @@ class Series(StgitObject):self.__refs_dir=os.path.join(self.__base_dir,'refs','patches',self.__name)-self.__applied_file=os.path.join(self._dir(),'applied')-self.__unapplied_file=os.path.join(self._dir(),'unapplied')self.__hidden_file=os.path.join(self._dir(),'hidden')# where this series keeps its patches
@@ -382,6 +543,8 @@ class Series(StgitObject):# trash directoryself.__trash_dir=os.path.join(self._dir(),'trash')+self.__applied_cache=AppliedCache(self)+def__patch_name_valid(self,name):"""Raise an exception if the patch name is not valid."""
@@ -409,11 +572,7 @@ class Series(StgitObject):defget_current(self):"""Return the name of the topmost patch, or None if there isnosuchpatch."""-try:-applied=self.get_applied()-exceptStackException:-# No "applied" file: branch is not initialized.-returnNone+applied=self.get_applied()try:returnapplied[-1]exceptIndexError:
@@ -421,20 +580,10 @@ class Series(StgitObject):returnNonedefget_applied(self):-ifnotos.path.isfile(self.__applied_file):-raiseStackException,'Branch "%s" not initialised'%self.__name-f=file(self.__applied_file)-names=[line.strip()forlineinf.readlines()]-f.close()-returnnames+returnself.__applied_cache.get_applied()defget_unapplied(self):-ifnotos.path.isfile(self.__unapplied_file):-raiseStackException,'Branch "%s" not initialised'%self.__name-f=file(self.__unapplied_file)-names=[line.strip()forlineinf.readlines()]-f.close()-returnnames+returnself.__applied_cache.get_unapplied()defget_hidden(self):ifnotos.path.isfile(self.__hidden_file):
@@ -446,12 +595,12 @@ class Series(StgitObject):defget_base(self):# Return the parent of the bottommost patch, if there is one.-ifos.path.isfile(self.__applied_file):-bottommost=file(self.__applied_file).readline().strip()-ifbottommost:-returnself.get_patch(bottommost).get_bottom()-# No bottommost patch, so just return HEAD-returngit.get_head()+applied=self.get_applied()+ifapplied:+returnself.get_patch(applied[0]).get_bottom()+else:+# No bottommost patch, so just return HEAD+returngit.get_head()defget_head(self):"""Return the head of the branch
@@ -585,8 +734,6 @@ class Series(StgitObject):self.set_parent(parent_remote,parent_branch)-self.create_empty_field('applied')-self.create_empty_field('unapplied')os.makedirs(self.__refs_dir)self._set_field('orig-base',git.get_head())
@@ -687,10 +834,6 @@ class Series(StgitObject):# FIXME: find a way to get rid of those manual removals# (move functionality to StgitObject ?)-ifos.path.exists(self.__applied_file):-os.remove(self.__applied_file)-ifos.path.exists(self.__unapplied_file):-os.remove(self.__unapplied_file)ifos.path.exists(self.__hidden_file):os.remove(self.__hidden_file)ifos.path.exists(self._dir()+'/orig-base'):
@@ -719,7 +862,7 @@ class Series(StgitObject):config.unset('branch.%s.merge'%self.__name)config.unset('branch.%s.stgit.parentbranch'%self.__name)-defrefresh_patch(self,files=None,message=None,edit=False,+defrefresh_patch(self,name,files=None,message=None,edit=False,show_patch=False,cache_update=True,author_name=None,author_email=None,
@@ -728,10 +871,6 @@ class Series(StgitObject):backup=False,sign_str=None,log='refresh'):"""Generates a new commit for the given patch"""-name=self.get_current()-ifnotname:-raiseStackException,'No patches applied'-patch=self.get_patch(name)descr=patch.get_description()
@@ -821,9 +960,10 @@ class Series(StgitObject):"""Creates a new patch"""+appl,unappl=self.get_applied(),self.get_unapplied()ifname!=None:self.__patch_name_valid(name)-ifself.patch_applied(name)orself.patch_unapplied(name):+ifnameinapplornameinunappl:raiseStackException,'Patch "%s" already exists'%nameifnotmessageandcan_edit:
@@ -860,20 +1000,29 @@ class Series(StgitObject):ifunapplied:self.log_patch(patch,'new')--patches=[patch.get_name()]+self.get_unapplied()--f=file(self.__unapplied_file,'w+')-f.writelines([line+'\n'forlineinpatches])-f.close()+order=appl+[patch.get_name()]+unapplelifbefore_existing:self.log_patch(patch,'new')--insert_string(self.__applied_file,patch.get_name())+order=[patch.get_name()]+appl+unapplelse:-append_string(self.__applied_file,patch.get_name())+order=appl+[patch.get_name()]+unapplifrefresh:-self.refresh_patch(cache_update=False,log='new')+self.refresh_patch(name,cache_update=False,log='new')+self.__applied_cache.set_patchorder(order)++returnpatch+++defdelete_patch_data(self,name):+"""Deletes the stgit data for a patch."""+patch=Patch(name,self.__patch_dir,self.__refs_dir)++# save the commit id to a trash file+write_string(os.path.join(self.__trash_dir,name),patch.get_top())++patch.delete()+ifself.patch_hidden(name):+self.unhide_patch(name)returnpatch
@@ -881,9 +1030,8 @@ class Series(StgitObject):"""Deletes a patch"""self.__patch_name_valid(name)-patch=Patch(name,self.__patch_dir,self.__refs_dir)-ifself.__patch_is_current(patch):+ifself.get_current()==name:self.pop_patch(name)elifself.patch_applied(name):raiseStackException,'Cannot remove an applied patch, "%s", ' \
@@ -891,19 +1039,8 @@ class Series(StgitObject):elifnotnameinself.get_unapplied():raiseStackException,'Unknown patch "%s"'%name-# save the commit id to a trash file-write_string(os.path.join(self.__trash_dir,name),patch.get_top())--patch.delete()--unapplied=self.get_unapplied()-unapplied.remove(name)-f=file(self.__unapplied_file,'w+')-f.writelines([line+'\n'forlineinunapplied])-f.close()--ifself.patch_hidden(name):-self.unhide_patch(name)+self.delete_patch_data(name)+self.__applied_cache.refresh()defforward_patches(self,names):"""Try to fast-forward an array of patches.
@@ -967,19 +1104,12 @@ class Series(StgitObject):breakforwarded+=1-unapplied.remove(name)ifforwarded==0:return0git.switch(top)--append_strings(self.__applied_file,names[0:forwarded])--f=file(self.__unapplied_file,'w+')-f.writelines([line+'\n'forlineinunapplied])-f.close()-+self.__applied_cache.refresh()returnforwardeddefmerged_patches(self,names):
@@ -1052,13 +1182,6 @@ class Series(StgitObject):'Use "refresh" after fixing the conflicts or'' revert the operation with "push --undo".')-append_string(self.__applied_file,name)--unapplied.remove(name)-f=file(self.__unapplied_file,'w+')-f.writelines([line+'\n'forlineinunapplied])-f.close()-# head == bottom case doesn't need to refresh the patchifemptyorhead!=bottom:ifnotex:
@@ -1068,15 +1191,17 @@ class Series(StgitObject):log='push(m)'else:log='push'-self.refresh_patch(cache_update=False,log=log)+self.refresh_patch(name,cache_update=False,log=log)else:# we store the correctly merged files only for# tracking the conflict history. Note that the# git.merge() operations should always leave the index# in a valid state (i.e. only stage 0 files)-self.refresh_patch(cache_update=False,log='push(c)')+self.refresh_patch(name,cache_update=False,log='push(c)')raiseStackException,str(ex)+self.__applied_cache.refresh()+returnmodifieddefundo_push(self):
@@ -1105,10 +1230,7 @@ class Series(StgitObject):defpop_patch(self,name,keep=False):"""Pops the top patch from the stack"""-applied=self.get_applied()-applied.reverse()-assert(nameinapplied)-+assert(nameinself.get_applied())patch=self.get_patch(name)ifgit.get_head_file()==self.get_branch():
@@ -1118,24 +1240,7 @@ class Series(StgitObject):git.switch(patch.get_bottom(),keep)else:git.set_branch(self.get_branch(),patch.get_bottom())--# save the new applied list-idx=applied.index(name)+1--popped=applied[:idx]-popped.reverse()-unapplied=popped+self.get_unapplied()--f=file(self.__unapplied_file,'w+')-f.writelines([line+'\n'forlineinunapplied])-f.close()--delapplied[:idx]-applied.reverse()--f=file(self.__applied_file,'w+')-f.writelines([line+'\n'forlineinapplied])-f.close()+self.__applied_cache.refresh()defempty_patch(self,name):"""Returns True if the patch is empty
@@ -1161,7 +1266,8 @@ class Series(StgitObject):ifoldname==newname:raiseStackException,'"To" name and "from" name are the same'-+ifoldnameinappliedoroldnameinunapplied:+raiseStackException,'Unknown patch "%s"'%oldnameifnewnameinappliedornewnameinunapplied:raiseStackException,'Patch "%s" already exists'%newname
@@ -1169,23 +1275,8 @@ class Series(StgitObject):self.unhide_patch(oldname)self.hide_patch(newname)-ifoldnameinunapplied:-self.get_patch(oldname).rename(newname)-unapplied[unapplied.index(oldname)]=newname--f=file(self.__unapplied_file,'w+')-f.writelines([line+'\n'forlineinunapplied])-f.close()-elifoldnameinapplied:-self.get_patch(oldname).rename(newname)--applied[applied.index(oldname)]=newname--f=file(self.__applied_file,'w+')-f.writelines([line+'\n'forlineinapplied])-f.close()-else:-raiseStackException,'Unknown patch "%s"'%oldname+self.get_patch(oldname).rename(newname)+self.__applied_cache.rename(oldname,newname)deflog_patch(self,patch,message):"""Generate a log commit for a patch
@@ -34,6 +34,12 @@ for ver in 0.12 0.8; do!gitshow-ref--verify--quietrefs/bases/master'+test_expect_success\+"v$ver: Make sure the applied and unapplied files are gone"'+[!-e.git/patches/master/applied]&&+[!-e.git/patches/master/unapplied]+'+cd..done
@@ -0,0 +1,60 @@+#!/bin/sh+# Copyright (c) 2007 Karl Hasselström+test_description='Test git/StGIT interoperability'+../test-lib.sh++test_expect_success\+'Create some git-only history''+echofoo>foo.txt&&+gitaddfoo.txt&&+gitcommit-a-mfoo&&+gittagfoo-tag&&+foriin01234;do+echofoo$i>>foo.txt&&+gitcommit-a-mfoo$i;+done+'++test_expect_success\+'Initialize the StGIT repository''+stginit+'++test_expect_success\+'Create five patches''+foriin01234;do+stgnewp$i-mp$i;+done&&+["$(echo$(stgapplied))"="p0 p1 p2 p3 p4"]&&+["$(echo$(stgunapplied))"=""]+'++test_expect_success\+'Pop two patches with git-reset''+gitreset--hardHEAD~2&&+["$(echo$(stgapplied))"="p0 p1 p2"]&&+["$(echo$(stgunapplied))"="p3 p4"]+'++test_expect_success\+'Create a new patch''+stgnewq0-mq0&&+["$(echo$(stgapplied))"="p0 p1 p2 q0"]&&+["$(echo$(stgunapplied))"="p3 p4"]+'++test_expect_success\+'Go to an unapplied patch with with git-reset''+gitreset--hard$(stgidp3)&&+["$(echo$(stgapplied))"="p0 p1 p2 p3"]&&+["$(echo$(stgunapplied))"="q0 p4"]+'++test_expect_success\+'Go back to below the stack base with git-reset''+gitreset--hardfoo-tag&&+["$(echo$(stgapplied))"=""]&&+["$(echo$(stgunapplied))"="p0 p1 p2 q0 p3 p4"]+'++test_done
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:15
The bash tab completion used the "applied", "unapplied" and "current"
files to generate completions. Since these don't exist anymore, use
stg applied/unapplied/series to obtain the same info. It's a bit
slower, but not terribly much so.
Signed-off-by: Karl Hasselström <redacted>
---
contrib/stgit-completion.bash | 15 ++++-----------
1 files changed, 4 insertions(+), 11 deletions(-)
@@ -70,32 +70,25 @@ _current_branch ()# List of all applied patches. _applied_patches(){-localg=$(_gitdir)-["$g"]&&cat"$g/patches/$(_current_branch)/applied"+stgapplied2>/dev/null}# List of all unapplied patches. _unapplied_patches(){-localg=$(_gitdir)-["$g"]&&cat"$g/patches/$(_current_branch)/unapplied"+stgunapplied2>/dev/null}# List of all patches. _all_patches(){-localb=$(_current_branch)-localg=$(_gitdir)-["$g"]&&cat"$g/patches/$b/applied""$g/patches/$b/unapplied"+stgseries--noprefix2>/dev/null}# List of all patches except the current patch. _all_other_patches(){-localb=$(_current_branch)-localg=$(_gitdir)-["$g"]&&cat"$g/patches/$b/applied""$g/patches/$b/unapplied"\-|grep-v"^$(cat$g/patches/$b/current2>/dev/null)$"+stgseries2>/dev/null|grep-v'^>'|cut-f2-d' '} _all_branches()
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:15
The appliedness test was too slow if at least one patch, applied or
unapplied, was too far away from HEAD, since we had to visit the whole
intervening part of the commit DAG.
This patch fixes that problem by maintaining a cache of uninteresting
commits that are known to not reach any patches in the commit DAG.
(Specifically, this is at all times the set of commits that are
parents to patch commits and do not have a patch commit as their
ancestor.) By exlcuding these commits when walking the graph, we only
have to visit the interesting places.
As a nice side effect, the cache of uninteresting commits makes it
possible to use just one git-rev-list call instead of two, since we
can list the applied patches without first computing the unapplied
patches; the unapplied patches are then simply all patches except
those that are applied.
Signed-off-by: Karl Hasselström <redacted>
---
stgit/stack.py | 278 +++++++++++++++++++++++++++++++++++++++++++++-----------
1 files changed, 223 insertions(+), 55 deletions(-)
@@ -156,7 +156,7 @@ class Patch(StgitObject):os.mkdir(self._dir())self.create_empty_field('bottom')self.create_empty_field('top')-+defdelete(self):forfinos.listdir(self._dir()):os.remove(os.path.join(self._dir(),f))
@@ -369,7 +369,11 @@ class PatchorderCache:savedinitspatchorderfile."""def__init__(self,series):self.__series=series+self.__file=os.path.join(self.__series._dir(),'patchorder')self.__invalidate()+defdelete_file(self):+ifos.path.isfile(self.__file):+os.remove(self.__file)def__invalidate(self):self.__patchnames=Noneself.__position=None
@@ -379,9 +383,8 @@ class PatchorderCache:self.__patchnames=[]self.__position={}-pof=os.path.join(self.__series._dir(),'patchorder')-ifos.path.isfile(pof):-forlineinfile(pof):+ifos.path.isfile(self.__file):+forlineinfile(self.__file):name=line.strip()assertnotnameinself.__positionself.__position[name]=len(self.__patchnames)
@@ -404,60 +407,200 @@ class PatchorderCache:pos2=self.__position.get(name2,largepos)returncmp((pos1,name1),(pos2,name2))+classUninterestingCache:+"""Keeps track of a set of commits that do not reach any patches.+Theseareusedtospeedupthedetectionofunappliedpatches.++Specifically,thisisatalltimesthesetofcommitscthat+fulfillthefollowingtwocriteria:++*cdoesnotreachanypatch++*cistheparentofapatch++"""+def__init__(self,series):+self.__series=series+self.__uninteresting=None+self.__filename=os.path.join(self.__series._dir(),'uninteresting')+def__invalidate(self):+self.__uninteresting=None+self.delete_file()+defdelete_file(self):+ifos.path.isfile(self.__filename):+os.remove(self.__filename)+def__other_patches(self,patchname):+"""All patches except the named one."""+ref2hash=read_refs(self.__series.get_branch())+return[self.__series.get_patch(ref)+forrefinref2hash.iterkeys()+ifrefandref!=patchname]+def__write_file(self):+"""Write the uninteresting commits to file."""+try:+f=file(self.__filename,'w')+foruinself.__uninteresting:+f.write('%s\n'%u)+f.close()+exceptIOError:+pass# this isn't fatal -- the directory is probably missing+def__read_file(self):+"""Read the uninteresting commits from file. Return true on+success,falseonfailure."""+ifnotos.path.isfile(self.__filename):+returnFalse+self.__uninteresting=Set()+forlineinfile(self.__filename):+self.__uninteresting.add(line.strip())+returnTrue+def__cache_file(self):+"""Try to cache the uninteresting commits using only the cache+file.Returntrueonsuccess,falseonfailure."""+ifself.__uninteresting!=None:+returnTrue# already cached+returnself.__read_file()+def__cache(self):+"""Cache the uninteresting commits, recomputing them if+necessary."""+ifself.__cache_file():+return+self.__compute_uninteresting()+self.__write_file()+def__compute_uninteresting(self):+"""Compute a reasonable set of uninteresting commits from+scratch.Thisisexpensive."""+out.start('Finding uninteresting commits')+ref2hash=read_refs(self.__series.get_branch())+patches=Set([sha1forref,sha1inref2hash.iteritems()ifref])+interesting,uninteresting=Set(),Set()++# Iterate over all commits. We are guaranteed to see each+# commit before any of its children.+forlineingit._output_lines(+'git-rev-list --topo-order --reverse --parents --all'):+commits=line.split()+commit,parents=commits[0],Set(commits[1:])++# Patches are interesting.+ifcommitinpatches:+interesting.add(commit)++# The parents of a patch are uninteresting unless they+# are interesting.+forpinparents:+ifnotpininteresting:+uninteresting.add(p)+continue++# Commits with interesting parents are interesting.+ifinteresting.intersection(parents):+interesting.add(commit)+self.__uninteresting=uninteresting+out.done()+defcreate_patch(self,name,top,bottom):+"""The given patch has been created. Update the uninterested+statetomaintaintheinvariant."""+ifnotself.__cache_file():+return# not cached++# New patch inserted just below an existing bottommost patch:+# need to move the uninteresting commit down one step.+iftopinself.__uninteresting:+self.__uninteresting.remove(top)+self.__uninteresting.add(bottom)+self.__write_file()+return++# New patch inserted next to an existing non-bottommost patch:+# don't need to do anything.+existing_patches=self.__other_patches(name)+tops=Set([p.get_top()forpinexisting_patches])+bottoms=Set([p.get_bottom()forpinexisting_patches])+ifbottominbottomsorbottomintopsortopinbottoms:+return++# The new patch is not adjacent to an existing patch. We'd+# need to first get rid of any uninteresting commit that+# reaches this patch, and then mark the patch's bottom+# uninteresting if it doesn't reach any other patch. This is a+# lot of work, so we chicken out and blow the whole cache+# instead.+self.__invalidate()+defdelete_patch(self,name,top,bottom):+"""The given patch has been deleted. Update the uninterested+statetomaintaintheinvariant."""+ifnotself.__cache_file():+return# not cached++# If this patch reaches another patch, there's nothing to do.+ifnotbottominself.__uninteresting:+return++# If another patch has the same bottom, it's still+# uninteresting and there's nothing more to do.+other_patches=self.__other_patches(name)+forpinother_patches:+ifp.get_bottom()==bottom:+return++# If there are other patches on top of this one, their bottoms+# (this patch's top) become uninteresting in place of this+# patch's bottom.+forpinother_patches:+ifp.get_bottom()==top:+self.__uninteresting.remove(bottom)+self.__uninteresting.add(top)+self.__write_file()+return++# The bottom of this patch is no longer uninteresting. But+# there might be other patches that reach it, whose bottoms+# would need to be marked uninteresting. That would require an+# expensive reachability analysis.+self.__invalidate()+defget(self):+self.__cache()+returnself.__uninteresting+defread_refs(branch):"""Return a mapping from patches and branch head to hashes for agivenbranch.Thepatchesarelistedbyname;thebranchheadisNone."""refs={}patchpat=re.compile(r'^refs/patches/%s/([^\.]+)$'%branch)+head='refs/heads/%s'%branchforlineingit._output_lines('git-show-ref'):sha1,ref=line.split()m=re.match(patchpat,ref)ifm:refs[m.group(1)]=sha1-elifref=='refs/heads/%s'%branch:+elifref==head:refs[None]=sha1+ifnotNoneinrefs:+raiseStackException,'Could not find %s'%headreturnrefs-defunapplied_patches(ref2hash):+defget_patches(ref2hash,uninteresting):"""Given a map of patch names (and the branch head, keyed by None)-tohashes,returnthesetofunappliedpatches."""-hash2refs={}-forr,hinref2hash.iteritems():-hash2refs.setdefault(h,Set()).add(r)-+tohashes,returnthelistofappliedpatchesandthesetof+unappliedpatches.Thesecondparameterisasetofcommitobjects+thatdonotreachanypatch."""+applied=[]unapplied=Set()-forlineingit._output_lines(-'git-rev-list --stdin',-('%s%s\n'%(['','^'][ref==None],sha1)-forref,sha1inref2hash.iteritems())):-forrefinhash2refs.get(line.strip(),[]):-unapplied.add(ref)-returnunapplied--defsort_applied_patches(ref2hash):-"""Given a map of patch names (and the branch head, keyed by None)-tohashes,returnalistwiththeappliedpatchesinstackorder.-Allpatchesinthemapmustbeapplied."""-hash2refs={}+hash2patches={}forr,hinref2hash.iteritems():-ifr!=None:-hash2refs.setdefault(h,Set()).add(r)+ifr:+hash2patches.setdefault(h,Set()).add(r)+unapplied.add(r)-missing=Set(refforrefinref2hash.iterkeys()ifref!=None)-ifnotmissing:-return[]-applied=[]-grl=popen2.Popen3('git-rev-list %s'%ref2hash[None],True)-forlineingrl.fromchild:-forrefinhash2refs.get(line.strip(),[]):+forlineingit._output_lines(+'git-rev-list --topo-order --stdin',['%s\n'%ref2hash[None]]++['^%s\n'%uforuinuninteresting]):+forrefinhash2patches.get(line.strip(),[]):applied.append(ref)-missing.remove(ref)-ifnotmissing:-applied.reverse()-returnapplied--raiseStackException,'Could not find patches: %s'%', '.join(missing)+unapplied.remove(ref)+applied.reverse()+returnapplied,unappliedclassAppliedCache:"""An object that keeps track of the appliedness and order of the
@@ -465,7 +608,11 @@ class AppliedCache:def__init__(self,series):self.__series=seriesself.__order=PatchorderCache(series)+self.__uninteresting=UninterestingCache(series)self.__invalidate()+defdelete_files(self):+forsubin[self.__uninteresting,self.__order]:+sub.delete_file()defget_applied(self):self.__cache()returnself.__applied
@@ -484,6 +631,17 @@ class AppliedCache:self.__write_patchorder()returnraiseStackException,'Unknown patch "%s"'%oldname+defnew(self,name,top,bottom):+"""Create new patch."""+self.__uninteresting.create_patch(name,top,bottom)+defdelete(self,name,top,bottom):+"""Delete a patch."""+self.__uninteresting.delete_patch(name,top,bottom)+defchange(self,name,old_top,old_bottom,new_top,new_bottom):+"""Change a patch."""+if(new_top,new_bottom)!=(old_top,old_bottom):+self.new(name,new_top,new_bottom)+self.delete(name,old_top,old_bottom)def__write_patchorder(self):self.__order.set_patchorder(self.get_applied()+self.get_unapplied())defset_patchorder(self,new_order):
@@ -502,11 +660,8 @@ class AppliedCache:def__cache(self):ifself.__cached():return-patches=read_refs(self.__series.get_branch())-unapplied=unapplied_patches(patches)-forpatchinunapplied:-delpatches[patch]-self.__applied=sort_applied_patches(patches)+self.__applied,unapplied=get_patches(+read_refs(self.__series.get_branch()),self.__uninteresting.get())self.__unapplied=list(unapplied)self.__unapplied.sort(self.__order.cmp)
@@ -838,6 +993,7 @@ class Series(StgitObject):os.remove(self.__hidden_file)ifos.path.exists(self._dir()+'/orig-base'):os.remove(self._dir()+'/orig-base')+self.__applied_cache.delete_files()ifnotos.listdir(self.__patch_dir):os.rmdir(self.__patch_dir)
@@ -940,16 +1096,20 @@ class Series(StgitObject):patch=self.get_patch(name)old_bottom=patch.get_old_bottom()old_top=patch.get_old_top()+curr_bottom=patch.get_bottom()+curr_top=patch.get_top()# the bottom of the patch is not changed by refresh. If the# old_bottom is different, there wasn't any previous 'refresh'# command (probably only a 'push')-ifold_bottom!=patch.get_bottom()orold_top==patch.get_top():+ifold_bottom!=curr_bottomorold_top==curr_top:raiseStackException,'No undo information available'git.reset(tree_id=old_top,check_out=False)ifpatch.restore_old_boundaries():self.log_patch(patch,'undo')+self.__applied_cache.change(name,curr_top,curr_bottom,+old_top,old_bottom)defnew_patch(self,name,message=None,can_edit=True,unapplied=False,show_patch=False,
@@ -982,14 +1142,11 @@ class Series(StgitObject):patch=self.get_patch(name)patch.create()-ifbottom:-patch.set_bottom(bottom)-else:-patch.set_bottom(head)-iftop:-patch.set_top(top)-else:-patch.set_top(head)+bottom=bottomorhead+top=toporhead+patch.set_bottom(bottom)+patch.set_top(top)+self.__applied_cache.new(name,top,bottom)patch.set_description(descr)patch.set_authname(author_name)
@@ -1016,15 +1173,16 @@ class Series(StgitObject):defdelete_patch_data(self,name):"""Deletes the stgit data for a patch."""patch=Patch(name,self.__patch_dir,self.__refs_dir)+top,bottom=patch.get_top(),patch.get_bottom()# save the commit id to a trash file-write_string(os.path.join(self.__trash_dir,name),patch.get_top())+write_string(os.path.join(self.__trash_dir,name),top)patch.delete()ifself.patch_hidden(name):self.unhide_patch(name)-returnpatch+self.__applied_cache.delete(name,top,bottom)defdelete_patch(self,name):"""Deletes a patch
@@ -1084,6 +1242,7 @@ class Series(StgitObject):top_tree=git.get_commit(top).get_tree()+old_top=toptop=git.commit(message=descr,parents=[head],cache_update=False,tree_id=top_tree,
@@ -1097,6 +1256,9 @@ class Series(StgitObject):patch.set_bottom(head,backup=True)patch.set_top(top,backup=True)+self.__applied_cache.change(+name,old_top=old_top,old_bottom=bottom,+new_top=top,new_bottom=head)self.log_patch(patch,'push(f)')else:top=head
@@ -1154,6 +1316,7 @@ class Series(StgitObject):# need an empty commitpatch.set_bottom(head,backup=True)patch.set_top(head,backup=True)+self.__applied_cache.change(name,top,bottom,head,head)modified=Trueelifhead==bottom:# reset the backup information. No need for logging
@@ -1166,6 +1329,7 @@ class Series(StgitObject):# The current patch is empty after merge.patch.set_bottom(head,backup=True)patch.set_top(head,backup=True)+self.__applied_cache.change(name,top,bottom,head,head)# Try the fast applying first. If this fails, fall back to the# three-way merge
@@ -1211,6 +1375,8 @@ class Series(StgitObject):patch=self.get_patch(name)old_bottom=patch.get_old_bottom()old_top=patch.get_old_top()+curr_bottom=patch.get_bottom()+curr_top=patch.get_top()# the top of the patch is changed by a push operation only# together with the bottom (otherwise the top was probably
@@ -1222,6 +1388,8 @@ class Series(StgitObject):git.reset()self.pop_patch(name)ret=patch.restore_old_boundaries()+self.__applied_cache.change(name,curr_top,curr_bottom,+old_top,old_bottom)ifret:self.log_patch(patch,'undo')
Hi Karl,
On Sun, Jun 10, 2007 at 02:54:47AM -0700, Karl Hasselström wrote:
This took a while, but here it is. (Actually, I finished this about a
week ago, but had no Internet connection so I couldn't send it.)
Is this the latest version of the DAG patches, or is there maybe a
public repo where you push your work ?
It happens that my refactorings touches virtually everything, so there
will be conflicts, and the best thing to do is probably that I rebase
my work on yours.
Best regards,
--
Yann
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:19
On 2007-06-30 21:54:51 +0200, Yann Dirson wrote:
On Sun, Jun 10, 2007 at 02:54:47AM -0700, Karl Hasselström wrote:
quoted
This took a while, but here it is. (Actually, I finished this
about a week ago, but had no Internet connection so I couldn't
send it.)
Is this the latest version of the DAG patches, or is there maybe a
public repo where you push your work ?
This is the latest version, and no, I don't yet have a public repo
that I push this stuff to.
I've been travelling a lot the last few weeks, and I'm not quite done
yet, so I haven't had time to even follow the mailing list properly,
but after that I plan to start a pu-ish (rebasing) integration branch
for the patches that I, you, and others post to the list.
It happens that my refactorings touches virtually everything,
I noticed. :-)
so there will be conflicts,
:-)
and the best thing to do is probably that I rebase my work on yours.
Thanks for the vote of confidence. Please go ahead; as I said, you
already have the latest version of my series, and it may be a while
yet before I have much time to burn on StGIT.
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle