[JGIT PATCH 6/6] Teach fetch to prune stale tracking branches

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



If the source branch for a local tracking branch has been removed
from the remote repository, users may also want (or need) to delete
it from the local repository.  A good example is when a branch named
"refs/heads/bar" switches from a file to a directory, and the local
path name in "refs/remotes/origin/bar" must also change.

Local deletes are done before updates, to handle this special case
of file to directory (or directory to file) conversions with as few
errors as possible.

Signed-off-by: Shawn O. Pearce <spearce@xxxxxxxxxxx>
---
 .../org/spearce/jgit/pgm/AbstractFetchCommand.java |    8 ++-
 .../src/org/spearce/jgit/pgm/Fetch.java            |    9 ++++
 .../org/spearce/jgit/transport/FetchProcess.java   |   48 ++++++++++++++++++++
 .../src/org/spearce/jgit/transport/Transport.java  |   27 +++++++++++
 4 files changed, 89 insertions(+), 3 deletions(-)

diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/AbstractFetchCommand.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/AbstractFetchCommand.java
index ea6f277..f5a9d65 100644
--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/AbstractFetchCommand.java
+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/AbstractFetchCommand.java
@@ -39,6 +39,7 @@
 
 import org.kohsuke.args4j.Option;
 import org.spearce.jgit.lib.Constants;
+import org.spearce.jgit.lib.ObjectId;
 import org.spearce.jgit.lib.RefUpdate;
 import org.spearce.jgit.transport.FetchResult;
 import org.spearce.jgit.transport.TrackingRefUpdate;
@@ -75,9 +76,12 @@ private String longTypeOf(final TrackingRefUpdate u) {
 		final RefUpdate.Result r = u.getResult();
 		if (r == RefUpdate.Result.LOCK_FAILURE)
 			return "[lock fail]";
-
 		if (r == RefUpdate.Result.IO_FAILURE)
 			return "[i/o error]";
+		if (r == RefUpdate.Result.REJECTED)
+			return "[rejected]";
+		if (ObjectId.zeroId().equals(u.getNewObjectId()))
+			return "[deleted]";
 
 		if (r == RefUpdate.Result.NEW) {
 			if (u.getRemoteName().startsWith(Constants.R_HEADS))
@@ -99,8 +103,6 @@ else if (u.getLocalName().startsWith(Constants.R_TAGS))
 			return aOld + ".." + aNew;
 		}
 
-		if (r == RefUpdate.Result.REJECTED)
-			return "[rejected]";
 		if (r == RefUpdate.Result.NO_CHANGE)
 			return "[up to date]";
 		return "[" + r.name() + "]";
diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Fetch.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Fetch.java
index 8f3f7d5..81d6893 100644
--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Fetch.java
+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Fetch.java
@@ -56,6 +56,12 @@ void nofsck(final boolean ignored) {
 		fsck = Boolean.FALSE;
 	}
 
+	@Option(name = "--prune", usage = "prune stale tracking refs")
+	private Boolean prune;
+
+	@Option(name = "--dry-run")
+	private boolean dryRun;
+
 	@Option(name = "--thin", usage = "fetch thin pack")
 	private Boolean thin;
 
@@ -75,6 +81,9 @@ protected void run() throws Exception {
 		final Transport tn = Transport.open(db, remote);
 		if (fsck != null)
 			tn.setCheckFetchedObjects(fsck.booleanValue());
+		if (prune != null)
+			tn.setRemoveDeletedRefs(prune.booleanValue());
+		tn.setDryRun(dryRun);
 		if (thin != null)
 			tn.setFetchThin(thin.booleanValue());
 		final FetchResult r;
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/FetchProcess.java b/org.spearce.jgit/src/org/spearce/jgit/transport/FetchProcess.java
index 2ca8aeb..df64817 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/FetchProcess.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/FetchProcess.java
@@ -59,6 +59,7 @@
 import org.spearce.jgit.lib.ObjectId;
 import org.spearce.jgit.lib.ProgressMonitor;
 import org.spearce.jgit.lib.Ref;
+import org.spearce.jgit.lib.Repository;
 import org.spearce.jgit.revwalk.ObjectWalk;
 import org.spearce.jgit.revwalk.RevWalk;
 
@@ -152,6 +153,8 @@ else if (tagopt == TagOpt.FETCH_TAGS)
 		}
 
 		final RevWalk walk = new RevWalk(transport.local);
+		if (transport.isRemoveDeletedRefs())
+			deleteStaleTrackingRefs(result, walk);
 		for (TrackingRefUpdate u : localUpdates) {
 			try {
 				u.update(walk);
@@ -366,6 +369,51 @@ private TrackingRefUpdate createUpdate(final RefSpec spec,
 		return new TrackingRefUpdate(transport.local, spec, newId, "fetch");
 	}
 
+	private void deleteStaleTrackingRefs(final FetchResult result,
+			final RevWalk walk) throws TransportException {
+		final Repository db = transport.local;
+		for (final Ref ref : db.getAllRefs().values()) {
+			final String refname = ref.getName();
+			for (final RefSpec spec : toFetch) {
+				if (spec.matchDestination(refname)) {
+					final RefSpec s = spec.expandFromDestination(refname);
+					if (result.getAdvertisedRef(s.getSource()) == null) {
+						deleteTrackingRef(result, db, walk, s, ref);
+					}
+				}
+			}
+		}
+	}
+
+	private void deleteTrackingRef(final FetchResult result,
+			final Repository db, final RevWalk walk, final RefSpec spec,
+			final Ref localRef) throws TransportException {
+		final String name = localRef.getName();
+		try {
+			final TrackingRefUpdate u = new TrackingRefUpdate(db, name, spec
+					.getSource(), true, ObjectId.zeroId(), "deleted");
+			result.add(u);
+			if (transport.isDryRun()){
+				return;
+			}
+			u.delete(walk);
+			switch (u.getResult()) {
+			case NEW:
+			case NO_CHANGE:
+			case FAST_FORWARD:
+			case FORCED:
+				break;
+			default:
+				throw new TransportException(transport.getURI(),
+						"Cannot delete stale tracking ref " + name + ": "
+								+ u.getResult().name());
+			}
+		} catch (IOException e) {
+			throw new TransportException(transport.getURI(),
+					"Cannot delete stale tracking ref " + name, e);
+		}
+	}
+
 	private static boolean isTag(final Ref r) {
 		return isTag(r.getName());
 	}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java
index e58b72a..3aec5ca 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java
@@ -355,6 +355,9 @@ private static String findTrackingRefName(final String remoteName,
 	/** Should an incoming (fetch) transfer validate objects? */
 	private boolean checkFetchedObjects;
 
+	/** Should refs no longer on the source be pruned from the destination? */
+	private boolean removeDeletedRefs;
+
 	/**
 	 * Create a new transport instance.
 	 * 
@@ -516,6 +519,30 @@ public void setPushThin(final boolean pushThin) {
 	}
 
 	/**
+	 * @return true if destination refs should be removed if they no longer
+	 *         exist at the source repository.
+	 */
+	public boolean isRemoveDeletedRefs() {
+		return removeDeletedRefs;
+	}
+
+	/**
+	 * Set whether or not to remove refs which no longer exist in the source.
+	 * <p>
+	 * If true, refs at the destination repository (local for fetch, remote for
+	 * push) are deleted if they no longer exist on the source side (remote for
+	 * fetch, local for push).
+	 * <p>
+	 * False by default, as this may cause data to become unreachable, and
+	 * eventually be deleted on the next GC.
+	 *
+	 * @param remove true to remove refs that no longer exist.
+	 */
+	public void setRemoveDeletedRefs(final boolean remove) {
+		removeDeletedRefs = remove;
+	}
+
+	/**
 	 * Apply provided remote configuration on this transport.
 	 *
 	 * @param cfg
-- 
1.6.2.rc0.226.gf08f

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux