diff --git a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin index e304e1efa..fa0daaa5f 100644 --- a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin +++ b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin @@ -23,7 +23,7 @@ org.eclipse.jgit.pgm.ReceivePack org.eclipse.jgit.pgm.RevList org.eclipse.jgit.pgm.RevParse org.eclipse.jgit.pgm.Rm -org.eclipse.jgit.pgm.ShowRev +org.eclipse.jgit.pgm.Show org.eclipse.jgit.pgm.ShowRef org.eclipse.jgit.pgm.Tag org.eclipse.jgit.pgm.UploadPack 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 49bab0374..a14f374f9 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 @@ -133,6 +133,8 @@ remoteSideDoesNotSupportDeletingRefs=remote side does not support deleting refs repaint=Repaint serviceNotSupported=Service '{0}' not supported skippingObject=skipping {0} {1} +tagLabel=tag +taggerInfo=Tagger: {0} <{1}> timeInMilliSeconds={0} ms tooManyRefsGiven=Too many refs given unknownMergeStrategy=unknown merge strategy {0} specified @@ -235,6 +237,8 @@ usage_resetHard=Resets the index and working tree usage_resetSoft=Resets without touching the index file nor the working tree usage_resetMixed=Resets the index but not the working tree usage_setTheGitRepositoryToOperateOn=set the git repository to operate on +usage_show=display one commit +usage_showRefNamesMatchingCommits=Show ref names matching commits usage_showPatch=display patch usage_showRefNamesMatchingCommits=Show ref names matching commits usage_showNotes=Add this ref to the list of note branches from which notes are displayed 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 3b815f457..954977e11 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 @@ -180,6 +180,8 @@ public class CLIText extends TranslationBundle { /***/ public String repaint; /***/ public String serviceNotSupported; /***/ public String skippingObject; + /***/ public String tagLabel; + /***/ public String taggerInfo; /***/ public String timeInMilliSeconds; /***/ public String tooManyRefsGiven; /***/ public String unknownMergeStrategy; diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java new file mode 100644 index 000000000..5eeb21492 --- /dev/null +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2010, Google Inc. + * Copyright (C) 2006-2008, Robin Rosenberg + * Copyright (C) 2008, Shawn O. Pearce + * 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; + +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.text.DateFormat; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.Locale; +import java.util.TimeZone; + +import org.eclipse.jgit.diff.DiffFormatter; +import org.eclipse.jgit.diff.RawTextComparator; +import org.eclipse.jgit.diff.RenameDetector; +import org.eclipse.jgit.errors.CorruptObjectException; +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.pgm.opt.PathTreeFilterHandler; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevObject; +import org.eclipse.jgit.revwalk.RevTag; +import org.eclipse.jgit.revwalk.RevTree; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.TreeFilter; +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.Option; + +@Command(common = true, usage = "usage_show") +class Show extends TextBuiltin { + private final TimeZone myTZ = TimeZone.getDefault(); + + private final DateFormat fmt; + + private final DiffFormatter diffFmt = new DiffFormatter( // + new BufferedOutputStream(System.out)); + + @Argument(index = 0, metaVar = "metaVar_object") + private String objectName; + + @Option(name = "--", metaVar = "metaVar_path", multiValued = true, handler = PathTreeFilterHandler.class) + protected TreeFilter pathFilter = TreeFilter.ALL; + + // BEGIN -- Options shared with Diff + @Option(name = "-p", usage = "usage_showPatch") + boolean showPatch; + + @Option(name = "-M", usage = "usage_detectRenames") + private Boolean detectRenames; + + @Option(name = "--no-renames", usage = "usage_noRenames") + void noRenames(@SuppressWarnings("unused") boolean on) { + detectRenames = Boolean.FALSE; + } + + @Option(name = "-l", usage = "usage_renameLimit") + private Integer renameLimit; + + @Option(name = "--name-status", usage = "usage_nameStatus") + private boolean showNameAndStatusOnly; + + @Option(name = "--ignore-space-at-eol") + void ignoreSpaceAtEol(@SuppressWarnings("unused") boolean on) { + diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_TRAILING); + } + + @Option(name = "--ignore-leading-space") + void ignoreLeadingSpace(@SuppressWarnings("unused") boolean on) { + diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_LEADING); + } + + @Option(name = "-b", aliases = { "--ignore-space-change" }) + void ignoreSpaceChange(@SuppressWarnings("unused") boolean on) { + diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_CHANGE); + } + + @Option(name = "-w", aliases = { "--ignore-all-space" }) + void ignoreAllSpace(@SuppressWarnings("unused") boolean on) { + diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_ALL); + } + + @Option(name = "-U", aliases = { "--unified" }, metaVar = "metaVar_linesOfContext") + void unified(int lines) { + diffFmt.setContext(lines); + } + + @Option(name = "--abbrev", metaVar = "metaVar_n") + void abbrev(int lines) { + diffFmt.setAbbreviationLength(lines); + } + + @Option(name = "--full-index") + void abbrev(@SuppressWarnings("unused") boolean on) { + diffFmt.setAbbreviationLength(Constants.OBJECT_ID_STRING_LENGTH); + } + + @Option(name = "--src-prefix", usage = "usage_srcPrefix") + void sourcePrefix(String path) { + diffFmt.setOldPrefix(path); + } + + @Option(name = "--dst-prefix", usage = "usage_dstPrefix") + void dstPrefix(String path) { + diffFmt.setNewPrefix(path); + } + + @Option(name = "--no-prefix", usage = "usage_noPrefix") + void noPrefix(@SuppressWarnings("unused") boolean on) { + diffFmt.setOldPrefix(""); + diffFmt.setNewPrefix(""); + } + + // END -- Options shared with Diff + + Show() { + fmt = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy ZZZZZ", Locale.US); + } + + @SuppressWarnings("boxing") + @Override + protected void run() throws Exception { + diffFmt.setRepository(db); + try { + diffFmt.setPathFilter(pathFilter); + if (detectRenames != null) + diffFmt.setDetectRenames(detectRenames.booleanValue()); + if (renameLimit != null && diffFmt.isDetectRenames()) { + RenameDetector rd = diffFmt.getRenameDetector(); + rd.setRenameLimit(renameLimit.intValue()); + } + + ObjectId objectId; + if (objectName == null) + objectId = db.resolve(Constants.HEAD); + else + objectId = db.resolve(objectName); + + RevWalk rw = new RevWalk(db); + try { + RevObject obj = rw.parseAny(objectId); + while (obj instanceof RevTag) { + show((RevTag) obj); + obj = ((RevTag) obj).getObject(); + rw.parseBody(obj); + } + + switch (obj.getType()) { + case Constants.OBJ_COMMIT: + show(rw, (RevCommit) obj); + break; + + case Constants.OBJ_TREE: + out.print("tree "); + out.print(objectName); + out.println(); + out.println(); + show((RevTree) obj); + break; + + case Constants.OBJ_BLOB: + db.open(obj, obj.getType()).copyTo(System.out); + System.out.flush(); + break; + + default: + throw die(MessageFormat.format( + CLIText.get().cannotReadBecause, obj.name(), + obj.getType())); + } + } finally { + rw.release(); + } + } finally { + diffFmt.release(); + } + } + + private void show(RevTag tag) { + out.print(CLIText.get().tagLabel); + out.print(" "); + out.print(tag.getTagName()); + out.println(); + + final PersonIdent tagger = tag.getTaggerIdent(); + if (tagger != null) { + out.println(MessageFormat.format(CLIText.get().taggerInfo, + tagger.getName(), tagger.getEmailAddress())); + + final TimeZone taggerTZ = tagger.getTimeZone(); + fmt.setTimeZone(taggerTZ != null ? taggerTZ : myTZ); + out.println(MessageFormat.format(CLIText.get().dateInfo, + fmt.format(tagger.getWhen()))); + } + + out.println(); + final String[] lines = tag.getFullMessage().split("\n"); + for (final String s : lines) { + out.print(" "); + out.print(s); + out.println(); + } + + out.println(); + } + + private void show(RevTree obj) throws MissingObjectException, + IncorrectObjectTypeException, CorruptObjectException, IOException { + final TreeWalk walk = new TreeWalk(db); + walk.reset(); + walk.addTree(obj); + + while (walk.next()) { + out.print(walk.getPathString()); + final FileMode mode = walk.getFileMode(0); + if (mode == FileMode.TREE) + out.print('/'); + out.println(); + } + } + + private void show(RevWalk rw, final RevCommit c) throws Exception { + char[] outbuffer = new char[Constants.OBJECT_ID_LENGTH * 2]; + + out.print(CLIText.get().commitLabel); + out.print(" "); + c.getId().copyTo(outbuffer, out); + out.println(); + + final PersonIdent author = c.getAuthorIdent(); + out.println(MessageFormat.format(CLIText.get().authorInfo, + author.getName(), author.getEmailAddress())); + + final TimeZone authorTZ = author.getTimeZone(); + fmt.setTimeZone(authorTZ != null ? authorTZ : myTZ); + out.println(MessageFormat.format(CLIText.get().dateInfo, + fmt.format(author.getWhen()))); + + out.println(); + final String[] lines = c.getFullMessage().split("\n"); + for (final String s : lines) { + out.print(" "); + out.print(s); + out.println(); + } + + out.println(); + if (c.getParentCount() == 1) { + rw.parseHeaders(c.getParent(0)); + showDiff(c); + } + out.flush(); + } + + private void showDiff(RevCommit c) throws IOException { + final RevTree a = c.getParent(0).getTree(); + final RevTree b = c.getTree(); + + if (showNameAndStatusOnly) + Diff.nameStatus(out, diffFmt.scan(a, b)); + else { + out.flush(); + diffFmt.format(a, b); + diffFmt.flush(); + } + out.println(); + } +}