From bbcbcab8d39dd4bdf194cdba02c97a9eb70b2379 Mon Sep 17 00:00:00 2001 From: Terry Parker Date: Mon, 9 Nov 2015 18:55:18 -0800 Subject: [PATCH 01/50] Remove PackWriter.Statistics and other deprecated classes These classes make improper use of internal classes in the public API and were replaced by corresponding classes in the JGit 4.1 release. Change-Id: I3d474210e49089aa788314b4e08f505f0d26619b Signed-off-by: Terry Parker --- .../internal/storage/pack/PackWriter.java | 286 ------------------ .../eclipse/jgit/transport/UploadPack.java | 40 --- .../jgit/transport/UploadPackLogger.java | 75 ----- .../jgit/transport/UploadPackLoggerChain.java | 96 ------ 4 files changed, 497 deletions(-) delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java delete mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java index 19b6b080d..e31f2412c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java @@ -2057,292 +2057,6 @@ private boolean reuseDeltaFor(ObjectToPack otp) { return true; } - /** - * Summary of how PackWriter created the pack. - * - * @deprecated Use {@link PackStatistics} instead. - */ - @Deprecated - public static class Statistics { - /** Statistics about a single class of object. */ - public static class ObjectType { - // All requests are forwarded to this object. - private PackStatistics.ObjectType objectType; - - /** - * Wraps an - * {@link org.eclipse.jgit.storage.pack.PackStatistics.ObjectType} - * instance to maintain backwards compatibility with existing API. - * - * @param type - * the wrapped instance - */ - public ObjectType(PackStatistics.ObjectType type) { - objectType = type; - } - - /** - * @return total number of objects output. This total includes the - * value of {@link #getDeltas()}. - */ - public long getObjects() { - return objectType.getObjects(); - } - - /** - * @return total number of deltas output. This may be lower than the - * actual number of deltas if a cached pack was reused. - */ - public long getDeltas() { - return objectType.getDeltas(); - } - - /** - * @return number of objects whose existing representation was - * reused in the output. This count includes - * {@link #getReusedDeltas()}. - */ - public long getReusedObjects() { - return objectType.getReusedObjects(); - } - - /** - * @return number of deltas whose existing representation was reused - * in the output, as their base object was also output or - * was assumed present for a thin pack. This may be lower - * than the actual number of reused deltas if a cached pack - * was reused. - */ - public long getReusedDeltas() { - return objectType.getReusedDeltas(); - } - - /** - * @return total number of bytes written. This size includes the - * object headers as well as the compressed data. This size - * also includes all of {@link #getDeltaBytes()}. - */ - public long getBytes() { - return objectType.getBytes(); - } - - /** - * @return number of delta bytes written. This size includes the - * object headers for the delta objects. - */ - public long getDeltaBytes() { - return objectType.getDeltaBytes(); - } - } - - // All requests are forwarded to this object. - private PackStatistics statistics; - - /** - * Wraps a {@link PackStatistics} object to maintain backwards - * compatibility with existing API. - * - * @param stats - * the wrapped PackStatitics object - */ - public Statistics(PackStatistics stats) { - statistics = stats; - } - - /** - * @return unmodifiable collection of objects to be included in the - * pack. May be null if the pack was hand-crafted in a unit - * test. - */ - public Set getInterestingObjects() { - return statistics.getInterestingObjects(); - } - - /** - * @return unmodifiable collection of objects that should be excluded - * from the pack, as the peer that will receive the pack already - * has these objects. - */ - public Set getUninterestingObjects() { - return statistics.getUninterestingObjects(); - } - - /** - * @return unmodifiable collection of the cached packs that were reused - * in the output, if any were selected for reuse. - */ - public Collection getReusedPacks() { - return statistics.getReusedPacks(); - } - - /** - * @return number of objects in the output pack that went through the - * delta search process in order to find a potential delta base. - */ - public int getDeltaSearchNonEdgeObjects() { - return statistics.getDeltaSearchNonEdgeObjects(); - } - - /** - * @return number of objects in the output pack that went through delta - * base search and found a suitable base. This is a subset of - * {@link #getDeltaSearchNonEdgeObjects()}. - */ - public int getDeltasFound() { - return statistics.getDeltasFound(); - } - - /** - * @return total number of objects output. This total includes the value - * of {@link #getTotalDeltas()}. - */ - public long getTotalObjects() { - return statistics.getTotalObjects(); - } - - /** - * @return the count of objects that needed to be discovered through an - * object walk because they were not found in bitmap indices. - * Returns -1 if no bitmap indices were found. - * - * @since 4.0 - */ - public long getBitmapIndexMisses() { - return statistics.getBitmapIndexMisses(); - } - - /** - * @return total number of deltas output. This may be lower than the - * actual number of deltas if a cached pack was reused. - */ - public long getTotalDeltas() { - return statistics.getTotalDeltas(); - } - - /** - * @return number of objects whose existing representation was reused in - * the output. This count includes {@link #getReusedDeltas()}. - */ - public long getReusedObjects() { - return statistics.getReusedObjects(); - } - - /** - * @return number of deltas whose existing representation was reused in - * the output, as their base object was also output or was - * assumed present for a thin pack. This may be lower than the - * actual number of reused deltas if a cached pack was reused. - */ - public long getReusedDeltas() { - return statistics.getReusedDeltas(); - } - - /** - * @return total number of bytes written. This size includes the pack - * header, trailer, thin pack, and reused cached pack(s). - */ - public long getTotalBytes() { - return statistics.getTotalBytes(); - } - - /** - * @return size of the thin pack in bytes, if a thin pack was generated. - * A thin pack is created when the client already has objects - * and some deltas are created against those objects, or if a - * cached pack is being used and some deltas will reference - * objects in the cached pack. This size does not include the - * pack header or trailer. - */ - public long getThinPackBytes() { - return statistics.getThinPackBytes(); - } - - /** - * @param typeCode - * object type code, e.g. OBJ_COMMIT or OBJ_TREE. - * @return information about this type of object in the pack. - */ - public ObjectType byObjectType(int typeCode) { - return new ObjectType(statistics.byObjectType(typeCode)); - } - - /** @return true if the resulting pack file was a shallow pack. */ - public boolean isShallow() { - return statistics.isShallow(); - } - - /** @return depth (in commits) the pack includes if shallow. */ - public int getDepth() { - return statistics.getDepth(); - } - - /** - * @return time in milliseconds spent enumerating the objects that need - * to be included in the output. This time includes any restarts - * that occur when a cached pack is selected for reuse. - */ - public long getTimeCounting() { - return statistics.getTimeCounting(); - } - - /** - * @return time in milliseconds spent matching existing representations - * against objects that will be transmitted, or that the client - * can be assumed to already have. - */ - public long getTimeSearchingForReuse() { - return statistics.getTimeSearchingForReuse(); - } - - /** - * @return time in milliseconds spent finding the sizes of all objects - * that will enter the delta compression search window. The - * sizes need to be known to better match similar objects - * together and improve delta compression ratios. - */ - public long getTimeSearchingForSizes() { - return statistics.getTimeSearchingForSizes(); - } - - /** - * @return time in milliseconds spent on delta compression. This is - * observed wall-clock time and does not accurately track CPU - * time used when multiple threads were used to perform the - * delta compression. - */ - public long getTimeCompressing() { - return statistics.getTimeCompressing(); - } - - /** - * @return time in milliseconds spent writing the pack output, from - * start of header until end of trailer. The transfer speed can - * be approximated by dividing {@link #getTotalBytes()} by this - * value. - */ - public long getTimeWriting() { - return statistics.getTimeWriting(); - } - - /** @return total time spent processing this pack. */ - public long getTimeTotal() { - return statistics.getTimeTotal(); - } - - /** - * @return get the average output speed in terms of bytes-per-second. - * {@code getTotalBytes() / (getTimeWriting() / 1000.0)}. - */ - public double getTransferRate() { - return statistics.getTransferRate(); - } - - /** @return formatted message string for display to clients. */ - public String getMessage() { - return statistics.getMessage(); - } - } - private class MutableState { /** Estimated size of a single ObjectToPack instance. */ // Assume 64-bit pointers, since this is just an estimate. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java index 101057fb4..f9ab56d95 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -312,8 +312,6 @@ public Set getOptions() { private PackStatistics statistics; - private UploadPackLogger logger = UploadPackLogger.NULL; - /** * Create a new pack upload for an open repository. * @@ -585,28 +583,6 @@ public void setTransferConfig(TransferConfig tc) { } } - /** - * @return the configured logger. - * - * @deprecated Use {@link #getPreUploadHook()}. - */ - @Deprecated - public UploadPackLogger getLogger() { - return logger; - } - - /** - * Set the logger. - * - * @param logger - * the logger instance. If null, no logging occurs. - * @deprecated Use {@link #setPreUploadHook(PreUploadHook)}. - */ - @Deprecated - public void setLogger(UploadPackLogger logger) { - this.logger = logger; - } - /** * Check whether the client expects a side-band stream. * @@ -677,21 +653,6 @@ public void upload(final InputStream input, final OutputStream output, } } - /** - * Get the PackWriter's statistics if a pack was sent to the client. - * - * @return statistics about pack output, if a pack was sent. Null if no pack - * was sent, such as during the negotation phase of a smart HTTP - * connection, or if the client was already up-to-date. - * @since 3.0 - * @deprecated Use {@link #getStatistics()}. - */ - @Deprecated - public PackWriter.Statistics getPackStatistics() { - return statistics == null ? null - : new PackWriter.Statistics(statistics); - } - /** * Get the PackWriter's statistics if a pack was sent to the client. * @@ -1547,7 +1508,6 @@ else if (ref.getName().startsWith(Constants.R_HEADS)) statistics = pw.getStatistics(); if (statistics != null) { postUploadHook.onPostUpload(statistics); - logger.onPackStatistics(new PackWriter.Statistics(statistics)); } pw.close(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java deleted file mode 100644 index 85ebecc45..000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2011, 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.transport; - -import org.eclipse.jgit.internal.storage.pack.PackWriter; - -/** - * Logs activity that occurred within {@link UploadPack}. - *

- * Implementors of the interface are responsible for associating the current - * thread to a particular connection, if they need to also include connection - * information. One method is to use a {@link java.lang.ThreadLocal} to remember - * the connection information before invoking UploadPack. - * - * @deprecated use {@link PostUploadHook} instead - */ -@Deprecated -public interface UploadPackLogger { - /** A simple no-op logger. */ - public static final UploadPackLogger NULL = new UploadPackLogger() { - public void onPackStatistics(PackWriter.Statistics stats) { - // Do nothing. - } - }; - - /** - * Notice to the logger after a pack has been sent. - * - * @param stats - * the statistics after sending a pack to the client. - * @since 3.0 - */ - public void onPackStatistics(PackWriter.Statistics stats); -} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java deleted file mode 100644 index 4ea0319d9..000000000 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2011, 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.transport; - -import java.util.List; - -import org.eclipse.jgit.internal.storage.pack.PackWriter; - -/** - * UploadPackLogger that delegates to a list of other loggers. - *

- * loggers are run in the order passed to the constructor. - * - * @deprecated Use {@link PostUploadHookChain} instead. - */ -@Deprecated -public class UploadPackLoggerChain implements UploadPackLogger { - private final UploadPackLogger[] loggers; - private final int count; - - /** - * Create a new logger chaining the given loggers together. - * - * @param loggers - * loggers to execute, in order. - * @return a new logger chain of the given loggers. - */ - public static UploadPackLogger newChain( - List loggers) { - UploadPackLogger[] newLoggers = new UploadPackLogger[loggers.size()]; - int i = 0; - for (UploadPackLogger logger : loggers) - if (logger != UploadPackLogger.NULL) - newLoggers[i++] = logger; - if (i == 0) - return UploadPackLogger.NULL; - else if (i == 1) - return newLoggers[0]; - else - return new UploadPackLoggerChain(newLoggers, i); - } - - /** - * @since 3.0 - */ - public void onPackStatistics(PackWriter.Statistics stats) { - for (int i = 0; i < count; i++) - loggers[i].onPackStatistics(stats); - } - - private UploadPackLoggerChain(UploadPackLogger[] loggers, int count) { - this.loggers = loggers; - this.count = count; - } -} From 1fa6f3a7509dc937551006e11dfd3bdaea921cd0 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 10 Nov 2015 20:14:01 -0500 Subject: [PATCH 02/50] Revert "Remove PackWriter.Statistics and other deprecated classes" This reverts commit bbcbcab8d39dd4bdf194cdba02c97a9eb70b2379. These classes were part of the public API and should not be removed until JGit 5.0. Change-Id: Ife4bee69f82151de6ef8ea1a4c6c146d91bbf0d5 --- .../internal/storage/pack/PackWriter.java | 286 ++++++++++++++++++ .../eclipse/jgit/transport/UploadPack.java | 40 +++ .../jgit/transport/UploadPackLogger.java | 75 +++++ .../jgit/transport/UploadPackLoggerChain.java | 96 ++++++ 4 files changed, 497 insertions(+) create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java index e31f2412c..19b6b080d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java @@ -2057,6 +2057,292 @@ private boolean reuseDeltaFor(ObjectToPack otp) { return true; } + /** + * Summary of how PackWriter created the pack. + * + * @deprecated Use {@link PackStatistics} instead. + */ + @Deprecated + public static class Statistics { + /** Statistics about a single class of object. */ + public static class ObjectType { + // All requests are forwarded to this object. + private PackStatistics.ObjectType objectType; + + /** + * Wraps an + * {@link org.eclipse.jgit.storage.pack.PackStatistics.ObjectType} + * instance to maintain backwards compatibility with existing API. + * + * @param type + * the wrapped instance + */ + public ObjectType(PackStatistics.ObjectType type) { + objectType = type; + } + + /** + * @return total number of objects output. This total includes the + * value of {@link #getDeltas()}. + */ + public long getObjects() { + return objectType.getObjects(); + } + + /** + * @return total number of deltas output. This may be lower than the + * actual number of deltas if a cached pack was reused. + */ + public long getDeltas() { + return objectType.getDeltas(); + } + + /** + * @return number of objects whose existing representation was + * reused in the output. This count includes + * {@link #getReusedDeltas()}. + */ + public long getReusedObjects() { + return objectType.getReusedObjects(); + } + + /** + * @return number of deltas whose existing representation was reused + * in the output, as their base object was also output or + * was assumed present for a thin pack. This may be lower + * than the actual number of reused deltas if a cached pack + * was reused. + */ + public long getReusedDeltas() { + return objectType.getReusedDeltas(); + } + + /** + * @return total number of bytes written. This size includes the + * object headers as well as the compressed data. This size + * also includes all of {@link #getDeltaBytes()}. + */ + public long getBytes() { + return objectType.getBytes(); + } + + /** + * @return number of delta bytes written. This size includes the + * object headers for the delta objects. + */ + public long getDeltaBytes() { + return objectType.getDeltaBytes(); + } + } + + // All requests are forwarded to this object. + private PackStatistics statistics; + + /** + * Wraps a {@link PackStatistics} object to maintain backwards + * compatibility with existing API. + * + * @param stats + * the wrapped PackStatitics object + */ + public Statistics(PackStatistics stats) { + statistics = stats; + } + + /** + * @return unmodifiable collection of objects to be included in the + * pack. May be null if the pack was hand-crafted in a unit + * test. + */ + public Set getInterestingObjects() { + return statistics.getInterestingObjects(); + } + + /** + * @return unmodifiable collection of objects that should be excluded + * from the pack, as the peer that will receive the pack already + * has these objects. + */ + public Set getUninterestingObjects() { + return statistics.getUninterestingObjects(); + } + + /** + * @return unmodifiable collection of the cached packs that were reused + * in the output, if any were selected for reuse. + */ + public Collection getReusedPacks() { + return statistics.getReusedPacks(); + } + + /** + * @return number of objects in the output pack that went through the + * delta search process in order to find a potential delta base. + */ + public int getDeltaSearchNonEdgeObjects() { + return statistics.getDeltaSearchNonEdgeObjects(); + } + + /** + * @return number of objects in the output pack that went through delta + * base search and found a suitable base. This is a subset of + * {@link #getDeltaSearchNonEdgeObjects()}. + */ + public int getDeltasFound() { + return statistics.getDeltasFound(); + } + + /** + * @return total number of objects output. This total includes the value + * of {@link #getTotalDeltas()}. + */ + public long getTotalObjects() { + return statistics.getTotalObjects(); + } + + /** + * @return the count of objects that needed to be discovered through an + * object walk because they were not found in bitmap indices. + * Returns -1 if no bitmap indices were found. + * + * @since 4.0 + */ + public long getBitmapIndexMisses() { + return statistics.getBitmapIndexMisses(); + } + + /** + * @return total number of deltas output. This may be lower than the + * actual number of deltas if a cached pack was reused. + */ + public long getTotalDeltas() { + return statistics.getTotalDeltas(); + } + + /** + * @return number of objects whose existing representation was reused in + * the output. This count includes {@link #getReusedDeltas()}. + */ + public long getReusedObjects() { + return statistics.getReusedObjects(); + } + + /** + * @return number of deltas whose existing representation was reused in + * the output, as their base object was also output or was + * assumed present for a thin pack. This may be lower than the + * actual number of reused deltas if a cached pack was reused. + */ + public long getReusedDeltas() { + return statistics.getReusedDeltas(); + } + + /** + * @return total number of bytes written. This size includes the pack + * header, trailer, thin pack, and reused cached pack(s). + */ + public long getTotalBytes() { + return statistics.getTotalBytes(); + } + + /** + * @return size of the thin pack in bytes, if a thin pack was generated. + * A thin pack is created when the client already has objects + * and some deltas are created against those objects, or if a + * cached pack is being used and some deltas will reference + * objects in the cached pack. This size does not include the + * pack header or trailer. + */ + public long getThinPackBytes() { + return statistics.getThinPackBytes(); + } + + /** + * @param typeCode + * object type code, e.g. OBJ_COMMIT or OBJ_TREE. + * @return information about this type of object in the pack. + */ + public ObjectType byObjectType(int typeCode) { + return new ObjectType(statistics.byObjectType(typeCode)); + } + + /** @return true if the resulting pack file was a shallow pack. */ + public boolean isShallow() { + return statistics.isShallow(); + } + + /** @return depth (in commits) the pack includes if shallow. */ + public int getDepth() { + return statistics.getDepth(); + } + + /** + * @return time in milliseconds spent enumerating the objects that need + * to be included in the output. This time includes any restarts + * that occur when a cached pack is selected for reuse. + */ + public long getTimeCounting() { + return statistics.getTimeCounting(); + } + + /** + * @return time in milliseconds spent matching existing representations + * against objects that will be transmitted, or that the client + * can be assumed to already have. + */ + public long getTimeSearchingForReuse() { + return statistics.getTimeSearchingForReuse(); + } + + /** + * @return time in milliseconds spent finding the sizes of all objects + * that will enter the delta compression search window. The + * sizes need to be known to better match similar objects + * together and improve delta compression ratios. + */ + public long getTimeSearchingForSizes() { + return statistics.getTimeSearchingForSizes(); + } + + /** + * @return time in milliseconds spent on delta compression. This is + * observed wall-clock time and does not accurately track CPU + * time used when multiple threads were used to perform the + * delta compression. + */ + public long getTimeCompressing() { + return statistics.getTimeCompressing(); + } + + /** + * @return time in milliseconds spent writing the pack output, from + * start of header until end of trailer. The transfer speed can + * be approximated by dividing {@link #getTotalBytes()} by this + * value. + */ + public long getTimeWriting() { + return statistics.getTimeWriting(); + } + + /** @return total time spent processing this pack. */ + public long getTimeTotal() { + return statistics.getTimeTotal(); + } + + /** + * @return get the average output speed in terms of bytes-per-second. + * {@code getTotalBytes() / (getTimeWriting() / 1000.0)}. + */ + public double getTransferRate() { + return statistics.getTransferRate(); + } + + /** @return formatted message string for display to clients. */ + public String getMessage() { + return statistics.getMessage(); + } + } + private class MutableState { /** Estimated size of a single ObjectToPack instance. */ // Assume 64-bit pointers, since this is just an estimate. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java index f9ab56d95..101057fb4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -312,6 +312,8 @@ public Set getOptions() { private PackStatistics statistics; + private UploadPackLogger logger = UploadPackLogger.NULL; + /** * Create a new pack upload for an open repository. * @@ -583,6 +585,28 @@ public void setTransferConfig(TransferConfig tc) { } } + /** + * @return the configured logger. + * + * @deprecated Use {@link #getPreUploadHook()}. + */ + @Deprecated + public UploadPackLogger getLogger() { + return logger; + } + + /** + * Set the logger. + * + * @param logger + * the logger instance. If null, no logging occurs. + * @deprecated Use {@link #setPreUploadHook(PreUploadHook)}. + */ + @Deprecated + public void setLogger(UploadPackLogger logger) { + this.logger = logger; + } + /** * Check whether the client expects a side-band stream. * @@ -653,6 +677,21 @@ public void upload(final InputStream input, final OutputStream output, } } + /** + * Get the PackWriter's statistics if a pack was sent to the client. + * + * @return statistics about pack output, if a pack was sent. Null if no pack + * was sent, such as during the negotation phase of a smart HTTP + * connection, or if the client was already up-to-date. + * @since 3.0 + * @deprecated Use {@link #getStatistics()}. + */ + @Deprecated + public PackWriter.Statistics getPackStatistics() { + return statistics == null ? null + : new PackWriter.Statistics(statistics); + } + /** * Get the PackWriter's statistics if a pack was sent to the client. * @@ -1508,6 +1547,7 @@ else if (ref.getName().startsWith(Constants.R_HEADS)) statistics = pw.getStatistics(); if (statistics != null) { postUploadHook.onPostUpload(statistics); + logger.onPackStatistics(new PackWriter.Statistics(statistics)); } pw.close(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java new file mode 100644 index 000000000..85ebecc45 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2011, 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.transport; + +import org.eclipse.jgit.internal.storage.pack.PackWriter; + +/** + * Logs activity that occurred within {@link UploadPack}. + *

+ * Implementors of the interface are responsible for associating the current + * thread to a particular connection, if they need to also include connection + * information. One method is to use a {@link java.lang.ThreadLocal} to remember + * the connection information before invoking UploadPack. + * + * @deprecated use {@link PostUploadHook} instead + */ +@Deprecated +public interface UploadPackLogger { + /** A simple no-op logger. */ + public static final UploadPackLogger NULL = new UploadPackLogger() { + public void onPackStatistics(PackWriter.Statistics stats) { + // Do nothing. + } + }; + + /** + * Notice to the logger after a pack has been sent. + * + * @param stats + * the statistics after sending a pack to the client. + * @since 3.0 + */ + public void onPackStatistics(PackWriter.Statistics stats); +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java new file mode 100644 index 000000000..4ea0319d9 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2011, 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.transport; + +import java.util.List; + +import org.eclipse.jgit.internal.storage.pack.PackWriter; + +/** + * UploadPackLogger that delegates to a list of other loggers. + *

+ * loggers are run in the order passed to the constructor. + * + * @deprecated Use {@link PostUploadHookChain} instead. + */ +@Deprecated +public class UploadPackLoggerChain implements UploadPackLogger { + private final UploadPackLogger[] loggers; + private final int count; + + /** + * Create a new logger chaining the given loggers together. + * + * @param loggers + * loggers to execute, in order. + * @return a new logger chain of the given loggers. + */ + public static UploadPackLogger newChain( + List loggers) { + UploadPackLogger[] newLoggers = new UploadPackLogger[loggers.size()]; + int i = 0; + for (UploadPackLogger logger : loggers) + if (logger != UploadPackLogger.NULL) + newLoggers[i++] = logger; + if (i == 0) + return UploadPackLogger.NULL; + else if (i == 1) + return newLoggers[0]; + else + return new UploadPackLoggerChain(newLoggers, i); + } + + /** + * @since 3.0 + */ + public void onPackStatistics(PackWriter.Statistics stats) { + for (int i = 0; i < count; i++) + loggers[i].onPackStatistics(stats); + } + + private UploadPackLoggerChain(UploadPackLogger[] loggers, int count) { + this.loggers = loggers; + this.count = count; + } +} From 797f94d3319a6bea2cacce707a6a5ee67f00ea17 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 10 Nov 2015 15:11:04 -0800 Subject: [PATCH 03/50] RefDirectory.exactRef: Do not ignore symrefs to unborn branch When asked to read a symref pointing to a branch-yet-to-be-born (such as HEAD in a newly initialized repository), DfsRepository and FileRepository return different results. FileRepository: exactRef("HEAD") => null DfsRepository: exactRef("HEAD") => SymbolicRef[HEAD -> refs/heads/master=00000000] getRef("HEAD") returns the same as DfsRepository's exactRef in both backends. The intended behavior is the DfsRepository one: exactRef() is supposed to be like getRef(), but more exact because it doesn't need to traverse the search path. The discrepancy is because DfsRefDatabase implements exactRef() directly with the intended semantics, while RefDirectory uses a fallback implementation built on top of getRefs(). getRefs() skips symrefs to an unborn branch. Override the fallback implementation with a correct implementation that is similar to getRef() to avoid this. A followup change will fix the fallback. Change-Id: Ic138a5564a099ebf32248d86b93e2de9ab3c94ee Reported-by: David Pursehouse Improved-by: Christian Halstrick Bug: 478865 --- .../storage/file/RefDirectoryTest.java | 19 +++++++++++++++ .../internal/storage/file/RefDirectory.java | 24 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java index 099159892..d66753da0 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java @@ -1024,6 +1024,25 @@ public void testGetRef_EmptyDatabase() throws IOException { assertNull(refdir.getRef("v1.0")); } + @Test + public void testExactRef_EmptyDatabase() throws IOException { + Ref r; + + r = refdir.exactRef(HEAD); + assertTrue(r.isSymbolic()); + assertSame(LOOSE, r.getStorage()); + assertEquals("refs/heads/master", r.getTarget().getName()); + assertSame(NEW, r.getTarget().getStorage()); + assertNull(r.getTarget().getObjectId()); + + assertNull(refdir.exactRef("refs/heads/master")); + assertNull(refdir.exactRef("refs/tags/v1.0")); + assertNull(refdir.exactRef("FETCH_HEAD")); + assertNull(refdir.exactRef("NOT.A.REF.NAME")); + assertNull(refdir.exactRef("master")); + assertNull(refdir.exactRef("v1.0")); + } + @Test public void testGetRef_FetchHead() throws IOException { // This is an odd special case where we need to make sure we read diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java index 6a04a538e..bb5b04440 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java @@ -261,6 +261,30 @@ private RefList getLooseRefs() { return loose; } + @Override + public Ref exactRef(String name) throws IOException { + RefList packed = getPackedRefs(); + Ref ref; + try { + ref = readRef(name, packed); + if (ref != null) { + ref = resolve(ref, 0, null, null, packed); + } + } catch (IOException e) { + if (name.contains("/") //$NON-NLS-1$ + || !(e.getCause() instanceof InvalidObjectIdException)) { + throw e; + } + + // While looking for a ref outside of refs/ (e.g., 'config'), we + // found a non-ref file (e.g., a config file) instead. Treat this + // as a ref-not-found condition. + ref = null; + } + fireRefsChanged(); + return ref; + } + @Override public Ref getRef(final String needle) throws IOException { final RefList packed = getPackedRefs(); From e18444de30f1f019b5f7f0a463da42a884cb6a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BCdiger=20Herrmann?= Date: Thu, 12 Nov 2015 15:54:13 +0100 Subject: [PATCH 04/50] Fix MissingObjectException in RenameDetector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When attempting to determine the size of a blob that does not exist, the RenameDetector throws a MissingObjectException. The fix is to return a size of zero if the size is requested for a blob id that doesn't exist. Bug: 481577 Change-Id: I4e86276039c630617610cc51d0eefa56d7d3952f Signed-off-by: RĂ¼diger Herrmann --- .../eclipse/jgit/diff/RenameDetectorTest.java | 31 +++++++++++++++++++ .../org/eclipse/jgit/diff/ContentSource.java | 6 +++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java index e83ef772f..4315be9e4 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java @@ -48,6 +48,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.util.Arrays; import java.util.List; import org.eclipse.jgit.diff.DiffEntry.ChangeType; @@ -226,6 +227,19 @@ public void testExactRename_OneDeleteManyAdds() throws Exception { assertCopy(d, b, 100, entries.get(2)); } + @Test + public void testExactRename_UnstagedFile() throws Exception { + ObjectId aId = blob("foo"); + DiffEntry a = DiffEntry.delete(PATH_A, aId); + DiffEntry b = DiffEntry.add(PATH_B, aId); + + rd.addAll(Arrays.asList(a, b)); + List entries = rd.compute(); + + assertEquals(1, entries.size()); + assertRename(a, b, 100, entries.get(0)); + } + @Test public void testInexactRename_OnePair() throws Exception { ObjectId aId = blob("foo\nbar\nbaz\nblarg\n"); @@ -429,6 +443,23 @@ public void testNoRenames_SymlinkAndFileSamePath() throws Exception { assertSame(b, entries.get(1)); } + @Test + public void testNoRenames_UntrackedFile() throws Exception { + ObjectId aId = blob("foo"); + ObjectId bId = ObjectId + .fromString("3049eb6eee7e1318f4e78e799bf33f1e54af9cbf"); + + DiffEntry a = DiffEntry.delete(PATH_A, aId); + DiffEntry b = DiffEntry.add(PATH_B, bId); + + rd.addAll(Arrays.asList(a, b)); + List entries = rd.compute(); + + assertEquals(2, entries.size()); + assertSame(a, entries.get(0)); + assertSame(b, entries.get(1)); + } + @Test public void testBreakModify_BreakAll() throws Exception { ObjectId aId = blob("foo"); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java index 0dc4b0578..444ab1cb8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java @@ -132,7 +132,11 @@ private static class ObjectReaderSource extends ContentSource { @Override public long size(String path, ObjectId id) throws IOException { - return reader.getObjectSize(id, Constants.OBJ_BLOB); + try { + return reader.getObjectSize(id, Constants.OBJ_BLOB); + } catch (MissingObjectException ignore) { + return 0; + } } @Override From 7dcb50a04a03a96e7514a8598eaf9bb052af867e Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Wed, 11 Nov 2015 10:42:46 -0800 Subject: [PATCH 05/50] Fallback exactRef: Do not ignore symrefs to unborn branch When asked to read a symref pointing to a branch-yet-to-be-born (such as HEAD in a newly initialized repository), getRef and getRefs provide different results. getRef: SymbolicRef[HEAD -> refs/heads/master=00000000] getRefs and getAdditionalRefs: nothing exactRef should match the getRef behavior: it is meant to be a simpler, faster version of getRef that lets you search for a ref without resolving it using the search path without other semantic changes. But the fallback implementation of exactRef relies on getRefs and produces null for this case. Luckily the in-tree RefDatabase implementations override exactRef and get the correct behavior. But any out-of-tree storage backend that doesn't inherit from DfsRefDatabase or RefDirectory would still return null when it shouldn't. Let the fallback implementation use getRef instead to avoid this. This means that exactRef would waste some effort traversing the ref search path when the named ref is not found --- but subclasses tend to override exactRef for performance already, so in the default implementation correctness is more important. Bug: 478865 Change-Id: I60f04e3ce3bf4731640ffd2433d329e621330029 --- .../src/org/eclipse/jgit/lib/RefDatabase.java | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java index ef22fb90f..b62033cbd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java @@ -239,20 +239,11 @@ public boolean performsAtomicTransactions() { * @since 4.1 */ public Ref exactRef(String name) throws IOException { - int slash = name.lastIndexOf('/'); - String prefix = name.substring(0, slash + 1); - String rest = name.substring(slash + 1); - Ref result = getRefs(prefix).get(rest); - if (result != null || slash != -1) { - return result; + Ref ref = getRef(name); + if (ref == null || !name.equals(ref.getName())) { + return null; } - - for (Ref ref : getAdditionalRefs()) { - if (name.equals(ref.getName())) { - return ref; - } - } - return null; + return ref; } /** From 3cb5f113982f4ba75f6720cf7428f82b61cd3a32 Mon Sep 17 00:00:00 2001 From: Matthias Sohn Date: Sat, 14 Nov 2015 00:09:54 +0100 Subject: [PATCH 06/50] JGit v4.1.1.201511131810-r Change-Id: If0246daab39fa279c30874549b198e7aa917bc62 Signed-off-by: Matthias Sohn --- org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF | 2 +- org.eclipse.jgit.ant.test/pom.xml | 2 +- org.eclipse.jgit.ant/META-INF/MANIFEST.MF | 2 +- org.eclipse.jgit.ant/pom.xml | 2 +- org.eclipse.jgit.archive/META-INF/MANIFEST.MF | 2 +- org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF | 4 ++-- org.eclipse.jgit.archive/pom.xml | 2 +- org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF | 2 +- org.eclipse.jgit.http.apache/pom.xml | 2 +- org.eclipse.jgit.http.server/META-INF/MANIFEST.MF | 2 +- org.eclipse.jgit.http.server/pom.xml | 2 +- org.eclipse.jgit.http.test/META-INF/MANIFEST.MF | 2 +- org.eclipse.jgit.http.test/pom.xml | 2 +- org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF | 2 +- org.eclipse.jgit.junit.http/pom.xml | 2 +- org.eclipse.jgit.junit/META-INF/MANIFEST.MF | 2 +- org.eclipse.jgit.junit/pom.xml | 2 +- .../org.eclipse.jgit.feature/feature.xml | 2 +- org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml | 2 +- .../org.eclipse.jgit.http.apache.feature/feature.xml | 2 +- .../org.eclipse.jgit.http.apache.feature/pom.xml | 2 +- .../org.eclipse.jgit.junit.feature/feature.xml | 2 +- .../org.eclipse.jgit.junit.feature/pom.xml | 2 +- .../org.eclipse.jgit.pgm.feature/feature.xml | 2 +- .../org.eclipse.jgit.pgm.feature/pom.xml | 2 +- .../org.eclipse.jgit.pgm.source.feature/feature.xml | 2 +- .../org.eclipse.jgit.pgm.source.feature/pom.xml | 2 +- .../org.eclipse.jgit.repository/pom.xml | 2 +- .../org.eclipse.jgit.source.feature/feature.xml | 2 +- .../org.eclipse.jgit.source.feature/pom.xml | 2 +- .../org.eclipse.jgit.target/META-INF/MANIFEST.MF | 2 +- org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml | 2 +- org.eclipse.jgit.packaging/pom.xml | 2 +- org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF | 2 +- org.eclipse.jgit.pgm.test/pom.xml | 2 +- org.eclipse.jgit.pgm/META-INF/MANIFEST.MF | 2 +- org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF | 4 ++-- org.eclipse.jgit.pgm/pom.xml | 2 +- org.eclipse.jgit.test/META-INF/MANIFEST.MF | 2 +- org.eclipse.jgit.test/pom.xml | 2 +- org.eclipse.jgit.ui/META-INF/MANIFEST.MF | 2 +- org.eclipse.jgit.ui/pom.xml | 2 +- org.eclipse.jgit/META-INF/MANIFEST.MF | 2 +- org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF | 4 ++-- org.eclipse.jgit/pom.xml | 2 +- pom.xml | 2 +- 46 files changed, 49 insertions(+), 49 deletions(-) diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF index 1ce2ec27f..e216612e3 100644 --- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.ant.test -Bundle-Version: 4.1.1.qualifier +Bundle-Version: 4.1.1.201511131810-r Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Import-Package: org.apache.tools.ant, diff --git a/org.eclipse.jgit.ant.test/pom.xml b/org.eclipse.jgit.ant.test/pom.xml index ff1ec8335..9e6c7d67a 100644 --- a/org.eclipse.jgit.ant.test/pom.xml +++ b/org.eclipse.jgit.ant.test/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.ant.test diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF index d9fbd7614..0e9ede4f1 100644 --- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.jgit.ant -Bundle-Version: 4.1.1.qualifier +Bundle-Version: 4.1.1.201511131810-r Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Import-Package: org.apache.tools.ant, org.eclipse.jgit.storage.file;version="[4.1.1,4.2.0)" diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml index 00351b901..2b03847c5 100644 --- a/org.eclipse.jgit.ant/pom.xml +++ b/org.eclipse.jgit.ant/pom.xml @@ -48,7 +48,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.ant diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF index 0d073188c..c3f51e980 100644 --- a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.archive -Bundle-Version: 4.1.1.qualifier +Bundle-Version: 4.1.1.201511131810-r Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-1.7 diff --git a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF index 743fe11f0..dbb647366 100644 --- a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.archive - Sources Bundle-SymbolicName: org.eclipse.jgit.archive.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 4.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.1.1.qualifier";roots="." +Bundle-Version: 4.1.1.201511131810-r +Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.1.1.201511131810-r";roots="." diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml index 65e87aa68..1a4dcabea 100644 --- a/org.eclipse.jgit.archive/pom.xml +++ b/org.eclipse.jgit.archive/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.archive diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF index 107dc8449..6036cafbd 100644 --- a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.jgit.http.apache -Bundle-Version: 4.1.1.qualifier +Bundle-Version: 4.1.1.201511131810-r Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-Localization: plugin Bundle-Vendor: %Provider-Name diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml index f8b29ed4e..0bf73b796 100644 --- a/org.eclipse.jgit.http.apache/pom.xml +++ b/org.eclipse.jgit.http.apache/pom.xml @@ -48,7 +48,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.http.apache diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF index 90c15fc59..e19980b57 100644 --- a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.http.server -Bundle-Version: 4.1.1.qualifier +Bundle-Version: 4.1.1.201511131810-r Bundle-Localization: plugin Bundle-Vendor: %provider_name Export-Package: org.eclipse.jgit.http.server;version="4.1.1", diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml index a487bd352..17ce3da12 100644 --- a/org.eclipse.jgit.http.server/pom.xml +++ b/org.eclipse.jgit.http.server/pom.xml @@ -52,7 +52,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.http.server diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF index 8aaeeb032..d167da39f 100644 --- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.http.test -Bundle-Version: 4.1.1.qualifier +Bundle-Version: 4.1.1.201511131810-r Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-1.7 diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml index 33a0f6d67..237f2267b 100644 --- a/org.eclipse.jgit.http.test/pom.xml +++ b/org.eclipse.jgit.http.test/pom.xml @@ -51,7 +51,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.http.test diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF index 5f25bf194..f669a1790 100644 --- a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.junit.http -Bundle-Version: 4.1.1.qualifier +Bundle-Version: 4.1.1.201511131810-r Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml index 524afd664..d1e09c32a 100644 --- a/org.eclipse.jgit.junit.http/pom.xml +++ b/org.eclipse.jgit.junit.http/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.junit.http diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF index 48b8b12fe..31c2304b7 100644 --- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.junit -Bundle-Version: 4.1.1.qualifier +Bundle-Version: 4.1.1.201511131810-r Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml index d231bc1a2..b4d4d27f4 100644 --- a/org.eclipse.jgit.junit/pom.xml +++ b/org.eclipse.jgit.junit/pom.xml @@ -52,7 +52,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.junit diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml index bfd343b0c..7cf81353f 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml index 5bd71f734..849f7bbfa 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit jgit.tycho.parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.feature diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml index bb766ac23..8544d1a2b 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml index 8dc47d0f9..060ad0e6f 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit jgit.tycho.parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.feature diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml index e28eac537..752f0444e 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml index 99dd9bcb8..3ed76d384 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit jgit.tycho.parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.feature diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml index f66c08934..a6f0a4c04 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml index f078da6ec..438173bea 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit jgit.tycho.parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.feature diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml index 7a511cbfa..61070bff1 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml index a1d785d06..3754fd6c9 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit jgit.tycho.parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.feature diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml index 5e1ff4849..72da41c7e 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit jgit.tycho.parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.repository diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml index bfc552c3e..09ee4c478 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml index dcc51618b..50816beaa 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit jgit.tycho.parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.feature diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF index 68b25b83d..1d3c436e9 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF @@ -2,4 +2,4 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: JGit Target Platform Bundle Bundle-SymbolicName: org.eclipse.jgit.target -Bundle-Version: 4.1.1.qualifier +Bundle-Version: 4.1.1.201511131810-r diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml index 11ae4e9a2..e95377f94 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml @@ -49,7 +49,7 @@ org.eclipse.jgit jgit.tycho.parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.target diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml index 1744cf247..64750ec37 100644 --- a/org.eclipse.jgit.packaging/pom.xml +++ b/org.eclipse.jgit.packaging/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jgit jgit.tycho.parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r pom JGit Tycho Parent diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF index 01ebb4563..5b9a11534 100644 --- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.pgm.test -Bundle-Version: 4.1.1.qualifier +Bundle-Version: 4.1.1.201511131810-r Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-ActivationPolicy: lazy diff --git a/org.eclipse.jgit.pgm.test/pom.xml b/org.eclipse.jgit.pgm.test/pom.xml index 873bcde97..acfe6009f 100644 --- a/org.eclipse.jgit.pgm.test/pom.xml +++ b/org.eclipse.jgit.pgm.test/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.pgm.test diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF index 62fcb68a6..30db7a84f 100644 --- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.pgm -Bundle-Version: 4.1.1.qualifier +Bundle-Version: 4.1.1.201511131810-r Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy Bundle-Localization: plugin diff --git a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF index 7f815943a..dfc5ab9a9 100644 --- a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.pgm - Sources Bundle-SymbolicName: org.eclipse.jgit.pgm.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 4.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.1.1.qualifier";roots="." +Bundle-Version: 4.1.1.201511131810-r +Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.1.1.201511131810-r";roots="." diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml index 0007d0417..2c2e55de6 100644 --- a/org.eclipse.jgit.pgm/pom.xml +++ b/org.eclipse.jgit.pgm/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.pgm diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF index 3b67921b7..9d25fca29 100644 --- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.test -Bundle-Version: 4.1.1.qualifier +Bundle-Version: 4.1.1.201511131810-r Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml index d1a5a46b3..2385d27f9 100644 --- a/org.eclipse.jgit.test/pom.xml +++ b/org.eclipse.jgit.test/pom.xml @@ -52,7 +52,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.test diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF index b7f44277f..a9cedc091 100644 --- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.ui -Bundle-Version: 4.1.1.qualifier +Bundle-Version: 4.1.1.201511131810-r Bundle-Vendor: %provider_name Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Export-Package: org.eclipse.jgit.awtui;version="4.1.1" diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml index a7a1992ab..d64de3243 100644 --- a/org.eclipse.jgit.ui/pom.xml +++ b/org.eclipse.jgit.ui/pom.xml @@ -52,7 +52,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit.ui diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index 43be20db1..a10e6d6ee 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit -Bundle-Version: 4.1.1.qualifier +Bundle-Version: 4.1.1.201511131810-r Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF index 07480e3c2..83b239cad 100644 --- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit - Sources Bundle-SymbolicName: org.eclipse.jgit.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 4.1.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit;version="4.1.1.qualifier";roots="." +Bundle-Version: 4.1.1.201511131810-r +Eclipse-SourceBundle: org.eclipse.jgit;version="4.1.1.201511131810-r";roots="." diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml index 52c702faa..875794062 100644 --- a/org.eclipse.jgit/pom.xml +++ b/org.eclipse.jgit/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r org.eclipse.jgit diff --git a/pom.xml b/pom.xml index 5f911c6ba..a89978477 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ org.eclipse.jgit org.eclipse.jgit-parent pom - 4.1.1-SNAPSHOT + 4.1.1.201511131810-r JGit - Parent ${jgit-url} From 3e2aff196e4f20183c13fd0c219101b6604477e8 Mon Sep 17 00:00:00 2001 From: Matthias Sohn Date: Sat, 14 Nov 2015 00:53:45 +0100 Subject: [PATCH 07/50] Prepare 4.1.2-SNAPSHOT builds Change-Id: I1d1c4d918f2260c866c4392c1abea1e40a1de962 Signed-off-by: Matthias Sohn --- .../META-INF/MANIFEST.MF | 12 +-- org.eclipse.jgit.ant.test/pom.xml | 2 +- org.eclipse.jgit.ant/META-INF/MANIFEST.MF | 6 +- org.eclipse.jgit.ant/pom.xml | 2 +- org.eclipse.jgit.archive/META-INF/MANIFEST.MF | 12 +-- .../META-INF/SOURCE-MANIFEST.MF | 4 +- org.eclipse.jgit.archive/pom.xml | 2 +- .../META-INF/MANIFEST.MF | 10 +-- org.eclipse.jgit.http.apache/pom.xml | 2 +- .../META-INF/MANIFEST.MF | 26 +++--- org.eclipse.jgit.http.server/pom.xml | 2 +- .../META-INF/MANIFEST.MF | 36 ++++----- org.eclipse.jgit.http.test/pom.xml | 2 +- .../META-INF/MANIFEST.MF | 20 ++--- org.eclipse.jgit.junit.http/pom.xml | 2 +- org.eclipse.jgit.junit/META-INF/MANIFEST.MF | 32 ++++---- org.eclipse.jgit.junit/pom.xml | 2 +- .../org.eclipse.jgit.feature/feature.xml | 2 +- .../org.eclipse.jgit.feature/pom.xml | 2 +- .../feature.xml | 2 +- .../pom.xml | 2 +- .../feature.xml | 2 +- .../org.eclipse.jgit.junit.feature/pom.xml | 2 +- .../org.eclipse.jgit.pgm.feature/feature.xml | 2 +- .../org.eclipse.jgit.pgm.feature/pom.xml | 2 +- .../feature.xml | 2 +- .../pom.xml | 2 +- .../org.eclipse.jgit.repository/pom.xml | 2 +- .../feature.xml | 2 +- .../org.eclipse.jgit.source.feature/pom.xml | 2 +- .../META-INF/MANIFEST.MF | 2 +- .../org.eclipse.jgit.target/pom.xml | 2 +- org.eclipse.jgit.packaging/pom.xml | 2 +- .../META-INF/MANIFEST.MF | 34 ++++---- org.eclipse.jgit.pgm.test/pom.xml | 2 +- org.eclipse.jgit.pgm/META-INF/MANIFEST.MF | 64 +++++++-------- .../META-INF/SOURCE-MANIFEST.MF | 4 +- org.eclipse.jgit.pgm/pom.xml | 2 +- org.eclipse.jgit.test/META-INF/MANIFEST.MF | 80 +++++++++---------- org.eclipse.jgit.test/pom.xml | 2 +- org.eclipse.jgit.ui/META-INF/MANIFEST.MF | 18 ++--- org.eclipse.jgit.ui/pom.xml | 2 +- org.eclipse.jgit/META-INF/MANIFEST.MF | 74 ++++++++--------- org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF | 4 +- org.eclipse.jgit/pom.xml | 2 +- pom.xml | 2 +- 46 files changed, 248 insertions(+), 248 deletions(-) diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF index e216612e3..8f5d0b90d 100644 --- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF @@ -3,14 +3,14 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.ant.test -Bundle-Version: 4.1.1.201511131810-r +Bundle-Version: 4.1.2.qualifier Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Import-Package: org.apache.tools.ant, - org.eclipse.jgit.ant.tasks;version="[4.1.1,4.2.0)", - org.eclipse.jgit.internal.storage.file;version="[4.1.1,4.2.0)", - org.eclipse.jgit.junit;version="[4.1.1,4.2.0)", - org.eclipse.jgit.lib;version="[4.1.1,4.2.0)", - org.eclipse.jgit.util;version="[4.1.1,4.2.0)", + org.eclipse.jgit.ant.tasks;version="[4.1.2,4.2.0)", + org.eclipse.jgit.internal.storage.file;version="[4.1.2,4.2.0)", + org.eclipse.jgit.junit;version="[4.1.2,4.2.0)", + org.eclipse.jgit.lib;version="[4.1.2,4.2.0)", + org.eclipse.jgit.util;version="[4.1.2,4.2.0)", org.hamcrest;version="[1.1.0,2.0.0)", org.junit;version="[4.0.0,5.0.0)" diff --git a/org.eclipse.jgit.ant.test/pom.xml b/org.eclipse.jgit.ant.test/pom.xml index 9e6c7d67a..a514e3dbc 100644 --- a/org.eclipse.jgit.ant.test/pom.xml +++ b/org.eclipse.jgit.ant.test/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.ant.test diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF index 0e9ede4f1..4986f0fbb 100644 --- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF @@ -2,11 +2,11 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.jgit.ant -Bundle-Version: 4.1.1.201511131810-r +Bundle-Version: 4.1.2.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Import-Package: org.apache.tools.ant, - org.eclipse.jgit.storage.file;version="[4.1.1,4.2.0)" + org.eclipse.jgit.storage.file;version="[4.1.2,4.2.0)" Bundle-Localization: plugin Bundle-Vendor: %Provider-Name -Export-Package: org.eclipse.jgit.ant.tasks;version="4.1.1"; +Export-Package: org.eclipse.jgit.ant.tasks;version="4.1.2"; uses:="org.apache.tools.ant.types,org.apache.tools.ant" diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml index 2b03847c5..cb0fcb0a1 100644 --- a/org.eclipse.jgit.ant/pom.xml +++ b/org.eclipse.jgit.ant/pom.xml @@ -48,7 +48,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.ant diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF index c3f51e980..4b1e81d40 100644 --- a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.archive -Bundle-Version: 4.1.1.201511131810-r +Bundle-Version: 4.1.2.qualifier Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-1.7 @@ -12,14 +12,14 @@ Import-Package: org.apache.commons.compress.archivers;version="[1.4,2.0)", org.apache.commons.compress.compressors.bzip2;version="[1.4,2.0)", org.apache.commons.compress.compressors.gzip;version="[1.4,2.0)", org.apache.commons.compress.compressors.xz;version="[1.4,2.0)", - org.eclipse.jgit.api;version="[4.1.1,4.2.0)", - org.eclipse.jgit.lib;version="[4.1.1,4.2.0)", - org.eclipse.jgit.nls;version="[4.1.1,4.2.0)", - org.eclipse.jgit.util;version="[4.1.1,4.2.0)", + org.eclipse.jgit.api;version="[4.1.2,4.2.0)", + org.eclipse.jgit.lib;version="[4.1.2,4.2.0)", + org.eclipse.jgit.nls;version="[4.1.2,4.2.0)", + org.eclipse.jgit.util;version="[4.1.2,4.2.0)", org.osgi.framework;version="[1.3.0,2.0.0)" Bundle-ActivationPolicy: lazy Bundle-Activator: org.eclipse.jgit.archive.FormatActivator -Export-Package: org.eclipse.jgit.archive;version="4.1.1"; +Export-Package: org.eclipse.jgit.archive;version="4.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.api, org.apache.commons.compress.archivers, diff --git a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF index dbb647366..cc3181962 100644 --- a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.archive - Sources Bundle-SymbolicName: org.eclipse.jgit.archive.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 4.1.1.201511131810-r -Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.1.1.201511131810-r";roots="." +Bundle-Version: 4.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.1.2.qualifier";roots="." diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml index 1a4dcabea..1127a773f 100644 --- a/org.eclipse.jgit.archive/pom.xml +++ b/org.eclipse.jgit.archive/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.archive diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF index 6036cafbd..700badbca 100644 --- a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.jgit.http.apache -Bundle-Version: 4.1.1.201511131810-r +Bundle-Version: 4.1.2.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-Localization: plugin Bundle-Vendor: %Provider-Name @@ -19,10 +19,10 @@ Import-Package: org.apache.http;version="[4.1.0,5.0.0)", org.apache.http.impl.client;version="[4.1.0,5.0.0)", org.apache.http.impl.client.cache;version="[4.1.0,5.0.0)", org.apache.http.params;version="[4.1.0,5.0.0)", - org.eclipse.jgit.nls;version="[4.1.1,4.2.0)", - org.eclipse.jgit.transport.http;version="[4.1.1,4.2.0)", - org.eclipse.jgit.util;version="[4.1.1,4.2.0)" -Export-Package: org.eclipse.jgit.transport.http.apache;version="4.1.1"; + org.eclipse.jgit.nls;version="[4.1.2,4.2.0)", + org.eclipse.jgit.transport.http;version="[4.1.2,4.2.0)", + org.eclipse.jgit.util;version="[4.1.2,4.2.0)" +Export-Package: org.eclipse.jgit.transport.http.apache;version="4.1.2"; uses:="org.eclipse.jgit.transport.http, javax.net.ssl, org.apache.http.client, diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml index 0bf73b796..dee349cd8 100644 --- a/org.eclipse.jgit.http.apache/pom.xml +++ b/org.eclipse.jgit.http.apache/pom.xml @@ -48,7 +48,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.http.apache diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF index e19980b57..85e1dd7d4 100644 --- a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF @@ -2,13 +2,13 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.http.server -Bundle-Version: 4.1.1.201511131810-r +Bundle-Version: 4.1.2.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name -Export-Package: org.eclipse.jgit.http.server;version="4.1.1", - org.eclipse.jgit.http.server.glue;version="4.1.1"; +Export-Package: org.eclipse.jgit.http.server;version="4.1.2", + org.eclipse.jgit.http.server.glue;version="4.1.2"; uses:="javax.servlet,javax.servlet.http", - org.eclipse.jgit.http.server.resolver;version="4.1.1"; + org.eclipse.jgit.http.server.resolver;version="4.1.2"; uses:="org.eclipse.jgit.transport.resolver, org.eclipse.jgit.lib, org.eclipse.jgit.transport, @@ -17,12 +17,12 @@ Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Import-Package: javax.servlet;version="[2.5.0,3.2.0)", javax.servlet.http;version="[2.5.0,3.2.0)", - org.eclipse.jgit.errors;version="[4.1.1,4.2.0)", - org.eclipse.jgit.internal.storage.dfs;version="[4.1.1,4.2.0)", - org.eclipse.jgit.internal.storage.file;version="[4.1.1,4.2.0)", - org.eclipse.jgit.lib;version="[4.1.1,4.2.0)", - org.eclipse.jgit.nls;version="[4.1.1,4.2.0)", - org.eclipse.jgit.revwalk;version="[4.1.1,4.2.0)", - org.eclipse.jgit.transport;version="[4.1.1,4.2.0)", - org.eclipse.jgit.transport.resolver;version="[4.1.1,4.2.0)", - org.eclipse.jgit.util;version="[4.1.1,4.2.0)" + org.eclipse.jgit.errors;version="[4.1.2,4.2.0)", + org.eclipse.jgit.internal.storage.dfs;version="[4.1.2,4.2.0)", + org.eclipse.jgit.internal.storage.file;version="[4.1.2,4.2.0)", + org.eclipse.jgit.lib;version="[4.1.2,4.2.0)", + org.eclipse.jgit.nls;version="[4.1.2,4.2.0)", + org.eclipse.jgit.revwalk;version="[4.1.2,4.2.0)", + org.eclipse.jgit.transport;version="[4.1.2,4.2.0)", + org.eclipse.jgit.transport.resolver;version="[4.1.2,4.2.0)", + org.eclipse.jgit.util;version="[4.1.2,4.2.0)" diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml index 17ce3da12..8707e0153 100644 --- a/org.eclipse.jgit.http.server/pom.xml +++ b/org.eclipse.jgit.http.server/pom.xml @@ -52,7 +52,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.http.server diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF index d167da39f..cff5d24fa 100644 --- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.http.test -Bundle-Version: 4.1.1.201511131810-r +Bundle-Version: 4.1.2.qualifier Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-1.7 @@ -22,23 +22,23 @@ Import-Package: javax.servlet;version="[2.5.0,3.2.0)", org.eclipse.jetty.util.log;version="[9.0.0,10.0.0)", org.eclipse.jetty.util.security;version="[9.0.0,10.0.0)", org.eclipse.jetty.util.thread;version="[9.0.0,10.0.0)", - org.eclipse.jgit.errors;version="[4.1.1,4.2.0)", - org.eclipse.jgit.http.server;version="[4.1.1,4.2.0)", - org.eclipse.jgit.http.server.glue;version="[4.1.1,4.2.0)", - org.eclipse.jgit.http.server.resolver;version="[4.1.1,4.2.0)", - org.eclipse.jgit.internal;version="[4.1.1,4.2.0)", - org.eclipse.jgit.internal.storage.file;version="[4.1.1,4.2.0)", - org.eclipse.jgit.junit;version="[4.1.1,4.2.0)", - org.eclipse.jgit.junit.http;version="[4.1.1,4.2.0)", - org.eclipse.jgit.lib;version="[4.1.1,4.2.0)", - org.eclipse.jgit.nls;version="[4.1.1,4.2.0)", - org.eclipse.jgit.revwalk;version="[4.1.1,4.2.0)", - org.eclipse.jgit.storage.file;version="[4.1.1,4.2.0)", - org.eclipse.jgit.transport;version="[4.1.1,4.2.0)", - org.eclipse.jgit.transport.http;version="[4.1.1,4.2.0)", - org.eclipse.jgit.transport.http.apache;version="[4.1.1,4.2.0)", - org.eclipse.jgit.transport.resolver;version="[4.1.1,4.2.0)", - org.eclipse.jgit.util;version="[4.1.1,4.2.0)", + org.eclipse.jgit.errors;version="[4.1.2,4.2.0)", + org.eclipse.jgit.http.server;version="[4.1.2,4.2.0)", + org.eclipse.jgit.http.server.glue;version="[4.1.2,4.2.0)", + org.eclipse.jgit.http.server.resolver;version="[4.1.2,4.2.0)", + org.eclipse.jgit.internal;version="[4.1.2,4.2.0)", + org.eclipse.jgit.internal.storage.file;version="[4.1.2,4.2.0)", + org.eclipse.jgit.junit;version="[4.1.2,4.2.0)", + org.eclipse.jgit.junit.http;version="[4.1.2,4.2.0)", + org.eclipse.jgit.lib;version="[4.1.2,4.2.0)", + org.eclipse.jgit.nls;version="[4.1.2,4.2.0)", + org.eclipse.jgit.revwalk;version="[4.1.2,4.2.0)", + org.eclipse.jgit.storage.file;version="[4.1.2,4.2.0)", + org.eclipse.jgit.transport;version="[4.1.2,4.2.0)", + org.eclipse.jgit.transport.http;version="[4.1.2,4.2.0)", + org.eclipse.jgit.transport.http.apache;version="[4.1.2,4.2.0)", + org.eclipse.jgit.transport.resolver;version="[4.1.2,4.2.0)", + org.eclipse.jgit.util;version="[4.1.2,4.2.0)", org.hamcrest.core;version="[1.1.0,2.0.0)", org.junit;version="[4.0.0,5.0.0)", org.junit.runner;version="[4.0.0,5.0.0)", diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml index 237f2267b..8f7cd03fd 100644 --- a/org.eclipse.jgit.http.test/pom.xml +++ b/org.eclipse.jgit.http.test/pom.xml @@ -51,7 +51,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.http.test diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF index f669a1790..aa1dc8b2f 100644 --- a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.junit.http -Bundle-Version: 4.1.1.201511131810-r +Bundle-Version: 4.1.2.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy @@ -20,16 +20,16 @@ Import-Package: javax.servlet;version="[2.5.0,3.2.0)", org.eclipse.jetty.util.component;version="[9.0.0,10.0.0)", org.eclipse.jetty.util.log;version="[9.0.0,10.0.0)", org.eclipse.jetty.util.security;version="[9.0.0,10.0.0)", - org.eclipse.jgit.errors;version="[4.1.1,4.2.0)", - org.eclipse.jgit.http.server;version="[4.1.1,4.2.0)", - org.eclipse.jgit.internal.storage.file;version="[4.1.1,4.2.0)", - org.eclipse.jgit.junit;version="[4.1.1,4.2.0)", - org.eclipse.jgit.lib;version="[4.1.1,4.2.0)", - org.eclipse.jgit.revwalk;version="[4.1.1,4.2.0)", - org.eclipse.jgit.transport;version="[4.1.1,4.2.0)", - org.eclipse.jgit.transport.resolver;version="[4.1.1,4.2.0)", + org.eclipse.jgit.errors;version="[4.1.2,4.2.0)", + org.eclipse.jgit.http.server;version="[4.1.2,4.2.0)", + org.eclipse.jgit.internal.storage.file;version="[4.1.2,4.2.0)", + org.eclipse.jgit.junit;version="[4.1.2,4.2.0)", + org.eclipse.jgit.lib;version="[4.1.2,4.2.0)", + org.eclipse.jgit.revwalk;version="[4.1.2,4.2.0)", + org.eclipse.jgit.transport;version="[4.1.2,4.2.0)", + org.eclipse.jgit.transport.resolver;version="[4.1.2,4.2.0)", org.junit;version="[4.0.0,5.0.0)" -Export-Package: org.eclipse.jgit.junit.http;version="4.1.1"; +Export-Package: org.eclipse.jgit.junit.http;version="4.1.2"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.junit, javax.servlet.http, diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml index d1e09c32a..40cdca158 100644 --- a/org.eclipse.jgit.junit.http/pom.xml +++ b/org.eclipse.jgit.junit.http/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.junit.http diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF index 31c2304b7..048f65d8f 100644 --- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF @@ -2,27 +2,27 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.junit -Bundle-Version: 4.1.1.201511131810-r +Bundle-Version: 4.1.2.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.7 -Import-Package: org.eclipse.jgit.api;version="[4.1.1,4.2.0)", - org.eclipse.jgit.api.errors;version="[4.1.1,4.2.0)", - org.eclipse.jgit.dircache;version="[4.1.1,4.2.0)", - org.eclipse.jgit.errors;version="[4.1.1,4.2.0)", - org.eclipse.jgit.internal.storage.file;version="[4.1.1,4.2.0)", - org.eclipse.jgit.internal.storage.pack;version="[4.1.1,4.2.0)", - org.eclipse.jgit.lib;version="[4.1.1,4.2.0)", - org.eclipse.jgit.merge;version="[4.1.1,4.2.0)", - org.eclipse.jgit.revwalk;version="[4.1.1,4.2.0)", - org.eclipse.jgit.storage.file;version="[4.1.1,4.2.0)", - org.eclipse.jgit.treewalk;version="[4.1.1,4.2.0)", - org.eclipse.jgit.treewalk.filter;version="[4.1.1,4.2.0)", - org.eclipse.jgit.util;version="[4.1.1,4.2.0)", - org.eclipse.jgit.util.io;version="[4.1.1,4.2.0)", +Import-Package: org.eclipse.jgit.api;version="[4.1.2,4.2.0)", + org.eclipse.jgit.api.errors;version="[4.1.2,4.2.0)", + org.eclipse.jgit.dircache;version="[4.1.2,4.2.0)", + org.eclipse.jgit.errors;version="[4.1.2,4.2.0)", + org.eclipse.jgit.internal.storage.file;version="[4.1.2,4.2.0)", + org.eclipse.jgit.internal.storage.pack;version="[4.1.2,4.2.0)", + org.eclipse.jgit.lib;version="[4.1.2,4.2.0)", + org.eclipse.jgit.merge;version="[4.1.2,4.2.0)", + org.eclipse.jgit.revwalk;version="[4.1.2,4.2.0)", + org.eclipse.jgit.storage.file;version="[4.1.2,4.2.0)", + org.eclipse.jgit.treewalk;version="[4.1.2,4.2.0)", + org.eclipse.jgit.treewalk.filter;version="[4.1.2,4.2.0)", + org.eclipse.jgit.util;version="[4.1.2,4.2.0)", + org.eclipse.jgit.util.io;version="[4.1.2,4.2.0)", org.junit;version="[4.0.0,5.0.0)" -Export-Package: org.eclipse.jgit.junit;version="4.1.1"; +Export-Package: org.eclipse.jgit.junit;version="4.1.2"; uses:="org.eclipse.jgit.dircache, org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml index b4d4d27f4..53f1bdcc8 100644 --- a/org.eclipse.jgit.junit/pom.xml +++ b/org.eclipse.jgit.junit/pom.xml @@ -52,7 +52,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.junit diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml index 7cf81353f..43575f525 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml index 849f7bbfa..f7ef5ee25 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit jgit.tycho.parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.feature diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml index 8544d1a2b..f54ec78c5 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml index 060ad0e6f..8be8b18b9 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit jgit.tycho.parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.feature diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml index 752f0444e..8dbf9f8a3 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml index 3ed76d384..aa1d85cdc 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit jgit.tycho.parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.feature diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml index a6f0a4c04..084c49f16 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml index 438173bea..6010467d6 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit jgit.tycho.parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.feature diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml index 61070bff1..8e13dc939 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml index 3754fd6c9..b72f82f45 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit jgit.tycho.parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.feature diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml index 72da41c7e..d7c1f6ad7 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit jgit.tycho.parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.repository diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml index 09ee4c478..9a7e6e0ba 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml index 50816beaa..d71064311 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit jgit.tycho.parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.feature diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF index 1d3c436e9..60492325c 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF @@ -2,4 +2,4 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: JGit Target Platform Bundle Bundle-SymbolicName: org.eclipse.jgit.target -Bundle-Version: 4.1.1.201511131810-r +Bundle-Version: 4.1.2.qualifier diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml index e95377f94..e8e036ed4 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml @@ -49,7 +49,7 @@ org.eclipse.jgit jgit.tycho.parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.target diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml index 64750ec37..e1f71cbd7 100644 --- a/org.eclipse.jgit.packaging/pom.xml +++ b/org.eclipse.jgit.packaging/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jgit jgit.tycho.parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT pom JGit Tycho Parent diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF index 5b9a11534..08a08a828 100644 --- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF @@ -2,27 +2,27 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.pgm.test -Bundle-Version: 4.1.1.201511131810-r +Bundle-Version: 4.1.2.qualifier Bundle-Vendor: %provider_name Bundle-Localization: plugin Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.7 -Import-Package: org.eclipse.jgit.api;version="[4.1.1,4.2.0)", - org.eclipse.jgit.api.errors;version="[4.1.1,4.2.0)", - org.eclipse.jgit.diff;version="[4.1.1,4.2.0)", - org.eclipse.jgit.dircache;version="[4.1.1,4.2.0)", - org.eclipse.jgit.junit;version="[4.1.1,4.2.0)", - org.eclipse.jgit.lib;version="[4.1.1,4.2.0)", - org.eclipse.jgit.merge;version="[4.1.1,4.2.0)", - org.eclipse.jgit.pgm;version="[4.1.1,4.2.0)", - org.eclipse.jgit.pgm.internal;version="[4.1.1,4.2.0)", - org.eclipse.jgit.pgm.opt;version="[4.1.1,4.2.0)", - org.eclipse.jgit.revwalk;version="[4.1.1,4.2.0)", - org.eclipse.jgit.storage.file;version="[4.1.1,4.2.0)", - org.eclipse.jgit.transport;version="[4.1.1,4.2.0)", - org.eclipse.jgit.treewalk;version="[4.1.1,4.2.0)", - org.eclipse.jgit.util;version="[4.1.1,4.2.0)", - org.eclipse.jgit.util.io;version="[4.1.1,4.2.0)", +Import-Package: org.eclipse.jgit.api;version="[4.1.2,4.2.0)", + org.eclipse.jgit.api.errors;version="[4.1.2,4.2.0)", + org.eclipse.jgit.diff;version="[4.1.2,4.2.0)", + org.eclipse.jgit.dircache;version="[4.1.2,4.2.0)", + org.eclipse.jgit.junit;version="[4.1.2,4.2.0)", + org.eclipse.jgit.lib;version="[4.1.2,4.2.0)", + org.eclipse.jgit.merge;version="[4.1.2,4.2.0)", + org.eclipse.jgit.pgm;version="[4.1.2,4.2.0)", + org.eclipse.jgit.pgm.internal;version="[4.1.2,4.2.0)", + org.eclipse.jgit.pgm.opt;version="[4.1.2,4.2.0)", + org.eclipse.jgit.revwalk;version="[4.1.2,4.2.0)", + org.eclipse.jgit.storage.file;version="[4.1.2,4.2.0)", + org.eclipse.jgit.transport;version="[4.1.2,4.2.0)", + org.eclipse.jgit.treewalk;version="[4.1.2,4.2.0)", + org.eclipse.jgit.util;version="[4.1.2,4.2.0)", + org.eclipse.jgit.util.io;version="[4.1.2,4.2.0)", org.hamcrest.core;bundle-version="[1.1.0,2.0.0)", org.junit;version="[4.4.0,5.0.0)", org.kohsuke.args4j;version="[2.0.12,2.1.0)" diff --git a/org.eclipse.jgit.pgm.test/pom.xml b/org.eclipse.jgit.pgm.test/pom.xml index acfe6009f..132645b97 100644 --- a/org.eclipse.jgit.pgm.test/pom.xml +++ b/org.eclipse.jgit.pgm.test/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.pgm.test diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF index 30db7a84f..3a78d94df 100644 --- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.pgm -Bundle-Version: 4.1.1.201511131810-r +Bundle-Version: 4.1.2.qualifier Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy Bundle-Localization: plugin @@ -10,38 +10,38 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Import-Package: org.apache.commons.compress.archivers;version="[1.3,2.0)", org.apache.commons.compress.archivers.tar;version="[1.3,2.0)", org.apache.commons.compress.archivers.zip;version="[1.3,2.0)", - org.eclipse.jgit.api;version="[4.1.1,4.2.0)", - org.eclipse.jgit.api.errors;version="[4.1.1,4.2.0)", - org.eclipse.jgit.archive;version="[4.1.1,4.2.0)", - org.eclipse.jgit.awtui;version="[4.1.1,4.2.0)", - org.eclipse.jgit.blame;version="[4.1.1,4.2.0)", - org.eclipse.jgit.diff;version="[4.1.1,4.2.0)", - org.eclipse.jgit.dircache;version="[4.1.1,4.2.0)", - org.eclipse.jgit.errors;version="[4.1.1,4.2.0)", - org.eclipse.jgit.gitrepo;version="[4.1.1,4.2.0)", - org.eclipse.jgit.internal.storage.file;version="[4.1.1,4.2.0)", - org.eclipse.jgit.internal.storage.pack;version="[4.1.1,4.2.0)", - org.eclipse.jgit.lib;version="[4.1.1,4.2.0)", - org.eclipse.jgit.merge;version="4.1.1", - org.eclipse.jgit.nls;version="[4.1.1,4.2.0)", - org.eclipse.jgit.notes;version="[4.1.1,4.2.0)", - org.eclipse.jgit.revplot;version="[4.1.1,4.2.0)", - org.eclipse.jgit.revwalk;version="[4.1.1,4.2.0)", - org.eclipse.jgit.revwalk.filter;version="[4.1.1,4.2.0)", - org.eclipse.jgit.storage.file;version="[4.1.1,4.2.0)", - org.eclipse.jgit.storage.pack;version="[4.1.1,4.2.0)", - org.eclipse.jgit.transport;version="[4.1.1,4.2.0)", - org.eclipse.jgit.transport.resolver;version="[4.1.1,4.2.0)", - org.eclipse.jgit.treewalk;version="[4.1.1,4.2.0)", - org.eclipse.jgit.treewalk.filter;version="[4.1.1,4.2.0)", - org.eclipse.jgit.util;version="[4.1.1,4.2.0)", - org.eclipse.jgit.util.io;version="[4.1.1,4.2.0)", + org.eclipse.jgit.api;version="[4.1.2,4.2.0)", + org.eclipse.jgit.api.errors;version="[4.1.2,4.2.0)", + org.eclipse.jgit.archive;version="[4.1.2,4.2.0)", + org.eclipse.jgit.awtui;version="[4.1.2,4.2.0)", + org.eclipse.jgit.blame;version="[4.1.2,4.2.0)", + org.eclipse.jgit.diff;version="[4.1.2,4.2.0)", + org.eclipse.jgit.dircache;version="[4.1.2,4.2.0)", + org.eclipse.jgit.errors;version="[4.1.2,4.2.0)", + org.eclipse.jgit.gitrepo;version="[4.1.2,4.2.0)", + org.eclipse.jgit.internal.storage.file;version="[4.1.2,4.2.0)", + org.eclipse.jgit.internal.storage.pack;version="[4.1.2,4.2.0)", + org.eclipse.jgit.lib;version="[4.1.2,4.2.0)", + org.eclipse.jgit.merge;version="4.1.2", + org.eclipse.jgit.nls;version="[4.1.2,4.2.0)", + org.eclipse.jgit.notes;version="[4.1.2,4.2.0)", + org.eclipse.jgit.revplot;version="[4.1.2,4.2.0)", + org.eclipse.jgit.revwalk;version="[4.1.2,4.2.0)", + org.eclipse.jgit.revwalk.filter;version="[4.1.2,4.2.0)", + org.eclipse.jgit.storage.file;version="[4.1.2,4.2.0)", + org.eclipse.jgit.storage.pack;version="[4.1.2,4.2.0)", + org.eclipse.jgit.transport;version="[4.1.2,4.2.0)", + org.eclipse.jgit.transport.resolver;version="[4.1.2,4.2.0)", + org.eclipse.jgit.treewalk;version="[4.1.2,4.2.0)", + org.eclipse.jgit.treewalk.filter;version="[4.1.2,4.2.0)", + org.eclipse.jgit.util;version="[4.1.2,4.2.0)", + org.eclipse.jgit.util.io;version="[4.1.2,4.2.0)", org.kohsuke.args4j;version="[2.0.12,2.1.0)", org.kohsuke.args4j.spi;version="[2.0.15,2.1.0)" -Export-Package: org.eclipse.jgit.console;version="4.1.1"; +Export-Package: org.eclipse.jgit.console;version="4.1.2"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.util", - org.eclipse.jgit.pgm;version="4.1.1"; + org.eclipse.jgit.pgm;version="4.1.2"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.pgm.opt, @@ -52,11 +52,11 @@ Export-Package: org.eclipse.jgit.console;version="4.1.1"; org.eclipse.jgit.treewalk, javax.swing, org.eclipse.jgit.transport", - org.eclipse.jgit.pgm.debug;version="4.1.1"; + org.eclipse.jgit.pgm.debug;version="4.1.2"; uses:="org.eclipse.jgit.util.io, org.eclipse.jgit.pgm", - org.eclipse.jgit.pgm.internal;version="4.1.1";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test", - org.eclipse.jgit.pgm.opt;version="4.1.1"; + org.eclipse.jgit.pgm.internal;version="4.1.2";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test", + org.eclipse.jgit.pgm.opt;version="4.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.kohsuke.args4j.spi, diff --git a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF index dfc5ab9a9..303ebf44f 100644 --- a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit.pgm - Sources Bundle-SymbolicName: org.eclipse.jgit.pgm.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 4.1.1.201511131810-r -Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.1.1.201511131810-r";roots="." +Bundle-Version: 4.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.1.2.qualifier";roots="." diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml index 2c2e55de6..42fde6fe9 100644 --- a/org.eclipse.jgit.pgm/pom.xml +++ b/org.eclipse.jgit.pgm/pom.xml @@ -50,7 +50,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.pgm diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF index 9d25fca29..8f2f4284f 100644 --- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF @@ -2,51 +2,51 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.test -Bundle-Version: 4.1.1.201511131810-r +Bundle-Version: 4.1.2.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Import-Package: com.googlecode.javaewah;version="[0.7.9,0.8.0)", - org.eclipse.jgit.api;version="[4.1.1,4.2.0)", - org.eclipse.jgit.api.errors;version="[4.1.1,4.2.0)", - org.eclipse.jgit.attributes;version="[4.1.1,4.2.0)", - org.eclipse.jgit.awtui;version="[4.1.1,4.2.0)", - org.eclipse.jgit.blame;version="[4.1.1,4.2.0)", - org.eclipse.jgit.diff;version="[4.1.1,4.2.0)", - org.eclipse.jgit.dircache;version="[4.1.1,4.2.0)", - org.eclipse.jgit.errors;version="[4.1.1,4.2.0)", - org.eclipse.jgit.events;version="[4.1.1,4.2.0)", - org.eclipse.jgit.fnmatch;version="[4.1.1,4.2.0)", - org.eclipse.jgit.gitrepo;version="[4.1.1,4.2.0)", - org.eclipse.jgit.hooks;version="[4.1.1,4.2.0)", - org.eclipse.jgit.ignore;version="[4.1.1,4.2.0)", - org.eclipse.jgit.ignore.internal;version="[4.1.1,4.2.0)", - org.eclipse.jgit.internal;version="[4.1.1,4.2.0)", - org.eclipse.jgit.internal.storage.dfs;version="[4.1.1,4.2.0)", - org.eclipse.jgit.internal.storage.file;version="[4.1.1,4.2.0)", - org.eclipse.jgit.internal.storage.pack;version="[4.1.1,4.2.0)", - org.eclipse.jgit.junit;version="[4.1.1,4.2.0)", - org.eclipse.jgit.lib;version="[4.1.1,4.2.0)", - org.eclipse.jgit.merge;version="[4.1.1,4.2.0)", - org.eclipse.jgit.nls;version="[4.1.1,4.2.0)", - org.eclipse.jgit.notes;version="[4.1.1,4.2.0)", - org.eclipse.jgit.patch;version="[4.1.1,4.2.0)", - org.eclipse.jgit.pgm;version="[4.1.1,4.2.0)", - org.eclipse.jgit.pgm.internal;version="[4.1.1,4.2.0)", - org.eclipse.jgit.revplot;version="[4.1.1,4.2.0)", - org.eclipse.jgit.revwalk;version="[4.1.1,4.2.0)", - org.eclipse.jgit.revwalk.filter;version="[4.1.1,4.2.0)", - org.eclipse.jgit.storage.file;version="[4.1.1,4.2.0)", - org.eclipse.jgit.storage.pack;version="[4.1.1,4.2.0)", - org.eclipse.jgit.submodule;version="[4.1.1,4.2.0)", - org.eclipse.jgit.transport;version="[4.1.1,4.2.0)", - org.eclipse.jgit.transport.http;version="[4.1.1,4.2.0)", - org.eclipse.jgit.transport.resolver;version="[4.1.1,4.2.0)", - org.eclipse.jgit.treewalk;version="[4.1.1,4.2.0)", - org.eclipse.jgit.treewalk.filter;version="[4.1.1,4.2.0)", - org.eclipse.jgit.util;version="[4.1.1,4.2.0)", - org.eclipse.jgit.util.io;version="[4.1.1,4.2.0)", + org.eclipse.jgit.api;version="[4.1.2,4.2.0)", + org.eclipse.jgit.api.errors;version="[4.1.2,4.2.0)", + org.eclipse.jgit.attributes;version="[4.1.2,4.2.0)", + org.eclipse.jgit.awtui;version="[4.1.2,4.2.0)", + org.eclipse.jgit.blame;version="[4.1.2,4.2.0)", + org.eclipse.jgit.diff;version="[4.1.2,4.2.0)", + org.eclipse.jgit.dircache;version="[4.1.2,4.2.0)", + org.eclipse.jgit.errors;version="[4.1.2,4.2.0)", + org.eclipse.jgit.events;version="[4.1.2,4.2.0)", + org.eclipse.jgit.fnmatch;version="[4.1.2,4.2.0)", + org.eclipse.jgit.gitrepo;version="[4.1.2,4.2.0)", + org.eclipse.jgit.hooks;version="[4.1.2,4.2.0)", + org.eclipse.jgit.ignore;version="[4.1.2,4.2.0)", + org.eclipse.jgit.ignore.internal;version="[4.1.2,4.2.0)", + org.eclipse.jgit.internal;version="[4.1.2,4.2.0)", + org.eclipse.jgit.internal.storage.dfs;version="[4.1.2,4.2.0)", + org.eclipse.jgit.internal.storage.file;version="[4.1.2,4.2.0)", + org.eclipse.jgit.internal.storage.pack;version="[4.1.2,4.2.0)", + org.eclipse.jgit.junit;version="[4.1.2,4.2.0)", + org.eclipse.jgit.lib;version="[4.1.2,4.2.0)", + org.eclipse.jgit.merge;version="[4.1.2,4.2.0)", + org.eclipse.jgit.nls;version="[4.1.2,4.2.0)", + org.eclipse.jgit.notes;version="[4.1.2,4.2.0)", + org.eclipse.jgit.patch;version="[4.1.2,4.2.0)", + org.eclipse.jgit.pgm;version="[4.1.2,4.2.0)", + org.eclipse.jgit.pgm.internal;version="[4.1.2,4.2.0)", + org.eclipse.jgit.revplot;version="[4.1.2,4.2.0)", + org.eclipse.jgit.revwalk;version="[4.1.2,4.2.0)", + org.eclipse.jgit.revwalk.filter;version="[4.1.2,4.2.0)", + org.eclipse.jgit.storage.file;version="[4.1.2,4.2.0)", + org.eclipse.jgit.storage.pack;version="[4.1.2,4.2.0)", + org.eclipse.jgit.submodule;version="[4.1.2,4.2.0)", + org.eclipse.jgit.transport;version="[4.1.2,4.2.0)", + org.eclipse.jgit.transport.http;version="[4.1.2,4.2.0)", + org.eclipse.jgit.transport.resolver;version="[4.1.2,4.2.0)", + org.eclipse.jgit.treewalk;version="[4.1.2,4.2.0)", + org.eclipse.jgit.treewalk.filter;version="[4.1.2,4.2.0)", + org.eclipse.jgit.util;version="[4.1.2,4.2.0)", + org.eclipse.jgit.util.io;version="[4.1.2,4.2.0)", org.hamcrest;version="[1.1.0,2.0.0)", org.junit;version="[4.4.0,5.0.0)", org.junit.experimental.theories;version="[4.4.0,5.0.0)", diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml index 2385d27f9..c174ba270 100644 --- a/org.eclipse.jgit.test/pom.xml +++ b/org.eclipse.jgit.test/pom.xml @@ -52,7 +52,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.test diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF index a9cedc091..3df434701 100644 --- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF @@ -3,14 +3,14 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.ui -Bundle-Version: 4.1.1.201511131810-r +Bundle-Version: 4.1.2.qualifier Bundle-Vendor: %provider_name Bundle-RequiredExecutionEnvironment: JavaSE-1.7 -Export-Package: org.eclipse.jgit.awtui;version="4.1.1" -Import-Package: org.eclipse.jgit.errors;version="[4.1.1,4.2.0)", - org.eclipse.jgit.lib;version="[4.1.1,4.2.0)", - org.eclipse.jgit.nls;version="[4.1.1,4.2.0)", - org.eclipse.jgit.revplot;version="[4.1.1,4.2.0)", - org.eclipse.jgit.revwalk;version="[4.1.1,4.2.0)", - org.eclipse.jgit.transport;version="[4.1.1,4.2.0)", - org.eclipse.jgit.util;version="[4.1.1,4.2.0)" +Export-Package: org.eclipse.jgit.awtui;version="4.1.2" +Import-Package: org.eclipse.jgit.errors;version="[4.1.2,4.2.0)", + org.eclipse.jgit.lib;version="[4.1.2,4.2.0)", + org.eclipse.jgit.nls;version="[4.1.2,4.2.0)", + org.eclipse.jgit.revplot;version="[4.1.2,4.2.0)", + org.eclipse.jgit.revwalk;version="[4.1.2,4.2.0)", + org.eclipse.jgit.transport;version="[4.1.2,4.2.0)", + org.eclipse.jgit.util;version="[4.1.2,4.2.0)" diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml index d64de3243..79c30f0ac 100644 --- a/org.eclipse.jgit.ui/pom.xml +++ b/org.eclipse.jgit.ui/pom.xml @@ -52,7 +52,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit.ui diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index a10e6d6ee..24798be02 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF @@ -2,11 +2,11 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit -Bundle-Version: 4.1.1.201511131810-r +Bundle-Version: 4.1.2.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy -Export-Package: org.eclipse.jgit.api;version="4.1.1"; +Export-Package: org.eclipse.jgit.api;version="4.1.2"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.diff, @@ -20,60 +20,60 @@ Export-Package: org.eclipse.jgit.api;version="4.1.1"; org.eclipse.jgit.submodule, org.eclipse.jgit.transport, org.eclipse.jgit.merge", - org.eclipse.jgit.api.errors;version="4.1.1"; + org.eclipse.jgit.api.errors;version="4.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.errors", - org.eclipse.jgit.attributes;version="4.1.1", - org.eclipse.jgit.blame;version="4.1.1"; + org.eclipse.jgit.attributes;version="4.1.2", + org.eclipse.jgit.blame;version="4.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.diff", - org.eclipse.jgit.diff;version="4.1.1"; + org.eclipse.jgit.diff;version="4.1.2"; uses:="org.eclipse.jgit.patch, org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.util", - org.eclipse.jgit.dircache;version="4.1.1"; + org.eclipse.jgit.dircache;version="4.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.util, org.eclipse.jgit.events, org.eclipse.jgit.attributes", - org.eclipse.jgit.errors;version="4.1.1"; + org.eclipse.jgit.errors;version="4.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.internal.storage.pack, org.eclipse.jgit.transport, org.eclipse.jgit.dircache", - org.eclipse.jgit.events;version="4.1.1"; + org.eclipse.jgit.events;version="4.1.2"; uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.fnmatch;version="4.1.1", - org.eclipse.jgit.gitrepo;version="4.1.1"; + org.eclipse.jgit.fnmatch;version="4.1.2", + org.eclipse.jgit.gitrepo;version="4.1.2"; uses:="org.eclipse.jgit.api, org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.xml.sax.helpers, org.xml.sax", - org.eclipse.jgit.gitrepo.internal;version="4.1.1";x-internal:=true, - org.eclipse.jgit.hooks;version="4.1.1"; + org.eclipse.jgit.gitrepo.internal;version="4.1.2";x-internal:=true, + org.eclipse.jgit.hooks;version="4.1.2"; uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.ignore;version="4.1.1", - org.eclipse.jgit.ignore.internal;version="4.1.1";x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal;version="4.1.1";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test", - org.eclipse.jgit.internal.storage.dfs;version="4.1.1"; + org.eclipse.jgit.ignore;version="4.1.2", + org.eclipse.jgit.ignore.internal;version="4.1.2";x-friends:="org.eclipse.jgit.test", + org.eclipse.jgit.internal;version="4.1.2";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test", + org.eclipse.jgit.internal.storage.dfs;version="4.1.2"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.http.server", - org.eclipse.jgit.internal.storage.file;version="4.1.1"; + org.eclipse.jgit.internal.storage.file;version="4.1.2"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.junit, org.eclipse.jgit.junit.http, org.eclipse.jgit.http.server, org.eclipse.jgit.java7.test, org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.pack;version="4.1.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", - org.eclipse.jgit.lib;version="4.1.1"; + org.eclipse.jgit.internal.storage.pack;version="4.1.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm", + org.eclipse.jgit.lib;version="4.1.2"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.util, @@ -83,45 +83,45 @@ Export-Package: org.eclipse.jgit.api;version="4.1.1"; org.eclipse.jgit.treewalk, org.eclipse.jgit.transport, org.eclipse.jgit.submodule", - org.eclipse.jgit.merge;version="4.1.1"; + org.eclipse.jgit.merge;version="4.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.revwalk, org.eclipse.jgit.diff, org.eclipse.jgit.dircache, org.eclipse.jgit.api", - org.eclipse.jgit.nls;version="4.1.1", - org.eclipse.jgit.notes;version="4.1.1"; + org.eclipse.jgit.nls;version="4.1.2", + org.eclipse.jgit.notes;version="4.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.revwalk, org.eclipse.jgit.merge", - org.eclipse.jgit.patch;version="4.1.1"; + org.eclipse.jgit.patch;version="4.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.diff", - org.eclipse.jgit.revplot;version="4.1.1"; + org.eclipse.jgit.revplot;version="4.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk", - org.eclipse.jgit.revwalk;version="4.1.1"; + org.eclipse.jgit.revwalk;version="4.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.diff, org.eclipse.jgit.revwalk.filter", - org.eclipse.jgit.revwalk.filter;version="4.1.1"; + org.eclipse.jgit.revwalk.filter;version="4.1.2"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.lib, org.eclipse.jgit.util", - org.eclipse.jgit.storage.file;version="4.1.1"; + org.eclipse.jgit.storage.file;version="4.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.util", - org.eclipse.jgit.storage.pack;version="4.1.1"; + org.eclipse.jgit.storage.pack;version="4.1.2"; uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.submodule;version="4.1.1"; + org.eclipse.jgit.submodule;version="4.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.treewalk", - org.eclipse.jgit.transport;version="4.1.1"; + org.eclipse.jgit.transport;version="4.1.2"; uses:="org.eclipse.jgit.transport.resolver, org.eclipse.jgit.revwalk, org.eclipse.jgit.internal.storage.pack, @@ -133,26 +133,26 @@ Export-Package: org.eclipse.jgit.api;version="4.1.1"; org.eclipse.jgit.transport.http, org.eclipse.jgit.errors, org.eclipse.jgit.storage.pack", - org.eclipse.jgit.transport.http;version="4.1.1"; + org.eclipse.jgit.transport.http;version="4.1.2"; uses:="javax.net.ssl", - org.eclipse.jgit.transport.resolver;version="4.1.1"; + org.eclipse.jgit.transport.resolver;version="4.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.transport", - org.eclipse.jgit.treewalk;version="4.1.1"; + org.eclipse.jgit.treewalk;version="4.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.eclipse.jgit.attributes, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.util, org.eclipse.jgit.dircache", - org.eclipse.jgit.treewalk.filter;version="4.1.1"; + org.eclipse.jgit.treewalk.filter;version="4.1.2"; uses:="org.eclipse.jgit.treewalk", - org.eclipse.jgit.util;version="4.1.1"; + org.eclipse.jgit.util;version="4.1.2"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.transport.http, org.eclipse.jgit.storage.file, org.ietf.jgss", - org.eclipse.jgit.util.io;version="4.1.1" + org.eclipse.jgit.util.io;version="4.1.2" Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Require-Bundle: com.jcraft.jsch;bundle-version="[0.1.37,0.2.0)", org.eclipse.jdt.annotation;bundle-version="[1.1.0,2.0.0)";resolution:=optional diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF index 83b239cad..c5476a85c 100644 --- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit - Sources Bundle-SymbolicName: org.eclipse.jgit.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 4.1.1.201511131810-r -Eclipse-SourceBundle: org.eclipse.jgit;version="4.1.1.201511131810-r";roots="." +Bundle-Version: 4.1.2.qualifier +Eclipse-SourceBundle: org.eclipse.jgit;version="4.1.2.qualifier";roots="." diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml index 875794062..8791389da 100644 --- a/org.eclipse.jgit/pom.xml +++ b/org.eclipse.jgit/pom.xml @@ -53,7 +53,7 @@ org.eclipse.jgit org.eclipse.jgit-parent - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT org.eclipse.jgit diff --git a/pom.xml b/pom.xml index a89978477..760d66fdb 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ org.eclipse.jgit org.eclipse.jgit-parent pom - 4.1.1.201511131810-r + 4.1.2-SNAPSHOT JGit - Parent ${jgit-url} From 4acff5a59e38eb13d4c7c17117526b5272c6e74f Mon Sep 17 00:00:00 2001 From: Matthias Sohn Date: Sun, 15 Nov 2015 22:56:55 +0100 Subject: [PATCH 08/50] Handle InternalError during symlink support detection on Windows When JGit tries to detect symlink support the attempt to create a symlink may fail with a java.lang.InternalError. This was reported for a case where the application using JGit runs in Windows XP compatibility mode using Oracle JDK 1.8. Handle this by assuming symlinks are not supported in this case. Bug: 471027 Change-Id: I978288754dea0c6fffd3457fad7d4d971e27c6c2 Signed-off-by: Matthias Sohn --- org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java index f0a2e721a..defe14f0e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java @@ -171,7 +171,8 @@ private void detectSymlinkSupport() { createSymLink(linkName, tempFile.getPath()); supportSymlinks = Boolean.TRUE; linkName.delete(); - } catch (IOException | UnsupportedOperationException e) { + } catch (IOException | UnsupportedOperationException + | InternalError e) { supportSymlinks = Boolean.FALSE; } finally { if (tempFile != null) From f1405a337a399a91a10497100a80e841b8ef7aea Mon Sep 17 00:00:00 2001 From: Andrey Loskutov Date: Sun, 15 Nov 2015 22:55:58 +0100 Subject: [PATCH 09/50] Added jgit own NonNull annotation type The annotation is required for example in Repository case (patch follows), where almost all non-void return methods return Nullable except few returning NonNull. I definitely do not favor this style, but it is a nightmare to clients to guess if the null check is needed or not. Change-Id: Ib2a778a246c6d84b7c32565f54df2385b59f6498 Signed-off-by: Andrey Loskutov --- .../org/eclipse/jgit/annotations/NonNull.java | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java new file mode 100644 index 000000000..a74654fb5 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2015, Andrey Loskutov + * 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.annotations; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * JGit's replacement for the {@code javax.annotation.Nonnull}. + *

+ * Denotes that a local variable, parameter, field, method return value expected + * to be non {@code null}. + */ +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({ FIELD, METHOD, PARAMETER, LOCAL_VARIABLE }) +public @interface NonNull { + // marker annotation with no members +} From 864d3899ad5decc6b2184fc8272b7caf3ce3bff4 Mon Sep 17 00:00:00 2001 From: Andrey Loskutov Date: Sun, 15 Nov 2015 22:59:14 +0100 Subject: [PATCH 10/50] Fixed typo in preferences: NonByDefault -> NonNullByDefault Change-Id: I6ef5fbdb57e7cba010ad23cb3f6af02891e7fe78 Signed-off-by: Andrey Loskutov --- org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs index 195987db6..45d6d2c4c 100644 --- a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs @@ -2,7 +2,7 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=enabled org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jgit.annotations.NonNull -org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jgit.annotations.NonByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jgit.annotations.NonNullByDefault org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jgit.annotations.Nullable org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled From 2f6959d9f31faadcc50b5b0ded25f5e5a11befa4 Mon Sep 17 00:00:00 2001 From: Andrey Loskutov Date: Mon, 16 Nov 2015 23:41:38 +0100 Subject: [PATCH 11/50] Make jgit annotations accessible to other plugins Other plugins which want to use JGit nullness annotations in their code cannot do this if the annotations aren't part of the published API. Unfortunately it looks like although Eclipse JDT allows to use custom nullness annotation types per project, it does not understand if those annotations are used mixed with other nullness annotations in other projects. E.g. EGit can either configure JGit annotations for NPE analysis and so "understand" nullness from JGit API but so it loses the ability to use any other nullness annotations to annotate its own code. Change-Id: Ieeeb578c2fe35223a7561d668dce8e767dc89ef0 Signed-off-by: Andrey Loskutov --- org.eclipse.jgit/META-INF/MANIFEST.MF | 52 ++++++------------- .../org/eclipse/jgit/annotations/NonNull.java | 2 + .../eclipse/jgit/annotations/Nullable.java | 2 + 3 files changed, 19 insertions(+), 37 deletions(-) diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index b6899b280..2a953b559 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF @@ -6,7 +6,8 @@ Bundle-Version: 4.2.0.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy -Export-Package: org.eclipse.jgit.api;version="4.2.0"; +Export-Package: org.eclipse.jgit.annotations;version="4.2.0", + org.eclipse.jgit.api;version="4.2.0"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.diff, @@ -20,9 +21,7 @@ Export-Package: org.eclipse.jgit.api;version="4.2.0"; org.eclipse.jgit.submodule, org.eclipse.jgit.transport, org.eclipse.jgit.merge", - org.eclipse.jgit.api.errors;version="4.2.0"; - uses:="org.eclipse.jgit.lib, - org.eclipse.jgit.errors", + org.eclipse.jgit.api.errors;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors", org.eclipse.jgit.attributes;version="4.2.0", org.eclipse.jgit.blame;version="4.2.0"; uses:="org.eclipse.jgit.lib, @@ -47,8 +46,7 @@ Export-Package: org.eclipse.jgit.api;version="4.2.0"; org.eclipse.jgit.internal.storage.pack, org.eclipse.jgit.transport, org.eclipse.jgit.dircache", - org.eclipse.jgit.events;version="4.2.0"; - uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.events;version="4.2.0";uses:="org.eclipse.jgit.lib", org.eclipse.jgit.fnmatch;version="4.2.0", org.eclipse.jgit.gitrepo;version="4.2.0"; uses:="org.eclipse.jgit.api, @@ -57,14 +55,11 @@ Export-Package: org.eclipse.jgit.api;version="4.2.0"; org.xml.sax.helpers, org.xml.sax", org.eclipse.jgit.gitrepo.internal;version="4.2.0";x-internal:=true, - org.eclipse.jgit.hooks;version="4.2.0"; - uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.hooks;version="4.2.0";uses:="org.eclipse.jgit.lib", org.eclipse.jgit.ignore;version="4.2.0", org.eclipse.jgit.ignore.internal;version="4.2.0";x-friends:="org.eclipse.jgit.test", org.eclipse.jgit.internal;version="4.2.0";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test", - org.eclipse.jgit.internal.storage.dfs;version="4.2.0"; - x-friends:="org.eclipse.jgit.test, - org.eclipse.jgit.http.server", + org.eclipse.jgit.internal.storage.dfs;version="4.2.0";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.server", org.eclipse.jgit.internal.storage.file;version="4.2.0"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.junit, @@ -96,31 +91,18 @@ Export-Package: org.eclipse.jgit.api;version="4.2.0"; org.eclipse.jgit.treewalk, org.eclipse.jgit.revwalk, org.eclipse.jgit.merge", - org.eclipse.jgit.patch;version="4.2.0"; - uses:="org.eclipse.jgit.lib, - org.eclipse.jgit.diff", - org.eclipse.jgit.revplot;version="4.2.0"; - uses:="org.eclipse.jgit.lib, - org.eclipse.jgit.revwalk", + org.eclipse.jgit.patch;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff", + org.eclipse.jgit.revplot;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk", org.eclipse.jgit.revwalk;version="4.2.0"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.diff, org.eclipse.jgit.revwalk.filter", - org.eclipse.jgit.revwalk.filter;version="4.2.0"; - uses:="org.eclipse.jgit.revwalk, - org.eclipse.jgit.lib, - org.eclipse.jgit.util", - org.eclipse.jgit.storage.file;version="4.2.0"; - uses:="org.eclipse.jgit.lib, - org.eclipse.jgit.util", - org.eclipse.jgit.storage.pack;version="4.2.0"; - uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.submodule;version="4.2.0"; - uses:="org.eclipse.jgit.lib, - org.eclipse.jgit.treewalk.filter, - org.eclipse.jgit.treewalk", + org.eclipse.jgit.revwalk.filter;version="4.2.0";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util", + org.eclipse.jgit.storage.file;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util", + org.eclipse.jgit.storage.pack;version="4.2.0";uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.submodule;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk", org.eclipse.jgit.transport;version="4.2.0"; uses:="org.eclipse.jgit.transport.resolver, org.eclipse.jgit.revwalk, @@ -133,11 +115,8 @@ Export-Package: org.eclipse.jgit.api;version="4.2.0"; org.eclipse.jgit.transport.http, org.eclipse.jgit.errors, org.eclipse.jgit.storage.pack", - org.eclipse.jgit.transport.http;version="4.2.0"; - uses:="javax.net.ssl", - org.eclipse.jgit.transport.resolver;version="4.2.0"; - uses:="org.eclipse.jgit.lib, - org.eclipse.jgit.transport", + org.eclipse.jgit.transport.http;version="4.2.0";uses:="javax.net.ssl", + org.eclipse.jgit.transport.resolver;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport", org.eclipse.jgit.treewalk;version="4.2.0"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, @@ -145,8 +124,7 @@ Export-Package: org.eclipse.jgit.api;version="4.2.0"; org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.util, org.eclipse.jgit.dircache", - org.eclipse.jgit.treewalk.filter;version="4.2.0"; - uses:="org.eclipse.jgit.treewalk", + org.eclipse.jgit.treewalk.filter;version="4.2.0";uses:="org.eclipse.jgit.treewalk", org.eclipse.jgit.util;version="4.2.0"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.transport.http, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java index a74654fb5..08f81cb9f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java @@ -58,6 +58,8 @@ *

* Denotes that a local variable, parameter, field, method return value expected * to be non {@code null}. + * + * @since 4.2 */ @Documented @Retention(RetentionPolicy.CLASS) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java index 254920e7a..4275dc4fa 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java @@ -58,6 +58,8 @@ *

* Denotes that a local variable, parameter, field, method return value can be * {@code null}. + * + * @since 4.2 */ @Documented @Retention(RetentionPolicy.CLASS) From 12280c02dbb8e4ac10893fbbd415be757afab4c1 Mon Sep 17 00:00:00 2001 From: Arthur Daussy Date: Fri, 31 Oct 2014 17:46:36 +0100 Subject: [PATCH 12/50] Adds the git attributes computation on the treewalk Adds the getAttributes feature to the tree walk. The computation of attributes needs to be done by the TreeWalk since it needs both a WorkingTreeIterator and a DirCacheIterator. Bug: 342372 CQ: 9120 Change-Id: I5e33257fd8c9895869a128bad3fd1e720409d361 Signed-off-by: Arthur Daussy Signed-off-by: Christian Halstrick --- .../AttributesNodeDirCacheIteratorTest.java | 12 +- ...eNodeTest.java => AttributesNodeTest.java} | 2 +- ...AttributesNodeWorkingTreeIteratorTest.java | 47 +- .../attributes/TreeWalkAttributeTest.java | 865 ++++++++++++++++++ org.eclipse.jgit/.settings/.api_filters | 14 + .../src/org/eclipse/jgit/api/AddCommand.java | 3 + .../org/eclipse/jgit/api/CommitCommand.java | 2 + .../attributes/AttributesNodeProvider.java | 81 ++ .../jgit/attributes/AttributesProvider.java | 57 ++ .../org/eclipse/jgit/dircache/DirCache.java | 2 + .../internal/storage/dfs/DfsRepository.java | 37 + .../internal/storage/file/FileRepository.java | 63 ++ .../storage/file/GlobalAttributesNode.java | 87 ++ .../storage/file/InfoAttributesNode.java | 80 ++ .../src/org/eclipse/jgit/lib/IndexDiff.java | 2 + .../src/org/eclipse/jgit/lib/Repository.java | 11 + .../jgit/treewalk/NameConflictTreeWalk.java | 2 +- .../org/eclipse/jgit/treewalk/TreeWalk.java | 268 +++++- .../jgit/treewalk/WorkingTreeIterator.java | 88 +- 19 files changed, 1608 insertions(+), 115 deletions(-) rename org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/{AttributeNodeTest.java => AttributesNodeTest.java} (99%) create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GlobalAttributesNode.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java index 49279e6e5..3e6ca6220 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java @@ -243,23 +243,23 @@ private void assertIteration(FileMode type, String pathName, DirCacheIterator itr = walk.getTree(0, DirCacheIterator.class); assertNotNull("has tree", itr); - AttributesNode attributeNode = itr.getEntryAttributesNode(db + AttributesNode attributesNode = itr.getEntryAttributesNode(db .newObjectReader()); - assertAttributeNode(pathName, attributeNode, nodeAttrs); + assertAttributesNode(pathName, attributesNode, nodeAttrs); if (D.equals(type)) walk.enterSubtree(); } - private void assertAttributeNode(String pathName, - AttributesNode attributeNode, List nodeAttrs) { - if (attributeNode == null) + private void assertAttributesNode(String pathName, + AttributesNode attributesNode, List nodeAttrs) { + if (attributesNode == null) assertTrue(nodeAttrs == null || nodeAttrs.isEmpty()); else { Map entryAttributes = new LinkedHashMap(); - attributeNode.getAttributes(pathName, false, entryAttributes); + attributesNode.getAttributes(pathName, false, entryAttributes); if (nodeAttrs != null && !nodeAttrs.isEmpty()) { for (Attribute attribute : nodeAttrs) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java similarity index 99% rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeNodeTest.java rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java index ea250369a..d82baaa36 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeNodeTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java @@ -60,7 +60,7 @@ /** * Test {@link AttributesNode} */ -public class AttributeNodeTest { +public class AttributesNodeTest { private static final Attribute A_SET_ATTR = new Attribute("A", SET); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java index 64b0535d6..bcf17174b 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java @@ -76,14 +76,10 @@ public class AttributesNodeWorkingTreeIteratorTest extends RepositoryTestCase { private static final FileMode F = FileMode.REGULAR_FILE; - private static Attribute EOL_CRLF = new Attribute("eol", "crlf"); - private static Attribute EOL_LF = new Attribute("eol", "lf"); private static Attribute DELTA_UNSET = new Attribute("delta", State.UNSET); - private static Attribute CUSTOM_VALUE = new Attribute("custom", "value"); - private TreeWalk walk; @Test @@ -112,25 +108,19 @@ public void testRules() throws Exception { walk = beginWalk(); assertIteration(F, ".gitattributes"); - assertIteration(F, "global.txt", asList(EOL_LF), null, - asList(CUSTOM_VALUE)); - assertIteration(F, "readme.txt", asList(EOL_LF), null, - asList(CUSTOM_VALUE)); + assertIteration(F, "global.txt", asList(EOL_LF)); + assertIteration(F, "readme.txt", asList(EOL_LF)); assertIteration(D, "src"); assertIteration(D, "src/config"); assertIteration(F, "src/config/.gitattributes"); - assertIteration(F, "src/config/readme.txt", asList(DELTA_UNSET), null, - asList(CUSTOM_VALUE)); - assertIteration(F, "src/config/windows.file", null, asList(EOL_CRLF), - null); - assertIteration(F, "src/config/windows.txt", asList(DELTA_UNSET), - asList(EOL_CRLF), asList(CUSTOM_VALUE)); + assertIteration(F, "src/config/readme.txt", asList(DELTA_UNSET)); + assertIteration(F, "src/config/windows.file", null); + assertIteration(F, "src/config/windows.txt", asList(DELTA_UNSET)); - assertIteration(F, "windows.file", null, asList(EOL_CRLF), null); - assertIteration(F, "windows.txt", asList(EOL_LF), asList(EOL_CRLF), - asList(CUSTOM_VALUE)); + assertIteration(F, "windows.file", null); + assertIteration(F, "windows.txt", asList(EOL_LF)); endWalk(); } @@ -212,14 +202,11 @@ public void testNoMatchingAttributes() throws Exception { private void assertIteration(FileMode type, String pathName) throws IOException { - assertIteration(type, pathName, Collections. emptyList(), - Collections. emptyList(), - Collections. emptyList()); + assertIteration(type, pathName, Collections. emptyList()); } private void assertIteration(FileMode type, String pathName, - List nodeAttrs, List infoAttrs, - List globalAttrs) + List nodeAttrs) throws IOException { assertTrue("walk has entry", walk.next()); assertEquals(pathName, walk.getPathString()); @@ -227,25 +214,21 @@ private void assertIteration(FileMode type, String pathName, WorkingTreeIterator itr = walk.getTree(0, WorkingTreeIterator.class); assertNotNull("has tree", itr); - AttributesNode attributeNode = itr.getEntryAttributesNode(); - assertAttributeNode(pathName, attributeNode, nodeAttrs); - AttributesNode infoAttributeNode = itr.getInfoAttributesNode(); - assertAttributeNode(pathName, infoAttributeNode, infoAttrs); - AttributesNode globalAttributeNode = itr.getGlobalAttributesNode(); - assertAttributeNode(pathName, globalAttributeNode, globalAttrs); + AttributesNode attributesNode = itr.getEntryAttributesNode(); + assertAttributesNode(pathName, attributesNode, nodeAttrs); if (D.equals(type)) walk.enterSubtree(); } - private void assertAttributeNode(String pathName, - AttributesNode attributeNode, List nodeAttrs) { - if (attributeNode == null) + private void assertAttributesNode(String pathName, + AttributesNode attributesNode, List nodeAttrs) { + if (attributesNode == null) assertTrue(nodeAttrs == null || nodeAttrs.isEmpty()); else { Map entryAttributes = new LinkedHashMap(); - attributeNode.getAttributes(pathName, false, entryAttributes); + attributesNode.getAttributes(pathName, false, entryAttributes); if (nodeAttrs != null && !nodeAttrs.isEmpty()) { for (Attribute attribute : nodeAttrs) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java new file mode 100644 index 000000000..c3d5e8752 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java @@ -0,0 +1,865 @@ +/* + * Copyright (C) 2014, Obeo. + * 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.attributes; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.api.errors.NoFilepatternException; +import org.eclipse.jgit.attributes.Attribute.State; +import org.eclipse.jgit.dircache.DirCacheIterator; +import org.eclipse.jgit.errors.NoWorkTreeException; +import org.eclipse.jgit.junit.JGitTestUtil; +import org.eclipse.jgit.junit.RepositoryTestCase; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.treewalk.FileTreeIterator; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.TreeWalk.OperationType; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests the attributes are correctly computed in a {@link TreeWalk}. + * + * @see TreeWalk#getAttributes() + */ +public class TreeWalkAttributeTest extends RepositoryTestCase { + + private static final FileMode M = FileMode.MISSING; + + private static final FileMode D = FileMode.TREE; + + private static final FileMode F = FileMode.REGULAR_FILE; + + private static Attribute EOL_CRLF = new Attribute("eol", "crlf"); + + private static Attribute EOL_LF = new Attribute("eol", "lf"); + + private static Attribute TEXT_SET = new Attribute("text", State.SET); + + private static Attribute TEXT_UNSET = new Attribute("text", State.UNSET); + + private static Attribute DELTA_UNSET = new Attribute("delta", State.UNSET); + + private static Attribute DELTA_SET = new Attribute("delta", State.SET); + + private static Attribute CUSTOM_GLOBAL = new Attribute("custom", "global"); + + private static Attribute CUSTOM_INFO = new Attribute("custom", "info"); + + private static Attribute CUSTOM_ROOT = new Attribute("custom", "root"); + + private static Attribute CUSTOM_PARENT = new Attribute("custom", "parent"); + + private static Attribute CUSTOM_CURRENT = new Attribute("custom", "current"); + + private static Attribute CUSTOM2_UNSET = new Attribute("custom2", + State.UNSET); + + private static Attribute CUSTOM2_SET = new Attribute("custom2", State.SET); + + private TreeWalk walk; + + private TreeWalk ci_walk; + + private Git git; + + private File customAttributeFile; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + git = new Git(db); + } + + @Override + @After + public void tearDown() throws Exception { + super.tearDown(); + if (customAttributeFile != null) + customAttributeFile.delete(); + } + + /** + * Checks that the attributes are computed correctly depending on the + * operation type. + *

+ * In this test we changed the content of the attribute files in the working + * tree compared to the one in the index. + *

+ * + * @throws IOException + * @throws NoFilepatternException + * @throws GitAPIException + */ + @Test + public void testCheckinCheckoutDifferences() throws IOException, + NoFilepatternException, GitAPIException { + + writeGlobalAttributeFile("globalAttributesFile", "*.txt -custom2"); + writeAttributesFile(".git/info/attributes", "*.txt eol=crlf"); + writeAttributesFile(".gitattributes", "*.txt custom=root"); + writeAttributesFile("level1/.gitattributes", "*.txt text"); + writeAttributesFile("level1/level2/.gitattributes", "*.txt -delta"); + + writeTrashFile("l0.txt", ""); + + writeTrashFile("level1/l1.txt", ""); + + writeTrashFile("level1/level2/l2.txt", ""); + + git.add().addFilepattern(".").call(); + + beginWalk(); + + // Modify all attributes + writeGlobalAttributeFile("globalAttributesFile", "*.txt custom2"); + writeAttributesFile(".git/info/attributes", "*.txt eol=lf"); + writeAttributesFile(".gitattributes", "*.txt custom=info"); + writeAttributesFile("level1/.gitattributes", "*.txt -text"); + writeAttributesFile("level1/level2/.gitattributes", "*.txt delta"); + + assertEntry(F, ".gitattributes"); + assertEntry(F, "l0.txt", asSet(EOL_LF, CUSTOM_INFO, CUSTOM2_SET), + asSet(EOL_LF, CUSTOM_ROOT, CUSTOM2_SET)); + + assertEntry(D, "level1"); + assertEntry(F, "level1/.gitattributes"); + assertEntry(F, "level1/l1.txt", + asSet(EOL_LF, CUSTOM_INFO, CUSTOM2_SET, TEXT_UNSET), + asSet(EOL_LF, CUSTOM_ROOT, CUSTOM2_SET, TEXT_SET)); + + assertEntry(D, "level1/level2"); + assertEntry(F, "level1/level2/.gitattributes"); + assertEntry(F, "level1/level2/l2.txt", + asSet(EOL_LF, CUSTOM_INFO, CUSTOM2_SET, TEXT_UNSET, DELTA_SET), + asSet(EOL_LF, CUSTOM_ROOT, CUSTOM2_SET, TEXT_SET, DELTA_UNSET)); + + endWalk(); + } + + /** + * Checks that the index is used as fallback when the git attributes file + * are missing in the working tree. + * + * @throws IOException + * @throws NoFilepatternException + * @throws GitAPIException + */ + @Test + public void testIndexOnly() throws IOException, NoFilepatternException, + GitAPIException { + List attrFiles = new ArrayList(); + attrFiles.add(writeGlobalAttributeFile("globalAttributesFile", + "*.txt -custom2")); + attrFiles.add(writeAttributesFile(".git/info/attributes", + "*.txt eol=crlf")); + attrFiles + .add(writeAttributesFile(".gitattributes", "*.txt custom=root")); + attrFiles + .add(writeAttributesFile("level1/.gitattributes", "*.txt text")); + attrFiles.add(writeAttributesFile("level1/level2/.gitattributes", + "*.txt -delta")); + + writeTrashFile("l0.txt", ""); + + writeTrashFile("level1/l1.txt", ""); + + writeTrashFile("level1/level2/l2.txt", ""); + + git.add().addFilepattern(".").call(); + + // Modify all attributes + for (File attrFile : attrFiles) + attrFile.delete(); + + beginWalk(); + + assertEntry(M, ".gitattributes"); + assertEntry(F, "l0.txt", asSet(CUSTOM_ROOT)); + + assertEntry(D, "level1"); + assertEntry(M, "level1/.gitattributes"); + assertEntry(F, "level1/l1.txt", + + asSet(CUSTOM_ROOT, TEXT_SET)); + + assertEntry(D, "level1/level2"); + assertEntry(M, "level1/level2/.gitattributes"); + assertEntry(F, "level1/level2/l2.txt", + + asSet(CUSTOM_ROOT, TEXT_SET, DELTA_UNSET)); + + endWalk(); + } + + /** + * Check that we search in the working tree for attributes although the file + * we are currently inspecting does not exist anymore in the working tree. + * + * @throws IOException + * @throws NoFilepatternException + * @throws GitAPIException + */ + @Test + public void testIndexOnly2() + throws IOException, NoFilepatternException, GitAPIException { + File l2 = writeTrashFile("level1/level2/l2.txt", ""); + writeTrashFile("level1/level2/l1.txt", ""); + + git.add().addFilepattern(".").call(); + + writeAttributesFile(".gitattributes", "*.txt custom=root"); + assertTrue(l2.delete()); + + beginWalk(); + + assertEntry(F, ".gitattributes"); + assertEntry(D, "level1"); + assertEntry(D, "level1/level2"); + assertEntry(F, "level1/level2/l1.txt", asSet(CUSTOM_ROOT)); + assertEntry(M, "level1/level2/l2.txt", asSet(CUSTOM_ROOT)); + + endWalk(); + } + + /** + * Basic test for git attributes. + *

+ * In this use case files are present in both the working tree and the index + *

+ * + * @throws IOException + * @throws NoFilepatternException + * @throws GitAPIException + */ + @Test + public void testRules() throws IOException, NoFilepatternException, + GitAPIException { + writeAttributesFile(".git/info/attributes", "windows* eol=crlf"); + + writeAttributesFile(".gitattributes", "*.txt eol=lf"); + writeTrashFile("windows.file", ""); + writeTrashFile("windows.txt", ""); + writeTrashFile("readme.txt", ""); + + writeAttributesFile("src/config/.gitattributes", "*.txt -delta"); + writeTrashFile("src/config/readme.txt", ""); + writeTrashFile("src/config/windows.file", ""); + writeTrashFile("src/config/windows.txt", ""); + + beginWalk(); + + git.add().addFilepattern(".").call(); + + assertEntry(F, ".gitattributes"); + assertEntry(F, "readme.txt", asSet(EOL_LF)); + + assertEntry(D, "src"); + assertEntry(D, "src/config"); + assertEntry(F, "src/config/.gitattributes"); + assertEntry(F, "src/config/readme.txt", asSet(DELTA_UNSET, EOL_LF)); + assertEntry(F, "src/config/windows.file", asSet(EOL_CRLF)); + assertEntry(F, "src/config/windows.txt", asSet(DELTA_UNSET, EOL_CRLF)); + + assertEntry(F, "windows.file", asSet(EOL_CRLF)); + assertEntry(F, "windows.txt", asSet(EOL_CRLF)); + + endWalk(); + } + + /** + * Checks that if there is no .gitattributes file in the repository + * everything still work fine. + * + * @throws IOException + */ + @Test + public void testNoAttributes() throws IOException { + writeTrashFile("l0.txt", ""); + writeTrashFile("level1/l1.txt", ""); + writeTrashFile("level1/level2/l2.txt", ""); + + beginWalk(); + + assertEntry(F, "l0.txt"); + + assertEntry(D, "level1"); + assertEntry(F, "level1/l1.txt"); + + assertEntry(D, "level1/level2"); + assertEntry(F, "level1/level2/l2.txt"); + + endWalk(); + } + + /** + * Checks that an empty .gitattribute file does not return incorrect value. + * + * @throws IOException + */ + @Test + public void testEmptyGitAttributeFile() throws IOException { + writeAttributesFile(".git/info/attributes", ""); + writeTrashFile("l0.txt", ""); + writeAttributesFile(".gitattributes", ""); + writeTrashFile("level1/l1.txt", ""); + writeTrashFile("level1/level2/l2.txt", ""); + + beginWalk(); + + assertEntry(F, ".gitattributes"); + assertEntry(F, "l0.txt"); + + assertEntry(D, "level1"); + assertEntry(F, "level1/l1.txt"); + + assertEntry(D, "level1/level2"); + assertEntry(F, "level1/level2/l2.txt"); + + endWalk(); + } + + @Test + public void testNoMatchingAttributes() throws IOException { + writeAttributesFile(".git/info/attributes", "*.java delta"); + writeAttributesFile(".gitattributes", "*.java -delta"); + writeAttributesFile("levelA/.gitattributes", "*.java eol=lf"); + writeAttributesFile("levelB/.gitattributes", "*.txt eol=lf"); + + writeTrashFile("levelA/lA.txt", ""); + + beginWalk(); + + assertEntry(F, ".gitattributes"); + + assertEntry(D, "levelA"); + assertEntry(F, "levelA/.gitattributes"); + assertEntry(F, "levelA/lA.txt"); + + assertEntry(D, "levelB"); + assertEntry(F, "levelB/.gitattributes"); + + endWalk(); + } + + /** + * Checks that $GIT_DIR/info/attributes file has the highest precedence. + * + * @throws IOException + */ + @Test + public void testPrecedenceInfo() throws IOException { + writeGlobalAttributeFile("globalAttributesFile", "*.txt custom=global"); + writeAttributesFile(".git/info/attributes", "*.txt custom=info"); + writeAttributesFile(".gitattributes", "*.txt custom=root"); + writeAttributesFile("level1/.gitattributes", "*.txt custom=parent"); + writeAttributesFile("level1/level2/.gitattributes", + "*.txt custom=current"); + + writeTrashFile("level1/level2/file.txt", ""); + + beginWalk(); + + assertEntry(F, ".gitattributes"); + + assertEntry(D, "level1"); + assertEntry(F, "level1/.gitattributes"); + + assertEntry(D, "level1/level2"); + assertEntry(F, "level1/level2/.gitattributes"); + assertEntry(F, "level1/level2/file.txt", asSet(CUSTOM_INFO)); + + endWalk(); + } + + /** + * Checks that a subfolder ".gitattributes" file has precedence over its + * parent. + * + * @throws IOException + */ + @Test + public void testPrecedenceCurrent() throws IOException { + writeGlobalAttributeFile("globalAttributesFile", "*.txt custom=global"); + writeAttributesFile(".gitattributes", "*.txt custom=root"); + writeAttributesFile("level1/.gitattributes", "*.txt custom=parent"); + writeAttributesFile("level1/level2/.gitattributes", + "*.txt custom=current"); + + writeTrashFile("level1/level2/file.txt", ""); + + beginWalk(); + + assertEntry(F, ".gitattributes"); + + assertEntry(D, "level1"); + assertEntry(F, "level1/.gitattributes"); + + assertEntry(D, "level1/level2"); + assertEntry(F, "level1/level2/.gitattributes"); + assertEntry(F, "level1/level2/file.txt", asSet(CUSTOM_CURRENT)); + + endWalk(); + } + + /** + * Checks that the parent ".gitattributes" file is used as fallback. + * + * @throws IOException + */ + @Test + public void testPrecedenceParent() throws IOException { + writeGlobalAttributeFile("globalAttributesFile", "*.txt custom=global"); + writeAttributesFile(".gitattributes", "*.txt custom=root"); + writeAttributesFile("level1/.gitattributes", "*.txt custom=parent"); + + writeTrashFile("level1/level2/file.txt", ""); + + beginWalk(); + + assertEntry(F, ".gitattributes"); + + assertEntry(D, "level1"); + assertEntry(F, "level1/.gitattributes"); + + assertEntry(D, "level1/level2"); + assertEntry(F, "level1/level2/file.txt", asSet(CUSTOM_PARENT)); + + endWalk(); + } + + /** + * Checks that the grand parent ".gitattributes" file is used as fallback. + * + * @throws IOException + */ + @Test + public void testPrecedenceRoot() throws IOException { + writeGlobalAttributeFile("globalAttributesFile", "*.txt custom=global"); + writeAttributesFile(".gitattributes", "*.txt custom=root"); + + writeTrashFile("level1/level2/file.txt", ""); + + beginWalk(); + + assertEntry(F, ".gitattributes"); + + assertEntry(D, "level1"); + + assertEntry(D, "level1/level2"); + assertEntry(F, "level1/level2/file.txt", asSet(CUSTOM_ROOT)); + + endWalk(); + } + + /** + * Checks that the global attribute file is used as fallback. + * + * @throws IOException + */ + @Test + public void testPrecedenceGlobal() throws IOException { + writeGlobalAttributeFile("globalAttributesFile", "*.txt custom=global"); + + writeTrashFile("level1/level2/file.txt", ""); + + beginWalk(); + + assertEntry(D, "level1"); + + assertEntry(D, "level1/level2"); + assertEntry(F, "level1/level2/file.txt", asSet(CUSTOM_GLOBAL)); + + endWalk(); + } + + /** + * Checks the precedence on a hierarchy with multiple attributes. + *

+ * In this test all file are present in both the working tree and the index. + *

+ * + * @throws IOException + * @throws GitAPIException + * @throws NoFilepatternException + */ + @Test + public void testHierarchyBothIterator() throws IOException, + NoFilepatternException, GitAPIException { + writeAttributesFile(".git/info/attributes", "*.global eol=crlf"); + writeAttributesFile(".gitattributes", "*.local eol=lf"); + writeAttributesFile("level1/.gitattributes", "*.local text"); + writeAttributesFile("level1/level2/.gitattributes", "*.local -text"); + + writeTrashFile("l0.global", ""); + writeTrashFile("l0.local", ""); + + writeTrashFile("level1/l1.global", ""); + writeTrashFile("level1/l1.local", ""); + + writeTrashFile("level1/level2/l2.global", ""); + writeTrashFile("level1/level2/l2.local", ""); + + beginWalk(); + + git.add().addFilepattern(".").call(); + + assertEntry(F, ".gitattributes"); + assertEntry(F, "l0.global", asSet(EOL_CRLF)); + assertEntry(F, "l0.local", asSet(EOL_LF)); + + assertEntry(D, "level1"); + assertEntry(F, "level1/.gitattributes"); + assertEntry(F, "level1/l1.global", asSet(EOL_CRLF)); + assertEntry(F, "level1/l1.local", asSet(EOL_LF, TEXT_SET)); + + assertEntry(D, "level1/level2"); + assertEntry(F, "level1/level2/.gitattributes"); + assertEntry(F, "level1/level2/l2.global", asSet(EOL_CRLF)); + assertEntry(F, "level1/level2/l2.local", asSet(EOL_LF, TEXT_UNSET)); + + endWalk(); + + } + + /** + * Checks the precedence on a hierarchy with multiple attributes. + *

+ * In this test all file are present only in the working tree. + *

+ * + * @throws IOException + * @throws GitAPIException + * @throws NoFilepatternException + */ + @Test + public void testHierarchyWorktreeOnly() + throws IOException, NoFilepatternException, GitAPIException { + writeAttributesFile(".git/info/attributes", "*.global eol=crlf"); + writeAttributesFile(".gitattributes", "*.local eol=lf"); + writeAttributesFile("level1/.gitattributes", "*.local text"); + writeAttributesFile("level1/level2/.gitattributes", "*.local -text"); + + writeTrashFile("l0.global", ""); + writeTrashFile("l0.local", ""); + + writeTrashFile("level1/l1.global", ""); + writeTrashFile("level1/l1.local", ""); + + writeTrashFile("level1/level2/l2.global", ""); + writeTrashFile("level1/level2/l2.local", ""); + + beginWalk(); + + assertEntry(F, ".gitattributes"); + assertEntry(F, "l0.global", asSet(EOL_CRLF)); + assertEntry(F, "l0.local", asSet(EOL_LF)); + + assertEntry(D, "level1"); + assertEntry(F, "level1/.gitattributes"); + assertEntry(F, "level1/l1.global", asSet(EOL_CRLF)); + assertEntry(F, "level1/l1.local", asSet(EOL_LF, TEXT_SET)); + + assertEntry(D, "level1/level2"); + assertEntry(F, "level1/level2/.gitattributes"); + assertEntry(F, "level1/level2/l2.global", asSet(EOL_CRLF)); + assertEntry(F, "level1/level2/l2.local", asSet(EOL_LF, TEXT_UNSET)); + + endWalk(); + + } + + /** + * Checks that the list of attributes is an aggregation of all the + * attributes from the attributes files hierarchy. + * + * @throws IOException + */ + @Test + public void testAggregation() throws IOException { + writeGlobalAttributeFile("globalAttributesFile", "*.txt -custom2"); + writeAttributesFile(".git/info/attributes", "*.txt eol=crlf"); + writeAttributesFile(".gitattributes", "*.txt custom=root"); + writeAttributesFile("level1/.gitattributes", "*.txt text"); + writeAttributesFile("level1/level2/.gitattributes", "*.txt -delta"); + + writeTrashFile("l0.txt", ""); + + writeTrashFile("level1/l1.txt", ""); + + writeTrashFile("level1/level2/l2.txt", ""); + + beginWalk(); + + assertEntry(F, ".gitattributes"); + assertEntry(F, "l0.txt", asSet(EOL_CRLF, CUSTOM_ROOT, CUSTOM2_UNSET)); + + assertEntry(D, "level1"); + assertEntry(F, "level1/.gitattributes"); + assertEntry(F, "level1/l1.txt", + asSet(EOL_CRLF, CUSTOM_ROOT, TEXT_SET, CUSTOM2_UNSET)); + + assertEntry(D, "level1/level2"); + assertEntry(F, "level1/level2/.gitattributes"); + assertEntry( + F, + "level1/level2/l2.txt", + asSet(EOL_CRLF, CUSTOM_ROOT, TEXT_SET, DELTA_UNSET, + CUSTOM2_UNSET)); + + endWalk(); + + } + + /** + * Checks that the last entry in .gitattributes is used if 2 lines match the + * same attribute + * + * @throws IOException + */ + @Test + public void testOverriding() throws IOException { + writeAttributesFile(".git/info/attributes",// + // + "*.txt custom=current",// + "*.txt custom=parent",// + "*.txt custom=root",// + "*.txt custom=info", + // + "*.txt delta",// + "*.txt -delta", + // + "*.txt eol=lf",// + "*.txt eol=crlf", + // + "*.txt text",// + "*.txt -text"); + + writeTrashFile("l0.txt", ""); + beginWalk(); + + assertEntry(F, "l0.txt", + asSet(TEXT_UNSET, EOL_CRLF, DELTA_UNSET, CUSTOM_INFO)); + + endWalk(); + } + + /** + * Checks that the last value of an attribute is used if in the same line an + * attribute is defined several time. + * + * @throws IOException + */ + @Test + public void testOverriding2() throws IOException { + writeAttributesFile(".git/info/attributes", + "*.txt custom=current custom=parent custom=root custom=info",// + "*.txt delta -delta",// + "*.txt eol=lf eol=crlf",// + "*.txt text -text"); + writeTrashFile("l0.txt", ""); + beginWalk(); + + assertEntry(F, "l0.txt", + asSet(TEXT_UNSET, EOL_CRLF, DELTA_UNSET, CUSTOM_INFO)); + + endWalk(); + } + + @Test + public void testRulesInherited() throws Exception { + writeAttributesFile(".gitattributes", "**/*.txt eol=lf"); + writeTrashFile("src/config/readme.txt", ""); + writeTrashFile("src/config/windows.file", ""); + + beginWalk(); + + assertEntry(F, ".gitattributes"); + assertEntry(D, "src"); + assertEntry(D, "src/config"); + + assertEntry(F, "src/config/readme.txt", asSet(EOL_LF)); + assertEntry(F, "src/config/windows.file", + Collections. emptySet()); + + endWalk(); + } + + private void beginWalk() throws NoWorkTreeException, IOException { + walk = new TreeWalk(db); + walk.addTree(new FileTreeIterator(db)); + walk.addTree(new DirCacheIterator(db.readDirCache())); + + ci_walk = new TreeWalk(db); + ci_walk.setOperationType(OperationType.CHECKIN_OP); + ci_walk.addTree(new FileTreeIterator(db)); + ci_walk.addTree(new DirCacheIterator(db.readDirCache())); + } + + /** + * Assert an entry in which checkin and checkout attributes are expected to + * be the same. + * + * @param type + * @param pathName + * @param forBothOperaiton + * @throws IOException + */ + private void assertEntry(FileMode type, String pathName, + Set forBothOperaiton) throws IOException { + assertEntry(type, pathName, forBothOperaiton, forBothOperaiton); + } + + /** + * Assert an entry with no attribute expected. + * + * @param type + * @param pathName + * @throws IOException + */ + private void assertEntry(FileMode type, String pathName) throws IOException { + assertEntry(type, pathName, Collections. emptySet(), + Collections. emptySet()); + } + + /** + * Assert that an entry; + *
    + *
  • Has the correct type
  • + *
  • Exist in the tree walk
  • + *
  • Has the expected attributes on a checkin operation
  • + *
  • Has the expected attributes on a checkout operation
  • + *
+ * + * @param type + * @param pathName + * @param checkinAttributes + * @param checkoutAttributes + * @throws IOException + */ + private void assertEntry(FileMode type, String pathName, + Set checkinAttributes, Set checkoutAttributes) + throws IOException { + assertTrue("walk has entry", walk.next()); + assertTrue("walk has entry", ci_walk.next()); + assertEquals(pathName, walk.getPathString()); + assertEquals(type, walk.getFileMode(0)); + + assertEquals(checkinAttributes, + asSet(ci_walk.getAttributes().values())); + assertEquals(checkoutAttributes, asSet(walk.getAttributes().values())); + + if (D.equals(type)) { + walk.enterSubtree(); + ci_walk.enterSubtree(); + } + } + + private static Set asSet(Collection attributes) { + Set ret = new HashSet(); + for (Attribute a : attributes) { + ret.add(a); + } + return (ret); + } + + private File writeAttributesFile(String name, String... rules) + throws IOException { + StringBuilder data = new StringBuilder(); + for (String line : rules) + data.append(line + "\n"); + return writeTrashFile(name, data.toString()); + } + + /** + * Creates an attributes file and set its location in the git configuration. + * + * @param fileName + * @param attributes + * @return The attribute file + * @throws IOException + * @see Repository#getConfig() + */ + private File writeGlobalAttributeFile(String fileName, String... attributes) + throws IOException { + customAttributeFile = File.createTempFile("tmp_", fileName, null); + customAttributeFile.deleteOnExit(); + StringBuilder attributesFileContent = new StringBuilder(); + for (String attr : attributes) { + attributesFileContent.append(attr).append("\n"); + } + JGitTestUtil.write(customAttributeFile, + attributesFileContent.toString()); + db.getConfig().setString("core", null, "attributesfile", + customAttributeFile.getAbsolutePath()); + return customAttributeFile; + } + + static Set asSet(Attribute... attrs) { + HashSet result = new HashSet(); + for (Attribute attr : attrs) + result.add(attr); + return result; + } + + private void endWalk() throws IOException { + assertFalse("Not all files tested", walk.next()); + assertFalse("Not all files tested", ci_walk.next()); + } +} diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters index ed330b151..c7a96a2f2 100644 --- a/org.eclipse.jgit/.settings/.api_filters +++ b/org.eclipse.jgit/.settings/.api_filters @@ -14,6 +14,20 @@ + + + + + + + + + + + + + + diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java index de6c32a80..ae297a643 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java @@ -63,6 +63,7 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.treewalk.FileTreeIterator; import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.TreeWalk.OperationType; import org.eclipse.jgit.treewalk.WorkingTreeIterator; import org.eclipse.jgit.treewalk.filter.PathFilterGroup; @@ -139,6 +140,7 @@ public DirCache call() throws GitAPIException, NoFilepatternException { try (ObjectInserter inserter = repo.newObjectInserter(); final TreeWalk tw = new TreeWalk(repo)) { + tw.setOperationType(OperationType.CHECKIN_OP); dc = repo.lockDirCache(); DirCacheIterator c; @@ -146,6 +148,7 @@ public DirCache call() throws GitAPIException, NoFilepatternException { tw.addTree(new DirCacheBuildIterator(builder)); if (workingTreeIterator == null) workingTreeIterator = new FileTreeIterator(repo); + workingTreeIterator.setDirCacheIterator(tw, 0); tw.addTree(workingTreeIterator); tw.setRecursive(true); if (!addAll) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java index 6174d48d3..53e18df47 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java @@ -86,6 +86,7 @@ import org.eclipse.jgit.treewalk.CanonicalTreeParser; import org.eclipse.jgit.treewalk.FileTreeIterator; import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.TreeWalk.OperationType; import org.eclipse.jgit.util.ChangeIdUtil; /** @@ -328,6 +329,7 @@ private DirCache createTemporaryIndex(ObjectId headId, DirCache index, boolean emptyCommit = true; try (TreeWalk treeWalk = new TreeWalk(repo)) { + treeWalk.setOperationType(OperationType.CHECKIN_OP); int dcIdx = treeWalk .addTree(new DirCacheBuildIterator(existingBuilder)); int fIdx = treeWalk.addTree(new FileTreeIterator(repo)); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java new file mode 100644 index 000000000..6f2ebad67 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2014, Arthur Daussy + * 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.attributes; + +import java.io.IOException; + +import org.eclipse.jgit.lib.CoreConfig; + +/** + * An interface used to retrieve the global and info {@link AttributesNode}s. + * + * @since 4.2 + * + */ +public interface AttributesNodeProvider { + + /** + * Retrieve the {@link AttributesNode} that holds the information located + * in $GIT_DIR/info/attributes file. + * + * @return the {@link AttributesNode} that holds the information located in + * $GIT_DIR/info/attributes file. + * @throws IOException + * if an error is raised while parsing the attributes file + */ + public AttributesNode getInfoAttributesNode() throws IOException; + + /** + * Retrieve the {@link AttributesNode} that holds the information located + * in the global gitattributes file. + * + * @return the {@link AttributesNode} that holds the information located in + * the global gitattributes file. + * @throws IOException + * IOException if an error is raised while parsing the + * attributes file + * @see CoreConfig#getAttributesFile() + */ + public AttributesNode getGlobalAttributesNode() throws IOException; + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java new file mode 100644 index 000000000..8f23a83f7 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015, Christian Halstrick + * 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.attributes; + +import java.util.Map; + +/** + * Interface for classes which provide git attributes + * + * @since 4.2 + */ +public interface AttributesProvider { + /** + * @return the currently active attributes by attribute key + */ + public Map getAttributes(); +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java index 6d9a32db9..92cdf391c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java @@ -76,6 +76,7 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.treewalk.FileTreeIterator; import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.TreeWalk.OperationType; import org.eclipse.jgit.treewalk.filter.PathFilterGroup; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.IO; @@ -963,6 +964,7 @@ private void registerIndexChangedListener(IndexChangedListener listener) { private void updateSmudgedEntries() throws IOException { List paths = new ArrayList(128); try (TreeWalk walk = new TreeWalk(repository)) { + walk.setOperationType(OperationType.CHECKIN_OP); for (int i = 0; i < entryCnt; i++) if (sortedEntries[i].isSmudged()) paths.add(sortedEntries[i].getPathString()); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java index 122f6d3d1..0d5fd0f85 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java @@ -44,8 +44,13 @@ package org.eclipse.jgit.internal.storage.dfs; import java.io.IOException; +import java.io.InputStream; import java.text.MessageFormat; +import java.util.Collections; +import org.eclipse.jgit.attributes.AttributesNode; +import org.eclipse.jgit.attributes.AttributesNodeProvider; +import org.eclipse.jgit.attributes.AttributesRule; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.RefUpdate; @@ -126,4 +131,36 @@ public void notifyIndexChanged() { public ReflogReader getReflogReader(String refName) throws IOException { throw new UnsupportedOperationException(); } + + @Override + public AttributesNodeProvider createAttributesNodeProvider() { + // TODO Check if the implementation used in FileRepository can be used + // for this kind of repository + return new EmptyAttributesNodeProvider(); + } + + private static class EmptyAttributesNodeProvider implements + AttributesNodeProvider { + private EmptyAttributesNode emptyAttributesNode = new EmptyAttributesNode(); + + public AttributesNode getInfoAttributesNode() throws IOException { + return emptyAttributesNode; + } + + public AttributesNode getGlobalAttributesNode() throws IOException { + return emptyAttributesNode; + } + + private static class EmptyAttributesNode extends AttributesNode { + + public EmptyAttributesNode() { + super(Collections. emptyList()); + } + + @Override + public void parse(InputStream in) throws IOException { + // Do nothing + } + } + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java index 995621ee3..490cbcaa8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java @@ -49,11 +49,15 @@ import static org.eclipse.jgit.lib.RefDatabase.ALL; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.text.MessageFormat; import java.util.HashSet; import java.util.Set; +import org.eclipse.jgit.attributes.AttributesNode; +import org.eclipse.jgit.attributes.AttributesNodeProvider; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.events.ConfigChangedEvent; import org.eclipse.jgit.events.ConfigChangedListener; @@ -479,4 +483,63 @@ public ReflogReader getReflogReader(String refName) throws IOException { return new ReflogReaderImpl(this, ref.getName()); return null; } + + @Override + public AttributesNodeProvider createAttributesNodeProvider() { + return new AttributesNodeProviderImpl(this); + } + + /** + * Implementation a {@link AttributesNodeProvider} for a + * {@link FileRepository}. + * + * @author Arthur Daussy + * + */ + static class AttributesNodeProviderImpl implements + AttributesNodeProvider { + + private AttributesNode infoAttributesNode; + + private AttributesNode globalAttributesNode; + + /** + * Constructor. + * + * @param repo + * {@link Repository} that will provide the attribute nodes. + */ + protected AttributesNodeProviderImpl(Repository repo) { + infoAttributesNode = new InfoAttributesNode(repo); + globalAttributesNode = new GlobalAttributesNode(repo); + } + + public AttributesNode getInfoAttributesNode() throws IOException { + if (infoAttributesNode instanceof InfoAttributesNode) + infoAttributesNode = ((InfoAttributesNode) infoAttributesNode) + .load(); + return infoAttributesNode; + } + + public AttributesNode getGlobalAttributesNode() throws IOException { + if (globalAttributesNode instanceof GlobalAttributesNode) + globalAttributesNode = ((GlobalAttributesNode) globalAttributesNode) + .load(); + return globalAttributesNode; + } + + static void loadRulesFromFile(AttributesNode r, File attrs) + throws FileNotFoundException, IOException { + if (attrs.exists()) { + FileInputStream in = new FileInputStream(attrs); + try { + r.parse(in); + } finally { + in.close(); + } + } + } + + } + } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GlobalAttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GlobalAttributesNode.java new file mode 100644 index 000000000..454d3bff6 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GlobalAttributesNode.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2014, Arthur Daussy + * Copyright (C) 2015, Christian Halstrick + * 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.internal.storage.file; + +import java.io.File; +import java.io.IOException; + +import org.eclipse.jgit.attributes.AttributesNode; +import org.eclipse.jgit.lib.CoreConfig; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.util.FS; + +/** Attribute node loaded from global system-wide file. */ +public class GlobalAttributesNode extends AttributesNode { + final Repository repository; + + /** + * @param repository + */ + public GlobalAttributesNode(Repository repository) { + this.repository = repository; + } + + /** + * @return the attributes node + * @throws IOException + */ + public AttributesNode load() throws IOException { + AttributesNode r = new AttributesNode(); + + FS fs = repository.getFS(); + String path = repository.getConfig().get(CoreConfig.KEY) + .getAttributesFile(); + if (path != null) { + File attributesFile; + if (path.startsWith("~/")) { //$NON-NLS-1$ + attributesFile = fs.resolve(fs.userHome(), + path.substring(2)); + } else { + attributesFile = fs.resolve(null, path); + } + FileRepository.AttributesNodeProviderImpl.loadRulesFromFile(r, attributesFile); + } + return r.getRules().isEmpty() ? null : r; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java new file mode 100644 index 000000000..eb53434b7 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2014, Arthur Daussy + * Copyright (C) 2015, Christian Halstrick + * 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.internal.storage.file; + +import java.io.File; +import java.io.IOException; + +import org.eclipse.jgit.attributes.AttributesNode; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.util.FS; + +/** Attribute node loaded from the $GIT_DIR/info/attributes file. */ +public class InfoAttributesNode extends AttributesNode { + final Repository repository; + + /** + * @param repository + */ + public InfoAttributesNode(Repository repository) { + this.repository = repository; + } + + /** + * @return the attributes node + * @throws IOException + */ + public AttributesNode load() throws IOException { + AttributesNode r = new AttributesNode(); + + FS fs = repository.getFS(); + + File attributes = fs.resolve(repository.getDirectory(), + "info/attributes"); //$NON-NLS-1$ + FileRepository.AttributesNodeProviderImpl.loadRulesFromFile(r, attributes); + + return r.getRules().isEmpty() ? null : r; + } + +} \ No newline at end of file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java index 3fde2f919..281cde875 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java @@ -74,6 +74,7 @@ import org.eclipse.jgit.treewalk.FileTreeIterator; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.WorkingTreeIterator; +import org.eclipse.jgit.treewalk.TreeWalk.OperationType; import org.eclipse.jgit.treewalk.filter.AndTreeFilter; import org.eclipse.jgit.treewalk.filter.IndexDiffFilter; import org.eclipse.jgit.treewalk.filter.SkipWorkTreeFilter; @@ -403,6 +404,7 @@ public boolean diff(final ProgressMonitor monitor, int estWorkTreeSize, dirCache = repository.readDirCache(); try (TreeWalk treeWalk = new TreeWalk(repository)) { + treeWalk.setOperationType(OperationType.CHECKIN_OP); treeWalk.setRecursive(true); // add the trees (tree, dirchache, workdir) if (tree != null) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java index d4c72cb9c..eda02dea4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -64,6 +64,7 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import org.eclipse.jgit.attributes.AttributesNodeProvider; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.errors.AmbiguousObjectException; import org.eclipse.jgit.errors.CorruptObjectException; @@ -209,6 +210,16 @@ public ObjectReader newObjectReader() { */ public abstract StoredConfig getConfig(); + /** + * @return a new {@link AttributesNodeProvider}. This + * {@link AttributesNodeProvider} is lazy loaded only once. It means + * that it will not be updated after loading. Prefer creating new + * instance for each use. + * @since 4.2 + */ + public abstract AttributesNodeProvider createAttributesNodeProvider(); + + /** * @return the used file system abstraction */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java index 2d6acbddf..350f56396 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java @@ -96,7 +96,7 @@ public class NameConflictTreeWalk extends TreeWalk { * the repository the walker will obtain data from. */ public NameConflictTreeWalk(final Repository repo) { - this(repo.newObjectReader()); + super(repo); } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java index 06e828419..7f7a5c393 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java @@ -45,7 +45,17 @@ package org.eclipse.jgit.treewalk; import java.io.IOException; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import org.eclipse.jgit.api.errors.JGitInternalException; +import org.eclipse.jgit.attributes.Attribute; +import org.eclipse.jgit.attributes.AttributesNode; +import org.eclipse.jgit.attributes.AttributesNodeProvider; +import org.eclipse.jgit.attributes.AttributesProvider; +import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; @@ -82,9 +92,38 @@ * Multiple simultaneous TreeWalk instances per {@link Repository} are * permitted, even from concurrent threads. */ -public class TreeWalk implements AutoCloseable { +public class TreeWalk implements AutoCloseable, AttributesProvider { private static final AbstractTreeIterator[] NO_TREES = {}; + /** + * @since 4.2 + */ + public static enum OperationType { + /** + * Represents a checkout operation (for example a checkout or reset + * operation). + */ + CHECKOUT_OP, + + /** + * Represents a checkin operation (for example an add operation) + */ + CHECKIN_OP + } + + /** + * Type of operation you want to retrieve the git attributes for. + */ + private OperationType operationType = OperationType.CHECKOUT_OP; + + /** + * @param operationType + * @since 4.2 + */ + public void setOperationType(OperationType operationType) { + this.operationType = operationType; + } + /** * Open a tree walk and filter to exactly one path. *

@@ -213,8 +252,13 @@ public static TreeWalk forPath(final Repository db, final String path, private boolean postChildren; + private AttributesNodeProvider attributesNodeProvider; + AbstractTreeIterator currentHead; + /** Cached attribute for the current entry */ + private Map attrs = null; + /** * Create a new tree walker for a given repository. * @@ -225,6 +269,7 @@ public static TreeWalk forPath(final Repository db, final String path, */ public TreeWalk(final Repository repo) { this(repo.newObjectReader(), true); + attributesNodeProvider = repo.createAttributesNodeProvider(); } /** @@ -356,8 +401,29 @@ public void setPostOrderTraversal(final boolean b) { postOrderTraversal = b; } + /** + * Sets the {@link AttributesNodeProvider} for this {@link TreeWalk}. + *

+ * This is a requirement for a correct computation of the git attributes. + * If this {@link TreeWalk} has been built using + * {@link #TreeWalk(Repository)} constructor, the + * {@link AttributesNodeProvider} has already been set. Indeed,the + * {@link Repository} can provide an {@link AttributesNodeProvider} using + * {@link Repository#createAttributesNodeProvider()} method. Otherwise you + * should provide one. + *

+ * + * @see Repository#createAttributesNodeProvider() + * @param provider + * @since 4.2 + */ + public void setAttributesNodeProvider(AttributesNodeProvider provider) { + attributesNodeProvider = provider; + } + /** Reset this walker so new tree iterators can be added to it. */ public void reset() { + attrs = null; trees = NO_TREES; advance = false; depth = 0; @@ -401,6 +467,7 @@ public void reset(final AnyObjectId id) throws MissingObjectException, advance = false; depth = 0; + attrs = null; } /** @@ -450,6 +517,7 @@ public void reset(final AnyObjectId... ids) throws MissingObjectException, trees = r; advance = false; depth = 0; + attrs = null; } /** @@ -546,6 +614,7 @@ public int getTreeCount() { public boolean next() throws MissingObjectException, IncorrectObjectTypeException, CorruptObjectException, IOException { try { + attrs = null; if (advance) { advance = false; postChildren = false; @@ -915,6 +984,7 @@ public boolean isPostChildren() { */ public void enterSubtree() throws MissingObjectException, IncorrectObjectTypeException, CorruptObjectException, IOException { + attrs = null; final AbstractTreeIterator ch = currentHead; final AbstractTreeIterator[] tmp = new AbstractTreeIterator[trees.length]; for (int i = 0; i < trees.length; i++) { @@ -1008,4 +1078,200 @@ static String pathOf(final AbstractTreeIterator t) { static String pathOf(final byte[] buf, int pos, int end) { return RawParseUtils.decode(Constants.CHARSET, buf, pos, end); } + + /** + * Retrieve the git attributes for the current entry. + * + *

Git attribute computation

+ * + *
    + *
  • Get the attributes matching the current path entry from the info file + * (see {@link AttributesNodeProvider#getInfoAttributesNode()}).
  • + *
  • Completes the list of attributes using the .gitattributes files + * located on the current path (the further the directory that contains + * .gitattributes is from the path in question, the lower its precedence). + * For a checkin operation, it will look first on the working tree (if any). + * If there is no attributes file, it will fallback on the index. For a + * checkout operation, it will first use the index entry and then fallback + * on the working tree if none.
  • + *
  • In the end, completes the list of matching attributes using the + * global attribute file define in the configuration (see + * {@link AttributesNodeProvider#getGlobalAttributesNode()})
  • + * + *
+ * + * + *

Iterator constraints

+ * + *

+ * In order to have a correct list of attributes for the current entry, this + * {@link TreeWalk} requires to have at least one + * {@link AttributesNodeProvider} and a {@link DirCacheIterator} set up. An + * {@link AttributesNodeProvider} is used to retrieve the attributes from + * the info attributes file and the global attributes file. The + * {@link DirCacheIterator} is used to retrieve the .gitattributes files + * stored in the index. A {@link WorkingTreeIterator} can also be provided + * to access the local version of the .gitattributes files. If none is + * provided it will fallback on the {@link DirCacheIterator}. + *

+ * + * @return a {@link Set} of {@link Attribute}s that match the current entry. + * @since 4.2 + */ + public Map getAttributes() { + if (attrs != null) + return attrs; + + if (attributesNodeProvider == null) { + // The work tree should have a AttributesNodeProvider to be able to + // retrieve the info and global attributes node + throw new IllegalStateException( + "The tree walk should have one AttributesNodeProvider set in order to compute the git attributes."); //$NON-NLS-1$ + } + + WorkingTreeIterator workingTreeIterator = getTree(WorkingTreeIterator.class); + DirCacheIterator dirCacheIterator = getTree(DirCacheIterator.class); + + if (workingTreeIterator == null && dirCacheIterator == null) { + // Can not retrieve the attributes without at least one of the above + // iterators. + return Collections. emptyMap(); + } + + String path = currentHead.getEntryPathString(); + final boolean isDir = FileMode.TREE.equals(currentHead.mode); + Map attributes = new LinkedHashMap(); + try { + // Gets the info attributes + AttributesNode infoNodeAttr = attributesNodeProvider + .getInfoAttributesNode(); + if (infoNodeAttr != null) { + infoNodeAttr.getAttributes(path, isDir, attributes); + } + + + // Gets the attributes located on the current entry path + getPerDirectoryEntryAttributes(path, isDir, operationType, + workingTreeIterator, dirCacheIterator, + attributes); + + // Gets the attributes located in the global attribute file + AttributesNode globalNodeAttr = attributesNodeProvider + .getGlobalAttributesNode(); + if (globalNodeAttr != null) { + globalNodeAttr.getAttributes(path, isDir, attributes); + } + } catch (IOException e) { + throw new JGitInternalException("Error while parsing attributes", e); //$NON-NLS-1$ + } + return attributes; + } + + /** + * Get the attributes located on the current entry path. + * + * @param path + * current entry path + * @param isDir + * holds true if the current entry is a directory + * @param opType + * type of operation + * @param workingTreeIterator + * a {@link WorkingTreeIterator} matching the current entry + * @param dirCacheIterator + * a {@link DirCacheIterator} matching the current entry + * @param attributes + * Non null map holding the existing attributes. This map will be + * augmented with new entry. None entry will be overrided. + * @throws IOException + * It raises an {@link IOException} if a problem appears while + * parsing one on the attributes file. + */ + private void getPerDirectoryEntryAttributes(String path, boolean isDir, + OperationType opType, WorkingTreeIterator workingTreeIterator, + DirCacheIterator dirCacheIterator, Map attributes) + throws IOException { + // Prevents infinite recurrence + if (workingTreeIterator != null || dirCacheIterator != null) { + AttributesNode currentAttributesNode = getCurrentAttributesNode( + opType, workingTreeIterator, dirCacheIterator); + if (currentAttributesNode != null) { + currentAttributesNode.getAttributes(path, isDir, attributes); + } + getPerDirectoryEntryAttributes(path, isDir, opType, + getParent(workingTreeIterator, WorkingTreeIterator.class), + getParent(dirCacheIterator, DirCacheIterator.class), + attributes); + } + } + + private T getParent(T current, + Class type) { + if (current != null) { + AbstractTreeIterator parent = current.parent; + if (type.isInstance(parent)) { + return type.cast(parent); + } + } + return null; + } + + private T getTree(Class type) { + for (int i = 0; i < trees.length; i++) { + AbstractTreeIterator tree = trees[i]; + if (type.isInstance(tree)) { + return type.cast(tree); + } + } + return null; + } + + /** + * Get the {@link AttributesNode} for the current entry. + *

+ * This method implements the fallback mechanism between the index and the + * working tree depending on the operation type + *

+ * + * @param opType + * @param workingTreeIterator + * @param dirCacheIterator + * @return a {@link AttributesNode} of the current entry, + * {@link NullPointerException} otherwise. + * @throws IOException + * It raises an {@link IOException} if a problem appears while + * parsing one on the attributes file. + */ + private AttributesNode getCurrentAttributesNode(OperationType opType, + WorkingTreeIterator workingTreeIterator, + DirCacheIterator dirCacheIterator) throws IOException { + AttributesNode attributesNode = null; + switch (opType) { + case CHECKIN_OP: + if (workingTreeIterator != null) { + attributesNode = workingTreeIterator.getEntryAttributesNode(); + } + if (attributesNode == null && dirCacheIterator != null) { + attributesNode = dirCacheIterator + .getEntryAttributesNode(getObjectReader()); + } + break; + case CHECKOUT_OP: + if (dirCacheIterator != null) { + attributesNode = dirCacheIterator + .getEntryAttributesNode(getObjectReader()); + } + if (attributesNode == null && workingTreeIterator != null) { + attributesNode = workingTreeIterator.getEntryAttributesNode(); + } + break; + default: + throw new IllegalStateException( + "The only supported operation types are:" //$NON-NLS-1$ + + OperationType.CHECKIN_OP + "," //$NON-NLS-1$ + + OperationType.CHECKOUT_OP); + } + + return attributesNode; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java index 73ab04f9c..e36ce0faf 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java @@ -74,6 +74,8 @@ import org.eclipse.jgit.ignore.FastIgnoreRule; import org.eclipse.jgit.ignore.IgnoreNode; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.internal.storage.file.GlobalAttributesNode; +import org.eclipse.jgit.internal.storage.file.InfoAttributesNode; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.CoreConfig; import org.eclipse.jgit.lib.CoreConfig.CheckStat; @@ -150,14 +152,14 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { * Holds the {@link AttributesNode} that is stored in * $GIT_DIR/info/attributes file. */ - private AttributesNode infoAttributeNode; + private AttributesNode infoAttributesNode; /** * Holds the {@link AttributesNode} that is stored in global attribute file. * * @see CoreConfig#getAttributesFile() */ - private AttributesNode globalAttributeNode; + private AttributesNode globalAttributesNode; /** * Create a new iterator with no parent. @@ -202,8 +204,8 @@ protected WorkingTreeIterator(final String prefix, protected WorkingTreeIterator(final WorkingTreeIterator p) { super(p); state = p.state; - infoAttributeNode = p.infoAttributeNode; - globalAttributeNode = p.globalAttributeNode; + infoAttributesNode = p.infoAttributesNode; + globalAttributesNode = p.globalAttributesNode; } /** @@ -224,9 +226,9 @@ protected void initRootIterator(Repository repo) { entry = null; ignoreNode = new RootIgnoreNode(entry, repo); - infoAttributeNode = new InfoAttributesNode(repo); + infoAttributesNode = new InfoAttributesNode(repo); - globalAttributeNode = new GlobalAttributesNode(repo); + globalAttributesNode = new GlobalAttributesNode(repo); } /** @@ -678,9 +680,9 @@ public AttributesNode getEntryAttributesNode() throws IOException { * @since 3.7 */ public AttributesNode getInfoAttributesNode() throws IOException { - if (infoAttributeNode instanceof InfoAttributesNode) - infoAttributeNode = ((InfoAttributesNode) infoAttributeNode).load(); - return infoAttributeNode; + if (infoAttributesNode instanceof InfoAttributesNode) + infoAttributesNode = ((InfoAttributesNode) infoAttributesNode).load(); + return infoAttributesNode; } /** @@ -696,10 +698,10 @@ public AttributesNode getInfoAttributesNode() throws IOException { * @since 3.7 */ public AttributesNode getGlobalAttributesNode() throws IOException { - if (globalAttributeNode instanceof GlobalAttributesNode) - globalAttributeNode = ((GlobalAttributesNode) globalAttributeNode) + if (globalAttributesNode instanceof GlobalAttributesNode) + globalAttributesNode = ((GlobalAttributesNode) globalAttributesNode) .load(); - return globalAttributeNode; + return globalAttributesNode; } private static final Comparator ENTRY_CMP = new Comparator() { @@ -1296,68 +1298,6 @@ AttributesNode load() throws IOException { } } - /** - * Attributes node loaded from global system-wide file. - */ - private static class GlobalAttributesNode extends AttributesNode { - final Repository repository; - - GlobalAttributesNode(Repository repository) { - this.repository = repository; - } - - AttributesNode load() throws IOException { - AttributesNode r = new AttributesNode(); - - FS fs = repository.getFS(); - String path = repository.getConfig().get(CoreConfig.KEY) - .getAttributesFile(); - if (path != null) { - File attributesFile; - if (path.startsWith("~/")) //$NON-NLS-1$ - attributesFile = fs.resolve(fs.userHome(), - path.substring(2)); - else - attributesFile = fs.resolve(null, path); - loadRulesFromFile(r, attributesFile); - } - return r.getRules().isEmpty() ? null : r; - } - } - - /** Magic type indicating there may be rules for the top level. */ - private static class InfoAttributesNode extends AttributesNode { - final Repository repository; - - InfoAttributesNode(Repository repository) { - this.repository = repository; - } - - AttributesNode load() throws IOException { - AttributesNode r = new AttributesNode(); - - FS fs = repository.getFS(); - - File attributes = fs.resolve(repository.getDirectory(), - "info/attributes"); //$NON-NLS-1$ - loadRulesFromFile(r, attributes); - - return r.getRules().isEmpty() ? null : r; - } - - } - - private static void loadRulesFromFile(AttributesNode r, File attrs) - throws FileNotFoundException, IOException { - if (attrs.exists()) { - FileInputStream in = new FileInputStream(attrs); - try { - r.parse(in); - } finally { - in.close(); - } - } - } private static final class IteratorState { /** Options used to process the working tree. */ From 6389948a814412267f46de375563f9d009d76ccb Mon Sep 17 00:00:00 2001 From: Christian Halstrick Date: Tue, 10 Nov 2015 16:21:00 +0100 Subject: [PATCH 13/50] Add an attribute accessor to CanonicalTreeParser and use it in Treewalk When checking out a branch we need to access the attributes stored in the tree to be checked out. E.g. directly after a clone we checkout the remote HEAD. In this case index and workingtree are still empty. So we have to search the tree to be checked out for attributes. Change-Id: I6d96f5d095ed2e3c259d4b12124e404f5215bd9f --- org.eclipse.jgit/.settings/.api_filters | 14 +++++ .../jgit/dircache/DirCacheIterator.java | 3 - .../jgit/treewalk/AbstractTreeIterator.java | 8 +++ .../jgit/treewalk/CanonicalTreeParser.java | 61 +++++++++++++++++++ .../org/eclipse/jgit/treewalk/TreeWalk.java | 53 ++++++++++++---- .../jgit/treewalk/WorkingTreeIterator.java | 59 ------------------ 6 files changed, 124 insertions(+), 74 deletions(-) diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters index c7a96a2f2..e98e360f7 100644 --- a/org.eclipse.jgit/.settings/.api_filters +++ b/org.eclipse.jgit/.settings/.api_filters @@ -49,6 +49,20 @@
+ + + + + + + + + + + + + + diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java index 354a07439..ad93f7213 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java @@ -103,9 +103,6 @@ public class DirCacheIterator extends AbstractTreeIterator { /** The subtree containing {@link #currentEntry} if this is first entry. */ protected DirCacheTree currentSubtree; - /** Holds an {@link AttributesNode} for the current entry */ - private AttributesNode attributesNode; - /** * Create a new iterator for an already loaded DirCache instance. *

diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java index 41e593eaa..aa5f32a87 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java @@ -49,6 +49,7 @@ import java.nio.ByteBuffer; import java.nio.CharBuffer; +import org.eclipse.jgit.attributes.AttributesNode; import org.eclipse.jgit.dircache.DirCacheCheckout; import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; @@ -92,6 +93,13 @@ public abstract class AbstractTreeIterator { /** The iterator this current entry is path equal to. */ AbstractTreeIterator matches; + /** + * Parsed rules of .gitattributes file if it exists. + * + * @since 4.2 + */ + protected AttributesNode attributesNode; + /** * Number of entries we moved forward to force a D/F conflict match. * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java index 0805e5068..df31558ff 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java @@ -45,8 +45,12 @@ package org.eclipse.jgit.treewalk; import java.io.IOException; +import java.io.InputStream; import java.util.Arrays; +import java.util.Collections; +import org.eclipse.jgit.attributes.AttributesNode; +import org.eclipse.jgit.attributes.AttributesRule; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.lib.AnyObjectId; @@ -54,10 +58,15 @@ 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.util.RawParseUtils; /** Parses raw Git trees from the canonical semi-text/semi-binary format. */ public class CanonicalTreeParser extends AbstractTreeIterator { + private static final int ATTRIBUTESLENGTH = Constants.DOT_GIT_ATTRIBUTES + .getBytes().length; + private static final byte[] EMPTY = {}; private byte[] raw; @@ -364,5 +373,57 @@ private void parseEntry() { } pathLen = tmp; nextPtr = ptr + Constants.OBJECT_ID_LENGTH; + + // Check if this entry is a .gitattributes file + if (RawParseUtils.match(path, pathOffset, + Constants.DOT_GIT_ATTRIBUTES.getBytes()) == ATTRIBUTESLENGTH) + attributesNode = new LazyLoadingAttributesNode( + ObjectId.fromRaw(idBuffer(), idOffset())); + } + + /** + * Retrieve the {@link AttributesNode} for the current entry. + * + * @param reader + * {@link ObjectReader} used to parse the .gitattributes entry. + * @return {@link AttributesNode} for the current entry. + * @throws IOException + * @since 4.2 + */ + public AttributesNode getEntryAttributesNode(ObjectReader reader) + throws IOException { + if (attributesNode instanceof LazyLoadingAttributesNode) + attributesNode = ((LazyLoadingAttributesNode) attributesNode) + .load(reader); + return attributesNode; + } + + /** + * {@link AttributesNode} implementation that provides lazy loading + */ + private static class LazyLoadingAttributesNode extends AttributesNode { + final ObjectId objectId; + + LazyLoadingAttributesNode(ObjectId objectId) { + super(Collections. emptyList()); + this.objectId = objectId; + + } + + AttributesNode load(ObjectReader reader) throws IOException { + AttributesNode r = new AttributesNode(); + ObjectLoader loader = reader.open(objectId); + if (loader != null) { + InputStream in = loader.openStream(); + try { + r.parse(in); + } finally { + in.close(); + } + } + return r.getRules().isEmpty() ? null : r; + } + } + } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java index 7f7a5c393..826ce0973 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java @@ -50,6 +50,7 @@ import java.util.Map; import java.util.Set; +import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.attributes.Attribute; import org.eclipse.jgit.attributes.AttributesNode; @@ -1131,8 +1132,10 @@ public Map getAttributes() { WorkingTreeIterator workingTreeIterator = getTree(WorkingTreeIterator.class); DirCacheIterator dirCacheIterator = getTree(DirCacheIterator.class); + CanonicalTreeParser other = getTree(CanonicalTreeParser.class); - if (workingTreeIterator == null && dirCacheIterator == null) { + if (workingTreeIterator == null && dirCacheIterator == null + && other == null) { // Can not retrieve the attributes without at least one of the above // iterators. return Collections. emptyMap(); @@ -1152,7 +1155,7 @@ public Map getAttributes() { // Gets the attributes located on the current entry path getPerDirectoryEntryAttributes(path, isDir, operationType, - workingTreeIterator, dirCacheIterator, + workingTreeIterator, dirCacheIterator, other, attributes); // Gets the attributes located in the global attribute file @@ -1180,6 +1183,8 @@ public Map getAttributes() { * a {@link WorkingTreeIterator} matching the current entry * @param dirCacheIterator * a {@link DirCacheIterator} matching the current entry + * @param other + * a {@link CanonicalTreeParser} matching the current entry * @param attributes * Non null map holding the existing attributes. This map will be * augmented with new entry. None entry will be overrided. @@ -1189,18 +1194,21 @@ public Map getAttributes() { */ private void getPerDirectoryEntryAttributes(String path, boolean isDir, OperationType opType, WorkingTreeIterator workingTreeIterator, - DirCacheIterator dirCacheIterator, Map attributes) + DirCacheIterator dirCacheIterator, CanonicalTreeParser other, + Map attributes) throws IOException { // Prevents infinite recurrence - if (workingTreeIterator != null || dirCacheIterator != null) { + if (workingTreeIterator != null || dirCacheIterator != null + || other != null) { AttributesNode currentAttributesNode = getCurrentAttributesNode( - opType, workingTreeIterator, dirCacheIterator); + opType, workingTreeIterator, dirCacheIterator, other); if (currentAttributesNode != null) { currentAttributesNode.getAttributes(path, isDir, attributes); } getPerDirectoryEntryAttributes(path, isDir, opType, getParent(workingTreeIterator, WorkingTreeIterator.class), getParent(dirCacheIterator, DirCacheIterator.class), + getParent(other, CanonicalTreeParser.class), attributes); } } @@ -1236,6 +1244,7 @@ private T getTree(Class type) { * @param opType * @param workingTreeIterator * @param dirCacheIterator + * @param other * @return a {@link AttributesNode} of the current entry, * {@link NullPointerException} otherwise. * @throws IOException @@ -1243,8 +1252,10 @@ private T getTree(Class type) { * parsing one on the attributes file. */ private AttributesNode getCurrentAttributesNode(OperationType opType, - WorkingTreeIterator workingTreeIterator, - DirCacheIterator dirCacheIterator) throws IOException { + @Nullable WorkingTreeIterator workingTreeIterator, + @Nullable DirCacheIterator dirCacheIterator, + @Nullable CanonicalTreeParser other) + throws IOException { AttributesNode attributesNode = null; switch (opType) { case CHECKIN_OP: @@ -1252,17 +1263,30 @@ private AttributesNode getCurrentAttributesNode(OperationType opType, attributesNode = workingTreeIterator.getEntryAttributesNode(); } if (attributesNode == null && dirCacheIterator != null) { - attributesNode = dirCacheIterator - .getEntryAttributesNode(getObjectReader()); + attributesNode = getAttributesNode(dirCacheIterator + .getEntryAttributesNode(getObjectReader()), + attributesNode); + } + if (attributesNode == null && other != null) { + attributesNode = getAttributesNode( + other.getEntryAttributesNode(getObjectReader()), + attributesNode); } break; case CHECKOUT_OP: - if (dirCacheIterator != null) { - attributesNode = dirCacheIterator + if (other != null) { + attributesNode = other .getEntryAttributesNode(getObjectReader()); } + if (dirCacheIterator != null) { + attributesNode = getAttributesNode(dirCacheIterator + .getEntryAttributesNode(getObjectReader()), + attributesNode); + } if (attributesNode == null && workingTreeIterator != null) { - attributesNode = workingTreeIterator.getEntryAttributesNode(); + attributesNode = getAttributesNode( + workingTreeIterator.getEntryAttributesNode(), + attributesNode); } break; default: @@ -1274,4 +1298,9 @@ private AttributesNode getCurrentAttributesNode(OperationType opType, return attributesNode; } + + private static AttributesNode getAttributesNode(AttributesNode value, + AttributesNode defaultValue) { + return (value == null) ? defaultValue : value; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java index e36ce0faf..8be7f9a84 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java @@ -74,8 +74,6 @@ import org.eclipse.jgit.ignore.FastIgnoreRule; import org.eclipse.jgit.ignore.IgnoreNode; import org.eclipse.jgit.internal.JGitText; -import org.eclipse.jgit.internal.storage.file.GlobalAttributesNode; -import org.eclipse.jgit.internal.storage.file.InfoAttributesNode; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.CoreConfig; import org.eclipse.jgit.lib.CoreConfig.CheckStat; @@ -136,9 +134,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { /** If there is a .gitignore file present, the parsed rules from it. */ private IgnoreNode ignoreNode; - /** If there is a .gitattributes file present, the parsed rules from it. */ - private AttributesNode attributesNode; - /** Repository that is the root level being iterated over */ protected Repository repository; @@ -148,19 +143,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { /** The offset of the content id in {@link #idBuffer()} */ private int contentIdOffset; - /** - * Holds the {@link AttributesNode} that is stored in - * $GIT_DIR/info/attributes file. - */ - private AttributesNode infoAttributesNode; - - /** - * Holds the {@link AttributesNode} that is stored in global attribute file. - * - * @see CoreConfig#getAttributesFile() - */ - private AttributesNode globalAttributesNode; - /** * Create a new iterator with no parent. * @@ -204,8 +186,6 @@ protected WorkingTreeIterator(final String prefix, protected WorkingTreeIterator(final WorkingTreeIterator p) { super(p); state = p.state; - infoAttributesNode = p.infoAttributesNode; - globalAttributesNode = p.globalAttributesNode; } /** @@ -225,10 +205,6 @@ protected void initRootIterator(Repository repo) { else entry = null; ignoreNode = new RootIgnoreNode(entry, repo); - - infoAttributesNode = new InfoAttributesNode(repo); - - globalAttributesNode = new GlobalAttributesNode(repo); } /** @@ -669,41 +645,6 @@ public AttributesNode getEntryAttributesNode() throws IOException { return attributesNode; } - /** - * Retrieves the {@link AttributesNode} that holds the information located - * in $GIT_DIR/info/attributes file. - * - * @return the {@link AttributesNode} that holds the information located in - * $GIT_DIR/info/attributes file. - * @throws IOException - * if an error is raised while parsing the attributes file - * @since 3.7 - */ - public AttributesNode getInfoAttributesNode() throws IOException { - if (infoAttributesNode instanceof InfoAttributesNode) - infoAttributesNode = ((InfoAttributesNode) infoAttributesNode).load(); - return infoAttributesNode; - } - - /** - * Retrieves the {@link AttributesNode} that holds the information located - * in system-wide file. - * - * @return the {@link AttributesNode} that holds the information located in - * system-wide file. - * @throws IOException - * IOException if an error is raised while parsing the - * attributes file - * @see CoreConfig#getAttributesFile() - * @since 3.7 - */ - public AttributesNode getGlobalAttributesNode() throws IOException { - if (globalAttributesNode instanceof GlobalAttributesNode) - globalAttributesNode = ((GlobalAttributesNode) globalAttributesNode) - .load(); - return globalAttributesNode; - } - private static final Comparator ENTRY_CMP = new Comparator() { public int compare(final Entry o1, final Entry o2) { final byte[] a = o1.encodedName; From 69cd6f586418291c980831cd92508c549c84ce94 Mon Sep 17 00:00:00 2001 From: Christian Halstrick Date: Wed, 28 Oct 2015 13:24:28 +0100 Subject: [PATCH 14/50] Introduce FS.execute() to execute a command defined by a ProcessBuilder Change-Id: I2ad2c71ad30b969455bdea89637b8e996b1dad8c Signed-off-by: Matthias Sohn --- .../jgit/util/RunExternalScriptTest.java | 40 ++++++++++ .../src/org/eclipse/jgit/util/FS.java | 78 ++++++++++++++++++- 2 files changed, 115 insertions(+), 3 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java index 82beab2dc..b6a2519c5 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java @@ -52,6 +52,7 @@ import java.io.InputStream; import org.eclipse.jgit.junit.JGitTestUtil; +import org.eclipse.jgit.util.FS.ExecutionResult; import org.junit.Before; import org.junit.Test; @@ -163,6 +164,45 @@ public void testWrongScript() throws IOException, InterruptedException { assertEquals(127, rc); } + @Test + public void testCopyStdInExecute() + throws IOException, InterruptedException { + String inputStr = "a\nb\rc\r\nd"; + File script = writeTempFile("cat -"); + ProcessBuilder pb = new ProcessBuilder("/bin/sh", script.getPath()); + ExecutionResult res = FS.DETECTED.execute(pb, + new ByteArrayInputStream(inputStr.getBytes())); + assertEquals(0, res.getRc()); + assertEquals(inputStr, new String(res.getStdout().toByteArray())); + assertEquals("", new String(res.getStderr().toByteArray())); + } + + @Test + public void testStdErrExecute() throws IOException, InterruptedException { + File script = writeTempFile("echo hi >&2"); + ProcessBuilder pb = new ProcessBuilder("/bin/sh", script.getPath()); + ExecutionResult res = FS.DETECTED.execute(pb, null); + assertEquals(0, res.getRc()); + assertEquals("", new String(res.getStdout().toByteArray())); + assertEquals("hi" + sep, new String(res.getStderr().toByteArray())); + } + + @Test + public void testAllTogetherBinExecute() + throws IOException, InterruptedException { + String inputStr = "a\nb\rc\r\nd"; + File script = writeTempFile( + "echo $#,$1,$2,$3,$4,$5,$6 >&2 ; cat -; exit 5"); + ProcessBuilder pb = new ProcessBuilder("/bin/sh", script.getPath(), "a", + "b", "c"); + ExecutionResult res = FS.DETECTED.execute(pb, + new ByteArrayInputStream(inputStr.getBytes())); + assertEquals(5, res.getRc()); + assertEquals(inputStr, new String(res.getStdout().toByteArray())); + assertEquals("3,a,b,c,,," + sep, + new String(res.getStderr().toByteArray())); + } + private File writeTempFile(String body) throws IOException { File f = File.createTempFile("RunProcessTestScript_", ""); JGitTestUtil.write(f, body); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java index e407dd8be..b61e47f5f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java @@ -110,7 +110,54 @@ public FS detect(Boolean cygwinUsed) { } } - final static Logger LOG = LoggerFactory.getLogger(FS.class); + /** + * Result of an executed process. The caller is responsible to close the + * contained {@link TemporaryBuffer}s + * + * @since 4.2 + */ + public static class ExecutionResult { + private TemporaryBuffer stdout; + + private TemporaryBuffer stderr; + + private int rc; + + /** + * @param stdout + * @param stderr + * @param rc + */ + public ExecutionResult(TemporaryBuffer stdout, TemporaryBuffer stderr, + int rc) { + this.stdout = stdout; + this.stderr = stderr; + this.rc = rc; + } + + /** + * @return buffered standard output stream + */ + public TemporaryBuffer getStdout() { + return stdout; + } + + /** + * @return buffered standard error stream + */ + public TemporaryBuffer getStderr() { + return stderr; + } + + /** + * @return the return code of the process + */ + public int getRc() { + return rc; + } + } + + private final static Logger LOG = LoggerFactory.getLogger(FS.class); /** The auto-detected implementation selected for this operating system and JRE. */ public static final FS DETECTED = detect(); @@ -1004,10 +1051,10 @@ private static boolean shutdownAndAwaitTermination(ExecutorService pool) { pool.shutdown(); // Disable new tasks from being submitted try { // Wait a while for existing tasks to terminate - if (!pool.awaitTermination(5, TimeUnit.SECONDS)) { + if (!pool.awaitTermination(60, TimeUnit.SECONDS)) { pool.shutdownNow(); // Cancel currently executing tasks // Wait a while for tasks to respond to being canceled - if (!pool.awaitTermination(5, TimeUnit.SECONDS)) + if (!pool.awaitTermination(60, TimeUnit.SECONDS)) hasShutdown = false; } } catch (InterruptedException ie) { @@ -1034,6 +1081,31 @@ private static boolean shutdownAndAwaitTermination(ExecutorService pool) { */ public abstract ProcessBuilder runInShell(String cmd, String[] args); + /** + * Execute a command defined by a {@link ProcessBuilder}. + * + * @param pb + * The command to be executed + * @param in + * The standard input stream passed to the process + * @return The result of the executed command + * @throws InterruptedException + * @throws IOException + * @since 4.2 + */ + public ExecutionResult execute(ProcessBuilder pb, InputStream in) + throws IOException, InterruptedException { + TemporaryBuffer stdout = new TemporaryBuffer.LocalFile(null); + TemporaryBuffer stderr = new TemporaryBuffer.Heap(1024, 1024 * 1024); + try { + int rc = runProcess(pb, stdout, stderr, in); + return new ExecutionResult(stdout, stderr, rc); + } finally { + stdout.close(); + stderr.close(); + } + } + private static class Holder { final V value; From d3e61db455174ce8af70fcc80a19f414881dfea9 Mon Sep 17 00:00:00 2001 From: Matthias Sohn Date: Tue, 17 Nov 2015 17:26:53 +0100 Subject: [PATCH 15/50] Fix pre-push hook to not set null remoteName as first argument According to [1] the pre-push hook expects two parameters which provide the name and location of the destination remote, if a named remote is not being used both values should be the same. We did set the first parameter to null in that case which caused ProcessBuilder to throw a NullPointerException since its start() method doesn't accept null arguments. [1] https://git-scm.com/docs/githooks#_pre_push Bug: 482393 Change-Id: Idb9b0a48cefac01abfcfdf00f6d173f8fa1d9a7b Signed-off-by: Matthias Sohn --- org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java index 2e6582819..a501fee90 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java @@ -113,6 +113,9 @@ public String getHookName() { */ @Override protected String[] getParameters() { + if (remoteName == null) { + remoteName = remoteLocation; + } return new String[] { remoteName, remoteLocation }; } From 4b114d3c6bc1ed80ba55a5a42241ce1ead6d51cc Mon Sep 17 00:00:00 2001 From: Christian Halstrick Date: Thu, 19 Nov 2015 17:21:46 +0100 Subject: [PATCH 16/50] Throw IndexReadException if existing index can't be read If the index file exists but can't be read for example because of wrong filesystem permissions we should throw a specific exception. This allows EGit to handle this error situation. Bug: 482607 Change-Id: I50bfcb719c45caac3cb5550a8b16307c2ea9def4 Signed-off-by: Matthias Sohn --- .../eclipse/jgit/internal/JGitText.properties | 1 + .../org/eclipse/jgit/dircache/DirCache.java | 7 ++ .../jgit/errors/IndexReadException.java | 82 +++++++++++++++++++ .../org/eclipse/jgit/internal/JGitText.java | 1 + 4 files changed, 91 insertions(+) create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/errors/IndexReadException.java diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index 51e44fd77..cdb2be151 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -87,6 +87,7 @@ cannotReadBlob=Cannot read blob {0} cannotReadCommit=Cannot read commit {0} cannotReadFile=Cannot read file {0} cannotReadHEAD=cannot read HEAD: {0} {1} +cannotReadIndex=The index file {0} exists but cannot be read cannotReadObject=Cannot read object cannotReadObjectsPath=Cannot read {0}/{1}: {2} cannotReadTree=Cannot read tree {0} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java index 92cdf391c..387d8ce73 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java @@ -63,6 +63,7 @@ import java.util.List; import org.eclipse.jgit.errors.CorruptObjectException; +import org.eclipse.jgit.errors.IndexReadException; import org.eclipse.jgit.errors.LockFailedException; import org.eclipse.jgit.errors.UnmergedPathException; import org.eclipse.jgit.events.IndexChangedEvent; @@ -418,6 +419,12 @@ else if (snapshot == null || snapshot.isModified(liveFile)) { } } } catch (FileNotFoundException fnfe) { + if (liveFile.exists()) { + // Panic: the index file exists but we can't read it + throw new IndexReadException( + MessageFormat.format(JGitText.get().cannotReadIndex, + liveFile.getAbsolutePath(), fnfe)); + } // Someone must have deleted it between our exists test // and actually opening the path. That's fine, its empty. // diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/IndexReadException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/IndexReadException.java new file mode 100644 index 000000000..70f650dde --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/IndexReadException.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015, Christian Halstrick 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.errors; + +import java.io.IOException; + +import org.eclipse.jgit.internal.JGitText; + +/** + * Cannot read the index. This is a serious error that users need to be made + * aware of. + * + * @since 4.2 + */ +public class IndexReadException extends IOException { + private static final long serialVersionUID = 1L; + + /** + * Constructs an IndexReadException with the default message. + */ + public IndexReadException() { + super(JGitText.get().indexWriteException); + } + + /** + * Constructs an IndexReadException with the specified detail message. + * + * @param s + * message + */ + public IndexReadException(final String s) { + super(s); + } + + /** + * Constructs an IndexReadException with the specified detail message. + * + * @param s + * message + * @param cause + * root cause exception + */ + public IndexReadException(final String s, final Throwable cause) { + super(s); + initCause(cause); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index e39469bd8..be29feabd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -146,6 +146,7 @@ public static JGitText get() { /***/ public String cannotReadCommit; /***/ public String cannotReadFile; /***/ public String cannotReadHEAD; + /***/ public String cannotReadIndex; /***/ public String cannotReadObject; /***/ public String cannotReadObjectsPath; /***/ public String cannotReadTree; From 68cea21f5384e184bb3fc2c33e5d2213721d6bf5 Mon Sep 17 00:00:00 2001 From: Thomas Meyer Date: Thu, 19 Nov 2015 23:14:03 +0100 Subject: [PATCH 17/50] git rev-parse: Add --verify option Add the --verify option to be more compatible with git Change-Id: I225a36ecc4711fd2eb9af67ca8fb79681d94c587 Signed-off-by: Thomas Meyer --- .../jgit/pgm/internal/CLIText.properties | 1 + .../src/org/eclipse/jgit/pgm/RevParse.java | 18 +++++++++++++++--- .../org/eclipse/jgit/pgm/internal/CLIText.java | 1 + 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties index 64afdad51..aa32478ff 100644 --- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties +++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties @@ -128,6 +128,7 @@ metaVar_user=USER metaVar_version=VERSION mostCommonlyUsedCommandsAre=The most commonly used commands are: needApprovalToDestroyCurrentRepository=Need approval to destroy current repository +needSingleRevision=Needed a single revision noGitRepositoryConfigured=No Git repository configured. noNamesFound=No names found, cannot describe anything. noSuchFile=no such file: {0} diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevParse.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevParse.java index 5530ac5c9..4456fd534 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevParse.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevParse.java @@ -1,6 +1,7 @@ /* * Copyright (C) 2009, Daniel Cheng (aka SDiZ) * Copyright (C) 2009, Daniel Cheng (aka SDiZ) + * Copyright (C) 2015 Thomas Meyer * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -51,14 +52,19 @@ import java.util.Map; import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.Option; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.pgm.internal.CLIText;; @Command(usage = "usage_RevParse") class RevParse extends TextBuiltin { @Option(name = "--all", usage = "usage_RevParseAll") - boolean all = false; + boolean all; + + @Option(name = "--verify", usage = "usage_RevParseVerify") + boolean verify; @Argument(index = 0, metaVar = "metaVar_commitish") private final List commits = new ArrayList(); @@ -67,11 +73,17 @@ class RevParse extends TextBuiltin { protected void run() throws Exception { if (all) { Map allRefs = db.getRefDatabase().getRefs(ALL); - for (final Ref r : allRefs.values()) + for (final Ref r : allRefs.values()) { outw.println(r.getObjectId().name()); + } } else { - for (final ObjectId o : commits) + if (verify && commits.size() > 1) { + throw new CmdLineException(CLIText.get().needSingleRevision); + } + + for (final ObjectId o : commits) { outw.println(o.name()); + } } } } diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java index 433ddf2b1..29d48ebd4 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java @@ -187,6 +187,7 @@ public static String formatLine(String line) { /***/ public String metaVar_version; /***/ public String mostCommonlyUsedCommandsAre; /***/ public String needApprovalToDestroyCurrentRepository; + /***/ public String needSingleRevision; /***/ public String noGitRepositoryConfigured; /***/ public String noNamesFound; /***/ public String noSuchFile; From 1e47c7058d930db920cb897faa2df14a4ae87c00 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 10 Nov 2015 16:34:00 -0800 Subject: [PATCH 18/50] RefDirectory.getRef: Treat fake missing symrefs like real ones getRef() loops over its search path to find a ref: Ref ref = null; for (String prefix : SEARCH_PATH) { ref = readRef(prefix + needle, packed); if (ref != null) { ref = resolve(ref, 0, null, null, packed); break; } } fireRefsChanged(); return ref; If readRef returns null (indicating that the ref does not exist), the loop continues so we can find the ref later in the search path. And resolve should never return null, so if we return null it should mean we exhausted the entire search path and didn't find the ref. ... except that resolve can return null: it does so when it has followed too many symrefs and concluded that there is a symref loop: if (MAX_SYMBOLIC_REF_DEPTH <= depth) return null; // claim it doesn't exist Continue the loop instead of returning null immediately. This makes the behavior more consistent. Arguably getRef should throw an exception when a symref loop is detected. That would be a more invasive change, so if it's a good idea it will have to wait for another patch. Change-Id: Icb1c7fafd4f1e34c9b43538e27ab5bbc17ad9eef Signed-off-by: Jonathan Nieder --- .../storage/file/RefDirectoryTest.java | 30 +++++++++++++++++++ .../internal/storage/file/RefDirectory.java | 2 ++ 2 files changed, 32 insertions(+) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java index d66753da0..52e181bc8 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java @@ -857,6 +857,36 @@ public void testGetRefs_CycleInSymbolicRef() throws IOException { assertNull("mising 1 due to cycle", r); } + @Test + public void testGetRef_CycleInSymbolicRef() throws IOException { + Ref r; + + writeLooseRef("refs/1", "ref: refs/2\n"); + writeLooseRef("refs/2", "ref: refs/3\n"); + writeLooseRef("refs/3", "ref: refs/4\n"); + writeLooseRef("refs/4", "ref: refs/5\n"); + writeLooseRef("refs/5", "ref: refs/end\n"); + writeLooseRef("refs/end", A); + + r = refdir.getRef("1"); + assertEquals("refs/1", r.getName()); + assertEquals(A, r.getObjectId()); + assertTrue(r.isSymbolic()); + + writeLooseRef("refs/5", "ref: refs/6\n"); + writeLooseRef("refs/6", "ref: refs/end\n"); + + r = refdir.getRef("1"); + assertNull("missing 1 due to cycle", r); + + writeLooseRef("refs/heads/1", B); + + r = refdir.getRef("1"); + assertEquals("refs/heads/1", r.getName()); + assertEquals(B, r.getObjectId()); + assertFalse(r.isSymbolic()); + } + @Test public void testGetRefs_PackedNotPeeled_Sorted() throws IOException { Map all; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java index 7851678a8..c8c12f5b6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java @@ -294,6 +294,8 @@ public Ref getRef(final String needle) throws IOException { ref = readRef(prefix + needle, packed); if (ref != null) { ref = resolve(ref, 0, null, null, packed); + } + if (ref != null) { break; } } catch (IOException e) { From a6bcc988e06d4ef041e9c0efe7232cc4a5648374 Mon Sep 17 00:00:00 2001 From: Matthias Sohn Date: Sat, 21 Nov 2015 00:18:14 +0100 Subject: [PATCH 19/50] Remove no longer needed outdated API warning filter This fixes a warning saying this filter isn't needed anymore. Change-Id: If77056378befe86c1773950dbe48a82c833fd532 Signed-off-by: Matthias Sohn --- org.eclipse.jgit/.settings/.api_filters | 6 ------ 1 file changed, 6 deletions(-) diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters index e98e360f7..b89aad4c5 100644 --- a/org.eclipse.jgit/.settings/.api_filters +++ b/org.eclipse.jgit/.settings/.api_filters @@ -21,12 +21,6 @@ - - - - - - From fe85311b3a66d8cc7e7a66d4f0e01f88acbf1c0a Mon Sep 17 00:00:00 2001 From: Matthias Sohn Date: Thu, 12 Nov 2015 11:28:01 +0100 Subject: [PATCH 20/50] Avoid UnknownHostException in WalkEncryptionTest Prevent that WalkEncryptionTest fails when it can't determine the public IP address using http://checkip.amazonws.com. Also set timeouts when determining IP address in order to prevent long wait times during tests. Change-Id: I1d2fe09f99df2a5f75f8077811a72fb2271cdddb Signed-off-by: Matthias Sohn --- .../jgit/transport/WalkEncryptionTest.java | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java index 90d78e4b6..ac2bfd12f 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java @@ -43,6 +43,20 @@ package org.eclipse.jgit.transport; +import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.UTF_8; +import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.cryptoCipherListPBE; +import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.cryptoCipherListTrans; +import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.folderDelete; +import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.permitLongTests; +import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.policySetup; +import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.product; +import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.proxySetup; +import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.publicAddress; +import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.reportPolicy; +import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.securityProviderName; +import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.textWrite; +import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.transferStream; +import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.verifyFileContent; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -59,7 +73,10 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; +import java.net.SocketTimeoutException; import java.net.URL; +import java.net.URLConnection; +import java.net.UnknownHostException; import java.nio.charset.Charset; import java.nio.file.Files; import java.security.GeneralSecurityException; @@ -94,8 +111,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.*; - /** * Amazon S3 encryption pipeline test. * @@ -401,14 +416,22 @@ static void folderDelete(String folder) throws Exception { * @throws Exception */ static String publicAddress() throws Exception { - String service = "http://checkip.amazonaws.com"; - URL url = new URL(service); - BufferedReader reader = new BufferedReader( - new InputStreamReader(url.openStream())); try { - return reader.readLine(); - } finally { - reader.close(); + String service = "http://checkip.amazonaws.com"; + URL url = new URL(service); + URLConnection c = url.openConnection(); + c.setConnectTimeout(500); + c.setReadTimeout(500); + BufferedReader reader = new BufferedReader( + new InputStreamReader(c.getInputStream())); + try { + return reader.readLine(); + } finally { + reader.close(); + } + } catch (UnknownHostException | SocketTimeoutException e) { + return "Can't reach http://checkip.amazonaws.com to" + + " determine public address"; } } From 0353b1d3ff5554c6f84ee261c0c776efb58eec9b Mon Sep 17 00:00:00 2001 From: Matthias Sohn Date: Thu, 19 Nov 2015 00:47:05 +0100 Subject: [PATCH 21/50] Fix classpath of test launch configurations Remove references to the bundle org.eclipse.jgit.java7 which was removed in 4.0. Change-Id: I85527eb2a34bb94979fdab1311043ae77a2b5ecd Signed-off-by: Matthias Sohn --- .../org.eclipse.jgit.pgm--All-Tests (Java7).launch | 6 ------ .../org.eclipse.jgit.pgm--All-Tests (Java8).launch | 1 - .../org.eclipse.jgit.core--All-Tests (Java 7).launch | 1 - .../org.eclipse.jgit.core--All-Tests (Java 8).launch | 1 - 4 files changed, 9 deletions(-) diff --git a/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java7).launch b/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java7).launch index 600ce7b72..3df0dcb64 100644 --- a/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java7).launch +++ b/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java7).launch @@ -15,12 +15,6 @@ - - - - - - diff --git a/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8).launch b/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8).launch index 1b61d3d3f..ce473ed03 100644 --- a/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8).launch +++ b/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8).launch @@ -19,7 +19,6 @@ - diff --git a/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 7).launch b/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 7).launch index af009c0d4..a83fabb75 100644 --- a/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 7).launch +++ b/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 7).launch @@ -18,7 +18,6 @@ - diff --git a/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8).launch b/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8).launch index 04aa3ea10..b221a11a5 100644 --- a/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8).launch +++ b/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8).launch @@ -19,7 +19,6 @@ - From 18af2d42657dbe758b0d7ef88efb0cc466105da9 Mon Sep 17 00:00:00 2001 From: Christian Halstrick Date: Fri, 20 Nov 2015 14:33:15 +0100 Subject: [PATCH 22/50] Let FS_Win32_Cygwin detect symlink support by creating temporary symlink The class FS_Win32 was always trying out to create a temporary symlink in order to find out whether symlinks are supported. FS_Win32_Cygwin was overwriting this method and always returned true. But when the user running JGit does not have administrative rights then the creation of symlinks is forbidden even if he is running on FS_Win32_Cygwin. A lot of tests failed only on the Windows platform because of this. It was correctly detected that FS_Win32_Cygwin is the filesystem abstraction to be used but creation of symlinks always failed because of lacking privileges of the user running the tests. This fix teaches FS_Win32_Cygwin to behave like FS_Win32 and to test whether symlinks can be created in order to find out whether symlinks are supported. Change-Id: Ie2394631ffc4c489bd37c3ec142ed44bbfcac726 Signed-off-by: Matthias Sohn --- .../tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java | 6 ++++++ .../eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java | 2 ++ .../tst/org/eclipse/jgit/treewalk/TreeWalkJava7Test.java | 1 + .../tst/org/eclipse/jgit/util/FSJava7Test.java | 2 ++ .../tst/org/eclipse/jgit/util/FileUtils7Test.java | 1 + .../src/org/eclipse/jgit/util/FS_Win32_Cygwin.java | 5 ----- 6 files changed, 12 insertions(+), 5 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java index 6d62528f8..51a6b5a8f 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java @@ -79,6 +79,7 @@ import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FileUtils; +import org.junit.Assume; import org.junit.Test; public class DirCacheCheckoutTest extends RepositoryTestCase { @@ -925,6 +926,7 @@ public void testCheckoutOutChanges() throws IOException { @Test public void testCheckoutChangeLinkToEmptyDir() throws Exception { + Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); String fname = "was_file"; Git git = Git.wrap(db); @@ -961,6 +963,7 @@ public void testCheckoutChangeLinkToEmptyDir() throws Exception { @Test public void testCheckoutChangeLinkToEmptyDirs() throws Exception { + Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); String fname = "was_file"; Git git = Git.wrap(db); @@ -999,6 +1002,7 @@ public void testCheckoutChangeLinkToEmptyDirs() throws Exception { @Test public void testCheckoutChangeLinkToNonEmptyDirs() throws Exception { + Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); String fname = "file"; Git git = Git.wrap(db); @@ -1043,6 +1047,7 @@ public void testCheckoutChangeLinkToNonEmptyDirs() throws Exception { @Test public void testCheckoutChangeLinkToNonEmptyDirsAndNewIndexEntry() throws Exception { + Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); String fname = "file"; Git git = Git.wrap(db); @@ -1364,6 +1369,7 @@ public void testOverwriteUntrackedFileModeChange() @Test public void testOverwriteUntrackedLinkModeChange() throws Exception { + Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); String fname = "file.txt"; Git git = Git.wrap(db); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java index 88754109f..429f35ea4 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java @@ -67,6 +67,7 @@ public class FileTreeIteratorJava7Test extends RepositoryTestCase { @Test public void testFileModeSymLinkIsNotATree() throws IOException { + org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); FS fs = db.getFS(); // mĂ¥l = target in swedish, just to get som unicode in here writeTrashFile("mĂ¥l/data", "targetdata"); @@ -163,6 +164,7 @@ public void apply(DirCacheEntry ent) { */ @Test public void testSymlinkActuallyModified() throws Exception { + org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); final String NORMALIZED = "target"; final byte[] NORMALIZED_BYTES = Constants.encode(NORMALIZED); try (ObjectInserter oi = db.newObjectInserter()) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkJava7Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkJava7Test.java index bb1f2a639..1328b38e6 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkJava7Test.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkJava7Test.java @@ -55,6 +55,7 @@ public class TreeWalkJava7Test extends RepositoryTestCase { @Test public void testSymlinkToDirNotRecursingViaSymlink() throws Exception { + org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); FS fs = db.getFS(); assertTrue(fs.supportsSymlinks()); writeTrashFile("target/data", "targetdata"); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSJava7Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSJava7Test.java index e6a244e14..53b6fecfd 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSJava7Test.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSJava7Test.java @@ -57,6 +57,7 @@ import org.eclipse.jgit.junit.RepositoryTestCase; import org.junit.After; +import org.junit.Assume; import org.junit.Before; import org.junit.Test; @@ -87,6 +88,7 @@ public void tearDown() throws Exception { */ @Test public void testSymlinkAttributes() throws IOException, InterruptedException { + Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); FS fs = FS.DETECTED; File link = new File(trash, "ä"); File target = new File(trash, "Ă¥"); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtils7Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtils7Test.java index 4625f3068..cc1fdc21b 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtils7Test.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtils7Test.java @@ -73,6 +73,7 @@ public void tearDown() throws Exception { @Test public void testDeleteSymlinkToDirectoryDoesNotDeleteTarget() throws IOException { + org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); FS fs = FS.DETECTED; File dir = new File(trash, "dir"); File file = new File(dir, "file"); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java index 2450be4c1..28d628b34 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java @@ -162,11 +162,6 @@ public ProcessResult runHookIfPresent(Repository repository, String hookName, errRedirect, stdinArgs); } - @Override - public boolean supportsSymlinks() { - return true; - } - /** * @since 3.7 */ From d2faec27a7af7c9ea7db6b4ac31f75533ca45b80 Mon Sep 17 00:00:00 2001 From: Matthias Sohn Date: Fri, 20 Nov 2015 14:40:41 +0100 Subject: [PATCH 23/50] Raise error if FileNotFoundException is caught for an existing file File, FileInputStream and friends may throw FileNotFoundException even if the file is existing e.g. when file permissions don't allow to access the file content. In most cases this is a severe error we should not suppress hence rethrow the FileNotFoundException in this case. This may also fix bug 451508. Bug: 451508 Change-Id: If4a94217fb5b7cfd4c04d881902f3e86193c7008 Signed-off-by: Matthias Sohn --- .../eclipse/jgit/internal/JGitText.properties | 1 + .../org/eclipse/jgit/api/RebaseCommand.java | 3 ++ .../org/eclipse/jgit/internal/JGitText.java | 1 + .../jgit/internal/storage/file/LockFile.java | 4 ++ .../storage/file/ObjectDirectory.java | 37 ++++++++++--------- .../internal/storage/file/RefDirectory.java | 8 +++- .../storage/file/ReflogReaderImpl.java | 6 +++ .../internal/storage/file/UnpackedObject.java | 3 ++ .../src/org/eclipse/jgit/lib/Repository.java | 6 +++ .../jgit/storage/file/FileBasedConfig.java | 3 ++ 10 files changed, 53 insertions(+), 19 deletions(-) diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index cdb2be151..6e3b3047f 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -451,6 +451,7 @@ packfileIsTruncated=Packfile {0} is truncated. packfileIsTruncatedNoParam=Packfile is truncated. packHandleIsStale=Pack file {0} handle is stale, removing it from pack list packHasUnresolvedDeltas=pack has unresolved deltas +packInaccessible=Pack file {0} now inaccessible; removing it from pack list packingCancelledDuringObjectsWriting=Packing cancelled during objects writing packObjectCountMismatch=Pack object count mismatch: pack {0} index {1}: {2} packRefs=Pack refs diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java index ff2900842..753bc85bc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java @@ -980,6 +980,9 @@ private PersonIdent parseAuthor() throws IOException { try { raw = IO.readFully(authorScriptFile); } catch (FileNotFoundException notFound) { + if (authorScriptFile.exists()) { + throw notFound; + } return null; } return parseAuthor(raw); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index be29feabd..b03038b06 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -510,6 +510,7 @@ public static JGitText get() { /***/ public String packfileIsTruncatedNoParam; /***/ public String packHandleIsStale; /***/ public String packHasUnresolvedDeltas; + /***/ public String packInaccessible; /***/ public String packingCancelledDuringObjectsWriting; /***/ public String packObjectCountMismatch; /***/ public String packRefs; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java index 50297a97a..e23ca741b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java @@ -227,6 +227,10 @@ public void copyCurrentContent() throws IOException { fis.close(); } } catch (FileNotFoundException fnfe) { + if (ref.exists()) { + unlock(); + throw fnfe; + } // Don't worry about a file that doesn't exist yet, it // conceptually has no current content to copy. // diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java index e7ef127dd..bd1d488d9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java @@ -433,16 +433,14 @@ ObjectLoader openPackedObject(WindowCursor curs, AnyObjectId objectId) { ObjectLoader openLooseObject(WindowCursor curs, AnyObjectId id) throws IOException { - try { - File path = fileFor(id); - FileInputStream in = new FileInputStream(path); - try { - unpackedObjectCache.add(id); - return UnpackedObject.open(in, path, id, curs); - } finally { - in.close(); - } + File path = fileFor(id); + try (FileInputStream in = new FileInputStream(path)) { + unpackedObjectCache.add(id); + return UnpackedObject.open(in, path, id, curs); } catch (FileNotFoundException noFile) { + if (path.exists()) { + throw noFile; + } unpackedObjectCache.remove(id); return null; } @@ -513,15 +511,14 @@ private long getPackedObjectSize(WindowCursor curs, AnyObjectId id) { private long getLooseObjectSize(WindowCursor curs, AnyObjectId id) throws IOException { - try { - FileInputStream in = new FileInputStream(fileFor(id)); - try { - unpackedObjectCache.add(id); - return UnpackedObject.getSize(in, id, curs); - } finally { - in.close(); - } + File f = fileFor(id); + try (FileInputStream in = new FileInputStream(f)) { + unpackedObjectCache.add(id); + return UnpackedObject.getSize(in, id, curs); } catch (FileNotFoundException noFile) { + if (f.exists()) { + throw noFile; + } unpackedObjectCache.remove(id); return -1; } @@ -561,7 +558,11 @@ private void handlePackError(IOException e, PackFile p) { // Assume the pack is corrupted, and remove it from the list. removePack(p); } else if (e instanceof FileNotFoundException) { - warnTmpl = JGitText.get().packWasDeleted; + if (p.getPackFile().exists()) { + warnTmpl = JGitText.get().packInaccessible; + } else { + warnTmpl = JGitText.get().packWasDeleted; + } removePack(p); } else if (FileUtils.isStaleFileHandle(e)) { warnTmpl = JGitText.get().packHandleIsStale; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java index c8c12f5b6..69f7e9707 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java @@ -788,6 +788,9 @@ private PackedRefList readPackedRefs() throws IOException { new DigestInputStream(new FileInputStream(packedRefsFile), digest), CHARSET)); } catch (FileNotFoundException noPackedRefs) { + if (packedRefsFile.exists()) { + throw noPackedRefs; + } // Ignore it and leave the new list empty. return PackedRefList.NO_PACKED_REFS; } @@ -944,7 +947,10 @@ LooseRef scanRef(LooseRef ref, String name) throws IOException { try { buf = IO.readSome(path, limit); } catch (FileNotFoundException noFile) { - return null; // doesn't exist; not a reference. + if (path.exists() && path.isFile()) { + throw noFile; + } + return null; // doesn't exist or no file; not a reference. } int n = buf.length; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java index dadc63119..2f583b275 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java @@ -96,6 +96,9 @@ public ReflogEntry getReverseEntry(int number) throws IOException { try { log = IO.readFully(logName); } catch (FileNotFoundException e) { + if (logName.exists()) { + throw e; + } return null; } @@ -118,6 +121,9 @@ public List getReverseEntries(int max) throws IOException { try { log = IO.readFully(logName); } catch (FileNotFoundException e) { + if (logName.exists()) { + throw e; + } return Collections.emptyList(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java index 2cc256396..a02743720 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java @@ -399,6 +399,9 @@ public ObjectStream openStream() throws MissingObjectException, try { in = buffer(new FileInputStream(path)); } catch (FileNotFoundException gone) { + if (path.exists()) { + throw gone; + } // If the loose file no longer exists, it may have been // moved into a pack file in the mean time. Try again // to locate the object. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java index eda02dea4..8d512ccb2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -1590,6 +1590,9 @@ private String readCommitMsgFile(String msgFilename) throws IOException { try { return RawParseUtils.decode(IO.readFully(mergeMsgFile)); } catch (FileNotFoundException e) { + if (mergeMsgFile.exists()) { + throw e; + } // the file has disappeared in the meantime ignore it return null; } @@ -1621,6 +1624,9 @@ private byte[] readGitDirectoryFile(String filename) throws IOException { byte[] raw = IO.readFully(file); return raw.length > 0 ? raw : null; } catch (FileNotFoundException notFound) { + if (file.exists()) { + throw notFound; + } return null; } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java index 5509fc6a7..29c7f9841 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java @@ -163,6 +163,9 @@ public void load() throws IOException, ConfigInvalidException { hash = newHash; } } catch (FileNotFoundException noFile) { + if (configFile.exists()) { + throw noFile; + } clear(); snapshot = newSnapshot; } catch (IOException e) { From 1020f40813356ffa4b2576f38c749fc0db30ea20 Mon Sep 17 00:00:00 2001 From: Andrey Loskutov Date: Sun, 15 Nov 2015 23:59:41 +0100 Subject: [PATCH 24/50] Null-annotated Repository class and fixed related compiler errors org.eclipse.jgit.lib.Repository class is an example of the API which should be written with Java 8 java.util.Optional type. Unfortunately this API is already released and widely used. The good clients are currently doing their best with checking return values for null and bad clients do not know how bad their code is. I've tried not to change any logic and to be as less intrusive as possible. Most of the JGit code was well prepared to this, only few classes needed some smaller fixes. This change fixes all compiler errors in JGit and replaces possible NPE's with either appropriate exceptions, avoiding multiple "Nullable return" method calls or early returning from the method. Because annotating getDirectory() and getFS() as Nullable would cause lot of additional changes in JGit and EGit they are postponed. Change-Id: Ie8369d2c9c5fac5ce83b3b1b9bc217d7b55502a3 Signed-off-by: Andrey Loskutov --- .../jgit/pgm/internal/CLIText.properties | 1 + .../src/org/eclipse/jgit/pgm/Branch.java | 10 +- .../src/org/eclipse/jgit/pgm/Commit.java | 3 + .../src/org/eclipse/jgit/pgm/Merge.java | 10 +- .../jgit/pgm/debug/DiffAlgorithms.java | 7 +- .../jgit/pgm/debug/RebuildCommitGraph.java | 5 +- .../jgit/pgm/debug/TextHashFunctions.java | 7 +- .../eclipse/jgit/pgm/internal/CLIText.java | 1 + .../eclipse/jgit/internal/JGitText.properties | 2 + .../org/eclipse/jgit/api/CheckoutCommand.java | 6 + .../org/eclipse/jgit/api/RebaseCommand.java | 11 +- .../eclipse/jgit/api/RenameBranchCommand.java | 5 + .../org/eclipse/jgit/internal/JGitText.java | 2 + .../jgit/internal/storage/file/GC.java | 12 +- .../src/org/eclipse/jgit/lib/RefRename.java | 2 +- .../src/org/eclipse/jgit/lib/Repository.java | 112 +++++++++++++++--- .../org/eclipse/jgit/merge/ResolveMerger.java | 5 - .../transport/HMACSHA1NonceGenerator.java | 15 ++- .../jgit/transport/TransportAmazonS3.java | 5 +- .../jgit/transport/TransportGitSsh.java | 6 +- .../eclipse/jgit/util/FS_Win32_Cygwin.java | 3 + 21 files changed, 175 insertions(+), 55 deletions(-) diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties index aa32478ff..8aeb7e875 100644 --- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties +++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties @@ -143,6 +143,7 @@ notAJgitCommand={0} is not a jgit command notARevision=Not a revision: {0} notATree={0} is not a tree notAValidRefName={0} is not a valid ref name +notAValidCommitName={0} is not a valid commit name notAnIndexFile={0} is not an index file notAnObject={0} is not an object notFound=!! NOT FOUND !! diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java index 83a1ca7e2..65aa24f35 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java @@ -154,10 +154,14 @@ protected void run() throws Exception { startBranch = Constants.HEAD; Ref startRef = db.getRef(startBranch); ObjectId startAt = db.resolve(startBranch + "^0"); //$NON-NLS-1$ - if (startRef != null) + if (startRef != null) { startBranch = startRef.getName(); - else + } else if (startAt != null) { startBranch = startAt.name(); + } else { + throw die(MessageFormat.format( + CLIText.get().notAValidCommitName, startBranch)); + } startBranch = Repository.shortenRefName(startBranch); String newRefName = newHead; if (!newRefName.startsWith(Constants.R_HEADS)) @@ -249,7 +253,7 @@ private void delete(boolean force) throws IOException { String current = db.getBranch(); ObjectId head = db.resolve(Constants.HEAD); for (String branch : branches) { - if (current.equals(branch)) { + if (branch.equals(current)) { throw die(MessageFormat.format(CLIText.get().cannotDeleteTheBranchWhichYouAreCurrentlyOn, branch)); } RefUpdate update = db.updateRef((remote ? Constants.R_REMOTES diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java index f18242d68..38d8d70ce 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java @@ -96,6 +96,9 @@ protected void run() throws NoHeadException, NoMessageException, commitCmd.setAmend(amend); commitCmd.setAll(all); Ref head = db.getRef(Constants.HEAD); + if (head == null) { + throw die(CLIText.get().onBranchToBeBorn); + } RevCommit commit; try { commit = commitCmd.call(); diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java index e0ff0583c..cd65af954 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java @@ -120,7 +120,7 @@ protected void run() throws Exception { throw die(MessageFormat.format( CLIText.get().refDoesNotExistOrNoCommit, ref)); - Ref oldHead = db.getRef(Constants.HEAD); + Ref oldHead = getOldHead(); MergeResult result; try (Git git = new Git(db)) { MergeCommand mergeCmd = git.merge().setStrategy(mergeStrategy) @@ -205,6 +205,14 @@ protected void run() throws Exception { } } + private Ref getOldHead() throws IOException { + Ref oldHead = db.getRef(Constants.HEAD); + if (oldHead == null) { + throw die(CLIText.get().onBranchToBeBorn); + } + return oldHead; + } + private boolean isMergedInto(Ref oldHead, AnyObjectId src) throws IOException { try (RevWalk revWalk = new RevWalk(db)) { diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java index df7ebb78b..d85698901 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java @@ -242,9 +242,10 @@ public int compare(Test a, Test b) { } }); - if (db.getDirectory() != null) { - String name = db.getDirectory().getName(); - File parent = db.getDirectory().getParentFile(); + File directory = db.getDirectory(); + if (directory != null) { + String name = directory.getName(); + File parent = directory.getParentFile(); if (name.equals(Constants.DOT_GIT) && parent != null) name = parent.getName(); outw.println(name + ": start at " + startId.name()); diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java index 494055a26..6260cd99f 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java @@ -117,9 +117,12 @@ class RebuildCommitGraph extends TextBuiltin { @Override protected void run() throws Exception { if (!really && !db.getRefDatabase().getRefs(ALL).isEmpty()) { + File directory = db.getDirectory(); + String absolutePath = directory == null ? "null" //$NON-NLS-1$ + : directory.getAbsolutePath(); errw.println( MessageFormat.format(CLIText.get().fatalThisProgramWillDestroyTheRepository - , db.getDirectory().getAbsolutePath(), REALLY)); + , absolutePath, REALLY)); throw die(CLIText.get().needApprovalToDestroyCurrentRepository); } if (!refList.isFile()) diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java index dcbc37bed..887ad08af 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java @@ -341,9 +341,10 @@ private void run(Repository db) throws Exception { } } - if (db.getDirectory() != null) { - String name = db.getDirectory().getName(); - File parent = db.getDirectory().getParentFile(); + File directory = db.getDirectory(); + if (directory != null) { + String name = directory.getName(); + File parent = directory.getParentFile(); if (name.equals(Constants.DOT_GIT) && parent != null) name = parent.getName(); outw.println(name + ":"); //$NON-NLS-1$ diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java index 29d48ebd4..ce2b10c98 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java @@ -202,6 +202,7 @@ public static String formatLine(String line) { /***/ public String notARevision; /***/ public String notATree; /***/ public String notAValidRefName; + /***/ public String notAValidCommitName; /***/ public String notAnIndexFile; /***/ public String notAnObject; /***/ public String notFound; diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index 6e3b3047f..5f80b8103 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -45,6 +45,7 @@ cannotBeCombined=Cannot be combined. cannotBeRecursiveWhenTreesAreIncluded=TreeWalk shouldn't be recursive when tree objects are included. cannotChangeActionOnComment=Cannot change action on comment line in git-rebase-todo file, old action: {0}, new action: {1}. cannotChangeToComment=Cannot change a non-comment line to a comment line. +cannotCheckoutFromUnbornBranch=Cannot checkout from unborn branch cannotCheckoutOursSwitchBranch=Checking out ours/theirs is only possible when checking out index, not when switching branches. cannotCombineSquashWithNoff=Cannot combine --squash with --no-ff. cannotCombineTreeFilterWithRevFilter=Cannot combine TreeFilter {0} with RevFilter {1}. @@ -345,6 +346,7 @@ invalidPathReservedOnWindows=Invalid path (''{0}'' is reserved on Windows): {1} invalidReflogRevision=Invalid reflog revision: {0} invalidRefName=Invalid ref name: {0} invalidRemote=Invalid remote: {0} +invalidRepositoryStateNoHead=Invalid repository --- cannot read HEAD invalidShallowObject=invalid shallow object {0}, expected commit invalidStageForPath=Invalid stage {0} for path {1} invalidTagOption=Invalid tag option: {0} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java index 8d85bfcb1..8743ea9ac 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java @@ -222,6 +222,12 @@ public Ref call() throws GitAPIException, RefAlreadyExistsException, } Ref headRef = repo.getRef(Constants.HEAD); + if (headRef == null) { + // TODO Git CLI supports checkout from unborn branch, we should + // also allow this + throw new UnsupportedOperationException( + JGitText.get().cannotCheckoutFromUnbornBranch); + } String shortHeadRef = getShortBranchName(headRef); String refLogMessage = "checkout: moving from " + shortHeadRef; //$NON-NLS-1$ ObjectId branch; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java index 753bc85bc..8582bbb0d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java @@ -668,12 +668,13 @@ private void writeCurrentCommit(RevCommit commit) throws IOException { } private void writeRewrittenHashes() throws RevisionSyntaxException, - IOException { + IOException, RefNotFoundException { File currentCommitFile = rebaseState.getFile(CURRENT_COMMIT); if (!currentCommitFile.exists()) return; - String head = repo.resolve(Constants.HEAD).getName(); + ObjectId headId = getHead().getObjectId(); + String head = headId.getName(); String currentCommits = rebaseState.readFile(CURRENT_COMMIT); for (String current : currentCommits.split("\n")) //$NON-NLS-1$ RebaseState @@ -743,8 +744,8 @@ private RevCommit doSquashFixup(boolean isSquash, RevCommit commitToPick, private void resetSoftToParent() throws IOException, GitAPIException, CheckoutConflictException { - Ref orig_head = repo.getRef(Constants.ORIG_HEAD); - ObjectId orig_headId = orig_head.getObjectId(); + Ref ref = repo.getRef(Constants.ORIG_HEAD); + ObjectId orig_head = ref == null ? null : ref.getObjectId(); try { // we have already commited the cherry-picked commit. // what we need is to have changes introduced by this @@ -755,7 +756,7 @@ private void resetSoftToParent() throws IOException, } finally { // set ORIG_HEAD back to where we started because soft // reset moved it - repo.writeOrigHead(orig_headId); + repo.writeOrigHead(orig_head); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java index 607253b76..0731dd45e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java @@ -51,6 +51,7 @@ import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.InvalidRefNameException; import org.eclipse.jgit.api.errors.JGitInternalException; +import org.eclipse.jgit.api.errors.NoHeadException; import org.eclipse.jgit.api.errors.RefAlreadyExistsException; import org.eclipse.jgit.api.errors.RefNotFoundException; import org.eclipse.jgit.internal.JGitText; @@ -121,6 +122,10 @@ public Ref call() throws GitAPIException, RefNotFoundException, InvalidRefNameEx fullOldName = ref.getName(); } else { fullOldName = repo.getFullBranch(); + if (fullOldName == null) { + throw new NoHeadException( + JGitText.get().invalidRepositoryStateNoHead); + } if (ObjectId.isId(fullOldName)) throw new DetachedHeadException(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index b03038b06..6680564ad 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -104,6 +104,7 @@ public static JGitText get() { /***/ public String cannotBeRecursiveWhenTreesAreIncluded; /***/ public String cannotChangeActionOnComment; /***/ public String cannotChangeToComment; + /***/ public String cannotCheckoutFromUnbornBranch; /***/ public String cannotCheckoutOursSwitchBranch; /***/ public String cannotCombineSquashWithNoff; /***/ public String cannotCombineTreeFilterWithRevFilter; @@ -411,6 +412,7 @@ public static JGitText get() { /***/ public String invalidURL; /***/ public String invalidWildcards; /***/ public String invalidRefSpec; + /***/ public String invalidRepositoryStateNoHead; /***/ public String invalidWindowSize; /***/ public String isAStaticFlagAndHasNorevWalkInstance; /***/ public String JRELacksMD5Implementation; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java index e7005c247..4c40538b6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java @@ -90,6 +90,7 @@ import org.eclipse.jgit.lib.Ref.Storage; import org.eclipse.jgit.lib.RefDatabase; import org.eclipse.jgit.lib.ReflogEntry; +import org.eclipse.jgit.lib.ReflogReader; import org.eclipse.jgit.revwalk.ObjectWalk; import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevWalk; @@ -592,7 +593,11 @@ public Collection repack() throws IOException { * @throws IOException */ private Set listRefLogObjects(Ref ref, long minTime) throws IOException { - List rlEntries = repo.getReflogReader(ref.getName()) + ReflogReader reflogReader = repo.getReflogReader(ref.getName()); + if (reflogReader == null) { + return Collections.emptySet(); + } + List rlEntries = reflogReader .getReverseEntries(); if (rlEntries == null || rlEntries.isEmpty()) return Collections. emptySet(); @@ -635,10 +640,7 @@ private Map getAllRefs() throws IOException { */ private Set listNonHEADIndexObjects() throws CorruptObjectException, IOException { - try { - if (repo.getIndexFile() == null) - return Collections.emptySet(); - } catch (NoWorkTreeException e) { + if (repo.isBare()) { return Collections.emptySet(); } try (TreeWalk treeWalk = new TreeWalk(repo)) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java index 05eb31183..59f852b8c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java @@ -170,7 +170,7 @@ public Result rename() throws IOException { */ protected boolean needToUpdateHEAD() throws IOException { Ref head = source.getRefDatabase().getRef(Constants.HEAD); - if (head.isSymbolic()) { + if (head != null && head.isSymbolic()) { head = head.getTarget(); return head.getName().equals(source.getName()); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java index 8d512ccb2..c91f2dab7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -64,6 +64,8 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.attributes.AttributesNodeProvider; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.errors.AmbiguousObjectException; @@ -138,6 +140,7 @@ protected Repository(final BaseRepositoryBuilder options) { } /** @return listeners observing only events on this repository. */ + @NonNull public ListenerList getListenerList() { return myListeners; } @@ -182,7 +185,16 @@ public void create() throws IOException { */ public abstract void create(boolean bare) throws IOException; - /** @return local metadata directory; null if repository isn't local. */ + /** + * @return local metadata directory; {@code null} if repository isn't local. + */ + /* + * TODO This method should be annotated as Nullable, because in some + * specific configurations metadata is not located in the local file system + * (for example in memory databases). In "usual" repositories this + * annotation would only cause compiler errors at places where the actual + * directory can never be null. + */ public File getDirectory() { return gitDir; } @@ -190,24 +202,29 @@ public File getDirectory() { /** * @return the object database which stores this repository's data. */ + @NonNull public abstract ObjectDatabase getObjectDatabase(); /** @return a new inserter to create objects in {@link #getObjectDatabase()} */ + @NonNull public ObjectInserter newObjectInserter() { return getObjectDatabase().newInserter(); } /** @return a new reader to read objects from {@link #getObjectDatabase()} */ + @NonNull public ObjectReader newObjectReader() { return getObjectDatabase().newReader(); } /** @return the reference database which stores the reference namespace. */ + @NonNull public abstract RefDatabase getRefDatabase(); /** * @return the configuration of this repository */ + @NonNull public abstract StoredConfig getConfig(); /** @@ -217,11 +234,20 @@ public ObjectReader newObjectReader() { * instance for each use. * @since 4.2 */ + @NonNull public abstract AttributesNodeProvider createAttributesNodeProvider(); /** - * @return the used file system abstraction + * @return the used file system abstraction, or or {@code null} if + * repository isn't local. + */ + /* + * TODO This method should be annotated as Nullable, because in some + * specific configurations metadata is not located in the local file system + * (for example in memory databases). In "usual" repositories this + * annotation would only cause compiler errors at places where the actual + * directory can never be null. */ public FS getFS() { return fs; @@ -255,6 +281,7 @@ public boolean hasObject(AnyObjectId objectId) { * @throws IOException * the object store cannot be accessed. */ + @NonNull public ObjectLoader open(final AnyObjectId objectId) throws MissingObjectException, IOException { return getObjectDatabase().open(objectId); @@ -282,6 +309,7 @@ public ObjectLoader open(final AnyObjectId objectId) * @throws IOException * the object store cannot be accessed. */ + @NonNull public ObjectLoader open(AnyObjectId objectId, int typeHint) throws MissingObjectException, IncorrectObjectTypeException, IOException { @@ -300,6 +328,7 @@ public ObjectLoader open(AnyObjectId objectId, int typeHint) * a symbolic ref was passed in and could not be resolved back * to the base ref, as the symbolic ref could not be read. */ + @NonNull public RefUpdate updateRef(final String ref) throws IOException { return updateRef(ref, false); } @@ -318,6 +347,7 @@ public RefUpdate updateRef(final String ref) throws IOException { * a symbolic ref was passed in and could not be resolved back * to the base ref, as the symbolic ref could not be read. */ + @NonNull public RefUpdate updateRef(final String ref, final boolean detach) throws IOException { return getRefDatabase().newUpdate(ref, detach); } @@ -334,6 +364,7 @@ public RefUpdate updateRef(final String ref, final boolean detach) throws IOExce * the rename could not be performed. * */ + @NonNull public RefRename renameRef(final String fromRef, final String toRef) throws IOException { return getRefDatabase().newRename(fromRef, toRef); } @@ -373,7 +404,8 @@ public RefRename renameRef(final String fromRef, final String toRef) throws IOEx * * @param revstr * A git object references expression - * @return an ObjectId or null if revstr can't be resolved to any ObjectId + * @return an ObjectId or {@code null} if revstr can't be resolved to any + * ObjectId * @throws AmbiguousObjectException * {@code revstr} contains an abbreviated ObjectId and this * repository contains more than one object which match to the @@ -387,6 +419,7 @@ public RefRename renameRef(final String fromRef, final String toRef) throws IOEx * @throws IOException * on serious errors */ + @Nullable public ObjectId resolve(final String revstr) throws AmbiguousObjectException, IncorrectObjectTypeException, RevisionSyntaxException, IOException { @@ -408,10 +441,12 @@ public ObjectId resolve(final String revstr) * expects a branch or revision id. * * @param revstr - * @return object id or ref name from resolved expression + * @return object id or ref name from resolved expression or {@code null} if + * given expression cannot be resolved * @throws AmbiguousObjectException * @throws IOException */ + @Nullable public String simplify(final String revstr) throws AmbiguousObjectException, IOException { try (RevWalk rw = new RevWalk(this)) { @@ -425,6 +460,7 @@ public String simplify(final String revstr) } } + @Nullable private Object resolve(final RevWalk rw, final String revstr) throws IOException { char[] revChars = revstr.toCharArray(); @@ -728,11 +764,13 @@ private static boolean isAllHex(String str, int ptr) { return true; } + @Nullable private RevObject parseSimple(RevWalk rw, String revstr) throws IOException { ObjectId id = resolveSimple(revstr); return id != null ? rw.parseAny(id) : null; } + @Nullable private ObjectId resolveSimple(final String revstr) throws IOException { if (ObjectId.isId(revstr)) return ObjectId.fromString(revstr); @@ -760,6 +798,7 @@ && isAllHex(revstr, dashg + 4)) { return null; } + @Nullable private String resolveReflogCheckout(int checkoutNo) throws IOException { ReflogReader reader = getReflogReader(Constants.HEAD); @@ -801,6 +840,7 @@ private RevCommit resolveReflog(RevWalk rw, Ref ref, String time) return rw.parseCommit(entry.getNewId()); } + @Nullable private ObjectId resolveAbbreviation(final String revstr) throws IOException, AmbiguousObjectException { AbbreviatedObjectId id = AbbreviatedObjectId.fromString(revstr); @@ -837,11 +877,13 @@ protected void doClose() { getRefDatabase().close(); } + @NonNull @SuppressWarnings("nls") public String toString() { String desc; - if (getDirectory() != null) - desc = getDirectory().getPath(); + File directory = getDirectory(); + if (directory != null) + desc = directory.getPath(); else desc = getClass().getSimpleName() + "-" //$NON-NLS-1$ + System.identityHashCode(this); @@ -861,10 +903,12 @@ public String toString() { * current ObjectId in hexadecimal string format. * * @return name of current branch (for example {@code refs/heads/master}), - * an ObjectId in hex format if the current branch is detached, - * or null if the repository is corrupt and has no HEAD reference. + * an ObjectId in hex format if the current branch is detached, or + * {@code null} if the repository is corrupt and has no HEAD + * reference. * @throws IOException */ + @Nullable public String getFullBranch() throws IOException { Ref head = getRef(Constants.HEAD); if (head == null) @@ -883,16 +927,17 @@ public String getFullBranch() throws IOException { * leading prefix {@code refs/heads/} is removed from the reference before * it is returned to the caller. * - * @return name of current branch (for example {@code master}), an - * ObjectId in hex format if the current branch is detached, - * or null if the repository is corrupt and has no HEAD reference. + * @return name of current branch (for example {@code master}), an ObjectId + * in hex format if the current branch is detached, or {@code null} + * if the repository is corrupt and has no HEAD reference. * @throws IOException */ + @Nullable public String getBranch() throws IOException { String name = getFullBranch(); if (name != null) return shortenRefName(name); - return name; + return null; } /** @@ -905,6 +950,7 @@ public String getBranch() throws IOException { * * @return unmodifiable collection of other known objects. */ + @NonNull public Set getAdditionalHaves() { return Collections.emptySet(); } @@ -916,9 +962,10 @@ public Set getAdditionalHaves() { * the name of the ref to lookup. May be a short-hand form, e.g. * "master" which is is automatically expanded to * "refs/heads/master" if "refs/heads/master" already exists. - * @return the Ref with the given name, or null if it does not exist + * @return the Ref with the given name, or {@code null} if it does not exist * @throws IOException */ + @Nullable public Ref getRef(final String name) throws IOException { return getRefDatabase().getRef(name); } @@ -926,6 +973,7 @@ public Ref getRef(final String name) throws IOException { /** * @return mutable map of all known refs (heads, tags, remotes). */ + @NonNull public Map getAllRefs() { try { return getRefDatabase().getRefs(RefDatabase.ALL); @@ -939,6 +987,7 @@ public Map getAllRefs() { * of the entry contains the ref with the full tag name * ("refs/tags/v1.0"). */ + @NonNull public Map getTags() { try { return getRefDatabase().getRefs(Constants.R_TAGS); @@ -960,6 +1009,7 @@ public Map getTags() { * will be true and getPeeledObjectId will contain the peeled object * (or null). */ + @NonNull public Ref peel(final Ref ref) { try { return getRefDatabase().peel(ref); @@ -974,6 +1024,7 @@ public Ref peel(final Ref ref) { /** * @return a map with all objects referenced by a peeled ref. */ + @NonNull public Map> getAllRefsByPeeledObjectId() { Map allRefs = getAllRefs(); Map> ret = new HashMap>(allRefs.size()); @@ -998,11 +1049,13 @@ public Map> getAllRefsByPeeledObjectId() { } /** - * @return the index file location + * @return the index file location or {@code null} if repository isn't + * local. * @throws NoWorkTreeException * if this is bare, which implies it has no working directory. * See {@link #isBare()}. */ + @NonNull public File getIndexFile() throws NoWorkTreeException { if (isBare()) throw new NoWorkTreeException(); @@ -1027,6 +1080,7 @@ public File getIndexFile() throws NoWorkTreeException { * the index file is using a format or extension that this * library does not support. */ + @NonNull public DirCache readDirCache() throws NoWorkTreeException, CorruptObjectException, IOException { return DirCache.read(this); @@ -1051,6 +1105,7 @@ public DirCache readDirCache() throws NoWorkTreeException, * the index file is using a format or extension that this * library does not support. */ + @NonNull public DirCache lockDirCache() throws NoWorkTreeException, CorruptObjectException, IOException { // we want DirCache to inform us so that we can inform registered @@ -1076,6 +1131,7 @@ static byte[] gitInternalSlash(byte[] bytes) { /** * @return an important state */ + @NonNull public RepositoryState getRepositoryState() { if (isBare() || getDirectory() == null) return RepositoryState.BARE; @@ -1218,6 +1274,7 @@ public static boolean isValidRefName(final String refName) { * @return normalized repository relative path or the empty * string if the file is not relative to the work directory. */ + @NonNull public static String stripWorkDir(File workDir, File file) { final String filePath = file.getPath(); final String workDirPath = workDir.getPath(); @@ -1252,6 +1309,7 @@ public boolean isBare() { * if this is bare, which implies it has no working directory. * See {@link #isBare()}. */ + @NonNull public File getWorkTree() throws NoWorkTreeException { if (isBare()) throw new NoWorkTreeException(); @@ -1275,6 +1333,7 @@ public File getWorkTree() throws NoWorkTreeException { * * @return a more user friendly ref name */ + @NonNull public static String shortenRefName(String refName) { if (refName.startsWith(Constants.R_HEADS)) return refName.substring(Constants.R_HEADS.length()); @@ -1290,9 +1349,10 @@ public static String shortenRefName(String refName) { * @return the remote branch name part of refName, i.e. without * the refs/remotes/<remote> prefix, if * refName represents a remote tracking branch; - * otherwise null. + * otherwise {@code null}. * @since 3.4 */ + @Nullable public String shortenRemoteBranchName(String refName) { for (String remote : getRemoteNames()) { String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$ @@ -1307,9 +1367,10 @@ public String shortenRemoteBranchName(String refName) { * @return the remote name part of refName, i.e. without the * refs/remotes/<remote> prefix, if * refName represents a remote tracking branch; - * otherwise null. + * otherwise {@code null}. * @since 3.4 */ + @Nullable public String getRemoteName(String refName) { for (String remote : getRemoteNames()) { String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$ @@ -1321,12 +1382,13 @@ public String getRemoteName(String refName) { /** * @param refName - * @return a {@link ReflogReader} for the supplied refname, or null if the - * named ref does not exist. + * @return a {@link ReflogReader} for the supplied refname, or {@code null} + * if the named ref does not exist. * @throws IOException * the ref could not be accessed. * @since 3.0 */ + @Nullable public abstract ReflogReader getReflogReader(String refName) throws IOException; @@ -1342,6 +1404,7 @@ public abstract ReflogReader getReflogReader(String refName) * if this is bare, which implies it has no working directory. * See {@link #isBare()}. */ + @Nullable public String readMergeCommitMsg() throws IOException, NoWorkTreeException { return readCommitMsgFile(Constants.MERGE_MSG); } @@ -1376,6 +1439,7 @@ public void writeMergeCommitMsg(String msg) throws IOException { * See {@link #isBare()}. * @since 4.0 */ + @Nullable public String readCommitEditMsg() throws IOException, NoWorkTreeException { return readCommitMsgFile(Constants.COMMIT_EDITMSG); } @@ -1410,6 +1474,7 @@ public void writeCommitEditMsg(String msg) throws IOException { * if this is bare, which implies it has no working directory. * See {@link #isBare()}. */ + @Nullable public List readMergeHeads() throws IOException, NoWorkTreeException { if (isBare() || getDirectory() == null) throw new NoWorkTreeException(); @@ -1453,6 +1518,7 @@ public void writeMergeHeads(List heads) throws IOException { * if this is bare, which implies it has no working directory. * See {@link #isBare()}. */ + @Nullable public ObjectId readCherryPickHead() throws IOException, NoWorkTreeException { if (isBare() || getDirectory() == null) @@ -1476,6 +1542,7 @@ public ObjectId readCherryPickHead() throws IOException, * if this is bare, which implies it has no working directory. * See {@link #isBare()}. */ + @Nullable public ObjectId readRevertHead() throws IOException, NoWorkTreeException { if (isBare() || getDirectory() == null) throw new NoWorkTreeException(); @@ -1541,6 +1608,7 @@ public void writeOrigHead(ObjectId head) throws IOException { * if this is bare, which implies it has no working directory. * See {@link #isBare()}. */ + @Nullable public ObjectId readOrigHead() throws IOException, NoWorkTreeException { if (isBare() || getDirectory() == null) throw new NoWorkTreeException(); @@ -1561,6 +1629,7 @@ public ObjectId readOrigHead() throws IOException, NoWorkTreeException { * if this is bare, which implies it has no working directory. * See {@link #isBare()}. */ + @Nullable public String readSquashCommitMsg() throws IOException { return readCommitMsgFile(Constants.SQUASH_MSG); } @@ -1582,6 +1651,7 @@ public void writeSquashCommitMsg(String msg) throws IOException { writeCommitMsg(squashMsgFile, msg); } + @Nullable private String readCommitMsgFile(String msgFilename) throws IOException { if (isBare() || getDirectory() == null) throw new NoWorkTreeException(); @@ -1615,9 +1685,11 @@ private void writeCommitMsg(File msgFile, String msg) throws IOException { * Read a file from the git directory. * * @param filename - * @return the raw contents or null if the file doesn't exist or is empty + * @return the raw contents or {@code null} if the file doesn't exist or is + * empty * @throws IOException */ + @Nullable private byte[] readGitDirectoryFile(String filename) throws IOException { File file = new File(getDirectory(), filename); try { @@ -1674,6 +1746,7 @@ private void writeHeadsFile(List heads, String filename) * @throws IOException * @since 3.2 */ + @NonNull public List readRebaseTodo(String path, boolean includeComments) throws IOException { @@ -1703,6 +1776,7 @@ public void writeRebaseTodoFile(String path, List steps, * @return the names of all known remotes * @since 3.4 */ + @NonNull public Set getRemoteNames() { return getConfig() .getSubsections(ConfigConstants.CONFIG_REMOTE_SECTION); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java index 8a6343c3c..de08e4b6a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java @@ -786,11 +786,6 @@ private void updateIndex(CanonicalTreeParser base, private File writeMergedFile(MergeResult result) throws FileNotFoundException, IOException { File workTree = db.getWorkTree(); - if (workTree == null) - // TODO: This should be handled by WorkingTreeIterators which - // support write operations - throw new UnsupportedOperationException(); - FS fs = db.getFS(); File of = new File(workTree, tw.getPathString()); File parentFolder = of.getParentFile(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java index 7e9434a0f..622680a27 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java @@ -42,6 +42,7 @@ */ package org.eclipse.jgit.transport; +import java.io.File; import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -85,12 +86,16 @@ public HMACSHA1NonceGenerator(String seed) throws IllegalStateException { public synchronized String createNonce(Repository repo, long timestamp) throws IllegalStateException { String path; - if (repo instanceof DfsRepository) + if (repo instanceof DfsRepository) { path = ((DfsRepository) repo).getDescription().getRepositoryName(); - else if (repo.getDirectory() != null) - path = repo.getDirectory().getPath(); - else - throw new IllegalStateException(); + } else { + File directory = repo.getDirectory(); + if (directory != null) { + path = directory.getPath(); + } else { + throw new IllegalStateException(); + } + } String input = path + ":" + String.valueOf(timestamp); //$NON-NLS-1$ byte[] rawHmac; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java index 7729c11ff..23c506b12 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java @@ -148,8 +148,9 @@ public Transport open(URIish uri, Repository local, String remoteName) super(local, uri); Properties props = loadProperties(); - if (!props.containsKey("tmpdir") && local.getDirectory() != null) //$NON-NLS-1$ - props.put("tmpdir", local.getDirectory().getPath()); //$NON-NLS-1$ + File directory = local.getDirectory(); + if (!props.containsKey("tmpdir") && directory != null) //$NON-NLS-1$ + props.put("tmpdir", directory.getPath()); //$NON-NLS-1$ s3 = new AmazonS3(props); bucket = uri.getHost(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java index b27fa0d6b..52f0f0456 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java @@ -46,6 +46,7 @@ package org.eclipse.jgit.transport; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.text.MessageFormat; @@ -235,9 +236,10 @@ public Process exec(String command, int timeout) ProcessBuilder pb = new ProcessBuilder(); pb.command(args); - if (local.getDirectory() != null) + File directory = local.getDirectory(); + if (directory != null) pb.environment().put(Constants.GIT_DIR_KEY, - local.getDirectory().getPath()); + directory.getPath()); try { return pb.start(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java index 28d628b34..ec581b397 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java @@ -168,6 +168,9 @@ public ProcessResult runHookIfPresent(Repository repository, String hookName, @Override public File findHook(Repository repository, String hookName) { final File gitdir = repository.getDirectory(); + if (gitdir == null) { + return null; + } final Path hookPath = gitdir.toPath().resolve(Constants.HOOKS) .resolve(hookName); if (Files.isExecutable(hookPath)) From 830117e761bddc182e4dc57150ca661976868203 Mon Sep 17 00:00:00 2001 From: Andrey Loskutov Date: Wed, 25 Nov 2015 21:19:05 +0100 Subject: [PATCH 25/50] Null-annotated RefDatabase class No other code changes except adding nullness annotations. Change-Id: If2606fb208f6690bd4fd7ad953e709a3ebd6398c Signed-off-by: Andrey Loskutov --- .../src/org/eclipse/jgit/lib/RefDatabase.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java index b62033cbd..986666f2f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java @@ -51,6 +51,9 @@ import java.util.List; import java.util.Map; +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.annotations.Nullable; + /** * Abstraction of name to {@link ObjectId} mapping. *

@@ -132,6 +135,7 @@ public abstract class RefDatabase { * @since 2.3 * @see #isNameConflicting(String) */ + @NonNull public Collection getConflictingNames(String name) throws IOException { Map allRefs = getRefs(ALL); @@ -169,6 +173,7 @@ public Collection getConflictingNames(String name) * @throws IOException * the reference space cannot be accessed. */ + @NonNull public abstract RefUpdate newUpdate(String name, boolean detach) throws IOException; @@ -183,6 +188,7 @@ public abstract RefUpdate newUpdate(String name, boolean detach) * @throws IOException * the reference space cannot be accessed. */ + @NonNull public abstract RefRename newRename(String fromName, String toName) throws IOException; @@ -193,6 +199,7 @@ public abstract RefRename newRename(String fromName, String toName) * * @return a new batch update object. */ + @NonNull public BatchRefUpdate newBatchUpdate() { return new BatchRefUpdate(this); } @@ -223,6 +230,7 @@ public boolean performsAtomicTransactions() { * @throws IOException * the reference space cannot be accessed. */ + @Nullable public abstract Ref getRef(String name) throws IOException; /** @@ -238,6 +246,7 @@ public boolean performsAtomicTransactions() { * the reference space cannot be accessed. * @since 4.1 */ + @Nullable public Ref exactRef(String name) throws IOException { Ref ref = getRef(name); if (ref == null || !name.equals(ref.getName())) { @@ -261,6 +270,7 @@ public Ref exactRef(String name) throws IOException { * the reference space cannot be accessed. * @since 4.1 */ + @NonNull public Map exactRef(String... refs) throws IOException { Map result = new HashMap<>(refs.length); for (String name : refs) { @@ -285,6 +295,7 @@ public Map exactRef(String... refs) throws IOException { * the reference space cannot be accessed. * @since 4.1 */ + @Nullable public Ref firstExactRef(String... refs) throws IOException { for (String name : refs) { Ref ref = exactRef(name); @@ -308,6 +319,7 @@ public Ref firstExactRef(String... refs) throws IOException { * @throws IOException * the reference space cannot be accessed. */ + @NonNull public abstract Map getRefs(String prefix) throws IOException; /** @@ -322,6 +334,7 @@ public Ref firstExactRef(String... refs) throws IOException { * @throws IOException * the reference space cannot be accessed. */ + @NonNull public abstract List getAdditionalRefs() throws IOException; /** @@ -338,10 +351,11 @@ public Ref firstExactRef(String... refs) throws IOException { * @return {@code ref} if {@code ref.isPeeled()} is true; otherwise a new * Ref object representing the same data as Ref, but isPeeled() will * be true and getPeeledObjectId() will contain the peeled object - * (or null). + * (or {@code null}). * @throws IOException * the reference space or object space cannot be accessed. */ + @NonNull public abstract Ref peel(Ref ref) throws IOException; /** @@ -365,9 +379,10 @@ public void refresh() { * @param name * short name of ref to find, e.g. "master" to find * "refs/heads/master" in map. - * @return The first ref matching the name, or null if not found. + * @return The first ref matching the name, or {@code null} if not found. * @since 3.4 */ + @Nullable public static Ref findRef(Map map, String name) { for (String prefix : SEARCH_PATH) { String fullname = prefix + name; From 5be4814e38f2c3983dc27ac6d74f95f2d73ed400 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Fri, 5 Jun 2015 15:20:24 -0700 Subject: [PATCH 26/50] Repository: Introduce exactRef and findRef, deprecate getRef The Repository class provides only one method to look up a ref by name, getRef. If I request refs/heads/master and that ref does not exist, getRef will look further in the search path: ref/refs/heads/master refs/heads/refs/heads/master refs/remotes/refs/heads/master This behavior is counterintuitive, needlessly inexpensive, and usually not what the caller expects. Allow callers to specify whether to use the search path by providing two separate methods: - exactRef, which looks up a ref when its exact name is known - findRef, which looks for a ref along the search path For backward compatibility, keep getRef as a deprecated synonym for findRef. This change introduces findRef and exactRef but does not update callers outside tests to use them yet. Change-Id: I35375d942baeb3ded15520388f8ebb9c0cc86f8c Signed-off-by: Jonathan Nieder --- .../http/test/DumbClientDumbServerTest.java | 4 +- .../http/test/DumbClientSmartServerTest.java | 4 +- .../jgit/http/test/HookMessageTest.java | 4 +- .../http/test/SmartClientSmartServerTest.java | 18 ++-- .../org/eclipse/jgit/pgm/CheckoutTest.java | 3 +- .../tst/org/eclipse/jgit/pgm/ResetTest.java | 8 +- .../tst/org/eclipse/jgit/pgm/StatusTest.java | 5 +- .../eclipse/jgit/api/CheckoutCommandTest.java | 12 ++- .../eclipse/jgit/api/CommitCommandTest.java | 6 +- .../eclipse/jgit/api/MergeCommandTest.java | 54 ++++++---- .../eclipse/jgit/api/NameRevCommandTest.java | 4 +- .../eclipse/jgit/api/ResetCommandTest.java | 6 +- .../jgit/api/StashCreateCommandTest.java | 2 +- .../jgit/api/StashDropCommandTest.java | 102 +++++++++--------- .../internal/storage/file/GcPackRefsTest.java | 38 +++---- .../internal/storage/file/RefUpdateTest.java | 8 +- .../jgit/junit/TestRepositoryTest.java | 40 +++---- .../tst/org/eclipse/jgit/lib/RefTest.java | 24 ++--- .../jgit/merge/MergeMessageFormatterTest.java | 48 ++++----- .../eclipse/jgit/merge/ResolveMergerTest.java | 2 +- .../merge/SquashMessageFormatterTest.java | 2 +- .../jgit/transport/TestProtocolTest.java | 12 +-- .../src/org/eclipse/jgit/lib/Repository.java | 35 ++++++ 23 files changed, 246 insertions(+), 195 deletions(-) diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java index dec9b59f2..362a09d64 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java @@ -209,7 +209,7 @@ public void testInitialClone_Loose() throws Exception { } assertTrue(dst.hasObject(A_txt)); - assertEquals(B, dst.getRef(master).getObjectId()); + assertEquals(B, dst.exactRef(master).getObjectId()); fsck(dst, B); List loose = getRequests(loose(remoteURI, A_txt)); @@ -234,7 +234,7 @@ public void testInitialClone_Packed() throws Exception { } assertTrue(dst.hasObject(A_txt)); - assertEquals(B, dst.getRef(master).getObjectId()); + assertEquals(B, dst.exactRef(master).getObjectId()); fsck(dst, B); List req; diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java index e385b9538..da3a09809 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java @@ -225,7 +225,7 @@ public void testInitialClone_Small() throws Exception { } assertTrue(dst.hasObject(A_txt)); - assertEquals(B, dst.getRef(master).getObjectId()); + assertEquals(B, dst.exactRef(master).getObjectId()); fsck(dst, B); List loose = getRequests(loose(remoteURI, A_txt)); @@ -253,7 +253,7 @@ public void testInitialClone_Packed() throws Exception { } assertTrue(dst.hasObject(A_txt)); - assertEquals(B, dst.getRef(master).getObjectId()); + assertEquals(B, dst.exactRef(master).getObjectId()); fsck(dst, B); List req; diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java index f1056f2d6..d67c8173c 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java @@ -164,8 +164,8 @@ public void testPush_CreateBranch() throws Exception { } assertTrue(remoteRepository.hasObject(Q_txt)); - assertNotNull("has " + dstName, remoteRepository.getRef(dstName)); - assertEquals(Q, remoteRepository.getRef(dstName).getObjectId()); + assertNotNull("has " + dstName, remoteRepository.exactRef(dstName)); + assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId()); fsck(remoteRepository, Q); List requests = getRequests(); diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java index 1b6c552ce..9ca0789e2 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java @@ -296,7 +296,7 @@ public void testInitialClone_Small() throws Exception { } assertTrue(dst.hasObject(A_txt)); - assertEquals(B, dst.getRef(master).getObjectId()); + assertEquals(B, dst.exactRef(master).getObjectId()); fsck(dst, B); List requests = getRequests(); @@ -337,7 +337,7 @@ public void testFetch_FewLocalCommits() throws Exception { } finally { t.close(); } - assertEquals(B, dst.getRepository().getRef(master).getObjectId()); + assertEquals(B, dst.getRepository().exactRef(master).getObjectId()); List cloneRequests = getRequests(); // Only create a few new commits. @@ -358,7 +358,7 @@ public void testFetch_FewLocalCommits() throws Exception { } finally { t.close(); } - assertEquals(Z, dst.getRepository().getRef(master).getObjectId()); + assertEquals(Z, dst.getRepository().exactRef(master).getObjectId()); List requests = getRequests(); requests.removeAll(cloneRequests); @@ -400,7 +400,7 @@ public void testFetch_TooManyLocalCommits() throws Exception { } finally { t.close(); } - assertEquals(B, dst.getRepository().getRef(master).getObjectId()); + assertEquals(B, dst.getRepository().exactRef(master).getObjectId()); List cloneRequests = getRequests(); // Force enough into the local client that enumeration will @@ -424,7 +424,7 @@ public void testFetch_TooManyLocalCommits() throws Exception { } finally { t.close(); } - assertEquals(Z, dst.getRepository().getRef(master).getObjectId()); + assertEquals(Z, dst.getRepository().exactRef(master).getObjectId()); List requests = getRequests(); requests.removeAll(cloneRequests); @@ -579,8 +579,8 @@ public void testPush_CreateBranch() throws Exception { } assertTrue(remoteRepository.hasObject(Q_txt)); - assertNotNull("has " + dstName, remoteRepository.getRef(dstName)); - assertEquals(Q, remoteRepository.getRef(dstName).getObjectId()); + assertNotNull("has " + dstName, remoteRepository.exactRef(dstName)); + assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId()); fsck(remoteRepository, Q); final ReflogReader log = remoteRepository.getReflogReader(dstName); @@ -657,8 +657,8 @@ public void testPush_ChunkedEncoding() throws Exception { } assertTrue(remoteRepository.hasObject(Q_bin)); - assertNotNull("has " + dstName, remoteRepository.getRef(dstName)); - assertEquals(Q, remoteRepository.getRef(dstName).getObjectId()); + assertNotNull("has " + dstName, remoteRepository.exactRef(dstName)); + assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId()); fsck(remoteRepository, Q); List requests = getRequests(); diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java index 7bf4c26c3..939a9f6fd 100644 --- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java +++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java @@ -198,7 +198,8 @@ public void testCheckoutOrphan() throws Exception { assertStringArrayEquals("Switched to a new branch 'new_branch'", execute("git checkout --orphan new_branch")); - assertEquals("refs/heads/new_branch", db.getRef("HEAD").getTarget().getName()); + assertEquals("refs/heads/new_branch", + db.exactRef("HEAD").getTarget().getName()); RevCommit commit = git.commit().setMessage("orphan commit").call(); assertEquals(0, commit.getParentCount()); } diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ResetTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ResetTest.java index 0785c5c5f..dae477928 100644 --- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ResetTest.java +++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ResetTest.java @@ -67,7 +67,7 @@ public void testResetSelf() throws Exception { assertStringArrayEquals("", execute("git reset --hard " + commit.getId().name())); assertEquals(commit.getId(), - git.getRepository().getRef("HEAD").getObjectId()); + git.getRepository().exactRef("HEAD").getObjectId()); } @Test @@ -77,7 +77,7 @@ public void testResetPrevious() throws Exception { assertStringArrayEquals("", execute("git reset --hard " + commit.getId().name())); assertEquals(commit.getId(), - git.getRepository().getRef("HEAD").getObjectId()); + git.getRepository().exactRef("HEAD").getObjectId()); } @Test @@ -86,7 +86,7 @@ public void testResetEmptyPath() throws Exception { assertStringArrayEquals("", execute("git reset --hard " + commit.getId().name() + " --")); assertEquals(commit.getId(), - git.getRepository().getRef("HEAD").getObjectId()); + git.getRepository().exactRef("HEAD").getObjectId()); } @Test @@ -119,7 +119,7 @@ private void resetPath(boolean useDoubleDash) throws Exception { (useDoubleDash) ? " --" : ""); assertStringArrayEquals("", execute(cmd)); assertEquals(commit.getId(), - git.getRepository().getRef("HEAD").getObjectId()); + git.getRepository().exactRef("HEAD").getObjectId()); org.eclipse.jgit.api.Status status = git.status().call(); // assert that file a is unstaged diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java index 793fc7daf..854c52d88 100644 --- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java +++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java @@ -42,13 +42,14 @@ */ package org.eclipse.jgit.pgm; +import static org.eclipse.jgit.lib.Constants.MASTER; +import static org.eclipse.jgit.lib.Constants.R_HEADS; import java.io.IOException; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.CLIRepositoryTestCase; -import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.revwalk.RevCommit; import org.junit.Test; @@ -254,7 +255,7 @@ private void mergeTestBranchInMaster(Git git, RevCommit aCommit) } private void detachHead(Git git) throws IOException, GitAPIException { - String commitId = db.getRef(Constants.MASTER).getObjectId().name(); + String commitId = db.exactRef(R_HEADS + MASTER).getObjectId().name(); git.checkout().setName(commitId).call(); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java index f7a50dffc..82d509f6f 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java @@ -43,6 +43,8 @@ */ package org.eclipse.jgit.api; +import static org.eclipse.jgit.lib.Constants.MASTER; +import static org.eclipse.jgit.lib.Constants.R_HEADS; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; @@ -134,7 +136,7 @@ public void testCheckout() throws Exception { @Test public void testCreateBranchOnCheckout() throws Exception { git.checkout().setCreateBranch(true).setName("test2").call(); - assertNotNull(db.getRef("test2")); + assertNotNull(db.exactRef("refs/heads/test2")); } @Test @@ -237,8 +239,8 @@ public void testCheckoutRemoteTrackingWithUpstream() throws Exception { .setStartPoint("origin/test") .setUpstreamMode(SetupUpstreamMode.TRACK).call(); - assertEquals("refs/heads/test", db2.getRef(Constants.HEAD).getTarget() - .getName()); + assertEquals("refs/heads/test", + db2.exactRef(Constants.HEAD).getTarget().getName()); StoredConfig config = db2.getConfig(); assertEquals("origin", config.getString( ConfigConstants.CONFIG_BRANCH_SECTION, "test", @@ -345,7 +347,7 @@ public void testDetachedHeadOnCheckout() throws JGitInternalException, CheckoutCommand co = git.checkout(); co.setName("master").call(); - String commitId = db.getRef(Constants.MASTER).getObjectId().name(); + String commitId = db.exactRef(R_HEADS + MASTER).getObjectId().name(); co = git.checkout(); co.setName(commitId).call(); @@ -443,7 +445,7 @@ private void assertNoHead() throws IOException { } private void assertHeadDetached() throws IOException { - Ref head = db.getRef(Constants.HEAD); + Ref head = db.exactRef(Constants.HEAD); assertFalse(head.isSymbolic()); assertSame(head, head.getTarget()); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java index 060a5b65c..0d03047d5 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java @@ -408,8 +408,10 @@ public void commitAfterSquashMerge() throws Exception { checkoutBranch("refs/heads/master"); - MergeResult result = git.merge().include(db.getRef("branch1")) - .setSquash(true).call(); + MergeResult result = git.merge() + .include(db.exactRef("refs/heads/branch1")) + .setSquash(true) + .call(); assertTrue(new File(db.getWorkTree(), "file1").exists()); assertTrue(new File(db.getWorkTree(), "file2").exists()); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java index 0c0c6e5b5..cea8393ff 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java @@ -43,6 +43,8 @@ */ package org.eclipse.jgit.api; +import static org.eclipse.jgit.lib.Constants.MASTER; +import static org.eclipse.jgit.lib.Constants.R_HEADS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; @@ -97,7 +99,7 @@ public void testMergeInItself() throws Exception { Git git = new Git(db); git.commit().setMessage("initial commit").call(); - MergeResult result = git.merge().include(db.getRef(Constants.HEAD)).call(); + MergeResult result = git.merge().include(db.exactRef(Constants.HEAD)).call(); assertEquals(MergeResult.MergeStatus.ALREADY_UP_TO_DATE, result.getMergeStatus()); // no reflog entry written by merge assertEquals("commit (initial): initial commit", @@ -115,7 +117,7 @@ public void testAlreadyUpToDate() throws Exception { createBranch(first, "refs/heads/branch1"); RevCommit second = git.commit().setMessage("second commit").call(); - MergeResult result = git.merge().include(db.getRef("refs/heads/branch1")).call(); + MergeResult result = git.merge().include(db.exactRef("refs/heads/branch1")).call(); assertEquals(MergeResult.MergeStatus.ALREADY_UP_TO_DATE, result.getMergeStatus()); assertEquals(second, result.getNewHead()); // no reflog entry written by merge @@ -135,7 +137,7 @@ public void testFastForward() throws Exception { checkoutBranch("refs/heads/branch1"); - MergeResult result = git.merge().include(db.getRef(Constants.MASTER)).call(); + MergeResult result = git.merge().include(db.exactRef(R_HEADS + MASTER)).call(); assertEquals(MergeResult.MergeStatus.FAST_FORWARD, result.getMergeStatus()); assertEquals(second, result.getNewHead()); @@ -155,7 +157,7 @@ public void testFastForwardNoCommit() throws Exception { checkoutBranch("refs/heads/branch1"); - MergeResult result = git.merge().include(db.getRef(Constants.MASTER)) + MergeResult result = git.merge().include(db.exactRef(R_HEADS + MASTER)) .setCommit(false).call(); assertEquals(MergeResult.MergeStatus.FAST_FORWARD, @@ -186,7 +188,7 @@ public void testFastForwardWithFiles() throws Exception { checkoutBranch("refs/heads/branch1"); assertFalse(new File(db.getWorkTree(), "file2").exists()); - MergeResult result = git.merge().include(db.getRef(Constants.MASTER)).call(); + MergeResult result = git.merge().include(db.exactRef(R_HEADS + MASTER)).call(); assertTrue(new File(db.getWorkTree(), "file1").exists()); assertTrue(new File(db.getWorkTree(), "file2").exists()); @@ -221,7 +223,7 @@ public void testMultipleHeads() throws Exception { MergeCommand merge = git.merge(); merge.include(second.getId()); - merge.include(db.getRef(Constants.MASTER)); + merge.include(db.exactRef(R_HEADS + MASTER)); try { merge.call(); fail("Expected exception not thrown when merging multiple heads"); @@ -248,7 +250,7 @@ public void testMergeSuccessAllStrategies(MergeStrategy mergeStrategy) git.commit().setMessage("third").call(); MergeResult result = git.merge().setStrategy(mergeStrategy) - .include(db.getRef(Constants.MASTER)).call(); + .include(db.exactRef(R_HEADS + MASTER)).call(); assertEquals(MergeStatus.MERGED, result.getMergeStatus()); assertEquals( "merge refs/heads/master: Merge made by " @@ -279,9 +281,9 @@ public void testMergeSuccessAllStrategiesNoCommit( MergeResult result = git.merge().setStrategy(mergeStrategy) .setCommit(false) - .include(db.getRef(Constants.MASTER)).call(); + .include(db.exactRef(R_HEADS + MASTER)).call(); assertEquals(MergeStatus.MERGED_NOT_COMMITTED, result.getMergeStatus()); - assertEquals(db.getRef(Constants.HEAD).getTarget().getObjectId(), + assertEquals(db.exactRef(Constants.HEAD).getTarget().getObjectId(), thirdCommit.getId()); } @@ -378,7 +380,7 @@ public void testMergeMessage() throws Exception { git.add().addFilepattern("a").call(); git.commit().setMessage("main").call(); - Ref sideBranch = db.getRef("side"); + Ref sideBranch = db.exactRef("refs/heads/side"); git.merge().include(sideBranch) .setStrategy(MergeStrategy.RESOLVE).call(); @@ -590,7 +592,7 @@ public void testSuccessfulContentMergeNoCommit() throws Exception { .setCommit(false) .setStrategy(MergeStrategy.RESOLVE).call(); assertEquals(MergeStatus.MERGED_NOT_COMMITTED, result.getMergeStatus()); - assertEquals(db.getRef(Constants.HEAD).getTarget().getObjectId(), + assertEquals(db.exactRef(Constants.HEAD).getTarget().getObjectId(), thirdCommit.getId()); assertEquals("1(side)\na\n3(main)\n", read(new File(db.getWorkTree(), @@ -1310,8 +1312,10 @@ public void testSquashFastForward() throws Exception { assertFalse(new File(db.getWorkTree(), "file2").exists()); assertFalse(new File(db.getWorkTree(), "file3").exists()); - MergeResult result = git.merge().include(db.getRef("branch1")) - .setSquash(true).call(); + MergeResult result = git.merge() + .include(db.exactRef("refs/heads/branch1")) + .setSquash(true) + .call(); assertTrue(new File(db.getWorkTree(), "file1").exists()); assertTrue(new File(db.getWorkTree(), "file2").exists()); @@ -1375,8 +1379,10 @@ public void testSquashMerge() throws Exception { assertTrue(new File(db.getWorkTree(), "file2").exists()); assertFalse(new File(db.getWorkTree(), "file3").exists()); - MergeResult result = git.merge().include(db.getRef("branch1")) - .setSquash(true).call(); + MergeResult result = git.merge() + .include(db.exactRef("refs/heads/branch1")) + .setSquash(true) + .call(); assertTrue(new File(db.getWorkTree(), "file1").exists()); assertTrue(new File(db.getWorkTree(), "file2").exists()); @@ -1430,8 +1436,10 @@ public void testSquashMergeConflict() throws Exception { assertTrue(new File(db.getWorkTree(), "file1").exists()); assertTrue(new File(db.getWorkTree(), "file2").exists()); - MergeResult result = git.merge().include(db.getRef("branch1")) - .setSquash(true).call(); + MergeResult result = git.merge() + .include(db.exactRef("refs/heads/branch1")) + .setSquash(true) + .call(); assertTrue(new File(db.getWorkTree(), "file1").exists()); assertTrue(new File(db.getWorkTree(), "file2").exists()); @@ -1468,7 +1476,7 @@ public void testFastForwardOnly() throws Exception { MergeCommand merge = git.merge(); merge.setFastForward(FastForwardMode.FF_ONLY); - merge.include(db.getRef(Constants.MASTER)); + merge.include(db.exactRef(R_HEADS + MASTER)); MergeResult result = merge.call(); assertEquals(MergeStatus.FAST_FORWARD, result.getMergeStatus()); @@ -1485,7 +1493,7 @@ public void testNoFastForward() throws Exception { MergeCommand merge = git.merge(); merge.setFastForward(FastForwardMode.NO_FF); - merge.include(db.getRef(Constants.MASTER)); + merge.include(db.exactRef(R_HEADS + MASTER)); MergeResult result = merge.call(); assertEquals(MergeStatus.MERGED, result.getMergeStatus()); @@ -1505,7 +1513,7 @@ public void testNoFastForwardNoCommit() throws Exception { // when MergeCommand merge = git.merge(); merge.setFastForward(FastForwardMode.NO_FF); - merge.include(db.getRef(Constants.MASTER)); + merge.include(db.exactRef(R_HEADS + MASTER)); merge.setCommit(false); MergeResult result = merge.call(); @@ -1531,7 +1539,7 @@ public void testFastForwardOnlyNotPossible() throws Exception { git.commit().setMessage("second commit on branch1").call(); MergeCommand merge = git.merge(); merge.setFastForward(FastForwardMode.FF_ONLY); - merge.include(db.getRef(Constants.MASTER)); + merge.include(db.exactRef(R_HEADS + MASTER)); MergeResult result = merge.call(); assertEquals(MergeStatus.ABORTED, result.getMergeStatus()); @@ -1588,7 +1596,7 @@ public void testMergeWithMessageOption() throws Exception { git.add().addFilepattern("c").call(); git.commit().setMessage("main").call(); - Ref sideBranch = db.getRef("side"); + Ref sideBranch = db.exactRef("refs/heads/side"); git.merge().include(sideBranch).setStrategy(MergeStrategy.RESOLVE) .setMessage("user message").call(); @@ -1621,7 +1629,7 @@ public void testMergeConflictWithMessageOption() throws Exception { git.add().addFilepattern("a").call(); git.commit().setMessage("main").call(); - Ref sideBranch = db.getRef("side"); + Ref sideBranch = db.exactRef("refs/heads/side"); git.merge().include(sideBranch).setStrategy(MergeStrategy.RESOLVE) .setMessage("user message").call(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java index 491595498..bd62200fc 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java @@ -95,9 +95,9 @@ public void ref() throws Exception { tr.update("refs/heads/master", c); tr.update("refs/tags/tag", c); assertOneResult("master", - git.nameRev().addRef(db.getRef("refs/heads/master")), c); + git.nameRev().addRef(db.exactRef("refs/heads/master")), c); assertOneResult("tag", - git.nameRev().addRef(db.getRef("refs/tags/tag")), c); + git.nameRev().addRef(db.exactRef("refs/tags/tag")), c); } @Test diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java index 9997c8c42..a67f2b912 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java @@ -477,8 +477,10 @@ public void testHardResetAfterSquashMerge() throws Exception { checkoutBranch("refs/heads/master"); - MergeResult result = g.merge().include(db.getRef("branch1")) - .setSquash(true).call(); + MergeResult result = g.merge() + .include(db.exactRef("refs/heads/branch1")) + .setSquash(true) + .call(); assertEquals(MergeResult.MergeStatus.FAST_FORWARD_SQUASHED, result.getMergeStatus()); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java index c317e3bee..ae8551e64 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java @@ -110,7 +110,7 @@ private void validateStashedCommit(final RevCommit commit, int parentCount) throws IOException { assertNotNull(commit); - Ref stashRef = db.getRef(Constants.R_STASH); + Ref stashRef = db.exactRef(Constants.R_STASH); assertNotNull(stashRef); assertEquals(commit, stashRef.getObjectId()); assertNotNull(commit.getAuthorIdent()); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java index cfad817d2..859277e93 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java @@ -96,13 +96,13 @@ public void dropWithNoStashedCommits() throws Exception { @Test public void dropWithInvalidLogIndex() throws Exception { write(committedFile, "content2"); - Ref stashRef = git.getRepository().getRef(Constants.R_STASH); + Ref stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNull(stashRef); RevCommit stashed = git.stashCreate().call(); assertNotNull(stashed); - stashRef = git.getRepository().getRef(Constants.R_STASH); - assertEquals(stashed, git.getRepository().getRef(Constants.R_STASH) - .getObjectId()); + stashRef = git.getRepository().exactRef(Constants.R_STASH); + assertEquals(stashed, + git.getRepository().exactRef(Constants.R_STASH).getObjectId()); try { assertNull(git.stashDrop().setStashRef(100).call()); fail("Exception not thrown"); @@ -115,15 +115,15 @@ public void dropWithInvalidLogIndex() throws Exception { @Test public void dropSingleStashedCommit() throws Exception { write(committedFile, "content2"); - Ref stashRef = git.getRepository().getRef(Constants.R_STASH); + Ref stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNull(stashRef); RevCommit stashed = git.stashCreate().call(); assertNotNull(stashed); - stashRef = git.getRepository().getRef(Constants.R_STASH); - assertEquals(stashed, git.getRepository().getRef(Constants.R_STASH) - .getObjectId()); + stashRef = git.getRepository().exactRef(Constants.R_STASH); + assertEquals(stashed, + git.getRepository().exactRef(Constants.R_STASH).getObjectId()); assertNull(git.stashDrop().call()); - stashRef = git.getRepository().getRef(Constants.R_STASH); + stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNull(stashRef); ReflogReader reader = git.getRepository().getReflogReader( @@ -134,25 +134,25 @@ public void dropSingleStashedCommit() throws Exception { @Test public void dropAll() throws Exception { write(committedFile, "content2"); - Ref stashRef = git.getRepository().getRef(Constants.R_STASH); + Ref stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNull(stashRef); RevCommit firstStash = git.stashCreate().call(); assertNotNull(firstStash); - stashRef = git.getRepository().getRef(Constants.R_STASH); + stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNotNull(stashRef); - assertEquals(firstStash, git.getRepository().getRef(Constants.R_STASH) - .getObjectId()); + assertEquals(firstStash, + git.getRepository().exactRef(Constants.R_STASH).getObjectId()); write(committedFile, "content3"); RevCommit secondStash = git.stashCreate().call(); assertNotNull(secondStash); - stashRef = git.getRepository().getRef(Constants.R_STASH); + stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNotNull(stashRef); - assertEquals(secondStash, git.getRepository().getRef(Constants.R_STASH) - .getObjectId()); + assertEquals(secondStash, + git.getRepository().exactRef(Constants.R_STASH).getObjectId()); assertNull(git.stashDrop().setAll(true).call()); - assertNull(git.getRepository().getRef(Constants.R_STASH)); + assertNull(git.getRepository().exactRef(Constants.R_STASH)); ReflogReader reader = git.getRepository().getReflogReader( Constants.R_STASH); @@ -162,25 +162,25 @@ public void dropAll() throws Exception { @Test public void dropFirstStashedCommit() throws Exception { write(committedFile, "content2"); - Ref stashRef = git.getRepository().getRef(Constants.R_STASH); + Ref stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNull(stashRef); RevCommit firstStash = git.stashCreate().call(); assertNotNull(firstStash); - stashRef = git.getRepository().getRef(Constants.R_STASH); + stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNotNull(stashRef); - assertEquals(firstStash, git.getRepository().getRef(Constants.R_STASH) - .getObjectId()); + assertEquals(firstStash, + git.getRepository().exactRef(Constants.R_STASH).getObjectId()); write(committedFile, "content3"); RevCommit secondStash = git.stashCreate().call(); assertNotNull(secondStash); - stashRef = git.getRepository().getRef(Constants.R_STASH); + stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNotNull(stashRef); - assertEquals(secondStash, git.getRepository().getRef(Constants.R_STASH) - .getObjectId()); + assertEquals(secondStash, + git.getRepository().exactRef(Constants.R_STASH).getObjectId()); assertEquals(firstStash, git.stashDrop().call()); - stashRef = git.getRepository().getRef(Constants.R_STASH); + stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNotNull(stashRef); assertEquals(firstStash, stashRef.getObjectId()); @@ -196,33 +196,33 @@ public void dropFirstStashedCommit() throws Exception { @Test public void dropMiddleStashCommit() throws Exception { write(committedFile, "content2"); - Ref stashRef = git.getRepository().getRef(Constants.R_STASH); + Ref stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNull(stashRef); RevCommit firstStash = git.stashCreate().call(); assertNotNull(firstStash); - stashRef = git.getRepository().getRef(Constants.R_STASH); + stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNotNull(stashRef); - assertEquals(firstStash, git.getRepository().getRef(Constants.R_STASH) - .getObjectId()); + assertEquals(firstStash, + git.getRepository().exactRef(Constants.R_STASH).getObjectId()); write(committedFile, "content3"); RevCommit secondStash = git.stashCreate().call(); assertNotNull(secondStash); - stashRef = git.getRepository().getRef(Constants.R_STASH); + stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNotNull(stashRef); - assertEquals(secondStash, git.getRepository().getRef(Constants.R_STASH) - .getObjectId()); + assertEquals(secondStash, + git.getRepository().exactRef(Constants.R_STASH).getObjectId()); write(committedFile, "content4"); RevCommit thirdStash = git.stashCreate().call(); assertNotNull(thirdStash); - stashRef = git.getRepository().getRef(Constants.R_STASH); + stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNotNull(stashRef); - assertEquals(thirdStash, git.getRepository().getRef(Constants.R_STASH) - .getObjectId()); + assertEquals(thirdStash, + git.getRepository().exactRef(Constants.R_STASH).getObjectId()); assertEquals(thirdStash, git.stashDrop().setStashRef(1).call()); - stashRef = git.getRepository().getRef(Constants.R_STASH); + stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNotNull(stashRef); assertEquals(thirdStash, stashRef.getObjectId()); @@ -241,46 +241,46 @@ public void dropMiddleStashCommit() throws Exception { @Test public void dropBoundaryStashedCommits() throws Exception { write(committedFile, "content2"); - Ref stashRef = git.getRepository().getRef(Constants.R_STASH); + Ref stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNull(stashRef); RevCommit firstStash = git.stashCreate().call(); assertNotNull(firstStash); - stashRef = git.getRepository().getRef(Constants.R_STASH); + stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNotNull(stashRef); - assertEquals(firstStash, git.getRepository().getRef(Constants.R_STASH) - .getObjectId()); + assertEquals(firstStash, + git.getRepository().exactRef(Constants.R_STASH).getObjectId()); write(committedFile, "content3"); RevCommit secondStash = git.stashCreate().call(); assertNotNull(secondStash); - stashRef = git.getRepository().getRef(Constants.R_STASH); + stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNotNull(stashRef); - assertEquals(secondStash, git.getRepository().getRef(Constants.R_STASH) - .getObjectId()); + assertEquals(secondStash, + git.getRepository().exactRef(Constants.R_STASH).getObjectId()); write(committedFile, "content4"); RevCommit thirdStash = git.stashCreate().call(); assertNotNull(thirdStash); - stashRef = git.getRepository().getRef(Constants.R_STASH); + stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNotNull(stashRef); - assertEquals(thirdStash, git.getRepository().getRef(Constants.R_STASH) - .getObjectId()); + assertEquals(thirdStash, + git.getRepository().exactRef(Constants.R_STASH).getObjectId()); write(committedFile, "content5"); RevCommit fourthStash = git.stashCreate().call(); assertNotNull(fourthStash); - stashRef = git.getRepository().getRef(Constants.R_STASH); + stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNotNull(stashRef); - assertEquals(fourthStash, git.getRepository().getRef(Constants.R_STASH) - .getObjectId()); + assertEquals(fourthStash, + git.getRepository().exactRef(Constants.R_STASH).getObjectId()); assertEquals(thirdStash, git.stashDrop().call()); - stashRef = git.getRepository().getRef(Constants.R_STASH); + stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNotNull(stashRef); assertEquals(thirdStash, stashRef.getObjectId()); assertEquals(thirdStash, git.stashDrop().setStashRef(2).call()); - stashRef = git.getRepository().getRef(Constants.R_STASH); + stashRef = git.getRepository().exactRef(Constants.R_STASH); assertNotNull(stashRef); assertEquals(thirdStash, stashRef.getObjectId()); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java index c7336da40..fa40458f3 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java @@ -77,7 +77,7 @@ public void looseRefPacked() throws Exception { tr.lightweightTag("t", a); gc.packRefs(); - assertSame(repo.getRef("t").getStorage(), Storage.PACKED); + assertSame(repo.exactRef("refs/tags/t").getStorage(), Storage.PACKED); } @Test @@ -126,8 +126,8 @@ public void whileRefLockedRefNotPackedNoError() refLock.unlock(); } - assertSame(repo.getRef("refs/tags/t1").getStorage(), Storage.LOOSE); - assertSame(repo.getRef("refs/tags/t2").getStorage(), Storage.PACKED); + assertSame(repo.exactRef("refs/tags/t1").getStorage(), Storage.LOOSE); + assertSame(repo.exactRef("refs/tags/t2").getStorage(), Storage.PACKED); } @Test @@ -146,7 +146,7 @@ public void whileRefUpdatedRefUpdateSucceeds() public Result call() throws Exception { RefUpdate update = new RefDirectoryUpdate( (RefDirectory) repo.getRefDatabase(), - repo.getRef("refs/tags/t")) { + repo.exactRef("refs/tags/t")) { @Override public boolean isForceUpdate() { try { @@ -182,7 +182,7 @@ public Void call() throws Exception { pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); } - assertEquals(repo.getRef("refs/tags/t").getObjectId(), b); + assertEquals(repo.exactRef("refs/tags/t").getObjectId(), b); } @Test @@ -194,23 +194,23 @@ public void dontPackHEAD_nonBare() throws Exception { // check for the unborn branch master. HEAD should point to master and // master doesn't exist. - assertEquals(repo.getRef("HEAD").getTarget().getName(), + assertEquals(repo.exactRef("HEAD").getTarget().getName(), "refs/heads/master"); - assertNull(repo.getRef("HEAD").getTarget().getObjectId()); + assertNull(repo.exactRef("HEAD").getTarget().getObjectId()); gc.packRefs(); - assertSame(repo.getRef("HEAD").getStorage(), Storage.LOOSE); - assertEquals(repo.getRef("HEAD").getTarget().getName(), + assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE); + assertEquals(repo.exactRef("HEAD").getTarget().getName(), "refs/heads/master"); - assertNull(repo.getRef("HEAD").getTarget().getObjectId()); + assertNull(repo.exactRef("HEAD").getTarget().getObjectId()); git.checkout().setName("refs/heads/side").call(); gc.packRefs(); - assertSame(repo.getRef("HEAD").getStorage(), Storage.LOOSE); + assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE); // check for detached HEAD git.checkout().setName(first.getName()).call(); gc.packRefs(); - assertSame(repo.getRef("HEAD").getStorage(), Storage.LOOSE); + assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE); } @Test @@ -229,20 +229,20 @@ public void dontPackHEAD_bare() throws Exception { // check for the unborn branch master. HEAD should point to master and // master doesn't exist. - assertEquals(repo.getRef("HEAD").getTarget().getName(), + assertEquals(repo.exactRef("HEAD").getTarget().getName(), "refs/heads/master"); - assertNull(repo.getRef("HEAD").getTarget().getObjectId()); + assertNull(repo.exactRef("HEAD").getTarget().getObjectId()); gc.packRefs(); - assertSame(repo.getRef("HEAD").getStorage(), Storage.LOOSE); - assertEquals(repo.getRef("HEAD").getTarget().getName(), + assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE); + assertEquals(repo.exactRef("HEAD").getTarget().getName(), "refs/heads/master"); - assertNull(repo.getRef("HEAD").getTarget().getObjectId()); + assertNull(repo.exactRef("HEAD").getTarget().getObjectId()); // check for non-detached HEAD repo.updateRef(Constants.HEAD).link("refs/heads/side"); gc.packRefs(); - assertSame(repo.getRef("HEAD").getStorage(), Storage.LOOSE); - assertEquals(repo.getRef("HEAD").getTarget().getObjectId(), + assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE); + assertEquals(repo.exactRef("HEAD").getTarget().getObjectId(), second.getId()); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java index 098b31fcc..8744ed1bd 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java @@ -347,7 +347,7 @@ public void testUpdateRefDetached() throws Exception { Result update = updateRef.update(); assertEquals(Result.FORCED, update); assertEquals(ppid, db.resolve("HEAD")); - Ref ref = db.getRef("HEAD"); + Ref ref = db.exactRef("HEAD"); assertEquals("HEAD", ref.getName()); assertTrue("is detached", !ref.isSymbolic()); @@ -377,7 +377,7 @@ public void testUpdateRefDetachedUnbornHead() throws Exception { Result update = updateRef.update(); assertEquals(Result.NEW, update); assertEquals(ppid, db.resolve("HEAD")); - Ref ref = db.getRef("HEAD"); + Ref ref = db.exactRef("HEAD"); assertEquals("HEAD", ref.getName()); assertTrue("is detached", !ref.isSymbolic()); @@ -681,13 +681,13 @@ public void testRenameCurrentBranch() throws IOException { public void testRenameBranchAlsoInPack() throws IOException { ObjectId rb = db.resolve("refs/heads/b"); ObjectId rb2 = db.resolve("refs/heads/b~1"); - assertEquals(Ref.Storage.PACKED, db.getRef("refs/heads/b").getStorage()); + assertEquals(Ref.Storage.PACKED, db.exactRef("refs/heads/b").getStorage()); RefUpdate updateRef = db.updateRef("refs/heads/b"); updateRef.setNewObjectId(rb2); updateRef.setForceUpdate(true); Result update = updateRef.update(); assertEquals("internal check new ref is loose", Result.FORCED, update); - assertEquals(Ref.Storage.LOOSE, db.getRef("refs/heads/b").getStorage()); + assertEquals(Ref.Storage.LOOSE, db.exactRef("refs/heads/b").getStorage()); writeReflog(db, rb, "Just a message", "refs/heads/b"); assertTrue("log on old branch", new File(db.getDirectory(), "logs/refs/heads/b").exists()); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java index b69b8e01e..8b54dabce 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java @@ -127,25 +127,25 @@ public void insertExplicitChangeId() throws Exception { @Test public void resetFromSymref() throws Exception { repo.updateRef("HEAD").link("refs/heads/master"); - Ref head = repo.getRef("HEAD"); + Ref head = repo.exactRef("HEAD"); RevCommit master = tr.branch("master").commit().create(); RevCommit branch = tr.branch("branch").commit().create(); RevCommit detached = tr.commit().create(); assertTrue(head.isSymbolic()); assertEquals("refs/heads/master", head.getTarget().getName()); - assertEquals(master, repo.getRef("refs/heads/master").getObjectId()); - assertEquals(branch, repo.getRef("refs/heads/branch").getObjectId()); + assertEquals(master, repo.exactRef("refs/heads/master").getObjectId()); + assertEquals(branch, repo.exactRef("refs/heads/branch").getObjectId()); // Reset to branches preserves symref. tr.reset("master"); - head = repo.getRef("HEAD"); + head = repo.exactRef("HEAD"); assertEquals(master, head.getObjectId()); assertTrue(head.isSymbolic()); assertEquals("refs/heads/master", head.getTarget().getName()); tr.reset("branch"); - head = repo.getRef("HEAD"); + head = repo.exactRef("HEAD"); assertEquals(branch, head.getObjectId()); assertTrue(head.isSymbolic()); assertEquals("refs/heads/master", head.getTarget().getName()); @@ -153,50 +153,50 @@ public void resetFromSymref() throws Exception { // Reset to a SHA-1 detaches. tr.reset(detached); - head = repo.getRef("HEAD"); + head = repo.exactRef("HEAD"); assertEquals(detached, head.getObjectId()); assertFalse(head.isSymbolic()); tr.reset(detached.name()); - head = repo.getRef("HEAD"); + head = repo.exactRef("HEAD"); assertEquals(detached, head.getObjectId()); assertFalse(head.isSymbolic()); // Reset back to a branch remains detached. tr.reset("master"); - head = repo.getRef("HEAD"); + head = repo.exactRef("HEAD"); assertEquals(lastHeadBeforeDetach, head.getObjectId()); assertFalse(head.isSymbolic()); } @Test public void resetFromDetachedHead() throws Exception { - Ref head = repo.getRef("HEAD"); + Ref head = repo.exactRef("HEAD"); RevCommit master = tr.branch("master").commit().create(); RevCommit branch = tr.branch("branch").commit().create(); RevCommit detached = tr.commit().create(); assertNull(head); - assertEquals(master, repo.getRef("refs/heads/master").getObjectId()); - assertEquals(branch, repo.getRef("refs/heads/branch").getObjectId()); + assertEquals(master, repo.exactRef("refs/heads/master").getObjectId()); + assertEquals(branch, repo.exactRef("refs/heads/branch").getObjectId()); tr.reset("master"); - head = repo.getRef("HEAD"); + head = repo.exactRef("HEAD"); assertEquals(master, head.getObjectId()); assertFalse(head.isSymbolic()); tr.reset("branch"); - head = repo.getRef("HEAD"); + head = repo.exactRef("HEAD"); assertEquals(branch, head.getObjectId()); assertFalse(head.isSymbolic()); tr.reset(detached); - head = repo.getRef("HEAD"); + head = repo.exactRef("HEAD"); assertEquals(detached, head.getObjectId()); assertFalse(head.isSymbolic()); tr.reset(detached.name()); - head = repo.getRef("HEAD"); + head = repo.exactRef("HEAD"); assertEquals(detached, head.getObjectId()); assertFalse(head.isSymbolic()); } @@ -222,7 +222,7 @@ public void amendRef() throws Exception { .tick(3) .add("bar", "fixed bar contents") .create(); - assertEquals(amended, repo.getRef("refs/heads/master").getObjectId()); + assertEquals(amended, repo.exactRef("refs/heads/master").getObjectId()); rw.parseBody(amended); assertEquals(1, amended.getParentCount()); @@ -257,7 +257,7 @@ public void amendHead() throws Exception { .add("foo", "fixed foo contents") .create(); - Ref head = repo.getRef(Constants.HEAD); + Ref head = repo.exactRef(Constants.HEAD); assertEquals(amended, head.getObjectId()); assertTrue(head.isSymbolic()); assertEquals("refs/heads/master", head.getTarget().getName()); @@ -291,7 +291,7 @@ public void amendCommit() throws Exception { public void commitToUnbornHead() throws Exception { repo.updateRef("HEAD").link("refs/heads/master"); RevCommit root = tr.branch("HEAD").commit().create(); - Ref ref = repo.getRef(Constants.HEAD); + Ref ref = repo.exactRef(Constants.HEAD); assertEquals(root, ref.getObjectId()); assertTrue(ref.isSymbolic()); assertEquals("refs/heads/master", ref.getTarget().getName()); @@ -316,7 +316,7 @@ public void cherryPick() throws Exception { RevCommit result = tr.cherryPick(toPick); rw.parseBody(result); - Ref headRef = tr.getRepository().getRef("HEAD"); + Ref headRef = tr.getRepository().exactRef("HEAD"); assertEquals(result, headRef.getObjectId()); assertTrue(headRef.isSymbolic()); assertEquals("refs/heads/master", headRef.getLeaf().getName()); @@ -371,7 +371,7 @@ public void cherryPickWithIdenticalContents() throws Exception { .create(); assertNotEquals(head, toPick); assertNull(tr.cherryPick(toPick)); - assertEquals(head, repo.getRef("HEAD").getObjectId()); + assertEquals(head, repo.exactRef("HEAD").getObjectId()); } private String blobAsString(AnyObjectId treeish, String path) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java index 109f40189..707757b34 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java @@ -163,7 +163,7 @@ public void testReadAllIncludingSymrefs() throws Exception { @Test public void testReadSymRefToPacked() throws IOException { writeSymref("HEAD", "refs/heads/b"); - Ref ref = db.getRef("HEAD"); + Ref ref = db.exactRef("HEAD"); assertEquals(Ref.Storage.LOOSE, ref.getStorage()); assertTrue("is symref", ref.isSymbolic()); ref = ref.getTarget(); @@ -181,7 +181,7 @@ public void testReadSymRefToLoosePacked() throws IOException { assertEquals(Result.FORCED, update); // internal writeSymref("HEAD", "refs/heads/master"); - Ref ref = db.getRef("HEAD"); + Ref ref = db.exactRef("HEAD"); assertEquals(Ref.Storage.LOOSE, ref.getStorage()); ref = ref.getTarget(); assertEquals("refs/heads/master", ref.getName()); @@ -194,13 +194,13 @@ public void testReadLooseRef() throws IOException { updateRef.setNewObjectId(db.resolve("refs/heads/master")); Result update = updateRef.update(); assertEquals(Result.NEW, update); - Ref ref = db.getRef("ref/heads/new"); + Ref ref = db.exactRef("ref/heads/new"); assertEquals(Storage.LOOSE, ref.getStorage()); } @Test public void testGetShortRef() throws IOException { - Ref ref = db.getRef("master"); + Ref ref = db.exactRef("refs/heads/master"); assertEquals("refs/heads/master", ref.getName()); assertEquals(db.resolve("refs/heads/master"), ref.getObjectId()); } @@ -222,7 +222,7 @@ public void testRefsUnderRefs() throws IOException { assertNull(db.getRefDatabase().exactRef("refs/foo/bar")); - Ref ref = db.getRef("refs/foo/bar"); + Ref ref = db.findRef("refs/foo/bar"); assertEquals("refs/heads/refs/foo/bar", ref.getName()); assertEquals(db.resolve("refs/heads/master"), ref.getObjectId()); } @@ -237,7 +237,7 @@ public void testAmbiguousRefsUnderRefs() throws IOException { assertEquals("refs/foo/bar", exactRef.getName()); assertEquals(masterId, exactRef.getObjectId()); - Ref ref = db.getRef("refs/foo/bar"); + Ref ref = db.findRef("refs/foo/bar"); assertEquals("refs/foo/bar", ref.getName()); assertEquals(masterId, ref.getObjectId()); } @@ -251,7 +251,7 @@ public void testAmbiguousRefsUnderRefs() throws IOException { @Test public void testReadLoosePackedRef() throws IOException, InterruptedException { - Ref ref = db.getRef("refs/heads/master"); + Ref ref = db.exactRef("refs/heads/master"); assertEquals(Storage.PACKED, ref.getStorage()); FileOutputStream os = new FileOutputStream(new File(db.getDirectory(), "refs/heads/master")); @@ -259,7 +259,7 @@ public void testReadLoosePackedRef() throws IOException, os.write('\n'); os.close(); - ref = db.getRef("refs/heads/master"); + ref = db.exactRef("refs/heads/master"); assertEquals(Storage.LOOSE, ref.getStorage()); } @@ -271,7 +271,7 @@ public void testReadLoosePackedRef() throws IOException, */ @Test public void testReadSimplePackedRefSameRepo() throws IOException { - Ref ref = db.getRef("refs/heads/master"); + Ref ref = db.exactRef("refs/heads/master"); ObjectId pid = db.resolve("refs/heads/master^"); assertEquals(Storage.PACKED, ref.getStorage()); RefUpdate updateRef = db.updateRef("refs/heads/master"); @@ -280,19 +280,19 @@ public void testReadSimplePackedRefSameRepo() throws IOException { Result update = updateRef.update(); assertEquals(Result.FORCED, update); - ref = db.getRef("refs/heads/master"); + ref = db.exactRef("refs/heads/master"); assertEquals(Storage.LOOSE, ref.getStorage()); } @Test public void testResolvedNamesBranch() throws IOException { - Ref ref = db.getRef("a"); + Ref ref = db.findRef("a"); assertEquals("refs/heads/a", ref.getName()); } @Test public void testResolvedSymRef() throws IOException { - Ref ref = db.getRef(Constants.HEAD); + Ref ref = db.exactRef(Constants.HEAD); assertEquals(Constants.HEAD, ref.getName()); assertTrue("is symbolic ref", ref.isSymbolic()); assertSame(Ref.Storage.LOOSE, ref.getStorage()); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java index 21ef7479a..6c90f7d44 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java @@ -84,44 +84,44 @@ public void setUp() throws Exception { @Test public void testOneBranch() throws IOException { - Ref a = db.getRef("refs/heads/a"); - Ref master = db.getRef("refs/heads/master"); + Ref a = db.exactRef("refs/heads/a"); + Ref master = db.exactRef("refs/heads/master"); String message = formatter.format(Arrays.asList(a), master); assertEquals("Merge branch 'a'", message); } @Test public void testTwoBranches() throws IOException { - Ref a = db.getRef("refs/heads/a"); - Ref b = db.getRef("refs/heads/b"); - Ref master = db.getRef("refs/heads/master"); + Ref a = db.exactRef("refs/heads/a"); + Ref b = db.exactRef("refs/heads/b"); + Ref master = db.exactRef("refs/heads/master"); String message = formatter.format(Arrays.asList(a, b), master); assertEquals("Merge branches 'a' and 'b'", message); } @Test public void testThreeBranches() throws IOException { - Ref c = db.getRef("refs/heads/c"); - Ref b = db.getRef("refs/heads/b"); - Ref a = db.getRef("refs/heads/a"); - Ref master = db.getRef("refs/heads/master"); + Ref c = db.exactRef("refs/heads/c"); + Ref b = db.exactRef("refs/heads/b"); + Ref a = db.exactRef("refs/heads/a"); + Ref master = db.exactRef("refs/heads/master"); String message = formatter.format(Arrays.asList(c, b, a), master); assertEquals("Merge branches 'c', 'b' and 'a'", message); } @Test public void testRemoteBranch() throws Exception { - Ref remoteA = db.getRef("refs/remotes/origin/remote-a"); - Ref master = db.getRef("refs/heads/master"); + Ref remoteA = db.exactRef("refs/remotes/origin/remote-a"); + Ref master = db.exactRef("refs/heads/master"); String message = formatter.format(Arrays.asList(remoteA), master); assertEquals("Merge remote-tracking branch 'origin/remote-a'", message); } @Test public void testMixed() throws IOException { - Ref c = db.getRef("refs/heads/c"); - Ref remoteA = db.getRef("refs/remotes/origin/remote-a"); - Ref master = db.getRef("refs/heads/master"); + Ref c = db.exactRef("refs/heads/c"); + Ref remoteA = db.exactRef("refs/remotes/origin/remote-a"); + Ref master = db.exactRef("refs/heads/master"); String message = formatter.format(Arrays.asList(c, remoteA), master); assertEquals("Merge branch 'c', remote-tracking branch 'origin/remote-a'", message); @@ -129,8 +129,8 @@ public void testMixed() throws IOException { @Test public void testTag() throws IOException { - Ref tagA = db.getRef("refs/tags/A"); - Ref master = db.getRef("refs/heads/master"); + Ref tagA = db.exactRef("refs/tags/A"); + Ref master = db.exactRef("refs/heads/master"); String message = formatter.format(Arrays.asList(tagA), master); assertEquals("Merge tag 'A'", message); } @@ -141,7 +141,7 @@ public void testCommit() throws IOException { .fromString("6db9c2ebf75590eef973081736730a9ea169a0c4"); Ref commit = new ObjectIdRef.Unpeeled(Storage.LOOSE, objectId.getName(), objectId); - Ref master = db.getRef("refs/heads/master"); + Ref master = db.exactRef("refs/heads/master"); String message = formatter.format(Arrays.asList(commit), master); assertEquals("Merge commit '6db9c2ebf75590eef973081736730a9ea169a0c4'", message); @@ -154,7 +154,7 @@ public void testPullWithUri() throws IOException { .fromString("6db9c2ebf75590eef973081736730a9ea169a0c4"); Ref remoteBranch = new ObjectIdRef.Unpeeled(Storage.LOOSE, name, objectId); - Ref master = db.getRef("refs/heads/master"); + Ref master = db.exactRef("refs/heads/master"); String message = formatter.format(Arrays.asList(remoteBranch), master); assertEquals("Merge branch 'test' of http://egit.eclipse.org/jgit.git", message); @@ -162,16 +162,16 @@ public void testPullWithUri() throws IOException { @Test public void testIntoOtherThanMaster() throws IOException { - Ref a = db.getRef("refs/heads/a"); - Ref b = db.getRef("refs/heads/b"); + Ref a = db.exactRef("refs/heads/a"); + Ref b = db.exactRef("refs/heads/b"); String message = formatter.format(Arrays.asList(a), b); assertEquals("Merge branch 'a' into b", message); } @Test public void testIntoHeadOtherThanMaster() throws IOException { - Ref a = db.getRef("refs/heads/a"); - Ref b = db.getRef("refs/heads/b"); + Ref a = db.exactRef("refs/heads/a"); + Ref b = db.exactRef("refs/heads/b"); SymbolicRef head = new SymbolicRef("HEAD", b); String message = formatter.format(Arrays.asList(a), head); assertEquals("Merge branch 'a' into b", message); @@ -179,8 +179,8 @@ public void testIntoHeadOtherThanMaster() throws IOException { @Test public void testIntoSymbolicRefHeadPointingToMaster() throws IOException { - Ref a = db.getRef("refs/heads/a"); - Ref master = db.getRef("refs/heads/master"); + Ref a = db.exactRef("refs/heads/a"); + Ref master = db.exactRef("refs/heads/master"); SymbolicRef head = new SymbolicRef("HEAD", master); String message = formatter.format(Arrays.asList(a), head); assertEquals("Merge branch 'a'", message); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java index cd6a4bea2..674619f0d 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java @@ -630,7 +630,7 @@ public void checkMergeCrissCross(MergeStrategy strategy) throws Exception { // ResolveMerge try { MergeResult mergeResult = git.merge().setStrategy(strategy) - .include(git.getRepository().getRef("refs/heads/side")) + .include(git.getRepository().exactRef("refs/heads/side")) .call(); assertEquals(MergeStrategy.RECURSIVE, strategy); assertEquals(MergeResult.MergeStatus.MERGED, diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java index b7b229142..cddbbd528 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java @@ -76,7 +76,7 @@ public void testCommit() throws Exception { Git git = new Git(db); revCommit = git.commit().setMessage("squash_me").call(); - Ref master = db.getRef("refs/heads/master"); + Ref master = db.exactRef("refs/heads/master"); String message = msgFormatter.format(Arrays.asList(revCommit), master); assertEquals( "Squashed commit of the following:\n\ncommit " diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java index 7f865263d..31b64187c 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java @@ -125,7 +125,7 @@ public void testFetch() throws Exception { .setRefSpecs(HEADS) .call(); assertEquals(master, - local.getRepository().getRef("master").getObjectId()); + local.getRepository().exactRef("refs/heads/master").getObjectId()); } } @@ -142,7 +142,7 @@ public void testPush() throws Exception { .setRefSpecs(HEADS) .call(); assertEquals(master, - remote.getRepository().getRef("master").getObjectId()); + remote.getRepository().exactRef("refs/heads/master").getObjectId()); } } @@ -177,7 +177,7 @@ public UploadPack create(User req, Repository db) // Expected. } assertEquals(1, rejected.get()); - assertNull(local.getRepository().getRef("master")); + assertNull(local.getRepository().exactRef("refs/heads/master")); git.fetch() .setRemote(user2Uri.toString()) @@ -185,7 +185,7 @@ public UploadPack create(User req, Repository db) .call(); assertEquals(1, rejected.get()); assertEquals(master, - local.getRepository().getRef("master").getObjectId()); + local.getRepository().exactRef("refs/heads/master").getObjectId()); } } @@ -222,7 +222,7 @@ public ReceivePack create(User req, Repository db) JGitText.get().pushNotPermitted)); } assertEquals(1, rejected.get()); - assertNull(remote.getRepository().getRef("master")); + assertNull(remote.getRepository().exactRef("refs/heads/master")); git.push() .setRemote(user2Uri.toString()) @@ -230,7 +230,7 @@ public ReceivePack create(User req, Repository db) .call(); assertEquals(1, rejected.get()); assertEquals(master, - remote.getRepository().getRef("master").getObjectId()); + remote.getRepository().exactRef("refs/heads/master").getObjectId()); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java index c91f2dab7..49a970d03 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -964,9 +964,44 @@ public Set getAdditionalHaves() { * "refs/heads/master" if "refs/heads/master" already exists. * @return the Ref with the given name, or {@code null} if it does not exist * @throws IOException + * @deprecated Use {@link #exactRef(String)} or {@link #findRef(String)} + * instead. */ + @Deprecated @Nullable public Ref getRef(final String name) throws IOException { + return findRef(name); + } + + /** + * Get a ref by name. + * + * @param name + * the name of the ref to lookup. Must not be a short-hand + * form; e.g., "master" is not automatically expanded to + * "refs/heads/master". + * @return the Ref with the given name, or {@code null} if it does not exist + * @throws IOException + * @since 4.2 + */ + @Nullable + public Ref exactRef(String name) throws IOException { + return getRefDatabase().exactRef(name); + } + + /** + * Search for a ref by (possibly abbreviated) name. + * + * @param name + * the name of the ref to lookup. May be a short-hand form, e.g. + * "master" which is is automatically expanded to + * "refs/heads/master" if "refs/heads/master" already exists. + * @return the Ref with the given name, or {@code null} if it does not exist + * @throws IOException + * @since 4.2 + */ + @Nullable + public Ref findRef(String name) throws IOException { return getRefDatabase().getRef(name); } From fe98218e0df85012cb9b901c71c44f232f65cb3a Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Thu, 19 Nov 2015 12:47:18 -0800 Subject: [PATCH 27/50] repo: Do not use search path to find refs/remotes/origin/ When running from a non-bare repository, "jgit repo" checks whether the rev passed in is a sha1 or branch name and in the latter case will check out origin/. We are expecting refs/remotes/origin/, but as a side effect of using getRef we also end up looking for refs/origin/, refs/heads/origin/, and so on. Avoid that by using exactRef instead. Signed-off-by: Jonathan Nieder Change-Id: I670b2e48a88138a1f2104ea201baa958e9edbddb --- .../src/org/eclipse/jgit/gitrepo/RepoCommand.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java index 1d2d3bfaa..ff9f233aa 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java @@ -42,6 +42,9 @@ */ package org.eclipse.jgit.gitrepo; +import static org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME; +import static org.eclipse.jgit.lib.Constants.R_REMOTES; + import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -574,7 +577,7 @@ private void addSubmodule(String url, String name, String revision, private static String findRef(String ref, Repository repo) throws IOException { if (!ObjectId.isId(ref)) { - Ref r = repo.getRef(Constants.DEFAULT_REMOTE_NAME + "/" + ref); //$NON-NLS-1$ + Ref r = repo.exactRef(R_REMOTES + DEFAULT_REMOTE_NAME + "/" + ref); //$NON-NLS-1$ if (r != null) return r.getName(); } From 55fb18feaa096b47acebbc8fb8c37942d68f49d5 Mon Sep 17 00:00:00 2001 From: Christian Halstrick Date: Tue, 24 Nov 2015 11:18:59 +0100 Subject: [PATCH 28/50] Fix FS.runProcess() to close the InputStream When FS.runProcess was called and an InputStream was given the method tried to pump the whole InputStream to the process. When the method ended the InputStream was not giving any data anymore. Consequently close the InputStream inside the method. Change-Id: I0ed738a775e5c977b21447d195acee1ecf5e2cb9 --- org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java index b61e47f5f..253acbb76 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java @@ -949,9 +949,7 @@ public int runProcess(ProcessBuilder processBuilder, * @param outRedirect * An OutputStream on which to redirect the processes stdout. Can * be null, in which case the processes standard - * output will be lost. If binary is set to false - * then it is expected that the process emits text data which - * should be processed line by line. + * output will be lost. * @param errRedirect * An OutputStream on which to redirect the processes stderr. Can * be null, in which case the processes standard @@ -959,9 +957,9 @@ public int runProcess(ProcessBuilder processBuilder, * @param inRedirect * An InputStream from which to redirect the processes stdin. Can * be null, in which case the process doesn't get - * any data over stdin. If binary is set to - * false then it is expected that the process - * expects text data which should be processed line by line. + * any data over stdin. It is assumed that the whole InputStream + * will be consumed by the process. The method will close the + * inputstream after all bytes are read. * @return the return code of this process. * @throws IOException * if an I/O error occurs while executing this process. @@ -1011,6 +1009,9 @@ public int runProcess(ProcessBuilder processBuilder, // A process doesn't clean its own resources even when destroyed // Explicitly try and close all three streams, preserving the // outer I/O exception if any. + if (inRedirect != null) { + inRedirect.close(); + } try { process.getErrorStream().close(); } catch (IOException e) { From 5c8f2d8feb60ddf380c46080ff290936b18f6277 Mon Sep 17 00:00:00 2001 From: Christian Halstrick Date: Fri, 20 Nov 2015 14:38:24 +0100 Subject: [PATCH 29/50] Fix HookTest when running on Win32_Cygwin This test expected that the test scripts emit a platform-dependent newline (crlf on windows, lf on linux). But that's not true. Expected result should always be a trailing "\n" because the test scripts explicitly echo a "\n" in the end. Change-Id: I604e08cda8cebe276b5214ba0f618b6112c3441f --- .../tst/org/eclipse/jgit/util/HookTest.java | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java index 8aa14c521..e07076e32 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java @@ -92,11 +92,9 @@ public void testFailedCommitMsgHookBlocksCommit() throws Exception { fail("expected commit-msg hook to abort commit"); } catch (AbortedByHookException e) { assertEquals("unexpected error message from commit-msg hook", - "Rejected by \"commit-msg\" hook.\nstderr" - + System.lineSeparator(), + "Rejected by \"commit-msg\" hook.\nstderr\n", e.getMessage()); - assertEquals("unexpected output from commit-msg hook", - "test" + System.lineSeparator(), + assertEquals("unexpected output from commit-msg hook", "test\n", out.toString()); } } @@ -114,7 +112,7 @@ public void testCommitMsgHookReceivesCorrectParameter() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); git.commit().setMessage("commit") .setHookOutputStream(new PrintStream(out)).call(); - assertEquals(".git/COMMIT_EDITMSG" + System.lineSeparator(), + assertEquals(".git/COMMIT_EDITMSG\n", out.toString("UTF-8")); } @@ -147,11 +145,10 @@ public void testRunHook() throws Exception { new String[] { "arg1", "arg2" }, new PrintStream(out), new PrintStream(err), "stdin"); - assertEquals("unexpected hook output", "test arg1 arg2" - + System.lineSeparator() + "stdin" + System.lineSeparator(), + + assertEquals("unexpected hook output", "test arg1 arg2\nstdin\n", out.toString("UTF-8")); - assertEquals("unexpected output on stderr stream", - "stderr" + System.lineSeparator(), + assertEquals("unexpected output on stderr stream", "stderr\n", err.toString("UTF-8")); assertEquals("unexpected exit code", 0, res.getExitCode()); assertEquals("unexpected process status", ProcessResult.Status.OK, @@ -175,11 +172,9 @@ public void testFailedPreCommitHookBlockCommit() throws Exception { fail("expected pre-commit hook to abort commit"); } catch (AbortedByHookException e) { assertEquals("unexpected error message from pre-commit hook", - "Rejected by \"pre-commit\" hook.\nstderr" - + System.lineSeparator(), + "Rejected by \"pre-commit\" hook.\nstderr\n", e.getMessage()); - assertEquals("unexpected output from pre-commit hook", - "test" + System.lineSeparator(), + assertEquals("unexpected output from pre-commit hook", "test\n", out.toString()); } } From 2cdc130dfc06179acce47a57cfa9562290598b64 Mon Sep 17 00:00:00 2001 From: Matthias Sohn Date: Thu, 26 Nov 2015 00:32:37 +0100 Subject: [PATCH 30/50] Add missing @since tags and missing javadoc Change-Id: I8575797127fc96abea8af56f019ca39f5897486f Signed-off-by: Matthias Sohn --- .../src/org/eclipse/jgit/junit/JGitTestUtil.java | 11 +++++++++++ .../jgit/junit/LocalDiskRepositoryTestCase.java | 5 ++++- .../src/org/eclipse/jgit/junit/MockSystemReader.java | 1 + .../org/eclipse/jgit/junit/RepositoryTestCase.java | 11 +++++++++++ .../src/org/eclipse/jgit/junit/TestRepository.java | 6 +++++- .../src/org/eclipse/jgit/util/FileUtils.java | 4 +++- 6 files changed, 35 insertions(+), 3 deletions(-) diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java index 521593ea8..2962e7192 100644 --- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java +++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java @@ -241,6 +241,17 @@ public static void deleteTrashFile(final Repository db, FileUtils.delete(path); } + /** + * @param db + * the repository + * @param link + * the path of the symbolic link to create + * @param target + * the target of the symbolic link + * @return the path to the symbolic link + * @throws Exception + * @since 4.2 + */ public static Path writeLink(Repository db, String link, String target) throws Exception { return FileUtils.createSymLink(new File(db.getWorkTree(), link), diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java index bb5f9efb8..4d713b5e7 100644 --- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java +++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java @@ -91,7 +91,10 @@ public abstract class LocalDiskRepositoryTestCase { /** A fake (but stable) identity for committer fields in the test. */ protected PersonIdent committer; - /** A {@link SystemReader} used to coordinate time, envars, etc. */ + /** + * A {@link SystemReader} used to coordinate time, envars, etc. + * @since 4.2 + */ protected MockSystemReader mockSystemReader; private final List toClose = new ArrayList(); diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java index 03a2b1a58..28a95569e 100644 --- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java +++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java @@ -151,6 +151,7 @@ public long getCurrentTime() { * * @param secDelta * number of seconds to add to the current time. + * @since 4.2 */ public void tick(final int secDelta) { now += secDelta * 1000L; diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java index 28c61778c..56962e869 100644 --- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java +++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java @@ -108,6 +108,17 @@ protected File writeTrashFile(final String name, final String data) return JGitTestUtil.writeTrashFile(db, name, data); } + /** + * Create a symbolic link + * + * @param link + * the path of the symbolic link to create + * @param target + * the target of the symbolic link + * @return the path to the symbolic link + * @throws Exception + * @since 4.2 + */ protected Path writeLink(final String link, final String target) throws Exception { return JGitTestUtil.writeLink(db, link, target); diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java index 251e65f55..ac9685d37 100644 --- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java +++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java @@ -175,6 +175,7 @@ public TestRepository(R db, RevWalk rw) throws IOException { * the MockSystemReader to use for clock and other system * operations. * @throws IOException + * @since 4.2 */ public TestRepository(R db, RevWalk rw, MockSystemReader reader) throws IOException { @@ -203,7 +204,10 @@ public Git git() { return git; } - /** @return current date. */ + /** + * @return current date. + * @since 4.2 + */ public Date getDate() { return new Date(mockSystemReader.getCurrentTime()); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java index 6d0318c02..727ea79cc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java @@ -398,8 +398,10 @@ public static void createNewFile(File f) throws IOException { * Create a symbolic link * * @param path + * the path of the symbolic link to create * @param target - * @return path to the created link + * the target of the symbolic link + * @return the path to the symbolic link * @throws IOException * @since 4.2 */ From adbe9006831a5d03a3513f4a09dbda8703e19f7c Mon Sep 17 00:00:00 2001 From: Christian Halstrick Date: Tue, 24 Nov 2015 15:16:42 +0100 Subject: [PATCH 31/50] Fix unit tests on Windows PushCommandTest and RunExternalScriptTest didn't succeed on Windows. Fix this by expecting a simple line-feed as line ending (instead of the platform dependent line separator. Additionally correct the computation of expected URLs in PushCommandTest. Change-Id: Idcdc41cd7e535ff88df33ea0a249333ed8fc91b0 Signed-off-by: Matthias Sohn --- .../org/eclipse/jgit/api/PushCommandTest.java | 8 ++--- .../jgit/util/RunExternalScriptTest.java | 35 ++++++++++--------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java index ccf1a51f1..1fcfef9c7 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java @@ -138,11 +138,9 @@ public void testPrePushHook() throws JGitInternalException, IOException, RefSpec spec = new RefSpec("refs/heads/master:refs/heads/x"); git1.push().setRemote("test").setRefSpecs(spec).call(); - assertEquals( - "1:test, 2:file://" + db2.getDirectory().toPath() // - + "/, 3:\n" + "refs/heads/master " + commit.getName() - + " refs/heads/x " + ObjectId.zeroId().name(), - read(hookOutput)); + assertEquals("1:test, 2:" + uri + ", 3:\n" + "refs/heads/master " + + commit.getName() + " refs/heads/x " + + ObjectId.zeroId().name(), read(hookOutput)); } private File writeHookFile(final String name, final String data) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java index b6a2519c5..7c0985ef4 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java @@ -57,12 +57,12 @@ import org.junit.Test; public class RunExternalScriptTest { + private static final String LF = "\n"; + private ByteArrayOutputStream out; private ByteArrayOutputStream err; - private String sep = System.getProperty("line.separator"); - @Before public void setUp() throws Exception { out = new ByteArrayOutputStream(); @@ -74,7 +74,7 @@ public void testCopyStdIn() throws IOException, InterruptedException { String inputStr = "a\nb\rc\r\nd"; File script = writeTempFile("cat -"); int rc = FS.DETECTED.runProcess( - new ProcessBuilder("/bin/sh", script.getPath()), out, err, + new ProcessBuilder("sh", script.getPath()), out, err, new ByteArrayInputStream(inputStr.getBytes())); assertEquals(0, rc); assertEquals(inputStr, new String(out.toByteArray())); @@ -85,7 +85,7 @@ public void testCopyStdIn() throws IOException, InterruptedException { public void testCopyNullStdIn() throws IOException, InterruptedException { File script = writeTempFile("cat -"); int rc = FS.DETECTED.runProcess( - new ProcessBuilder("/bin/sh", script.getPath()), out, err, + new ProcessBuilder("sh", script.getPath()), out, err, (InputStream) null); assertEquals(0, rc); assertEquals("", new String(out.toByteArray())); @@ -95,7 +95,8 @@ public void testCopyNullStdIn() throws IOException, InterruptedException { @Test public void testArguments() throws IOException, InterruptedException { File script = writeTempFile("echo $#,$1,$2,$3,$4,$5,$6"); - int rc = FS.DETECTED.runProcess(new ProcessBuilder("/bin/bash", + int rc = FS.DETECTED.runProcess( + new ProcessBuilder("sh", script.getPath(), "a", "b", "c"), out, err, (InputStream) null); assertEquals(0, rc); assertEquals("3,a,b,c,,,\n", new String(out.toByteArray())); @@ -106,7 +107,7 @@ public void testArguments() throws IOException, InterruptedException { public void testRc() throws IOException, InterruptedException { File script = writeTempFile("exit 3"); int rc = FS.DETECTED.runProcess( - new ProcessBuilder("/bin/sh", script.getPath(), "a", "b", "c"), + new ProcessBuilder("sh", script.getPath(), "a", "b", "c"), out, err, (InputStream) null); assertEquals(3, rc); assertEquals("", new String(out.toByteArray())); @@ -117,7 +118,7 @@ public void testRc() throws IOException, InterruptedException { public void testNullStdout() throws IOException, InterruptedException { File script = writeTempFile("echo hi"); int rc = FS.DETECTED.runProcess( - new ProcessBuilder("/bin/sh", script.getPath()), null, err, + new ProcessBuilder("sh", script.getPath()), null, err, (InputStream) null); assertEquals(0, rc); assertEquals("", new String(out.toByteArray())); @@ -128,11 +129,11 @@ public void testNullStdout() throws IOException, InterruptedException { public void testStdErr() throws IOException, InterruptedException { File script = writeTempFile("echo hi >&2"); int rc = FS.DETECTED.runProcess( - new ProcessBuilder("/bin/sh", script.getPath()), null, err, + new ProcessBuilder("sh", script.getPath()), null, err, (InputStream) null); assertEquals(0, rc); assertEquals("", new String(out.toByteArray())); - assertEquals("hi" + sep, new String(err.toByteArray())); + assertEquals("hi" + LF, new String(err.toByteArray())); } @Test @@ -140,11 +141,11 @@ public void testAllTogetherBin() throws IOException, InterruptedException { String inputStr = "a\nb\rc\r\nd"; File script = writeTempFile("echo $#,$1,$2,$3,$4,$5,$6 >&2 ; cat -; exit 5"); int rc = FS.DETECTED.runProcess( - new ProcessBuilder("/bin/sh", script.getPath(), "a", "b", "c"), + new ProcessBuilder("sh", script.getPath(), "a", "b", "c"), out, err, new ByteArrayInputStream(inputStr.getBytes())); assertEquals(5, rc); assertEquals(inputStr, new String(out.toByteArray())); - assertEquals("3,a,b,c,,," + sep, new String(err.toByteArray())); + assertEquals("3,a,b,c,,," + LF, new String(err.toByteArray())); } @Test(expected = IOException.class) @@ -159,7 +160,7 @@ public void testWrongSh() throws IOException, InterruptedException { public void testWrongScript() throws IOException, InterruptedException { File script = writeTempFile("cat-foo -"); int rc = FS.DETECTED.runProcess( - new ProcessBuilder("/bin/sh", script.getPath(), "a", "b", "c"), + new ProcessBuilder("sh", script.getPath(), "a", "b", "c"), out, err, (InputStream) null); assertEquals(127, rc); } @@ -169,7 +170,7 @@ public void testCopyStdInExecute() throws IOException, InterruptedException { String inputStr = "a\nb\rc\r\nd"; File script = writeTempFile("cat -"); - ProcessBuilder pb = new ProcessBuilder("/bin/sh", script.getPath()); + ProcessBuilder pb = new ProcessBuilder("sh", script.getPath()); ExecutionResult res = FS.DETECTED.execute(pb, new ByteArrayInputStream(inputStr.getBytes())); assertEquals(0, res.getRc()); @@ -180,11 +181,11 @@ public void testCopyStdInExecute() @Test public void testStdErrExecute() throws IOException, InterruptedException { File script = writeTempFile("echo hi >&2"); - ProcessBuilder pb = new ProcessBuilder("/bin/sh", script.getPath()); + ProcessBuilder pb = new ProcessBuilder("sh", script.getPath()); ExecutionResult res = FS.DETECTED.execute(pb, null); assertEquals(0, res.getRc()); assertEquals("", new String(res.getStdout().toByteArray())); - assertEquals("hi" + sep, new String(res.getStderr().toByteArray())); + assertEquals("hi" + LF, new String(res.getStderr().toByteArray())); } @Test @@ -193,13 +194,13 @@ public void testAllTogetherBinExecute() String inputStr = "a\nb\rc\r\nd"; File script = writeTempFile( "echo $#,$1,$2,$3,$4,$5,$6 >&2 ; cat -; exit 5"); - ProcessBuilder pb = new ProcessBuilder("/bin/sh", script.getPath(), "a", + ProcessBuilder pb = new ProcessBuilder("sh", script.getPath(), "a", "b", "c"); ExecutionResult res = FS.DETECTED.execute(pb, new ByteArrayInputStream(inputStr.getBytes())); assertEquals(5, res.getRc()); assertEquals(inputStr, new String(res.getStdout().toByteArray())); - assertEquals("3,a,b,c,,," + sep, + assertEquals("3,a,b,c,,," + LF, new String(res.getStderr().toByteArray())); } From 75697adc5a0024449351aacac89618c3b83add11 Mon Sep 17 00:00:00 2001 From: Ivan Motsch Date: Tue, 17 Nov 2015 13:32:20 +0100 Subject: [PATCH 32/50] Add the new class Attributes holding multiple Attribute(s) Attributes represents a semantic collector of Attribute(s) and replaces the anonymous Map. This class will be returned by TreeWalk.getAttributes(). It offers convenient access to the attributes wrapped in the Attributes object. Adds preparations for a future Attribute Macro Expansion Change-Id: I8348c8c457a2a7f1f0c48050e10399b0fa1cdbe1 Signed-off-by: Ivan Motsch --- .../AttributesNodeDirCacheIteratorTest.java | 10 +- .../jgit/attributes/AttributesNodeTest.java | 27 +-- ...AttributesNodeWorkingTreeIteratorTest.java | 10 +- .../attributes/TreeWalkAttributeTest.java | 5 +- org.eclipse.jgit/.settings/.api_filters | 8 + .../eclipse/jgit/attributes/Attribute.java | 16 +- .../eclipse/jgit/attributes/Attributes.java | 202 ++++++++++++++++++ .../jgit/attributes/AttributesNode.java | 12 +- .../jgit/attributes/AttributesProvider.java | 6 +- .../jgit/attributes/AttributesRule.java | 19 ++ .../storage/file/InfoAttributesNode.java | 3 +- .../src/org/eclipse/jgit/lib/Constants.java | 7 + .../org/eclipse/jgit/treewalk/TreeWalk.java | 41 ++-- 13 files changed, 306 insertions(+), 60 deletions(-) create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java index 3e6ca6220..0e595e61f 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java @@ -52,9 +52,7 @@ import java.io.IOException; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.attributes.Attribute.State; @@ -258,12 +256,14 @@ private void assertAttributesNode(String pathName, assertTrue(nodeAttrs == null || nodeAttrs.isEmpty()); else { - Map entryAttributes = new LinkedHashMap(); - attributesNode.getAttributes(pathName, false, entryAttributes); + Attributes entryAttributes = new Attributes(); + attributesNode.getAttributes(pathName, + false, entryAttributes); if (nodeAttrs != null && !nodeAttrs.isEmpty()) { for (Attribute attribute : nodeAttrs) { - assertThat(entryAttributes.values(), hasItem(attribute)); + assertThat(entryAttributes.getAll(), + hasItem(attribute)); } } else { assertTrue( diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java index d82baaa36..d478a7cf0 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java @@ -49,10 +49,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; import org.junit.After; import org.junit.Test; @@ -104,8 +100,8 @@ public void testNegativePattern() throws IOException { is = new ByteArrayInputStream(attributeFileContent.getBytes()); AttributesNode node = new AttributesNode(); node.parse(is); - assertAttribute("file.type1", node, Collections. emptySet()); - assertAttribute("file.type2", node, Collections. emptySet()); + assertAttribute("file.type1", node, new Attributes()); + assertAttribute("file.type2", node, new Attributes()); } @Test @@ -115,7 +111,7 @@ public void testEmptyNegativeAttributeKey() throws IOException { is = new ByteArrayInputStream(attributeFileContent.getBytes()); AttributesNode node = new AttributesNode(); node.parse(is); - assertAttribute("file.type1", node, Collections. emptySet()); + assertAttribute("file.type1", node, new Attributes()); assertAttribute("file.type2", node, asSet(A_UNSET_ATTR)); } @@ -127,8 +123,8 @@ public void testEmptyValueKey() throws IOException { is = new ByteArrayInputStream(attributeFileContent.getBytes()); AttributesNode node = new AttributesNode(); node.parse(is); - assertAttribute("file.type1", node, Collections. emptySet()); - assertAttribute("file.type2", node, Collections. emptySet()); + assertAttribute("file.type1", node, new Attributes()); + assertAttribute("file.type2", node, new Attributes()); assertAttribute("file.type3", node, asSet(new Attribute("attr", ""))); } @@ -166,17 +162,14 @@ public void testTabSeparator() throws IOException { } private void assertAttribute(String path, AttributesNode node, - Set attrs) { - HashMap attributes = new HashMap(); + Attributes attrs) { + Attributes attributes = new Attributes(); node.getAttributes(path, false, attributes); - assertEquals(attrs, new HashSet(attributes.values())); + assertEquals(attrs, attributes); } - static Set asSet(Attribute... attrs) { - Set result = new HashSet(); - for (Attribute attr : attrs) - result.add(attr); - return result; + static Attributes asSet(Attribute... attrs) { + return new Attributes(attrs); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java index bcf17174b..6ad19a249 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java @@ -53,9 +53,7 @@ import java.io.File; import java.io.IOException; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import org.eclipse.jgit.attributes.Attribute.State; import org.eclipse.jgit.errors.CorruptObjectException; @@ -227,12 +225,14 @@ private void assertAttributesNode(String pathName, assertTrue(nodeAttrs == null || nodeAttrs.isEmpty()); else { - Map entryAttributes = new LinkedHashMap(); - attributesNode.getAttributes(pathName, false, entryAttributes); + Attributes entryAttributes = new Attributes(); + attributesNode.getAttributes(pathName, + false, entryAttributes); if (nodeAttrs != null && !nodeAttrs.isEmpty()) { for (Attribute attribute : nodeAttrs) { - assertThat(entryAttributes.values(), hasItem(attribute)); + assertThat(entryAttributes.getAll(), + hasItem(attribute)); } } else { assertTrue( diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java index c3d5e8752..b044c01db 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java @@ -802,8 +802,9 @@ private void assertEntry(FileMode type, String pathName, assertEquals(type, walk.getFileMode(0)); assertEquals(checkinAttributes, - asSet(ci_walk.getAttributes().values())); - assertEquals(checkoutAttributes, asSet(walk.getAttributes().values())); + asSet(ci_walk.getAttributes().getAll())); + assertEquals(checkoutAttributes, + asSet(walk.getAttributes().getAll())); if (D.equals(type)) { walk.enterSubtree(); diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters index b89aad4c5..b2a8f677f 100644 --- a/org.eclipse.jgit/.settings/.api_filters +++ b/org.eclipse.jgit/.settings/.api_filters @@ -1,5 +1,13 @@ + + + + + + + + diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java index d3ce68518..905ad7692 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java @@ -50,8 +50,10 @@ *

  • Set - represented by {@link State#SET}
  • *
  • Unset - represented by {@link State#UNSET}
  • *
  • Set to a value - represented by {@link State#CUSTOM}
  • - *
  • Unspecified - null is used instead of an instance of this - * class
  • + *
  • Unspecified - used to revert an attribute . This is crucial in order to + * mark an attribute as unspecified in the attributes map and thus preventing + * following (with lower priority) nodes from setting the attribute to a value + * at all
  • * *

    * @@ -61,6 +63,7 @@ public final class Attribute { /** * The attribute value state + * see also https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html */ public static enum State { /** the attribute is set */ @@ -69,6 +72,13 @@ public static enum State { /** the attribute is unset */ UNSET, + /** + * the attribute appears as if it would not be defined at all + * + * @since 4.2 + */ + UNSPECIFIED, + /** the attribute is set to a custom value */ CUSTOM } @@ -176,6 +186,8 @@ public String toString() { return key; case UNSET: return "-" + key; //$NON-NLS-1$ + case UNSPECIFIED: + return "!" + key; //$NON-NLS-1$ case CUSTOM: default: return key + "=" + value; //$NON-NLS-1$ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java new file mode 100644 index 000000000..0810e3168 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2015, Ivan Motsch + * + * 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.attributes; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jgit.attributes.Attribute.State; + +/** + * Represents a set of attributes for a path + *

    + * + * @since 4.2 + */ +public final class Attributes { + private final Map map = new LinkedHashMap<>(); + + /** + * Creates a new instance + * + * @param attributes + */ + public Attributes(Attribute... attributes) { + if (attributes != null) { + for (Attribute a : attributes) { + put(a); + } + } + } + + /** + * @return true if the set does not contain any attributes + */ + public boolean isEmpty() { + return map.isEmpty(); + } + + /** + * @param key + * @return the attribute or null + */ + public Attribute get(String key) { + return map.get(key); + } + + /** + * @return all attributes + */ + public Collection getAll() { + return new ArrayList<>(map.values()); + } + + /** + * @param a + */ + public void put(Attribute a) { + map.put(a.getKey(), a); + } + + /** + * @param key + */ + public void remove(String key) { + map.remove(key); + } + + /** + * @param key + * @return true if the {@link Attributes} contains this key + */ + public boolean containsKey(String key) { + return map.containsKey(key); + } + + /** + * Returns the state. + * + * @param key + * + * @return the state (never returns null) + */ + public Attribute.State getState(String key) { + Attribute a = map.get(key); + return a != null ? a.getState() : Attribute.State.UNSPECIFIED; + } + + /** + * @param key + * @return true if the key is {@link State#SET}, false in all other cases + */ + public boolean isSet(String key) { + return (getState(key) == State.SET); + } + + /** + * @param key + * @return true if the key is {@link State#UNSET}, false in all other cases + */ + public boolean isUnset(String key) { + return (getState(key) == State.UNSET); + } + + /** + * @param key + * @return true if the key is {@link State#UNSPECIFIED}, false in all other + * cases + */ + public boolean isUnspecified(String key) { + return (getState(key) == State.UNSPECIFIED); + } + + /** + * @param key + * @return true if the key is {@link State#CUSTOM}, false in all other cases + * see {@link #getValue(String)} for the value of the key + */ + public boolean isCustom(String key) { + return (getState(key) == State.CUSTOM); + } + + /** + * @param key + * @return the attribute value (may be null) + */ + public String getValue(String key) { + Attribute a = map.get(key); + return a != null ? a.getValue() : null; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(getClass().getSimpleName()); + buf.append("["); //$NON-NLS-1$ + buf.append(" "); //$NON-NLS-1$ + for (Attribute a : map.values()) { + buf.append(a.toString()); + buf.append(" "); //$NON-NLS-1$ + } + buf.append("]"); //$NON-NLS-1$ + return buf.toString(); + } + + @Override + public int hashCode() { + return map.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof Attributes)) + return false; + Attributes other = (Attributes) obj; + return this.map.equals(other.map); + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java index 70f56ff96..5c0aba2e0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java @@ -50,7 +50,6 @@ import java.util.Collections; import java.util.List; import java.util.ListIterator; -import java.util.Map; import org.eclipse.jgit.lib.Constants; @@ -134,11 +133,12 @@ public List getRules() { * true if the target item is a directory. * @param attributes * Map that will hold the attributes matching this entry path. If - * it is not empty, this method will NOT override any - * existing entry. + * it is not empty, this method will NOT override any existing + * entry. + * @since 4.2 */ - public void getAttributes(String entryPath, boolean isDirectory, - Map attributes) { + public void getAttributes(String entryPath, + boolean isDirectory, Attributes attributes) { // Parse rules in the reverse order that they were read since the last // entry should be used ListIterator ruleIterator = rules.listIterator(rules @@ -153,7 +153,7 @@ public void getAttributes(String entryPath, boolean isDirectory, while (attributeIte.hasPrevious()) { Attribute attr = attributeIte.previous(); if (!attributes.containsKey(attr.getKey())) - attributes.put(attr.getKey(), attr); + attributes.put(attr); } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java index 8f23a83f7..1037f697d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java @@ -42,8 +42,6 @@ */ package org.eclipse.jgit.attributes; -import java.util.Map; - /** * Interface for classes which provide git attributes * @@ -51,7 +49,7 @@ */ public interface AttributesProvider { /** - * @return the currently active attributes by attribute key + * @return the currently active attributes */ - public Map getAttributes(); + public Attributes getAttributes(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java index bcac14b5f..35d18c4b2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java @@ -84,6 +84,13 @@ private static List parseAttributes(String attributesLine) { continue; } + if (attribute.startsWith("!")) {//$NON-NLS-1$ + if (attribute.length() > 1) + result.add(new Attribute(attribute.substring(1), + State.UNSPECIFIED)); + continue; + } + final int equalsIndex = attribute.indexOf("="); //$NON-NLS-1$ if (equalsIndex == -1) result.add(new Attribute(attribute, State.SET)); @@ -200,4 +207,16 @@ public boolean isMatch(String relativeTarget, boolean isDirectory) { boolean match = matcher.matches(relativeTarget, isDirectory); return match; } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(pattern); + for (Attribute a : attributes) { + sb.append(" "); //$NON-NLS-1$ + sb.append(a); + } + return sb.toString(); + + } } \ No newline at end of file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java index eb53434b7..bda5cbeba 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java @@ -47,6 +47,7 @@ import java.io.IOException; import org.eclipse.jgit.attributes.AttributesNode; +import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.util.FS; @@ -71,7 +72,7 @@ public AttributesNode load() throws IOException { FS fs = repository.getFS(); File attributes = fs.resolve(repository.getDirectory(), - "info/attributes"); //$NON-NLS-1$ + Constants.INFO_ATTRIBUTES); FileRepository.AttributesNodeProviderImpl.loadRulesFromFile(r, attributes); return r.getRules().isEmpty() ? null : r; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java index 535a6ee17..613df37a7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java @@ -272,6 +272,13 @@ public final class Constants { */ public static final String INFO_EXCLUDE = "info/exclude"; + /** + * Attributes-override-file + * + * @since 4.2 + */ + public static final String INFO_ATTRIBUTES = "info/attributes"; + /** * The system property that contains the system user name * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java index 826ce0973..8a59a700e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java @@ -45,17 +45,16 @@ package org.eclipse.jgit.treewalk; import java.io.IOException; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; import java.util.Set; import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.attributes.Attribute; +import org.eclipse.jgit.attributes.Attributes; import org.eclipse.jgit.attributes.AttributesNode; import org.eclipse.jgit.attributes.AttributesNodeProvider; import org.eclipse.jgit.attributes.AttributesProvider; +import org.eclipse.jgit.attributes.Attribute.State; import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; @@ -258,7 +257,7 @@ public static TreeWalk forPath(final Repository db, final String path, AbstractTreeIterator currentHead; /** Cached attribute for the current entry */ - private Map attrs = null; + private Attributes attrs = null; /** * Create a new tree walker for a given repository. @@ -1119,7 +1118,7 @@ static String pathOf(final byte[] buf, int pos, int end) { * @return a {@link Set} of {@link Attribute}s that match the current entry. * @since 4.2 */ - public Map getAttributes() { + public Attributes getAttributes() { if (attrs != null) return attrs; @@ -1138,35 +1137,42 @@ public Map getAttributes() { && other == null) { // Can not retrieve the attributes without at least one of the above // iterators. - return Collections. emptyMap(); + return new Attributes(); } String path = currentHead.getEntryPathString(); final boolean isDir = FileMode.TREE.equals(currentHead.mode); - Map attributes = new LinkedHashMap(); + Attributes attributes = new Attributes(); try { - // Gets the info attributes + // Gets the global attributes node + AttributesNode globalNodeAttr = attributesNodeProvider + .getGlobalAttributesNode(); + // Gets the info attributes node AttributesNode infoNodeAttr = attributesNodeProvider .getInfoAttributesNode(); + + // Gets the info attributes if (infoNodeAttr != null) { infoNodeAttr.getAttributes(path, isDir, attributes); } - // Gets the attributes located on the current entry path getPerDirectoryEntryAttributes(path, isDir, operationType, - workingTreeIterator, dirCacheIterator, other, - attributes); + workingTreeIterator, dirCacheIterator, other, attributes); // Gets the attributes located in the global attribute file - AttributesNode globalNodeAttr = attributesNodeProvider - .getGlobalAttributesNode(); if (globalNodeAttr != null) { globalNodeAttr.getAttributes(path, isDir, attributes); } } catch (IOException e) { throw new JGitInternalException("Error while parsing attributes", e); //$NON-NLS-1$ } + // now after all attributes are collected - in the correct hierarchy + // order - remove all unspecified entries (the ! marker) + for (Attribute a : attributes.getAll()) { + if (a.getState() == State.UNSPECIFIED) + attributes.remove(a.getKey()); + } return attributes; } @@ -1195,7 +1201,7 @@ public Map getAttributes() { private void getPerDirectoryEntryAttributes(String path, boolean isDir, OperationType opType, WorkingTreeIterator workingTreeIterator, DirCacheIterator dirCacheIterator, CanonicalTreeParser other, - Map attributes) + Attributes attributes) throws IOException { // Prevents infinite recurrence if (workingTreeIterator != null || dirCacheIterator != null @@ -1208,12 +1214,11 @@ private void getPerDirectoryEntryAttributes(String path, boolean isDir, getPerDirectoryEntryAttributes(path, isDir, opType, getParent(workingTreeIterator, WorkingTreeIterator.class), getParent(dirCacheIterator, DirCacheIterator.class), - getParent(other, CanonicalTreeParser.class), - attributes); + getParent(other, CanonicalTreeParser.class), attributes); } } - private T getParent(T current, + private static T getParent(T current, Class type) { if (current != null) { AbstractTreeIterator parent = current.parent; @@ -1224,7 +1229,7 @@ private T getParent(T current, return null; } - private T getTree(Class type) { + private T getTree(Class type) { for (int i = 0; i < trees.length; i++) { AbstractTreeIterator tree = trees[i]; if (type.isInstance(tree)) { From 5d9f595eb87fba31c2253051102116fc7876e6c0 Mon Sep 17 00:00:00 2001 From: Christian Halstrick Date: Wed, 28 Oct 2015 13:25:09 +0100 Subject: [PATCH 33/50] Add support for clean filters When filters are defined for certain paths in gitattributes make sure that clean filters are processed when adding new content to the object database. Change-Id: Iffd72914cec5b434ba4d0de232e285b7492db868 Signed-off-by: Matthias Sohn --- .../jgit/junit/RepositoryTestCase.java | 13 ++ .../org/eclipse/jgit/api/AddCommandTest.java | 188 ++++++++++++++++++ .../eclipse/jgit/internal/JGitText.properties | 2 + .../src/org/eclipse/jgit/api/AddCommand.java | 4 + .../org/eclipse/jgit/api/CommitCommand.java | 4 +- .../api/errors/FilterFailedException.java | 144 ++++++++++++++ .../org/eclipse/jgit/dircache/DirCache.java | 1 + .../org/eclipse/jgit/internal/JGitText.java | 2 + .../src/org/eclipse/jgit/lib/Constants.java | 14 ++ .../src/org/eclipse/jgit/lib/IndexDiff.java | 1 + .../org/eclipse/jgit/treewalk/TreeWalk.java | 77 ++++++- .../jgit/treewalk/WorkingTreeIterator.java | 74 ++++++- .../eclipse/jgit/util/TemporaryBuffer.java | 31 +++ 13 files changed, 546 insertions(+), 9 deletions(-) create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/api/errors/FilterFailedException.java diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java index 56962e869..c649eb908 100644 --- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java +++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java @@ -282,6 +282,19 @@ public static String lookup(Object l, String nameTemplate, return name; } + /** + * Replaces '\' by '/' + * + * @param str + * the string in which backslashes should be replaced + * @return the resulting string with slashes + * @since 4.2 + */ + public static String slashify(String str) { + str = str.replace('\\', '/'); + return str; + } + /** * Waits until it is guaranteed that a subsequent file modification has a * younger modification timestamp than the modification timestamp of the diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java index 2abed3adc..a5ad18d10 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java @@ -45,6 +45,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.File; @@ -52,11 +53,13 @@ import java.io.IOException; import java.io.PrintWriter; +import org.eclipse.jgit.api.errors.FilterFailedException; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.NoFilepatternException; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCacheBuilder; import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.junit.JGitTestUtil; import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; @@ -111,6 +114,191 @@ public void testAddExistingSingleFile() throws IOException, GitAPIException { indexState(CONTENT)); } + @Test + public void testCleanFilter() throws IOException, + GitAPIException { + writeTrashFile(".gitattributes", "*.txt filter=tstFilter"); + writeTrashFile("src/a.tmp", "foo"); + // Caution: we need a trailing '\n' since sed on mac always appends + // linefeeds if missing + writeTrashFile("src/a.txt", "foo\n"); + File script = writeTempFile("sed s/o/e/g"); + + Git git = new Git(db); + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "tstFilter", "clean", + "sh " + slashify(script.getPath())); + config.save(); + + git.add().addFilepattern("src/a.txt").addFilepattern("src/a.tmp") + .call(); + + assertEquals( + "[src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:fee\n]", + indexState(CONTENT)); + } + + @Test + public void testCleanFilterEnvironment() + throws IOException, GitAPIException { + writeTrashFile(".gitattributes", "*.txt filter=tstFilter"); + writeTrashFile("src/a.txt", "foo"); + File script = writeTempFile("echo $GIT_DIR; echo 1 >xyz"); + + Git git = new Git(db); + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "tstFilter", "clean", + "sh " + slashify(script.getPath())); + config.save(); + git.add().addFilepattern("src/a.txt").call(); + + String gitDir = db.getDirectory().getAbsolutePath(); + assertEquals("[src/a.txt, mode:100644, content:" + gitDir + + "\n]", indexState(CONTENT)); + assertTrue(new File(db.getWorkTree(), "xyz").exists()); + } + + @Test + public void testMultipleCleanFilter() throws IOException, GitAPIException { + writeTrashFile(".gitattributes", + "*.txt filter=tstFilter\n*.tmp filter=tstFilter2"); + // Caution: we need a trailing '\n' since sed on mac always appends + // linefeeds if missing + writeTrashFile("src/a.tmp", "foo\n"); + writeTrashFile("src/a.txt", "foo\n"); + File script = writeTempFile("sed s/o/e/g"); + File script2 = writeTempFile("sed s/f/x/g"); + + Git git = new Git(db); + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "tstFilter", "clean", + "sh " + slashify(script.getPath())); + config.setString("filter", "tstFilter2", "clean", + "sh " + slashify(script2.getPath())); + config.save(); + + git.add().addFilepattern("src/a.txt").addFilepattern("src/a.tmp") + .call(); + + assertEquals( + "[src/a.tmp, mode:100644, content:xoo\n][src/a.txt, mode:100644, content:fee\n]", + indexState(CONTENT)); + + // TODO: multiple clean filters for one file??? + } + + /** + * The path of an added file name contains ';' and afterwards malicious + * commands. Make sure when calling filter commands to properly escape the + * filenames + * + * @throws IOException + * @throws GitAPIException + */ + @Test + public void testCommandInjection() throws IOException, GitAPIException { + // Caution: we need a trailing '\n' since sed on mac always appends + // linefeeds if missing + writeTrashFile("; echo virus", "foo\n"); + File script = writeTempFile("sed s/o/e/g"); + + Git git = new Git(db); + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "tstFilter", "clean", + "sh " + slashify(script.getPath()) + " %f"); + writeTrashFile(".gitattributes", "* filter=tstFilter"); + + git.add().addFilepattern("; echo virus").call(); + // Without proper escaping the content would be "feovirus". The sed + // command and the "echo virus" would contribute to the content + assertEquals("[; echo virus, mode:100644, content:fee\n]", + indexState(CONTENT)); + } + + @Test + public void testBadCleanFilter() throws IOException, GitAPIException { + writeTrashFile("a.txt", "foo"); + File script = writeTempFile("sedfoo s/o/e/g"); + + Git git = new Git(db); + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "tstFilter", "clean", + "sh " + script.getPath()); + config.save(); + writeTrashFile(".gitattributes", "*.txt filter=tstFilter"); + + try { + git.add().addFilepattern("a.txt").call(); + fail("Didn't received the expected exception"); + } catch (FilterFailedException e) { + assertEquals(127, e.getReturnCode()); + } + } + + @Test + public void testBadCleanFilter2() throws IOException, GitAPIException { + writeTrashFile("a.txt", "foo"); + File script = writeTempFile("sed s/o/e/g"); + + Git git = new Git(db); + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "tstFilter", "clean", + "shfoo " + script.getPath()); + config.save(); + writeTrashFile(".gitattributes", "*.txt filter=tstFilter"); + + try { + git.add().addFilepattern("a.txt").call(); + fail("Didn't received the expected exception"); + } catch (FilterFailedException e) { + assertEquals(127, e.getReturnCode()); + } + } + + @Test + public void testCleanFilterReturning12() throws IOException, + GitAPIException { + writeTrashFile("a.txt", "foo"); + File script = writeTempFile("exit 12"); + + Git git = new Git(db); + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "tstFilter", "clean", + "sh " + slashify(script.getPath())); + config.save(); + writeTrashFile(".gitattributes", "*.txt filter=tstFilter"); + + try { + git.add().addFilepattern("a.txt").call(); + fail("Didn't received the expected exception"); + } catch (FilterFailedException e) { + assertEquals(12, e.getReturnCode()); + } + } + + @Test + public void testNotApplicableFilter() throws IOException, GitAPIException { + writeTrashFile("a.txt", "foo"); + File script = writeTempFile("sed s/o/e/g"); + + Git git = new Git(db); + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "tstFilter", "something", + "sh " + script.getPath()); + config.save(); + writeTrashFile(".gitattributes", "*.txt filter=tstFilter"); + + git.add().addFilepattern("a.txt").call(); + + assertEquals("[a.txt, mode:100644, content:foo]", indexState(CONTENT)); + } + + private File writeTempFile(String body) throws IOException { + File f = File.createTempFile("AddCommandTest_", ""); + JGitTestUtil.write(f, body); + return f; + } + @Test public void testAddExistingSingleSmallFileWithNewLine() throws IOException, GitAPIException { diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index 5f80b8103..d0e1c779e 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -281,6 +281,8 @@ fileCannotBeDeleted=File cannot be deleted: {0} fileIsTooBigForThisConvenienceMethod=File is too big for this convenience method ({0} bytes). fileIsTooLarge=File is too large: {0} fileModeNotSetForPath=FileMode not set for path {0} +filterExecutionFailed=Execution of filter command ''{0}'' on file ''{1}'' failed +filterExecutionFailedRc=Execution of filter command ''{0}'' on file ''{1}'' failed with return code ''{2}'', message on stderr: ''{3}'' findingGarbage=Finding garbage flagIsDisposed={0} is disposed. flagNotFromThis={0} not from this. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java index ae297a643..67fb342fe 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java @@ -48,6 +48,7 @@ import java.util.Collection; import java.util.LinkedList; +import org.eclipse.jgit.api.errors.FilterFailedException; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.api.errors.NoFilepatternException; @@ -211,6 +212,9 @@ else if (!(path.equals(lastAddedFile))) { builder.commit(); setCallable(false); } catch (IOException e) { + Throwable cause = e.getCause(); + if (cause != null && cause instanceof FilterFailedException) + throw (FilterFailedException) cause; throw new JGitInternalException( JGitText.get().exceptionCaughtDuringExecutionOfAddCommand, e); } finally { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java index 53e18df47..9466dab74 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java @@ -332,7 +332,9 @@ private DirCache createTemporaryIndex(ObjectId headId, DirCache index, treeWalk.setOperationType(OperationType.CHECKIN_OP); int dcIdx = treeWalk .addTree(new DirCacheBuildIterator(existingBuilder)); - int fIdx = treeWalk.addTree(new FileTreeIterator(repo)); + FileTreeIterator fti = new FileTreeIterator(repo); + fti.setDirCacheIterator(treeWalk, 0); + int fIdx = treeWalk.addTree(fti); int hIdx = -1; if (headId != null) hIdx = treeWalk.addTree(rw.parseTree(headId)); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/FilterFailedException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/FilterFailedException.java new file mode 100644 index 000000000..fbc30ef16 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/FilterFailedException.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2015, Christian Halstrick 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.api.errors; + +import java.text.MessageFormat; + +import org.eclipse.jgit.internal.JGitText; + +/** + * Exception thrown when the execution of a filter command failed + * + * @since 4.2 + */ +public class FilterFailedException extends GitAPIException { + private static final long serialVersionUID = 1L; + + private String filterCommand; + + private String path; + + private byte[] stdout; + + private String stderr; + + private int rc; + + /** + * Thrown if during execution of filter command an exception occurred + * + * @param cause + * the exception + * @param filterCommand + * the command which failed + * @param path + * the path processed by the filter + */ + public FilterFailedException(Exception cause, String filterCommand, + String path) { + super(MessageFormat.format(JGitText.get().filterExecutionFailed, + filterCommand, path), cause); + this.filterCommand = filterCommand; + this.path = path; + } + + /** + * Thrown if a filter command returns a non-zero return code + * + * @param rc + * the return code + * @param filterCommand + * the command which failed + * @param path + * the path processed by the filter + * @param stdout + * the output the filter generated so far. This should be limited + * to reasonable size. + * @param stderr + * the stderr output of the filter + */ + @SuppressWarnings("boxing") + public FilterFailedException(int rc, String filterCommand, String path, + byte[] stdout, String stderr) { + super(MessageFormat.format(JGitText.get().filterExecutionFailedRc, + filterCommand, path, rc, stderr)); + this.rc = rc; + this.filterCommand = filterCommand; + this.path = path; + this.stdout = stdout; + this.stderr = stderr; + } + + /** + * @return the filterCommand + */ + public String getFilterCommand() { + return filterCommand; + } + + /** + * @return the path of the file processed by the filter command + */ + public String getPath() { + return path; + } + + /** + * @return the output generated by the filter command. Might be truncated to + * limit memory consumption. + */ + public byte[] getOutput() { + return stdout; + } + + /** + * @return the error output returned by the filter command + */ + public String getError() { + return stderr; + } + + /** + * @return the return code returned by the filter command + */ + public int getReturnCode() { + return rc; + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java index 387d8ce73..a3980d212 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java @@ -983,6 +983,7 @@ private void updateSmudgedEntries() throws IOException { FileTreeIterator fIter = new FileTreeIterator(repository); walk.addTree(iIter); walk.addTree(fIter); + fIter.setDirCacheIterator(walk, 0); walk.setRecursive(true); while (walk.next()) { iIter = walk.getTree(0, DirCacheIterator.class); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index 6680564ad..f6fd8a396 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -340,6 +340,8 @@ public static JGitText get() { /***/ public String fileIsTooBigForThisConvenienceMethod; /***/ public String fileIsTooLarge; /***/ public String fileModeNotSetForPath; + /***/ public String filterExecutionFailed; + /***/ public String filterExecutionFailedRc; /***/ public String findingGarbage; /***/ public String flagIsDisposed; /***/ public String flagNotFromThis; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java index 613df37a7..1a3111ab4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java @@ -370,6 +370,20 @@ public final class Constants { */ public static final String DOT_GIT_ATTRIBUTES = ".gitattributes"; + /** + * Key for filters in .gitattributes + * + * @since 4.2 + */ + public static final String ATTR_FILTER = "filter"; + + /** + * clean command name, used to call filter driver + * + * @since 4.2 + */ + public static final String ATTR_FILTER_TYPE_CLEAN = "clean"; + /** Name of the ignore file */ public static final String DOT_GIT_IGNORE = ".gitignore"; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java index 281cde875..9e474f86a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java @@ -413,6 +413,7 @@ public boolean diff(final ProgressMonitor monitor, int estWorkTreeSize, treeWalk.addTree(new EmptyTreeIterator()); treeWalk.addTree(new DirCacheIterator(dirCache)); treeWalk.addTree(initialWorkingTreeIterator); + initialWorkingTreeIterator.setDirCacheIterator(treeWalk, 1); Collection filters = new ArrayList(4); if (monitor != null) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java index 8a59a700e..06dc0bf6d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java @@ -45,22 +45,25 @@ package org.eclipse.jgit.treewalk; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import java.util.Set; import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.attributes.Attribute; +import org.eclipse.jgit.attributes.Attribute.State; import org.eclipse.jgit.attributes.Attributes; import org.eclipse.jgit.attributes.AttributesNode; import org.eclipse.jgit.attributes.AttributesNodeProvider; import org.eclipse.jgit.attributes.AttributesProvider; -import org.eclipse.jgit.attributes.Attribute.State; import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.StopWalkException; import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.MutableObjectId; @@ -70,6 +73,7 @@ import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.treewalk.filter.PathFilter; import org.eclipse.jgit.treewalk.filter.TreeFilter; +import org.eclipse.jgit.util.QuotedString; import org.eclipse.jgit.util.RawParseUtils; /** @@ -116,6 +120,12 @@ public static enum OperationType { */ private OperationType operationType = OperationType.CHECKOUT_OP; + /** + * The filter command as defined in gitattributes. The keys are + * filterName+"."+filterCommandType. E.g. "lfs.clean" + */ + private Map filterCommandsByNameDotType = new HashMap(); + /** * @param operationType * @since 4.2 @@ -259,6 +269,8 @@ public static TreeWalk forPath(final Repository db, final String path, /** Cached attribute for the current entry */ private Attributes attrs = null; + private Config config; + /** * Create a new tree walker for a given repository. * @@ -269,6 +281,7 @@ public static TreeWalk forPath(final Repository db, final String path, */ public TreeWalk(final Repository repo) { this(repo.newObjectReader(), true); + config = repo.getConfig(); attributesNodeProvider = repo.createAttributesNodeProvider(); } @@ -1308,4 +1321,66 @@ private static AttributesNode getAttributesNode(AttributesNode value, AttributesNode defaultValue) { return (value == null) ? defaultValue : value; } + + /** + * Inspect config and attributes to return a filtercommand applicable for + * the current path + * + * @param filterCommandType + * which type of filterCommand should be executed. E.g. "clean", + * "smudge" + * @return a filter command + * @throws IOException + * @since 4.2 + */ + public String getFilterCommand(String filterCommandType) + throws IOException { + Attributes attributes = getAttributes(); + + Attribute f = attributes.get(Constants.ATTR_FILTER); + if (f == null) { + return null; + } + String filterValue = f.getValue(); + if (filterValue == null) { + return null; + } + + String filterCommand = getFilterCommandDefinition(filterValue, + filterCommandType); + if (filterCommand == null) { + return null; + } + return filterCommand.replaceAll("%f", //$NON-NLS-1$ + QuotedString.BOURNE.quote((getPathString()))); + } + + /** + * Get the filter command how it is defined in gitconfig. The returned + * string may contain "%f" which needs to be replaced by the current path + * before executing the filter command. These filter definitions are cached + * for better performance. + * + * @param filterDriverName + * The name of the filter driver as it is referenced in the + * gitattributes file. E.g. "lfs". For each filter driver there + * may be many commands defined in the .gitconfig + * @param filterCommandType + * The type of the filter command for a specific filter driver. + * May be "clean" or "smudge". + * @return the definition of the command to be executed for this filter + * driver and filter command + */ + private String getFilterCommandDefinition(String filterDriverName, + String filterCommandType) { + String key = filterDriverName + "." + filterCommandType; //$NON-NLS-1$ + String filterCommand = filterCommandsByNameDotType.get(key); + if (filterCommand != null) + return filterCommand; + filterCommand = config.getString(Constants.ATTR_FILTER, + filterDriverName, filterCommandType); + if (filterCommand != null) + filterCommandsByNameDotType.put(key, filterCommand); + return filterCommand; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java index 8be7f9a84..94beeeb56 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java @@ -62,6 +62,7 @@ import java.util.Collections; import java.util.Comparator; +import org.eclipse.jgit.api.errors.FilterFailedException; import org.eclipse.jgit.attributes.AttributesNode; import org.eclipse.jgit.attributes.AttributesRule; import org.eclipse.jgit.diff.RawText; @@ -76,6 +77,7 @@ import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.CoreConfig; +import org.eclipse.jgit.lib.CoreConfig.AutoCRLF; import org.eclipse.jgit.lib.CoreConfig.CheckStat; import org.eclipse.jgit.lib.CoreConfig.SymLinks; import org.eclipse.jgit.lib.FileMode; @@ -85,6 +87,7 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.submodule.SubmoduleWalk; import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.FS.ExecutionResult; import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.RawParseUtils; import org.eclipse.jgit.util.io.EolCanonicalizingInputStream; @@ -101,6 +104,8 @@ * @see FileTreeIterator */ public abstract class WorkingTreeIterator extends AbstractTreeIterator { + private static final int MAX_EXCEPTION_TEXT_SIZE = 10 * 1024; + /** An empty entry array, suitable for {@link #init(Entry[])}. */ protected static final Entry[] EOF = {}; @@ -134,6 +139,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { /** If there is a .gitignore file present, the parsed rules from it. */ private IgnoreNode ignoreNode; + private String cleanFilterCommand; + /** Repository that is the root level being iterated over */ protected Repository repository; @@ -186,6 +193,7 @@ protected WorkingTreeIterator(final String prefix, protected WorkingTreeIterator(final WorkingTreeIterator p) { super(p); state = p.state; + repository = p.repository; } /** @@ -348,7 +356,8 @@ private byte[] idBufferBlob(final Entry e) { private InputStream possiblyFilteredInputStream(final Entry e, final InputStream is, final long len) throws IOException { - if (!mightNeedCleaning()) { + boolean mightNeedCleaning = mightNeedCleaning(); + if (!mightNeedCleaning) { canonLen = len; return is; } @@ -366,7 +375,8 @@ private InputStream possiblyFilteredInputStream(final Entry e, return new ByteArrayInputStream(raw, 0, n); } - if (isBinary(e)) { + // TODO: fix autocrlf causing mightneedcleaning + if (!mightNeedCleaning && isBinary(e)) { canonLen = len; return is; } @@ -390,10 +400,12 @@ private static void safeClose(final InputStream in) { } } - private boolean mightNeedCleaning() { + private boolean mightNeedCleaning() throws IOException { switch (getOptions().getAutoCRLF()) { case FALSE: default: + if (getCleanFilterCommand() != null) + return true; return false; case TRUE: @@ -415,8 +427,7 @@ private static boolean isBinary(Entry entry) throws IOException { } } - private static ByteBuffer filterClean(byte[] src, int n) - throws IOException { + private ByteBuffer filterClean(byte[] src, int n) throws IOException { InputStream in = new ByteArrayInputStream(src); try { return IO.readWholeStream(filterClean(in), n); @@ -425,8 +436,42 @@ private static ByteBuffer filterClean(byte[] src, int n) } } - private static InputStream filterClean(InputStream in) { - return new EolCanonicalizingInputStream(in, true); + private InputStream filterClean(InputStream in) throws IOException { + in = handleAutoCRLF(in); + String filterCommand = getCleanFilterCommand(); + if (filterCommand != null) { + FS fs = repository.getFS(); + ProcessBuilder filterProcessBuilder = fs.runInShell(filterCommand, + new String[0]); + filterProcessBuilder.directory(repository.getWorkTree()); + filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY, + repository.getDirectory().getAbsolutePath()); + ExecutionResult result; + try { + result = fs.execute(filterProcessBuilder, in); + } catch (IOException | InterruptedException e) { + throw new IOException(new FilterFailedException(e, + filterCommand, getEntryPathString())); + } + int rc = result.getRc(); + if (rc != 0) { + throw new IOException(new FilterFailedException(rc, + filterCommand, getEntryPathString(), + result.getStdout().toByteArray(MAX_EXCEPTION_TEXT_SIZE), + RawParseUtils.decode(result.getStderr() + .toByteArray(MAX_EXCEPTION_TEXT_SIZE)))); + } + return result.getStdout().openInputStream(); + } + return in; + } + + private InputStream handleAutoCRLF(InputStream in) { + AutoCRLF autoCRLF = getOptions().getAutoCRLF(); + if (autoCRLF == AutoCRLF.TRUE || autoCRLF == AutoCRLF.INPUT) { + in = new EolCanonicalizingInputStream(in, true); + } + return in; } /** @@ -485,6 +530,7 @@ private void parseEntry() { System.arraycopy(e.encodedName, 0, path, pathOffset, nameLen); pathLen = pathOffset + nameLen; canonLen = -1; + cleanFilterCommand = null; } /** @@ -1271,4 +1317,18 @@ void initializeDigestAndReadBuffer() { } } } + + /** + * @return the clean filter command for the current entry or + * null if no such command is defined + * @throws IOException + * @since 4.2 + */ + public String getCleanFilterCommand() throws IOException { + if (cleanFilterCommand == null && state.walk != null) { + cleanFilterCommand = state.walk + .getFilterCommand(Constants.ATTR_FILTER_TYPE_CLEAN); + } + return cleanFilterCommand; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java index ca47f50fd..3cd5929c7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java @@ -246,6 +246,37 @@ public byte[] toByteArray() throws IOException { return out; } + /** + * Convert this buffer's contents into a contiguous byte array. If this size + * of the buffer exceeds the limit only return the first {@code limit} bytes + *

    + * The buffer is only complete after {@link #close()} has been invoked. + * + * @param limit + * the maximum number of bytes to be returned + * + * @return the byte array limited to {@code limit} bytes. + * @throws IOException + * an error occurred reading from a local temporary file + * @throws OutOfMemoryError + * the buffer cannot fit in memory + * + * @since 4.2 + */ + public byte[] toByteArray(int limit) throws IOException { + final long len = Math.min(length(), limit); + if (Integer.MAX_VALUE < len) + throw new OutOfMemoryError( + JGitText.get().lengthExceedsMaximumArraySize); + final byte[] out = new byte[(int) len]; + int outPtr = 0; + for (final Block b : blocks) { + System.arraycopy(b.buffer, 0, out, outPtr, b.count); + outPtr += b.count; + } + return out; + } + /** * Send this buffer to an output stream. *

    From bd31555ee507cd29a11060756297031354b9b0c3 Mon Sep 17 00:00:00 2001 From: Christian Halstrick Date: Thu, 29 Oct 2015 14:15:08 +0100 Subject: [PATCH 34/50] Add support for smudge filters If defined in .gitattributes call smudge filter during checkout. To support checkout where current HEAD,index do not contain attributes we need to also consider attributes from the tree we checkout. Therefore CanonicalTreeParser has to learn how to provide attributes. Change-Id: I168fdb81a8e1a9f991587b3e95a36550ea845f0a Signed-off-by: Matthias Sohn --- .../eclipse/jgit/api/CheckoutCommandTest.java | 124 ++++++++++++++++++ .../jgit/lib/DirCacheCheckoutTest.java | 7 +- .../jgit/dircache/DirCacheCheckout.java | 115 ++++++++++++++-- .../src/org/eclipse/jgit/lib/Constants.java | 7 + 4 files changed, 234 insertions(+), 19 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java index 82d509f6f..4bfb128cb 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java @@ -72,12 +72,14 @@ import org.eclipse.jgit.api.errors.TransportException; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.junit.JGitTestUtil; import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.Sets; import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.storage.file.FileBasedConfig; @@ -86,6 +88,7 @@ import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.util.FileUtils; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; public class CheckoutCommandTest extends RepositoryTestCase { @@ -556,4 +559,125 @@ public void testCheckoutAutoCrlfTrue() throws Exception { } org.junit.Assume.assumeTrue(foundUnsmudged); } + + @Test + public void testSmudgeFilter_modifyExisting() throws IOException, GitAPIException { + File script = writeTempFile("sed s/o/e/g"); + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "tstFilter", "smudge", + "sh " + slashify(script.getPath())); + config.save(); + + writeTrashFile(".gitattributes", "*.txt filter=tstFilter"); + git.add().addFilepattern(".gitattributes").call(); + git.commit().setMessage("add filter").call(); + + writeTrashFile("src/a.tmp", "x"); + // Caution: we need a trailing '\n' since sed on mac always appends + // linefeeds if missing + writeTrashFile("src/a.txt", "x\n"); + git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt") + .call(); + RevCommit content1 = git.commit().setMessage("add content").call(); + + writeTrashFile("src/a.tmp", "foo"); + writeTrashFile("src/a.txt", "foo\n"); + git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt") + .call(); + RevCommit content2 = git.commit().setMessage("changed content").call(); + + git.checkout().setName(content1.getName()).call(); + git.checkout().setName(content2.getName()).call(); + + assertEquals( + "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:foo\n]", + indexState(CONTENT)); + assertEquals(Sets.of("src/a.txt"), git.status().call().getModified()); + assertEquals("foo", read("src/a.tmp")); + assertEquals("fee\n", read("src/a.txt")); + } + + @Test + public void testSmudgeFilter_createNew() + throws IOException, GitAPIException { + File script = writeTempFile("sed s/o/e/g"); + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "tstFilter", "smudge", + "sh " + slashify(script.getPath())); + config.save(); + + writeTrashFile("foo", "foo"); + git.add().addFilepattern("foo").call(); + RevCommit initial = git.commit().setMessage("initial").call(); + + writeTrashFile(".gitattributes", "*.txt filter=tstFilter"); + git.add().addFilepattern(".gitattributes").call(); + git.commit().setMessage("add filter").call(); + + writeTrashFile("src/a.tmp", "foo"); + // Caution: we need a trailing '\n' since sed on mac always appends + // linefeeds if missing + writeTrashFile("src/a.txt", "foo\n"); + git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt") + .call(); + RevCommit content = git.commit().setMessage("added content").call(); + + git.checkout().setName(initial.getName()).call(); + git.checkout().setName(content.getName()).call(); + + assertEquals( + "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][foo, mode:100644, content:foo][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:foo\n]", + indexState(CONTENT)); + assertEquals("foo", read("src/a.tmp")); + assertEquals("fee\n", read("src/a.txt")); + } + + @Test + @Ignore + public void testSmudgeAndClean() throws IOException, GitAPIException { + // @TODO: fix this test + File clean_filter = writeTempFile("sed s/V1/@version/g -"); + File smudge_filter = writeTempFile("sed s/@version/V1/g -"); + + Git git = new Git(db); + StoredConfig config = git.getRepository().getConfig(); + config.setString("filter", "tstFilter", "smudge", + "sh " + slashify(smudge_filter.getPath())); + config.setString("filter", "tstFilter", "clean", + "sh " + slashify(clean_filter.getPath())); + config.save(); + writeTrashFile(".gitattributes", "*.txt filter=tstFilter"); + git.add().addFilepattern(".gitattributes").call(); + git.commit().setMessage("add attributes").call(); + + writeTrashFile("filterTest.txt", "hello world, V1"); + git.add().addFilepattern("filterTest.txt").call(); + git.commit().setMessage("add filterText.txt").call(); + assertEquals( + "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some other change][filterTest.txt, mode:100644, content:hello world, @version]", + indexState(CONTENT)); + + git.checkout().setCreateBranch(true).setName("test2").call(); + writeTrashFile("filterTest.txt", "bon giorno world, V1"); + git.add().addFilepattern("filterTest.txt").call(); + git.commit().setMessage("modified filterText.txt").call(); + + assertTrue(git.status().call().isClean()); + assertEquals( + "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some other change][filterTest.txt, mode:100644, content:bon giorno world, @version]", + indexState(CONTENT)); + + git.checkout().setName("refs/heads/test").call(); + assertTrue(git.status().call().isClean()); + assertEquals( + "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some other change][filterTest.txt, mode:100644, content:hello world, @version]", + indexState(CONTENT)); + assertEquals("hello world, V1", read("filterTest.txt")); + } + + private File writeTempFile(String body) throws IOException { + File f = File.createTempFile("AddCommandTest_", ""); + JGitTestUtil.write(f, body); + return f; + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java index 51a6b5a8f..d768e0fa0 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java @@ -113,7 +113,7 @@ private List getRemoved() { return dco.getRemoved(); } - private Map getUpdated() { + private Map getUpdated() { return dco.getUpdated(); } @@ -268,8 +268,6 @@ private void assertIndex(HashMap i) @Test public void testRules1thru3_NoIndexEntry() throws IOException { ObjectId head = buildTree(mk("foo")); - TreeWalk tw = TreeWalk.forPath(db, "foo", head); - ObjectId objectId = tw.getObjectId(0); ObjectId merge = db.newObjectInserter().insert(Constants.OBJ_TREE, new byte[0]); @@ -279,10 +277,9 @@ public void testRules1thru3_NoIndexEntry() throws IOException { prescanTwoTrees(merge, head); - assertEquals(objectId, getUpdated().get("foo")); + assertTrue(getUpdated().containsKey("foo")); merge = buildTree(mkmap("foo", "a")); - tw = TreeWalk.forPath(db, "foo", merge); prescanTwoTrees(head, merge); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java index 0036ab508..393a85494 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java @@ -52,15 +52,18 @@ import java.util.List; import java.util.Map; +import org.eclipse.jgit.api.errors.FilterFailedException; import org.eclipse.jgit.errors.CheckoutConflictException; import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.IndexWriteException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.CoreConfig.AutoCRLF; import org.eclipse.jgit.lib.CoreConfig.SymLinks; import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectChecker; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectLoader; @@ -76,6 +79,7 @@ import org.eclipse.jgit.treewalk.WorkingTreeOptions; import org.eclipse.jgit.treewalk.filter.PathFilter; import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.FS.ExecutionResult; import org.eclipse.jgit.util.FileUtils; import org.eclipse.jgit.util.RawParseUtils; import org.eclipse.jgit.util.SystemReader; @@ -85,9 +89,10 @@ * This class handles checking out one or two trees merging with the index. */ public class DirCacheCheckout { + private static final int MAX_EXCEPTION_TEXT_SIZE = 10 * 1024; private Repository repo; - private HashMap updated = new HashMap(); + private HashMap updated = new HashMap(); private ArrayList conflicts = new ArrayList(); @@ -112,9 +117,9 @@ public class DirCacheCheckout { private boolean emptyDirCache; /** - * @return a list of updated paths and objectIds + * @return a list of updated paths and smudgeFilterCommands */ - public Map getUpdated() { + public Map getUpdated() { return updated; } @@ -447,7 +452,8 @@ private boolean doCheckout() throws CorruptObjectException, IOException, for (String path : updated.keySet()) { DirCacheEntry entry = dc.getEntry(path); if (!FileMode.GITLINK.equals(entry.getRawMode())) - checkoutEntry(repo, entry, objectReader, false); + checkoutEntry(repo, entry, objectReader, false, + updated.get(path)); } // commit the index builder - a new index is persisted @@ -996,9 +1002,12 @@ private void remove(String path) { removed.add(path); } - private void update(String path, ObjectId mId, FileMode mode) { + private void update(String path, ObjectId mId, FileMode mode) + throws IOException { if (!FileMode.TREE.equals(mode)) { - updated.put(path, mId); + updated.put(path, + walk.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE)); + DirCacheEntry entry = new DirCacheEntry(path, DirCacheEntry.STAGE_0); entry.setObjectId(mId); entry.setFileMode(mode); @@ -1150,7 +1159,7 @@ private boolean isModifiedSubtree_IndexTree(String path, ObjectId tree) */ public static void checkoutEntry(Repository repo, DirCacheEntry entry, ObjectReader or) throws IOException { - checkoutEntry(repo, entry, or, false); + checkoutEntry(repo, entry, or, false, null); } /** @@ -1186,6 +1195,46 @@ public static void checkoutEntry(Repository repo, DirCacheEntry entry, */ public static void checkoutEntry(Repository repo, DirCacheEntry entry, ObjectReader or, boolean deleteRecursive) throws IOException { + checkoutEntry(repo, entry, or, deleteRecursive, null); + } + + /** + * Updates the file in the working tree with content and mode from an entry + * in the index. The new content is first written to a new temporary file in + * the same directory as the real file. Then that new file is renamed to the + * final filename. + * + *

    + * Note: if the entry path on local file system exists as a file, it + * will be deleted and if it exists as a directory, it will be deleted + * recursively, independently if has any content. + *

    + * + *

    + * TODO: this method works directly on File IO, we may need another + * abstraction (like WorkingTreeIterator). This way we could tell e.g. + * Eclipse that Files in the workspace got changed + *

    + * + * @param repo + * repository managing the destination work tree. + * @param entry + * the entry containing new mode and content + * @param or + * object reader to use for checkout + * @param deleteRecursive + * true to recursively delete final path if it exists on the file + * system + * @param smudgeFilterCommand + * the filter command to be run for smudging the entry to be + * checked out + * + * @throws IOException + * @since 4.2 + */ + public static void checkoutEntry(Repository repo, DirCacheEntry entry, + ObjectReader or, boolean deleteRecursive, + String smudgeFilterCommand) throws IOException { ObjectLoader ol = or.open(entry.getObjectId()); File f = new File(repo.getWorkTree(), entry.getPathString()); File parentDir = f.getParentFile(); @@ -1210,14 +1259,52 @@ public static void checkoutEntry(Repository repo, DirCacheEntry entry, OutputStream channel = new FileOutputStream(tmpFile); if (opt.getAutoCRLF() == AutoCRLF.TRUE) channel = new AutoCRLFOutputStream(channel); - try { - ol.copyTo(channel); - } finally { - channel.close(); + if (smudgeFilterCommand != null) { + ProcessBuilder filterProcessBuilder = fs + .runInShell(smudgeFilterCommand, new String[0]); + filterProcessBuilder.directory(repo.getWorkTree()); + filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY, + repo.getDirectory().getAbsolutePath()); + ExecutionResult result; + int rc; + try { + // TODO: wire correctly with AUTOCRLF + result = fs.execute(filterProcessBuilder, ol.openStream()); + rc = result.getRc(); + if (rc == 0) { + result.getStdout().writeTo(channel, + NullProgressMonitor.INSTANCE); + } + } catch (IOException | InterruptedException e) { + throw new IOException(new FilterFailedException(e, + smudgeFilterCommand, entry.getPathString())); + + } finally { + channel.close(); + } + if (rc != 0) { + throw new IOException(new FilterFailedException(rc, + smudgeFilterCommand, entry.getPathString(), + result.getStdout().toByteArray(MAX_EXCEPTION_TEXT_SIZE), + RawParseUtils.decode(result.getStderr() + .toByteArray(MAX_EXCEPTION_TEXT_SIZE)))); + } + } else { + try { + ol.copyTo(channel); + } finally { + channel.close(); + } + } + // The entry needs to correspond to the on-disk filesize. If the content + // was filtered (either by autocrlf handling or smudge filters) ask the + // filesystem again for the length. Otherwise the objectloader knows the + // size + if (opt.getAutoCRLF() == AutoCRLF.TRUE || smudgeFilterCommand != null) { + entry.setLength(tmpFile.length()); + } else { + entry.setLength(ol.getSize()); } - entry.setLength(opt.getAutoCRLF() == AutoCRLF.TRUE ? // - tmpFile.length() // AutoCRLF wants on-disk-size - : (int) ol.getSize()); if (opt.isFileMode() && fs.supportsExecute()) { if (FileMode.EXECUTABLE_FILE.equals(entry.getRawMode())) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java index 1a3111ab4..d30edaf41 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java @@ -384,6 +384,13 @@ public final class Constants { */ public static final String ATTR_FILTER_TYPE_CLEAN = "clean"; + /** + * smudge command name, used to call filter driver + * + * @since 4.2 + */ + public static final String ATTR_FILTER_TYPE_SMUDGE = "smudge"; + /** Name of the ignore file */ public static final String DOT_GIT_IGNORE = ".gitignore"; From 46e4992e9267211a55aae03b744b50edb87740fd Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Fri, 27 Nov 2015 23:21:33 -0800 Subject: [PATCH 35/50] Fix performance regression in CanonicalTreeParser Change-Id: I14046559fddb9656d890d3099010117e84cd9439 --- .../jgit/treewalk/CanonicalTreeParser.java | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java index df31558ff..c24efe20a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java @@ -64,11 +64,11 @@ /** Parses raw Git trees from the canonical semi-text/semi-binary format. */ public class CanonicalTreeParser extends AbstractTreeIterator { - private static final int ATTRIBUTESLENGTH = Constants.DOT_GIT_ATTRIBUTES - .getBytes().length; - private static final byte[] EMPTY = {}; + private static final byte[] ATTRS = Constants + .encode(Constants.DOT_GIT_ATTRIBUTES); + private byte[] raw; /** First offset within {@link #raw} of the prior entry. */ @@ -375,11 +375,9 @@ private void parseEntry() { nextPtr = ptr + Constants.OBJECT_ID_LENGTH; // Check if this entry is a .gitattributes file - if (RawParseUtils.match(path, pathOffset, - Constants.DOT_GIT_ATTRIBUTES.getBytes()) == ATTRIBUTESLENGTH) - attributesNode = new LazyLoadingAttributesNode( - ObjectId.fromRaw(idBuffer(), idOffset())); - + if (path[pathOffset] == '.' + && RawParseUtils.match(path, pathOffset, ATTRS) > 0) + attributesNode = new LazyLoadingAttributesNode(idOffset()); } /** @@ -402,18 +400,18 @@ public AttributesNode getEntryAttributesNode(ObjectReader reader) /** * {@link AttributesNode} implementation that provides lazy loading */ - private static class LazyLoadingAttributesNode extends AttributesNode { - final ObjectId objectId; + private class LazyLoadingAttributesNode extends AttributesNode { + private final int idOffset; - LazyLoadingAttributesNode(ObjectId objectId) { + LazyLoadingAttributesNode(int idOffset) { super(Collections. emptyList()); - this.objectId = objectId; - + this.idOffset = idOffset; } AttributesNode load(ObjectReader reader) throws IOException { AttributesNode r = new AttributesNode(); - ObjectLoader loader = reader.open(objectId); + ObjectId id = ObjectId.fromRaw(raw, idOffset); + ObjectLoader loader = reader.open(id); if (loader != null) { InputStream in = loader.openStream(); try { @@ -425,5 +423,4 @@ AttributesNode load(ObjectReader reader) throws IOException { return r.getRules().isEmpty() ? null : r; } } - } From 885879ffe907a5df1c6fcc40ef6972e27dfecdd6 Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Fri, 27 Nov 2015 21:34:16 -0800 Subject: [PATCH 36/50] DirCache: Fix getEntriesWithin("") to not include null entries The internal array may be longer than entryCnt, in this case the tail of the array is padded with null entries. Do not return those to the caller of getEntriesWithin(). Change-Id: I19efb05e103fab6b739ced407f6e28155a48dba6 --- org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java index a3980d212..6dcb9488b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java @@ -877,8 +877,8 @@ public DirCacheEntry getEntry(final String path) { */ public DirCacheEntry[] getEntriesWithin(String path) { if (path.length() == 0) { - final DirCacheEntry[] r = new DirCacheEntry[sortedEntries.length]; - System.arraycopy(sortedEntries, 0, r, 0, sortedEntries.length); + DirCacheEntry[] r = new DirCacheEntry[entryCnt]; + System.arraycopy(sortedEntries, 0, r, 0, entryCnt); return r; } if (!path.endsWith("/")) //$NON-NLS-1$ From 761814fe9c3fc0503d4654ef4aace6a804da5ae7 Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Sat, 28 Nov 2015 08:58:15 -0800 Subject: [PATCH 37/50] DirCacheEntry: Speed up creation by avoiding string cast The checkPath function is available as a byte[] form, in fact the String form just converts to byte[] to run the algorithm. Having DirCacheEntry take a byte[] -> String -> byte[] to check if each path is valid is a huge waste of CPU time. On some systems it can double the time required to read 38,999 files from trees to the DirCache. This slows down any operation using a DirCache. Expose the byte[] form and use it for DirCacheEntry creation. Change-Id: I6db7bc793ece99ff3c356338d793c07c061aeac7 --- .../jgit/dircache/DirCacheEntryTest.java | 4 ++-- .../jgit/dircache/DirCacheCheckout.java | 18 --------------- .../eclipse/jgit/dircache/DirCacheEntry.java | 23 ++++++++++++++----- .../org/eclipse/jgit/util/SystemReader.java | 15 ++++++++++++ 4 files changed, 34 insertions(+), 26 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java index e159ed939..dc7181aec 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java @@ -69,9 +69,9 @@ public void testIsValidPath() { assertFalse(isValidPath("a\u0000b")); } - private static boolean isValidPath(final String path) { + private static boolean isValidPath(String path) { try { - DirCacheCheckout.checkValidPath(path); + new DirCacheEntry(path); return true; } catch (InvalidPathException e) { return false; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java index 393a85494..4eb688170 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java @@ -1342,24 +1342,6 @@ private static void checkValidPath(CanonicalTreeParser t) checkValidPathSegment(chk, i); } - /** - * Check if path is a valid path for a checked out file name or ref name. - * - * @param path - * @throws InvalidPathException - * if the path is invalid - * @since 3.3 - */ - static void checkValidPath(String path) throws InvalidPathException { - try { - SystemReader.getInstance().checkPath(path); - } catch (CorruptObjectException e) { - InvalidPathException p = new InvalidPathException(path); - p.initCause(e); - throw p; - } - } - private static void checkValidPathSegment(ObjectChecker chk, CanonicalTreeParser t) throws InvalidPathException { try { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java index eef2e6d3c..404ff1738 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java @@ -65,6 +65,7 @@ import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.MutableInteger; import org.eclipse.jgit.util.NB; +import org.eclipse.jgit.util.SystemReader; /** * A single file (or stage of a file) in a {@link DirCache}. @@ -191,7 +192,7 @@ public class DirCacheEntry { } try { - DirCacheCheckout.checkValidPath(toString(path)); + checkPath(path); } catch (InvalidPathException e) { CorruptObjectException p = new CorruptObjectException(e.getMessage()); @@ -263,7 +264,7 @@ public DirCacheEntry(final byte[] newPath) { /** * Create an empty entry at the specified stage. * - * @param newPath + * @param path * name of the cache entry, in the standard encoding. * @param stage * the stage index of the new entry. @@ -274,16 +275,16 @@ public DirCacheEntry(final byte[] newPath) { * range 0..3, inclusive. */ @SuppressWarnings("boxing") - public DirCacheEntry(final byte[] newPath, final int stage) { - DirCacheCheckout.checkValidPath(toString(newPath)); + public DirCacheEntry(byte[] path, final int stage) { + checkPath(path); if (stage < 0 || 3 < stage) throw new IllegalArgumentException(MessageFormat.format( JGitText.get().invalidStageForPath, - stage, toString(newPath))); + stage, toString(path))); info = new byte[INFO_LEN]; infoOffset = 0; - path = newPath; + this.path = path; int flags = ((stage & 0x3) << 12); if (path.length < NAME_MASK) @@ -730,6 +731,16 @@ private int getExtendedFlags() { return 0; } + private static void checkPath(byte[] path) { + try { + SystemReader.getInstance().checkPath(path); + } catch (CorruptObjectException e) { + InvalidPathException p = new InvalidPathException(toString(path)); + p.initCause(e); + throw p; + } + } + private static String toString(final byte[] path) { return Constants.CHARSET.decode(ByteBuffer.wrap(path)).toString(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java index 4795c89e7..9860ef070 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java @@ -339,4 +339,19 @@ public String run() { public void checkPath(String path) throws CorruptObjectException { platformChecker.checkPath(path); } + + /** + * Check tree path entry for validity. + *

    + * Scans a multi-directory path string such as {@code "src/main.c"}. + * + * @param path + * path string to scan. + * @throws CorruptObjectException + * path is invalid. + * @since 4.2 + */ + public void checkPath(byte[] path) throws CorruptObjectException { + platformChecker.checkPath(path, 0, path.length); + } } From 45cc76524bc29d856340736a9de8d0889b17bc13 Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Sat, 28 Nov 2015 09:21:31 -0800 Subject: [PATCH 38/50] DirCache: Fix bad code formatting Line breaking before , is ugly to read. Most formatters and humans expect line break after , so update a few offensive locations. Use String.format() for the construction of the error message when a bad DirCachEntry is being failed on. This simplifies the code and its not a performance critical section. Change-Id: I5d990389e7ba24ef0861cf8ec0026ed030d4aeda --- .../org/eclipse/jgit/dircache/DirCacheBuilder.java | 13 +++++++------ .../org/eclipse/jgit/dircache/DirCacheEntry.java | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java index 6c7a70c52..73405cb40 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java @@ -102,8 +102,9 @@ protected DirCacheBuilder(final DirCache dc, final int ecnt) { */ public void add(final DirCacheEntry newEntry) { if (newEntry.getRawMode() == 0) - throw new IllegalArgumentException(MessageFormat.format(JGitText.get().fileModeNotSetForPath - , newEntry.getPathString())); + throw new IllegalArgumentException(MessageFormat.format( + JGitText.get().fileModeNotSetForPath, + newEntry.getPathString())); beforeAdd(newEntry); fastAdd(newEntry); } @@ -242,9 +243,9 @@ private void resort() { sorted = true; } - private static IllegalStateException bad(final DirCacheEntry a, - final String msg) { - return new IllegalStateException(msg + ": " + a.getStage() + " " //$NON-NLS-1$ //$NON-NLS-2$ - + a.getPathString()); + private static IllegalStateException bad(DirCacheEntry a, String msg) { + return new IllegalStateException(String.format( + "%s: %d %s", //$NON-NLS-1$ + msg, Integer.valueOf(a.getStage()), a.getPathString())); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java index 404ff1738..22c32ffd1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java @@ -499,8 +499,8 @@ public void setFileMode(final FileMode mode) { switch (mode.getBits() & FileMode.TYPE_MASK) { case FileMode.TYPE_MISSING: case FileMode.TYPE_TREE: - throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidModeForPath - , mode, getPathString())); + throw new IllegalArgumentException(MessageFormat.format( + JGitText.get().invalidModeForPath, mode, getPathString())); } NB.encodeInt32(info, infoOffset + P_MODE, mode.getBits()); } From b0eb744604391c94ec505f846604df1a07a166b1 Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Sun, 29 Nov 2015 12:04:03 -0800 Subject: [PATCH 39/50] Delay locating .gitattributes until requested Instead of checking every entry for .gitattributes only look for the entry on request by TreeWalk. This avoids impacting uses like RevWalk filtering history. When the attrs is requested skip to the start of the tree and look for .gitattributes until either it is found, or it is impossible to be present. Due to the sorting rules of tree entries .gitattributes should be among the first or second entries in the tree so very few entries will need to be considered. Waiting to find the .gitattributes file by native ordering may miss attrs for files like .config, which sorts before .gitattributes. Starting from the front of the tree on demand ensures the attributes are parsed as early as necessary to process any entry in the tree. Due to TreeWalk recursively processing up the tree of iterators we cannot just reset the current CanonicalTreeParser to the start as parent parsers share the same path buffer as their children. Resetting a parent to look for .gitattributes may overwrite path buffer data used by a child iterator. Work around this by building a new temporary CanonicalTreeParser instance. Change-Id: Ife950253b687be325340d27e9915c9a40df2641c --- .../treewalk/CanonicalTreeParserTest.java | 41 ++++++++++ .../jgit/treewalk/AbstractTreeIterator.java | 36 +++++++++ .../jgit/treewalk/CanonicalTreeParser.java | 80 +++++++++---------- 3 files changed, 114 insertions(+), 43 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/CanonicalTreeParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/CanonicalTreeParserTest.java index 52da69ef5..f5e97c2dc 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/CanonicalTreeParserTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/CanonicalTreeParserTest.java @@ -43,6 +43,8 @@ package org.eclipse.jgit.treewalk; +import static org.eclipse.jgit.lib.FileMode.REGULAR_FILE; +import static org.eclipse.jgit.lib.FileMode.SYMLINK; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; @@ -50,9 +52,11 @@ import java.io.ByteArrayOutputStream; +import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.TreeFormatter; import org.eclipse.jgit.util.RawParseUtils; import org.junit.Before; import org.junit.Test; @@ -369,4 +373,41 @@ public void testFreakingHugePathName() throws Exception { assertEquals(name, RawParseUtils.decode(Constants.CHARSET, ctp.path, ctp.pathOffset, ctp.pathLen)); } + + @Test + public void testFindAttributesWhenFirst() throws CorruptObjectException { + TreeFormatter tree = new TreeFormatter(); + tree.append(".gitattributes", REGULAR_FILE, hash_a); + ctp.reset(tree.toByteArray()); + + assertTrue(ctp.findFile(".gitattributes")); + assertEquals(REGULAR_FILE.getBits(), ctp.getEntryRawMode()); + assertEquals(".gitattributes", ctp.getEntryPathString()); + assertEquals(hash_a, ctp.getEntryObjectId()); + } + + @Test + public void testFindAttributesWhenSecond() throws CorruptObjectException { + TreeFormatter tree = new TreeFormatter(); + tree.append(".config", SYMLINK, hash_a); + tree.append(".gitattributes", REGULAR_FILE, hash_foo); + ctp.reset(tree.toByteArray()); + + assertTrue(ctp.findFile(".gitattributes")); + assertEquals(REGULAR_FILE.getBits(), ctp.getEntryRawMode()); + assertEquals(".gitattributes", ctp.getEntryPathString()); + assertEquals(hash_foo, ctp.getEntryObjectId()); + } + + @Test + public void testFindAttributesWhenMissing() throws CorruptObjectException { + TreeFormatter tree = new TreeFormatter(); + tree.append("src", REGULAR_FILE, hash_a); + tree.append("zoo", REGULAR_FILE, hash_foo); + ctp.reset(tree.toByteArray()); + + assertFalse(ctp.findFile(".gitattributes")); + assertEquals(11, ctp.idOffset()); // Did not walk the entire tree. + assertEquals("src", ctp.getEntryPathString()); + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java index aa5f32a87..5e7188957 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java @@ -327,6 +327,42 @@ int pathCompare(final AbstractTreeIterator p, final int pMode) { return pathCompare(p.path, cPos, p.pathLen, pMode, cPos); } + /** + * Seek the iterator on a file, if present. + * + * @param name + * file name to find (will not find a directory). + * @return true if the file exists in this tree; false otherwise. + * @throws CorruptObjectException + * tree is invalid. + * @since 4.2 + */ + public boolean findFile(String name) throws CorruptObjectException { + return findFile(Constants.encode(name)); + } + + /** + * Seek the iterator on a file, if present. + * + * @param name + * file name to find (will not find a directory). + * @return true if the file exists in this tree; false otherwise. + * @throws CorruptObjectException + * tree is invalid. + * @since 4.2 + */ + public boolean findFile(byte[] name) throws CorruptObjectException { + for (; !eof(); next(1)) { + int cmp = pathCompare(name, 0, name.length, 0, pathOffset); + if (cmp == 0) { + return true; + } else if (cmp > 0) { + return false; + } + } + return false; + } + /** * Compare the path of this current entry to a raw buffer. * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java index c24efe20a..c038f0772 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java @@ -44,6 +44,13 @@ package org.eclipse.jgit.treewalk; +import static org.eclipse.jgit.lib.Constants.DOT_GIT_ATTRIBUTES; +import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH; +import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; +import static org.eclipse.jgit.lib.Constants.OBJ_TREE; +import static org.eclipse.jgit.lib.Constants.TYPE_TREE; +import static org.eclipse.jgit.lib.Constants.encode; + import java.io.IOException; import java.io.InputStream; import java.util.Arrays; @@ -54,20 +61,15 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.lib.AnyObjectId; -import org.eclipse.jgit.lib.Constants; 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.util.RawParseUtils; /** Parses raw Git trees from the canonical semi-text/semi-binary format. */ public class CanonicalTreeParser extends AbstractTreeIterator { private static final byte[] EMPTY = {}; - - private static final byte[] ATTRS = Constants - .encode(Constants.DOT_GIT_ATTRIBUTES); + private static final byte[] ATTRS = encode(DOT_GIT_ATTRIBUTES); private byte[] raw; @@ -133,6 +135,7 @@ public CanonicalTreeParser getParent() { * the raw tree content. */ public void reset(final byte[] treeData) { + attributesNode = null; raw = treeData; prevPtr = -1; currPtr = 0; @@ -208,7 +211,7 @@ public CanonicalTreeParser next() { */ public void reset(final ObjectReader reader, final AnyObjectId id) throws IncorrectObjectTypeException, IOException { - reset(reader.open(id, Constants.OBJ_TREE).getCachedBytes()); + reset(reader.open(id, OBJ_TREE).getCachedBytes()); } @Override @@ -218,7 +221,7 @@ public CanonicalTreeParser createSubtreeIterator(final ObjectReader reader, idBuffer.fromRaw(idBuffer(), idOffset()); if (!FileMode.TREE.equals(mode)) { final ObjectId me = idBuffer.toObjectId(); - throw new IncorrectObjectTypeException(me, Constants.TYPE_TREE); + throw new IncorrectObjectTypeException(me, TYPE_TREE); } return createSubtreeIterator0(reader, idBuffer); } @@ -263,7 +266,7 @@ public byte[] idBuffer() { @Override public int idOffset() { - return nextPtr - Constants.OBJECT_ID_LENGTH; + return nextPtr - OBJECT_ID_LENGTH; } @Override @@ -301,7 +304,7 @@ public void next(int delta) { prevPtr = ptr; while (raw[ptr] != 0) ptr++; - ptr += Constants.OBJECT_ID_LENGTH + 1; + ptr += OBJECT_ID_LENGTH + 1; } if (delta != 0) throw new ArrayIndexOutOfBoundsException(delta); @@ -337,7 +340,7 @@ public void back(int delta) { trace[delta] = ptr; while (raw[ptr] != 0) ptr++; - ptr += Constants.OBJECT_ID_LENGTH + 1; + ptr += OBJECT_ID_LENGTH + 1; } if (trace[1] == -1) throw new ArrayIndexOutOfBoundsException(delta); @@ -372,12 +375,7 @@ private void parseEntry() { } } pathLen = tmp; - nextPtr = ptr + Constants.OBJECT_ID_LENGTH; - - // Check if this entry is a .gitattributes file - if (path[pathOffset] == '.' - && RawParseUtils.match(path, pathOffset, ATTRS) > 0) - attributesNode = new LazyLoadingAttributesNode(idOffset()); + nextPtr = ptr + OBJECT_ID_LENGTH; } /** @@ -391,36 +389,32 @@ private void parseEntry() { */ public AttributesNode getEntryAttributesNode(ObjectReader reader) throws IOException { - if (attributesNode instanceof LazyLoadingAttributesNode) - attributesNode = ((LazyLoadingAttributesNode) attributesNode) - .load(reader); - return attributesNode; + if (attributesNode == null) { + attributesNode = findAttributes(reader); + } + return attributesNode.getRules().isEmpty() ? null : attributesNode; } - /** - * {@link AttributesNode} implementation that provides lazy loading - */ - private class LazyLoadingAttributesNode extends AttributesNode { - private final int idOffset; - - LazyLoadingAttributesNode(int idOffset) { - super(Collections. emptyList()); - this.idOffset = idOffset; + private AttributesNode findAttributes(ObjectReader reader) + throws IOException { + CanonicalTreeParser itr = new CanonicalTreeParser(); + itr.reset(raw); + if (itr.findFile(ATTRS)) { + return loadAttributes(reader, itr.getEntryObjectId()); } + return noAttributes(); + } - AttributesNode load(ObjectReader reader) throws IOException { - AttributesNode r = new AttributesNode(); - ObjectId id = ObjectId.fromRaw(raw, idOffset); - ObjectLoader loader = reader.open(id); - if (loader != null) { - InputStream in = loader.openStream(); - try { - r.parse(in); - } finally { - in.close(); - } - } - return r.getRules().isEmpty() ? null : r; + private static AttributesNode loadAttributes(ObjectReader reader, + AnyObjectId id) throws IOException { + AttributesNode r = new AttributesNode(); + try (InputStream in = reader.open(id, OBJ_BLOB).openStream()) { + r.parse(in); } + return r.getRules().isEmpty() ? noAttributes() : r; + } + + private static AttributesNode noAttributes() { + return new AttributesNode(Collections. emptyList()); } } From 93eca6dfe1c013ab49fab3816c72e84e12e8112a Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Sat, 28 Nov 2015 12:34:55 -0800 Subject: [PATCH 40/50] DirCache: Add helper to read from a tree Application code sometimes wants to read a DirCache from an ObjectId, but its confusing how to do this because its buried inside the DirCacheBuilder. Use this utility in a few places within JGit that also want to read a DirCache from a tree's ObjectId. Change-Id: I578b7e18e58753d154937f4ab835012b09e5adca --- .../org/eclipse/jgit/dircache/DirCache.java | 24 ++++++++++++++ .../eclipse/jgit/merge/RecursiveMerger.java | 31 +------------------ .../jgit/transport/PushCertificateStore.java | 8 ++--- 3 files changed, 27 insertions(+), 36 deletions(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java index 6dcb9488b..fa0339544 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java @@ -71,9 +71,11 @@ import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.storage.file.FileSnapshot; import org.eclipse.jgit.internal.storage.file.LockFile; +import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; +import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.treewalk.FileTreeIterator; import org.eclipse.jgit.treewalk.TreeWalk; @@ -146,6 +148,28 @@ public static DirCache newInCore() { return new DirCache(null, null); } + /** + * Create a new in memory index read from the contents of a tree. + * + * @param reader + * reader to access the tree objects from a repository. + * @param treeId + * tree to read. Must identify a tree, not a tree-ish. + * @return a new cache which has no backing store file, but contains the + * contents of {@code treeId}. + * @throws IOException + * one or more trees not available from the ObjectReader. + * @since 4.2 + */ + public static DirCache read(ObjectReader reader, AnyObjectId treeId) + throws IOException { + DirCache d = newInCore(); + DirCacheBuilder b = d.builder(); + b.addTree(null, DirCacheEntry.STAGE_0, reader, treeId); + b.finish(); + return d; + } + /** * Create a new in-core index representation and read an index from disk. *

    diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java index aef47c58c..e0556447c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java @@ -57,8 +57,6 @@ import java.util.TimeZone; import org.eclipse.jgit.dircache.DirCache; -import org.eclipse.jgit.dircache.DirCacheBuilder; -import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.NoMergeBaseException; import org.eclipse.jgit.internal.JGitText; @@ -70,7 +68,6 @@ import org.eclipse.jgit.revwalk.filter.RevFilter; import org.eclipse.jgit.treewalk.AbstractTreeIterator; import org.eclipse.jgit.treewalk.EmptyTreeIterator; -import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.WorkingTreeIterator; /** @@ -181,7 +178,7 @@ protected RevCommit getBaseCommit(RevCommit a, RevCommit b, int callDepth) WorkingTreeIterator oldWTreeIt = workingTreeIterator; workingTreeIterator = null; try { - dircache = dircacheFromTree(currentBase.getTree()); + dircache = DirCache.read(reader, currentBase.getTree()); inCore = true; List parents = new ArrayList(); @@ -256,30 +253,4 @@ private static PersonIdent mockAuthor(List parents) { new Date((time + 1) * 1000L), TimeZone.getTimeZone("GMT+0000")); //$NON-NLS-1$ } - - /** - * Create a new in memory dircache which has the same content as a given - * tree. - * - * @param treeId - * the tree which should be used to fill the dircache - * @return a new in memory dircache - * @throws IOException - */ - private DirCache dircacheFromTree(ObjectId treeId) throws IOException { - DirCache ret = DirCache.newInCore(); - DirCacheBuilder aBuilder = ret.builder(); - try (TreeWalk atw = new TreeWalk(reader)) { - atw.addTree(treeId); - atw.setRecursive(true); - while (atw.next()) { - DirCacheEntry e = new DirCacheEntry(atw.getRawPath()); - e.setFileMode(atw.getFileMode(0)); - e.setObjectId(atw.getObjectId(0)); - aBuilder.add(e); - } - } - aBuilder.finish(); - return ret; - } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java index 8947f2779..d436e08df 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java @@ -65,7 +65,6 @@ import java.util.NoSuchElementException; import org.eclipse.jgit.dircache.DirCache; -import org.eclipse.jgit.dircache.DirCacheBuilder; import org.eclipse.jgit.dircache.DirCacheEditor; import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit; import org.eclipse.jgit.dircache.DirCacheEntry; @@ -448,13 +447,10 @@ public int compare(PendingCert a, PendingCert b) { } private DirCache newDirCache() throws IOException { - DirCache dc = DirCache.newInCore(); if (commit != null) { - DirCacheBuilder b = dc.builder(); - b.addTree(new byte[0], DirCacheEntry.STAGE_0, reader, commit.getTree()); - b.finish(); + return DirCache.read(reader, commit.getTree()); } - return dc; + return DirCache.newInCore(); } private ObjectId saveCert(ObjectInserter inserter, DirCache dc, From 2d011cd648ce33bc6eee577c380a178cb22bfe54 Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Sat, 28 Nov 2015 09:23:59 -0800 Subject: [PATCH 41/50] DirCacheBuilder: Speed up reading from trees Recursively copying a tree into a DirCache is a bottleneck for some algorithms like the in memory merge code in Gerrit Code Review. Drop a layer down in the stack and use CanonicalTreeParser directly as the addition logic only processes 1 tree at a time and does not need the merge sorting feature (or overhead) of TreeWalk. Combined with 761814fe9c ("DirCacheEntry: Speed up creation by avoiding string cast") tree loading 38,900 entries nearly halves in running time from 70ms to 36ms on some platforms. Change-Id: If1490ca25de0679a71cf508f59b486f9cc816165 --- .../jgit/dircache/DirCacheBuilder.java | 68 +++++++++++++------ .../eclipse/jgit/dircache/DirCacheEntry.java | 4 ++ 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java index 73405cb40..cfebe2d07 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java @@ -44,6 +44,9 @@ package org.eclipse.jgit.dircache; +import static org.eclipse.jgit.lib.FileMode.TYPE_MASK; +import static org.eclipse.jgit.lib.FileMode.TYPE_TREE; + import java.io.IOException; import java.text.MessageFormat; import java.util.Arrays; @@ -51,9 +54,7 @@ import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.ObjectReader; -import org.eclipse.jgit.treewalk.AbstractTreeIterator; import org.eclipse.jgit.treewalk.CanonicalTreeParser; -import org.eclipse.jgit.treewalk.TreeWalk; /** * Updates a {@link DirCache} by adding individual {@link DirCacheEntry}s. @@ -163,27 +164,56 @@ public void keep(final int pos, int cnt) { * @throws IOException * a tree cannot be read to iterate through its entries. */ - public void addTree(final byte[] pathPrefix, final int stage, - final ObjectReader reader, final AnyObjectId tree) throws IOException { - final TreeWalk tw = new TreeWalk(reader); - tw.addTree(new CanonicalTreeParser(pathPrefix, reader, tree - .toObjectId())); - tw.setRecursive(true); - if (tw.next()) { - final DirCacheEntry newEntry = toEntry(stage, tw); - beforeAdd(newEntry); - fastAdd(newEntry); - while (tw.next()) - fastAdd(toEntry(stage, tw)); + public void addTree(byte[] pathPrefix, int stage, ObjectReader reader, + AnyObjectId tree) throws IOException { + CanonicalTreeParser p = createTreeParser(pathPrefix, reader, tree); + while (!p.eof()) { + if (isTree(p)) { + p = enterTree(p, reader); + continue; + } + + DirCacheEntry first = toEntry(stage, p); + beforeAdd(first); + fastAdd(first); + p = p.next(); + break; + } + + // Rest of tree entries are correctly sorted; use fastAdd(). + while (!p.eof()) { + if (isTree(p)) { + p = enterTree(p, reader); + } else { + fastAdd(toEntry(stage, p)); + p = p.next(); + } } } - private DirCacheEntry toEntry(final int stage, final TreeWalk tw) { - final DirCacheEntry e = new DirCacheEntry(tw.getRawPath(), stage); - final AbstractTreeIterator i; + private static CanonicalTreeParser createTreeParser(byte[] pathPrefix, + ObjectReader reader, AnyObjectId tree) throws IOException { + return new CanonicalTreeParser(pathPrefix, reader, tree); + } - i = tw.getTree(0, AbstractTreeIterator.class); - e.setFileMode(tw.getFileMode(0)); + private static boolean isTree(CanonicalTreeParser p) { + return (p.getEntryRawMode() & TYPE_MASK) == TYPE_TREE; + } + + private static CanonicalTreeParser enterTree(CanonicalTreeParser p, + ObjectReader reader) throws IOException { + p = p.createSubtreeIterator(reader); + return p.eof() ? p.next() : p; + } + + private static DirCacheEntry toEntry(int stage, CanonicalTreeParser i) { + byte[] buf = i.getEntryPathBuffer(); + int len = i.getEntryPathLength(); + byte[] path = new byte[len]; + System.arraycopy(buf, 0, path, 0, len); + + DirCacheEntry e = new DirCacheEntry(path, stage); + e.setFileMode(i.getEntryRawMode()); e.setObjectIdFromRaw(i.idBuffer(), i.idOffset()); return e; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java index 22c32ffd1..c8bc0960f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java @@ -505,6 +505,10 @@ public void setFileMode(final FileMode mode) { NB.encodeInt32(info, infoOffset + P_MODE, mode.getBits()); } + void setFileMode(int mode) { + NB.encodeInt32(info, infoOffset + P_MODE, mode); + } + /** * Get the cached creation time of this file, in milliseconds. * From f89f30ffcdfbd6d18e674f9d5e565bd6a21ee938 Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Tue, 1 Dec 2015 14:20:44 -0800 Subject: [PATCH 42/50] ReceiveCommand.filter: Accept Iterable PreReceiveHook is given a Collection and it can be very useful here to call ReceiveCommand.filter(cmds, NOT_ATTEMPTED). Overload filter to accept both Iterable and List. Keep backwards binary compatibility for List by upcasting to Iterable. Change-Id: Ib1341876c703670945ef209edc8259715ee86c26 --- .../jgit/transport/ReceiveCommand.java | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java index 7c44dba4a..0cc7e5c50 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java @@ -46,6 +46,7 @@ import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import org.eclipse.jgit.internal.JGitText; @@ -126,6 +127,31 @@ public static enum Result { OK; } + /** + * Filter a collection of commands according to result. + * + * @param in + * commands to filter. + * @param want + * desired status to filter by. + * @return a copy of the command list containing only those commands with + * the desired status. + * @since 4.3 + */ + public static List filter(Iterable in, + Result want) { + List r; + if (in instanceof Collection) + r = new ArrayList<>(((Collection) in).size()); + else + r = new ArrayList<>(); + for (ReceiveCommand cmd : in) { + if (cmd.getResult() == want) + r.add(cmd); + } + return r; + } + /** * Filter a list of commands according to result. * @@ -138,13 +164,8 @@ public static enum Result { * @since 2.0 */ public static List filter(List commands, - final Result want) { - List r = new ArrayList(commands.size()); - for (final ReceiveCommand cmd : commands) { - if (cmd.getResult() == want) - r.add(cmd); - } - return r; + Result want) { + return filter((Iterable) commands, want); } private final ObjectId oldId; From f109af47d07ecc20767f245a384ebd1dc201b6af Mon Sep 17 00:00:00 2001 From: Matthias Sohn Date: Wed, 2 Dec 2015 15:24:26 +0100 Subject: [PATCH 43/50] Fix wrong @since tag In ReceiveCommand Change-Id: I58fa657dd4783fed0ffca94020c87c49d99009c6 Signed-off-by: Matthias Sohn --- .../src/org/eclipse/jgit/transport/ReceiveCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java index 0cc7e5c50..5702b6d7b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java @@ -136,7 +136,7 @@ public static enum Result { * desired status to filter by. * @return a copy of the command list containing only those commands with * the desired status. - * @since 4.3 + * @since 4.2 */ public static List filter(Iterable in, Result want) { From 3d8e6b1e16092701c31463092e945b8f00886bb7 Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Wed, 2 Dec 2015 21:47:58 -0800 Subject: [PATCH 44/50] Support atomic push in JGit client This should mirror the behavior of `git push --atomic` where the client asks the server to apply all-or-nothing. Some JGit servers already support this based on a custom DFS backend. InMemoryRepository is extended to support atomic push for unit testing purposes. Local disk server side support inside of JGit is a more complex animal due to the excessive amount of file locking required to protect every reference as a loose reference. Change-Id: I15083fbe48447678e034afeffb4639572a32f50c --- .../src/org/eclipse/jgit/pgm/Push.java | 4 + .../jgit/transport/AtomicPushTest.java | 200 ++++++++++++++++++ .../eclipse/jgit/internal/JGitText.properties | 1 + .../src/org/eclipse/jgit/api/PushCommand.java | 27 ++- .../org/eclipse/jgit/internal/JGitText.java | 1 + .../storage/dfs/InMemoryRepository.java | 182 +++++++++++++--- .../transport/BasePackPushConnection.java | 16 +- .../eclipse/jgit/transport/PushProcess.java | 23 +- .../org/eclipse/jgit/transport/Transport.java | 28 +++ 9 files changed, 447 insertions(+), 35 deletions(-) create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java index 1879ef51f..9098c1263 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java @@ -82,6 +82,9 @@ class Push extends TextBuiltin { @Option(name = "--all") private boolean all; + @Option(name = "--atomic") + private boolean atomic; + @Option(name = "--tags") private boolean tags; @@ -122,6 +125,7 @@ protected void run() throws Exception { push.setPushTags(); push.setRemote(remote); push.setThin(thin); + push.setAtomic(atomic); push.setTimeout(timeout); Iterable results = push.call(); for (PushResult result : results) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java new file mode 100644 index 000000000..782e414b6 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2015, 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.transport; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jgit.errors.TransportException; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription; +import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.NullProgressMonitor; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectInserter; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.resolver.ReceivePackFactory; +import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; +import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class AtomicPushTest { + private URIish uri; + private TestProtocol testProtocol; + private Object ctx = new Object(); + private InMemoryRepository server; + private InMemoryRepository client; + private ObjectId obj1; + private ObjectId obj2; + + @Before + public void setUp() throws Exception { + server = newRepo("server"); + client = newRepo("client"); + testProtocol = new TestProtocol<>( + null, + new ReceivePackFactory() { + @Override + public ReceivePack create(Object req, Repository db) + throws ServiceNotEnabledException, + ServiceNotAuthorizedException { + return new ReceivePack(db); + } + }); + uri = testProtocol.register(ctx, server); + + try (ObjectInserter ins = client.newObjectInserter()) { + obj1 = ins.insert(Constants.OBJ_BLOB, Constants.encode("test")); + obj2 = ins.insert(Constants.OBJ_BLOB, Constants.encode("file")); + ins.flush(); + } + } + + @After + public void tearDown() { + Transport.unregister(testProtocol); + } + + private static InMemoryRepository newRepo(String name) { + return new InMemoryRepository(new DfsRepositoryDescription(name)); + } + + @Test + public void pushNonAtomic() throws Exception { + PushResult r; + server.setPerformsAtomicTransactions(false); + Transport tn = testProtocol.open(uri, client, "server"); + try { + tn.setPushAtomic(false); + r = tn.push(NullProgressMonitor.INSTANCE, commands()); + } finally { + tn.close(); + } + + RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one"); + RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two"); + assertSame(RemoteRefUpdate.Status.OK, one.getStatus()); + assertSame( + RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED, + two.getStatus()); + } + + @Test + public void pushAtomicClientGivesUpEarly() throws Exception { + PushResult r; + Transport tn = testProtocol.open(uri, client, "server"); + try { + tn.setPushAtomic(true); + r = tn.push(NullProgressMonitor.INSTANCE, commands()); + } finally { + tn.close(); + } + + RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one"); + RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two"); + assertSame( + RemoteRefUpdate.Status.REJECTED_OTHER_REASON, + one.getStatus()); + assertSame( + RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED, + two.getStatus()); + assertEquals(JGitText.get().transactionAborted, one.getMessage()); + } + + @Test + public void pushAtomicDisabled() throws Exception { + List cmds = new ArrayList<>(); + cmds.add(new RemoteRefUpdate( + null, null, + obj1, "refs/heads/one", + true /* force update */, + null /* no local tracking ref */, + ObjectId.zeroId())); + cmds.add(new RemoteRefUpdate( + null, null, + obj2, "refs/heads/two", + true /* force update */, + null /* no local tracking ref */, + ObjectId.zeroId())); + + server.setPerformsAtomicTransactions(false); + Transport tn = testProtocol.open(uri, client, "server"); + try { + tn.setPushAtomic(true); + tn.push(NullProgressMonitor.INSTANCE, cmds); + fail("did not throw TransportException"); + } catch (TransportException e) { + assertEquals( + uri + ": " + JGitText.get().atomicPushNotSupported, + e.getMessage()); + } finally { + tn.close(); + } + } + + private List commands() throws IOException { + List cmds = new ArrayList<>(); + cmds.add(new RemoteRefUpdate( + null, null, + obj1, "refs/heads/one", + true /* force update */, + null /* no local tracking ref */, + ObjectId.zeroId())); + cmds.add(new RemoteRefUpdate( + null, null, + obj2, "refs/heads/two", + true /* force update */, + null /* no local tracking ref */, + obj1)); + return cmds; + } +} diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index d0e1c779e..0e9b0b59e 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -20,6 +20,7 @@ argumentIsNotAValidCommentString=Invalid comment: {0} atLeastOnePathIsRequired=At least one path is required. atLeastOnePatternIsRequired=At least one pattern is required. atLeastTwoFiltersNeeded=At least two filters needed. +atomicPushNotSupported=Atomic push not supported. authenticationNotSupported=authentication not supported badBase64InputCharacterAt=Bad Base64 input character at {0} : {1} (decimal) badEntryDelimiter=Bad entry delimiter diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java index 227e32236..f5b82bdd7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java @@ -89,9 +89,8 @@ public class PushCommand extends private String receivePack = RemoteConfig.DEFAULT_RECEIVE_PACK; private boolean dryRun; - + private boolean atomic; private boolean force; - private boolean thin = Transport.DEFAULT_PUSH_THIN; private OutputStream out; @@ -145,6 +144,7 @@ public Iterable call() throws GitAPIException, transports = Transport.openAll(repo, remote, Transport.Operation.PUSH); for (final Transport transport : transports) { transport.setPushThin(thin); + transport.setPushAtomic(atomic); if (receivePack != null) transport.setOptionReceivePack(receivePack); transport.setDryRun(dryRun); @@ -396,6 +396,29 @@ public PushCommand setThin(boolean thin) { return this; } + /** + * @return true if all-or-nothing behavior is requested. + * @since 4.2 + */ + public boolean isAtomic() { + return atomic; + } + + /** + * Requests atomic push (all references updated, or no updates). + * + * Default setting is false. + * + * @param atomic + * @return {@code this} + * @since 4.2 + */ + public PushCommand setAtomic(boolean atomic) { + checkCallable(); + this.atomic = atomic; + return this; + } + /** * @return the force preference for push operation */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index f6fd8a396..796eaaebf 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -79,6 +79,7 @@ public static JGitText get() { /***/ public String atLeastOnePathIsRequired; /***/ public String atLeastOnePatternIsRequired; /***/ public String atLeastTwoFiltersNeeded; + /***/ public String atomicPushNotSupported; /***/ public String authenticationNotSupported; /***/ public String badBase64InputCharacterAt; /***/ public String badEntryDelimiter; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java index 832e4fb6a..1c664b409 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java @@ -13,14 +13,22 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.storage.pack.PackExt; +import org.eclipse.jgit.lib.BatchRefUpdate; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectIdRef; +import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref.Storage; import org.eclipse.jgit.lib.SymbolicRef; +import org.eclipse.jgit.revwalk.RevObject; +import org.eclipse.jgit.revwalk.RevTag; import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.transport.ReceiveCommand; import org.eclipse.jgit.util.RefList; /** @@ -46,8 +54,8 @@ public InMemoryRepository build() throws IOException { static final AtomicInteger packId = new AtomicInteger(); private final DfsObjDatabase objdb; - private final DfsRefDatabase refdb; + private boolean performsAtomicTransactions = true; /** * Initialize a new in-memory repository. @@ -76,6 +84,17 @@ public DfsRefDatabase getRefDatabase() { return refdb; } + /** + * Enable (or disable) the atomic reference transaction support. + *

    + * Useful for testing atomic support enabled or disabled. + * + * @param atomic + */ + public void setPerformsAtomicTransactions(boolean atomic) { + performsAtomicTransactions = atomic; + } + private class MemObjDatabase extends DfsObjDatabase { private List packs = new ArrayList(); @@ -235,41 +254,143 @@ public void setReadAheadBytes(int b) { private class MemRefDatabase extends DfsRefDatabase { private final ConcurrentMap refs = new ConcurrentHashMap(); + private final ReadWriteLock lock = new ReentrantReadWriteLock(true /* fair */); MemRefDatabase() { super(InMemoryRepository.this); } + @Override + public boolean performsAtomicTransactions() { + return performsAtomicTransactions; + } + + @Override + public BatchRefUpdate newBatchUpdate() { + return new BatchRefUpdate(this) { + @Override + public void execute(RevWalk walk, ProgressMonitor monitor) + throws IOException { + if (performsAtomicTransactions()) { + try { + lock.writeLock().lock(); + batch(walk, getCommands()); + } finally { + lock.writeLock().unlock(); + } + } else { + super.execute(walk, monitor); + } + } + }; + } + @Override protected RefCache scanAllRefs() throws IOException { RefList.Builder ids = new RefList.Builder(); RefList.Builder sym = new RefList.Builder(); - for (Ref ref : refs.values()) { - if (ref.isSymbolic()) - sym.add(ref); - ids.add(ref); + try { + lock.readLock().lock(); + for (Ref ref : refs.values()) { + if (ref.isSymbolic()) + sym.add(ref); + ids.add(ref); + } + } finally { + lock.readLock().unlock(); } ids.sort(); sym.sort(); return new RefCache(ids.toRefList(), sym.toRefList()); } + private void batch(RevWalk walk, List cmds) { + // Validate that the target exists in a new RevWalk, as the RevWalk + // from the RefUpdate might be reading back unflushed objects. + Map peeled = new HashMap<>(); + try (RevWalk rw = new RevWalk(getRepository())) { + for (ReceiveCommand c : cmds) { + if (!ObjectId.zeroId().equals(c.getNewId())) { + try { + RevObject o = rw.parseAny(c.getNewId()); + if (o instanceof RevTag) { + peeled.put(o, rw.peel(o).copy()); + } + } catch (IOException e) { + c.setResult(ReceiveCommand.Result.REJECTED_MISSING_OBJECT); + reject(cmds); + return; + } + } + } + } + + // Check all references conform to expected old value. + for (ReceiveCommand c : cmds) { + Ref r = refs.get(c.getRefName()); + if (r == null) { + if (c.getType() != ReceiveCommand.Type.CREATE) { + c.setResult(ReceiveCommand.Result.LOCK_FAILURE); + reject(cmds); + return; + } + } else if (r.isSymbolic() || r.getObjectId() == null + || !r.getObjectId().equals(c.getOldId())) { + c.setResult(ReceiveCommand.Result.LOCK_FAILURE); + reject(cmds); + return; + } + } + + // Write references. + for (ReceiveCommand c : cmds) { + if (c.getType() == ReceiveCommand.Type.DELETE) { + refs.remove(c.getRefName()); + c.setResult(ReceiveCommand.Result.OK); + continue; + } + + ObjectId p = peeled.get(c.getNewId()); + Ref r; + if (p != null) { + r = new ObjectIdRef.PeeledTag(Storage.PACKED, + c.getRefName(), c.getNewId(), p); + } else { + r = new ObjectIdRef.PeeledNonTag(Storage.PACKED, + c.getRefName(), c.getNewId()); + } + refs.put(r.getName(), r); + c.setResult(ReceiveCommand.Result.OK); + } + clearCache(); + } + + private void reject(List cmds) { + for (ReceiveCommand c : cmds) { + if (c.getResult() == ReceiveCommand.Result.NOT_ATTEMPTED) { + c.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, + JGitText.get().transactionAborted); + } + } + } + @Override protected boolean compareAndPut(Ref oldRef, Ref newRef) throws IOException { - ObjectId id = newRef.getObjectId(); - if (id != null) { - try (RevWalk rw = new RevWalk(getRepository())) { - // Validate that the target exists in a new RevWalk, as the RevWalk - // from the RefUpdate might be reading back unflushed objects. - rw.parseAny(id); + try { + lock.writeLock().lock(); + ObjectId id = newRef.getObjectId(); + if (id != null) { + try (RevWalk rw = new RevWalk(getRepository())) { + // Validate that the target exists in a new RevWalk, as the RevWalk + // from the RefUpdate might be reading back unflushed objects. + rw.parseAny(id); + } } - } - String name = newRef.getName(); - if (oldRef == null) - return refs.putIfAbsent(name, newRef) == null; + String name = newRef.getName(); + if (oldRef == null) + return refs.putIfAbsent(name, newRef) == null; - synchronized (refs) { Ref cur = refs.get(name); Ref toCompare = cur; if (toCompare != null) { @@ -294,22 +415,29 @@ protected boolean compareAndPut(Ref oldRef, Ref newRef) if (eq(toCompare, oldRef)) return refs.replace(name, cur, newRef); } + + if (oldRef.getStorage() == Storage.NEW) + return refs.putIfAbsent(name, newRef) == null; + + return false; + } finally { + lock.writeLock().unlock(); } - - if (oldRef.getStorage() == Storage.NEW) - return refs.putIfAbsent(name, newRef) == null; - - return false; } @Override protected boolean compareAndRemove(Ref oldRef) throws IOException { - String name = oldRef.getName(); - Ref cur = refs.get(name); - if (cur != null && eq(cur, oldRef)) - return refs.remove(name, cur); - else - return false; + try { + lock.writeLock().lock(); + String name = oldRef.getName(); + Ref cur = refs.get(name); + if (cur != null && eq(cur, oldRef)) + return refs.remove(name, cur); + else + return false; + } finally { + lock.writeLock().unlock(); + } } private boolean eq(Ref a, Ref b) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java index 24fb3be64..f7bac6d06 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java @@ -44,6 +44,8 @@ package org.eclipse.jgit.transport; +import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_ATOMIC; + import java.io.IOException; import java.io.OutputStream; import java.text.MessageFormat; @@ -110,17 +112,15 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen public static final String CAPABILITY_SIDE_BAND_64K = GitProtocolConstants.CAPABILITY_SIDE_BAND_64K; private final boolean thinPack; + private final boolean atomic; + private boolean capableAtomic; private boolean capableDeleteRefs; - private boolean capableReport; - private boolean capableSideBand; - private boolean capableOfsDelta; private boolean sentCommand; - private boolean writePack; /** Time in milliseconds spent transferring the pack data. */ @@ -135,6 +135,7 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen public BasePackPushConnection(final PackTransport packTransport) { super(packTransport); thinPack = transport.isPushThin(); + atomic = transport.isPushAtomic(); } public void push(final ProgressMonitor monitor, @@ -224,6 +225,11 @@ protected void doPush(final ProgressMonitor monitor, private void writeCommands(final Collection refUpdates, final ProgressMonitor monitor, OutputStream outputStream) throws IOException { final String capabilities = enableCapabilities(monitor, outputStream); + if (atomic && !capableAtomic) { + throw new TransportException(uri, + JGitText.get().atomicPushNotSupported); + } + for (final RemoteRefUpdate rru : refUpdates) { if (!capableDeleteRefs && rru.isDelete()) { rru.setStatus(Status.REJECTED_NODELETE); @@ -259,6 +265,8 @@ private void writeCommands(final Collection refUpdates, private String enableCapabilities(final ProgressMonitor monitor, OutputStream outputStream) { final StringBuilder line = new StringBuilder(); + if (atomic) + capableAtomic = wantCapability(line, CAPABILITY_ATOMIC); capableReport = wantCapability(line, CAPABILITY_REPORT_STATUS); capableDeleteRefs = wantCapability(line, CAPABILITY_DELETE_REFS); capableOfsDelta = wantCapability(line, CAPABILITY_OFS_DELTA); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java index 9721ee9eb..b557812ad 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java @@ -47,6 +47,7 @@ import java.io.OutputStream; import java.text.MessageFormat; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -183,6 +184,7 @@ else if (!preprocessed.isEmpty()) private Map prepareRemoteUpdates() throws TransportException { + boolean atomic = transport.isPushAtomic(); final Map result = new HashMap(); for (final RemoteRefUpdate rru : toPush.values()) { final Ref advertisedRef = connection.getRef(rru.getRemoteName()); @@ -205,6 +207,9 @@ private Map prepareRemoteUpdates() if (rru.isExpectingOldObjectId() && !rru.getExpectedOldObjectId().equals(advertisedOld)) { rru.setStatus(Status.REJECTED_REMOTE_CHANGED); + if (atomic) { + return rejectAll(); + } continue; } @@ -236,14 +241,28 @@ private Map prepareRemoteUpdates() JGitText.get().readingObjectsFromLocalRepositoryFailed, x.getMessage()), x); } rru.setFastForward(fastForward); - if (!fastForward && !rru.isForceUpdate()) + if (!fastForward && !rru.isForceUpdate()) { rru.setStatus(Status.REJECTED_NONFASTFORWARD); - else + if (atomic) { + return rejectAll(); + } + } else { result.put(rru.getRemoteName(), rru); + } } return result; } + private Map rejectAll() { + for (RemoteRefUpdate rru : toPush.values()) { + if (rru.getStatus() == Status.NOT_ATTEMPTED) { + rru.setStatus(RemoteRefUpdate.Status.REJECTED_OTHER_REASON); + rru.setMessage(JGitText.get().transactionAborted); + } + } + return Collections.emptyMap(); + } + private void modifyUpdatesForDryRun() { for (final RemoteRefUpdate rru : toPush.values()) if (rru.getStatus() == Status.NOT_ATTEMPTED) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java index cc7db47df..6af153cbc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java @@ -752,6 +752,9 @@ private static String findTrackingRefName(final String remoteName, /** Should push produce thin-pack when sending objects to remote repository. */ private boolean pushThin = DEFAULT_PUSH_THIN; + /** Should push be all-or-nothing atomic behavior? */ + private boolean pushAtomic; + /** Should push just check for operation result, not really push. */ private boolean dryRun; @@ -969,6 +972,31 @@ public void setPushThin(final boolean pushThin) { this.pushThin = pushThin; } + /** + * Default setting is false. + * + * @return true if push requires all-or-nothing atomic behavior. + * @since 4.2 + */ + public boolean isPushAtomic() { + return pushAtomic; + } + + /** + * Request atomic push (all references succeed, or none do). + *

    + * Server must also support atomic push. If the server does not support the + * feature the push will abort without making changes. + * + * @param atomic + * true when push should be an all-or-nothing operation. + * @see PackTransport + * @since 4.2 + */ + public void setPushAtomic(final boolean atomic) { + this.pushAtomic = atomic; + } + /** * @return true if destination refs should be removed if they no longer * exist at the source repository. From ac89b47eeb3c843b2686739cc4ca75e7b14b7bbb Mon Sep 17 00:00:00 2001 From: Matthias Sohn Date: Fri, 27 Nov 2015 11:02:32 +0100 Subject: [PATCH 45/50] Fix NPE in HttpAuthMethod If the password char array is null constructing a new String from this array fails with a NPE. Add a null check to fix this. Change-Id: Ifae6eecca38d5f114861f44658a32521e6e96866 Signed-off-by: Matthias Sohn --- .../src/org/eclipse/jgit/transport/HttpAuthMethod.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java index 3594ea91b..998f28001 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java @@ -219,7 +219,8 @@ boolean authorize(URIish uri, CredentialsProvider credentialsProvider) { if (credentialsProvider.supports(u, p) && credentialsProvider.get(uri, u, p)) { username = u.getValue(); - password = new String(p.getValue()); + char[] v = p.getValue(); + password = (v == null) ? null : new String(p.getValue()); p.clear(); } else return false; From e12482deed7b6120b4f65da042dc73912c4c4075 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Mon, 7 Dec 2015 12:56:32 -0800 Subject: [PATCH 46/50] Use runtime retention for Nullable annotation JGit's Nullable type was added[1] in the hope of being able to add nullness annotations that (a) do not preclude building and running with Java 7 and (b) could be shared by Gerrit, which uses a custom Nullable type for other reasons[2]. Sharing a type is useful because Eclipse's null analysis is only able to use one Nullable type at a time in a given workspace (so for this analysis to function in a workspace used to develop Gerrit, JGit and Gerrit would need to use the same Nullable type). The new Nullable type has CLASS instead of RUNTIME retention because there wasn't any obvious use for the annotation at run time. Gerrit uses the Nullable annotation to communicate with Guice. Guice injection happens at runtime, so it needs to be able to read the @Nullable annotations at run time[3]. Otherwise Guice produces provisioning errors, such as 3) null returned by binding at com.google.gerrit.lucene.LuceneChangeIndex$Factory.create() but parameter 7 of com.google.gerrit.lucene.LuceneChangeIndex.() is not @Nullable Switch to RUNTIME retention to avoid this. While at it, update the javadoc to explain more clearly how this annotation relates to other Nullable types[4]. This should make it clearer why JGit needed another Nullable type: A. Avoiding dependency on Java 8 B. RUNTIME retention to allow Guice to read the annotation at run time C. Named Nullable so Guice can recognize the annotation D. Not an addition to Java EE's javax.annotation package, to avoid the split-package problem[2] that prevents the annotation from being readable at run time when loaded from an OSGi container E. Avoiding heavyweight dependencies, deprecated dependencies, and dependencies on package internals org.checkerframework.checker.nullness.qual.Nullable: A com.sun.istack.internal.Nullable: B, E *.CheckForNull, *.NullAllowed, etc: C edu.umd.cs.findbugs.annotations.Nullable: B, E javax.annotation.Nullable: D org.eclipse.jdt.annotation.Nullable: B org.jetbrains.annotations.Nullable: B org.jmlspecs.annotation.Nullable: E android.annotation.Nullable, android.support.annotation.Nullable: E [1] https://git.eclipse.org/r/59993 [2] https://gerrit-review.googlesource.com/50112 [3] https://github.com/google/guice/blob/master/core/src/com/google/inject/internal/Nullability.java [4] https://github.com/typetools/checker-framework/blob/5832a01f1/checker/src/org/checkerframework/checker/nullness/NullnessAnnotatedTypeFactory.java#L118 http://types.cs.washington.edu/checker-framework/current/checker-framework-manual.html#nullness-related-work Change-Id: I6c482653d2b53e3509abb11211b67fc29cf2949c Signed-off-by: Jonathan Nieder --- .../eclipse/jgit/annotations/Nullable.java | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java index 4275dc4fa..7b9156710 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java @@ -54,15 +54,46 @@ import java.lang.annotation.Target; /** - * JGit's replacement for the {@code javax.annotations.Nullable}. + * Marks types that can hold the value {@code null} at run time. *

    - * Denotes that a local variable, parameter, field, method return value can be - * {@code null}. + * Unlike {@code org.eclipse.jdt.annotation.Nullable}, this has run-time + * retention, allowing the annotation to be recognized by + * Guice. Unlike + * {@code javax.annotation.Nullable}, this does not involve importing new classes + * to a standard (Java EE) package, so it can be deployed in an OSGi container + * without running into + * split-package + * problems. + *

    + * You can use this annotation to qualify a type in a method signature or local + * variable declaration. The entity whose type has this annotation is allowed to + * hold the value {@code null} at run time. This allows annotation based null + * analysis to infer that + *

      + *
    • Binding a {@code null} value to the entity is legal. + *
    • Dereferencing the entity is unsafe and can trigger a + * {@code NullPointerException}. + *
    + *

    + * To avoid a dependency on Java 8, this annotation does not use + * {@link Target @Target} {@code TYPE_USE}. That may change when JGit starts + * requiring Java 8. + *

    + * Warning: Please do not use this annotation on arrays. Different + * annotation processors treat {@code @Nullable Object[]} differently: some + * treat it as an array of nullable objects, for consistency with versions of + * {@code Nullable} defined with {@code @Target} {@code TYPE_USE}, while others + * treat it as a nullable array of objects. JGit therefore avoids using this + * annotation on arrays altogether. + * + * @see + * The checker-framework manual * * @since 4.2 */ @Documented -@Retention(RetentionPolicy.CLASS) +@Retention(RetentionPolicy.RUNTIME) @Target({ FIELD, METHOD, PARAMETER, LOCAL_VARIABLE }) public @interface Nullable { // marker annotation with no members From e23521f0d9040d49dead2ce6fbfab6bde3b7b2c6 Mon Sep 17 00:00:00 2001 From: Kaloyan Raev Date: Mon, 6 Jan 2014 14:40:47 +0200 Subject: [PATCH 47/50] Add remote command to JGit CLI Supported subcommands are: - (lists available remotes) - add - remove - set-url - update Supported options are: --verbose --push --prune Bug: 481316 Change-Id: I57c34ed6daabb7d308bc383b17c1ef4af433e714 Signed-off-by: Kaloyan Raev Signed-off-by: Matthias Sohn --- .../tst/org/eclipse/jgit/pgm/RemoteTest.java | 159 ++++++++++++++ .../services/org.eclipse.jgit.pgm.TextBuiltin | 1 + .../jgit/pgm/internal/CLIText.properties | 3 + .../src/org/eclipse/jgit/pgm/Remote.java | 197 ++++++++++++++++++ .../eclipse/jgit/pgm/internal/CLIText.java | 1 + .../jgit/api/AbstractRemoteCommandTest.java | 96 +++++++++ .../jgit/api/RemoteAddCommandTest.java | 81 +++++++ .../jgit/api/RemoteDeleteCommandTest.java | 68 ++++++ .../jgit/api/RemoteListCommandTest.java | 68 ++++++ .../jgit/api/RemoteSetUrlCommandTest.java | 100 +++++++++ .../src/org/eclipse/jgit/api/Git.java | 44 +++- .../eclipse/jgit/api/RemoteAddCommand.java | 133 ++++++++++++ .../eclipse/jgit/api/RemoteListCommand.java | 91 ++++++++ .../eclipse/jgit/api/RemoteRemoveCommand.java | 110 ++++++++++ .../eclipse/jgit/api/RemoteSetUrlCommand.java | 155 ++++++++++++++ .../org/eclipse/jgit/transport/URIish.java | 2 + 16 files changed, 1307 insertions(+), 2 deletions(-) create mode 100644 org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RemoteTest.java create mode 100644 org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Remote.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AbstractRemoteCommandTest.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteAddCommandTest.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteDeleteCommandTest.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteListCommandTest.java create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteSetUrlCommandTest.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteAddCommand.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteListCommand.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RemoteTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RemoteTest.java new file mode 100644 index 000000000..58e0e19f8 --- /dev/null +++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RemoteTest.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2015, Kaloyan Raev + * 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 static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.lib.CLIRepositoryTestCase; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.transport.RefSpec; +import org.eclipse.jgit.transport.RemoteConfig; +import org.eclipse.jgit.transport.URIish; +import org.junit.Before; +import org.junit.Test; + +public class RemoteTest extends CLIRepositoryTestCase { + + private StoredConfig config; + + private RemoteConfig remote; + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + + // create another repository + Repository remoteRepository = createWorkRepository(); + + // set it up as a remote to this repository + config = db.getConfig(); + remote = new RemoteConfig(config, "test"); + remote.addFetchRefSpec( + new RefSpec("+refs/heads/*:refs/remotes/test/*")); + URIish uri = new URIish( + remoteRepository.getDirectory().toURI().toURL()); + remote.addURI(uri); + remote.update(config); + config.save(); + + Git.wrap(remoteRepository).commit().setMessage("initial commit").call(); + } + + @Test + public void testList() throws Exception { + assertArrayEquals(new String[] { remote.getName(), "" }, + execute("git remote")); + } + + @Test + public void testVerboseList() throws Exception { + assertArrayEquals( + new String[] { + String.format("%s\t%s (fetch)", remote.getName(), + remote.getURIs().get(0)), + String.format("%s\t%s (push)", remote.getName(), + remote.getURIs().get(0)), + "" }, + execute("git remote -v")); + } + + @Test + public void testAdd() throws Exception { + assertArrayEquals(new String[] { "" }, + execute("git remote add second git://test.com/second")); + + List remotes = RemoteConfig.getAllRemoteConfigs(config); + assertEquals(2, remotes.size()); + assertEquals("second", remotes.get(0).getName()); + assertEquals("test", remotes.get(1).getName()); + } + + @Test + public void testRemove() throws Exception { + assertArrayEquals(new String[] { "" }, + execute("git remote remove test")); + + assertTrue(RemoteConfig.getAllRemoteConfigs(config).isEmpty()); + } + + @Test + public void testSetUrl() throws Exception { + assertArrayEquals(new String[] { "" }, + execute("git remote set-url test git://test.com/test")); + + RemoteConfig result = new RemoteConfig(config, "test"); + assertEquals("test", result.getName()); + assertArrayEquals(new URIish[] { new URIish("git://test.com/test") }, + result.getURIs().toArray()); + assertTrue(result.getPushURIs().isEmpty()); + } + + @Test + public void testSetUrlPush() throws Exception { + assertArrayEquals(new String[] { "" }, + execute("git remote set-url --push test git://test.com/test")); + + RemoteConfig result = new RemoteConfig(config, "test"); + assertEquals("test", result.getName()); + assertEquals(remote.getURIs(), result.getURIs()); + assertArrayEquals(new URIish[] { new URIish("git://test.com/test") }, + result.getPushURIs().toArray()); + } + + @Test + public void testUpdate() throws Exception { + assertArrayEquals(new String[] { + "From " + remote.getURIs().get(0).toString(), + " * [new branch] master -> test/master", "", "" }, + execute("git remote update test")); + } + +} 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 e1b05491b..c13f63e80 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 @@ -24,6 +24,7 @@ org.eclipse.jgit.pgm.MergeBase org.eclipse.jgit.pgm.Push org.eclipse.jgit.pgm.ReceivePack org.eclipse.jgit.pgm.Reflog +org.eclipse.jgit.pgm.Remote org.eclipse.jgit.pgm.Repo org.eclipse.jgit.pgm.Reset org.eclipse.jgit.pgm.RevList diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties index 8aeb7e875..335336da2 100644 --- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties +++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties @@ -188,6 +188,7 @@ treeIsRequired=argument tree is required tooManyRefsGiven=Too many refs given unknownIoErrorStdout=An unknown I/O error occurred on standard output unknownMergeStrategy=unknown merge strategy {0} specified +unknownSubcommand=Unknown subcommand: {0} unmergedPaths=Unmerged paths: unsupportedOperation=Unsupported operation: {0} untrackedFiles=Untracked files: @@ -222,6 +223,7 @@ usage_MergeBase=Find as good common ancestors as possible for a merge usage_MergesTwoDevelopmentHistories=Merges two development histories usage_ReadDirCache= Read the DirCache 100 times usage_RebuildCommitGraph=Recreate a repository from another one's commit graph +usage_Remote=Manage set of tracked repositories usage_RepositoryToReadFrom=Repository to read from usage_RepositoryToReceiveInto=Repository to receive into usage_RevList=List commit objects in reverse chronological order @@ -329,6 +331,7 @@ usage_performFsckStyleChecksOnReceive=perform fsck style checks on receive usage_portNumberToListenOn=port number to listen on usage_printOnlyBranchesThatContainTheCommit=print only branches that contain the commit usage_pruneStaleTrackingRefs=prune stale tracking refs +usage_pushUrls=push URLs are manipulated usage_quiet=don't show progress messages usage_recordChangesToRepository=Record changes to the repository usage_recurseIntoSubtrees=recurse into subtrees diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Remote.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Remote.java new file mode 100644 index 000000000..70868e920 --- /dev/null +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Remote.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2015, Kaloyan Raev + * 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.IOException; +import java.io.StringWriter; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.RemoteAddCommand; +import org.eclipse.jgit.api.RemoteListCommand; +import org.eclipse.jgit.api.RemoteRemoveCommand; +import org.eclipse.jgit.api.RemoteSetUrlCommand; +import org.eclipse.jgit.api.errors.JGitInternalException; +import org.eclipse.jgit.pgm.internal.CLIText; +import org.eclipse.jgit.pgm.opt.CmdLineParser; +import org.eclipse.jgit.transport.RemoteConfig; +import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.util.io.ThrowingPrintWriter; +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.Option; + +@Command(common = false, usage = "usage_Remote") +class Remote extends TextBuiltin { + + @Option(name = "--verbose", aliases = { "-v" }, usage = "usage_beVerbose") + private boolean verbose = false; + + @Option(name = "--prune", aliases = { + "-p" }, usage = "usage_pruneStaleTrackingRefs") + private boolean prune; + + @Option(name = "--push", usage = "usage_pushUrls") + private boolean push; + + @Argument(index = 0, metaVar = "metaVar_command") + private String command; + + @Argument(index = 1, metaVar = "metaVar_remoteName") + private String name; + + @Argument(index = 2, metaVar = "metaVar_uriish") + private String uri; + + @Override + protected void run() throws Exception { + try (Git git = new Git(db)) { + if (command == null) { + RemoteListCommand cmd = git.remoteList(); + List remotes = cmd.call(); + print(remotes); + } else if ("add".equals(command)) { //$NON-NLS-1$ + RemoteAddCommand cmd = git.remoteAdd(); + cmd.setName(name); + cmd.setUri(new URIish(uri)); + cmd.call(); + } else if ("remove".equals(command) || "rm".equals(command)) { //$NON-NLS-1$ //$NON-NLS-2$ + RemoteRemoveCommand cmd = git.remoteRemove(); + cmd.setName(name); + cmd.call(); + } else if ("set-url".equals(command)) { //$NON-NLS-1$ + RemoteSetUrlCommand cmd = git.remoteSetUrl(); + cmd.setName(name); + cmd.setUri(new URIish(uri)); + cmd.setPush(push); + cmd.call(); + } else if ("update".equals(command)) { //$NON-NLS-1$ + // reuse fetch command for basic implementation of remote update + Fetch fetch = new Fetch(); + fetch.init(db, gitdir); + + // redirect the output stream + StringWriter osw = new StringWriter(); + fetch.outw = new ThrowingPrintWriter(osw); + // redirect the error stream + StringWriter esw = new StringWriter(); + fetch.errw = new ThrowingPrintWriter(esw); + + List fetchArgs = new ArrayList<>(); + if (verbose) { + fetchArgs.add("--verbose"); //$NON-NLS-1$ + } + if (prune) { + fetchArgs.add("--prune"); //$NON-NLS-1$ + } + if (name != null) { + fetchArgs.add(name); + } + + fetch.execute(fetchArgs.toArray(new String[fetchArgs.size()])); + + // flush the streams + fetch.outw.flush(); + fetch.errw.flush(); + outw.println(osw.toString()); + errw.println(esw.toString()); + } else { + throw new JGitInternalException(MessageFormat + .format(CLIText.get().unknownSubcommand, command)); + } + } + } + + @Override + public void printUsageAndExit(final String message, final CmdLineParser clp) + throws IOException { + errw.println(message); + errw.println("jgit remote [--verbose (-v)] [--help (-h)]"); //$NON-NLS-1$ + errw.println("jgit remote add name uri-ish [--help (-h)]"); //$NON-NLS-1$ + errw.println("jgit remote remove name [--help (-h)]"); //$NON-NLS-1$ + errw.println("jgit remote rm name [--help (-h)]"); //$NON-NLS-1$ + errw.println( + "jgit remote [--verbose (-v)] update [name] [--prune (-p)] [--help (-h)]"); //$NON-NLS-1$ + errw.println("jgit remote set-url name uri-ish [--push] [--help (-h)]"); //$NON-NLS-1$ + + errw.println(); + clp.printUsage(errw, getResourceBundle()); + errw.println(); + + errw.flush(); + throw die(true); + } + + private void print(List remotes) throws IOException { + for (RemoteConfig remote : remotes) { + String remoteName = remote.getName(); + if (verbose) { + List fetchURIs = remote.getURIs(); + List pushURIs = remote.getPushURIs(); + + String fetchURI = ""; //$NON-NLS-1$ + if (!fetchURIs.isEmpty()) { + fetchURI = fetchURIs.get(0).toString(); + } else if (!pushURIs.isEmpty()) { + fetchURI = pushURIs.get(0).toString(); + } + + String pushURI = ""; //$NON-NLS-1$ + if (!pushURIs.isEmpty()) { + pushURI = pushURIs.get(0).toString(); + } else if (!fetchURIs.isEmpty()) { + pushURI = fetchURIs.get(0).toString(); + } + + outw.println( + String.format("%s\t%s (fetch)", remoteName, fetchURI)); //$NON-NLS-1$ + outw.println( + String.format("%s\t%s (push)", remoteName, pushURI)); //$NON-NLS-1$ + } else { + outw.println(remoteName); + } + } + } + +} diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java index ce2b10c98..f5d581ad0 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java @@ -247,6 +247,7 @@ public static String formatLine(String line) { /***/ public String treeIsRequired; /***/ public char[] unknownIoErrorStdout; /***/ public String unknownMergeStrategy; + /***/ public String unknownSubcommand; /***/ public String unmergedPaths; /***/ public String unsupportedOperation; /***/ public String untrackedFiles; diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AbstractRemoteCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AbstractRemoteCommandTest.java new file mode 100644 index 000000000..d6a6342cb --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AbstractRemoteCommandTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2015, Kaloyan Raev + * 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.api; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.net.URISyntaxException; + +import org.eclipse.jgit.junit.RepositoryTestCase; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.transport.RefSpec; +import org.eclipse.jgit.transport.RemoteConfig; +import org.eclipse.jgit.transport.URIish; + +public class AbstractRemoteCommandTest extends RepositoryTestCase { + + protected static final String REMOTE_NAME = "test"; + + protected RemoteConfig setupRemote() + throws IOException, URISyntaxException { + // create another repository + Repository remoteRepository = createWorkRepository(); + + // set it up as a remote to this repository + final StoredConfig config = db.getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, REMOTE_NAME); + + RefSpec refSpec = new RefSpec(); + refSpec = refSpec.setForceUpdate(true); + refSpec = refSpec.setSourceDestination(Constants.R_HEADS + "*", + Constants.R_REMOTES + REMOTE_NAME + "/*"); + remoteConfig.addFetchRefSpec(refSpec); + + URIish uri = new URIish( + remoteRepository.getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + + remoteConfig.update(config); + config.save(); + + return remoteConfig; + } + + protected void assertRemoteConfigEquals(RemoteConfig expected, + RemoteConfig actual) { + assertEquals(expected.getName(), actual.getName()); + assertEquals(expected.getURIs(), actual.getURIs()); + assertEquals(expected.getPushURIs(), actual.getPushURIs()); + assertEquals(expected.getFetchRefSpecs(), actual.getFetchRefSpecs()); + assertEquals(expected.getPushRefSpecs(), actual.getPushRefSpecs()); + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteAddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteAddCommandTest.java new file mode 100644 index 000000000..ed0944676 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteAddCommandTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2015, Kaloyan Raev + * 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.api; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.RemoteConfig; +import org.eclipse.jgit.transport.URIish; +import org.junit.Test; + +public class RemoteAddCommandTest extends AbstractRemoteCommandTest { + + @Test + public void testAdd() throws Exception { + // create another repository + Repository remoteRepository = createWorkRepository(); + URIish uri = new URIish( + remoteRepository.getDirectory().toURI().toURL()); + + // execute the command to add a new remote + RemoteAddCommand cmd = Git.wrap(db).remoteAdd(); + cmd.setName(REMOTE_NAME); + cmd.setUri(uri); + RemoteConfig remote = cmd.call(); + + // assert that the added remote represents the remote repository + assertEquals(REMOTE_NAME, remote.getName()); + assertArrayEquals(new URIish[] { uri }, remote.getURIs().toArray()); + assertEquals(1, remote.getFetchRefSpecs().size()); + assertEquals( + String.format("+refs/heads/*:refs/remotes/%s/*", REMOTE_NAME), + remote.getFetchRefSpecs().get(0).toString()); + + // assert that the added remote is available in the git configuration + assertRemoteConfigEquals(remote, + new RemoteConfig(db.getConfig(), REMOTE_NAME)); + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteDeleteCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteDeleteCommandTest.java new file mode 100644 index 000000000..7055daff9 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteDeleteCommandTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2015, Kaloyan Raev + * 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.api; + +import static org.junit.Assert.assertTrue; + +import org.eclipse.jgit.transport.RemoteConfig; +import org.junit.Test; + +public class RemoteDeleteCommandTest extends AbstractRemoteCommandTest { + + @Test + public void testDelete() throws Exception { + // setup an initial remote + RemoteConfig remoteConfig = setupRemote(); + + // execute the command to remove the remote + RemoteRemoveCommand cmd = Git.wrap(db).remoteRemove(); + cmd.setName(REMOTE_NAME); + RemoteConfig remote = cmd.call(); + + // assert that the removed remote is the initial remote + assertRemoteConfigEquals(remoteConfig, remote); + // assert that there are no remotes left + assertTrue(RemoteConfig.getAllRemoteConfigs(db.getConfig()).isEmpty()); + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteListCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteListCommandTest.java new file mode 100644 index 000000000..cf522ff66 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteListCommandTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2015, Kaloyan Raev + * 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.api; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.eclipse.jgit.transport.RemoteConfig; +import org.junit.Test; + +public class RemoteListCommandTest extends AbstractRemoteCommandTest { + + @Test + public void testList() throws Exception { + // setup an initial remote + RemoteConfig remoteConfig = setupRemote(); + + // execute the command to list the remotes + List remotes = Git.wrap(db).remoteList().call(); + + // assert that there is only one remote + assertEquals(1, remotes.size()); + // assert that the available remote is the initial remote + assertRemoteConfigEquals(remoteConfig, remotes.get(0)); + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteSetUrlCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteSetUrlCommandTest.java new file mode 100644 index 000000000..6969c3df6 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteSetUrlCommandTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2015, Kaloyan Raev + * 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.api; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import org.eclipse.jgit.transport.RemoteConfig; +import org.eclipse.jgit.transport.URIish; +import org.junit.Test; + +public class RemoteSetUrlCommandTest extends AbstractRemoteCommandTest { + + @Test + public void testSetUrl() throws Exception { + // setup an initial remote + setupRemote(); + + // execute the command to change the fetch url + RemoteSetUrlCommand cmd = Git.wrap(db).remoteSetUrl(); + cmd.setName(REMOTE_NAME); + URIish newUri = new URIish("git://test.com/test"); + cmd.setUri(newUri); + RemoteConfig remote = cmd.call(); + + // assert that the changed remote has the new fetch url + assertEquals(REMOTE_NAME, remote.getName()); + assertArrayEquals(new URIish[] { newUri }, remote.getURIs().toArray()); + + // assert that the changed remote is available in the git configuration + assertRemoteConfigEquals(remote, + new RemoteConfig(db.getConfig(), REMOTE_NAME)); + } + + @Test + public void testSetPushUrl() throws Exception { + // setup an initial remote + RemoteConfig remoteConfig = setupRemote(); + + // execute the command to change the push url + RemoteSetUrlCommand cmd = Git.wrap(db).remoteSetUrl(); + cmd.setName(REMOTE_NAME); + URIish newUri = new URIish("git://test.com/test"); + cmd.setUri(newUri); + cmd.setPush(true); + RemoteConfig remote = cmd.call(); + + // assert that the changed remote has the old fetch url and the new push + // url + assertEquals(REMOTE_NAME, remote.getName()); + assertEquals(remoteConfig.getURIs(), remote.getURIs()); + assertArrayEquals(new URIish[] { newUri }, + remote.getPushURIs().toArray()); + + // assert that the changed remote is available in the git configuration + assertRemoteConfigEquals(remote, + new RemoteConfig(db.getConfig(), REMOTE_NAME)); + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java index addca4c46..2cd5f59a7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java @@ -713,8 +713,48 @@ public DescribeCommand describe() { } /** - * @return the git repository this class is interacting with; see {@link - * #close()} for notes on closing this repository. + * Return a command used to list the available remotes. + * + * @return a {@link RemoteListCommand} + * @since 4.2 + */ + public RemoteListCommand remoteList() { + return new RemoteListCommand(repo); + } + + /** + * Return a command used to add a new remote. + * + * @return a {@link RemoteAddCommand} + * @since 4.2 + */ + public RemoteAddCommand remoteAdd() { + return new RemoteAddCommand(repo); + } + + /** + * Return a command used to remove an existing remote. + * + * @return a {@link RemoteRemoveCommand} + * @since 4.2 + */ + public RemoteRemoveCommand remoteRemove() { + return new RemoteRemoveCommand(repo); + } + + /** + * Return a command used to change the URL of an existing remote. + * + * @return a {@link RemoteSetUrlCommand} + * @since 4.2 + */ + public RemoteSetUrlCommand remoteSetUrl() { + return new RemoteSetUrlCommand(repo); + } + + /** + * @return the git repository this class is interacting with; see + * {@link #close()} for notes on closing this repository. */ public Repository getRepository() { return repo; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteAddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteAddCommand.java new file mode 100644 index 000000000..679566903 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteAddCommand.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2015, Kaloyan Raev + * 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.api; + +import java.io.IOException; +import java.net.URISyntaxException; + +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.api.errors.JGitInternalException; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.transport.RefSpec; +import org.eclipse.jgit.transport.RemoteConfig; +import org.eclipse.jgit.transport.URIish; + +/** + * Used to add a new remote. + * + * This class has setters for all supported options and arguments of this + * command and a {@link #call()} method to finally execute the command. + * + * @see Git + * documentation about Remote + * + * @since 4.2 + */ +public class RemoteAddCommand extends GitCommand { + + private String name; + + private URIish uri; + + /** + * @param repo + */ + protected RemoteAddCommand(Repository repo) { + super(repo); + } + + /** + * The name of the remote to add. + * + * @param name + * a remote name + */ + public void setName(String name) { + this.name = name; + } + + /** + * The URL of the repository for the new remote. + * + * @param uri + * an URL for the remote + */ + public void setUri(URIish uri) { + this.uri = uri; + } + + /** + * Executes the {@code remote add} command with all the options and + * parameters collected by the setter methods of this class. + * + * @return the {@link RemoteConfig} object of the added remote + */ + @Override + public RemoteConfig call() throws GitAPIException { + checkCallable(); + + try { + StoredConfig config = repo.getConfig(); + RemoteConfig remote = new RemoteConfig(config, name); + + RefSpec refSpec = new RefSpec(); + refSpec = refSpec.setForceUpdate(true); + refSpec = refSpec.setSourceDestination(Constants.R_HEADS + "*", //$NON-NLS-1$ + Constants.R_REMOTES + name + "/*"); //$NON-NLS-1$ + remote.addFetchRefSpec(refSpec); + + remote.addURI(uri); + + remote.update(config); + config.save(); + return remote; + } catch (IOException | URISyntaxException e) { + throw new JGitInternalException(e.getMessage(), e); + } + + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteListCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteListCommand.java new file mode 100644 index 000000000..f778eaa28 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteListCommand.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015, Kaloyan Raev + * 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.api; + +import java.net.URISyntaxException; +import java.util.List; + +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.api.errors.JGitInternalException; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.RemoteConfig; + +/** + * Used to obtain the list of remotes. + * + * This class has setters for all supported options and arguments of this + * command and a {@link #call()} method to finally execute the command. + * + * @see Git + * documentation about Remote + * + * @since 4.2 + */ +public class RemoteListCommand extends GitCommand> { + + /** + * @param repo + */ + protected RemoteListCommand(Repository repo) { + super(repo); + } + + /** + * Executes the {@code remote} command with all the options and parameters + * collected by the setter methods of this class. + * + * @return a list of {@link RemoteConfig} objects. + */ + @Override + public List call() throws GitAPIException { + checkCallable(); + + try { + return RemoteConfig.getAllRemoteConfigs(repo.getConfig()); + } catch (URISyntaxException e) { + throw new JGitInternalException(e.getMessage(), e); + } + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java new file mode 100644 index 000000000..5782bf61b --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2015, Kaloyan Raev + * 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.api; + +import java.io.IOException; +import java.net.URISyntaxException; + +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.api.errors.JGitInternalException; +import org.eclipse.jgit.lib.ConfigConstants; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.transport.RemoteConfig; + +/** + * Used to remove an existing remote. + * + * This class has setters for all supported options and arguments of this + * command and a {@link #call()} method to finally execute the command. + * + * @see Git + * documentation about Remote + * + * @since 4.2 + */ +public class RemoteRemoveCommand extends GitCommand { + + private String name; + + /** + * @param repo + */ + protected RemoteRemoveCommand(Repository repo) { + super(repo); + } + + /** + * The name of the remote to remove. + * + * @param name + * a remote name + */ + public void setName(String name) { + this.name = name; + } + + /** + * Executes the {@code remote} command with all the options and parameters + * collected by the setter methods of this class. + * + * @return the {@link RemoteConfig} object of the removed remote + */ + @Override + public RemoteConfig call() throws GitAPIException { + checkCallable(); + + try { + StoredConfig config = repo.getConfig(); + RemoteConfig remote = new RemoteConfig(config, name); + config.unsetSection(ConfigConstants.CONFIG_KEY_REMOTE, name); + config.save(); + return remote; + } catch (IOException | URISyntaxException e) { + throw new JGitInternalException(e.getMessage(), e); + } + + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java new file mode 100644 index 000000000..6bd2ac799 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2015, Kaloyan Raev + * 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.api; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.List; + +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.api.errors.JGitInternalException; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.transport.RemoteConfig; +import org.eclipse.jgit.transport.URIish; + +/** + * Used to to change the URL of a remote. + * + * This class has setters for all supported options and arguments of this + * command and a {@link #call()} method to finally execute the command. + * + * @see Git + * documentation about Remote + * + * @since 4.2 + */ +public class RemoteSetUrlCommand extends GitCommand { + + private String name; + + private URIish uri; + + private boolean push; + + /** + * @param repo + */ + protected RemoteSetUrlCommand(Repository repo) { + super(repo); + } + + /** + * The name of the remote to change the URL for. + * + * @param name + * a remote name + */ + public void setName(String name) { + this.name = name; + } + + /** + * The new URL for the remote. + * + * @param uri + * an URL for the remote + */ + public void setUri(URIish uri) { + this.uri = uri; + } + + /** + * Whether to change the push URL of the remote instead of the fetch URL. + * + * @param push + * true to set the push url, false to + * set the fetch url + */ + public void setPush(boolean push) { + this.push = push; + } + + /** + * Executes the {@code remote} command with all the options and parameters + * collected by the setter methods of this class. + * + * @return the {@link RemoteConfig} object of the modified remote + */ + @Override + public RemoteConfig call() throws GitAPIException { + checkCallable(); + + try { + StoredConfig config = repo.getConfig(); + RemoteConfig remote = new RemoteConfig(config, name); + if (push) { + List uris = remote.getPushURIs(); + if (uris.size() > 1) { + throw new JGitInternalException( + "remote.newtest.pushurl has multiple values"); //$NON-NLS-1$ + } else if (uris.size() == 1) { + remote.removePushURI(uris.get(0)); + } + remote.addPushURI(uri); + } else { + List uris = remote.getURIs(); + if (uris.size() > 1) { + throw new JGitInternalException( + "remote.newtest.url has multiple values"); //$NON-NLS-1$ + } else if (uris.size() == 1) { + remote.removeURI(uris.get(0)); + } + remote.addURI(uri); + } + + remote.update(config); + config.save(); + return remote; + } catch (IOException | URISyntaxException e) { + throw new JGitInternalException(e.getMessage(), e); + } + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java index 3700b4955..3c22b1cad 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java @@ -593,6 +593,8 @@ public boolean equals(final Object obj) { private static boolean eq(final String a, final String b) { if (a == b) return true; + if (StringUtils.isEmptyOrNull(a) && StringUtils.isEmptyOrNull(b)) + return true; if (a == null || b == null) return false; return a.equals(b); From 85d09a9ec7d599d0234efd53b924dc2767a3843b Mon Sep 17 00:00:00 2001 From: Andrey Loskutov Date: Mon, 30 Nov 2015 22:48:13 +0100 Subject: [PATCH 48/50] URIish: fixed full uri pattern not expecting end of line after host name Bug: 483326 Change-Id: I8b6e3eb648c8ec2c38f73de22382537b1276b779 Signed-off-by: Andrey Loskutov --- .../org/eclipse/jgit/transport/URIishTest.java | 15 +++++++++++++++ .../src/org/eclipse/jgit/transport/URIish.java | 12 ++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java index 745c32201..76eb18afd 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java @@ -928,4 +928,19 @@ public void testALot() throws URISyntaxException { } } } + + @Test + public void testStringConstructor() throws Exception { + String str = "http://example.com/"; + URIish u = new URIish(str); + assertEquals("example.com", u.getHost()); + assertEquals("/", u.getPath()); + assertEquals(str, u.toString()); + + str = "http://example.com"; + u = new URIish(str); + assertEquals("example.com", u.getHost()); + assertEquals("", u.getPath()); + assertEquals(str, u.toString()); + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java index 3c22b1cad..9aeb840eb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java @@ -137,7 +137,11 @@ public class URIish implements Serializable { + OPT_PORT_P // + "(" // open a group capturing the user-home-dir-part //$NON-NLS-1$ + (USER_HOME_P + "?") //$NON-NLS-1$ - + "[\\\\/])" //$NON-NLS-1$ + + "(?:" // start non capturing group for host //$NON-NLS-1$ + // separator or end of line + + "[\\\\/])|$" //$NON-NLS-1$ + + ")" // close non capturing group for the host//$NON-NLS-1$ + // separator or end of line + ")?" // close the optional group containing hostname //$NON-NLS-1$ + "(.+)?" //$NON-NLS-1$ + "$"); //$NON-NLS-1$ @@ -640,7 +644,7 @@ private String format(final boolean includePassword, boolean escapeNonAscii) { if (getPath() != null) { if (getScheme() != null) { - if (!getPath().startsWith("/")) //$NON-NLS-1$ + if (!getPath().startsWith("/") && !getPath().isEmpty()) //$NON-NLS-1$ r.append('/'); } else if (getHost() != null) r.append(':'); @@ -711,9 +715,9 @@ public String toPrivateASCIIString() { */ public String getHumanishName() throws IllegalArgumentException { String s = getPath(); - if ("/".equals(s)) //$NON-NLS-1$ + if ("/".equals(s) || "".equals(s)) //$NON-NLS-1$ s = getHost(); - if ("".equals(s) || s == null) //$NON-NLS-1$ + if (s == null) // $NON-NLS-1$ throw new IllegalArgumentException(); String[] elements; From 5b55498b16267102b021a47b3a7c0bdbfae63e71 Mon Sep 17 00:00:00 2001 From: Christian Halstrick Date: Wed, 2 Dec 2015 09:16:03 +0100 Subject: [PATCH 49/50] Fix FileTreeIterator.idSubmodule(Entry) FileTreeIterator was calling by mistake WorkingTreeIterator.idSubmodule(Entry). Instead it should always compute idSubmodule on its own. Change-Id: Id1b988aded06939b1d7edd2671e34bf756896c0e --- .../tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java | 2 +- .../src/org/eclipse/jgit/treewalk/FileTreeIterator.java | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java index 863d79dde..3259f622f 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java @@ -87,7 +87,7 @@ public void setUp() throws Exception { .call(); submodule_db = (FileRepository) Git.wrap(db).submoduleAdd() - .setPath("submodule") + .setPath("modules/submodule") .setURI(submoduleStandalone.getDirectory().toURI().toString()) .call(); submoduleStandalone.close(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java index 8d2cb1d8c..accf4956f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java @@ -67,8 +67,8 @@ */ public class FileTreeIterator extends WorkingTreeIterator { /** - * the starting directory. This directory should correspond to the root of - * the repository. + * the starting directory of this Iterator. All entries are located directly + * in this directory. */ protected final File directory; @@ -238,8 +238,6 @@ public File getEntryFile() { @Override protected byte[] idSubmodule(final Entry e) { - if (repository == null) - return idSubmodule(getDirectory(), e); - return super.idSubmodule(e); + return idSubmodule(getDirectory(), e); } } From 4f8c993c494f1708eef264ca6e6e5193513ab51b Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Sun, 13 Dec 2015 20:26:01 -0800 Subject: [PATCH 50/50] push: Do not blindly overwrite peer If an application uses PushConnection directly on the native Git wire protocols JGit should send along the application's expected oldId, not the advertised value. This allows the remote peer to compare-and-swap since it was not tested inside JGit. Discovered when I tried to use a PushConnection (bypassing the standard PushProcess) and the client blindly overwrote the remote reference, even though my app had supplied the wrong ObjectId for the expectedOldObjectId. This was not expected and cost me over an hour of debugging, plus "corruption" in the remote repository. By passing along the exact expectedOldObjectId from the app the remote side can do the check that the application skipped, and avoid data loss. Change-Id: Id3920837e6c47100376225bb4dd61fa3e88c64db --- .../jgit/transport/PushConnectionTest.java | 143 ++++++++++++++++++ .../transport/BasePackPushConnection.java | 8 +- .../eclipse/jgit/transport/PushProcess.java | 3 + .../jgit/transport/RemoteRefUpdate.java | 6 +- 4 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java new file mode 100644 index 000000000..a3b4134ae --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2015, 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.transport; + +import static org.eclipse.jgit.transport.RemoteRefUpdate.Status.REJECTED_OTHER_REASON; +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription; +import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.NullProgressMonitor; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectInserter; +import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.resolver.ReceivePackFactory; +import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; +import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class PushConnectionTest { + private URIish uri; + private TestProtocol testProtocol; + private Object ctx = new Object(); + private InMemoryRepository server; + private InMemoryRepository client; + private ObjectId obj1; + private ObjectId obj2; + private ObjectId obj3; + private String refName = "refs/tags/blob"; + + @Before + public void setUp() throws Exception { + server = newRepo("server"); + client = newRepo("client"); + testProtocol = new TestProtocol<>( + null, + new ReceivePackFactory() { + @Override + public ReceivePack create(Object req, Repository db) + throws ServiceNotEnabledException, + ServiceNotAuthorizedException { + return new ReceivePack(db); + } + }); + uri = testProtocol.register(ctx, server); + + try (ObjectInserter ins = server.newObjectInserter()) { + obj1 = ins.insert(Constants.OBJ_BLOB, Constants.encode("test")); + obj3 = ins.insert(Constants.OBJ_BLOB, Constants.encode("not")); + ins.flush(); + + RefUpdate u = server.updateRef(refName); + u.setNewObjectId(obj1); + assertEquals(RefUpdate.Result.NEW, u.update()); + } + + try (ObjectInserter ins = client.newObjectInserter()) { + obj2 = ins.insert(Constants.OBJ_BLOB, Constants.encode("file")); + ins.flush(); + } + } + + @After + public void tearDown() { + Transport.unregister(testProtocol); + } + + private static InMemoryRepository newRepo(String name) { + return new InMemoryRepository(new DfsRepositoryDescription(name)); + } + + @Test + public void testWrongOldIdDoesNotReplace() throws IOException { + RemoteRefUpdate rru = new RemoteRefUpdate(null, null, obj2, refName, + false, null, obj3); + + Map updates = new HashMap<>(); + updates.put(rru.getRemoteName(), rru); + + Transport tn = testProtocol.open(uri, client, "server"); + try { + PushConnection connection = tn.openPush(); + try { + connection.push(NullProgressMonitor.INSTANCE, updates); + } finally { + connection.close(); + } + } finally { + tn.close(); + } + + assertEquals(REJECTED_OTHER_REASON, rru.getStatus()); + assertEquals("invalid old id sent", rru.getMessage()); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java index f7bac6d06..0834c359a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java @@ -237,9 +237,11 @@ private void writeCommands(final Collection refUpdates, } final StringBuilder sb = new StringBuilder(); - final Ref advertisedRef = getRef(rru.getRemoteName()); - final ObjectId oldId = (advertisedRef == null ? ObjectId.zeroId() - : advertisedRef.getObjectId()); + ObjectId oldId = rru.getExpectedOldObjectId(); + if (oldId == null) { + Ref adv = getRef(rru.getRemoteName()); + oldId = adv != null ? adv.getObjectId() : ObjectId.zeroId(); + } sb.append(oldId.name()); sb.append(' '); sb.append(rru.getNewObjectId().name()); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java index b557812ad..4fd192dbb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java @@ -212,6 +212,9 @@ private Map prepareRemoteUpdates() } continue; } + if (!rru.isExpectingOldObjectId()) { + rru.setExpectedOldObjectId(advertisedOld); + } // create ref (hasn't existed on remote side) and delete ref // are always fast-forward commands, feasible at this level diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java index 1b82a3610..5c5834618 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java @@ -125,7 +125,7 @@ public static enum Status { OK; } - private final ObjectId expectedOldObjectId; + private ObjectId expectedOldObjectId; private final ObjectId newObjectId; @@ -440,6 +440,10 @@ public String getMessage() { return message; } + void setExpectedOldObjectId(ObjectId id) { + expectedOldObjectId = id; + } + void setStatus(final Status status) { this.status = status; }