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: Re: [EGIT PATCH 08/10] Add ref rename support to JGit
Date: Wed, 3 Jun 2009 09:43:30 -0700	[thread overview]
Message-ID: <20090603164330.GI3355@spearce.org> (raw)
In-Reply-To: <1243462137-24133-9-git-send-email-robin.rosenberg@dewire.com>

Robin Rosenberg <robin.rosenberg@dewire.com> wrote:
> Now refs can be renamed. The intent is that should be safe. Only the named
> refs and associated logs are updated. Any symbolic refs referring to the renames
> branches are unaffected, except HEAD, which is updated if the branch it refers
> to is being renamed.
...
> +//	public void testRenameBranchCannotLockAFileHEADisToLockHead() throws IOException {
> +//		an example following the rename failure pattern, but this one is ok!
> +//		tryRenameWhenLocked("HEAD", "refs/heads/b", "refs/heads/new/name",
> +//				"refs/heads/new/name");
> +//	}

Commented out test case?  Eh?

> +//	public void testRenameBranchCannotLockAFileHEADisOtherLockHead() throws IOException {
> +//		an example following the rename failure pattern, but this one is ok!
> +//		tryRenameWhenLocked("HEAD", "refs/heads/b", "refs/heads/new/name",
> +//				"refs/heads/a");
> +//	}

Ditto.

> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java
...
> +	RefRename newRename(String fromRef, String toRef) throws IOException {
> +		refreshPackedRefs();
> +		Ref f = readRefBasic(fromRef, 0);
> +		Ref t = readRefBasic(toRef, 0);
> +		if (t != null)
> +			throw new IOException("Ref rename target exists: " + t.getName());

NAK, I'd prefer to have the RefRename return successfully,
but when we try to execute it, it has a result that fails with
RefUpdate.REJECTED.

> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java
...
> +	static void renameTo(final Repository db, final RefUpdate from,
> +			final RefUpdate to) throws IOException {
> +		final File logdir = new File(db.getDirectory(), Constants.LOGS);
> +		final File reflogFrom = new File(logdir, from.getName());
> +		if (!reflogFrom.exists())
> +			return;
> +		final File reflogTo = new File(logdir, to.getName());
> +		final File reflogToDir = reflogTo.getParentFile();
> +		File tmp = new File(logdir,"tmp-renamed-log");
> +		tmp.delete(); // if any exists

That's not thread safe.  Most of the rest of JGit is actually
thread safe.  I think we should be here too.  We should use
a temporary file name inside logdir, one that isn't a legal
ref name at all.  Maybe:

  File tmp = File.createTempFile("tmp..renamed", "-log", logdir);
  if (!reflogFrom.renameTo(tmp)) {
    tmp.delete();
    if (!reflogFrom.renameTo(tmp))
      throw new IOException....
  }

> +		if (!reflogToDir.exists() && !reflogToDir.mkdirs()) {
> +			throw new IOException("Cannot create directory " + reflogToDir);
> +		}
> +		if (!tmp.renameTo(reflogTo)) {
> +			throw new IOException("Cannot rename (" + tmp + ")" + reflogFrom
> +					+ " to " + reflogTo);
> +		}

If we fail here, should we attempt to put the tmp log file back
into the old name?  Or leave it orphaned under the tmp file?

> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java
...
> +	public Result rename() throws IOException {
> +		Ref oldRef = oldFromDelete.db.readRef(Constants.HEAD);
> +		boolean renameHEADtoo = oldRef != null
> +				&& oldRef.getName().equals(oldFromDelete.getName());
> +		try {
> +			Repository db = oldFromDelete.getRepository();

Style nit: Hoist to top of method, so oldRef init can use it.

> +			RefLogWriter.renameTo(db, oldFromDelete,
> +					newToUpdate);

Style nit: This fits on one line.

> +			newToUpdate.setRefLogMessage(null, false);
> +			RefUpdate tmpUpdateRef = oldFromDelete.db.getRepository().updateRef(
> +					"RENAMED-REF");

Like the log, this isn't thread safe.

> +			if (renameHEADtoo) {
> +				try {
> +					oldFromDelete.db.link(Constants.HEAD, "RENAMED-REF");
> +				} catch (IOException e) {
> +					RefLogWriter.renameTo(db,
> +							newToUpdate, oldFromDelete);
> +					return renameResult = Result.LOCK_FAILURE;
> +				}
> +			}
> +			tmpUpdateRef.setNewObjectId(oldFromDelete.getOldObjectId());

We should also do:

  oldFromDelete.setExpectedOldObjectId(oldFromDelete.getOldObjectId())

so that if the old ref was modified between the time we read it
and the time we delete it, we fail on the delete, and don't whack
a ref that was modified by another thread.

> +			RefLogWriter.append(this, "jgit branch: renamed "

I think this should just be "renamed: $old to $new".

> +					+ db.shortenRefName(oldFromDelete.getName()) + " to "
> +					+ db.shortenRefName(newToUpdate.getName()));
> +			return renameResult = Result.RENAMED;
> +		} catch (RuntimeException e) {
> +			throw e;

What's the point of this catch block?

> +		} catch (IOException e) {
> +			System.err.println(e);

Please don't print to System.err from such a low level method.

> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java
...
> +		/**
> +		 * The ref was renamed from another name
> +		 * <p>
> +		 */
> +		RENAMED

Is a new state really necessary?  Can't we use NEW on success?
After all, the dst is now created.

>  	}
>  
>  	/** Repository the ref is stored in. */
> -	private final RefDatabase db;
> +	final RefDatabase db;

I wonder if this shouldn't just be a public accessor method; we expose
getRepository() on other application visible objects like RevWalk.
  
>  	/** Location of the loose file holding the value of this ref. */
> -	private final File looseFile;
> +	final File looseFile;

I don't see this used outside of the class, is it still necessary?

>  	/** Result of the update operation. */
> -	private Result result = Result.NOT_ATTEMPTED;
> +	Result result = Result.NOT_ATTEMPTED;

Ditto.
  
> +	static void deleteEmptyDir(File dir, int depth) {
> +		for (; depth > 0 && dir != null; depth--) {
> +			if (dir.exists() && !dir.delete())

Why bother with a stat() call, just to do an rmdir()?

> -	private static int count(final String s, final char c) {
> +	static int count(final String s, final char c) {

Since all callers pass '/', maybe we should just change this to
use '/' as a constant in the loop and remove the char c parameter?
I think that would fix the nasty line wrapping in RefLogWriter too.

-- 
Shawn.

  parent reply	other threads:[~2009-06-03 16:43 UTC|newest]

Thread overview: 43+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-05-06 23:32 [EGIT PATCH 0/3] Rename refs Robin Rosenberg
2009-05-06 23:32 ` [EGIT PATCH 1/3] Add ref rename support to JGit Robin Rosenberg
2009-05-06 23:33   ` [EGIT PATCH 2/3] Use Constants.R_* in Branch dialog Robin Rosenberg
2009-05-06 23:33     ` [EGIT PATCH 3/3] Add ref rename support to the branch dialog Robin Rosenberg
2009-05-07 15:51   ` [EGIT PATCH 1/3] Add ref rename support to JGit Shawn O. Pearce
2009-05-19 23:13     ` [EGIT PATCH 0/6] Ref rename Robin Rosenberg
2009-05-19 23:13       ` [EGIT PATCH 1/6] Make sure we get the right storage for loose/pack/loose and packed refs Robin Rosenberg
2009-05-19 23:13         ` [EGIT PATCH 2/6] Add ref rename support to JGit Robin Rosenberg
2009-05-19 23:13           ` [EGIT PATCH 3/6] Add ref rename support to the branch dialog Robin Rosenberg
2009-05-19 23:13             ` [EGIT PATCH 4/6] Allow non-ASCII ref names when writing the packed-refs file Robin Rosenberg
2009-05-19 23:13               ` [EGIT PATCH 5/6] Use Constants.PACKED_REFS in RefWriter Robin Rosenberg
2009-05-19 23:13                 ` [EGIT PATCH 6/6] Improve error reporting in the branch dialog Robin Rosenberg
2009-05-20 22:16           ` [EGIT PATCH 2/6] Add ref rename support to JGit Shawn O. Pearce
2009-05-27 22:08             ` [EGIT PATCH 00/10] Rename support Robin Rosenberg
2009-05-27 22:08               ` [EGIT PATCH 01/10] Make sure we get the right storage for loose/pack/loose and packed refs Robin Rosenberg
2009-05-27 22:08                 ` [EGIT PATCH 02/10] Add a toString for debugging to RemoteRefUpdate Robin Rosenberg
2009-05-27 22:08                   ` [EGIT PATCH 03/10] Add a toString to LockFile Robin Rosenberg
2009-05-27 22:08                     ` [EGIT PATCH 04/10] Add more tests for RefUpdate Robin Rosenberg
2009-05-27 22:08                       ` [EGIT PATCH 05/10] Do not write to the reflog unless the refupdate logmessage is set Robin Rosenberg
2009-05-27 22:08                         ` [EGIT PATCH 06/10] Add a utility method for shortening long ref names to short ones Robin Rosenberg
2009-05-27 22:08                           ` [EGIT PATCH 07/10] Set a nice reflog message in the branch command Robin Rosenberg
2009-05-27 22:08                             ` [EGIT PATCH 08/10] Add ref rename support to JGit Robin Rosenberg
2009-05-27 22:08                               ` [EGIT PATCH 09/10] Add ref rename support to the branch dialog Robin Rosenberg
2009-05-27 22:08                                 ` [EGIT PATCH 10/10] Improve error reporting in " Robin Rosenberg
2009-06-03 16:43                               ` Shawn O. Pearce [this message]
2009-06-03 15:41                         ` [EGIT PATCH 05/10] Do not write to the reflog unless the refupdate logmessage is set Shawn O. Pearce
2009-06-07 22:27                           ` Robin Rosenberg
2009-06-07 22:44                             ` Shawn O. Pearce
2009-05-20 21:43         ` [EGIT PATCH 1/6] Make sure we get the right storage for loose/pack/loose and packed refs Shawn O. Pearce
2009-05-21 15:22           ` Robin Rosenberg
2009-05-21 15:48             ` Shawn O. Pearce
2009-06-10 21:22 ` [EGIT PATCH v5 0/7] Ref rename support again Robin Rosenberg
2009-06-10 21:22   ` [EGIT PATCH v5 1/7] Add methods to RawParseUtils for scanning backwards Robin Rosenberg
2009-06-10 21:22     ` [EGIT PATCH v5 2/7] Add a ref log reader class Robin Rosenberg
2009-06-10 21:22       ` [EGIT PATCH v5 3/7] Do not write to the reflog unless the refupdate logmessage is set Robin Rosenberg
2009-06-10 21:22         ` [EGIT PATCH v5 4/7] Add ref rename support to JGit Robin Rosenberg
2009-06-10 21:22           ` [EGIT PATCH v5 5/7] Add ref rename support to the branch dialog Robin Rosenberg
2009-06-10 21:22             ` [EGIT PATCH v5 6/7] Improve error reporting in " Robin Rosenberg
2009-06-10 21:22               ` [EGIT PATCH v5 7/7] Remove a TAB from the message Egit generates into the reflog on commit Robin Rosenberg
2009-06-12 20:02             ` [EGIT PATCH v5 5/7] Add ref rename support to the branch dialog Shawn O. Pearce
2009-06-14 19:47               ` [EGIT PATCH v6 5a/7] Warn for unlocalized string literals in ui plugin Robin Rosenberg
2009-06-14 19:47                 ` [EGIT PATCH v6 5b/6] Add ref rename support to the branch dialog Robin Rosenberg
2009-06-14 19:47                   ` [EGIT PATCH v6 6/7] Improve error reporting in " Robin Rosenberg

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=20090603164330.GI3355@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).