From: David Kågedal <hidden> Date: 2016-06-15 22:43:34
The following series removes the 'bottom' and 'top' files for each
patch, and instead uses the commit objects to keep track of the
patches.
The patches are based on kha/safe
24a81d7a94cd7c9ad2fc741b0179db5b830cce78 and will conflict with the
'conflict' series in at least one place.
The first ten patches are actually only cleanups and refactoring that
could go in regardless of the last ones. Some of the changes are not
really necessary, but I did them while digging into the the code, and
believe they are improvements. They should not change the way stg
behaves in any way.
The eleventh one is just a sanity check before applying the last two
that removes the top and bottom files.
The last two patches do the final cleansing. Obviously, this changes
the format, and the format version should be increased and and update
function be written. So it's not really ready to go in yet.
Also, there are some documentation changes not included in this.
Maybe I should have sent the first ten patches separately, but this
makes it clearer why I did it.
---
David Kågedal (13):
Remove the 'top' field
Remove the 'bottom' field
Check bottom and invariants
Refactor Series.new_patch
Clear up the semantics of Series.new_patch
Add a 'bottom' parameter to Series.refresh_patch and use it
Clean up Series.refresh_patch
Refactor Series.push_patch
Remove dead code from push_empty_patch
Split Series.push_patch in two
Moved that status function to the status command file
Clear up semantics of tree_status
Add some more tests of "stg status" output
stgit/commands/common.py | 2
stgit/commands/status.py | 42 +++++++++-
stgit/commands/sync.py | 1
stgit/commands/uncommit.py | 1
stgit/git.py | 61 +++-----------
stgit/stack.py | 189 ++++++++++++++++++++++----------------------
t/t0002-status.sh | 36 ++++++++
7 files changed, 185 insertions(+), 147 deletions(-)
--
Signature
@@ -85,6 +85,12 @@ test_expect_success 'Status after refresh' 'diff-uexpected.txtoutput.txt'+test_expect_success'Add another file''+echolajbans>fie&&+stgaddfie&&+stgrefresh+'+ test_expect_success'Make a conflicting patch''stgpop&&stgnew-m"third patch"&&
@@ -105,6 +111,28 @@ test_expect_success 'Status after conflicting push' '' cat>expected.txt<<EOF+Cfoo/bar+EOF+test_expect_success'Status of file''+stgstatusfoo/bar>output.txt&&+diff-uexpected.txtoutput.txt+'++cat>expected.txt<<EOF+EOF+test_expect_success'Status of dir''+stgstatusfoo>output.txt&&+diff-uexpected.txtoutput.txt+'++cat>expected.txt<<EOF+EOF+test_expect_success'Status of other file''+stgstatusfie>output.txt&&+diff-uexpected.txtoutput.txt+'++cat>expected.txt<<EOF Mfoo/bar EOF test_expect_success'Status after resolving the push''
From: David Kågedal <hidden> Date: 2016-06-15 22:43:34
The git.status() was more of a command than a library function, and
was only used in one place.
Signed-off-by: David Kågedal <redacted>
---
stgit/commands/status.py | 39 ++++++++++++++++++++++++++++++++++++---
stgit/git.py | 33 ---------------------------------
t/t0002-status.sh | 8 ++++++++
3 files changed, 44 insertions(+), 36 deletions(-)
@@ -65,6 +65,39 @@ options = [make_option('-m', '--modified',action='store_true')]+defstatus(files=None,modified=False,new=False,deleted=False,+conflict=False,unknown=False,noexclude=False,+diff_flags=[]):+"""Show the tree status+"""+cache_files=git.tree_status(files,+unknown=(files==None),+noexclude=noexclude,+diff_flags=diff_flags)+filtered=(modifiedornewordeletedorconflictorunknown)++iffiltered:+filestat=[]+ifmodified:+filestat.append('M')+ifnew:+filestat.append('A')+filestat.append('N')+ifdeleted:+filestat.append('D')+ifconflict:+filestat.append('C')+ifunknown:+filestat.append('?')+cache_files=[xforxincache_filesifx[0]infilestat]++forfsincache_files:+assertfiles==Noneorfs[1]infiles+ifnotfiltered:+out.stdout('%s%s'%(fs[0],fs[1]))+else:+out.stdout('%s'%fs[1])+deffunc(parser,options,args):"""Show the tree status"""
@@ -85,6 +118,6 @@ def func(parser, options, args):# No args means all filesifnotargs:args=None-git.status(args,options.modified,options.new,options.deleted,-options.conflict,options.unknown,options.noexclude,-diff_flags=diff_flags)+status(args,options.modified,options.new,options.deleted,+options.conflict,options.unknown,options.noexclude,+diff_flags=diff_flags)
@@ -711,39 +711,6 @@ def merge(base, head1, head2, recursive = False):iferrors:raiseGitException,'GIT index merging failed (possible conflicts)'-defstatus(files=None,modified=False,new=False,deleted=False,-conflict=False,unknown=False,noexclude=False,-diff_flags=[]):-"""Show the tree status-"""-cache_files=tree_status(files,-unknown=(files==None),-noexclude=noexclude,-diff_flags=diff_flags)-filtered=(modifiedornewordeletedorconflictorunknown)--iffiltered:-filestat=[]-ifmodified:-filestat.append('M')-ifnew:-filestat.append('A')-filestat.append('N')-ifdeleted:-filestat.append('D')-ifconflict:-filestat.append('C')-ifunknown:-filestat.append('?')-cache_files=[xforxincache_filesifx[0]infilestat]--forfsincache_files:-assertfiles==Noneorfs[1]infiles-ifnotfiltered:-out.stdout('%s%s'%(fs[0],fs[1]))-else:-out.stdout('%s'%fs[1])-defdiff(files=None,rev1='HEAD',rev2=None,diff_flags=[]):"""Show the diff between rev1 and rev2"""
@@ -60,6 +60,14 @@ test_expect_success 'Status with an added file' '' cat>expected.txt<<EOF+foo/bar+EOF+test_expect_success'Status with an added file and -n option''+stgstatus-n>output.txt&&+diff-uexpected.txtoutput.txt+'++cat>expected.txt<<EOF EOF test_expect_success'Status after refresh''stgnew-m"first patch"&&
From: David Kågedal <hidden> Date: 2016-06-15 22:43:34
Since the split from push_patch, the push_empty_patch contains some
code that fills no purpose. Remove it and simplify the code.
Signed-off-by: David Kågedal <redacted>
---
stgit/stack.py | 33 +++------------------------------
1 files changed, 3 insertions(+), 30 deletions(-)
@@ -1029,46 +1029,19 @@ class Series(PatchSet):assert(nameinunapplied)patch=self.get_patch(name)-head=git.get_head()-bottom=patch.get_bottom()-top=patch.get_top()--ex=None-modified=False-# top != bottom always since we have a commit for each patch-# just make an empty patch (top = bottom = HEAD). This-# option is useful to allow undoing already merged-# patches. The top is updated by refresh_patch since we-# need an empty commit+# The top is updated by refresh_patch since we need an empty+# commitpatch.set_bottom(head,backup=True)patch.set_top(head,backup=True)-modified=Trueappend_string(self.__applied_file,name)unapplied.remove(name)write_strings(self.__unapplied_file,unapplied)-# head == bottom case doesn't need to refresh the patch-ifnotex:-# if the merge was OK and no conflicts, just refresh the patch-# The GIT cache was already updated by the merge operation-ifmodified:-log='push(m)'-else:-log='push'-self.refresh_patch(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)')-raiseStackException,str(ex)--returnmodified+self.refresh_patch(cache_update=False,log='push(m)')defpush_patch(self,name):"""Pushes a patch on the stack
From: David Kågedal <hidden> Date: 2016-06-15 22:43:34
The tree_status() function does a few slightly different things
depending on the arguments. This patch adds checks that the arguments
are consistent and that the returned value looks good.
It also changes the semantics slightly. If the 'files' parameter is
None, it will run status on all files. If 'files' is a list, it will
run status on only those files. This changes two things:
1) If 'files' is the empty list, it will run status on no files.
2) It 'files' is a list, it will never return the status for other files
Clearing this up will make it easier to understand code that is using
this function.
Signed-off-by: David Kågedal <redacted>
---
stgit/commands/status.py | 3 +++
stgit/git.py | 44 +++++++++++++++++++++++---------------------
2 files changed, 26 insertions(+), 21 deletions(-)
@@ -82,6 +82,9 @@ def func(parser, options, args):else:diff_flags=[]+# No args means all files+ifnotargs:+args=Nonegit.status(args,options.modified,options.new,options.deleted,options.conflict,options.unknown,options.noexclude,diff_flags=diff_flags)
@@ -167,15 +167,20 @@ def exclude_files():deftree_status(files=None,tree_id='HEAD',unknown=False,noexclude=True,verbose=False,diff_flags=[]):-"""Returns a list of pairs - (status, filename)+"""Get the status of all changed files, or of a selected set of+files.Returnsalistofpairs-(status,filename).++If'files'isNone,itwillcheckallfiles,andoptionallyall+unknownfiles.If'files'isalist,itwillonlycheckthefiles+inthelist."""+assertfiles==Noneornotunknown+ifverbose:out.start('Checking for changes in the working directory')refresh_index()-ifnotfiles:-files=[]cache_files=[]# unknown files
@@ -538,9 +547,6 @@ def committer():defupdate_cache(files=None,force=False):"""Update the cache information for the given files"""-ifnotfiles:-files=[]-cache_files=tree_status(files,verbose=False)# everything is up-to-date
@@ -569,8 +575,6 @@ def commit(message, files = None, parents = None, allowempty = False,committer_name=None,committer_email=None):"""Commit the current tree to repository"""-ifnotfiles:-files=[]ifnotparents:parents=[]
@@ -712,14 +716,13 @@ def status(files = None, modified = False, new = False, deleted = False,diff_flags=[]):"""Show the tree status"""-ifnotfiles:-files=[]--cache_files=tree_status(files,unknown=True,noexclude=noexclude,-diff_flags=diff_flags)-all=not(modifiedornewordeletedorconflictorunknown)+cache_files=tree_status(files,+unknown=(files==None),+noexclude=noexclude,+diff_flags=diff_flags)+filtered=(modifiedornewordeletedorconflictorunknown)-ifnotall:+iffiltered:filestat=[]ifmodified:filestat.append('M')
From: David Kågedal <hidden> Date: 2016-06-15 22:43:34
The push_patch() function has a complex control flow and actually does
two different things depending on the 'empty' parameter. This patch
splits in in two functions without other code changes.
Later patches will refactor the code to simplify it.
Signed-off-by: David Kågedal <redacted>
---
stgit/commands/common.py | 2 +
stgit/stack.py | 62 ++++++++++++++++++++++++++++++++++++++--------
2 files changed, 52 insertions(+), 12 deletions(-)
@@ -1022,7 +1022,55 @@ class Series(PatchSet):returnmerged-defpush_patch(self,name,empty=False):+defpush_empty_patch(self,name):+"""Pushes an empty patch on the stack+"""+unapplied=self.get_unapplied()+assert(nameinunapplied)++patch=self.get_patch(name)++head=git.get_head()+bottom=patch.get_bottom()+top=patch.get_top()++ex=None+modified=False++# top != bottom always since we have a commit for each patch+# just make an empty patch (top = bottom = HEAD). This+# option is useful to allow undoing already merged+# patches. The top is updated by refresh_patch since we+# need an empty commit+patch.set_bottom(head,backup=True)+patch.set_top(head,backup=True)+modified=True++append_string(self.__applied_file,name)++unapplied.remove(name)+write_strings(self.__unapplied_file,unapplied)++# head == bottom case doesn't need to refresh the patch+ifnotex:+# if the merge was OK and no conflicts, just refresh the patch+# The GIT cache was already updated by the merge operation+ifmodified:+log='push(m)'+else:+log='push'+self.refresh_patch(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)')+raiseStackException,str(ex)++returnmodified++defpush_patch(self,name):"""Pushes a patch on the stack"""unapplied=self.get_unapplied()
@@ -1038,15 +1086,7 @@ class Series(PatchSet):modified=False# top != bottom always since we have a commit for each patch-ifempty:-# just make an empty patch (top = bottom = HEAD). This-# option is useful to allow undoing already merged-# patches. The top is updated by refresh_patch since we-# need an empty commit-patch.set_bottom(head,backup=True)-patch.set_top(head,backup=True)-modified=True-elifhead==bottom:+ifhead==bottom:# reset the backup information. No need for loggingpatch.set_bottom(bottom,backup=True)patch.set_top(top,backup=True)
@@ -1079,7 +1119,7 @@ class Series(PatchSet):write_strings(self.__unapplied_file,unapplied)# head == bottom case doesn't need to refresh the patch-ifemptyorhead!=bottom:+ifhead!=bottom:ifnotex:# if the merge was OK and no conflicts, just refresh the patch# The GIT cache was already updated by the merge operation
From: David Kågedal <hidden> Date: 2016-06-15 22:43:34
Refactor the Series.push_patch function to make the code flow
simpler. It identifies the simple case and handles it early, and
reduces the number of if statements.
Most changes are simply indentation changes.
Signed-off-by: David Kågedal <redacted>
---
stgit/stack.py | 87 ++++++++++++++++++++++++++++++--------------------------
1 files changed, 46 insertions(+), 41 deletions(-)
@@ -1054,60 +1054,65 @@ class Series(PatchSet):head=git.get_head()bottom=patch.get_bottom()top=patch.get_top()--ex=None-modified=False-# top != bottom always since we have a commit for each patch+ifhead==bottom:-# reset the backup information. No need for logging+# A fast-forward push. Just reset the backup+# information. No need for loggingpatch.set_bottom(bottom,backup=True)patch.set_top(top,backup=True)git.switch(top)-else:-# new patch needs to be refreshed.-# The current patch is empty after merge.-patch.set_bottom(head,backup=True)-patch.set_top(head,backup=True)--# Try the fast applying first. If this fails, fall back to the-# three-way merge-ifnotgit.apply_diff(bottom,top):-# if git.apply_diff() fails, the patch requires a diff3-# merge and can be reported as modified-modified=True--# merge can fail but the patch needs to be pushed-try:-git.merge(bottom,head,top,recursive=True)-exceptgit.GitException,ex:-out.error('The merge failed during "push".',-'Use "refresh" after fixing the conflicts or'-' revert the operation with "push --undo".')+append_string(self.__applied_file,name)++unapplied.remove(name)+write_strings(self.__unapplied_file,unapplied)+returnFalse++# Need to create a new commit an merge in the old patch+ex=None+modified=False++# new patch needs to be refreshed.+# The current patch is empty after merge.+patch.set_bottom(head,backup=True)+patch.set_top(head,backup=True)++# Try the fast applying first. If this fails, fall back to the+# three-way merge+ifnotgit.apply_diff(bottom,top):+# if git.apply_diff() fails, the patch requires a diff3+# merge and can be reported as modified+modified=True++# merge can fail but the patch needs to be pushed+try:+git.merge(bottom,head,top,recursive=True)+exceptgit.GitException,ex:+out.error('The merge failed during "push".',+'Use "refresh" after fixing the conflicts or'+' revert the operation with "push --undo".')append_string(self.__applied_file,name)unapplied.remove(name)write_strings(self.__unapplied_file,unapplied)-# head == bottom case doesn't need to refresh the patch-ifhead!=bottom:-ifnotex:-# if the merge was OK and no conflicts, just refresh the patch-# The GIT cache was already updated by the merge operation-ifmodified:-log='push(m)'-else:-log='push'-self.refresh_patch(cache_update=False,log=log)+ifnotex:+# if the merge was OK and no conflicts, just refresh the patch+# The GIT cache was already updated by the merge operation+ifmodified:+log='push(m)'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)')-raiseStackException,str(ex)+log='push'+self.refresh_patch(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)')+raiseStackException,str(ex)returnmodified
From: David Kågedal <hidden> Date: 2016-06-15 22:43:34
This patch does some minor simplifications of the code and updates the
documentation string of Series.refresh_patch.
Signed-off-by: David Kågedal <redacted>
---
stgit/stack.py | 10 ++++------
1 files changed, 4 insertions(+), 6 deletions(-)
@@ -749,14 +749,12 @@ class Series(PatchSet):committer_name=None,committer_email=None,backup=False,sign_str=None,log='refresh',notes=None):-"""Generates a new commit for the given patch+"""Generates a new commit for the topmost patch"""-name=self.get_current()-ifnotname:+patch=self.get_current_patch()+ifnotpatch:raiseStackException,'No patches applied'-patch=self.get_patch(name)-descr=patch.get_description()ifnot(messageordescr):edit=True
@@ -767,7 +765,7 @@ class Series(PatchSet):ifnotmessageandedit:descr=edit_file(self,descr.rstrip(), \'Please edit the description for patch "%s" ' \-'above.'%name,show_patch)+'above.'%patch.get_name(),show_patch)ifnotauthor_name:author_name=patch.get_authname()
From: David Kågedal <hidden> Date: 2016-06-15 22:43:34
By specifying a bottom for the new patch commit, it is no longer
necessary to update the bottom of the patch before calling
refresh_patch. This ensures that the patch top always correspond to a
commit object, and the bottom to its parent.
Signed-off-by: David Kågedal <redacted>
---
stgit/stack.py | 24 ++++++++----------------
1 files changed, 8 insertions(+), 16 deletions(-)
@@ -748,7 +748,7 @@ class Series(PatchSet):author_date=None,committer_name=None,committer_email=None,backup=False,sign_str=None,log='refresh',-notes=None):+notes=None,bottom=None):"""Generates a new commit for the topmost patch"""patch=self.get_current_patch()
@@ -780,7 +780,8 @@ class Series(PatchSet):descr=add_sign_line(descr,sign_str,committer_name,committer_email)-bottom=patch.get_bottom()+ifnotbottom:+bottom=patch.get_bottom()commit_id=git.commit(files=files,message=descr,parents=[bottom],
@@ -1026,20 +1027,15 @@ class Series(PatchSet):unapplied=self.get_unapplied()assert(nameinunapplied)-patch=self.get_patch(name)+# patch = self.get_patch(name)head=git.get_head()-# The top is updated by refresh_patch since we need an empty-# commit-patch.set_bottom(head,backup=True)-patch.set_top(head,backup=True)-append_string(self.__applied_file,name)unapplied.remove(name)write_strings(self.__unapplied_file,unapplied)-self.refresh_patch(cache_update=False,log='push(m)')+self.refresh_patch(bottom=head,cache_update=False,log='push(m)')defpush_patch(self,name):"""Pushes a patch on the stack
@@ -1071,11 +1067,6 @@ class Series(PatchSet):ex=Nonemodified=False-# new patch needs to be refreshed.-# The current patch is empty after merge.-patch.set_bottom(head,backup=True)-patch.set_top(head,backup=True)-# Try the fast applying first. If this fails, fall back to the# three-way mergeifnotgit.apply_diff(bottom,top):
@@ -1103,13 +1094,14 @@ class Series(PatchSet):log='push(m)'else:log='push'-self.refresh_patch(cache_update=False,log=log)+self.refresh_patch(bottom=head,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(bottom=head,cache_update=False,+log='push(c)')raiseStackException,str(ex)returnmodified
From: David Kågedal <hidden> Date: 2016-06-15 22:43:34
The bottom is instead always calculated from the top by getting its
parent commit.
Signed-off-by: David Kågedal <redacted>
---
stgit/commands/sync.py | 1 -
stgit/stack.py | 29 +++--------------------------
2 files changed, 3 insertions(+), 27 deletions(-)
@@ -148,7 +148,6 @@ def func(parser, options, args):# reset the patch backup information. That's needed in case we# undo the sync but there were no changes made-patch.set_bottom(bottom,backup=True)patch.set_top(top,backup=True)# the actual merging (either from a branch or an external file)
@@ -158,7 +158,6 @@ class Patch(StgitObject):defcreate(self):os.mkdir(self._dir())-self.create_empty_field('bottom')self.create_empty_field('top')defdelete(self):
@@ -197,22 +196,10 @@ class Patch(StgitObject):self.__update_top_ref(top)defget_old_bottom(self):-old_bottom=self._get_field('bottom.old')-old_top=self.get_old_top()-assertold_bottom==git.get_commit(old_top).get_parent()-returnold_bottom+returngit.get_commit(self.get_old_top()).get_parent()defget_bottom(self):-bottom=self._get_field('bottom')-top=self.get_top()-assertbottom==git.get_commit(top).get_parent()-returnself._get_field('bottom')--defset_bottom(self,value,backup=False):-ifbackup:-curr=self._get_field('bottom')-self._set_field('bottom.old',curr)-self._set_field('bottom',value)+returngit.get_commit(self.get_top()).get_parent()defget_old_top(self):returnself._get_field('top.old')
@@ -232,14 +219,11 @@ class Patch(StgitObject):self._set_field('top.old',curr)self._set_field('top',value)self.__update_top_ref(value)-self.get_bottom()# check the assertdefrestore_old_boundaries(self):-bottom=self._get_field('bottom.old')top=self._get_field('top.old')-iftopandbottom:-self._set_field('bottom',bottom)+iftop:self._set_field('top',top)self.__update_top_ref(top)returnTrue
@@ -806,7 +790,6 @@ class Series(PatchSet):committer_name=committer_name,committer_email=committer_email)-patch.set_bottom(bottom,backup=backup)patch.set_top(commit_id,backup=backup)patch.set_description(descr)patch.set_authname(author_name)
@@ -914,11 +897,8 @@ class Series(PatchSet):committer_name=committer_name,committer_email=committer_email)# set the patch top to the new commit-patch.set_bottom(bottom)patch.set_top(commit_id)else:-asserttop!=bottom-patch.set_bottom(bottom)patch.set_top(top)self.log_patch(patch,'new')
@@ -972,7 +952,6 @@ class Series(PatchSet):ifhead==bottom:# reset the backup information. No logging since the# patch hasn't changed-patch.set_bottom(head,backup=True)patch.set_top(top,backup=True)else:
@@ -1000,7 +979,6 @@ class Series(PatchSet):committer_name=committer_name,committer_email=committer_email)-patch.set_bottom(head,backup=True)patch.set_top(top,backup=True)self.log_patch(patch,'push(f)')
@@ -1073,7 +1051,6 @@ class Series(PatchSet):ifhead==bottom:# A fast-forward push. Just reset the backup# information. No need for logging-patch.set_bottom(bottom,backup=True)patch.set_top(top,backup=True)git.switch(top)
From: David Kågedal <hidden> Date: 2016-06-15 22:43:34
This shuffles some code so that the top and bottom never need to be
set to anything other than a valid commit and its parent.
Signed-off-by: David Kågedal <redacted>
---
stgit/stack.py | 17 +++++++++--------
1 files changed, 9 insertions(+), 8 deletions(-)
@@ -864,13 +864,6 @@ class Series(PatchSet):patch=self.get_patch(name)patch.create()-ifnotbottom:-bottom=head-ifnottop:-top=head--patch.set_bottom(bottom)-patch.set_top(top)patch.set_description(descr)patch.set_authname(author_name)patch.set_authemail(author_email)
@@ -889,10 +882,15 @@ class Series(PatchSet):set_head=Trueifcommit:+iftop:+top_commit=git.get_commit(top)+else:+bottom=head+top_commit=git.get_commit(head)+# create a commit for the patch (may be empty if top == bottom);# only commit on top of the current branchassert(unappliedorbottom==head)-top_commit=git.get_commit(top)commit_id=git.commit(message=descr,parents=[bottom],cache_update=False,tree_id=top_commit.get_tree(),
@@ -903,9 +901,12 @@ class Series(PatchSet):committer_name=committer_name,committer_email=committer_email)# set the patch top to the new commit+patch.set_bottom(bottom)patch.set_top(commit_id)else:asserttop!=bottom+patch.set_bottom(bottom)+patch.set_top(top)self.log_patch(patch,'new')
From: David Kågedal <hidden> Date: 2016-06-15 22:43:34
This code adds some checks that the bottom is actually always the
parent of top.
It also checks that the top is the same as what the patch ref points
to.
This is only do to ensure that the next patches are correct.
Signed-off-by: David Kågedal <redacted>
---
stgit/stack.py | 17 +++++++++++++++--
1 files changed, 15 insertions(+), 2 deletions(-)
@@ -197,9 +197,15 @@ class Patch(StgitObject):self.__update_top_ref(top)defget_old_bottom(self):-returnself._get_field('bottom.old')+old_bottom=self._get_field('bottom.old')+old_top=self.get_old_top()+assertold_bottom==git.get_commit(old_top).get_parent()+returnold_bottomdefget_bottom(self):+bottom=self._get_field('bottom')+top=self.get_top()+assertbottom==git.get_commit(top).get_parent()returnself._get_field('bottom')defset_bottom(self,value,backup=False):
@@ -212,7 +218,13 @@ class Patch(StgitObject):returnself._get_field('top.old')defget_top(self):-returnself._get_field('top')+top=self._get_field('top')+try:+ref=git.rev_parse(self.__top_ref)+except:+ref=None+assertnotrefortop==ref+returntopdefset_top(self,value,backup=False):ifbackup:
@@ -220,6 +232,7 @@ class Patch(StgitObject):self._set_field('top.old',curr)self._set_field('top',value)self.__update_top_ref(value)+self.get_bottom()# check the assertdefrestore_old_boundaries(self):bottom=self._get_field('bottom.old')
From: David Kågedal <hidden> Date: 2016-06-15 22:43:34
This patch adds a number of assertions to document and verify the
complex restrictions of the input parameters to the Series.new_patch
function. It also adds the requirement that 'before_existing' and
'commit' cannot be true at the same time when calling it, instead of
updating 'commit' inside the function.
Signed-off-by: David Kågedal <redacted>
---
stgit/commands/uncommit.py | 1 +
stgit/stack.py | 14 ++++++++++----
2 files changed, 11 insertions(+), 4 deletions(-)
@@ -833,9 +833,16 @@ class Series(PatchSet):author_name=None,author_email=None,author_date=None,committer_name=None,committer_email=None,before_existing=False):-"""Creates a new patch+"""Creates a new patch, either pointing to an existing commit object,+orbycreatinganewcommitobject."""+assertcommitor(topandbottom)+assertnotbefore_existingor(topandbottom)+assertnot(commitandbefore_existing)+assert(topandbottom)or(nottopandnotbottom)+assertnottopor(bottom==git.get_commit(top).get_parent())+ifname!=None:self.__patch_name_valid(name)ifself.patch_exists(name):
@@ -873,9 +880,6 @@ class Series(PatchSet):ifbefore_existing:insert_string(self.__applied_file,patch.get_name())-# no need to commit anything as the object is already-# present (mainly used by 'uncommit')-commit=Falseelifunapplied:patches=[patch.get_name()]+self.get_unapplied()write_strings(self.__unapplied_file,patches)
@@ -900,6 +904,8 @@ class Series(PatchSet):committer_email=committer_email)# set the patch top to the new commitpatch.set_top(commit_id)+else:+asserttop!=bottomself.log_patch(patch,'new')
From: David Kågedal <hidden> Date: 2016-06-15 22:43:34
The top is instead implicitly defined by the patch ref.
Signed-off-by: David Kågedal <redacted>
---
stgit/stack.py | 26 +++++++++-----------------
1 files changed, 9 insertions(+), 17 deletions(-)
@@ -60,6 +60,14 @@ test_expect_success 'Status with an added file' '' cat>expected.txt<<EOF+foo/bar+EOF+test_expect_success'Status with an added file and -n option''+stgstatus-n>output.txt&&+diff-uexpected.txtoutput.txt+'++cat>expected.txt<<EOF EOF test_expect_success'Status after refresh''stgnew-m"first patch"&&
Oops, that should have been in the first patch.
--
David Kågedal
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:34
On 2007-09-15 00:32:15 +0200, David Kågedal wrote:
quoted hunk
@@ -436,7 +422,13 @@ class Series(PatchSet): patch = patch.strip() os.rename(os.path.join(branch_dir, patch), os.path.join(patch_dir, patch))- Patch(patch, patch_dir, refs_base).update_top_ref()+ topfield = os.path.join(patch_dir, patch, 'top')+ if os.path.isfile(topfield):+ top = read_string(topfield, False)+ else:+ top = None+ if top:+ git.set_ref(refs_base + '/' + patch, top) set_format_version(1) # Update 1 -> 2.
And remove the top file, maybe? (Or I may be mistaken; I don't have a
copy of the surrounding code handy.)
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:34
On 2007-09-15 00:31:09 +0200, David Kågedal wrote:
The following series removes the 'bottom' and 'top' files for each
patch, and instead uses the commit objects to keep track of the
patches.
Wonderful! Does this ensure that there's a bijection between patches
and commits at _all_ times, or am I missing something?
The last two patches do the final cleansing. Obviously, this changes
the format, and the format version should be increased and and
update function be written. So it's not really ready to go in yet.
It's a trivial format update, though: just delete those two files and
increase the number from 2 to 3.
Hmm, wait, no. Right. We also have to create commits for those patches
that don't have exactly one commit object. Not that there'll be many
of them, but better not make assumptions ...
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
On 16/09/2007, Karl Hasselström [off-list ref] wrote:
On 2007-09-15 00:31:09 +0200, David Kågedal wrote:
quoted
The following series removes the 'bottom' and 'top' files for each
patch, and instead uses the commit objects to keep track of the
patches.
Wonderful! Does this ensure that there's a bijection between patches
and commits at _all_ times, or am I missing something?
We should get rid of top.old and bottom.old as well.
My question - does this conflict with the DAG patches in any way? I
intend to include the them at some point, once I get a chance to test
the performance penalty with a big tree like the Linux kernel.
Hmm, wait, no. Right. We also have to create commits for those patches
that don't have exactly one commit object. Not that there'll be many
of them, but better not make assumptions ...
Is there any patch which consists of more than one commit? Maybe only
uncommit could generate one but I think we put some tests in place.
--
Catalin
From: David Kågedal <hidden> Date: 2016-06-15 22:43:34
16 sep 2007 kl. 01.36 skrev Karl Hasselström:
On 2007-09-15 00:32:15 +0200, David Kågedal wrote:
quoted
@@ -436,7 +422,13 @@ class Series(PatchSet): patch = patch.strip() os.rename(os.path.join(branch_dir, patch), os.path.join(patch_dir, patch))- Patch(patch, patch_dir, refs_base).update_top_ref()+ topfield = os.path.join(patch_dir, patch, 'top')+ if os.path.isfile(topfield):+ top = read_string(topfield, False)+ else:+ top = None+ if top:+ git.set_ref(refs_base + '/' + patch, top) set_format_version(1) # Update 1 -> 2.
And remove the top file, maybe? (Or I may be mistaken; I don't have a
copy of the surrounding code handy.)
No, this is the code that updates from version 0 to version 1. The
problem was that the update functionality used the update_top_ref()
function in the Patch class which I changed. So I had to inline the
code instead.
--
David Kågedal
davidk@lysator.liu.se
From: David Kågedal <hidden> Date: 2016-06-15 22:43:34
16 sep 2007 kl. 01.42 skrev Karl Hasselström:
On 2007-09-15 00:31:09 +0200, David Kågedal wrote:
quoted
The following series removes the 'bottom' and 'top' files for each
patch, and instead uses the commit objects to keep track of the
patches.
Wonderful! Does this ensure that there's a bijection between patches
and commits at _all_ times, or am I missing something?
That's the intention, at least. As far as I could tell, the biject
already held, except temporarily in the middle of executing some
commands. So I had to refactor them to never create that intermedate
state.
quoted
The last two patches do the final cleansing. Obviously, this changes
the format, and the format version should be increased and and
update function be written. So it's not really ready to go in yet.
It's a trivial format update, though: just delete those two files and
increase the number from 2 to 3.
Hmm, wait, no. Right. We also have to create commits for those patches
that don't have exactly one commit object. Not that there'll be many
of them, but better not make assumptions ...
I haven't seen any such patches, but I haven't tried everything.
Neither do the test suite test everything.
--
David Kågedal
davidk@lysator.liu.se
From: David Kågedal <hidden> Date: 2016-06-15 22:43:34
16 sep 2007 kl. 09.28 skrev Catalin Marinas:
On 16/09/2007, Karl Hasselström [off-list ref] wrote:
quoted
On 2007-09-15 00:31:09 +0200, David Kågedal wrote:
quoted
The following series removes the 'bottom' and 'top' files for each
patch, and instead uses the commit objects to keep track of the
patches.
Wonderful! Does this ensure that there's a bijection between patches
and commits at _all_ times, or am I missing something?
We should get rid of top.old and bottom.old as well.
My question - does this conflict with the DAG patches in any way? I
intend to include the them at some point, once I get a chance to test
the performance penalty with a big tree like the Linux kernel.
My refactoring of the push_patch function will conflict because of
refactoring, but it doesn't change how the appliedness is used, so it
should be pretty simple to resolve.
Or I could try to redo the patches so it only has the minimal changes
to actually remove the top and bottom files.
quoted
Hmm, wait, no. Right. We also have to create commits for those
patches
that don't have exactly one commit object. Not that there'll be many
of them, but better not make assumptions ...
Is there any patch which consists of more than one commit? Maybe only
uncommit could generate one but I think we put some tests in place.
I haven't seen any such case. Can uncommit create one? Or did it use
to do that before? I added checks to detect it, and no test case
caught it at least.
--
David Kågedal
davidk@lysator.liu.se
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:35
On 2007-09-16 12:22:28 +0200, David Kågedal wrote:
16 sep 2007 kl. 01.36 skrev Karl Hasselström:
quoted
And remove the top file, maybe? (Or I may be mistaken; I don't
have a copy of the surrounding code handy.)
No, this is the code that updates from version 0 to version 1. The
problem was that the update functionality used the update_top_ref()
function in the Patch class which I changed. So I had to inline the
code instead.
Ah. Right, you did say that you hadn't built an update function yet.
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:35
On 2007-09-16 08:28:53 +0100, Catalin Marinas wrote:
We should get rid of top.old and bottom.old as well.
Yeah, I guess that data could be computed from the patch log?
My question - does this conflict with the DAG patches in any way? I
intend to include the them at some point, once I get a chance to
test the performance penalty with a big tree like the Linux kernel.
I haven't been able to get rid of all the expensive DAG walking, so
I've been considering a different approach: continue using the current
applied and unapplied files if the existing HEAD == top patch check
passes, and letting the assimilate command do a full DAG walk to
regenerate those files. (And by "full DAG walk", I mean walking from
HEAD down to the first commit with parents != 1; the patches we see
are applied (in the order we see them), and the rest are unapplied.)
Is there any patch which consists of more than one commit? Maybe
only uncommit could generate one but I think we put some tests in
place.
Uncommit does not generate such patches, unless I've made a thinko; I
don't approve of them. But I always assumed they could exist, since
the code (at least in places) seems careful to not assume anything
about the number of commits between top and bottom.
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
On 14/09/2007, David Kågedal [off-list ref] wrote:
This patch adds a number of assertions to document and verify the
complex restrictions of the input parameters to the Series.new_patch
function. It also adds the requirement that 'before_existing' and
'commit' cannot be true at the same time when calling it, instead of
updating 'commit' inside the function.
[...]
quoted hunk
--- a/stgit/stack.py+++ b/stgit/stack.py
@@ -833,9 +833,16 @@ class Series(PatchSet):author_name=None,author_email=None,author_date=None,committer_name=None,committer_email=None,before_existing=False):-"""Creates a new patch+"""Creates a new patch, either pointing to an existing commit object,+orbycreatinganewcommitobject."""+assertcommitor(topandbottom)+assertnotbefore_existingor(topandbottom)+assertnot(commitandbefore_existing)+assert(topandbottom)or(nottopandnotbottom)+assertnottopor(bottom==git.get_commit(top).get_parent())
The last assertion here prevents the use of 'stg pick --reverse'. This
command creates an unapplied patch with top and bottom reversed and
pushes it to force a three-way merge.
It seems to work OK if I comment it out but I wonder whether it will
break in the future with the planned removal of the top and bottom
files.
Thanks.
--
Catalin
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:40
On 2007-10-08 14:16:10 +0100, Catalin Marinas wrote:
On 14/09/2007, David Kågedal [off-list ref] wrote:
quoted
+ assert commit or (top and bottom)
+ assert not before_existing or (top and bottom)
+ assert not (commit and before_existing)
+ assert (top and bottom) or (not top and not bottom)
+ assert not top or (bottom == git.get_commit(top).get_parent())
The last assertion here prevents the use of 'stg pick --reverse'.
This command creates an unapplied patch with top and bottom reversed
and pushes it to force a three-way merge.
It seems to work OK if I comment it out but I wonder whether it will
break in the future with the planned removal of the top and bottom
files.
I think the assert represents a real constraint, namely that there has
to be a 1:1 correspondance between patches and commits.
Couldn't "stg pick --reverse" create a new commit and use that? That
is, given that we want to revert commit C, create a new commit C* with
tree(C*) := tree(parent(C))
parent(C*) := C
Creating just one new commit object seems like a cheap thing to do.
And shouldn't there be a test for this? :-)
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
On 08/10/2007, Karl Hasselström [off-list ref] wrote:
On 2007-10-08 14:16:10 +0100, Catalin Marinas wrote:
quoted
On 14/09/2007, David Kågedal [off-list ref] wrote:
quoted
+ assert commit or (top and bottom)
+ assert not before_existing or (top and bottom)
+ assert not (commit and before_existing)
+ assert (top and bottom) or (not top and not bottom)
+ assert not top or (bottom == git.get_commit(top).get_parent())
The last assertion here prevents the use of 'stg pick --reverse'.
This command creates an unapplied patch with top and bottom reversed
and pushes it to force a three-way merge.
It seems to work OK if I comment it out but I wonder whether it will
break in the future with the planned removal of the top and bottom
files.
I think the assert represents a real constraint, namely that there has
to be a 1:1 correspondance between patches and commits.
Couldn't "stg pick --reverse" create a new commit and use that? That
is, given that we want to revert commit C, create a new commit C* with
Series.new_patch already creates a commit, why should we move the
functionality to 'pick'? The only call to new_patch with commit=False
seems to be from 'uncommit' (and it makes sense indeed).
And shouldn't there be a test for this? :-)
Yes :-). I think there are many other tests needed. It would be useful
to do a code coverage with the existing tests to see what we are
missing. Unit testing might be useful as well but we all have limited
spare time.
--
Catalin
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:40
On 2007-10-09 22:01:44 +0100, Catalin Marinas wrote:
On 08/10/2007, Karl Hasselström [off-list ref] wrote:
quoted
On 2007-10-08 14:16:10 +0100, Catalin Marinas wrote:
quoted
It seems to work OK if I comment it out but I wonder whether it
will break in the future with the planned removal of the top and
bottom files.
I think the assert represents a real constraint, namely that there
has to be a 1:1 correspondance between patches and commits.
Couldn't "stg pick --reverse" create a new commit and use that?
That is, given that we want to revert commit C, create a new
commit C* with
Series.new_patch already creates a commit, why should we move the
functionality to 'pick'?
I didn't say that. :-) You could accomplish the commit creation by
calling Series.new_patch if you like.
The only call to new_patch with commit=False seems to be from
'uncommit' (and it makes sense indeed).
Yes. For uncommit anything else would be insane.
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle
From: Karl Hasselström <hidden> Date: 2016-06-15 22:43:40
On 2007-10-10 09:45:17 +0200, Karl Hasselström wrote:
On 2007-10-09 22:01:44 +0100, Catalin Marinas wrote:
quoted
Series.new_patch already creates a commit, why should we move the
functionality to 'pick'?
I didn't say that. :-) You could accomplish the commit creation by
calling Series.new_patch if you like.
Ummm ... which was presumably the case already, and broke because of
the new assertion. Just ignore me. :-)
--
Karl Hasselström, kha@treskal.com
www.treskal.com/kalle