def pop_patch(self, name, keep=False): """Pops the top patch from the stack """ applied = self.get_applied() applied.reverse() assert(name in applied) patch = self.get_patch(name) if git.get_head_file() == self.get_name(): if keep and not git.apply_diff(git.get_head(), patch.get_bottom(), check_index=False): raise StackException( 'Failed to pop patches while preserving the local changes') git.switch(patch.get_bottom(), keep) else: git.set_branch(self.get_name(), patch.get_bottom()) # save the new applied list idx = applied.index(name) + 1 popped = applied[:idx] popped.reverse() unapplied = popped + self.get_unapplied() write_strings(self.__unapplied_file, unapplied) del applied[:idx] applied.reverse() write_strings(self.__applied_file, applied)
def pop_patch(self, name, keep = False): """Pops the top patch from the stack """ applied = self.get_applied() applied.reverse() assert(name in applied) patch = self.get_patch(name) if git.get_head_file() == self.get_name(): if keep and not git.apply_diff(git.get_head(), patch.get_bottom(), check_index = False): raise StackException( 'Failed to pop patches while preserving the local changes') git.switch(patch.get_bottom(), keep) else: git.set_branch(self.get_name(), patch.get_bottom()) # save the new applied list idx = applied.index(name) + 1 popped = applied[:idx] popped.reverse() unapplied = popped + self.get_unapplied() write_strings(self.__unapplied_file, unapplied) del applied[:idx] applied.reverse() write_strings(self.__applied_file, applied)
def push_empty_patch(self, name): """Pushes an empty patch on the stack """ unapplied = self.get_unapplied() assert(name in unapplied) # patch = self.get_patch(name) head = git.get_head() append_string(self.__applied_file, name) unapplied.remove(name) write_strings(self.__unapplied_file, unapplied) self.refresh_patch(bottom = head, cache_update = False, log = 'push(m)')
def push_empty_patch(self, name): """Pushes an empty patch on the stack """ unapplied = self.get_unapplied() assert(name in unapplied) # patch = self.get_patch(name) head = git.get_head() append_string(self.__applied_file, name) unapplied.remove(name) write_strings(self.__unapplied_file, unapplied) self.refresh_patch(bottom=head, cache_update=False, log='push(m)')
def delete_patch(self, name, keep_log = False): """Deletes a patch """ self.__patch_name_valid(name) patch = self.get_patch(name) if self.__patch_is_current(patch): self.pop_patch(name) elif self.patch_applied(name): raise StackException('Cannot remove an applied patch, "%s", ' 'which is not current' % name) elif name not in self.get_unapplied(): raise StackException('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(keep_log = keep_log) unapplied = self.get_unapplied() unapplied.remove(name) write_strings(self.__unapplied_file, unapplied)
def delete_patch(self, name, keep_log=False): """Deletes a patch """ self.__patch_name_valid(name) patch = self.get_patch(name) if self.__patch_is_current(patch): self.pop_patch(name) elif self.patch_applied(name): raise StackException('Cannot remove an applied patch, "%s", ' 'which is not current' % name) elif name not in self.get_unapplied(): raise StackException('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(keep_log=keep_log) unapplied = self.get_unapplied() unapplied.remove(name) write_strings(self.__unapplied_file, unapplied)
def rename_patch(self, oldname, newname): self.__patch_name_valid(newname) applied = self.get_applied() unapplied = self.get_unapplied() if oldname == newname: raise StackException('"To" name and "from" name are the same') if newname in applied or newname in unapplied: raise StackException('Patch "%s" already exists' % newname) if oldname in unapplied: self.get_patch(oldname).rename(newname) unapplied[unapplied.index(oldname)] = newname write_strings(self.__unapplied_file, unapplied) elif oldname in applied: self.get_patch(oldname).rename(newname) applied[applied.index(oldname)] = newname write_strings(self.__applied_file, applied) else: raise StackException('Unknown patch "%s"' % oldname)
def new_patch(self, name, message = None, can_edit = True, unapplied = False, show_patch = False, top = None, bottom = None, commit = True, author_name = None, author_email = None, author_date = None, committer_name = None, committer_email = None, before_existing = False, sign_str = None): """Creates a new patch, either pointing to an existing commit object, or by creating a new commit object. """ 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 commit or (not top or (bottom == git.get_commit(top).get_parent())) if name is not None: self.__patch_name_valid(name) if self.patch_exists(name): raise StackException('Patch "%s" already exists' % name) # TODO: move this out of the stgit.stack module, it is really # for higher level commands to handle the user interaction def sign(msg): return add_sign_line(msg, sign_str, committer_name or git.committer().name, committer_email or git.committer().email) if not message and can_edit: descr = edit_file( self, sign(''), 'Please enter the description for the patch above.', show_patch) else: descr = sign(message) head = git.get_head() if name is None: name = make_patch_name(descr, self.patch_exists) patch = self.get_patch(name) patch.create() patch.set_description(descr) patch.set_authname(author_name) patch.set_authemail(author_email) patch.set_authdate(author_date) patch.set_commname(committer_name) patch.set_commemail(committer_email) if before_existing: insert_string(self.__applied_file, patch.get_name()) elif unapplied: patches = [patch.get_name()] + self.get_unapplied() write_strings(self.__unapplied_file, patches) set_head = False else: append_string(self.__applied_file, patch.get_name()) set_head = True if commit: if top: 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 branch assert(unapplied or bottom == head) commit_id = git.commit(message = descr, parents = [bottom], cache_update = False, tree_id = top_commit.get_tree(), allowempty = True, set_head = set_head, author_name = author_name, author_email = author_email, author_date = author_date, committer_name = committer_name, committer_email = committer_email) # set the patch top to the new commit patch.set_top(commit_id) else: patch.set_top(top) self.log_patch(patch, 'new') return patch
def set_hidden(self, hidden): write_strings(self.__hidden_file, hidden)
def set_unapplied(self, unapplied): write_strings(self.__unapplied_file, unapplied)
def set_applied(self, applied): write_strings(self.__applied_file, applied)
def push_patch(self, name): """Pushes a patch on the stack """ unapplied = self.get_unapplied() assert(name in unapplied) patch = self.get_patch(name) head = git.get_head() bottom = patch.get_bottom() top = patch.get_top() # top != bottom always since we have a commit for each patch if head == bottom: # A fast-forward push. Just reset the backup # information. No need for logging patch.set_top(top, backup = True) git.switch(top) append_string(self.__applied_file, name) unapplied.remove(name) write_strings(self.__unapplied_file, unapplied) return False # Need to create a new commit an merge in the old patch ex = None modified = False # Try the fast applying first. If this fails, fall back to the # three-way merge if not git.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_recursive(bottom, head, top) except git.GitException: out.error('The merge failed during "push".', 'Revert the operation with "stg undo".') append_string(self.__applied_file, name) unapplied.remove(name) write_strings(self.__unapplied_file, unapplied) if not ex: # if the merge was OK and no conflicts, just refresh the patch # The GIT cache was already updated by the merge operation if modified: log = 'push(m)' else: log = 'push' self.refresh_patch(bottom = head, cache_update = False, log = log) else: # we make the patch empty, with the merged state in the # working tree. self.refresh_patch(bottom = head, cache_update = False, empty = True, log = 'push(c)') raise StackException(str(ex)) return modified
def new_patch( self, name, message=None, can_edit=True, unapplied=False, show_patch=False, top=None, bottom=None, commit=True, author_name=None, author_email=None, author_date=None, committer_name=None, committer_email=None, sign_str=None, ): """Creates a new patch, either pointing to an existing commit object, or by creating a new commit object. """ assert commit or (top and bottom) assert (top and bottom) or (not top and not bottom) assert commit or ( not top or bottom == git.get_commit(top).get_parent() ) if name is not None: self.__patch_name_valid(name) if self.patch_exists(name): raise StackException('Patch "%s" already exists' % name) # TODO: move this out of the stgit.stack module, it is really # for higher level commands to handle the user interaction def sign(msg): return add_sign_line(msg, sign_str, committer_name or git.committer().name, committer_email or git.committer().email) if not message and can_edit: descr = edit_file( self, sign(''), 'Please enter the description for the patch above.', show_patch) else: descr = sign(message) head = git.get_head() if name is None: name = make_patch_name(descr, self.patch_exists) patch = self.get_patch(name) patch.create() patch.set_description(descr) patch.set_authname(author_name) patch.set_authemail(author_email) patch.set_authdate(author_date) patch.set_commname(committer_name) patch.set_commemail(committer_email) if unapplied: patches = [patch.get_name()] + self.get_unapplied() write_strings(self.__unapplied_file, patches) set_head = False else: append_string(self.__applied_file, patch.get_name()) set_head = True if commit: if top: 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 branch assert(unapplied or bottom == head) commit_id = git.commit( message=descr, parents=[bottom], cache_update=False, tree_id=top_commit.get_tree(), allowempty=True, set_head=set_head, author_name=author_name, author_email=author_email, author_date=author_date, committer_name=committer_name, committer_email=committer_email, ) # set the patch top to the new commit patch.set_top(commit_id) else: patch.set_top(top) self.log_patch(patch, 'new') return patch
def forward_patches(self, names): """Try to fast-forward an array of patches. On return, patches in names[0:returned_value] have been pushed on the stack. Apply the rest with push_patch """ unapplied = self.get_unapplied() forwarded = 0 top = git.get_head() for name in names: assert(name in unapplied) patch = self.get_patch(name) head = top bottom = patch.get_bottom() top = patch.get_top() # top != bottom always since we have a commit for each patch if head == bottom: # reset the backup information. No logging since the # patch hasn't changed patch.set_top(top, backup = True) else: head_tree = git.get_commit(head).get_tree() bottom_tree = git.get_commit(bottom).get_tree() if head_tree == bottom_tree: # We must just reparent this patch and create a new commit # for it descr = patch.get_description() author_name = patch.get_authname() author_email = patch.get_authemail() author_date = patch.get_authdate() committer_name = patch.get_commname() committer_email = patch.get_commemail() top_tree = git.get_commit(top).get_tree() top = git.commit(message = descr, parents = [head], cache_update = False, tree_id = top_tree, allowempty = True, author_name = author_name, author_email = author_email, author_date = author_date, committer_name = committer_name, committer_email = committer_email) patch.set_top(top, backup = True) self.log_patch(patch, 'push(f)') else: top = head # stop the fast-forwarding, must do a real merge break forwarded+=1 unapplied.remove(name) if forwarded == 0: return 0 git.switch(top) append_strings(self.__applied_file, names[0:forwarded]) write_strings(self.__unapplied_file, unapplied) return forwarded
def forward_patches(self, names): """Try to fast-forward an array of patches. On return, patches in names[0:returned_value] have been pushed on the stack. Apply the rest with push_patch """ unapplied = self.get_unapplied() forwarded = 0 top = git.get_head() for name in names: assert(name in unapplied) patch = self.get_patch(name) head = top bottom = patch.get_bottom() top = patch.get_top() # top != bottom always since we have a commit for each patch if head == bottom: # reset the backup information. No logging since the # patch hasn't changed patch.set_top(top, backup=True) else: head_tree = git.get_commit(head).get_tree() bottom_tree = git.get_commit(bottom).get_tree() if head_tree == bottom_tree: # We must just reparent this patch and create a new commit # for it descr = patch.get_description() author_name = patch.get_authname() author_email = patch.get_authemail() author_date = patch.get_authdate() committer_name = patch.get_commname() committer_email = patch.get_commemail() top_tree = git.get_commit(top).get_tree() top = git.commit( message=descr, parents=[head], cache_update=False, tree_id=top_tree, allowempty=True, author_name=author_name, author_email=author_email, author_date=author_date, committer_name=committer_name, committer_email=committer_email, ) patch.set_top(top, backup=True) self.log_patch(patch, 'push(f)') else: top = head # stop the fast-forwarding, must do a real merge break forwarded += 1 unapplied.remove(name) if forwarded == 0: return 0 git.switch(top) append_strings(self.__applied_file, names[0:forwarded]) write_strings(self.__unapplied_file, unapplied) return forwarded
def push_patch(self, name): """Pushes a patch on the stack """ unapplied = self.get_unapplied() assert(name in unapplied) patch = self.get_patch(name) head = git.get_head() bottom = patch.get_bottom() top = patch.get_top() # top != bottom always since we have a commit for each patch if head == bottom: # A fast-forward push. Just reset the backup # information. No need for logging patch.set_top(top, backup=True) git.switch(top) append_string(self.__applied_file, name) unapplied.remove(name) write_strings(self.__unapplied_file, unapplied) return False # Need to create a new commit an merge in the old patch ex = None modified = False # Try the fast applying first. If this fails, fall back to the # three-way merge if not git.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_recursive(bottom, head, top) except git.GitException: out.error('The merge failed during "push".', 'Revert the operation with "stg undo".') append_string(self.__applied_file, name) unapplied.remove(name) write_strings(self.__unapplied_file, unapplied) if not ex: # if the merge was OK and no conflicts, just refresh the patch # The GIT cache was already updated by the merge operation if modified: log = 'push(m)' else: log = 'push' self.refresh_patch(bottom=head, cache_update=False, log=log) else: # we make the patch empty, with the merged state in the # working tree. self.refresh_patch( bottom=head, cache_update=False, empty=True, log='push(c)', ) raise StackException(str(ex)) return modified
def _write_file(self, fn, val): utils.write_strings(os.path.join(self._stack.directory, fn), val)
def __write_file(self, fn, val): utils.write_strings(os.path.join(self.__stack.directory, fn), val)