Git development
 help / color / mirror / Atom feed
* Re: [PATCH 1/8] Add is_absolute_path() and make_absolute_path()
From: Junio C Hamano @ 2007-07-27 20:51 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, matled
In-Reply-To: <Pine.LNX.4.64.0707271955450.14781@racer.site>

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> diff --git a/path.c b/path.c
> index c4ce962..0f7012f 100644
> --- a/path.c
> +++ b/path.c
> @@ -292,3 +292,65 @@ int adjust_shared_perm(const char *path)
>  		return -2;
>  	return 0;
>  }
> +
> +/* We allow "recursive" symbolic links. Only within reason, though. */
> +#define MAXDEPTH 5
> +
> +const char *make_absolute_path(const char *path)
> +{
> +	static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
> +	char cwd[1024] = "";
> +	int buf_index = 1, len;
> +
> +	int depth = MAXDEPTH;
> +	char *last_elem = NULL;
> +	struct stat st;
> +
> +	if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
> +		die ("Too long path: %.*s", 60, path);
> +
> +	while (depth--) {
> +		if (stat(buf, &st) || !S_ISDIR(st.st_mode)) {
> +			char *last_slash = strrchr(buf, '/');
> +			*last_slash = '\0';
> +			last_elem = xstrdup(last_slash + 1);

What happens when incoming path is just "abc"?  Does your test
script checks that case?

^ permalink raw reply

* Re: [PATCH 2/2] gitk: Markup many strings for translation.
From: Brett Schwarz @ 2007-07-27 20:19 UTC (permalink / raw)
  To: Christian Stimming, Paul Mackerras; +Cc: git

> 
> ----- Original Message ----
> From: Christian Stimming <stimming@tuhh.de>
> To: Paul Mackerras <paulus@samba.org>
> Cc: git@vger.kernel.org
> Sent: Friday, July 27, 2007 8:03:00 AM
> Subject: [PATCH 2/2] gitk: Markup many strings for translation.
> 
> Similar to the discussion in git-gui, all user-visible strings are  
> passed through the [mc ...] procedure to have them translated by msgcat.
> 
> Signed-off-by: Christian Stimming <stimming@tuhh.de>
> ---

<snip>

>   proc getcommitlines {fd view}  {
> @@ -273,7 +273,7 @@ proc chewcommits {view} {
>           #set ms [expr {[clock clicks -milliseconds] - $startmsecs}]
>           #puts "overall $ms ms for $numcommits commits"
>           } else {
> -        show_status "No commits selected"
> +        show_status [mc "No commits selected"]
>           }
>           notbusy layout
>           set phase {}
> @@ -378,7 +378,7 @@ proc getcommit {id} {
>       } else {
>       readcommit $id
>       if {![info exists commitinfo($id)]} {
> -        set commitinfo($id) {"No commit information available"}
> +        set commitinfo($id) {[mc "No commit information available"]}

I think this is probably a typo (on the original), and carrying that forward will probably result in what was not intended.

The original has the {} and "". I don't know if this was intended (it will keep the quotes).

The translated version, will result in a literal string [mc "No commit information available"], and unless it is explicitly eval'ed later, it will not result in the translated string.

So, if the quotes need to stay in the string, then the translation will have to be:

  set commitinfo($id) \"[mc "No commit information available"]\"

Or, if the quotes are not needed:

  set commitinfo($id) [mc "No commit information available"]


Regards,
    --brett


       
____________________________________________________________________________________
Pinpoint customers who are looking for what you sell. 
http://searchmarketing.yahoo.com/

^ permalink raw reply

* Re: Can you do this with GIT?
From: Ray Lehtiniemi @ 2007-07-27 20:08 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: CPD, git
In-Reply-To: <Pine.LNX.4.64.0707272048280.14781@racer.site>

On Friday 27 July 2007 13:49, Johannes Schindelin wrote:
> Hi,
>
> On Fri, 27 Jul 2007, Ray Lehtiniemi wrote:
> > On Friday 27 July 2007 13:05, Johannes Schindelin wrote:
> > > On Fri, 27 Jul 2007, CPD wrote:
> > > > We produce variations based on a (mostly) common codebase. In CVS I
> > > > set up "environment" modules for each platform, then when you are
> > > > working on that platform, you simply check out the correct
> > > > environment and build. Only the needed code and tools are exposed in
> > > > that environment (this is important as clients must NOT see each
> > > > other's code and most customers have some customization). I do this
> > > > by defining and renaming modules in the CVSROOT modules file.
> > >
> > > I would use branches for that.  A base branch with the common code, and
> > > the customisations in all the branches, which merge from the base
> > > branch.
> >
> > this would break down if there were client-specific modules in the base
> > branch, though... how could those be hidden from the other clients?
>
> Umm.  Don't put the client-specific modules in the base branch, then?  The
> base branch is the common code, the code that every client may look at.
> Nothing else.

yes, i don;t think there's any other way to do it with branches (and just be 
careful not to merge the private branches back! :-)


> Maybe I did not get the whole picture...  do you want your _clients_ to
> access your main repo with Git?

not in my case, anyway... although if you define "client" to 
include "subcontractor", then yes, i'd be interested in going down that 
road... sometimes there are pieces which they aren't licensed to see, but if 
we could somehow track their work in a separate repo and then easily merge it 
back into the fully licensed tree, that would have great value.

from the original message, i also keyed on "Only the needed code and tools are 
exposed in that environment".  this is something i'd like to see too... i'm 
working in a software eco-system with hundreds of standalone components, and 
would like the ability to pick and choose a small handful of those for any 
given project, without pulling in reams of history on the stuff i'm not 
using...

thanks
ray


>
> Ciao,
> Dscho

^ permalink raw reply

* Re: Can you do this with GIT?
From: Ray Lehtiniemi @ 2007-07-27 19:52 UTC (permalink / raw)
  To: CPD; +Cc: git
In-Reply-To: <11834063.post@talk.nabble.com>

On Friday 27 July 2007 12:02, CPD wrote:

> We produce variations based on a (mostly) common codebase. In CVS I set up 
> "environment" modules for each platform, then when you are working on that
> platform, you simply check out the correct environment and build. Only the
> needed code and tools are exposed in that environment (this is important as
> clients must NOT see each other's code and most customers have some
> customization). I do this by defining and renaming modules in the CVSROOT
> modules file.

this sounds very close to what i'm trying to achieve at the moment.


> Does GIT support anything like this? Or another way to acheive the same
> end?

i couldn;t find any ready way to do it.... but i've hacked up a few 
preliminary thoughts on how this might work in git.  comments and feedback 
are welcome :-)

the basic idea is to import each snapshot of each vendor release as a tagged 
root commit, then push all these tagged root commits into a central "library" 
repository. each tagged root commit stands alone and functions as a cvs 
module.

library repos are set up as remotes under git, and a simple script will import 
a set of snapshots into a brand new project as a baseline for customization.

the three shell scripts, and the two control files used by them, are included 
here:

==> vendor-tracking.sh <==
###########################################################
#
# Import a vendor snapshot into a "library" repository.
#
# To import a vendor snapshot, unzip the snapshot into
# a new folder.  Then go into the folder and type
#
#  xxx-import <library> <tag>
#
# This will create an empty git repo in the current
# snapshot folder and create a root commit in 
# it which holds the snapshot.  This root commit will be
# tagged as "tag" and then pushed into the "library" repo.
#
# No error checking is done.
#
###########################################################
xxx-import ()
{(
	repo=$1
	tag=$2

	export GIT_DIR=/tmp/$$.$tag

	rm -rf $GIT_DIR
	mkdir -p $GIT_DIR

	git init
	git add .
	git commit -s -m "Import $tag"
	git tag $tag master
	git push $repo refs/tags/$tag:refs/tags/$tag

	rm -rf $GIT_DIR
)}

###########################################################
#
# Use a vendor release in the current project.
#
# To use a copy of some vendor snapshot in the current
# project, go to the top level of your current project,
# then type
#
#  xxx-use <library> <tag> <path>
#
# The tagged snapshot will be fetched from the library
# and deposited into the <path> subdirectory, then committed
# to the project.
# 
# No error checking is done.
#
###########################################################
xxx-use ()
{(
	repo=$1
	tag=$2
	path=$3

	git fetch $repo refs/tags/$tag:refs/tags/$tag
	git read-tree --prefix=$path/ $tag
	git commit -s -m "Use $tag as $path"
)}

###########################################################
#
# Create a new project from a set of vendor components.
#
# To start a new project with a known array of vendor
# components, create a remotes file describing the libraries
# and an manifest file describing the components and how they
# will be used. Then, in a new folder, type
#
#  xxx-start <remotes> <manifest>
#
# This will create a new Git project in the current folder,
# add a remote for each library repository, then proceed to
# fetch and commit each vendor component into the new project.
# The new project is now ready to be customized.
# 
# No error checking is done.
#
###########################################################
xxx-start ()
{(
	remotes=$1
	manifest=$2

	git init

	cat $remotes |
	while read name url
	do
		git remote add $name $url
	done

	cat $manifest |
	while read path remote tag
	do
		xxx-use $remote $tag $path
	done

	git checkout -f master
)}


==> remotes <==
lib1	ssh://me@home.com/home/me/work/library
lib2	//filesrv/git/library

==> manifest <==
path/to/A		lib1	vendorA/componentA/version1
path/to/B		lib2	vendorB/componentB/version2






i would prefer to import and tag raw tree objects, since i get some of my 
snapshots as pre-assembled collections of (modified copies of) several 
components.  it would therefore be convenient to have several tags per 
snapshot pointing at the subtrees corresponding to each component.  however,  
fetch and push seem to prefer commits over trees.... 

as it stands, the vendor snapshots have no "history" to them.  each snapshot 
stands in isolation.  there was an interesting thread a few days ago about 
importing ancient kernel history as a series of trees, then "stitching 
together" a "synthetic history" from a collection of trees.  i would see 
something like that as the basis for "vendor tracking branches", which could 
be restitched if an older snapshot is received "out of order".


hope it helps
ray

^ permalink raw reply

* Re: [PATCH] Make verify-tag a builtin.
From: Carlos Rica @ 2007-07-27 19:51 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Johannes Schindelin
In-Reply-To: <7vr6mu2uo0.fsf@assigned-by-dhcp.cox.net>

2007/7/27, Junio C Hamano <gitster@pobox.com>:
> Carlos Rica <jasampler@gmail.com> writes:
> > Signal SIGPIPE is ignored because the program sometimes was
> > terminated because that signal when writing the input for gpg.
>
> This "sometimes" does not give confidence to readers, I am
> afraid.
>
> What is happening is perfectly normal, not "sometimes it gets
> the signal mysteriously, so we need to paper it over by ignoring
> it".
>
> You invoke gpg, giving it a file that is supposed to contain a
> detached signature, and start feeding the payload that ought to
> verify Ok with the signature.  If the tag is not signed, after
> gpg has read the detached signature, it already knows that the
> signature will not verify and it can exit without reading the
> payload from its standard input.  When you try to write the
> payload to the pipe, you would get SIGPIPE.

By using "sometimes" I was requesting an answer to this strange
behaviour (for me) to you and the mailing list. Now I could reproduce it
removing the call to signal and running this:

#!/bin/sh
echo "creating vtag1..."
git tag -s -m "a signed tag" vtag1
echo "creating vtag2..."
git tag -m "an annotated non-signed tag" vtag2
echo "creating vtag3..."
git tag -m "another annotated non-signed tag is " vtag3
# 141 returned sometimes when gpg got
# a non-signed file as detached signature:
# (it refuses to read from the input when
# no signature is given, sometimes)
for i in 1 2 3 4 5 6 7 8 9 10
do
        ./git verify-tag vtag1 vtag2 vtag3
        echo "git verify-tag vtag1 vtag2 vtag3 exit code: $?"
        echo
done
git tag -d vtag1
git tag -d vtag2
git tag -d vtag3

In my system, some of the tests give 141 and others give 1 as exit code.
Dscho said that it could depend on the CPU current load of the computer,
since he got always 141 as you said, so perhaps it's me.

^ permalink raw reply

* Re: Can you do this with GIT?
From: Johannes Schindelin @ 2007-07-27 19:49 UTC (permalink / raw)
  To: Ray Lehtiniemi; +Cc: CPD, git
In-Reply-To: <200707271345.03300.rayl@mail.com>

Hi,

On Fri, 27 Jul 2007, Ray Lehtiniemi wrote:

> On Friday 27 July 2007 13:05, Johannes Schindelin wrote:
> > On Fri, 27 Jul 2007, CPD wrote:
> 
> > > We produce variations based on a (mostly) common codebase. In CVS I set
> > > up "environment" modules for each platform, then when you are working on
> > > that platform, you simply check out the correct environment and build.
> > > Only the needed code and tools are exposed in that environment (this is
> > > important as clients must NOT see each other's code and most customers
> > > have some customization). I do this by defining and renaming modules in
> > > the CVSROOT modules file.
> >
> > I would use branches for that.  A base branch with the common code, and
> > the customisations in all the branches, which merge from the base branch.
> 
> this would break down if there were client-specific modules in the base 
> branch, though... how could those be hidden from the other clients?

Umm.  Don't put the client-specific modules in the base branch, then?  The 
base branch is the common code, the code that every client may look at.  
Nothing else.

Maybe I did not get the whole picture...  do you want your _clients_ to 
access your main repo with Git?

Ciao,
Dscho

^ permalink raw reply

* Re: Can you do this with GIT?
From: Ray Lehtiniemi @ 2007-07-27 19:45 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: CPD, git
In-Reply-To: <Pine.LNX.4.64.0707272002250.14781@racer.site>

On Friday 27 July 2007 13:05, Johannes Schindelin wrote:
> On Fri, 27 Jul 2007, CPD wrote:

> > We produce variations based on a (mostly) common codebase. In CVS I set
> > up "environment" modules for each platform, then when you are working on
> > that platform, you simply check out the correct environment and build.
> > Only the needed code and tools are exposed in that environment (this is
> > important as clients must NOT see each other's code and most customers
> > have some customization). I do this by defining and renaming modules in
> > the CVSROOT modules file.
>
> I would use branches for that.  A base branch with the common code, and
> the customisations in all the branches, which merge from the base branch.

this would break down if there were client-specific modules in the base 
branch, though... how could those be hidden from the other clients?


ray

^ permalink raw reply

* Re: Can you do this with GIT?
From: Linus Torvalds @ 2007-07-27 19:45 UTC (permalink / raw)
  To: CPD; +Cc: git
In-Reply-To: <11834063.post@talk.nabble.com>



On Fri, 27 Jul 2007, CPD wrote:
> 
> We produce variations based on a (mostly) common codebase. In CVS I set up
> "environment" modules for each platform, then when you are working on that
> platform, you simply check out the correct environment and build. Only the
> needed code and tools are exposed in that environment (this is important as
> clients must NOT see each other's code and most customers have some
> customization). I do this by defining and renaming modules in the CVSROOT
> modules file.
> 
> Does GIT support anything like this? Or another way to acheive the same end?

Depending on how big the individual pieces are, you can do it either with 
branches or subprojects.

If the "mostly common" codebase means that 90% of it really ends up being 
common, and what you have is a few config things and perhaps a library or 
two that either shows up or not, git branches would work really well. 

And branches are totally independent of each other, so it's quite possible 
to have even "secret" things in one branch, and it would never get exposed 
to anybody else. The biggest worry you'd need to make sure of is that you 
don't want to merge such private branches directly with each other - since 
then the merged branch would see everything that was in the history of the 
other branch, even if the merge itself ended up not picking the "secret 
stuff".

So with branches that have private information, you'd have to have one 
"common" branch that doesn't have any of the private stuff at all, and do 
the general development on that, and then you can merge that common stuff 
into all the individual private branches (and if you want to go the other 
way, moving something from a private branch to the "common area", you'd 
have to cherry-pick just that particular detail into the common branch).

If you have bigger building blocks, and some of the whole building blocks 
need to be private, you'd probably want to use the subproject support, 
which would make for more of a CVS "modules" kind of thing and in that 
sense perhaps closer to what you do now, but I suspect that just using 
branches would actually end up being much simpler for you. It would depend 
mostly on how big those building blocks that you need to keep separate 
are.

		Linus

^ permalink raw reply

* Re: [PATCH] rebase -i: fix interrupted squashing
From: Johannes Schindelin @ 2007-07-27 19:35 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7v1wet1wmw.fsf@assigned-by-dhcp.cox.net>

Hi,

On Fri, 27 Jul 2007, Junio C Hamano wrote:

> Thanks for reminding.
> 
> I thought I read the patch, felt satisfied and already applied,
> but obviously I forgot the last step.  Anything else that should
> be in 1.5.3 I forgot?

I'll try to fix that unpack bug I talked about lately, and I'd like to get 
in a little bit saner filter-branch.  One where you can read in the 
documentation "pruning paths without --subdirectory-filter is not yet 
supported, and likewise pruning by --grep".

I guess the proper fix would be to build a rev-list command line leaving 
out all the parameters which can prune the commits that the passed refs 
point to, but that is probably a little painful, to say the least.

> BTW, I _think_ somebody between your brain and my mbox mangled
> umlaut in Uwe's name.  I'll fix it up.

Thanks.

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH 3/5] Clean up work-tree handling
From: Johannes Schindelin @ 2007-07-27 19:32 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Matthias Lederhofer, git
In-Reply-To: <7v8x911wn6.fsf@assigned-by-dhcp.cox.net>

Hi,

On Fri, 27 Jul 2007, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > But there is a fundamental question I have to ask: Is there any reason why 
> >
> > 	$ git --git-dir=/some/where/else.git bla
> >
> > should pretend that the repo is bare if core.bare == 1?  I mean, we are 
> > implicitely setting the work tree to the cwd, no?
> 
> I have two repositories at the primary k.org machine.
> 
>  - /home/junio/git.git --- this is with a worktree so that I can
>    build and test on a FC machine (my primary development
>    machine at home is a Debian).
> 
>  - /pub/scm/git/git.git/ --- this is a bare repository that is
>    mirrored out to git://git.kernel.org/ and friends.
> 
> And I usually am in the former.  From time to time, I do this:
> 
>  $ GIT_DIR=/pub/scm/git/git.git/ git fsck
>  $ GIT_DIR=/pub/scm/git/git.git/ git repack
> 
> because I am old fashioned, but I would expect these to be
> equivalent to the above:
> 
>  $ git --git-dir=/pub/scm/git/git.git/ fsck
>  $ git --git-dir=/pub/scm/git/git.git/ repack
> 
> I do not think these imply that the repository is with worktree.

But in your use cases it does not matter, since neither fsck nor repack 
need a worktree.

Here is one of _my_ scenarios: I want to track a directory I have no write 
access to (or better put: I should have no write access to).  So I created 
a bare repository, and when I add new files I use the "GIT_DIR=... git..." 
mantra.

But you're right, I could just set core.worktree=... and unset core.bare.

I can change that easily enough.  Other comments on the series?

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH 0/2] another attempt at make_absolute_path()
From: Johannes Schindelin @ 2007-07-27 19:22 UTC (permalink / raw)
  To: Bradford C Smith; +Cc: git, Junio C Hamano
In-Reply-To: <11855634561516-git-send-email-bradford.carl.smith@gmail.com>

Hi,

On Fri, 27 Jul 2007, Bradford C Smith wrote:

> Here's my attempt at make_absolute_path() and friends.  I think this
> version handles symlinks cleanly to avoid problems with '..' path
> elements Junio pointed out recently.
> 
> I built these with another patch I previously submitted to make
> git-config consistently use lockfile.c routines and tested it with the
> regular test suite plus some extra tests Junio sent to the list for
> checking git-config symlink handling.
> 
> I also built a separate executable with just the path handling routines
> in it and spot-checked several cases to make sure it appeared to be
> working as expected.  ('/', loop of symlinks, lots of extra slashes, .
> and .. elements, etc.)

Heh.  Just a couple of minutes after I sent my version...

Although I added tests, and you did not...

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH] rebase -i: fix interrupted squashing
From: Junio C Hamano @ 2007-07-27 19:20 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0707271817100.14781@racer.site>

Thanks for reminding.

I thought I read the patch, felt satisfied and already applied,
but obviously I forgot the last step.  Anything else that should
be in 1.5.3 I forgot?

BTW, I _think_ somebody between your brain and my mbox mangled
umlaut in Uwe's name.  I'll fix it up.

^ permalink raw reply

* Re: [PATCH 3/5] Clean up work-tree handling
From: Junio C Hamano @ 2007-07-27 19:20 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Matthias Lederhofer, git
In-Reply-To: <Pine.LNX.4.64.0707271146290.14781@racer.site>

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> But there is a fundamental question I have to ask: Is there any reason why 
>
> 	$ git --git-dir=/some/where/else.git bla
>
> should pretend that the repo is bare if core.bare == 1?  I mean, we are 
> implicitely setting the work tree to the cwd, no?

I have two repositories at the primary k.org machine.

 - /home/junio/git.git --- this is with a worktree so that I can
   build and test on a FC machine (my primary development
   machine at home is a Debian).

 - /pub/scm/git/git.git/ --- this is a bare repository that is
   mirrored out to git://git.kernel.org/ and friends.

And I usually am in the former.  From time to time, I do this:

 $ GIT_DIR=/pub/scm/git/git.git/ git fsck
 $ GIT_DIR=/pub/scm/git/git.git/ git repack

because I am old fashioned, but I would expect these to be
equivalent to the above:

 $ git --git-dir=/pub/scm/git/git.git/ fsck
 $ git --git-dir=/pub/scm/git/git.git/ repack

I do not think these imply that the repository is with worktree.

^ permalink raw reply

* [PATCH 2/2] use make_absolute_path() in lock_file()
From: Bradford C Smith @ 2007-07-27 19:10 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin, Bradford C. Smith
In-Reply-To: <11855634582686-git-send-email-bradford.carl.smith@gmail.com>

From: Bradford C. Smith <bradford.carl.smith@gmail.com>

Use make_absolute_path() to get fully resolved path name for creating
the lock file.

Signed-off-by: Bradford C. Smith <bradford.carl.smith@gmail.com>
---
 lockfile.c |   16 +++-------------
 1 files changed, 3 insertions(+), 13 deletions(-)

diff --git a/lockfile.c b/lockfile.c
index 9202472..57f850f 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -28,20 +28,10 @@ static void remove_lock_file_on_signal(int signo)
 static int lock_file(struct lock_file *lk, const char *path)
 {
 	int fd;
-	struct stat st;
 
-	if ((!lstat(path, &st)) && S_ISLNK(st.st_mode)) {
-		ssize_t sz;
-		static char target[PATH_MAX];
-		sz = readlink(path, target, sizeof(target));
-		if (sz < 0)
-			warning("Cannot readlink %s", path);
-		else if (target[0] != '/')
-			warning("Cannot lock target of relative symlink %s", path);
-		else
-			path = target;
-	}
-	sprintf(lk->filename, "%s.lock", path);
+	strcpy(lk->filename, path);
+	make_absolute_path(lk->filename);
+	strcat(lk->filename, ".lock");
 	fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
 	if (0 <= fd) {
 		if (!lock_file_list) {
-- 
1.5.3.rc3.9.g9ef91

^ permalink raw reply related

* [PATCH 1/2] added file path helper routines
From: Bradford C Smith @ 2007-07-27 19:10 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin, Bradford C. Smith
In-Reply-To: <11855634561516-git-send-email-bradford.carl.smith@gmail.com>

From: Bradford C. Smith <bradford.carl.smith@gmail.com>

Added the following routines:

is_absolute_path()
split_path()
join_path()
is_valid_path()
make_absolute_path()

Signed-off-by: Bradford C. Smith <bradford.carl.smith@gmail.com>
---
 cache.h |    5 ++
 path.c  |  247 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 252 insertions(+), 0 deletions(-)

diff --git a/cache.h b/cache.h
index 53801b8..8480716 100644
--- a/cache.h
+++ b/cache.h
@@ -356,6 +356,11 @@ enum sharedrepo {
 };
 int git_config_perm(const char *var, const char *value);
 int adjust_shared_perm(const char *path);
+int is_absolute_path(char *p);
+void split_path(const char *p, char *start, char *rest);
+void join_path(char *p, const char *start, const char *rest);
+int is_valid_path(const char *p);
+char *make_absolute_path(char *p);
 int safe_create_leading_directories(char *path);
 char *enter_repo(char *path, int strict);
 
diff --git a/path.c b/path.c
index dfff41f..2d14677 100644
--- a/path.c
+++ b/path.c
@@ -288,3 +288,250 @@ int adjust_shared_perm(const char *path)
 		return -2;
 	return 0;
 }
+
+
+int is_absolute_path(char *p)
+{
+	return p[0] == '/';
+}
+
+static void strip_trailing_slashes(char *p)
+{
+	char *r = strrchr(p, '/');
+
+	if (!r)
+		return; /* no slashes at all */
+	if (*(r + 1) != '\0')
+		return; /* last slash is not at the end */
+	/*
+	 * last character is a slash, back up overwriting slashes with
+	 * nulls until I find a non-null or the beginning of p, but
+	 * don't overwrite a '/' at the beginning of p.
+	 */
+	while (r > p && *r == '/') {
+		*r = '\0';
+		r--;
+	}
+}
+
+/*
+ * p = path name that will fit in a PATH_MAX size buffer
+ * start = NULL or PATH_MAX size buffer
+ * rest = NULL or PATH_MAX size buffer
+ *
+ * split p on the last slash that isn't a trailing slash.
+ * Copy everything before the slash into start if it is not NULL.  Copy
+ * everything after the slash, except trailing slashes, into rest if it
+ * is not NULL.  The slash itself isn't put in either one.
+ *
+ * If p contains no non-trailing slashes, all of p will be put into
+ * start and rest will be an empty string.
+ *
+ * This routine is meant to be the exact reverse of join_path() as long
+ * as p has no trailing slashes.  If p has trailing slashes, spliting
+ * and rejoining will cause them to disappear.
+ *
+ * NOTE: p is copied into a temporary buffer, so it is safe for start or
+ * rest to point into p.
+ * NOTE: if p is too big to fit in a PATH_MAX size buffer, it will be
+ * silently truncated when copied to the temporary buffer.
+ */
+void split_path(const char *p, char *start, char *rest)
+{
+	char buf[PATH_MAX];
+	char * last_slash;
+	const char * after_slash;
+
+	strncpy(buf, p, sizeof(buf));
+	buf[sizeof(buf) - 1] = '\0';
+	strip_trailing_slashes(buf);
+	last_slash = strrchr(buf, '/');
+	if (last_slash) {
+		*last_slash = '\0';
+		after_slash = last_slash + 1;
+	} else {
+		after_slash = "";
+	}
+	if (start) {
+		strcpy(start, buf);
+	}
+	if (rest) {
+		strcpy(rest, after_slash);
+	}
+}
+
+/*
+ * p = PATH_MAX size buffer to hold result
+ * start = beginning of a path (shorter than PATH_MAX)
+ * rest = end of a path (shorter than PATH_MAX)
+ *
+ * fill p with start + '/' + rest, removing any trailing slashes from
+ * the result.  If the result is too big to fit in a PATH_MAX size
+ * buffer, it will be silently truncated.
+ *
+ * NOTE: This routine uses a temporary buffer to hold the result, so it
+ *       is safe to have start or rest pointing into p.
+ */
+void join_path(char *p, const char *start, const char *rest)
+{
+	char buf[PATH_MAX];
+
+	snprintf(buf, sizeof(buf), "%s/%s", start, rest);
+	strip_trailing_slashes(buf);
+	strcpy(p, buf);
+}
+
+/*
+ * p = path that will fit in a PATH_MAX size buffer
+ *
+ * return true if p is a valid path, false otherwise
+ *
+ * p is considered valid if
+ * 1. stat(p) succeeds
+ *    OR
+ * 2. stat(p) fails with ENOENT and I can successfully stat() the
+ * directory part of p and see that it is a directory.
+ *
+ * NOTE: The caller must ensure that p will fit in a PATH_MAX size
+ *       buffer.
+ */
+int is_valid_path(const char *p)
+{
+	char dir[PATH_MAX];
+	struct stat st;
+
+	if (stat(p, &st) == 0) {
+		return 1;
+	}
+	if (errno != ENOENT) {
+		/*
+		 * there's something wrong with p other than it just not
+		 * existing
+		 */
+		return 0;
+	}
+	split_path(p, dir, NULL);
+	if (dir[0] == '\0') {
+		/* path is '/something' and '/' always exists */
+		return 1;
+	}
+	return (stat(dir, &st) == 0) && S_ISDIR(st.st_mode);
+}
+
+/*
+ * p = PATH_MAX size buffer containing a path that may specify a symlink
+ *
+ * If p is a symlink, overwrite p with the target of the symlink.  If
+ * the target would be too big to fit in a PATH_MAX size buffer, p will
+ * not be overwritten.
+ *
+ * Returns true if p is overwritten, false otherwise.
+ */
+static int expand_symlink(char *p)
+{
+	char buf[PATH_MAX];
+	size_t len;
+
+	/* don't try to expand a symlink in an invalid path */
+	if (!is_valid_path(p)) {
+		return 0;
+	}
+	len = readlink(p, buf, sizeof(buf));
+	if (len < 0) {
+		return 0; /* not a symlink or couldn't read it */
+	}
+	if (len >= sizeof(buf)) {
+		return 0; /* link too long to expand */
+	}
+	buf[len] = '\0'; /* readlink() doesn't null terminate */
+	if (is_absolute_path(buf)) {
+		strcpy(p, buf);
+		return 1;
+	} else {
+		/* replace basename with relative symlink */
+		char dir[PATH_MAX];
+
+		split_path(p, dir, NULL);
+		if ((strlen(dir) + 1 + strlen(buf)) < PATH_MAX) {
+			join_path(p, dir, buf);
+			return 1;
+		} else {
+			/* link too big to fit in p */
+			return 0;
+		}
+	}
+}
+
+/*
+ * p = absolute path in a PATH_MAX size buffer
+ *
+ * Attempt to replace contents of p with an equivalent absolute path
+ * containing no extra slashes, symlinks, '.', or '..' elements.  This
+ * is done recursively beginning with '/'.  Resolution of symlinks will
+ * stop at the first element in the path that doesn't exist or cannot be
+ * read/searched for some reason, but extra slashes, '.', and '..'
+ * elements will still be resolved after that point.
+ *
+ * Always returns p.
+ */
+static char *normalize_path(char *p)
+{
+	char start[PATH_MAX];
+	char rest[PATH_MAX];
+
+	/*
+	 * recursion stopping case: nothing to normalize in an empty
+	 * string (represents root directory)
+	 */
+	if (*p == '\0') {
+		return p;
+	}
+	split_path(p, start, rest);
+	normalize_path(start);
+	if (!strcmp(rest, ".")) {
+		/* "self" expands to nothing */
+		rest[0] = '\0';
+	}
+	if (!strcmp(rest, "..")) {
+		/*
+		 * "parent" expands to nothing and removes the last
+		 * element from start.
+		 */
+		rest[0] = '\0';
+		split_path(start, start, NULL);
+	}
+
+	/* put the path back together */
+	join_path(p, start, rest);
+
+	if (expand_symlink(p)) {
+		/* p was a symlink, so I must normalize its expansion */
+		normalize_path(p);
+	}
+	return p;
+}
+
+
+/*
+ * p = absolute or partial path in a PATH_MAX size buffer
+ *
+ * normalize p to an absolute path containing no symlinks and no . or ..
+ * directories.
+ *
+ * NOTE: If any of the path components do not exist or cannot be read/searched
+ * for some reason, this routine will only standardize the parts of the
+ * path up to the "bad" component.
+ *
+ * Always returns p.
+ */
+char *make_absolute_path(char *p)
+{
+	if (!is_absolute_path(p)) {
+		char cwd[PATH_MAX];
+
+		if (NULL == getcwd(cwd, sizeof(cwd)))
+			die("cannot get working directory");
+		join_path(p, cwd, p);
+	}
+	return normalize_path(p);
+}
-- 
1.5.3.rc3.9.g9ef91

^ permalink raw reply related

* [PATCH 0/2] another attempt at make_absolute_path()
From: Bradford C Smith @ 2007-07-27 19:10 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin

Here's my attempt at make_absolute_path() and friends.  I think this
version handles symlinks cleanly to avoid problems with '..' path
elements Junio pointed out recently.

I built these with another patch I previously submitted to make
git-config consistently use lockfile.c routines and tested it with the
regular test suite plus some extra tests Junio sent to the list for
checking git-config symlink handling.

I also built a separate executable with just the path handling routines
in it and spot-checked several cases to make sure it appeared to be
working as expected.  ('/', loop of symlinks, lots of extra slashes, .
and .. elements, etc.)

Best Regards,

Bradford

^ permalink raw reply

* Re: Can you do this with GIT?
From: Johannes Schindelin @ 2007-07-27 19:05 UTC (permalink / raw)
  To: CPD; +Cc: git
In-Reply-To: <11834063.post@talk.nabble.com>

Hi,

On Fri, 27 Jul 2007, CPD wrote:

> I hope this is the right forum, it's all I could find. Sincere apologies 
> in advance if I in the wrong place.

You might be interested in http://git.or.cz: there is a lot of useful 
information to find; amongst others what is the right forum...

So yes, this is the right forum.

> I set up a source control system for the company around CVS, but GIT has
> some very attractive features and I'd like to migrate if it can do some
> other things that we need.
> 
> We produce variations based on a (mostly) common codebase. In CVS I set 
> up "environment" modules for each platform, then when you are working on 
> that platform, you simply check out the correct environment and build. 
> Only the needed code and tools are exposed in that environment (this is 
> important as clients must NOT see each other's code and most customers 
> have some customization). I do this by defining and renaming modules in 
> the CVSROOT modules file.

I would use branches for that.  A base branch with the common code, and 
the customisations in all the branches, which merge from the base branch.

If you have an interesting change in a custom branch, you can percolate 
that back into the base branch, by checking out that base branch and 
cherry-picking the commit you want to have.

Of course, you can automate this merging (or even rebasing, if you are 
interested in keeping your customisations nice and tidy) with a script.  
With a git alias even.

Hth,
Dscho

^ permalink raw reply

* [PATCH 8/8] Fix t1500 for sane work-tree behavior
From: Johannes Schindelin @ 2007-07-27 18:59 UTC (permalink / raw)
  To: gitster, git, matled
In-Reply-To: <Pine.LNX.4.64.0707271851370.14781@racer.site>


When GIT_DIR=../.git, and no worktree is specified, it is reasonable
to assume that the repository is not bare, that the work tree is ".."
and that the prefix is the basename of the current directory.

This is the sane behavior.

t1500 tested for the old behavior, which was plain wrong.  And this
patch fixes it minimally.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
	Yeah, I broke down.  But fixing t1501's style was painful enough.

 t/t1500-rev-parse.sh |   16 ++++++++--------
 1 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index ec49966..6e792b6 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -31,9 +31,9 @@ test_rev_parse() {
 test_rev_parse toplevel false false true ''
 
 cd .git || exit 1
-test_rev_parse .git/ false true true .git/
+test_rev_parse .git/ true true false ''
 cd objects || exit 1
-test_rev_parse .git/objects/ false true true .git/objects/
+test_rev_parse .git/objects/ true true false ''
 cd ../.. || exit 1
 
 mkdir -p sub/dir || exit 1
@@ -42,7 +42,7 @@ test_rev_parse subdirectory false false true sub/dir/
 cd ../.. || exit 1
 
 git config core.bare true
-test_rev_parse 'core.bare = true' true false true
+test_rev_parse 'core.bare = true' false false true
 
 git config --unset core.bare
 test_rev_parse 'core.bare undefined' false false true
@@ -53,13 +53,13 @@ export GIT_DIR=../.git
 export GIT_CONFIG="$GIT_DIR"/config
 
 git config core.bare false
-test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true ''
+test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true work/
 
 git config core.bare true
-test_rev_parse 'GIT_DIR=../.git, core.bare = true' true false true ''
+test_rev_parse 'GIT_DIR=../.git, core.bare = true' false false true work/
 
 git config --unset core.bare
-test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true ''
+test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true work/
 
 mv ../.git ../repo.git || exit 1
 export GIT_DIR=../repo.git
@@ -69,9 +69,9 @@ git config core.bare false
 test_rev_parse 'GIT_DIR=../repo.git, core.bare = false' false false true ''
 
 git config core.bare true
-test_rev_parse 'GIT_DIR=../repo.git, core.bare = true' true false true ''
+test_rev_parse 'GIT_DIR=../repo.git, core.bare = true' false false true ''
 
 git config --unset core.bare
-test_rev_parse 'GIT_DIR=../repo.git, core.bare undefined' true false true ''
+test_rev_parse 'GIT_DIR=../repo.git, core.bare undefined' false false true ''
 
 test_done
-- 
1.5.3.rc3.18.g49a1

^ permalink raw reply related

* [PATCH 7/8] Make t1501 a little saner, and fix it
From: Johannes Schindelin @ 2007-07-27 18:58 UTC (permalink / raw)
  To: gitster, git, matled
In-Reply-To: <Pine.LNX.4.64.0707271851370.14781@racer.site>


t1501 is still a PITA to debug, but less so than before.  This patch
fixes also the tests which tested for the wrong (old) work-tree behavior.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/t1501-worktree.sh |  161 +++++++++++++++++++++++++++++----------------------
 1 files changed, 92 insertions(+), 69 deletions(-)

diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh
index aadeeab..fad9a9e 100755
--- a/t/t1501-worktree.sh
+++ b/t/t1501-worktree.sh
@@ -4,89 +4,112 @@ test_description='test separate work tree'
 . ./test-lib.sh
 
 test_rev_parse() {
-	name=$1
-	shift
-
-	test_expect_success "$name: is-bare-repository" \
-	"test '$1' = \"\$(git rev-parse --is-bare-repository)\""
-	shift
-	[ $# -eq 0 ] && return
-
-	test_expect_success "$name: is-inside-git-dir" \
-	"test '$1' = \"\$(git rev-parse --is-inside-git-dir)\""
-	shift
-	[ $# -eq 0 ] && return
-
-	test_expect_success "$name: is-inside-work-tree" \
-	"test '$1' = \"\$(git rev-parse --is-inside-work-tree)\""
-	shift
-	[ $# -eq 0 ] && return
-
-	test_expect_success "$name: prefix" \
-	"test '$1' = \"\$(git rev-parse --show-prefix)\""
-	shift
-	[ $# -eq 0 ] && return
+	name="$1"
+	for option in --is-bare-repository --is-inside-git-dir \
+		--is-inside-work-tree --show-prefix
+	do
+		shift
+		test_expect_success "$name: $option" \
+			"test '$1' = \"\$(git rev-parse $option)\""
+		test $# -eq 1 && return
+	done
 }
 
-mkdir -p work/sub/dir || exit 1
-mv .git repo.git || exit 1
+# usage: set_repo <working directory> [<git dir> [<work tree> [env]]]
+set_repo () {
+	cd "$1"
+	say "switching to $(pwd) with GIT_DIR $2"
+
+	test -z "$2" || {
+		GIT_DIR="$2"
+		GIT_CONFIG="$2"/config
+		export GIT_DIR GIT_CONFIG
+	}
+
+	test -z "$3" ||
+	case "$4" in
+	env)
+		GIT_WORK_TREE="$3"
+		export GIT_WORK_TREE
+		git config core.workTree non-existent
+	;;
+	*)
+		git config core.workTree "$3"
+	esac
+}
+
+test_expect_success 'setup' '
+	mkdir -p work/sub/dir &&
+	mv .git repo.git
+'
 
 say "core.worktree = relative path"
-export GIT_DIR=repo.git
-export GIT_CONFIG=$GIT_DIR/config
-unset GIT_WORK_TREE
-git config core.worktree ../work
-test_rev_parse 'outside'      false false false
-cd work || exit 1
-export GIT_DIR=../repo.git
-export GIT_CONFIG=$GIT_DIR/config
-test_rev_parse 'inside'       false false true ''
-cd sub/dir || exit 1
-export GIT_DIR=../../../repo.git
-export GIT_CONFIG=$GIT_DIR/config
+
+set_repo . repo.git ../work
+test_rev_parse 'outside git dir' false false false
+
+set_repo work ../repo.git
+test_rev_parse 'inside git dir' false false true ''
+
+set_repo sub/dir ../../../repo.git
 test_rev_parse 'subdirectory' false false true sub/dir/
-cd ../../.. || exit 1
+cd ../../..
 
 say "core.worktree = absolute path"
-export GIT_DIR=$(pwd)/repo.git
-export GIT_CONFIG=$GIT_DIR/config
-git config core.worktree "$(pwd)/work"
-test_rev_parse 'outside'      false false false
-cd work || exit 1
-test_rev_parse 'inside'       false false true ''
-cd sub/dir || exit 1
+set_repo . $(pwd)/repo.git $(pwd)/work
+test_rev_parse 'outside git dir' false false false
+
+set_repo work
+test_rev_parse 'inside git dir' false false true ''
+
+set_repo sub/dir
 test_rev_parse 'subdirectory' false false true sub/dir/
-cd ../../.. || exit 1
+cd ../../..
 
 say "GIT_WORK_TREE=relative path (override core.worktree)"
-export GIT_DIR=$(pwd)/repo.git
-export GIT_CONFIG=$GIT_DIR/config
-git config core.worktree non-existent
-export GIT_WORK_TREE=work
-test_rev_parse 'outside'      false false false
-cd work || exit 1
-export GIT_WORK_TREE=.
-test_rev_parse 'inside'       false false true ''
-cd sub/dir || exit 1
-export GIT_WORK_TREE=../..
+
+set_repo . $(pwd)/repo.git work env
+test_rev_parse 'outside git dir' false false false
+
+set_repo work "" . env
+test_rev_parse 'inside git dir' false false true ''
+
+set_repo sub/dir "" ../../ env
 test_rev_parse 'subdirectory' false false true sub/dir/
 cd ../../.. || exit 1
 
+say "GIT_WORK_TREE=absolute path, work tree below git dir"
+
 mv work repo.git/work
 
-say "GIT_WORK_TREE=absolute path, work tree below git dir"
-export GIT_DIR=$(pwd)/repo.git
-export GIT_CONFIG=$GIT_DIR/config
-export GIT_WORK_TREE=$(pwd)/repo.git/work
-test_rev_parse 'outside'              false false false
-cd repo.git || exit 1
-test_rev_parse 'in repo.git'              false true  false
-cd objects || exit 1
-test_rev_parse 'in repo.git/objects'      false true  false
-cd ../work || exit 1
-test_rev_parse 'in repo.git/work'         false false true ''
-cd sub/dir || exit 1
-test_rev_parse 'in repo.git/sub/dir' false false true sub/dir/
-cd ../../../.. || exit 1
+set_repo . $(pwd)/repo.git $(pwd)/repo.git/work env
+test_rev_parse 'outside git dir' false false false
+
+set_repo repo.git
+test_rev_parse 'in repo.git' false true false
+
+set_repo objects
+test_rev_parse 'in repo.git/objects' false true  false
+
+set_repo ../work
+test_rev_parse 'in repo.git/work' false true true ''
+
+set_repo sub/dir
+test_rev_parse 'in repo.git/sub/dir' false true true sub/dir/
+cd ../../../..
+
+test_expect_success 'repo finds its work tree' '
+	(cd repo.git &&
+	 : > work/sub/dir/untracked &&
+	 test sub/dir/untracked = "$(git ls-files --others)")
+'
+
+test_expect_success 'repo finds its work tree from work tree, too' '
+	(cd repo.git/work/sub/dir &&
+	 : > tracked &&
+	 git --git-dir=../../.. add tracked &&
+	 cd ../../.. &&
+	 test sub/dir/tracked = "$(git ls-files)")
+'
 
 test_done
-- 
1.5.3.rc3.18.g49a1

^ permalink raw reply related

* [PATCH 6/8] init: use get_git_work_tree() instead of rolling our own
From: Johannes Schindelin @ 2007-07-27 18:58 UTC (permalink / raw)
  To: gitster, git, matled
In-Reply-To: <Pine.LNX.4.64.0707271851370.14781@racer.site>


Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---

	This is no change in function, but in readability.

 builtin-init-db.c |   48 +++++++++++-------------------------------------
 1 files changed, 11 insertions(+), 37 deletions(-)

diff --git a/builtin-init-db.c b/builtin-init-db.c
index 66ddaeb..0d9b1e0 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -174,36 +174,7 @@ static void copy_templates(const char *git_dir, int len, const char *template_di
 	closedir(dir);
 }
 
-/*
- * Get the full path to the working tree specified in $GIT_WORK_TREE
- * or NULL if no working tree is specified.
- */
-static const char *get_work_tree(void)
-{
-	const char *git_work_tree;
-	char cwd[PATH_MAX];
-	static char worktree[PATH_MAX];
-
-	git_work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
-	if (!git_work_tree)
-		return NULL;
-	if (!getcwd(cwd, sizeof(cwd)))
-		die("Unable to read current working directory");
-	if (chdir(git_work_tree))
-		die("Cannot change directory to specified working tree '%s'",
-			git_work_tree);
-	if (git_work_tree[0] != '/') {
-		if (!getcwd(worktree, sizeof(worktree)))
-			die("Unable to read current working directory");
-		git_work_tree = worktree;
-	}
-	if (chdir(cwd))
-		die("Cannot come back to cwd");
-	return git_work_tree;
-}
-
-static int create_default_files(const char *git_dir, const char *git_work_tree,
-	const char *template_path)
+static int create_default_files(const char *git_dir, const char *template_path)
 {
 	unsigned len = strlen(git_dir);
 	static char path[PATH_MAX];
@@ -282,16 +253,16 @@ static int create_default_files(const char *git_dir, const char *git_work_tree,
 	}
 	git_config_set("core.filemode", filemode ? "true" : "false");
 
-	if (is_bare_repository() && !git_work_tree) {
+	if (is_bare_repository())
 		git_config_set("core.bare", "true");
-	}
 	else {
+		const char *work_tree = get_git_work_tree();
 		git_config_set("core.bare", "false");
 		/* allow template config file to override the default */
 		if (log_all_ref_updates == -1)
 		    git_config_set("core.logallrefupdates", "true");
-		if (git_work_tree)
-			git_config_set("core.worktree", git_work_tree);
+		if (work_tree != git_work_tree_cfg)
+			git_config_set("core.worktree", work_tree);
 	}
 	return reinit;
 }
@@ -308,7 +279,6 @@ static const char init_db_usage[] =
 int cmd_init_db(int argc, const char **argv, const char *prefix)
 {
 	const char *git_dir;
-	const char *git_work_tree;
 	const char *sha1_dir;
 	const char *template_dir = NULL;
 	char *path;
@@ -329,7 +299,11 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
 			usage(init_db_usage);
 	}
 
-	git_work_tree = get_work_tree();
+	git_work_tree_cfg = xcalloc(PATH_MAX, 1);
+	if (!getcwd(git_work_tree_cfg, PATH_MAX))
+		die ("Cannot access current working directory.");
+	if (access(get_git_work_tree(), X_OK))
+		die ("Cannot access work tree '%s'", get_git_work_tree());
 
 	/*
 	 * Set up the default .git directory contents
@@ -346,7 +320,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
 	 */
 	check_repository_format();
 
-	reinit = create_default_files(git_dir, git_work_tree, template_dir);
+	reinit = create_default_files(git_dir, template_dir);
 
 	/*
 	 * And set up the object store.
-- 
1.5.3.rc3.18.g49a1

^ permalink raw reply related

* [PATCH 5/8] work-trees are allowed inside a git-dir
From: Johannes Schindelin @ 2007-07-27 18:57 UTC (permalink / raw)
  To: gitster, git, matled
In-Reply-To: <Pine.LNX.4.64.0707271851370.14781@racer.site>


It is allowed to call

	$ git --git-dir=../ --work-tree=. bla

when you really want to.  In this case, you are both in the git directory
and in the working tree.

The earlier handling of this situation was seriously bogus.  For regular
working tree operations, it checked if inside git dir.  That makes no
sense, of course, since the check should be for a work tree, and nothing
else.

Fix that.

Coincidently, you can omit the "--work-tree" now, if your repository knows
about the location already.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 builtin-ls-files.c |    8 +++++---
 git-sh-setup.sh    |    3 +--
 git.c              |   11 ++++++++---
 3 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/builtin-ls-files.c b/builtin-ls-files.c
index 61577ea..d36181a 100644
--- a/builtin-ls-files.c
+++ b/builtin-ls-files.c
@@ -469,9 +469,11 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
 		break;
 	}
 
-	if (require_work_tree &&
-			(!is_inside_work_tree() || is_inside_git_dir()))
-		die("This operation must be run in a work tree");
+	if (require_work_tree && !is_inside_work_tree()) {
+		const char *work_tree = get_git_work_tree();
+		if (!work_tree || chdir(work_tree))
+			die("This operation must be run in a work tree");
+	}
 
 	pathspec = get_pathspec(prefix, argv + i);
 
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index c51985e..7bef43f 100755
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -59,8 +59,7 @@ cd_to_toplevel () {
 }
 
 require_work_tree () {
-	test $(git rev-parse --is-inside-work-tree) = true &&
-	test $(git rev-parse --is-inside-git-dir) = false ||
+	test $(git rev-parse --is-inside-work-tree) = true ||
 	die "fatal: $0 cannot be used without a working tree."
 }
 
diff --git a/git.c b/git.c
index a647f9c..2433355 100644
--- a/git.c
+++ b/git.c
@@ -272,9 +272,14 @@ static int run_command(struct cmd_struct *p, int argc, const char **argv)
 		prefix = setup_git_directory();
 	if (p->option & USE_PAGER)
 		setup_pager();
-	if ((p->option & NEED_WORK_TREE) &&
-	    (!is_inside_work_tree() || is_inside_git_dir()))
-		die("%s must be run in a work tree", p->cmd);
+	if (p->option & NEED_WORK_TREE) {
+		const char *work_tree = get_git_work_tree();
+		const char *git_dir = get_git_dir();
+		if (!is_absolute_path(git_dir))
+			set_git_dir(make_absolute_path(git_dir));
+		if (!work_tree || chdir(work_tree))
+			die("%s must be run in a work tree", p->cmd);
+	}
 	trace_argv_printf(argv, argc, "trace: built-in: git");
 
 	status = p->fn(argc, argv, prefix);
-- 
1.5.3.rc3.18.g49a1

^ permalink raw reply related

* [PATCH 4/8] Add set_git_dir() function
From: Johannes Schindelin @ 2007-07-27 18:57 UTC (permalink / raw)
  To: gitster, git, matled
In-Reply-To: <Pine.LNX.4.64.0707271851370.14781@racer.site>


With the function set_git_dir() you can reset the path that will
be used for git_path(), git_dir() and friends.

The responsibility to close files and throw away information from the
old git_dir lies with the caller.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---

	This is just ripped out from the branch--new-workdir topic
	I was working on, just that it has a shorter name now.

	Ah, and just to be sure, it xstrdup()s the path.

 cache.h       |    1 +
 environment.c |    8 ++++++++
 2 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/cache.h b/cache.h
index c0cab34..36963f2 100644
--- a/cache.h
+++ b/cache.h
@@ -216,6 +216,7 @@ extern char *get_refs_directory(void);
 extern char *get_index_file(void);
 extern char *get_graft_file(void);
 extern const char *get_git_work_tree(void);
+extern int set_git_dir(const char *path);
 
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
diff --git a/environment.c b/environment.c
index cb112a4..33c0f90 100644
--- a/environment.c
+++ b/environment.c
@@ -121,3 +121,11 @@ char *get_graft_file(void)
 		setup_git_env();
 	return git_graft_file;
 }
+
+int set_git_dir(const char *path)
+{
+	if (setenv(GIT_DIR_ENVIRONMENT, xstrdup(path), 1))
+		return error("Could not set GIT_DIR to '%s'", path);
+	setup_git_env();
+	return 0;
+}
-- 
1.5.3.rc3.18.g49a1

^ permalink raw reply related

* [PATCH 3/8] Clean up work-tree handling
From: Johannes Schindelin @ 2007-07-27 18:56 UTC (permalink / raw)
  To: gitster, git, matled
In-Reply-To: <Pine.LNX.4.64.0707271851370.14781@racer.site>


The old version of work-tree support was an unholy mess, barely readable,
and not to the point.

For example, why do you have to provide a worktree, when it is not used?
As in "git status".  Now it works.

Another riddle was: if you can have work trees inside the git dir, why
are some programs complaining that they need a work tree?

IOW when inside repo.git/work, where GIT_DIR points to repo.git
and GIT_WORK_TREE to work, and cwd is work, --is-inside-git-dir _must_
return true, because it is _in the git dir_, but scripts _must_ test
for the right thing.

Also, GIT_DIR=../.git should behave the same as if no GIT_DIR was
specified, unless there is a repository in the current working directory.
It does now.

In related news, a long standing bug was fixed: when in .git/bla/x.git/,
which is a bare repository, git formerly assumed ../.. to be the
appropriate git dir.  This problem was reported by Shawn Pearce to have
caused much pain, where a colleague mistakenly ran "git init" in "/" a
long time ago, and bare repositories just would not work.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
	Not only because of ohloh am I proud that in spite of removing
	more lines than I added, there were more comments added than
	removed...

 builtin-rev-parse.c |   12 +--
 cache.h             |    2 +
 environment.c       |   32 +++++--
 setup.c             |  270 ++++++++++++++++++++++-----------------------------
 4 files changed, 143 insertions(+), 173 deletions(-)

diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index 497903a..3f787a8 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -320,15 +320,9 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 				continue;
 			}
 			if (!strcmp(arg, "--show-cdup")) {
-				const char *pfx = prefix;
-				while (pfx) {
-					pfx = strchr(pfx, '/');
-					if (pfx) {
-						pfx++;
-						printf("../");
-					}
-				}
-				putchar('\n');
+				const char *work_tree = get_git_work_tree();
+				if (work_tree)
+					printf("%s\n", work_tree);
 				continue;
 			}
 			if (!strcmp(arg, "--git-dir")) {
diff --git a/cache.h b/cache.h
index 98af530..c0cab34 100644
--- a/cache.h
+++ b/cache.h
@@ -208,12 +208,14 @@ enum object_type {
 extern int is_bare_repository_cfg;
 extern int is_bare_repository(void);
 extern int is_inside_git_dir(void);
+extern char *git_work_tree_cfg;
 extern int is_inside_work_tree(void);
 extern const char *get_git_dir(void);
 extern char *get_object_directory(void);
 extern char *get_refs_directory(void);
 extern char *get_index_file(void);
 extern char *get_graft_file(void);
+extern const char *get_git_work_tree(void);
 
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
diff --git a/environment.c b/environment.c
index f83fb9e..cb112a4 100644
--- a/environment.c
+++ b/environment.c
@@ -35,6 +35,10 @@ int pager_in_use;
 int pager_use_color = 1;
 int auto_crlf = 0;	/* 1: both ways, -1: only when adding git objects */
 
+/* This is set by setup_git_dir_gently() and/or git_default_config() */
+char *git_work_tree_cfg;
+static const char *work_tree;
+
 static const char *git_dir;
 static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
 
@@ -62,15 +66,8 @@ static void setup_git_env(void)
 
 int is_bare_repository(void)
 {
-	const char *dir, *s;
-	if (0 <= is_bare_repository_cfg)
-		return is_bare_repository_cfg;
-
-	dir = get_git_dir();
-	if (!strcmp(dir, DEFAULT_GIT_DIR_ENVIRONMENT))
-		return 0;
-	s = strrchr(dir, '/');
-	return !s || strcmp(s + 1, DEFAULT_GIT_DIR_ENVIRONMENT);
+	/* if core.bare is not 'false', let's see if there is a work tree */
+	return is_bare_repository_cfg && !get_git_work_tree();
 }
 
 const char *get_git_dir(void)
@@ -80,6 +77,23 @@ const char *get_git_dir(void)
 	return git_dir;
 }
 
+const char *get_git_work_tree(void)
+{
+	static int initialized = 0;
+	if (!initialized) {
+		work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
+		if (!work_tree) {
+			work_tree = git_work_tree_cfg;
+			if (work_tree && !is_absolute_path(work_tree))
+				work_tree = xstrdup(git_path(work_tree));
+		}
+		if (work_tree && !is_absolute_path(work_tree))
+			work_tree = xstrdup(make_absolute_path(work_tree));
+		initialized = 1;
+	}
+	return work_tree;
+}
+
 char *get_object_directory(void)
 {
 	if (!git_object_dir)
diff --git a/setup.c b/setup.c
index 7b07144..0323d25 100644
--- a/setup.c
+++ b/setup.c
@@ -1,4 +1,8 @@
 #include "cache.h"
+#include "dir.h"
+
+static int inside_git_dir = -1;
+static int inside_work_tree = -1;
 
 const char *prefix_path(const char *prefix, int len, const char *path)
 {
@@ -170,100 +174,80 @@ static int is_git_directory(const char *suspect)
 	return 1;
 }
 
-static int inside_git_dir = -1;
-
 int is_inside_git_dir(void)
 {
-	if (inside_git_dir >= 0)
-		return inside_git_dir;
-	die("BUG: is_inside_git_dir called before setup_git_directory");
+	if (inside_git_dir < 0)
+		inside_git_dir = is_inside_dir(get_git_dir());
+	return inside_git_dir;
 }
 
-static int inside_work_tree = -1;
-
 int is_inside_work_tree(void)
 {
-	if (inside_git_dir >= 0)
-		return inside_work_tree;
-	die("BUG: is_inside_work_tree called before setup_git_directory");
+	if (inside_work_tree < 0)
+		inside_work_tree = is_inside_dir(get_git_work_tree());
+	return inside_work_tree;
 }
 
-static char *gitworktree_config;
-
-static int git_setup_config(const char *var, const char *value)
+/*
+ * If no worktree was given, and we are outside of a default work tree,
+ * now is the time to set it.
+ *
+ * In other words, if the user calls git with something like
+ *
+ *	git --git-dir=/some/where/else bla
+ *
+ * default to the current working directory for the work-tree root.
+ */
+const char* set_work_tree(const char *dir)
 {
-	if (!strcmp(var, "core.worktree")) {
-		if (gitworktree_config)
-			strlcpy(gitworktree_config, value, PATH_MAX);
-		return 0;
+	char dir_buffer[PATH_MAX];
+	static char buffer[PATH_MAX + 1], *rel;
+	int len, postfix_len = strlen(DEFAULT_GIT_DIR_ENVIRONMENT);
+
+	len = strlen(dir);
+	if (len > postfix_len + 1 && dir[len - postfix_len - 1] == '/' &&
+			!strcmp(dir + len - postfix_len,
+				DEFAULT_GIT_DIR_ENVIRONMENT))
+		dir = strncpy(dir_buffer, dir, len - postfix_len - 1);
+
+	rel = get_relative_cwd(buffer, sizeof(buffer), dir);
+	if (rel && *rel)
+		strcat(rel, "/");
+	else {
+		rel = NULL;
+		dir = getcwd(buffer, sizeof(buffer));
 	}
-	return git_default_config(var, value);
+	git_work_tree_cfg = xstrdup(dir);
+	inside_work_tree = 1;
+
+	return rel;
 }
 
+/*
+ * We cannot decide in this function whether we are in the work tree or
+ * not, since the config can only be read _after_ this function was called.
+ */
 const char *setup_git_directory_gently(int *nongit_ok)
 {
+	const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
 	static char cwd[PATH_MAX+1];
-	char worktree[PATH_MAX+1], gitdir[PATH_MAX+1];
-	const char *gitdirenv, *gitworktree;
-	int wt_rel_gitdir = 0;
+	const char *gitdirenv;
+	int len, offset;
 
+	/*
+	 * If GIT_DIR is set explicitly, we're not going
+	 * to do any discovery, but we still do repository
+	 * validation.
+	 */
 	gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
-	if (!gitdirenv) {
-		int len, offset;
-
-		if (!getcwd(cwd, sizeof(cwd)-1))
-			die("Unable to read current working directory");
-
-		offset = len = strlen(cwd);
-		for (;;) {
-			if (is_git_directory(".git"))
-				break;
-			if (offset == 0) {
-				offset = -1;
-				break;
-			}
-			chdir("..");
-			while (cwd[--offset] != '/')
-				; /* do nothing */
-		}
-
-		if (offset >= 0) {
-			inside_work_tree = 1;
-			git_config(git_default_config);
-			if (offset == len) {
-				inside_git_dir = 0;
-				return NULL;
-			}
-
-			cwd[len++] = '/';
-			cwd[len] = '\0';
-			inside_git_dir = !prefixcmp(cwd + offset + 1, ".git/");
-			return cwd + offset + 1;
-		}
-
-		if (chdir(cwd))
-			die("Cannot come back to cwd");
-		if (!is_git_directory(".")) {
-			if (nongit_ok) {
-				*nongit_ok = 1;
-				return NULL;
-			}
-			die("Not a git repository");
-		}
-		setenv(GIT_DIR_ENVIRONMENT, cwd, 1);
-		gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
-		if (!gitdirenv)
-			die("getenv after setenv failed");
-	}
-
-	if (PATH_MAX - 40 < strlen(gitdirenv)) {
-		if (nongit_ok) {
-			*nongit_ok = 1;
+	if (gitdirenv) {
+		if (PATH_MAX - 40 < strlen(gitdirenv))
+			die("'$%s' too big", GIT_DIR_ENVIRONMENT);
+		if (is_git_directory(gitdirenv)) {
+			if (!work_tree_env)
+				return set_work_tree(gitdirenv);
 			return NULL;
 		}
-		die("$%s too big", GIT_DIR_ENVIRONMENT);
-	}
-	if (!is_git_directory(gitdirenv)) {
 		if (nongit_ok) {
 			*nongit_ok = 1;
 			return NULL;
@@ -273,92 +257,53 @@ const char *setup_git_directory_gently(int *nongit_ok)
 
 	if (!getcwd(cwd, sizeof(cwd)-1))
 		die("Unable to read current working directory");
-	if (chdir(gitdirenv)) {
-		if (nongit_ok) {
-			*nongit_ok = 1;
-			return NULL;
-		}
-		die("Cannot change directory to $%s '%s'",
-			GIT_DIR_ENVIRONMENT, gitdirenv);
-	}
-	if (!getcwd(gitdir, sizeof(gitdir)-1))
-		die("Unable to read current working directory");
-	if (chdir(cwd))
-		die("Cannot come back to cwd");
 
 	/*
-	 * In case there is a work tree we may change the directory,
-	 * therefore make GIT_DIR an absolute path.
+	 * Test in the following order (relative to the cwd):
+	 * - .git/
+	 * - ./ (bare)
+	 * - ../.git/
+	 * - ../ (bare)
+	 * - ../../.git/
+	 *   etc.
 	 */
-	if (gitdirenv[0] != '/') {
-		setenv(GIT_DIR_ENVIRONMENT, gitdir, 1);
-		gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
-		if (!gitdirenv)
-			die("getenv after setenv failed");
-		if (PATH_MAX - 40 < strlen(gitdirenv)) {
-			if (nongit_ok) {
-				*nongit_ok = 1;
-				return NULL;
-			}
-			die("$%s too big after expansion to absolute path",
-				GIT_DIR_ENVIRONMENT);
-		}
-	}
-
-	strcat(cwd, "/");
-	strcat(gitdir, "/");
-	inside_git_dir = !prefixcmp(cwd, gitdir);
-
-	gitworktree = getenv(GIT_WORK_TREE_ENVIRONMENT);
-	if (!gitworktree) {
-		gitworktree_config = worktree;
-		worktree[0] = '\0';
-	}
-	git_config(git_setup_config);
-	if (!gitworktree) {
-		gitworktree_config = NULL;
-		if (worktree[0])
-			gitworktree = worktree;
-		if (gitworktree && gitworktree[0] != '/')
-			wt_rel_gitdir = 1;
-	}
-
-	if (wt_rel_gitdir && chdir(gitdirenv))
-		die("Cannot change directory to $%s '%s'",
-			GIT_DIR_ENVIRONMENT, gitdirenv);
-	if (gitworktree && chdir(gitworktree)) {
-		if (nongit_ok) {
-			if (wt_rel_gitdir && chdir(cwd))
-				die("Cannot come back to cwd");
-			*nongit_ok = 1;
+	offset = len = strlen(cwd);
+	for (;;) {
+		if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
+			break;
+		if (is_git_directory(".")) {
+			inside_git_dir = 1;
+			if (!work_tree_env)
+				inside_work_tree = 0;
+			setenv(GIT_DIR_ENVIRONMENT, ".", 1);
 			return NULL;
 		}
-		if (wt_rel_gitdir)
-			die("Cannot change directory to working tree '%s'"
-				" from $%s", gitworktree, GIT_DIR_ENVIRONMENT);
-		else
-			die("Cannot change directory to working tree '%s'",
-				gitworktree);
-	}
-	if (!getcwd(worktree, sizeof(worktree)-1))
-		die("Unable to read current working directory");
-	strcat(worktree, "/");
-	inside_work_tree = !prefixcmp(cwd, worktree);
-
-	if (gitworktree && inside_work_tree && !prefixcmp(worktree, gitdir) &&
-	    strcmp(worktree, gitdir)) {
-		inside_git_dir = 0;
+		chdir("..");
+		do {
+			if (!offset) {
+				if (nongit_ok) {
+					if (chdir(cwd))
+						die("Cannot come back to cwd");
+					*nongit_ok = 1;
+					return NULL;
+				}
+				die("Not a git repository");
+			}
+		} while (cwd[--offset] != '/');
 	}
 
-	if (!inside_work_tree) {
-		if (chdir(cwd))
-			die("Cannot come back to cwd");
+	inside_git_dir = 0;
+	if (!work_tree_env)
+		inside_work_tree = 1;
+	git_work_tree_cfg = xstrndup(cwd, offset);
+	if (offset == len)
 		return NULL;
-	}
 
-	if (!strcmp(cwd, worktree))
-		return NULL;
-	return cwd+strlen(worktree);
+	/* Make "offset" point to past the '/', and add a '/' at the end */
+	offset++;
+	cwd[len++] = '/';
+	cwd[len] = 0;
+	return cwd + offset;
 }
 
 int git_config_perm(const char *var, const char *value)
@@ -382,11 +327,17 @@ int git_config_perm(const char *var, const char *value)
 
 int check_repository_format_version(const char *var, const char *value)
 {
-       if (strcmp(var, "core.repositoryformatversion") == 0)
-               repository_format_version = git_config_int(var, value);
+	if (strcmp(var, "core.repositoryformatversion") == 0)
+		repository_format_version = git_config_int(var, value);
 	else if (strcmp(var, "core.sharedrepository") == 0)
 		shared_repository = git_config_perm(var, value);
-       return 0;
+	else if (strcmp(var, "core.worktree") == 0) {
+		if (git_work_tree_cfg)
+			free(git_work_tree_cfg);
+		git_work_tree_cfg = xstrdup(value);
+		inside_work_tree = -1;
+	}
+	return 0;
 }
 
 int check_repository_format(void)
@@ -402,5 +353,14 @@ const char *setup_git_directory(void)
 {
 	const char *retval = setup_git_directory_gently(NULL);
 	check_repository_format();
+
+	/* If the work tree is not the default one, recompute prefix */
+	if (inside_work_tree < 0) {
+		static char buffer[PATH_MAX + 1];
+		char *rel = get_relative_cwd(buffer, PATH_MAX,
+				get_git_work_tree());
+		return rel && *rel ? strcat(rel, "/") : NULL;
+	}
+
 	return retval;
 }
-- 
1.5.3.rc3.18.g49a1

^ permalink raw reply related

* [PATCH 2/8] Add functions get_relative_cwd() and is_inside_dir()
From: Johannes Schindelin @ 2007-07-27 18:56 UTC (permalink / raw)
  To: gitster, git, matled
In-Reply-To: <Pine.LNX.4.64.0707271851370.14781@racer.site>


The function get_relative_cwd() works just as getcwd(), only that it
takes an absolute path as additional parameter, returning the prefix
of the current working directory relative to the given path.  If the
cwd is no subdirectory of the given path, it returns NULL.

is_inside_dir() is just a trivial wrapper over get_relative_cwd().

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
	This patch should be the same as I sent out earlier.  Just to
	make sure that you get the correct version, I send it again.

 dir.c |   30 ++++++++++++++++++++++++++++++
 dir.h |    3 +++
 2 files changed, 33 insertions(+), 0 deletions(-)

diff --git a/dir.c b/dir.c
index 8d8faf5..c16a004 100644
--- a/dir.c
+++ b/dir.c
@@ -642,3 +642,33 @@ file_exists(const char *f)
   struct stat sb;
   return stat(f, &sb) == 0;
 }
+
+char *get_relative_cwd(char *buffer, int size, const char *dir)
+{
+	char *cwd = buffer;
+
+	if (!dir)
+		return 0;
+
+	if (!getcwd(buffer, PATH_MAX))
+		return 0;
+
+	if (!is_absolute_path(dir))
+		dir = make_absolute_path(dir);
+
+	while (*dir && *dir == *cwd) {
+		dir++;
+		cwd++;
+	}
+	if (*dir)
+		return NULL;
+	if (*cwd == '/')
+		return cwd + 1;
+	return cwd;
+}
+
+int is_inside_dir(const char *dir)
+{
+	char buffer[PATH_MAX];
+	return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL;
+}
diff --git a/dir.h b/dir.h
index ec0e8ab..f55a87b 100644
--- a/dir.h
+++ b/dir.h
@@ -61,4 +61,7 @@ extern void add_exclude(const char *string, const char *base,
 extern int file_exists(const char *);
 extern struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len);
 
+extern char *get_relative_cwd(char *buffer, int size, const char *dir);
+extern int is_inside_dir(const char *dir);
+
 #endif
-- 
1.5.3.rc3.18.g49a1

^ permalink raw reply related

* [PATCH 1/8] Add is_absolute_path() and make_absolute_path()
From: Johannes Schindelin @ 2007-07-27 18:56 UTC (permalink / raw)
  To: gitster, git, matled
In-Reply-To: <Pine.LNX.4.64.0707271851370.14781@racer.site>


This patch adds convenience functions to work with absolute paths.
The function is_absolute_path() should help the efforts to integrate
the MinGW fork.

Note that make_absolute_path() returns a pointer to a static buffer.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---

	Okay, gone is normalize_path().  In contrast, "make_absolute_path()"
	does the getcwd() && chdir() && getcwd() && chdir(back) mantra, to
	follow symlinks.

	AFAICT, make_absolute_path() would be a Good Thing to use for the
	recent lockfile stuff.

 Makefile             |    2 +-
 cache.h              |    5 ++++
 path.c               |   62 ++++++++++++++++++++++++++++++++++++++++++++++++++
 t/t0000-basic.sh     |   16 +++++++++++++
 test-absolute-path.c |   11 +++++++++
 5 files changed, 95 insertions(+), 1 deletions(-)
 create mode 100644 test-absolute-path.c

diff --git a/Makefile b/Makefile
index d8100ad..546e008 100644
--- a/Makefile
+++ b/Makefile
@@ -936,7 +936,7 @@ endif
 
 ### Testing rules
 
-TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X
+TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X
 
 all:: $(TEST_PROGRAMS)
 
diff --git a/cache.h b/cache.h
index 53801b8..98af530 100644
--- a/cache.h
+++ b/cache.h
@@ -358,6 +358,11 @@ int git_config_perm(const char *var, const char *value);
 int adjust_shared_perm(const char *path);
 int safe_create_leading_directories(char *path);
 char *enter_repo(char *path, int strict);
+static inline int is_absolute_path(const char *path)
+{
+	return path[0] == '/';
+}
+const char *make_absolute_path(const char *path);
 
 /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
 extern int sha1_object_info(const unsigned char *, unsigned long *);
diff --git a/path.c b/path.c
index c4ce962..0f7012f 100644
--- a/path.c
+++ b/path.c
@@ -292,3 +292,65 @@ int adjust_shared_perm(const char *path)
 		return -2;
 	return 0;
 }
+
+/* We allow "recursive" symbolic links. Only within reason, though. */
+#define MAXDEPTH 5
+
+const char *make_absolute_path(const char *path)
+{
+	static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
+	char cwd[1024] = "";
+	int buf_index = 1, len;
+
+	int depth = MAXDEPTH;
+	char *last_elem = NULL;
+	struct stat st;
+
+	if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
+		die ("Too long path: %.*s", 60, path);
+
+	while (depth--) {
+		if (stat(buf, &st) || !S_ISDIR(st.st_mode)) {
+			char *last_slash = strrchr(buf, '/');
+			*last_slash = '\0';
+			last_elem = xstrdup(last_slash + 1);
+		}
+
+		if (*buf) {
+			if (!*cwd && getcwd(cwd, sizeof(cwd)) < 0)
+				die ("Could not get current working directory");
+
+			if (chdir(buf))
+				die ("Could not switch to '%s'", buf);
+		}
+		if (getcwd(buf, PATH_MAX) < 0)
+			die ("Could not get current working directory");
+
+		if (last_elem) {
+			int len = strlen(buf);
+			if (len + strlen(last_elem) + 2 > PATH_MAX)
+				die ("Too long path name: '%s/%s'",
+						buf, last_elem);
+			buf[len] = '/';
+			strcpy(buf + len + 1, last_elem);
+			free(last_elem);
+			last_elem = NULL;
+		}
+
+		if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
+			len = readlink(buf, next_buf, PATH_MAX);
+			if (len < 0)
+				die ("Invalid symlink: %s", buf);
+			next_buf[len] = '\0';
+			buf = next_buf;
+			buf_index = 1 - buf_index;
+			next_buf = bufs[buf_index];
+		} else
+			break;
+	}
+
+	if (*cwd && chdir(cwd))
+		die ("Could not change back to '%s'", cwd);
+
+	return buf;
+}
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 4bba9c0..4e49d59 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -281,4 +281,20 @@ test_expect_success 'update-index D/F conflict' '
 	test $numpath0 = 1
 '
 
+test_expect_success 'absolute path works as expected' '
+	mkdir first &&
+	ln -s ../.git first/.git &&
+	mkdir second &&
+	ln -s ../first second/other &&
+	mkdir third &&
+	dir="$(cd .git; pwd -P)" &&
+	dir2=third/../second/other/.git &&
+	test "$dir" = "$(test-absolute-path $dir2)" &&
+	file="$dir"/index &&
+	test "$file" = "$(test-absolute-path $dir2/index)" &&
+	ln -s ../first/file .git/syml &&
+	sym="$(cd first; pwd -P)"/file &&
+	test "$sym" = "$(test-absolute-path $dir2/syml)"
+'
+
 test_done
diff --git a/test-absolute-path.c b/test-absolute-path.c
new file mode 100644
index 0000000..c959ea2
--- /dev/null
+++ b/test-absolute-path.c
@@ -0,0 +1,11 @@
+#include "cache.h"
+
+int main(int argc, char **argv)
+{
+	while (argc > 1) {
+		puts(make_absolute_path(argv[1]));
+		argc--;
+		argv++;
+	}
+	return 0;
+}
-- 
1.5.3.rc3.18.g49a1

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox