| author | Robin Stocker | 2012-01-16 18:04:57 (EST) |
|---|---|---|
| committer | Matthias Sohn | 2012-01-16 18:04:57 (EST) |
| commit | a15b717a4b9138fb4d11fc07a26e9f9979b0562f (patch) (side-by-side diff) | |
| tree | 356354a145129bdf428ccef846b6984717ffc47a | |
| parent | 37cb29166bfa9abcf09db703d0e7ba5c694277c3 (diff) | |
| download | egit-a15b717a4b9138fb4d11fc07a26e9f9979b0562f.zip egit-a15b717a4b9138fb4d11fc07a26e9f9979b0562f.tar.gz egit-a15b717a4b9138fb4d11fc07a26e9f9979b0562f.tar.bz2 | |
Show detailed branch status in label decoration of projectrefs/changes/78/4678/5
The branch status is shown after the branch name in the decoration if
the current branch is different from the remote tracking branch. This is
useful as a visual indication that the branch needs to be pushed (or
merged/rebased).
If there are commits on the branch that are not on the tracking branch,
an upwards arrow (↑) followed by the number of commits is shown. This
can be read as "the branch is N commits ahead" or "N commits need to be
pushed".
If there are commits on the tracking branch that are not on the local
branch, a downwards arrow (↓) is shown, followed by the number of
commits. This can be read as "the branch is N commits behind" or "N
commits need to be merged".
For example, if there are 2 unpushed commits on master, the project is
now shown like this:
project [repo master ↑2]
If there are both unpushed and unmerged commits (the branches have
diverged), it's shown like this:
project [repo master ↑2 ↓1]
In C Git, information about the tracking branch is displayed in "git
status" output. EGit currently doesn't have any indication that a branch
needs to be pushed, apart from the labels in History View. When there
are multiple repositories involved, it can easily happen that a push is
forgotten. This change helps to prevent that.
This depends on JGit change I8d2b108c89905c3f0496f3d517879596740787c0.
Change-Id: I1e1caca561d1b0a0c194bfc42e64b698f42c6e6a
Signed-off-by: Robin Stocker <robin@nibor.org>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
9 files changed, 115 insertions, 14 deletions
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java index 854b814..2f89cf6 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java @@ -2623,6 +2623,9 @@ public class UIText extends NLS { public static String DecoratorPreferencesPage_bindingBranchName; /** */ + public static String DecoratorPreferencesPage_bindingBranchStatus; + + /** */ public static String DecoratorPreferencesPage_bindingDirtyFlag; /** */ diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/GitLabelProvider.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/GitLabelProvider.java index 78b732d..3bf8cae 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/GitLabelProvider.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/GitLabelProvider.java @@ -35,6 +35,7 @@ import org.eclipse.jface.resource.ResourceManager; import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.StyledString; +import org.eclipse.jgit.lib.BranchTrackingStatus; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryState; @@ -55,6 +56,29 @@ public class GitLabelProvider extends LabelProvider implements private LabelProvider workbenchLabelProvider; + /** + * Format the branch tracking status suitable for displaying in decorations and labels. + * + * @param status + * @return the branch tracking status as a string + */ + public static String formatBranchTrackingStatus(BranchTrackingStatus status) { + StringBuilder sb = new StringBuilder(); + int ahead = status.getAheadCount(); + int behind = status.getBehindCount(); + if (ahead != 0) { + sb.append('↑'); + sb.append(ahead); + } + if (behind != 0) { + if (sb.length() != 0) + sb.append(' '); + sb.append('↓'); + sb.append(status.getBehindCount()); + } + return sb.toString(); + } + @Override public String getText(Object element) { if (element instanceof Repository) @@ -147,6 +171,15 @@ public class GitLabelProvider extends LabelProvider implements string.append(repositoryState.getDescription(), StyledString.DECORATIONS_STYLER); } + + BranchTrackingStatus trackingStatus = BranchTrackingStatus.of(repository, branch); + if (trackingStatus != null + && (trackingStatus.getAheadCount() != 0 || trackingStatus + .getBehindCount() != 0)) { + String formattedTrackingStatus = formatBranchTrackingStatus(trackingStatus); + string.append(' '); + string.append(formattedTrackingStatus, StyledString.DECORATIONS_STYLER); + } string.append(']', StyledString.DECORATIONS_STYLER); } diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResource.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResource.java index 08bfabf..085439c 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResource.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResource.java @@ -33,6 +33,11 @@ public class DecoratableResource implements IDecoratableResource { protected String branch = null; /** + * Branch status relative to remote tracking branch + */ + protected String branchStatus = null; + + /** * Flag indicating whether or not the resource is tracked */ protected boolean tracked = false; @@ -92,6 +97,10 @@ public class DecoratableResource implements IDecoratableResource { return branch; } + public String getBranchStatus() { + return branchStatus; + } + public boolean isTracked() { return tracked; } diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResourceAdapter.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResourceAdapter.java index 1d6c6be..382a256 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResourceAdapter.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResourceAdapter.java @@ -62,6 +62,7 @@ class DecoratableResourceAdapter extends DecoratableResource { repositoryName = DecoratableResourceHelper .getRepositoryName(repository); branch = DecoratableResourceHelper.getShortBranch(repository); + branchStatus = DecoratableResourceHelper.getBranchStatus(repository); switch (resource.getType()) { case IResource.FILE: extractResourceProperties(); diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResourceHelper.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResourceHelper.java index b8984a5..855dcf2 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResourceHelper.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/DecoratableResourceHelper.java @@ -14,6 +14,8 @@ import java.io.IOException; import org.eclipse.egit.ui.Activator; import org.eclipse.egit.ui.UIText; +import org.eclipse.egit.ui.internal.GitLabelProvider; +import org.eclipse.jgit.lib.BranchTrackingStatus; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; @@ -56,4 +58,20 @@ public class DecoratableResourceHelper { return repository.getBranch(); } + + static String getBranchStatus(Repository repo) throws IOException { + String branchName = repo.getBranch(); + if (branchName == null) + return null; + + BranchTrackingStatus status = BranchTrackingStatus.of(repo, branchName); + if (status == null) + return null; + + if (status.getAheadCount() == 0 && status.getBehindCount() == 0) + return null; + + String formattedStatus = GitLabelProvider.formatBranchTrackingStatus(status); + return formattedStatus; + } } diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/GitLightweightDecorator.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/GitLightweightDecorator.java index a94d9c9..26238d5 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/GitLightweightDecorator.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/GitLightweightDecorator.java @@ -224,6 +224,9 @@ public class GitLightweightDecorator extends LabelProvider implements public static final String BINDING_BRANCH_NAME = "branch"; //$NON-NLS-1$ /** */ + public static final String BINDING_BRANCH_STATUS = "branch_status"; //$NON-NLS-1$ + + /** */ public static final String BINDING_REPOSITORY_NAME = "repository"; //$NON-NLS-1$ /** */ @@ -239,7 +242,7 @@ public class GitLightweightDecorator extends LabelProvider implements public static final String FOLDER_FORMAT_DEFAULT = "{dirty:>} {name}"; //$NON-NLS-1$ /** */ - public static final String PROJECT_FORMAT_DEFAULT ="{dirty:>} {name} [{repository} {branch}]"; //$NON-NLS-1$ + public static final String PROJECT_FORMAT_DEFAULT ="{dirty:>} {name} [{repository} {branch}{ branch_status}]"; //$NON-NLS-1$ private IPreferenceStore store; @@ -365,6 +368,7 @@ public class GitLightweightDecorator extends LabelProvider implements bindings.put(BINDING_RESOURCE_NAME, resource.getName()); bindings.put(BINDING_REPOSITORY_NAME, resource.getRepositoryName()); bindings.put(BINDING_BRANCH_NAME, resource.getBranch()); + bindings.put(BINDING_BRANCH_STATUS, resource.getBranchStatus()); bindings.put(BINDING_DIRTY_FLAG, resource.isDirty() ? ">" : null); //$NON-NLS-1$ bindings.put(BINDING_STAGED_FLAG, resource.staged() != Staged.NOT_STAGED ? "*" : null); //$NON-NLS-1$ @@ -446,6 +450,8 @@ public class GitLightweightDecorator extends LabelProvider implements if ((start = format.indexOf('}', end)) > -1) { String key = format.substring(end + 1, start); String s; + boolean spaceBefore = false; + boolean spaceAfter = false; // Allow users to override the binding if (key.indexOf(':') > -1) { @@ -454,8 +460,18 @@ public class GitLightweightDecorator extends LabelProvider implements if (keyAndBinding.length > 1 && bindings.get(key) != null) bindings.put(key, keyAndBinding[1]); + } else { + if (key.charAt(0) == ' ') { + spaceBefore = true; + key = key.substring(1); + } + if (key.charAt(key.length() - 1) == ' ') { + spaceAfter = true; + key = key.substring(0, key.length() - 1); + } } + // We use the BINDING_RESOURCE_NAME key to determine if // we are doing the prefix or suffix. The name isn't // actually part of either. @@ -467,7 +483,11 @@ public class GitLightweightDecorator extends LabelProvider implements } if (s != null) { + if (spaceBefore) + output.append(' '); output.append(s); + if (spaceAfter) + output.append(' '); } else { // Support removing prefix character if binding is // null diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/IDecoratableResource.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/IDecoratableResource.java index 6ab99a1..9875882 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/IDecoratableResource.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/decorators/IDecoratableResource.java @@ -63,6 +63,12 @@ public interface IDecoratableResource { String getBranch(); /** + * @return a symbol indicating the branch status relative to the remote + * tracking branch, or <code>null</code> if not applicable + */ + String getBranchStatus(); + + /** * Returns whether or not the resource is tracked by Git * * @return whether or not the resource is tracked by Git diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/preferences/GitDecoratorPreferencePage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/preferences/GitDecoratorPreferencePage.java index 9162c62..bda054c 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/preferences/GitDecoratorPreferencePage.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/preferences/GitDecoratorPreferencePage.java @@ -115,42 +115,42 @@ public class GitDecoratorPreferencePage extends PreferencePage implements static { final PreviewResource project = new PreviewResource( - "Project", IResource.PROJECT, "repository" + '|' + RepositoryState.MERGING.getDescription(), "master", true, false, true, Staged.NOT_STAGED, false, false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + "Project", IResource.PROJECT, "repository" + '|' + RepositoryState.MERGING.getDescription(), "master", "↑2 ↓1", true, false, true, Staged.NOT_STAGED, false, false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ final ArrayList<PreviewResource> children = new ArrayList<PreviewResource>(); children .add(new PreviewResource( - "folder", IResource.FOLDER, "repository", null, true, false, true, Staged.NOT_STAGED, false, false)); //$NON-NLS-1$ //$NON-NLS-2$ + "folder", IResource.FOLDER, "repository", null, null, true, false, true, Staged.NOT_STAGED, false, false)); //$NON-NLS-1$ //$NON-NLS-2$ children .add(new PreviewResource( - "tracked.txt", IResource.FILE, "repository", null, true, false, false, Staged.NOT_STAGED, false, false)); //$NON-NLS-1$ //$NON-NLS-2$ + "tracked.txt", IResource.FILE, "repository", null, null, true, false, false, Staged.NOT_STAGED, false, false)); //$NON-NLS-1$ //$NON-NLS-2$ children .add(new PreviewResource( - "untracked.txt", IResource.FILE, "repository", null, false, false, false, Staged.NOT_STAGED, false, false)); //$NON-NLS-1$ //$NON-NLS-2$ + "untracked.txt", IResource.FILE, "repository", null, null, false, false, false, Staged.NOT_STAGED, false, false)); //$NON-NLS-1$ //$NON-NLS-2$ children .add(new PreviewResource( - "ignored.txt", IResource.FILE, "repository", null, false, true, false, Staged.NOT_STAGED, false, false)); //$NON-NLS-1$ //$NON-NLS-2$ + "ignored.txt", IResource.FILE, "repository", null, null, false, true, false, Staged.NOT_STAGED, false, false)); //$NON-NLS-1$ //$NON-NLS-2$ children .add(new PreviewResource( - "dirty.txt", IResource.FILE, "repository", null, true, false, true, Staged.NOT_STAGED, false, false)); //$NON-NLS-1$ //$NON-NLS-2$ + "dirty.txt", IResource.FILE, "repository", null, null, true, false, true, Staged.NOT_STAGED, false, false)); //$NON-NLS-1$ //$NON-NLS-2$ children .add(new PreviewResource( - "staged.txt", IResource.FILE, "repository", null, true, false, false, Staged.MODIFIED, false, false)); //$NON-NLS-1$ //$NON-NLS-2$ + "staged.txt", IResource.FILE, "repository", null, null, true, false, false, Staged.MODIFIED, false, false)); //$NON-NLS-1$ //$NON-NLS-2$ children .add(new PreviewResource( - "partially-staged.txt", IResource.FILE, "repository", null, true, false, true, Staged.MODIFIED, false, false)); //$NON-NLS-1$ //$NON-NLS-2$ + "partially-staged.txt", IResource.FILE, "repository", null, null, true, false, true, Staged.MODIFIED, false, false)); //$NON-NLS-1$ //$NON-NLS-2$ children .add(new PreviewResource( - "added.txt", IResource.FILE, "repository", null, true, false, false, Staged.ADDED, false, false)); //$NON-NLS-1$ //$NON-NLS-2$ + "added.txt", IResource.FILE, "repository", null, null, true, false, false, Staged.ADDED, false, false)); //$NON-NLS-1$ //$NON-NLS-2$ children .add(new PreviewResource( - "removed.txt", IResource.FILE, "repository", null, true, false, false, Staged.REMOVED, false, false)); //$NON-NLS-1$ //$NON-NLS-2$ + "removed.txt", IResource.FILE, "repository", null, null, true, false, false, Staged.REMOVED, false, false)); //$NON-NLS-1$ //$NON-NLS-2$ children .add(new PreviewResource( - "conflict.txt", IResource.FILE, "repository", null, true, false, true, Staged.NOT_STAGED, true, false)); //$NON-NLS-1$ //$NON-NLS-2$ + "conflict.txt", IResource.FILE, "repository", null, null, true, false, true, Staged.NOT_STAGED, true, false)); //$NON-NLS-1$ //$NON-NLS-2$ children .add(new PreviewResource( - "assume-valid.txt", IResource.FILE, "repository", null, true, false, false, Staged.NOT_STAGED, false, true)); //$NON-NLS-1$ //$NON-NLS-2$ + "assume-valid.txt", IResource.FILE, "repository", null, null, true, false, false, Staged.NOT_STAGED, false, true)); //$NON-NLS-1$ //$NON-NLS-2$ project.children = children; PREVIEW_FILESYSTEM_ROOT = Collections.singleton(project); @@ -173,6 +173,9 @@ public class GitDecoratorPreferencePage extends PreferencePage implements UIText.GitDecoratorPreferencePage_bindingRepositoryNameFlag); PROJECT_BINDINGS.put(DecorationHelper.BINDING_BRANCH_NAME, UIText.DecoratorPreferencesPage_bindingBranchName); + PROJECT_BINDINGS.put(DecorationHelper.BINDING_BRANCH_STATUS, + UIText.DecoratorPreferencesPage_bindingBranchStatus); + CHANGESET_LABEL_BINDINGS = new HashMap<String, String>(); CHANGESET_LABEL_BINDINGS.put(removeBraces(GitChangeSetLabelProvider.BINDING_CHANGESET_AUTHOR), @@ -998,6 +1001,8 @@ public class GitDecoratorPreferencePage extends PreferencePage implements private final String branch; + private final String branchStatus; + private final int type; private Collection children; @@ -1015,12 +1020,13 @@ public class GitDecoratorPreferencePage extends PreferencePage implements private boolean assumeValid; public PreviewResource(String name, int type, String repositoryName, String branch, - boolean tracked, boolean ignored, boolean dirty, Staged staged, + String branchStatus, boolean tracked, boolean ignored, boolean dirty, Staged staged, boolean conflicts, boolean assumeValid) { this.name = name; this.repositoryName = repositoryName; this.branch = branch; + this.branchStatus = branchStatus; this.type = type; this.children = Collections.EMPTY_LIST; this.tracked = tracked; @@ -1047,6 +1053,10 @@ public class GitDecoratorPreferencePage extends PreferencePage implements return branch; } + public String getBranchStatus() { + return branchStatus; + } + public boolean isTracked() { return tracked; } diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties index 62d2323..d690a02 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties @@ -907,6 +907,7 @@ DecoratorPreferencesPage_labelDecorationsLink=See <a>''{0}''</a> to enable or di DecoratorPreferencesPage_generalTabFolder=&General DecoratorPreferencesPage_bindingResourceName=Name of the resource being decorated DecoratorPreferencesPage_bindingBranchName=Current branch of the repository +DecoratorPreferencesPage_bindingBranchStatus=Branch status (compared to remote-tracking) DecoratorPreferencesPage_bindingDirtyFlag=Flag indicating whether or not the resource is dirty DecoratorPreferencesPage_bindingStagedFlag=Flag indicating whether or not the resource is staged DecoratorPreferencesPage_selectVariablesToAdd=Select the &variables to add to the decoration format: |

