James Moger
2012-01-30 2dfa21c4e10e33ef2b736353ae6c2b31fc0a4fd7
Merge pull request #5 from plm/admin_default_head

Allow administrative modification of the default branch/tag referenced by HEAD
6 files modified
132 ■■■■■ changed files
src/com/gitblit/GitBlit.java 7 ●●●●● patch | view | raw | blame | history
src/com/gitblit/models/RepositoryModel.java 2 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/JGitUtils.java 94 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp.properties 2 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/EditRepositoryPage.html 25 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/EditRepositoryPage.java 2 ●●●●● patch | view | raw | blame | history
src/com/gitblit/GitBlit.java
@@ -786,6 +786,8 @@
            model.mailingLists = new ArrayList<String>(Arrays.asList(config.getStringList(
                    "gitblit", null, "mailingList")));
        }
        model.defaultHead = JGitUtils.getSymbolicHeadTarget(r);
        model.availableHeads = JGitUtils.getAvailableHeadTargets(r);
        r.close();
        return model;
    }
@@ -981,6 +983,11 @@
        // update settings
        if (r != null) {
            updateConfiguration(r, repository);
            // only update symbolic head if it changes
            if (!StringUtils.isEmpty(repository.defaultHead) &&
                    !repository.defaultHead.equals(JGitUtils.getSymbolicHeadTarget(r))) {
                JGitUtils.setSymbolicHeadTarget(r, repository.defaultHead);
            }
            r.close();
        }
    }
src/com/gitblit/models/RepositoryModel.java
@@ -58,6 +58,8 @@
    public List<String> preReceiveScripts;
    public List<String> postReceiveScripts;
    public List<String> mailingLists;
    public String defaultHead;
    public List<String> availableHeads;
    private String displayName;
    
src/com/gitblit/utils/JGitUtils.java
@@ -1156,6 +1156,100 @@
    }
    /**
     * Returns the target of the symbolic HEAD reference for a repository.
     * Normally returns a branch reference name, but when HEAD is detached,
     * the commit is matched against the known tags. The most recent matching
     * tag ref name will be returned if it references the HEAD commit. If
     * no match is found, the SHA1 is returned.
     *
     * @param repository
     * @return the ref name or the SHA1 for detached HEADs
     */
    public static String getSymbolicHeadTarget(Repository repository) {
        String target = null;
        try {
            target = repository.getFullBranch();
            if (!target.startsWith(Constants.R_HEADS)) {
                // refers to an actual commit, probably a tag
                // find latest tag that matches the commit, if any
                List<RefModel> tagModels = getTags(repository, true, -1);
                if (tagModels.size() > 0) {
                    RefModel tag = null;
                    Date lastDate = new Date(0);
                    for (RefModel tagModel : tagModels) {
                        if (tagModel.getReferencedObjectId().getName().equals(target) &&
                                tagModel.getDate().after(lastDate)) {
                            tag = tagModel;
                            lastDate = tag.getDate();
                        }
                    }
                    target = tag.getName();
                }
            }
        } catch (Throwable t) {
            error(t, repository, "{0} failed to get symbolic HEAD target");
        }
        return target;
    }
    /**
     * Sets the HEAD symbolic ref name for a repository. The HEAD will
     * be detached if the name does not reference a branch.
     *
     * @param repository
     * @param name
     */
    public static void setSymbolicHeadTarget(Repository repository, String name) {
        try {
            boolean detach = !name.startsWith(Constants.R_HEADS); // detach if not a branch
            RefUpdate.Result result;
            RefUpdate head = repository.updateRef(Constants.HEAD, detach);
            if (detach) { // Tag
                RevCommit commit = getCommit(repository, name);
                head.setNewObjectId(commit.getId());
                result = head.forceUpdate();
            } else {
                result = head.link(name);
            }
            switch (result) {
            case NEW:
            case FORCED:
            case NO_CHANGE:
            case FAST_FORWARD:
                break;
            default:
                LOGGER.error(MessageFormat.format("{0} symbolic HEAD update to {1} returned result {2}",
                        repository.getDirectory().getAbsolutePath(), name, result));
            }
        } catch (Throwable t) {
            error(t, repository, "{0} failed to set symbolic HEAD to {1}", name);
        }
    }
    /**
     * Get the full branch and tag ref names for any potential symbolic head targets.
     *
     * @param repository
     * @return a list of ref names
     */
    public static List<String> getAvailableHeadTargets(Repository repository) {
        List<String> targets = new ArrayList<String>();
        List<RefModel> branchModels = JGitUtils.getLocalBranches(repository, true, -1);
        if (branchModels.size() > 0) {
            for (RefModel branchModel : branchModels) {
                targets.add(branchModel.getName());
            }
        }
        List<RefModel> tagModels = JGitUtils.getTags(repository, true, -1);
        if (tagModels.size() > 0) {
            for (RefModel tagModel : tagModels) {
                targets.add(tagModel.getName());
            }
        }
        return targets;
    }
    /**
     * Returns all refs grouped by their associated object id.
     * 
     * @param repository
src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -131,6 +131,8 @@
gb.sendProposal propose
gb.status = status
gb.origin = origin
gb.defaultHead = default HEAD
gb.defaultHeadDescription = current branch after clone. e.g. refs/heads/master
gb.federationStrategy = federation strategy
gb.federationRegistration = federation registration
gb.federationResults = federation pull results
src/com/gitblit/wicket/pages/EditRepositoryPage.html
@@ -14,26 +14,27 @@
                <tr><th><wicket:message key="gb.name"></wicket:message></th><td class="edit"><input class="span6" type="text" wicket:id="name" id="name" size="40" tabindex="1" /> &nbsp;<span class="help-inline"><wicket:message key="gb.nameDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.description"></wicket:message></th><td class="edit"><input class="span6" type="text" wicket:id="description" size="40" tabindex="2" /></td></tr>
                <tr><th><wicket:message key="gb.origin"></wicket:message></th><td class="edit"><input class="span7" type="text" wicket:id="origin" size="80" tabindex="3" /></td></tr>
                <tr><th><wicket:message key="gb.owner"></wicket:message></th><td class="edit"><select wicket:id="owner" tabindex="4" /> &nbsp;<span class="help-inline"><wicket:message key="gb.ownerDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.enableTickets"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="useTickets" tabindex="5" /> &nbsp;<span class="help-inline"><wicket:message key="gb.useTicketsDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.enableDocs"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="useDocs" tabindex="6" /> &nbsp;<span class="help-inline"><wicket:message key="gb.useDocsDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.showRemoteBranches"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="showRemoteBranches" tabindex="7" /> &nbsp;<span class="help-inline"><wicket:message key="gb.showRemoteBranchesDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.showReadme"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="showReadme" tabindex="8" /> &nbsp;<span class="help-inline"><wicket:message key="gb.showReadmeDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.skipSizeCalculation"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="skipSizeCalculation" tabindex="9" /> &nbsp;<span class="help-inline"><wicket:message key="gb.skipSizeCalculationDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.skipSummaryMetrics"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="skipSummaryMetrics" tabindex="10" /> &nbsp;<span class="help-inline"><wicket:message key="gb.skipSummaryMetricsDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.isFrozen"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="isFrozen" tabindex="11" /> &nbsp;<span class="help-inline"><wicket:message key="gb.isFrozenDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.mailingLists"></wicket:message></th><td class="edit"><input class="span14" type="text" wicket:id="mailingLists" size="40" tabindex="12" /></td></tr>
                <tr><th><wicket:message key="gb.defaultHead"></wicket:message></th><td class="edit"><select wicket:id="defaultHead" tabindex="4" /> &nbsp;<span class="help-inline"><wicket:message key="gb.defaultHeadDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.owner"></wicket:message></th><td class="edit"><select wicket:id="owner" tabindex="5" /> &nbsp;<span class="help-inline"><wicket:message key="gb.ownerDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.enableTickets"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="useTickets" tabindex="6" /> &nbsp;<span class="help-inline"><wicket:message key="gb.useTicketsDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.enableDocs"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="useDocs" tabindex="7" /> &nbsp;<span class="help-inline"><wicket:message key="gb.useDocsDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.showRemoteBranches"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="showRemoteBranches" tabindex="8" /> &nbsp;<span class="help-inline"><wicket:message key="gb.showRemoteBranchesDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.showReadme"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="showReadme" tabindex="9" /> &nbsp;<span class="help-inline"><wicket:message key="gb.showReadmeDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.skipSizeCalculation"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="skipSizeCalculation" tabindex="10" /> &nbsp;<span class="help-inline"><wicket:message key="gb.skipSizeCalculationDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.skipSummaryMetrics"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="skipSummaryMetrics" tabindex="11" /> &nbsp;<span class="help-inline"><wicket:message key="gb.skipSummaryMetricsDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.isFrozen"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="isFrozen" tabindex="12" /> &nbsp;<span class="help-inline"><wicket:message key="gb.isFrozenDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.mailingLists"></wicket:message></th><td class="edit"><input class="span14" type="text" wicket:id="mailingLists" size="40" tabindex="13" /></td></tr>
                <tr><td colspan="2" style="padding-top:15px"><h3><wicket:message key="gb.accessPermissions"></wicket:message> &nbsp;<small><wicket:message key="gb.accessPermissionsDescription"></wicket:message></small></h3></td></tr>    
                <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select class="span6" wicket:id="accessRestriction" tabindex="13" /></td></tr>
                <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select class="span6" wicket:id="accessRestriction" tabindex="14" /></td></tr>
                <tr><th style="vertical-align: top;"><wicket:message key="gb.permittedUsers"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr>
                <tr><th style="vertical-align: top;"><wicket:message key="gb.permittedTeams"></wicket:message></th><td style="padding:2px;"><span wicket:id="teams"></span></td></tr>
                <tr><td colspan="2"><h3><wicket:message key="gb.federation"></wicket:message> &nbsp;<small><wicket:message key="gb.federationRepositoryDescription"></wicket:message></small></h3></td></tr>    
                <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span6" wicket:id="federationStrategy" tabindex="14" /></td></tr>
                <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span6" wicket:id="federationStrategy" tabindex="15" /></td></tr>
                <tr><th style="vertical-align: top;"><wicket:message key="gb.federationSets"></wicket:message></th><td style="padding:2px;"><span wicket:id="federationSets"></span></td></tr>
                <tr><td colspan="2"><h3><wicket:message key="gb.hookScripts"></wicket:message> &nbsp;<small><wicket:message key="gb.hookScriptsDescription"></wicket:message></small></h3></td></tr>    
                <tr><th style="vertical-align: top;"><wicket:message key="gb.preReceiveScripts"></wicket:message><p></p><span wicket:id="inheritedPreReceive"></span></th><td style="padding:2px;"><span wicket:id="preReceiveScripts"></span></td></tr>
                <tr><th style="vertical-align: top;"><wicket:message key="gb.postReceiveScripts"></wicket:message><p></p><span wicket:id="inheritedPostReceive"></span></th><td style="padding:2px;"><span wicket:id="postReceiveScripts"></span></td></tr>
                <tr><td colspan='2'><div class="actions" "><input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" tabindex="15" /> &nbsp; <input class="btn primary" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" tabindex="16" /></div></td></tr>
                <tr><td colspan='2'><div class="actions" "><input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" tabindex="16" /> &nbsp; <input class="btn primary" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" tabindex="16" /></div></td></tr>
            </tbody>
        </table>
    </form>    
src/com/gitblit/wicket/pages/EditRepositoryPage.java
@@ -271,6 +271,8 @@
        form.add(new CheckBox("isFrozen"));
        // TODO enable origin definition
        form.add(new TextField<String>("origin").setEnabled(false/* isCreate */));
        // enable alteration of the default branch after clone
        form.add(new DropDownChoice<String>("defaultHead", repositoryModel.availableHeads));
        // federation strategies - remove ORIGIN choice if this repository has
        // no origin.