diff options
Diffstat (limited to 'org.eclipse.jgit.pgm')
5 files changed, 349 insertions, 129 deletions
diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/CLIText.properties index 43b6f17768..3d6952bc93 100644 --- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/CLIText.properties +++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/CLIText.properties @@ -7,7 +7,6 @@ N=N alreadyOnBranch=Already on ''{0}'' alreadyUpToDate=Already up-to-date. -archiveEntryModeIgnored=warning: mode of {0} ignored authorInfo=Author: {0} <{1}> averageMSPerRead=average {0} ms/read branchAlreadyExists=A branch named ''{0}'' already exists. @@ -48,6 +47,7 @@ deletedRemoteBranch=Deleted remote branch {0} doesNotExist={0} does not exist dontOverwriteLocalChanges=error: Your local changes to the following file would be overwritten by merge: everythingUpToDate=Everything up-to-date +exceptionCaughtDuringExecutionOfArchiveCommand=Exception caught during execution of archive command expectedNumberOfbytes=Expected {0} bytes. exporting=Exporting {0} failedToCommitIndex=failed to commit index @@ -153,9 +153,17 @@ serviceNotSupported=Service ''{0}'' not supported skippingObject=skipping {0} {1} statusFileListFormat=\t%1$s statusFileListFormatWithPrefix=\t%1$-11s %2$s +statusFileListFormatUnmerged=\t%1$-20s%2$s statusModified=modified: statusNewFile=new file: statusRemoved=deleted: +statusBothDeleted=both deleted: +statusAddedByUs=added by us: +statusDeletedByThem=deleted by them: +statusAddedByThem=added by them: +statusDeletedByUs=deleted by us: +statusBothAdded=both added: +statusBothModified=both modified: switchedToNewBranch=Switched to a new branch ''{0}'' switchedToBranch=Switched to branch ''{0}'' tagAlreadyExists=tag ''{0}'' already exists diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java index 963528d106..5685aa4267 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java @@ -43,144 +43,33 @@ package org.eclipse.jgit.pgm; -import java.lang.String; -import java.lang.System; -import java.io.IOException; -import java.io.OutputStream; -import java.util.EnumMap; -import java.util.Map; -import java.text.MessageFormat; - -import org.apache.commons.compress.archivers.ArchiveOutputStream; -import org.apache.commons.compress.archivers.tar.TarArchiveEntry; -import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; -import org.apache.commons.compress.archivers.tar.TarConstants; -import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; -import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; -import org.eclipse.jgit.lib.FileMode; -import org.eclipse.jgit.lib.MutableObjectId; -import org.eclipse.jgit.lib.ObjectLoader; -import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.pgm.CLIText; import org.eclipse.jgit.pgm.TextBuiltin; -import org.eclipse.jgit.treewalk.AbstractTreeIterator; -import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.pgm.archive.ArchiveCommand; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.Option; @Command(common = true, usage = "usage_archive") class Archive extends TextBuiltin { @Argument(index = 0, metaVar = "metaVar_treeish") - private AbstractTreeIterator tree; + private ObjectId tree; @Option(name = "--format", metaVar = "metaVar_archiveFormat", usage = "usage_archiveFormat") - private Format format = Format.ZIP; + private ArchiveCommand.Format format = ArchiveCommand.Format.ZIP; @Override protected void run() throws Exception { - final TreeWalk walk = new TreeWalk(db); - final ObjectReader reader = walk.getObjectReader(); - final MutableObjectId idBuf = new MutableObjectId(); - final Archiver fmt = formats.get(format); - final ArchiveOutputStream outa = fmt.createArchiveOutputStream(outs); - if (tree == null) throw die(CLIText.get().treeIsRequired); - walk.reset(); - walk.addTree(tree); - walk.setRecursive(true); - while (walk.next()) { - final String name = walk.getPathString(); - final FileMode mode = walk.getFileMode(0); - - if (mode == FileMode.TREE) - // ZIP entries for directories are optional. - // Leave them out, mimicking "git archive". - continue; - - walk.getObjectId(idBuf, 0); - fmt.putEntry(name, mode, reader.open(idBuf), outa); + final ArchiveCommand cmd = new ArchiveCommand(db); + try { + cmd.setTree(tree) + .setFormat(format) + .setOutputStream(outs).call(); + } finally { + cmd.release(); } - - outa.close(); - } - - static private void warnArchiveEntryModeIgnored(String name) { - System.err.println(MessageFormat.format( // - CLIText.get().archiveEntryModeIgnored, // - name)); - } - - public enum Format { - ZIP, - TAR - } - - private static interface Archiver { - ArchiveOutputStream createArchiveOutputStream(OutputStream s); - void putEntry(String path, FileMode mode, // - ObjectLoader loader, ArchiveOutputStream out) // - throws IOException; - } - - private static final Map<Format, Archiver> formats; - static { - Map<Format, Archiver> fmts = new EnumMap<Format, Archiver>(Format.class); - fmts.put(Format.ZIP, new Archiver() { - public ArchiveOutputStream createArchiveOutputStream(OutputStream s) { - return new ZipArchiveOutputStream(s); - } - - public void putEntry(String path, FileMode mode, // - ObjectLoader loader, ArchiveOutputStream out) // - throws IOException { - final ZipArchiveEntry entry = new ZipArchiveEntry(path); - - if (mode == FileMode.REGULAR_FILE) { - // ok - } else if (mode == FileMode.EXECUTABLE_FILE - || mode == FileMode.SYMLINK) { - entry.setUnixMode(mode.getBits()); - } else { - warnArchiveEntryModeIgnored(path); - } - entry.setSize(loader.getSize()); - out.putArchiveEntry(entry); - loader.copyTo(out); - out.closeArchiveEntry(); - } - }); - fmts.put(Format.TAR, new Archiver() { - public ArchiveOutputStream createArchiveOutputStream(OutputStream s) { - return new TarArchiveOutputStream(s); - } - - public void putEntry(String path, FileMode mode, // - ObjectLoader loader, ArchiveOutputStream out) // - throws IOException { - if (mode == FileMode.SYMLINK) { - final TarArchiveEntry entry = new TarArchiveEntry( // - path, TarConstants.LF_SYMLINK); - entry.setLinkName(new String( // - loader.getCachedBytes(100), "UTF-8")); //$NON-NLS-1$ - out.putArchiveEntry(entry); - out.closeArchiveEntry(); - return; - } - - final TarArchiveEntry entry = new TarArchiveEntry(path); - if (mode == FileMode.REGULAR_FILE || - mode == FileMode.EXECUTABLE_FILE) - entry.setMode(mode.getBits()); - else - warnArchiveEntryModeIgnored(path); - entry.setSize(loader.getSize()); - out.putArchiveEntry(entry); - loader.copyTo(out); - out.closeArchiveEntry(); - } - }); - formats = fmts; } } diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java index 1f14fa272b..c6f913c681 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, 2012 Sasa Zivkov <sasa.zivkov@sap.com> + * Copyright (C) 2010, 2013 Sasa Zivkov <sasa.zivkov@sap.com> * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -76,7 +76,6 @@ public class CLIText extends TranslationBundle { // @formatter:off /***/ public String alreadyOnBranch; /***/ public String alreadyUpToDate; - /***/ public String archiveEntryModeIgnored; /***/ public String authorInfo; /***/ public String averageMSPerRead; /***/ public String branchAlreadyExists; @@ -117,6 +116,7 @@ public class CLIText extends TranslationBundle { /***/ public String doesNotExist; /***/ public String dontOverwriteLocalChanges; /***/ public String everythingUpToDate; + /***/ public String exceptionCaughtDuringExecutionOfArchiveCommand; /***/ public String expectedNumberOfbytes; /***/ public String exporting; /***/ public String failedToCommitIndex; @@ -216,9 +216,17 @@ public class CLIText extends TranslationBundle { /***/ public String skippingObject; /***/ public String statusFileListFormat; /***/ public String statusFileListFormatWithPrefix; + /***/ public String statusFileListFormatUnmerged; /***/ public String statusModified; /***/ public String statusNewFile; /***/ public String statusRemoved; + /***/ public String statusBothDeleted; + /***/ public String statusAddedByUs; + /***/ public String statusDeletedByThem; + /***/ public String statusAddedByThem; + /***/ public String statusDeletedByUs; + /***/ public String statusBothAdded; + /***/ public String statusBothModified; /***/ public String switchedToNewBranch; /***/ public String switchedToBranch; /***/ public String tagAlreadyExists; diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java index 4d2308e214..c1b635ca9f 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, François Rey <eclipse.org_@_francois_._rey_._name> + * Copyright (C) 2011, 2013 François Rey <eclipse.org_@_francois_._rey_._name> * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -47,10 +47,13 @@ import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Map; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.IndexDiff.StageState; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; @@ -63,6 +66,8 @@ class Status extends TextBuiltin { protected final String statusFileListFormatWithPrefix = CLIText.get().statusFileListFormatWithPrefix; + protected final String statusFileListFormatUnmerged = CLIText.get().statusFileListFormatUnmerged; + @Override protected void run() throws Exception { // Print current branch name @@ -82,7 +87,8 @@ class Status extends TextBuiltin { Collection<String> modified = status.getModified(); Collection<String> missing = status.getMissing(); Collection<String> untracked = status.getUntracked(); - Collection<String> unmerged = status.getConflicting(); + Map<String, StageState> unmergedStates = status + .getConflictingStageState(); Collection<String> toBeCommitted = new ArrayList<String>(added); toBeCommitted.addAll(changed); toBeCommitted.addAll(removed); @@ -106,12 +112,12 @@ class Status extends TextBuiltin { modified, missing, null); firstHeader = false; } - int nbUnmerged = unmerged.size(); + int nbUnmerged = unmergedStates.size(); if (nbUnmerged > 0) { if (!firstHeader) printSectionHeader(""); //$NON-NLS-1$ printSectionHeader(CLIText.get().unmergedPaths); - printList(unmerged); + printUnmerged(unmergedStates); firstHeader = false; } int nbUntracked = untracked.size(); @@ -168,4 +174,40 @@ class Status extends TextBuiltin { } return list.size(); } + + private void printUnmerged(Map<String, StageState> unmergedStates) + throws IOException { + List<String> paths = new ArrayList<String>(unmergedStates.keySet()); + Collections.sort(paths); + for (String path : paths) { + StageState state = unmergedStates.get(path); + String stateDescription = getStageStateDescription(state); + outw.println(CLIText.formatLine(String.format( + statusFileListFormatUnmerged, stateDescription, path))); + outw.flush(); + } + } + + private static String getStageStateDescription(StageState stageState) { + CLIText text = CLIText.get(); + switch (stageState) { + case BOTH_DELETED: + return text.statusBothDeleted; + case ADDED_BY_US: + return text.statusAddedByUs; + case DELETED_BY_THEM: + return text.statusDeletedByThem; + case ADDED_BY_THEM: + return text.statusAddedByThem; + case DELETED_BY_US: + return text.statusDeletedByUs; + case BOTH_ADDED: + return text.statusBothAdded; + case BOTH_MODIFIED: + return text.statusBothModified; + default: + throw new IllegalArgumentException("Unknown StageState: " //$NON-NLS-1$ + + stageState); + } + } } diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/archive/ArchiveCommand.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/archive/ArchiveCommand.java new file mode 100644 index 0000000000..7c2e7c0993 --- /dev/null +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/archive/ArchiveCommand.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2012 Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.pgm.archive; + +import java.lang.String; +import java.io.IOException; +import java.io.OutputStream; +import java.util.EnumMap; +import java.util.Map; +import java.text.MessageFormat; + +import org.apache.commons.compress.archivers.ArchiveOutputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.apache.commons.compress.archivers.tar.TarConstants; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; +import org.eclipse.jgit.api.GitCommand; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.api.errors.JGitInternalException; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.MutableObjectId; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectLoader; +import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.pgm.CLIText; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.treewalk.TreeWalk; + +/** + * Create an archive of files from a named tree. + * <p> + * Examples (<code>git</code> is a {@link Git} instance): + * <p> + * Create a tarball from HEAD: + * + * <pre> + * cmd = new ArchiveCommand(git.getRepository()); + * try { + * cmd.setTree(db.resolve("HEAD")) + * .setOutputStream(out).call(); + * } finally { + * cmd.release(); + * } + * </pre> + * <p> + * Create a ZIP file from master: + * + * <pre> + * try { + * cmd.setTree(db.resolve("master")) + * .setFormat(ArchiveCommand.Format.ZIP) + * .setOutputStream(out).call(); + * } finally { + * cmd.release(); + * } + * </pre> + * + * @see <a href="http://git-htmldocs.googlecode.com/git/git-archive.html" + * >Git documentation about archive</a> + */ +public class ArchiveCommand extends GitCommand<OutputStream> { + /** + * Available archival formats (corresponding to values for + * the --format= option) + */ + public static enum Format { + ZIP, + TAR + } + + private static interface Archiver { + ArchiveOutputStream createArchiveOutputStream(OutputStream s); + void putEntry(String path, FileMode mode, // + ObjectLoader loader, ArchiveOutputStream out) // + throws IOException; + } + + private static final Map<Format, Archiver> formats; + + static { + Map<Format, Archiver> fmts = new EnumMap<Format, Archiver>(Format.class); + fmts.put(Format.ZIP, new Archiver() { + public ArchiveOutputStream createArchiveOutputStream(OutputStream s) { + return new ZipArchiveOutputStream(s); + } + + public void putEntry(String path, FileMode mode, // + ObjectLoader loader, ArchiveOutputStream out) // + throws IOException { + final ZipArchiveEntry entry = new ZipArchiveEntry(path); + + if (mode == FileMode.REGULAR_FILE) { + // ok + } else if (mode == FileMode.EXECUTABLE_FILE + || mode == FileMode.SYMLINK) { + entry.setUnixMode(mode.getBits()); + } else { + // TODO(jrn): Let the caller know the tree contained + // an entry with unsupported mode (e.g., a submodule). + } + entry.setSize(loader.getSize()); + out.putArchiveEntry(entry); + loader.copyTo(out); + out.closeArchiveEntry(); + } + }); + fmts.put(Format.TAR, new Archiver() { + public ArchiveOutputStream createArchiveOutputStream(OutputStream s) { + return new TarArchiveOutputStream(s); + } + + public void putEntry(String path, FileMode mode, // + ObjectLoader loader, ArchiveOutputStream out) // + throws IOException { + if (mode == FileMode.SYMLINK) { + final TarArchiveEntry entry = new TarArchiveEntry( // + path, TarConstants.LF_SYMLINK); + entry.setLinkName(new String( // + loader.getCachedBytes(100), "UTF-8")); //$NON-NLS-1$ + out.putArchiveEntry(entry); + out.closeArchiveEntry(); + return; + } + + final TarArchiveEntry entry = new TarArchiveEntry(path); + if (mode == FileMode.REGULAR_FILE || + mode == FileMode.EXECUTABLE_FILE) { + entry.setMode(mode.getBits()); + } else { + // TODO(jrn): Let the caller know the tree contained + // an entry with unsupported mode (e.g., a submodule). + } + entry.setSize(loader.getSize()); + out.putArchiveEntry(entry); + loader.copyTo(out); + out.closeArchiveEntry(); + } + }); + formats = fmts; + } + + private OutputStream out; + private TreeWalk walk; + private Format format = Format.TAR; + + /** + * @param repo + */ + public ArchiveCommand(Repository repo) { + super(repo); + walk = new TreeWalk(repo); + } + + /** + * Release any resources used by the internal ObjectReader. + * <p> + * This does not close the output stream set with setOutputStream, which + * belongs to the caller. + */ + public void release() { + walk.release(); + } + + /** + * @return the stream to which the archive has been written + */ + @Override + public OutputStream call() throws GitAPIException { + final MutableObjectId idBuf = new MutableObjectId(); + final Archiver fmt = formats.get(format); + final ArchiveOutputStream outa = fmt.createArchiveOutputStream(out); + final ObjectReader reader = walk.getObjectReader(); + + try { + try { + walk.setRecursive(true); + while (walk.next()) { + final String name = walk.getPathString(); + final FileMode mode = walk.getFileMode(0); + + if (mode == FileMode.TREE) + // ZIP entries for directories are optional. + // Leave them out, mimicking "git archive". + continue; + + walk.getObjectId(idBuf, 0); + fmt.putEntry(name, mode, reader.open(idBuf), outa); + } + } finally { + outa.close(); + } + } catch (IOException e) { + // TODO(jrn): Throw finer-grained errors. + throw new JGitInternalException( + CLIText.get().exceptionCaughtDuringExecutionOfArchiveCommand, e); + } + + return out; + } + + /** + * @param tree + * the tag, commit, or tree object to produce an archive for + * @return this + */ + public ArchiveCommand setTree(ObjectId tree) throws IOException { + final RevWalk rw = new RevWalk(walk.getObjectReader()); + walk.reset(rw.parseTree(tree)); + return this; + } + + /** + * @param out + * the stream to which to write the archive + * @return this + */ + public ArchiveCommand setOutputStream(OutputStream out) { + this.out = out; + return this; + } + + /** + * @param fmt + * archive format (e.g., Format.TAR) + * @return this + */ + public ArchiveCommand setFormat(Format fmt) { + this.format = fmt; + return this; + } +} |