diff options
| author | Robin Stocker | 2012-01-16 23:04:57 +0000 |
|---|---|---|
| committer | Matthias Sohn | 2012-01-16 23:04:57 +0000 |
| commit | a15b717a4b9138fb4d11fc07a26e9f9979b0562f (patch) | |
| tree | 356354a145129bdf428ccef846b6984717ffc47a | |
| parent | 37cb29166bfa9abcf09db703d0e7ba5c694277c3 (diff) | |
| download | egit-a15b717a4b9138fb4d11fc07a26e9f9979b0562f.tar.gz egit-a15b717a4b9138fb4d11fc07a26e9f9979b0562f.tar.xz egit-a15b717a4b9138fb4d11fc07a26e9f9979b0562f.zip | |
Show detailed branch status in label decoration of project
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 854b814394..2f89cf654f 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 78b732dabd..3bf8caecb2 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 08bfabfdba..085439c933 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 1d6c6beec6..382a256b26 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 b8984a59fa..855dcf2fde 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 a94d9c9a0e..26238d55c4 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 6ab99a14d1..98758825f1 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 9162c62cd2..bda054ce92 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 62d2323ac8..d690a02b44 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: |
