Git development
 help / color / mirror / Atom feed
* Re: [PATCH 5/5] fetch: Clean output
From: Santi @ 2006-09-30  9:42 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vpsdehzcs.fsf@assigned-by-dhcp.cox.net>

2006/9/30, Junio C Hamano <junkio@cox.net>:
> Santi Béjar <sbejar@gmail.com> writes:
>
> > Do not show duplicated remote branch information and reformat the output as:
> >
> > $ git fetch -v   # the committish lines for the -v.
> > * refs/heads/origin: fast forward to remote branch 'master' of ../git/
> >   1ad7a06..bc1a580
> >   committish: bc1a580
>
> I am not quite sure about this --- it is not obvious what these
> two numbers represent anymore.  Also I think the last line
> outlived its usefulness (99% of the time refs are committish, so
> noting exception is good but otherwise it is not interesting).

I agree in the case of updating a ref, but I would keep it otherwise
(and even in this case only when -v is given).

>
> I know you opted for minimum patch, but it might be a good time
> to polish the wording a bit while we are touching the general
> vicinity of the code.
>
> How about saying something like:
>
>  * refs/heads/origin: fast forward to remote branch 'master' of ../git/
>    old..new = 1ad7a06..bc1a580

I like it.

>
> > * refs/heads/pu: does not fast forward to remote branch 'pu' of ../git/;
> >   7c733a8...5faa935
> >   not updating.
> >   forcing update.
> >   committish: 5faa935
>
> This is even more confusing.  Perhaps we would want to have two
> cases, depending on --force (and +).

Sorry, it was from two copy'n'paste. I did it in a new repository
(without the +), then it stoped at this point (I suppose to force the
user to solve the problem). Then I put the + and did the copy'n'paste
again without deleting this extra line.

>
>  * refs/heads/pu: does not fast forward to remote branch 'pu' of ../git/;
>    but forcing update anyway.  old...new = 7c733a8...5faa935

With the old...new in a new line?

So at the end, something like this output?

$ git fetch -v   # the committish lines for the -v.
* refs/heads/origin: fast forward to remote branch 'master' of ../git/
 old..new: 1ad7a06..bc1a580
* refs/heads/pu: does not fast forward to remote branch 'pu' of ../git/
 old...new: 7c733a8...5faa935
 forcing update.
* refs/heads/next: same as remote branch 'origin/next' of ../git/
 committish: ce47b9f
...
* refs/tags/v1.4.2-rc4: storing tag 'v1.4.2-rc4' of ../git/
 committish: 8c7a107

$ git fetch -v origin refs/heads/master
* committish: 695dffe
 branch 'master' of ../git/

Santi

^ permalink raw reply

* Re: [PATCH 5/5] fetch: Clean output
From: Junio C Hamano @ 2006-09-30 10:37 UTC (permalink / raw)
  To: Santi; +Cc: git
In-Reply-To: <8aa486160609300242i6f52ee90o235e668a5d865fb7@mail.gmail.com>

Santi <sbejar@gmail.com> writes:

> So at the end, something like this output?
>
> $ git fetch -v   # the committish lines for the -v.
> * refs/heads/origin: fast forward to remote branch 'master' of ../git/
> old..new: 1ad7a06..bc1a580
> * refs/heads/pu: does not fast forward to remote branch 'pu' of ../git/
> old...new: 7c733a8...5faa935
> forcing update.
> * refs/heads/next: same as remote branch 'origin/next' of ../git/
> committish: ce47b9f
> ...
> * refs/tags/v1.4.2-rc4: storing tag 'v1.4.2-rc4' of ../git/
> committish: 8c7a107
>
> $ git fetch -v origin refs/heads/master
> * committish: 695dffe
> branch 'master' of ../git/

For the last one, the second and third line feel backwards.

Most cases in your summary (i.e. storing on a tracking branch or
tag) in general look like:

        * <local ref name>: <what happened and where it came from>
	<label>: <object names>

where <label> (e.g. old..new) describes what <object names>
mean.  So to be consistent, the <label> for "same as" case
(i.e. storing what is fetched in a local ref, but it was already
the same) and "storing anew" case, we might want to say their
true object types, not "committish".

Also we probably would want to "s/remote branch/branch" in the
"where from" part, since we always say "remote" in fetch anyway.

Then the last case where we do not use local tracking branch
would simply be something like:

	* fetched branch 'master' of ../git
        commit: 695dffe

How about this?  Essentially the same as the above but with
minor tweaks.

1. the ones we store in our local ref (either branches or tags):

 1a) fast-forward

  * refs/heads/origin: fast forward to branch 'master' of ../git/
    old..new: 1ad7a06..bc1a580

 1b) same (only shown under -v)

  * refs/heads/next: same as branch 'origin/next' of ../git/
    commit: ce47b9f

 1c) non-fast-forward, forced

  * refs/heads/pu: forcing update to branch 'pu' of ../git/
    old...new: 7c733a8...5faa935

 1d) non-fast-forward, did not update because not forced

  * refs/heads/po: does not fast-forward to branch 'po' of ../git/
    old...new: 7c733a8...5faa935

 1e) creating a new local ref to store

  * refs/tags/v1.4.2-rc4: storing tag 'v1.4.2-rc4' of ../git/
    tag: 8c7a107

2. the ones we do not store in our local ref (only shown under -v):

  * fetched branch 'master' of ../git
    commit: 695dffe

I think we should reword the "what happened" for 1d to clarify
that we did not touch refs/heads/po, but other than that I am
reasonably happy about how they look consistent.

But please don't take the above too seriously.  As everybody
should be very well aware by now, I am very bad at UI designs.

^ permalink raw reply

* Re: git and time
From: Linus Torvalds @ 2006-09-30 17:36 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Shawn Pearce, git
In-Reply-To: <7v4pupizix.fsf@assigned-by-dhcp.cox.net>



On Sat, 30 Sep 2006, Junio C Hamano wrote:

> Shawn Pearce <spearce@spearce.org> writes:
> 
> > Agreed.  I've been missing such a command and have wanted to add
> > one but it wasn't important enough to me to actually code it.  :)
> 
> Everything you said in your message sounds sane and makes sense
> to me.  Now we have to find a sucker^Wvolunteer to implement it
> ;-).

Ehh. As far as I can see it's 
 - a damn hard thing to do efficiently
 - essentially exactly the same problem you already solved with "git 
   describe"

In other words, I think you could make git describe do it, by simply 
making it parse not just all tags, but also walking the branch log.

		Linus

^ permalink raw reply

* Re: [PATCH] gitweb: start to generate PATH_INFO URLs
From: Martin Waitz @ 2006-09-30 18:14 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7v8xk2jofc.fsf@assigned-by-dhcp.cox.net>

[-- Attachment #1: Type: text/plain, Size: 944 bytes --]

hoi :)

On Fri, Sep 29, 2006 at 03:30:47PM -0700, Junio C Hamano wrote:
> Martin Waitz <tali@admingilde.org> writes:
> 
> > Instead of providing the project as a ?p= parameter it is simply appended
> > to the base URI.
> > All other parameters are appended to that, except for ?a=summary which
> > is the default and can be omitted.
> 
> Supporting PATH_INFO in the sense that we do sensible things
> when we get called with one is one thing, but generating such a
> URL that uses PATH_INFO is a different thing.  I suspect not
> everybody's webserver is configured to call us with PATH_INFO,
> so this should be conditional.

right, and in fact it was more intended as a RFC, to see what
people think about such a thing.  Obviously I wanted to have
it for my repository, so I implemented it unconditional first.

Should we use the gitweb feature mechanism to enable/disable
PATH_INFO URL generation?

-- 
Martin Waitz

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply

* Re: [PATCH] gitweb: start to generate PATH_INFO URLs
From: Junio C Hamano @ 2006-09-30 19:42 UTC (permalink / raw)
  To: Martin Waitz; +Cc: git
In-Reply-To: <20060930181408.GD2871@admingilde.org>

Martin Waitz <tali@admingilde.org> writes:

> Should we use the gitweb feature mechanism to enable/disable
> PATH_INFO URL generation?

That sounds sensible.  I personally do not think many people
would object if you made the default to true.  Also I do not
think it would make much sense to make this overridable by
repository configuration.

^ permalink raw reply

* [PATCH 1/2] ref locking: allow 'foo' when 'foo/bar' used to exist but not anymore.
From: Junio C Hamano @ 2006-09-30 19:49 UTC (permalink / raw)
  To: git

It is normal to have .git/refs/heads/foo directory which is
empty after the last branch whose name starts with foo/ is
removed.  Make sure we notice this case and allow creation of
branch foo by removing the empty directory.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---

 * Recent git-branch in "next" does the moral equivalent of this
   itself, but other users of git-update-ref would not be helped
   without something like this.

   We would need to do the same for $GIT_DIR/logs/ but that is
   for a later round.  I want to get the deletion of packed refs
   right first but before doing that the code needs some more
   clean-ups.

 refs.c |   64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 64 insertions(+), 0 deletions(-)

diff --git a/refs.c b/refs.c
index 3d4cdd1..b433c0c 100644
--- a/refs.c
+++ b/refs.c
@@ -473,6 +473,59 @@ static struct ref_lock *verify_lock(stru
 	return lock;
 }
 
+static int remove_empty_dir_recursive(char *path, int len)
+{
+	DIR *dir = opendir(path);
+	struct dirent *e;
+	int ret = 0;
+
+	if (!dir)
+		return -1;
+	if (path[len-1] != '/')
+		path[len++] = '/';
+	while ((e = readdir(dir)) != NULL) {
+		struct stat st;
+		int namlen;
+		if ((e->d_name[0] == '.') &&
+		    ((e->d_name[1] == 0) ||
+		     ((e->d_name[1] == '.') && e->d_name[2] == 0)))
+			continue; /* "." and ".." */
+
+		namlen = strlen(e->d_name);
+		if ((len + namlen < PATH_MAX) &&
+		    strcpy(path + len, e->d_name) &&
+		    !lstat(path, &st) &&
+		    S_ISDIR(st.st_mode) &&
+		    remove_empty_dir_recursive(path, len + namlen))
+			continue; /* happy */
+
+		/* path too long, stat fails, or non-directory still exists */
+		ret = -1;
+		break;
+	}
+	closedir(dir);
+	if (!ret) {
+		path[len] = 0;
+		ret = rmdir(path);
+	}
+	return ret;
+}
+
+static int remove_empty_directories(char *file)
+{
+	/* we want to create a file but there is a directory there;
+	 * if that is an empty directory (or a directory that contains
+	 * only empty directories), remove them.
+	 */
+	char path[PATH_MAX];
+	int len = strlen(file);
+
+	if (len >= PATH_MAX) /* path too long ;-) */
+		return -1;
+	strcpy(path, file);
+	return remove_empty_dir_recursive(path, len);
+}
+
 static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1)
 {
 	char *ref_file;
@@ -485,6 +538,17 @@ static struct ref_lock *lock_ref_sha1_ba
 	lock->lock_fd = -1;
 
 	ref = resolve_ref(ref, lock->old_sha1, mustexist, NULL);
+	if (!ref && errno == EISDIR) {
+		/* we are trying to lock foo but we used to
+		 * have foo/bar which now does not exist;
+		 * it is normal for the empty directory 'foo'
+		 * to remain.
+		 */
+		ref_file = git_path("%s", orig_ref);
+		if (remove_empty_directories(ref_file))
+			die("there are still refs under '%s'", orig_ref);
+		ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, NULL);
+	}
 	if (!ref) {
 		int last_errno = errno;
 		error("unable to resolve reference %s: %s",
-- 
1.4.2.1.g5a98f

^ permalink raw reply related

* [PATCH 2/2] refs: minor restructuring of cached refs data.
From: Junio C Hamano @ 2006-09-30 19:49 UTC (permalink / raw)
  To: git

Once we read packed and loose refs, for_each_ref() and friends
kept using them even after write_ref_sha1() and delete_ref()
changed the refs.  This adds invalidate_cached_refs() as a way
to flush the cached information.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 * This should not change the behaviour of the existing code in
   any way, but is a necessary step to handle ref deletion
   correctly.

 refs.c |   56 +++++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/refs.c b/refs.c
index b433c0c..6ee5f96 100644
--- a/refs.c
+++ b/refs.c
@@ -66,12 +66,42 @@ static struct ref_list *add_ref(const ch
 	return list;
 }
 
-static struct ref_list *get_packed_refs(void)
+/*
+ * Future: need to be in "struct repository"
+ * when doing a full libification.
+ */
+struct cached_refs {
+	char did_loose;
+	char did_packed;
+	struct ref_list *loose;
+	struct ref_list *packed;
+} cached_refs;
+
+static void free_ref_list(struct ref_list *list)
+{
+	struct ref_list *next;
+	for ( ; list; list = next) {
+		next = list->next;
+		free(list);
+	}
+}
+
+static void invalidate_cached_refs(void)
 {
-	static int did_refs = 0;
-	static struct ref_list *refs = NULL;
+	struct cached_refs *ca = &cached_refs;
+
+	if (ca->did_loose && ca->loose)
+		free_ref_list(ca->loose);
+	if (ca->did_packed && ca->packed)
+		free_ref_list(ca->packed);
+	ca->loose = ca->packed = NULL;
+	ca->did_loose = ca->did_packed = 0;
+}
 
-	if (!did_refs) {
+static struct ref_list *get_packed_refs(void)
+{
+	if (!cached_refs.did_packed) {
+		struct ref_list *refs = NULL;
 		FILE *f = fopen(git_path("packed-refs"), "r");
 		if (f) {
 			struct ref_list *list = NULL;
@@ -86,9 +116,10 @@ static struct ref_list *get_packed_refs(
 			fclose(f);
 			refs = list;
 		}
-		did_refs = 1;
+		cached_refs.packed = refs;
+		cached_refs.did_packed = 1;
 	}
-	return refs;
+	return cached_refs.packed;
 }
 
 static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
@@ -138,14 +169,11 @@ static struct ref_list *get_ref_dir(cons
 
 static struct ref_list *get_loose_refs(void)
 {
-	static int did_refs = 0;
-	static struct ref_list *refs = NULL;
-
-	if (!did_refs) {
-		refs = get_ref_dir("refs", NULL);
-		did_refs = 1;
+	if (!cached_refs.did_loose) {
+		cached_refs.loose = get_ref_dir("refs", NULL);
+		cached_refs.did_loose = 1;
 	}
-	return refs;
+	return cached_refs.loose;
 }
 
 /* We allow "recursive" symbolic refs. Only within reason, though */
@@ -401,6 +429,7 @@ int delete_ref(const char *refname, unsi
 		fprintf(stderr, "warning: unlink(%s) failed: %s",
 			lock->log_file, strerror(errno));
 
+	invalidate_cached_refs();
 	return ret;
 }
 
@@ -665,6 +694,7 @@ int write_ref_sha1(struct ref_lock *lock
 		unlock_ref(lock);
 		return -1;
 	}
+	invalidate_cached_refs();
 	if (log_ref_write(lock, sha1, logmsg) < 0) {
 		unlock_ref(lock);
 		return -1;
-- 
1.4.2.1.g5a98f

^ permalink raw reply related

* [PATCH 1/2] Move code resolving packed refs into its own function.
From: Christian Couder @ 2006-09-30 20:01 UTC (permalink / raw)
  To: Junio Hamano; +Cc: git

This patch move Linus' packed refs resolving code from
"resolve_ref" into a new "resolve_packed_ref" extern
function so that it can be reused when needed.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
 cache.h |    2 ++
 refs.c  |   34 ++++++++++++++++++++--------------
 2 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/cache.h b/cache.h
index 4e01a74..f37bc18 100644
--- a/cache.h
+++ b/cache.h
@@ -296,6 +296,8 @@ extern int get_sha1_hex(const char *hex,
 extern char *sha1_to_hex(const unsigned char *sha1);	/* static buffer result! */
 extern int read_ref(const char *filename, unsigned char *sha1);
 extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
+extern const char *resolve_packed_ref(const char *ref, unsigned char *sha1, int, int *);
+
 extern int create_symref(const char *ref, const char *refs_heads_master);
 extern int validate_symref(const char *ref);
 
diff --git a/refs.c b/refs.c
index 3d4cdd1..5e3988b 100644
--- a/refs.c
+++ b/refs.c
@@ -148,6 +148,25 @@ static struct ref_list *get_loose_refs(v
 	return refs;
 }
 
+const char *resolve_packed_ref(const char *ref, unsigned char *sha1,
+			       int reading, int *flag)
+{
+	struct ref_list *list = get_packed_refs();
+	while (list) {
+		if (!strcmp(ref, list->name)) {
+			hashcpy(sha1, list->sha1);
+			if (flag)
+				*flag |= REF_ISPACKED;
+			return ref;
+		}
+		list = list->next;
+	}
+	if (reading || errno != ENOENT)
+		return NULL;
+	hashclr(sha1);
+	return ref;
+}
+
 /* We allow "recursive" symbolic refs. Only within reason, though */
 #define MAXDEPTH 5
 
@@ -177,20 +196,7 @@ const char *resolve_ref(const char *ref,
 		 * reading.
 		 */
 		if (lstat(path, &st) < 0) {
-			struct ref_list *list = get_packed_refs();
-			while (list) {
-				if (!strcmp(ref, list->name)) {
-					hashcpy(sha1, list->sha1);
-					if (flag)
-						*flag |= REF_ISPACKED;
-					return ref;
-				}
-				list = list->next;
-			}
-			if (reading || errno != ENOENT)
-				return NULL;
-			hashclr(sha1);
-			return ref;
+			return resolve_packed_ref(ref, sha1, reading, flag);
 		}
 
 		/* Follow "normalized" - ie "refs/.." symlinks by hand */
-- 
1.4.2.1.g7cf04

^ permalink raw reply related

* [PATCH 2/2] Check packed refs when creating leading directories.
From: Christian Couder @ 2006-09-30 20:02 UTC (permalink / raw)
  To: Junio Hamano; +Cc: git

This patch adds an optionnal packed refs check to the
"safe_create_leading_directories" function. This check is
controled by a new int parameter "check_packed_ref" that
must be set.

In "lock_ref_sha1_basic", "safe_create_leading_directories"
is now called with this parameter set, so that no directory
frotz can be created if there is already a frotz packed ref.

The previous check for this is removed from "git-branch.sh"
as it is now useless.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
 builtin-apply.c   |    2 +-
 cache.h           |    2 +-
 git-branch.sh     |   10 ----------
 local-fetch.c     |    2 +-
 merge-recursive.c |    2 +-
 refs.c            |    4 ++--
 server-info.c     |    4 ++--
 sha1_file.c       |   12 +++++++++++-
 8 files changed, 19 insertions(+), 19 deletions(-)

diff --git a/builtin-apply.c b/builtin-apply.c
index de5f855..0d6a20c 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -2291,7 +2291,7 @@ static void create_one_file(char *path, 
 		return;
 
 	if (errno == ENOENT) {
-		if (safe_create_leading_directories(path))
+		if (safe_create_leading_directories(path, 0))
 			return;
 		if (!try_create_file(path, mode, buf, size))
 			return;
diff --git a/cache.h b/cache.h
index f37bc18..e05a7b0 100644
--- a/cache.h
+++ b/cache.h
@@ -239,7 +239,7 @@ enum sharedrepo {
 };
 int git_config_perm(const char *var, const char *value);
 int adjust_shared_perm(const char *path);
-int safe_create_leading_directories(char *path);
+int safe_create_leading_directories(char *path, int check_packed_ref);
 char *enter_repo(char *path, int strict);
 
 /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
diff --git a/git-branch.sh b/git-branch.sh
index c616830..bf84b30 100755
--- a/git-branch.sh
+++ b/git-branch.sh
@@ -121,16 +121,6 @@ then
 	done
 fi
 
-branchdir=$(dirname $branchname)
-while test "$branchdir" != "."
-do
-	if git-show-ref --verify --quiet -- "refs/heads/$branchdir"
-	then
-		die "$branchdir already exists."
-	fi
-	branchdir=$(dirname $branchdir)
-done
-
 prev=''
 if git-show-ref --verify --quiet -- "refs/heads/$branchname"
 then
diff --git a/local-fetch.c b/local-fetch.c
index 7b6875c..14672f6 100644
--- a/local-fetch.c
+++ b/local-fetch.c
@@ -56,7 +56,7 @@ static int setup_indices(void)
 static int copy_file(const char *source, char *dest, const char *hex,
 		     int warn_if_not_exists)
 {
-	safe_create_leading_directories(dest);
+	safe_create_leading_directories(dest, 0);
 	if (use_link) {
 		if (!link(source, dest)) {
 			pull_say("link %s\n", hex);
diff --git a/merge-recursive.c b/merge-recursive.c
index 611cd95..4c9b6b2 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -515,7 +515,7 @@ static int mkdir_p(const char *path, uns
 {
 	/* path points to cache entries, so xstrdup before messing with it */
 	char *buf = xstrdup(path);
-	int result = safe_create_leading_directories(buf);
+	int result = safe_create_leading_directories(buf, 0);
 	free(buf);
 	return result;
 }
diff --git a/refs.c b/refs.c
index 5e3988b..1ac7d61 100644
--- a/refs.c
+++ b/refs.c
@@ -506,7 +506,7 @@ static struct ref_lock *lock_ref_sha1_ba
 	ref_file = git_path("%s", ref);
 	lock->force_write = lstat(ref_file, &st) && errno == ENOENT;
 
-	if (safe_create_leading_directories(ref_file))
+	if (safe_create_leading_directories(ref_file, 1))
 		die("unable to create directory for %s", ref_file);
 	lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1);
 
@@ -549,7 +549,7 @@ static int log_ref_write(struct ref_lock
 	const char *committer;
 
 	if (log_all_ref_updates) {
-		if (safe_create_leading_directories(lock->log_file) < 0)
+		if (safe_create_leading_directories(lock->log_file, 0) < 0)
 			return error("unable to create directory for %s",
 				lock->log_file);
 		oflags |= O_CREAT;
diff --git a/server-info.c b/server-info.c
index 6cd38be..455f333 100644
--- a/server-info.c
+++ b/server-info.c
@@ -30,7 +30,7 @@ static int update_info_refs(int force)
 	strcpy(path1, path0);
 	strcpy(path1 + len, "+");
 
-	safe_create_leading_directories(path0);
+	safe_create_leading_directories(path0, 0);
 	info_ref_fp = fopen(path1, "w");
 	if (!info_ref_fp)
 		return error("unable to update %s", path0);
@@ -219,7 +219,7 @@ static int update_info_packs(int force)
 
 	init_pack_info(infofile, force);
 
-	safe_create_leading_directories(name);
+	safe_create_leading_directories(name, 0);
 	fp = fopen(name, "w");
 	if (!fp)
 		return error("cannot open %s", name);
diff --git a/sha1_file.c b/sha1_file.c
index 18c2f88..b6c0da3 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -74,7 +74,7 @@ int get_sha1_hex(const char *hex, unsign
 	return 0;
 }
 
-int safe_create_leading_directories(char *path)
+int safe_create_leading_directories(char *path, int check_packed_ref)
 {
 	char *pos = path;
 	struct stat st;
@@ -102,6 +102,16 @@ int safe_create_leading_directories(char
 			*pos = '/';
 			return -2;
 		}
+		else if (check_packed_ref) {
+			unsigned char sha1[20];
+			const char *ref = path + strlen(get_git_dir()) + 1;
+			if (resolve_packed_ref(ref, sha1, 1, NULL)) {
+				rmdir(path);
+				fprintf(stderr, "%s already exists.\n", ref);
+				*pos = '/';
+				return -4;
+			}
+		}
 		*pos++ = '/';
 	}
 	return 0;
-- 
1.4.2.1.g7cf04

^ permalink raw reply related

* Re: [PATCH 1/2] Move code resolving packed refs into its own function.
From: Junio C Hamano @ 2006-09-30 20:33 UTC (permalink / raw)
  To: Christian Couder; +Cc: git
In-Reply-To: <20060930220158.d331bb7c.chriscool@tuxfamily.org>

Christian Couder <chriscool@tuxfamily.org> writes:

> This patch move Linus' packed refs resolving code from
> "resolve_ref" into a new "resolve_packed_ref" extern
> function so that it can be reused when needed.

I think we are stepping on each other's toes.  How far into the
process of making correct branch deletion are you?

^ permalink raw reply

* [PATCH] refs: refactor reflog reading code to be reusable
From: Jeff King @ 2006-09-30 21:29 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

This just moves code around; the behavior of read_ref_at (currently the
only user) should be identical.

Signed-off-by: Jeff King <peff@peff.net>
---
This patch is in preparation for doing more interesting things with
reflogs, like git-describe.

 refs.c |  176 +++++++++++++++++++++++++++++++++++++++------------------------
 refs.h |   21 ++++++++
 2 files changed, 130 insertions(+), 67 deletions(-)

diff --git a/refs.c b/refs.c
index 3d4cdd1..f5dfe4d 100644
--- a/refs.c
+++ b/refs.c
@@ -615,83 +615,125 @@ int write_ref_sha1(struct ref_lock *lock
 	return 0;
 }
 
-int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1)
+int reflog_open(struct reflog *log, const char *ref)
 {
-	const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec;
-	char *tz_c;
-	int logfd, tz;
+	int logfd;
 	struct stat st;
-	unsigned long date;
-	unsigned char logged_sha1[20];
 
-	logfile = git_path("logs/%s", ref);
-	logfd = open(logfile, O_RDONLY, 0);
+	log->file = xstrdup(git_path("logs/%s", ref));
+	logfd = open(log->file, O_RDONLY, 0);
 	if (logfd < 0)
-		die("Unable to read log %s: %s", logfile, strerror(errno));
+		return -1;
 	fstat(logfd, &st);
-	if (!st.st_size)
-		die("Log %s is empty.", logfile);
-	logdata = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, logfd, 0);
+	log->size = st.st_size;
+	log->data = mmap(NULL, log->size, PROT_READ, MAP_PRIVATE, logfd, 0);
 	close(logfd);
+	return 0;
+}
 
-	lastrec = NULL;
-	rec = logend = logdata + st.st_size;
-	while (logdata < rec) {
-		if (logdata < rec && *(rec-1) == '\n')
-			rec--;
-		lastgt = NULL;
-		while (logdata < rec && *(rec-1) != '\n') {
-			rec--;
-			if (*rec == '>')
-				lastgt = rec;
-		}
-		if (!lastgt)
-			die("Log %s is corrupt.", logfile);
-		date = strtoul(lastgt + 1, &tz_c, 10);
-		if (date <= at_time) {
-			if (lastrec) {
-				if (get_sha1_hex(lastrec, logged_sha1))
-					die("Log %s is corrupt.", logfile);
-				if (get_sha1_hex(rec + 41, sha1))
-					die("Log %s is corrupt.", logfile);
-				if (hashcmp(logged_sha1, sha1)) {
-					tz = strtoul(tz_c, NULL, 10);
-					fprintf(stderr,
-						"warning: Log %s has gap after %s.\n",
-						logfile, show_rfc2822_date(date, tz));
-				}
-			}
-			else if (date == at_time) {
-				if (get_sha1_hex(rec + 41, sha1))
-					die("Log %s is corrupt.", logfile);
-			}
-			else {
-				if (get_sha1_hex(rec + 41, logged_sha1))
-					die("Log %s is corrupt.", logfile);
-				if (hashcmp(logged_sha1, sha1)) {
-					tz = strtoul(tz_c, NULL, 10);
-					fprintf(stderr,
-						"warning: Log %s unexpectedly ended on %s.\n",
-						logfile, show_rfc2822_date(date, tz));
-				}
-			}
-			munmap((void*)logdata, st.st_size);
+void reflog_close(struct reflog *log)
+{
+	free(log->file);
+	munmap((void *)log->data, log->size);
+}
+
+int reflog_next(struct reflog *log, struct reflog_entry *out)
+{
+	const char *rec, *lastgt;
+	char *tz_c;
+
+	rec = log->rec;
+	if (rec == log->data)
+		return 0;
+
+	lastgt = NULL;
+	if (log->data < rec && *(rec-1) == '\n')
+		rec--;
+	while (log->data < rec && *(rec-1) != '\n') {
+		rec--;
+		if (*rec == '>')
+			lastgt = rec;
+	}
+	if (log->data == rec - 1)
+		rec--;
+	if (!lastgt)
+		die("Log %s is corrupt (nolastgt).", log->file);
+
+	out->date = strtoul(lastgt + 1, &tz_c, 10);
+	out->tz = strtoul(tz_c, NULL, 10);
+	out->rec = rec;
+	out->log = log;
+	log->rec = rec;
+
+	return 1;
+}
+
+void reflog_start(struct reflog *log)
+{
+	log->rec = log->data + log->size;
+}
+
+void reflog_entry_from(const struct reflog_entry *e, unsigned char *sha1)
+{
+	if (get_sha1_hex(e->rec, sha1))
+		die("Log %s is corrupt.", e->log->file);
+}
+
+void reflog_entry_to(const struct reflog_entry *e, unsigned char *sha1)
+{
+	if (get_sha1_hex(e->rec+41, sha1))
+		die("Log %s is corrupt.", e->log->file);
+}
+
+int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1)
+{
+	struct reflog log;
+	struct reflog_entry cur, last;
+	int last_is_valid;
+	unsigned char sha1_from[20];
+	unsigned char sha1_to[20];
+
+	if (reflog_open(&log, ref) < 0)
+		die("Unable to read log %s: %s", log.file, strerror(errno));
+
+	last_is_valid = 0;
+	reflog_start(&log);
+	do {
+		if (!reflog_next(&log, &cur)) {
+			if (!last_is_valid)
+				die("Log %s is empty.", log.file);
+			reflog_entry_to(&last, sha1);
+			fprintf(stderr,
+				"warning: Log %s only goes back to %s.\n",
+				log.file,
+				show_rfc2822_date(last.date, last.tz));
+			reflog_close(&log);
 			return 0;
 		}
-		lastrec = rec;
+		last = cur;
+		last_is_valid = 1;
+	} while (at_time < cur.date);
+
+	reflog_entry_to(&cur, sha1_to);
+	if (last_is_valid) {
+		reflog_entry_from(&last, sha1_from);
+		if (hashcmp(sha1_to, sha1_from)) {
+			fprintf(stderr,
+				"warning: Log %s has gap after %s.\n",
+				log.file,
+				show_rfc2822_date(cur.date, cur.tz));
+		}
+	}
+	else if(cur.date != at_time) {
+		if (hashcmp(sha1_to, sha1)) {
+			fprintf(stderr,
+				"warning: Log %s unexpectedly ended on %s.\n",
+				log.file,
+				show_rfc2822_date(cur.date, cur.tz));
+		}
 	}
 
-	rec = logdata;
-	while (rec < logend && *rec != '>' && *rec != '\n')
-		rec++;
-	if (rec == logend || *rec == '\n')
-		die("Log %s is corrupt.", logfile);
-	date = strtoul(rec + 1, &tz_c, 10);
-	tz = strtoul(tz_c, NULL, 10);
-	if (get_sha1_hex(logdata, sha1))
-		die("Log %s is corrupt.", logfile);
-	munmap((void*)logdata, st.st_size);
-	fprintf(stderr, "warning: Log %s only goes back to %s.\n",
-		logfile, show_rfc2822_date(date, tz));
+	reflog_close(&log);
+	hashcpy(sha1, sha1_to);
 	return 0;
 }
diff --git a/refs.h b/refs.h
index 0d4d79e..9f2667b 100644
--- a/refs.h
+++ b/refs.h
@@ -10,6 +10,20 @@ struct ref_lock {
 	int force_write;
 };
 
+struct reflog {
+	char *file;
+	const char *data;
+	unsigned long size;
+	const char *rec;
+};
+
+struct reflog_entry {
+	unsigned long date;
+	unsigned tz;
+	const char *rec;
+	const struct reflog *log;
+};
+
 /*
  * Calls the specified function for each ref file until it returns nonzero,
  * and returns the value
@@ -44,4 +58,11 @@ extern int read_ref_at(const char *ref, 
 /** Returns 0 if target has the right format for a ref. **/
 extern int check_ref_format(const char *target);
 
+int reflog_open(struct reflog *log, const char *ref);
+void reflog_close(struct reflog *log);
+void reflog_start(struct reflog *log);
+int reflog_next(struct reflog *log, struct reflog_entry *out);
+void reflog_entry_from(const struct reflog_entry *e, unsigned char *sha1);
+void reflog_entry_to(const struct reflog_entry *e, unsigned char *sha1);
+
 #endif /* REFS_H */
-- 
1.4.2.1.g67e7-dirty

^ permalink raw reply related

* [PATCH 0/6] ref deletion and D/F conflict avoidance with packed-refs.
From: Junio C Hamano @ 2006-09-30 22:26 UTC (permalink / raw)
  To: git; +Cc: Christian Couder, Jeff King
In-Reply-To: <7vmz8hccxl.fsf@assigned-by-dhcp.cox.net>

This series cleans up the area that was affected by the recent
addition of "packed-refs".  Christian Couder and Jeff King CC'ed
since they seem to be touching in the general vicinity of the
code these patches touch.

[1/6] ref locking: allow 'foo' when 'foo/bar' used to exist but not anymore.
[2/6] refs: minor restructuring of cached refs data.
[3/6] lock_ref_sha1(): do not sometimes error() and sometimes die().
[4/6] lock_ref_sha1(): check D/F conflict with packed ref when creating.
[5/6] delete_ref(): delete packed ref
[6/6] git-branch: remove D/F check done by hand.

I opted for removing from the packed-ref file when a ref that is
packed is deleted.  

^ permalink raw reply

* [PATCH] git-describe: allow reflogs as reference points
From: Jeff King @ 2006-09-30 22:29 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git


Signed-off-by: Jeff King <peff@peff.net>
---
It's basically impossible to use this in the git repository, since we
prefer tags to reflogs. Should --reflog turn off tag matching? Should
there be a command line option to turn off tags?

 Documentation/git-describe.txt |    9 ++++++-
 describe.c                     |   51 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 58 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
index 2700f35..975da0f 100644
--- a/Documentation/git-describe.txt
+++ b/Documentation/git-describe.txt
@@ -8,7 +8,7 @@ git-describe - Show the most recent tag 
 
 SYNOPSIS
 --------
-'git-describe' [--all] [--tags] [--abbrev=<n>] <committish>...
+'git-describe' [--all] [--tags] [--reflog] [--abbrev=<n>] <committish>...
 
 DESCRIPTION
 -----------
@@ -31,6 +31,13 @@ OPTIONS
 	Instead of using only the annotated tags, use any tag
 	found in `.git/refs/tags`.
 
+--reflog=<fmt>::
+	Instead of using only the annotated tags, use reference dates
+	marked in branch reflogs. The output will be of the form
+	'branch@{date}' where the format of date is specified by <fmt>, which
+	must be one of 'relative', '822', 'git'. If no format is specified,
+	the default is 'relative'.
+
 --abbrev=<n>::
 	Instead of using the default 8 hexadecimal digits as the
 	abbreviated object name, use <n> digits.
diff --git a/describe.c b/describe.c
index f4029ee..1dc5209 100644
--- a/describe.c
+++ b/describe.c
@@ -6,11 +6,16 @@ #include "refs.h"
 #define SEEN (1u << 0)
 
 static const char describe_usage[] =
-"git-describe [--all] [--tags] [--abbrev=<n>] <committish>*";
+"git-describe [--all] [--tags] [--reflog] [--abbrev=<n>] <committish>*";
 
 static int all;	/* Default to annotated tags only */
 static int tags;	/* But allow any tags if --tags is specified */
 
+#define FMT_DATE_822 1
+#define FMT_DATE_GIT 2
+#define FMT_DATE_REL 3
+static int reflog;
+
 static int abbrev = DEFAULT_ABBREV;
 
 static int names, allocs;
@@ -85,6 +90,35 @@ static int get_name(const char *path, co
 	return 0;
 }
 
+static int get_reflog_name(const char *ref, const unsigned char *head_sha1,
+		int flag, void *cb_data)
+{
+	unsigned char sha1[20];
+	char buf[1024];
+	struct reflog log;
+	struct reflog_entry c;
+
+	snprintf(buf, sizeof(buf), "refs/heads/%s", ref);
+	if (reflog_open(&log, buf) < 0) {
+		if (errno == ENOENT)
+			return 0;
+		die("unable to read log %s: %s\n", log.file, strerror(errno));
+	}
+	reflog_start(&log);
+	while (reflog_next(&log, &c)) {
+		snprintf(buf, sizeof(buf), "%s@{%s}",
+			ref,
+			reflog == FMT_DATE_REL ? show_date(c.date, 0, 1) :
+			reflog == FMT_DATE_GIT ? show_date(c.date, c.tz, 0) :
+			show_rfc2822_date(c.date, c.tz));
+		reflog_entry_to(&c, sha1);
+		add_to_known_names(buf, lookup_commit_reference(sha1), 0);
+	}
+	reflog_close(&log);
+	return 0;
+}
+
+
 static int compare_names(const void *_a, const void *_b)
 {
 	struct commit_name *a = *(struct commit_name **)_a;
@@ -114,6 +148,8 @@ static void describe(const char *arg, in
 	if (!initialized) {
 		initialized = 1;
 		for_each_ref(get_name, NULL);
+		if (reflog)
+			for_each_branch_ref(get_reflog_name, NULL);
 		qsort(name_array, names, sizeof(*name_array), compare_names);
 	}
 
@@ -152,6 +188,19 @@ int main(int argc, char **argv)
 			all = 1;
 		else if (!strcmp(arg, "--tags"))
 			tags = 1;
+		else if (!strncmp(arg, "--reflog=", 9)) {
+			if (!strcmp(arg+9, "2822") || !strcmp(arg+9, "822"))
+				reflog = FMT_DATE_822;
+			else if (!strcmp(arg+9, "git"))
+				reflog = FMT_DATE_GIT;
+			else if (!strcmp(arg+9, "relative"))
+				reflog = FMT_DATE_REL;
+			else
+				usage(describe_usage);
+		}
+		else if (!strcmp(arg, "--reflog")) {
+			reflog = FMT_DATE_REL;
+		}
 		else if (!strncmp(arg, "--abbrev=", 9)) {
 			abbrev = strtoul(arg + 9, NULL, 10);
 			if (abbrev < MINIMUM_ABBREV || 40 < abbrev)
-- 
1.4.2.1.gd4f1-dirty

^ permalink raw reply related

* [PATCH 1/6] ref locking: allow 'foo' when 'foo/bar' used to exist but not anymore.
From: Junio C Hamano @ 2006-09-30 22:29 UTC (permalink / raw)
  To: git
In-Reply-To: <7v1wptc7ou.fsf@assigned-by-dhcp.cox.net>

It is normal to have .git/refs/heads/foo directory which is
empty after the last branch whose name starts with foo/ is
removed.  Make sure we notice this case and allow creation of
branch foo by removing the empty directory.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 * This and the next one are essentially the same as my previous
   two patch series.

 refs.c |   64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 64 insertions(+), 0 deletions(-)

diff --git a/refs.c b/refs.c
index 3d4cdd1..b433c0c 100644
--- a/refs.c
+++ b/refs.c
@@ -473,6 +473,59 @@ static struct ref_lock *verify_lock(stru
 	return lock;
 }
 
+static int remove_empty_dir_recursive(char *path, int len)
+{
+	DIR *dir = opendir(path);
+	struct dirent *e;
+	int ret = 0;
+
+	if (!dir)
+		return -1;
+	if (path[len-1] != '/')
+		path[len++] = '/';
+	while ((e = readdir(dir)) != NULL) {
+		struct stat st;
+		int namlen;
+		if ((e->d_name[0] == '.') &&
+		    ((e->d_name[1] == 0) ||
+		     ((e->d_name[1] == '.') && e->d_name[2] == 0)))
+			continue; /* "." and ".." */
+
+		namlen = strlen(e->d_name);
+		if ((len + namlen < PATH_MAX) &&
+		    strcpy(path + len, e->d_name) &&
+		    !lstat(path, &st) &&
+		    S_ISDIR(st.st_mode) &&
+		    remove_empty_dir_recursive(path, len + namlen))
+			continue; /* happy */
+
+		/* path too long, stat fails, or non-directory still exists */
+		ret = -1;
+		break;
+	}
+	closedir(dir);
+	if (!ret) {
+		path[len] = 0;
+		ret = rmdir(path);
+	}
+	return ret;
+}
+
+static int remove_empty_directories(char *file)
+{
+	/* we want to create a file but there is a directory there;
+	 * if that is an empty directory (or a directory that contains
+	 * only empty directories), remove them.
+	 */
+	char path[PATH_MAX];
+	int len = strlen(file);
+
+	if (len >= PATH_MAX) /* path too long ;-) */
+		return -1;
+	strcpy(path, file);
+	return remove_empty_dir_recursive(path, len);
+}
+
 static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1)
 {
 	char *ref_file;
@@ -485,6 +538,17 @@ static struct ref_lock *lock_ref_sha1_ba
 	lock->lock_fd = -1;
 
 	ref = resolve_ref(ref, lock->old_sha1, mustexist, NULL);
+	if (!ref && errno == EISDIR) {
+		/* we are trying to lock foo but we used to
+		 * have foo/bar which now does not exist;
+		 * it is normal for the empty directory 'foo'
+		 * to remain.
+		 */
+		ref_file = git_path("%s", orig_ref);
+		if (remove_empty_directories(ref_file))
+			die("there are still refs under '%s'", orig_ref);
+		ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, NULL);
+	}
 	if (!ref) {
 		int last_errno = errno;
 		error("unable to resolve reference %s: %s",
-- 
1.4.2.1.g5a98f

^ permalink raw reply related

* [PATCH 2/6] refs: minor restructuring of cached refs data.
From: Junio C Hamano @ 2006-09-30 22:30 UTC (permalink / raw)
  To: git
In-Reply-To: <7v1wptc7ou.fsf@assigned-by-dhcp.cox.net>

Once we read packed and loose refs, for_each_ref() and friends
kept using them even after write_ref_sha1() and delete_ref()
changed the refs.  This adds invalidate_cached_refs() as a way
to flush the cached information.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 * This and the previous one are essentially the same as my previous
   two patch series.

   getting rid of "we run only once" mentality.  otherwise we
   can never libify the thing...

 refs.c |   56 +++++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/refs.c b/refs.c
index b433c0c..6ee5f96 100644
--- a/refs.c
+++ b/refs.c
@@ -66,12 +66,42 @@ static struct ref_list *add_ref(const ch
 	return list;
 }
 
-static struct ref_list *get_packed_refs(void)
+/*
+ * Future: need to be in "struct repository"
+ * when doing a full libification.
+ */
+struct cached_refs {
+	char did_loose;
+	char did_packed;
+	struct ref_list *loose;
+	struct ref_list *packed;
+} cached_refs;
+
+static void free_ref_list(struct ref_list *list)
+{
+	struct ref_list *next;
+	for ( ; list; list = next) {
+		next = list->next;
+		free(list);
+	}
+}
+
+static void invalidate_cached_refs(void)
 {
-	static int did_refs = 0;
-	static struct ref_list *refs = NULL;
+	struct cached_refs *ca = &cached_refs;
+
+	if (ca->did_loose && ca->loose)
+		free_ref_list(ca->loose);
+	if (ca->did_packed && ca->packed)
+		free_ref_list(ca->packed);
+	ca->loose = ca->packed = NULL;
+	ca->did_loose = ca->did_packed = 0;
+}
 
-	if (!did_refs) {
+static struct ref_list *get_packed_refs(void)
+{
+	if (!cached_refs.did_packed) {
+		struct ref_list *refs = NULL;
 		FILE *f = fopen(git_path("packed-refs"), "r");
 		if (f) {
 			struct ref_list *list = NULL;
@@ -86,9 +116,10 @@ static struct ref_list *get_packed_refs(
 			fclose(f);
 			refs = list;
 		}
-		did_refs = 1;
+		cached_refs.packed = refs;
+		cached_refs.did_packed = 1;
 	}
-	return refs;
+	return cached_refs.packed;
 }
 
 static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
@@ -138,14 +169,11 @@ static struct ref_list *get_ref_dir(cons
 
 static struct ref_list *get_loose_refs(void)
 {
-	static int did_refs = 0;
-	static struct ref_list *refs = NULL;
-
-	if (!did_refs) {
-		refs = get_ref_dir("refs", NULL);
-		did_refs = 1;
+	if (!cached_refs.did_loose) {
+		cached_refs.loose = get_ref_dir("refs", NULL);
+		cached_refs.did_loose = 1;
 	}
-	return refs;
+	return cached_refs.loose;
 }
 
 /* We allow "recursive" symbolic refs. Only within reason, though */
@@ -401,6 +429,7 @@ int delete_ref(const char *refname, unsi
 		fprintf(stderr, "warning: unlink(%s) failed: %s",
 			lock->log_file, strerror(errno));
 
+	invalidate_cached_refs();
 	return ret;
 }
 
@@ -665,6 +694,7 @@ int write_ref_sha1(struct ref_lock *lock
 		unlock_ref(lock);
 		return -1;
 	}
+	invalidate_cached_refs();
 	if (log_ref_write(lock, sha1, logmsg) < 0) {
 		unlock_ref(lock);
 		return -1;
-- 
1.4.2.1.g5a98f

^ permalink raw reply related

* [PATCH 4/6] lock_ref_sha1(): check D/F conflict with packed ref when creating.
From: Junio C Hamano @ 2006-09-30 22:30 UTC (permalink / raw)
  To: git
In-Reply-To: <7v1wptc7ou.fsf@assigned-by-dhcp.cox.net>

This makes the ref locking codepath to notice if an existing ref
overlaps with the ref we are creating.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 refs.c |   24 ++++++++++++++++++++++++
 1 files changed, 24 insertions(+), 0 deletions(-)

diff --git a/refs.c b/refs.c
index 157de43..2bfa92a 100644
--- a/refs.c
+++ b/refs.c
@@ -588,6 +588,30 @@ static struct ref_lock *lock_ref_sha1_ba
 			orig_ref, strerror(errno));
 		goto error_return;
 	}
+	if (is_null_sha1(lock->old_sha1)) {
+		/* The ref did not exist and we are creating it.
+		 * Make sure there is no existing ref that is packed
+		 * whose name begins with our refname, nor a ref whose
+		 * name is a proper prefix of our refname.
+		 */
+		int namlen = strlen(ref); /* e.g. 'foo/bar' */
+		struct ref_list *list = get_packed_refs();
+		while (list) {
+			/* list->name could be 'foo' or 'foo/bar/baz' */
+			int len = strlen(list->name);
+			int cmplen = (namlen < len) ? namlen : len;
+			const char *lead = (namlen < len) ? list->name : ref;
+
+			if (!strncmp(ref, list->name, cmplen) &&
+			    lead[cmplen] == '/') {
+				error("'%s' exists; cannot create '%s'",
+				      list->name, ref);
+				goto error_return;
+			}
+			list = list->next;
+		}
+	}
+
 	lock->lk = xcalloc(1, sizeof(struct lock_file));
 
 	lock->ref_name = xstrdup(ref);
-- 
1.4.2.1.g5a98f

^ permalink raw reply related

* [PATCH 5/6] delete_ref(): delete packed ref
From: Junio C Hamano @ 2006-09-30 22:30 UTC (permalink / raw)
  To: git
In-Reply-To: <7v1wptc7ou.fsf@assigned-by-dhcp.cox.net>

This implements deletion of a packed ref.  Since it is a very
rare event to delete a ref compared to looking up, creating and
updating, this opts to remove the ref from the packed-ref file
instead of doing any of the filesystem based "negative ref" trick
to optimize the deletion path.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 * This looks larger than it actually is, because it moves delete_ref()
   down so that it can use lock_ref_sha1_basic().

 refs.c |  109 +++++++++++++++++++++++++++++++++++++++++++++-------------------
 1 files changed, 77 insertions(+), 32 deletions(-)

diff --git a/refs.c b/refs.c
index 2bfa92a..858c534 100644
--- a/refs.c
+++ b/refs.c
@@ -406,33 +406,6 @@ int get_ref_sha1(const char *ref, unsign
 	return read_ref(mkpath("refs/%s", ref), sha1);
 }
 
-int delete_ref(const char *refname, unsigned char *sha1)
-{
-	struct ref_lock *lock;
-	int err, i, ret = 0;
-
-	lock = lock_any_ref_for_update(refname, sha1);
-	if (!lock)
-		return 1;
-	i = strlen(lock->lk->filename) - 5; /* .lock */
-	lock->lk->filename[i] = 0;
-	err = unlink(lock->lk->filename);
-	if (err) {
-		ret = 1;
-		error("unlink(%s) failed: %s",
-		      lock->lk->filename, strerror(errno));
-	}
-	lock->lk->filename[i] = '.';
-
-	err = unlink(lock->log_file);
-	if (err && errno != ENOENT)
-		fprintf(stderr, "warning: unlink(%s) failed: %s",
-			lock->log_file, strerror(errno));
-
-	invalidate_cached_refs();
-	return ret;
-}
-
 /*
  * Make sure "ref" is something reasonable to have under ".git/refs/";
  * We do not like it if:
@@ -555,7 +528,7 @@ static int remove_empty_directories(char
 	return remove_empty_dir_recursive(path, len);
 }
 
-static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1)
+static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int *flag)
 {
 	char *ref_file;
 	const char *orig_ref = ref;
@@ -567,7 +540,7 @@ static struct ref_lock *lock_ref_sha1_ba
 	lock = xcalloc(1, sizeof(struct ref_lock));
 	lock->lock_fd = -1;
 
-	ref = resolve_ref(ref, lock->old_sha1, mustexist, NULL);
+	ref = resolve_ref(ref, lock->old_sha1, mustexist, flag);
 	if (!ref && errno == EISDIR) {
 		/* we are trying to lock foo but we used to
 		 * have foo/bar which now does not exist;
@@ -580,7 +553,7 @@ static struct ref_lock *lock_ref_sha1_ba
 			error("there are still refs under '%s'", orig_ref);
 			goto error_return;
 		}
-		ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, NULL);
+		ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, flag);
 	}
 	if (!ref) {
 		last_errno = errno;
@@ -640,12 +613,84 @@ struct ref_lock *lock_ref_sha1(const cha
 	if (check_ref_format(ref))
 		return NULL;
 	strcpy(refpath, mkpath("refs/%s", ref));
-	return lock_ref_sha1_basic(refpath, old_sha1);
+	return lock_ref_sha1_basic(refpath, old_sha1, NULL);
 }
 
 struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1)
 {
-	return lock_ref_sha1_basic(ref, old_sha1);
+	return lock_ref_sha1_basic(ref, old_sha1, NULL);
+}
+
+static int repack_without_ref(const char *refname)
+{
+	struct ref_list *list, *packed_ref_list;
+	int fd;
+	int found = 0;
+	struct lock_file packlock;
+
+	packed_ref_list = get_packed_refs();
+	for (list = packed_ref_list; list; list = list->next) {
+		if (!strcmp(refname, list->name)) {
+			found = 1;
+			break;
+		}
+	}
+	if (!found)
+		return 0;
+	memset(&packlock, 0, sizeof(packlock));
+	fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
+	if (fd < 0)
+		return error("cannot delete '%s' from packed refs", refname);
+
+	for (list = packed_ref_list; list; list = list->next) {
+		char line[PATH_MAX + 100];
+		int len;
+
+		if (!strcmp(refname, list->name))
+			continue;
+		len = snprintf(line, sizeof(line), "%s %s\n",
+			       sha1_to_hex(list->sha1), list->name);
+		/* this should not happen but just being defensive */
+		if (len > sizeof(line))
+			die("too long a refname '%s'", list->name);
+		write_or_die(fd, line, len);
+	}
+	return commit_lock_file(&packlock);
+}
+
+int delete_ref(const char *refname, unsigned char *sha1)
+{
+	struct ref_lock *lock;
+	int err, i, ret = 0, flag = 0;
+
+	lock = lock_ref_sha1_basic(refname, sha1, &flag);
+	if (!lock)
+		return 1;
+	if (!(flag & REF_ISPACKED)) {
+		/* loose */
+		i = strlen(lock->lk->filename) - 5; /* .lock */
+		lock->lk->filename[i] = 0;
+		err = unlink(lock->lk->filename);
+		if (err) {
+			ret = 1;
+			error("unlink(%s) failed: %s",
+			      lock->lk->filename, strerror(errno));
+		}
+		lock->lk->filename[i] = '.';
+	}
+	/* removing the loose one could have resurrected an earlier
+	 * packed one.  Also, if it was not loose we need to repack
+	 * without it.
+	 */
+	ret |= repack_without_ref(refname);
+
+	err = unlink(lock->log_file);
+	if (err && errno != ENOENT)
+		fprintf(stderr, "warning: unlink(%s) failed: %s",
+			lock->log_file, strerror(errno));
+	invalidate_cached_refs();
+	unlock_ref(lock);
+	return ret;
 }
 
 void unlock_ref(struct ref_lock *lock)
-- 
1.4.2.1.g5a98f

^ permalink raw reply related

* [PATCH 3/6] lock_ref_sha1(): do not sometimes error() and sometimes die().
From: Junio C Hamano @ 2006-09-30 22:30 UTC (permalink / raw)
  To: git
In-Reply-To: <7v1wptc7ou.fsf@assigned-by-dhcp.cox.net>

This cleans up the error path in the function so it does not
die() itself sometimes while signalling an error with NULL some
other times which was inconsistent and confusing.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 refs.c |   26 ++++++++++++++++++--------
 1 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/refs.c b/refs.c
index 6ee5f96..157de43 100644
--- a/refs.c
+++ b/refs.c
@@ -561,6 +561,7 @@ static struct ref_lock *lock_ref_sha1_ba
 	const char *orig_ref = ref;
 	struct ref_lock *lock;
 	struct stat st;
+	int last_errno = 0;
 	int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
 
 	lock = xcalloc(1, sizeof(struct ref_lock));
@@ -574,17 +575,18 @@ static struct ref_lock *lock_ref_sha1_ba
 		 * to remain.
 		 */
 		ref_file = git_path("%s", orig_ref);
-		if (remove_empty_directories(ref_file))
-			die("there are still refs under '%s'", orig_ref);
+		if (remove_empty_directories(ref_file)) {
+			last_errno = errno;
+			error("there are still refs under '%s'", orig_ref);
+			goto error_return;
+		}
 		ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, NULL);
 	}
 	if (!ref) {
-		int last_errno = errno;
+		last_errno = errno;
 		error("unable to resolve reference %s: %s",
 			orig_ref, strerror(errno));
-		unlock_ref(lock);
-		errno = last_errno;
-		return NULL;
+		goto error_return;
 	}
 	lock->lk = xcalloc(1, sizeof(struct lock_file));
 
@@ -593,11 +595,19 @@ static struct ref_lock *lock_ref_sha1_ba
 	ref_file = git_path("%s", ref);
 	lock->force_write = lstat(ref_file, &st) && errno == ENOENT;
 
-	if (safe_create_leading_directories(ref_file))
-		die("unable to create directory for %s", ref_file);
+	if (safe_create_leading_directories(ref_file)) {
+		last_errno = errno;
+		error("unable to create directory for %s", ref_file);
+		goto error_return;
+	}
 	lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1);
 
 	return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
+
+ error_return:
+	unlock_ref(lock);
+	errno = last_errno;
+	return NULL;
 }
 
 struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
-- 
1.4.2.1.g5a98f

^ permalink raw reply related

* [PATCH 6/6] git-branch: remove D/F check done by hand.
From: Junio C Hamano @ 2006-09-30 22:30 UTC (permalink / raw)
  To: git
In-Reply-To: <7v1wptc7ou.fsf@assigned-by-dhcp.cox.net>

Now ref creation codepath in lock_ref_sha1() and friends notices
the directory/file conflict situation, we do not do this by hand
in git-branch anymore.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-branch.sh |   10 ----------
 1 files changed, 0 insertions(+), 10 deletions(-)

diff --git a/git-branch.sh b/git-branch.sh
index c616830..bf84b30 100755
--- a/git-branch.sh
+++ b/git-branch.sh
@@ -121,16 +121,6 @@ then
 	done
 fi
 
-branchdir=$(dirname $branchname)
-while test "$branchdir" != "."
-do
-	if git-show-ref --verify --quiet -- "refs/heads/$branchdir"
-	then
-		die "$branchdir already exists."
-	fi
-	branchdir=$(dirname $branchdir)
-done
-
 prev=''
 if git-show-ref --verify --quiet -- "refs/heads/$branchname"
 then
-- 
1.4.2.1.g5a98f

^ permalink raw reply related

* Re: [PATCH 0/6] ref deletion and D/F conflict avoidance with packed-refs.
From: Jeff King @ 2006-09-30 22:36 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7v1wptc7ou.fsf@assigned-by-dhcp.cox.net>

On Sat, Sep 30, 2006 at 03:26:25PM -0700, Junio C Hamano wrote:

> This series cleans up the area that was affected by the recent
> addition of "packed-refs".  Christian Couder and Jeff King CC'ed
> since they seem to be touching in the general vicinity of the
> code these patches touch.

I don't believe there is any conflict between my patches and yours.

-Peff

^ permalink raw reply

* Re: git and time
From: Junio C Hamano @ 2006-09-30 23:04 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0609301033460.3952@g5.osdl.org>

Linus Torvalds <torvalds@osdl.org> writes:

> On Sat, 30 Sep 2006, Junio C Hamano wrote:
>
>> Shawn Pearce <spearce@spearce.org> writes:
>> 
>> > Agreed.  I've been missing such a command and have wanted to add
>> > one but it wasn't important enough to me to actually code it.  :)
>> 
>> Everything you said in your message sounds sane and makes sense
>> to me.  Now we have to find a sucker^Wvolunteer to implement it
>> ;-).
>
> Ehh. As far as I can see it's 
>  - a damn hard thing to do efficiently
>  - essentially exactly the same problem you already solved with "git 
>    describe"
>
> In other words, I think you could make git describe do it, by simply 
> making it parse not just all tags, but also walking the branch log.

As a user interface, I think it makes a lot of sense to have
"git describe" do it without introducing a new command.

However, I think the traditional "find the closest ancestor"
behaviour and ref-log behaviour are mutually incompatible, while
they both return information to help address similar issues to
the end user when viewed at a very high level.

Especially, "find the closest ancestor" behaviour means when you
get "tag-gXXXX" as an answer, the tag proper does _not_ contain
the given commit (e.g. commit v1.4.2-g4839bd8 is not part of
v1.4.2).  To answer "when did the fix deadbeef go into master
branch", reporting "master@{yesterday}-gdeadbeef" with the same
logic and format is misleading; "master@{yesterday}" may be the
closest ancestor of commit deadbeef, but that means it does
_not_ contain the fix.  When walking ref-log, we want it the
other way around: "find the earliest descendant among the
entries in ref-log for a particular branch".

The internal logic for doing that may be somewhat different and
I suspect you may not be able to share much code with the
existing logic..

^ permalink raw reply

* Re: git and time
From: Linus Torvalds @ 2006-10-01  0:13 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vd59d7y8v.fsf@assigned-by-dhcp.cox.net>



On Sat, 30 Sep 2006, Junio C Hamano wrote:
> 
> However, I think the traditional "find the closest ancestor"
> behaviour and ref-log behaviour are mutually incompatible, while
> they both return information to help address similar issues to
> the end user when viewed at a very high level.
> 
> Especially, "find the closest ancestor" behaviour means when you
> get "tag-gXXXX" as an answer, the tag proper does _not_ contain
> the given commit (e.g. commit v1.4.2-g4839bd8 is not part of
> v1.4.2).

Correct.

But that just means that we should take the _next_ one in the time-ordered 
list, no?

		Linus

^ permalink raw reply

* Re: [PATCH] git-describe: allow reflogs as reference points
From: Junio C Hamano @ 2006-10-01  0:20 UTC (permalink / raw)
  To: Jeff King; +Cc: git
In-Reply-To: <20060930222903.GA11252@coredump.intra.peff.net>

Jeff King <peff@peff.net> writes:

> Signed-off-by: Jeff King <peff@peff.net>
> ---
> It's basically impossible to use this in the git repository, since we
> prefer tags to reflogs. Should --reflog turn off tag matching? Should
> there be a command line option to turn off tags?

(Also please see my other message to Linus).

I think the behaviour we want out of the command when using
ref-log and when describing based on well-known points (aka
tags) are mutually incompatible, so I agree --reflog should turn
off tag matching.  Also you need to be prepared to handle a
ref-log that happens to have a rewound history.  See attached
patch.

I do not however think the patch is useful to get the
information the discussion which lead to this patch wanted to
know, as I described in a separate message today.  For example,
asking:

	$ git describe --reflog ce74618

answers "next@{3 months ago}-gce74618", in my repository, which
is quite bogus.  "next@{3 months ago}" is 47e5c0; it indeed is
an ancestor of ce74618 and it probably was once at the tip of
"next", so you are not giving any incorrect information, but
that one certainly did _not_ contain the change ce74618
introduced, so that form of output is not useful for answering
the question "when did this commit hit this branch".

	$ gitk master..next

shows that the commit ce74618 is a direct descendant of the
current "master" (4839bd8), and was merged into "next" for the
first time with 5a98f4e which was done last night.

Another example.  Recently somebody on the kernel list was
bitten by an ancient bug in diff.c::emit_rewrite_diff().  The
bug was there in the very first version of this feature (June
19, 2005) and nobody touched the vicinity of the code.  I fixed
it on top of recent master.

However, somebody else could have fixed it on top of v0.99 and
then I could have merged the fix from his branch that is based
on v0.99 (the bug is that ancient).  I do not enable the reflog
on my "master", but if I did, it would have looked like:

	from v0.99^ to v0.99 on July 10th 15:40 2005
	from v0.99 to 013aab on July 10th 23:55 2005
	...
	from 18b633 to 4839bd on Sept 29th 18:54 2006

IOW, my reflog would have recorded v0.99 was once at the tip of
the "master" branch.  Using it, describing 

	git describe --reflog $that_fixes_on_v0_99

would say v0.99 is the closest ancestor of the fix, which is
technically correct.  And if I merge that on top of my "master"
creating a new merge commit, that merge commit is the first
commit my "master" branch sees the fix.  So when I make the
merge, the reflog would have a new entry at the end:

	from 4839bd to xxxxxx on Sept 30th 17:10 2006 merge the fix

and what would be useful is to find this record and notice the
commit is the first one in the "master"'s history that has the
fix as its ancestor.


diff --git a/describe.c b/describe.c
index 1dc5209..cc38651 100644
--- a/describe.c
+++ b/describe.c
@@ -106,13 +106,18 @@ static int get_reflog_name(const char *r
 	}
 	reflog_start(&log);
 	while (reflog_next(&log, &c)) {
+		struct commit *commit;
 		snprintf(buf, sizeof(buf), "%s@{%s}",
 			ref,
 			reflog == FMT_DATE_REL ? show_date(c.date, 0, 1) :
 			reflog == FMT_DATE_GIT ? show_date(c.date, c.tz, 0) :
 			show_rfc2822_date(c.date, c.tz));
 		reflog_entry_to(&c, sha1);
-		add_to_known_names(buf, lookup_commit_reference(sha1), 0);
+		commit = lookup_commit_reference(sha1);
+		if (!commit)
+			/* rewound head */
+			continue;
+		add_to_known_names(buf, commit, 0);
 	}
 	reflog_close(&log);
 	return 0;
@@ -147,9 +151,10 @@ static void describe(const char *arg, in
 
 	if (!initialized) {
 		initialized = 1;
-		for_each_ref(get_name, NULL);
 		if (reflog)
 			for_each_branch_ref(get_reflog_name, NULL);
+		else
+			for_each_ref(get_name, NULL);
 		qsort(name_array, names, sizeof(*name_array), compare_names);
 	}
 

^ permalink raw reply related

* Re: git and time
From: Junio C Hamano @ 2006-10-01  0:24 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0609301712340.3952@g5.osdl.org>

Linus Torvalds <torvalds@osdl.org> writes:

> On Sat, 30 Sep 2006, Junio C Hamano wrote:
>> 
>> However, I think the traditional "find the closest ancestor"
>> behaviour and ref-log behaviour are mutually incompatible, while
>> they both return information to help address similar issues to
>> the end user when viewed at a very high level.
>> 
>> Especially, "find the closest ancestor" behaviour means when you
>> get "tag-gXXXX" as an answer, the tag proper does _not_ contain
>> the given commit (e.g. commit v1.4.2-g4839bd8 is not part of
>> v1.4.2).
>
> Correct.
>
> But that just means that we should take the _next_ one in the time-ordered 
> list, no?

I do not think so.

Extending the example (sorry for doing the same topic on two
separate threads) I just gave Jeff on "fix based on v0.99",
after finding that the fix is based on v0.99, finding another
commit that immediately followed the v0.99 commit on my master
branch does not help finding out that I very recently merged the
fix in at all.  I think we cannot get away without honestly
doing the first descendant, which is unfortunately a lot more
expensive.

^ permalink raw reply

* [PATCH 1/2] fetch: Misc output cleanup
From: Santi Béjar @ 2006-10-01  3:33 UTC (permalink / raw)
  To: git


In particular it removes duplicate information, uses short hashes (as
git-log and company) and uses .. for fast forwarding commits and ... for
not-fast-forwarding commits (shorter, easier to copy&paste). It also
reformat the output as:

1. the ones we store in our local ref (either branches or tags):

 1a) fast-forward

 * refs/heads/origin: fast forward to branch 'master' of ../git/
   old..new: 1ad7a06..bc1a580

 1b) same (only shown under -v)

 * refs/heads/next: same as branch 'origin/next' of ../git/
   commit: ce47b9f

 1c) non-fast-forward, forced

 * refs/heads/pu: forcing update to not fast forwarding branch 'pu' of ../git/
   old...new: 7c733a8...5faa935

 1d) non-fast-forward, did not update because not forced

 * refs/heads/po: not updating to not fast forwarding branch 'po' of ../git/
   old...new: 7c733a8...5faa935

 1e) creating a new local ref to store

 * refs/tags/v1.4.2-rc4: storing tag 'v1.4.2-rc4' of ../git/
   tag: 8c7a107
 * refs/heads/next: storing branch 'next' of ../git/
   commit: f8a20ae

2. the ones we do not store in our local ref (only shown under -v):

 * fetched branch 'master' of ../git
   commit: 695dffe
 * fetched tag 'v1.4.2-rc4' of ../git
   tag: 8c7a107

With the help of Junio.

Signed-off-by: Santi Béjar <sbejar@gmail.com>
---
 git-fetch.sh |   41 +++++++++++++++++++++++++----------------
 1 files changed, 25 insertions(+), 16 deletions(-)

diff --git a/git-fetch.sh b/git-fetch.sh
index f1522bd..60ae552 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -129,22 +129,25 @@ append_fetch_head () {
     then
 	headc_=$(git-rev-parse --verify "$head_^0") || exit
 	echo "$headc_	$not_for_merge_	$note_" >>"$GIT_DIR/FETCH_HEAD"
-	[ "$verbose" ] && echo >&2 "* committish: $head_"
-	[ "$verbose" ] && echo >&2 "  $note_"
     else
 	echo "$head_	not-for-merge	$note_" >>"$GIT_DIR/FETCH_HEAD"
-	[ "$verbose" ] && echo >&2 "* non-commit: $head_"
-	[ "$verbose" ] && echo >&2 "  $note_"
-    fi
-    if test "$local_name_" != ""
-    then
-	# We are storing the head locally.  Make sure that it is
-	# a fast forward (aka "reverse push").
-	fast_forward_local "$local_name_" "$head_" "$note_"
     fi
+
+    update_local_ref "$local_name_" "$head_" "$note_"
 }
 
-fast_forward_local () {
+update_local_ref () {
+    # If we are storing the head locally make sure that it is
+    # a fast forward (aka "reverse push").
+
+    label_=$(git-cat-file -t $2)
+    newshort_=$(git-rev-parse --short $2)
+    if test -z "$1" ; then
+	[ "$verbose" ] && echo >&2 "* fetched $3"
+	[ "$verbose" ] && echo >&2 "  $label_: $newshort_"
+	return 0
+    fi
+    oldshort_=$(git-rev-parse --short "$1" 2>/dev/null)
     mkdir -p "$(dirname "$GIT_DIR/$1")"
     case "$1" in
     refs/tags/*)
@@ -154,13 +157,16 @@ fast_forward_local () {
 	then
 		if now_=$(cat "$GIT_DIR/$1") && test "$now_" = "$2"
 		then
-			[ "$verbose" ] && echo >&2 "* $1: same as $3" ||:
+			[ "$verbose" ] && echo >&2 "* $1: same as $3"
+			[ "$verbose" ] && echo >&2 "  $label_: $newshort_" ||:
 		else
 			echo >&2 "* $1: updating with $3"
+			echo >&2 "  $label_: $newshort_"
 			git-update-ref -m "$rloga: updating tag" "$1" "$2"
 		fi
 	else
 		echo >&2 "* $1: storing $3"
+		echo >&2 "  $label_: $newshort_"
 		git-update-ref -m "$rloga: storing tag" "$1" "$2"
 	fi
 	;;
@@ -178,31 +184,34 @@ fast_forward_local () {
 	        if test -n "$verbose"
 		then
 			echo >&2 "* $1: same as $3"
+			echo >&2 "  $label_: $newshort_"
 		fi
 		;;
 	    *,$local)
 		echo >&2 "* $1: fast forward to $3"
-		echo >&2 "  from $local to $2"
+		echo >&2 "  old..new: $oldshort_..$newshort_"
 		git-update-ref -m "$rloga: fast-forward" "$1" "$2" "$local"
 		;;
 	    *)
 		false
 		;;
 	    esac || {
-		echo >&2 "* $1: does not fast forward to $3;"
 		case ",$force,$single_force," in
 		*,t,*)
-			echo >&2 "  forcing update."
+			echo >&2 "* $1: forcing update to not fast forwarding $3"
+			echo >&2 "  old...new: $oldshort_...$newshort_"
 			git-update-ref -m "$rloga: forced-update" "$1" "$2" "$local"
 			;;
 		*)
-			echo >&2 "  not updating."
+			echo >&2 "* $1: not updating to not fast forwarding $3"
+			echo >&2 "  old...new: $oldshort_...$newshort_"
 			exit 1
 			;;
 		esac
 	    }
 	else
 	    echo >&2 "* $1: storing $3"
+	    echo >&2 "  $label_: $newshort_"
 	    git-update-ref -m "$rloga: storing head" "$1" "$2"
 	fi
 	;;
-- 
1.4.2.1.g38049

^ 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