]> source.dussan.org Git - gitblit.git/commitdiff
Fix for #962 - Delete patchset ability 1039/head
authorPaul Martin <paul@paulsputer.com>
Sun, 3 Apr 2016 19:30:22 +0000 (20:30 +0100)
committerPaul Martin <paul@paulsputer.com>
Wed, 6 Apr 2016 18:46:58 +0000 (19:46 +0100)
src/main/java/com/gitblit/models/TicketModel.java
src/main/java/com/gitblit/tickets/ITicketService.java
src/main/java/com/gitblit/utils/JGitUtils.java
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
src/main/java/com/gitblit/wicket/pages/TicketPage.html
src/main/java/com/gitblit/wicket/pages/TicketPage.java
src/main/resources/gitblit.css

index fd0b09eb9323abd2022ed83e315a9ad77ad18d99..7495448f228a866245946b5c8ef7af005bc99361 100644 (file)
@@ -107,6 +107,30 @@ public class TicketModel implements Serializable, Comparable<TicketModel> {
                TicketModel ticket;
                List<Change> effectiveChanges = new ArrayList<Change>();
                Map<String, Change> comments = new HashMap<String, Change>();
+               Map<Integer, Integer> latestRevisions = new HashMap<Integer, Integer>();
+               
+               int latestPatchsetNumber = -1;
+               
+               List<Integer> deletedPatchsets = new ArrayList<Integer>();
+               
+               for (Change change : changes) {
+                       if (change.patchset != null) {
+                               if (change.patchset.isDeleted()) {
+                                       deletedPatchsets.add(change.patchset.number);
+                               } else {
+                                       Integer latestRev = latestRevisions.get(change.patchset.number);
+                                       
+                                       if (latestRev == null || change.patchset.rev > latestRev) {
+                                               latestRevisions.put(change.patchset.number, change.patchset.rev);
+                                       }
+                                       
+                                       if (change.patchset.number > latestPatchsetNumber) {
+                                               latestPatchsetNumber = change.patchset.number;
+                                       }       
+                               }
+                       }
+               }
+               
                for (Change change : changes) {
                        if (change.comment != null) {
                                if (comments.containsKey(change.comment.id)) {
@@ -122,6 +146,19 @@ public class TicketModel implements Serializable, Comparable<TicketModel> {
                                        effectiveChanges.add(change);
                                        comments.put(change.comment.id, change);
                                }
+                       } else if (change.patchset != null) {
+                               //All revisions of a deleted patchset are not displayed
+                               if (!deletedPatchsets.contains(change.patchset.number)) {
+                                       
+                                       Integer latestRev = latestRevisions.get(change.patchset.number);
+                                       
+                                       if (    (change.patchset.number < latestPatchsetNumber) 
+                                                && (change.patchset.rev == latestRev)) {
+                                               change.patchset.canDelete = true;
+                                       }
+                                       
+                                       effectiveChanges.add(change);
+                               }
                        } else {
                                effectiveChanges.add(change);
                        }
@@ -1033,10 +1070,16 @@ public class TicketModel implements Serializable, Comparable<TicketModel> {
                public int added;
                public PatchsetType type;
 
+               public transient boolean canDelete = false;
+
                public boolean isFF() {
                        return PatchsetType.FastForward == type;
                }
 
+               public boolean isDeleted() {
+                       return PatchsetType.Delete == type;
+               }
+
                @Override
                public int hashCode() {
                        return toString().hashCode();
@@ -1287,7 +1330,7 @@ public class TicketModel implements Serializable, Comparable<TicketModel> {
        }
 
        public static enum PatchsetType {
-               Proposal, FastForward, Rebase, Squash, Rebase_Squash, Amend;
+               Proposal, FastForward, Rebase, Squash, Rebase_Squash, Amend, Delete;
 
                public boolean isRewrite() {
                        return (this != FastForward) && (this != Proposal);
index 5e3e372a2d3c6bec6c67b651bbca46b24c9f0761..e8310039851a873674f7430da6e9e893cc64046d 100644 (file)
@@ -48,6 +48,7 @@ import com.gitblit.models.TicketModel.Attachment;
 import com.gitblit.models.TicketModel.Change;
 import com.gitblit.models.TicketModel.Field;
 import com.gitblit.models.TicketModel.Patchset;
+import com.gitblit.models.TicketModel.PatchsetType;
 import com.gitblit.models.TicketModel.Status;
 import com.gitblit.tickets.TicketIndexer.Lucene;
 import com.gitblit.utils.DeepCopier;
@@ -1213,6 +1214,30 @@ public abstract class ITicketService implements IManager {
                TicketModel revisedTicket = updateTicket(repository, ticket.number, deletion);
                return revisedTicket;
        }
+       
+       /**
+        * Deletes a patchset from a ticket.
+        *
+        * @param ticket
+        * @param patchset
+        *            the patchset to delete (should be the highest revision)
+        * @param userName
+        *                      the user deleting the commit
+        * @return the revised ticket if the deletion was successful
+        * @since 1.8.0
+        */
+       public final TicketModel deletePatchset(TicketModel ticket, Patchset patchset, String userName) {
+               Change deletion = new Change(userName);
+               deletion.patchset = new Patchset();
+               deletion.patchset.number = patchset.number;
+               deletion.patchset.rev = patchset.rev;
+               deletion.patchset.type = PatchsetType.Delete;
+               
+               RepositoryModel repository = repositoryManager.getRepositoryModel(ticket.repository);
+               TicketModel revisedTicket = updateTicket(repository, ticket.number, deletion);
+               
+               return revisedTicket;
+       } 
 
        /**
         * Commit a ticket change to the repository.
index 90d40020484eff9de8e6954ec3fe723be214c7c7..adcbb4def626eaee5c27fafb5948ccb9537c1177 100644 (file)
@@ -1745,13 +1745,9 @@ public class JGitUtils {
         * @return true if successful\r
         */\r
        public static boolean deleteBranchRef(Repository repository, String branch) {\r
-               String branchName = branch;\r
-               if (!branchName.startsWith(Constants.R_HEADS)) {\r
-                       branchName = Constants.R_HEADS + branch;\r
-               }\r
 \r
                try {\r
-                       RefUpdate refUpdate = repository.updateRef(branchName, false);\r
+                       RefUpdate refUpdate = repository.updateRef(branch, false);\r
                        refUpdate.setForceUpdate(true);\r
                        RefUpdate.Result result = refUpdate.delete();\r
                        switch (result) {\r
@@ -1762,10 +1758,10 @@ public class JGitUtils {
                                return true;\r
                        default:\r
                                LOGGER.error(MessageFormat.format("{0} failed to delete to {1} returned result {2}",\r
-                                               repository.getDirectory().getAbsolutePath(), branchName, result));\r
+                                               repository.getDirectory().getAbsolutePath(), branch, result));\r
                        }\r
                } catch (Throwable t) {\r
-                       error(t, repository, "{0} failed to delete {1}", branchName);\r
+                       error(t, repository, "{0} failed to delete {1}", branch);\r
                }\r
                return false;\r
        }\r
index f425615ccd9372a8a40d7f4d86b1958f0020b42b..cee7eaba04a1148e5dcbf695c995eb155f50b5ca 100644 (file)
@@ -775,4 +775,7 @@ gb.editFile = edit file
 gb.continueEditing = Continue Editing
 gb.commitChanges = Commit Changes
 gb.fileNotMergeable = Unable to commit {0}.  This file can not be automatically merged.
-gb.fileCommitted = Successfully committed {0}.
\ No newline at end of file
+gb.fileCommitted = Successfully committed {0}.
+gb.deletePatchset = Delete Patchset {0}
+gb.deletePatchsetSuccess = Deleted Patchset {0}.
+gb.deletePatchsetFailure = Error deleting Patchset {0}.  
index 5ae005ebbfec3d8ef707834a89da75d41f4f5298..974dcc03a3255d8841b10280c30b1aa015afa634 100644 (file)
@@ -462,6 +462,7 @@ pt push</pre>
                                        <span wicket:id="patchsetType">[revision type]</span>                                   \r
                                </td>\r
                                <td><span class="hidden-phone hidden-tablet aui-lozenge aui-lozenge-subtle" wicket:id="patchsetRevision">[R1]</span>\r
+                                       <span class="fa fa-fw" style="padding-left:15px;"><a wicket:id="deleteRevision" class="fa fa-fw fa-trash delete-patchset"></a></span>\r
                                        <span class="hidden-tablet hidden-phone" style="padding-left:15px;"><span wicket:id="patchsetDiffStat"></span></span>\r
                                </td>                   \r
                                <td style="text-align:right;"><span class="attribution-date" wicket:id="changeDate">[patch date]</span></td>    \r
index 8bf5c6d98b2aaa0918f34482401ec7f0df2cc70c..b2e63a60254ece83cbb0e9cf8e4ac183bbe2e21a 100644 (file)
@@ -15,6 +15,7 @@
  */\r
 package com.gitblit.wicket.pages;\r
 \r
+import java.io.IOException;\r
 import java.text.DateFormat;\r
 import java.text.MessageFormat;\r
 import java.text.SimpleDateFormat;\r
@@ -35,6 +36,7 @@ import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.Component;\r
 import org.apache.wicket.MarkupContainer;\r
 import org.apache.wicket.PageParameters;\r
+import org.apache.wicket.RequestCycle;\r
 import org.apache.wicket.RestartResponseException;\r
 import org.apache.wicket.ajax.AjaxRequestTarget;\r
 import org.apache.wicket.behavior.SimpleAttributeModifier;\r
@@ -42,12 +44,17 @@ import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.image.ContextImage;\r
 import org.apache.wicket.markup.html.link.BookmarkablePageLink;\r
 import org.apache.wicket.markup.html.link.ExternalLink;\r
+import org.apache.wicket.markup.html.link.Link;\r
+import org.apache.wicket.markup.html.link.StatelessLink;\r
+import org.apache.wicket.markup.html.pages.RedirectPage;\r
 import org.apache.wicket.markup.html.panel.Fragment;\r
 import org.apache.wicket.markup.repeater.Item;\r
 import org.apache.wicket.markup.repeater.data.DataView;\r
 import org.apache.wicket.markup.repeater.data.ListDataProvider;\r
 import org.apache.wicket.model.Model;\r
+import org.apache.wicket.protocol.http.RequestUtils;\r
 import org.apache.wicket.protocol.http.WebRequest;\r
+import org.apache.wicket.request.target.basic.RedirectRequestTarget;\r
 import org.eclipse.jgit.diff.DiffEntry.ChangeType;\r
 import org.eclipse.jgit.lib.PersonIdent;\r
 import org.eclipse.jgit.lib.Ref;\r
@@ -82,13 +89,16 @@ import com.gitblit.tickets.TicketResponsible;
 import com.gitblit.utils.ArrayUtils;\r
 import com.gitblit.utils.JGitUtils;\r
 import com.gitblit.utils.JGitUtils.MergeStatus;\r
+import com.gitblit.utils.CommitCache;\r
 import com.gitblit.utils.MarkdownUtils;\r
+import com.gitblit.utils.RefLogUtils;\r
 import com.gitblit.utils.StringUtils;\r
 import com.gitblit.utils.TimeUtils;\r
 import com.gitblit.wicket.GitBlitWebSession;\r
 import com.gitblit.wicket.TicketsUI;\r
 import com.gitblit.wicket.WicketUtils;\r
 import com.gitblit.wicket.panels.AvatarImage;\r
+import com.gitblit.wicket.panels.BasePanel.JavascriptEventConfirmation;\r
 import com.gitblit.wicket.panels.BasePanel.JavascriptTextPrompt;\r
 import com.gitblit.wicket.panels.CommentPanel;\r
 import com.gitblit.wicket.panels.DiffStatPanel;\r
@@ -853,6 +863,9 @@ public class TicketPage extends RepositoryPage {
                                if (event.hasPatchset()) {\r
                                        // patchset\r
                                        Patchset patchset = event.patchset;\r
+                                       //In the case of using a cached change list\r
+                                       item.setVisible(!patchset.isDeleted());\r
+                                       \r
                                        String what;\r
                                        if (event.isStatusChange() && (Status.New == event.getStatus())) {\r
                                                what = getString("gb.proposedThisChange");\r
@@ -880,6 +893,14 @@ public class TicketPage extends RepositoryPage {
                                        }\r
                                        item.add(typeLabel);\r
 \r
+                                       Link<Void> deleteLink = createDeletePatchsetLink(repository, patchset);\r
+                                       \r
+                                       if (user.canDeleteRef(repository)) {\r
+                                               item.add(deleteLink.setVisible(patchset.canDelete));\r
+                                       } else {\r
+                                               item.add(deleteLink.setVisible(false));\r
+                                       }\r
+\r
                                        // show commit diffstat\r
                                        item.add(new DiffStatPanel("patchsetDiffStat", patchset.insertions, patchset.deletions, patchset.rev > 1));\r
                                } else if (event.hasComment()) {\r
@@ -887,6 +908,7 @@ public class TicketPage extends RepositoryPage {
                                        item.add(new Label("what", getString("gb.commented")));\r
                                        item.add(new Label("patchsetRevision").setVisible(false));\r
                                        item.add(new Label("patchsetType").setVisible(false));\r
+                                       item.add(new Label("deleteRevision").setVisible(false));\r
                                        item.add(new Label("patchsetDiffStat").setVisible(false));\r
                                } else if (event.hasReview()) {\r
                                        // review\r
@@ -906,11 +928,13 @@ public class TicketPage extends RepositoryPage {
                                                        .setEscapeModelStrings(false));\r
                                        item.add(new Label("patchsetRevision").setVisible(false));\r
                                        item.add(new Label("patchsetType").setVisible(false));\r
+                                       item.add(new Label("deleteRevision").setVisible(false));\r
                                        item.add(new Label("patchsetDiffStat").setVisible(false));\r
                                } else {\r
                                        // field change\r
                                        item.add(new Label("patchsetRevision").setVisible(false));\r
                                        item.add(new Label("patchsetType").setVisible(false));\r
+                                       item.add(new Label("deleteRevision").setVisible(false));\r
                                        item.add(new Label("patchsetDiffStat").setVisible(false));\r
 \r
                                        String what = "";\r
@@ -1600,4 +1624,85 @@ public class TicketPage extends RepositoryPage {
                        return copyFragment;\r
                }\r
        }\r
+       \r
+       private Link<Void> createDeletePatchsetLink(final RepositoryModel repositoryModel, final Patchset patchset)\r
+       {\r
+               Link<Void> deleteLink = new Link<Void>("deleteRevision") {\r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       @Override\r
+                       public void onClick() {\r
+                               Repository r = app().repositories().getRepository(repositoryModel.name);\r
+                               UserModel user = GitBlitWebSession.get().getUser();\r
+                               \r
+                               if (r == null) {\r
+                                       if (app().repositories().isCollectingGarbage(repositoryModel.name)) {\r
+                                               error(MessageFormat.format(getString("gb.busyCollectingGarbage"), repositoryModel.name));\r
+                                       } else {\r
+                                               error(MessageFormat.format("Failed to find repository {0}", repositoryModel.name));\r
+                                       }\r
+                                       return;\r
+                               }\r
+                               \r
+                               //Construct the ref name based on the patchset\r
+                               String ticketShard = String.format("%02d", ticket.number);\r
+                               ticketShard = ticketShard.substring(ticketShard.length() - 2);\r
+                               final String refName = String.format("%s%s/%d/%d", Constants.R_TICKETS_PATCHSETS, ticketShard, ticket.number, patchset.number);\r
+\r
+                               Ref ref = null;\r
+                               boolean success = true;\r
+                               \r
+                               try {\r
+                                       ref = r.getRef(refName);\r
+                                       \r
+                                       if (ref != null) {\r
+                                               success = JGitUtils.deleteBranchRef(r, ref.getName());\r
+                                       } else {\r
+                                               success = false;\r
+                                       }\r
+                                       \r
+                                       if (success) {\r
+                                               // clear commit cache\r
+                                               CommitCache.instance().clear(repositoryModel.name, refName);\r
+\r
+                                               // optionally update reflog\r
+                                               if (RefLogUtils.hasRefLogBranch(r)) {\r
+                                                       RefLogUtils.deleteRef(user, r, ref);\r
+                                               }\r
+\r
+                                               TicketModel updatedTicket = app().tickets().deletePatchset(ticket, patchset, user.username);\r
+                                                                                               \r
+                                               if (updatedTicket == null) {\r
+                                                       success = false;\r
+                                               }\r
+                                       }\r
+                               } catch (IOException e) {\r
+                                       logger().error("failed to determine ticket from ref", e);\r
+                                       success = false;\r
+                               } finally {\r
+                                       r.close();\r
+                               }\r
+\r
+                               if (success) {\r
+                                       getSession().info(MessageFormat.format(getString("gb.deletePatchsetSuccess"), patchset.number));\r
+                                       logger().info(MessageFormat.format("{0} deleted patchset {1} from ticket {2}", \r
+                                                       user.username, patchset.number, ticket.number));\r
+                               } else {\r
+                                       getSession().error(MessageFormat.format(getString("gb.deletePatchsetFailure"),patchset.number));\r
+                               }\r
+                               \r
+                               //Force reload of the page to rebuild ticket change cache\r
+                               String relativeUrl = urlFor(TicketsPage.class, getPageParameters()).toString();\r
+                               String absoluteUrl = RequestUtils.toAbsolutePath(relativeUrl);\r
+                               setResponsePage(new RedirectPage(absoluteUrl));\r
+                       }\r
+               };\r
+\r
+               WicketUtils.setHtmlTooltip(deleteLink, MessageFormat.format(getString("gb.deletePatchset"), patchset.number));\r
+               \r
+               deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(getString("gb.deletePatchset"), patchset.number)));\r
+               \r
+               return deleteLink;\r
+       }\r
+       \r
 }\r
index 0199190970dfae4f20b04de71f249554081e8b1b..1cae6d5fd15bc715d8fd4a6d53a1a04dbf4e792a 100644 (file)
@@ -2359,4 +2359,9 @@ div.markdown table.text th, div.markdown table.text td {
 \r
 .filestore-item {\r
        color:#815b3a;\r
+}\r
+\r
+.delete-patchset {\r
+       color:#D51900;\r
+       font-size: 1.2em;\r
 }
\ No newline at end of file