git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Shawn O. Pearce" <spearce@spearce.org>
To: Robin Rosenberg <robin.rosenberg@dewire.com>
Cc: git@vger.kernel.org
Subject: [JGIT PATCH 6/6] Teach fetch to prune stale tracking branches
Date: Thu, 12 Feb 2009 15:54:40 -0800	[thread overview]
Message-ID: <1234482880-1316-7-git-send-email-spearce@spearce.org> (raw)
In-Reply-To: <1234482880-1316-6-git-send-email-spearce@spearce.org>

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@spearce.org>
---
 .../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

  reply	other threads:[~2009-02-12 23:56 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-02-12 23:54 [JGIT PATCH 0/6] Add prune of stale tracking branches to fetch Shawn O. Pearce
2009-02-12 23:54 ` [JGIT PATCH 1/6] Fix RefUpdate.delete to update the result status Shawn O. Pearce
2009-02-12 23:54   ` [JGIT PATCH 2/6] Add setBoolean, setInt to RepositoryConfig Shawn O. Pearce
2009-02-12 23:54     ` [JGIT PATCH 3/6] Add RefSpec.expandFromDestination for reverse mappings Shawn O. Pearce
2009-02-12 23:54       ` [JGIT PATCH 4/6] Add the remote.name.mirror flag to RemoteConfig Shawn O. Pearce
2009-02-12 23:54         ` [JGIT PATCH 5/6] Don't pass TagOpt to FetchProcess, get it from the Transport Shawn O. Pearce
2009-02-12 23:54           ` Shawn O. Pearce [this message]
2009-02-13  0:37 ` [JGIT PATCH 0/6] Add prune of stale tracking branches to fetch Junio C Hamano
2009-02-13  0:42   ` Shawn O. Pearce
2009-02-13  0:48     ` Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1234482880-1316-7-git-send-email-spearce@spearce.org \
    --to=spearce@spearce.org \
    --cc=git@vger.kernel.org \
    --cc=robin.rosenberg@dewire.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).