git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Preserving empty directories when doing a git-svn clone/rebase
@ 2009-11-15  2:06 Steven J. Murdoch
  2009-11-15  5:02 ` PEBKAC or bug: unable to create path-like branch names Todd A. Jacobs
  2009-11-16  3:32 ` Preserving empty directories when doing a git-svn clone/rebase Eric Wong
  0 siblings, 2 replies; 7+ messages in thread
From: Steven J. Murdoch @ 2009-11-15  2:06 UTC (permalink / raw)
  To: git; +Cc: normalperson

When git-svn clones a Subversion repository, any empty directories
appear to be silently dropped (tested using git version 1.6.5.2 on Mac
OS X Snow Leopard). This causes problems for using git with software
projects which depend on Subversion's ability to track empty
directories. I was recently caught out by this, and it was difficult
to debug what had gone wrong.

Would it be possible to change git-svn to handle this case? Since git
doesn't have the ability to track empty directories, probably the
simplest thing to do would be to automatically add a file (e.g.
.gitignore) to any empty directories. In theory this could cause
problems, but I would think the chances of this are far lower than
with the current behaviour.

I think this feature would help projects in which some contributors
are transitioning to git. It would especially be useful to novice
users of git, who are not aware of the potential problems with having
empty directories.

I see there was a discussion in 2006:
 http://kerneltrap.org/mailarchive/git/2006/11/29/231586

However, since then I haven't seen any updates. The rationale behind
the original request still seems applicable today:

 "I think there are many potential git users out there who are
 currently svn users.  And git-svn is a really nice way to get started,
 but this sort of stumbling block could really turn people off.  For
 example, it made me look pretty dumb when I carelessly complained to
 my colleague about his code not working and then it turns out to be
 because my super-advanced scm tool "messed things up"."
  (git-svn and empty directories in svn (was: [PATCH 1.2/2 (fixed)]
   git-svn: fix output reporting from the delta fetcher))

Thanks,
Steven Murdoch.

-- 
http://www.cl.cam.ac.uk/users/sjm217/

^ permalink raw reply	[flat|nested] 7+ messages in thread

* PEBKAC or bug: unable to create path-like branch names
  2009-11-15  2:06 Preserving empty directories when doing a git-svn clone/rebase Steven J. Murdoch
@ 2009-11-15  5:02 ` Todd A. Jacobs
  2009-11-15  5:36   ` Jacob Helwig
  2009-11-15  7:16   ` Daniel Barkalow
  2009-11-16  3:32 ` Preserving empty directories when doing a git-svn clone/rebase Eric Wong
  1 sibling, 2 replies; 7+ messages in thread
From: Todd A. Jacobs @ 2009-11-15  5:02 UTC (permalink / raw)
  To: git

I want to create a nested feature branch, but git keeps complaining if I
nest more than one level deep:

    $ git checkout -b dev/feature/foo
    error: unable to resolve reference refs/heads/dev/feature/foo:
    Not a directory
    fatal: Failed to lock ref for update: Not a directory

Based on my reading of the manual pages, it seems like I should be able
to nest branch names as long as it conforms to certain rules. I read
git-branch(1), which points me to git-check-ref-format(1), which seems
to say that the rules are being followed.

On the other hand, running:

    $ git check-ref-format foo; echo $?

always results in a non-zero error code, even with a literal 'foo' as a
branch name, so clearly it isn't saying what I think it's saying.
*shrug*

Can someone provide a little clarity here?

-- 
"Oh, look: rocks!"
	-- Doctor Who, "Destiny of the Daleks"

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: PEBKAC or bug: unable to create path-like branch names
  2009-11-15  5:02 ` PEBKAC or bug: unable to create path-like branch names Todd A. Jacobs
@ 2009-11-15  5:36   ` Jacob Helwig
  2009-11-15  7:36     ` Todd A. Jacobs
  2009-11-15  7:16   ` Daniel Barkalow
  1 sibling, 1 reply; 7+ messages in thread
From: Jacob Helwig @ 2009-11-15  5:36 UTC (permalink / raw)
  To: git

On Sat, Nov 14, 2009 at 21:02, Todd A. Jacobs <nospam@codegnome.org> wrote:
> I want to create a nested feature branch, but git keeps complaining if I
> nest more than one level deep:
>
>    $ git checkout -b dev/feature/foo
>    error: unable to resolve reference refs/heads/dev/feature/foo:
>    Not a directory
>    fatal: Failed to lock ref for update: Not a directory
>
> Based on my reading of the manual pages, it seems like I should be able
> to nest branch names as long as it conforms to certain rules. I read
> git-branch(1), which points me to git-check-ref-format(1), which seems
> to say that the rules are being followed.
>
> On the other hand, running:
>
>    $ git check-ref-format foo; echo $?
>
> always results in a non-zero error code, even with a literal 'foo' as a
> branch name, so clearly it isn't saying what I think it's saying.
> *shrug*
>
> Can someone provide a little clarity here?
>
> --
> "Oh, look: rocks!"
>        -- Doctor Who, "Destiny of the Daleks"
>

What version of git are you using?  git checkout -b foo/bar/baz works
for me on 1.6.5.2.  As far as git check-ref-format, it works (returns
0) if I do 'refs/heads/foo', but returns 1 on 'foo'.  This makes
sense, given rule 2 from the manpage: They must contain at least one
/. This enforces the presence of a category like heads/, tags/ etc.
but the actual names are not restricted.

Hope this helps.

-Jacob

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: PEBKAC or bug: unable to create path-like branch names
  2009-11-15  5:02 ` PEBKAC or bug: unable to create path-like branch names Todd A. Jacobs
  2009-11-15  5:36   ` Jacob Helwig
@ 2009-11-15  7:16   ` Daniel Barkalow
  1 sibling, 0 replies; 7+ messages in thread
From: Daniel Barkalow @ 2009-11-15  7:16 UTC (permalink / raw)
  To: Todd A. Jacobs; +Cc: git

On Sat, 14 Nov 2009, Todd A. Jacobs wrote:

> I want to create a nested feature branch, but git keeps complaining if I
> nest more than one level deep:
> 
>     $ git checkout -b dev/feature/foo
>     error: unable to resolve reference refs/heads/dev/feature/foo:
>     Not a directory
>     fatal: Failed to lock ref for update: Not a directory
> 
> Based on my reading of the manual pages, it seems like I should be able
> to nest branch names as long as it conforms to certain rules. I read
> git-branch(1), which points me to git-check-ref-format(1), which seems
> to say that the rules are being followed.

Do you have a branch "dev/feature"? Branch names are path-like in that you 
can't have dev/feature as both a branch and a prefix for branches.

> On the other hand, running:
> 
>     $ git check-ref-format foo; echo $?
> 
> always results in a non-zero error code, even with a literal 'foo' as a
> branch name, so clearly it isn't saying what I think it's saying.
> *shrug*

You want either "git check-ref-format --branch foo" or "git 
check-ref-format refs/heads/foo".

	-Daniel
*This .sig left intentionally blank*

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: PEBKAC or bug: unable to create path-like branch names
  2009-11-15  5:36   ` Jacob Helwig
@ 2009-11-15  7:36     ` Todd A. Jacobs
  2009-11-15  7:55       ` Jacob Helwig
  0 siblings, 1 reply; 7+ messages in thread
From: Todd A. Jacobs @ 2009-11-15  7:36 UTC (permalink / raw)
  To: git

On Sat, Nov 14, 2009 at 09:36:47PM -0800, Jacob Helwig wrote:

> What version of git are you using?  git checkout -b foo/bar/baz works

I'm using 1.6.5.2 as well. Okay, try this in a temp directory, and
you'll see what I mean:

    git init
    echo foo > foo
    git add foo
    git commit -m testing foo
    git checkout -b dev
    git checkout -b dev/feature/foobar

The first branch works fine, but after attempting the nested branch the
message reappears:

    error: unable to resolve reference refs/heads/dev/feature/foobar: Not a directory
    fatal: Failed to lock ref for update: Not a directory

I can recreate this behavior at any time; it isn't just a problem with
an existing repository.

-- 
"Oh, look: rocks!"
	-- Doctor Who, "Destiny of the Daleks"

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: PEBKAC or bug: unable to create path-like branch names
  2009-11-15  7:36     ` Todd A. Jacobs
@ 2009-11-15  7:55       ` Jacob Helwig
  0 siblings, 0 replies; 7+ messages in thread
From: Jacob Helwig @ 2009-11-15  7:55 UTC (permalink / raw)
  To: git

On Sat, Nov 14, 2009 at 23:36, Todd A. Jacobs <nospam@codegnome.org> wrote:
> On Sat, Nov 14, 2009 at 09:36:47PM -0800, Jacob Helwig wrote:
>
>> What version of git are you using?  git checkout -b foo/bar/baz works
>
> I'm using 1.6.5.2 as well. Okay, try this in a temp directory, and
> you'll see what I mean:
>
>    git init
>    echo foo > foo
>    git add foo
>    git commit -m testing foo
>    git checkout -b dev
>    git checkout -b dev/feature/foobar
>
> The first branch works fine, but after attempting the nested branch the
> message reappears:
>
>    error: unable to resolve reference refs/heads/dev/feature/foobar: Not a directory
>    fatal: Failed to lock ref for update: Not a directory
>
> I can recreate this behavior at any time; it isn't just a problem with
> an existing repository.
>
> --
> "Oh, look: rocks!"
>        -- Doctor Who, "Destiny of the Daleks"
>

The problem is that you have a branch dev.  You can't have both a
file, and a directory with the same name.

You're trying to get git to do basically this:

% cd .git/refs/heads
% ls -l
total 0
-rw-rw-r-- 1 jhe jhe 41 2009-11-14 23:51 dev
-rw-rw-r-- 1 jhe jhe 41 2009-11-14 23:51 master
% mkdir dev
mkdir: cannot create directory `dev': File exists

You're getting the equivalent of the "cannot create directory" error.
When you have a branch with slashes in it, it gets stored as a
directory hierarchy under .git/refs/heads.

-Jacob

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Preserving empty directories when doing a git-svn clone/rebase
  2009-11-15  2:06 Preserving empty directories when doing a git-svn clone/rebase Steven J. Murdoch
  2009-11-15  5:02 ` PEBKAC or bug: unable to create path-like branch names Todd A. Jacobs
@ 2009-11-16  3:32 ` Eric Wong
  1 sibling, 0 replies; 7+ messages in thread
From: Eric Wong @ 2009-11-16  3:32 UTC (permalink / raw)
  To: Steven J. Murdoch; +Cc: git

"Steven J. Murdoch" <git+Steven.Murdoch@cl.cam.ac.uk> wrote:
> When git-svn clones a Subversion repository, any empty directories
> appear to be silently dropped (tested using git version 1.6.5.2 on Mac
> OS X Snow Leopard). This causes problems for using git with software
> projects which depend on Subversion's ability to track empty
> directories. I was recently caught out by this, and it was difficult
> to debug what had gone wrong.
> 
> Would it be possible to change git-svn to handle this case? Since git
> doesn't have the ability to track empty directories, probably the
> simplest thing to do would be to automatically add a file (e.g.
> .gitignore) to any empty directories. In theory this could cause
> problems, but I would think the chances of this are far lower than
> with the current behaviour.

Hi Steven,

The problem is that some git-svn using folks have started using
.gitignore to create empty directories on their own.  Dealing with
conflicts like this is very problematic because we have to know
if the .gitignore file is supposed to be committed up to SVN or
not (it can be quite expensive to check).

Attempting to deal with mismatching the information stored in the git
index and SVN nearly made my head explode when I tried to implement
svn:externals support via git submodules.  Fortunately I stopped in
time, but the mental scars still remain.

> I think this feature would help projects in which some contributors
> are transitioning to git. It would especially be useful to novice
> users of git, who are not aware of the potential problems with having
> empty directories.
> 
> I see there was a discussion in 2006:
>  http://kerneltrap.org/mailarchive/git/2006/11/29/231586
> 
> However, since then I haven't seen any updates. The rationale behind
> the original request still seems applicable today:
> 
>  "I think there are many potential git users out there who are
>  currently svn users.  And git-svn is a really nice way to get started,
>  but this sort of stumbling block could really turn people off.  For
>  example, it made me look pretty dumb when I carelessly complained to
>  my colleague about his code not working and then it turns out to be
>  because my super-advanced scm tool "messed things up"."
>   (git-svn and empty directories in svn (was: [PATCH 1.2/2 (fixed)]
>    git-svn: fix output reporting from the delta fetcher))

Shortly afterwards, git svn started logging unhandled information into
unhandled.log files.  I hoped that somebody would write a parser for
those log files to be able to recreate useful information from them.
Since I'm lazy, forgetful and absent-minded, I never got around to it
until now.

Let me know how it works and if the "git svn mkdirs" command name makes
sense.  Thanks for reminding me :)

>From 023675791988373beab921ad3ada115b2c224edf Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Sun, 15 Nov 2009 18:57:16 -0800
Subject: [PATCH] git svn: attempt to create empty dirs on clone+rebase

Attempt to parse unhandled.log files for empty_dir statements
and make a best effort attempt to recreate empty directories
on fresh clones and rebase.

This cannot affect "normal" git commands like "checkout" or
"reset", so users switching between branches in a single working
directory should use the new "git svn mkdirs" command after
switching branches.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
 Documentation/git-svn.txt                |    7 +++
 git-svn.perl                             |   45 ++++++++++++++++
 t/t9115-git-svn-dcommit-funky-renames.sh |    4 +-
 t/t9146-git-svn-empty-dirs.sh            |   85 ++++++++++++++++++++++++++++++
 4 files changed, 139 insertions(+), 2 deletions(-)
 create mode 100755 t/t9146-git-svn-empty-dirs.sh

diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 1812890..db00ed4 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -320,6 +320,13 @@ Any other arguments are passed directly to 'git log'
 	directories.  The output is suitable for appending to
 	the $GIT_DIR/info/exclude file.
 
+'mkdirs'::
+	Attempts to recreate empty directories that core git cannot track
+	based on information in $GIT_DIR/svn/<refname>/unhandled.log files.
+	Empty directories are automatically recreated when using
+	"git svn clone" and "git svn rebase", so "mkdirs" is intended
+	for use after commands like "git checkout" or "git reset".
+
 'commit-diff'::
 	Commits the diff of two tree-ish arguments from the
 	command-line.  This command does not rely on being inside an `git svn
diff --git a/git-svn.perl b/git-svn.perl
index ea922ac..ab0a8dd 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -168,6 +168,9 @@ my %cmd = (
 			     'Create a .gitignore per svn:ignore',
 			     { 'revision|r=i' => \$_revision
 			     } ],
+	'mkdirs' => [ \&cmd_mkdirs ,
+	              "recreate empty directories after a checkout",
+	              { 'revision|r=i' => \$_revision } ],
         'propget' => [ \&cmd_propget,
 		       'Print the value of a property on a file or directory',
 		       { 'revision|r=i' => \$_revision } ],
@@ -769,6 +772,7 @@ sub cmd_rebase {
 		$_fetch_all ? $gs->fetch_all : $gs->fetch;
 	}
 	command_noisy(rebase_cmd(), $gs->refname);
+	$gs->mkemptydirs;
 }
 
 sub cmd_show_ignore {
@@ -830,6 +834,12 @@ sub cmd_create_ignore {
 	});
 }
 
+sub cmd_mkdirs {
+	my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
+	$gs ||= Git::SVN->new;
+	$gs->mkemptydirs($_revision);
+}
+
 sub canonicalize_path {
 	my ($path) = @_;
 	my $dot_slash_added = 0;
@@ -1196,6 +1206,7 @@ sub post_fetch_checkout {
 	command_noisy(qw/read-tree -m -u -v HEAD HEAD/);
 	print STDERR "Checked out HEAD:\n  ",
 	             $gs->full_url, " r", $gs->last_rev, "\n";
+	$gs->mkemptydirs($gs->last_rev);
 }
 
 sub complete_svn_url {
@@ -2724,6 +2735,34 @@ sub do_fetch {
 	$self->make_log_entry($rev, \@parents, $ed);
 }
 
+sub mkemptydirs {
+	my ($self, $r) = @_;
+	my %empty_dirs = ();
+
+	open my $fh, '<', "$self->{dir}/unhandled.log" or return;
+	binmode $fh or croak "binmode: $!";
+	while (<$fh>) {
+		if (defined $r && /^r(\d+)$/) {
+			last if $1 > $r;
+		} elsif (/^  \+empty_dir: (.+)$/) {
+			$empty_dirs{$1} = 1;
+		} elsif (/^  \-empty_dir: (.+)$/) {
+			delete $empty_dirs{$1};
+		}
+	}
+	close $fh;
+	foreach my $d (sort keys %empty_dirs) {
+		$d = uri_decode($d);
+		next if -d $d;
+		if (-e _) {
+			warn "$d exists but is not a directory\n";
+		} else {
+			print "creating empty directory: $d\n";
+			mkpath([$d]);
+		}
+	}
+}
+
 sub get_untracked {
 	my ($self, $ed) = @_;
 	my @out;
@@ -3556,6 +3595,12 @@ sub uri_encode {
 	$f
 }
 
+sub uri_decode {
+	my ($f) = @_;
+	$f =~ s#%([0-9a-fA-F]{2})#chr(hex($1))#eg;
+	$f
+}
+
 sub remove_username {
 	$_[0] =~ s{^([^:]*://)[^@]+@}{$1};
 }
diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh
index 9be7aef..767799e 100755
--- a/t/t9115-git-svn-dcommit-funky-renames.sh
+++ b/t/t9115-git-svn-dcommit-funky-renames.sh
@@ -19,7 +19,7 @@ test_expect_success 'init and fetch repository' '
 	'
 
 test_expect_success 'create file in existing ugly and empty dir' '
-	mkdir "#{bad_directory_name}" &&
+	mkdir -p "#{bad_directory_name}" &&
 	echo hi > "#{bad_directory_name}/ foo" &&
 	git update-index --add "#{bad_directory_name}/ foo" &&
 	git commit -m "new file in ugly parent" &&
@@ -37,7 +37,7 @@ test_expect_success 'rename pretty file' '
 	git update-index --add pretty &&
 	git commit -m "pretty :x" &&
 	git svn dcommit &&
-	mkdir regular_dir_name &&
+	mkdir -p regular_dir_name &&
 	git mv pretty regular_dir_name/pretty &&
 	git commit -m "moved pretty file" &&
 	git svn dcommit
diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
new file mode 100755
index 0000000..5948544
--- /dev/null
+++ b/t/t9146-git-svn-empty-dirs.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+
+test_description='git svn creates empty directories'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize repo' '
+	for i in a b c d d/e d/e/f "weird file name"
+	do
+		svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
+	done
+'
+
+test_expect_success 'clone' 'git svn clone "$svnrepo" cloned'
+
+test_expect_success 'empty directories exist' '
+	(
+		cd cloned &&
+		for i in a b c d d/e d/e/f "weird file name"
+		do
+			if ! test -d "$i"
+			then
+				echo >&2 "$i does not exist"
+				exit 1
+			fi
+		done
+	)
+'
+
+test_expect_success 'more emptiness' '
+	svn_cmd mkdir -m "bang bang"  "$svnrepo"/"! !"
+'
+
+test_expect_success 'git svn rebase creates empty directory' '
+	( cd cloned && git svn rebase )
+	test -d cloned/"! !"
+'
+
+test_expect_success 'git svn mkdirs recreates empty directories' '
+	(
+		cd cloned &&
+		rm -r * &&
+		git svn mkdirs &&
+		for i in a b c d d/e d/e/f "weird file name" "! !"
+		do
+			if ! test -d "$i"
+			then
+				echo >&2 "$i does not exist"
+				exit 1
+			fi
+		done
+	)
+'
+
+test_expect_success 'git svn mkdirs -r works' '
+	(
+		cd cloned &&
+		rm -r * &&
+		git svn mkdirs -r7 &&
+		for i in a b c d d/e d/e/f "weird file name"
+		do
+			if ! test -d "$i"
+			then
+				echo >&2 "$i does not exist"
+				exit 1
+			fi
+		done
+
+		if test -d "! !"
+		then
+			echo >&2 "$i should not exist"
+			exit 1
+		fi
+
+		git svn mkdirs -r8 &&
+		if ! test -d "! !"
+		then
+			echo >&2 "$i not exist"
+			exit 1
+		fi
+	)
+'
+
+test_done
-- 
Eric Wong

^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2009-11-16  3:33 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-11-15  2:06 Preserving empty directories when doing a git-svn clone/rebase Steven J. Murdoch
2009-11-15  5:02 ` PEBKAC or bug: unable to create path-like branch names Todd A. Jacobs
2009-11-15  5:36   ` Jacob Helwig
2009-11-15  7:36     ` Todd A. Jacobs
2009-11-15  7:55       ` Jacob Helwig
2009-11-15  7:16   ` Daniel Barkalow
2009-11-16  3:32 ` Preserving empty directories when doing a git-svn clone/rebase Eric Wong

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).