Git development
 help / color / mirror / Atom feed
* Re: Merge with local conflicts in new files
From: Pavel Roskin @ 2006-05-17  7:47 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Santi, git
In-Reply-To: <7vu07p35xn.fsf@assigned-by-dhcp.cox.net>

Hello, Junio!

On Tue, 2006-05-16 at 16:28 -0700, Junio C Hamano wrote:
> Santi <sbejar@gmail.com> writes:
> 
> > 2006/5/17, Junio C Hamano <junkio@cox.net>:
> >>  - You have not told git that these files matter.
> >
> > For me it is the other way, all my files matter but git can do
> > whatever it wants with the ones it controls.
> 
> You really do not mean that.
> 
> If you told git a file matters, and have local modifications to
> the file in the working tree that you have not run update-index
> yet, merge and apply should be careful not to overwrite your
> changes that is not ready while doing whatever thing they have
> to do.  And they are careful, because you have told git that
> they matter, and the way you tell git that they matter is to
> have entries for them in the index.

I'm afraid this approach, while understandable from the technical
standpoint, could prevent git from ever becoming a version control
system that "just works" without any porcelains.

I know a person who refuses to use any version control.  If he
encountered this situation, he would never try any version control
again.

After all, we are talking about files in the _working_ directory.  It's
not merely a transient appendix to the repository.  git is not the only
player here.  If a file doesn't "belong" to git, it belongs to its
"supreme commander", i.e. the user, and should be approached with utmost
care.

Merging a branch should not cause an irreparable loss of user data.  The
same applies to other commands.  Exceptions can be made for commands
that are specifically meant to clean user data, for commands with
special options (e.g. --force or --hard), and for the files explicitly
marked as transient (e.g. in .gitignore).

-- 
Regards,
Pavel Roskin

^ permalink raw reply

* Re: Git 1.3.2 on Solaris
From: Stefan Pfetzing @ 2006-05-17  8:05 UTC (permalink / raw)
  To: Git Mailing List
In-Reply-To: <Pine.LNX.4.64.0605162047380.10823@g5.osdl.org>

Hi Linus,

2006/5/17, Linus Torvalds <torvalds@osdl.org>:
>
> On Tue, 16 May 2006, Jason Riedy wrote:
> >
> > But for recommending and using git on these systems _now_...
>
> Yes. For that, I would literally suggest having people install the GNU
> tools (and/or a recent enough perl) somewhere early in the path.
>
> If you use the git wrapper, for example, you can already depend on the
> fact that it will prepend the git installation directory to the path, so
> while the GNU tools might not _normally_ be on the path, if you put them
> in the same directory as your git install, you'll automatically get them
> as long as you use the "git cmd" format (rather than the "git-cmd"
> format).

Well I guess for my pkgsrc environment this won't work.
I already (quite some time ago) tried to have gnu coreutils, findutils and
diffutils installed without the g prefix.
This broke several things on NetBSD and on Solaris.

So I'd prefer a solution where one could set one flag for the Makefile of git,
and git would check for the g prefix, create somewhere a directory with
symlinks to the "real" gnu binaries and put it into $PATH upon startup of
every git c-program or shellscript.

I suggest having these gnu "tools" dependancies removed can only be a long
term goal.

bye

dreamind

P.S.: I had to re-sent this mail, somehow gmail did put html crap into it.
--
       http://www.dreamind.de/
Oroborus and Debian GNU/Linux Developer.

^ permalink raw reply

* [PATCH/RFC] read-tree -m -u: do not overwrite or remove untracked working tree files.
From: Junio C Hamano @ 2006-05-17  8:17 UTC (permalink / raw)
  To: Santi; +Cc: git
In-Reply-To: <8aa486160605161611p4c9ddbc0v@mail.gmail.com>

When a merge results in a creation of a path that did not exist
in HEAD, and if you already have that path on the working tree,
because the index has not been told about the working tree file,
read-tree happily removes it.  The issue was brought up by Santi
Béjar on the list.

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

---
>> This is totally untested, but on top of "next" you could do
>> something like this, perhaps.
>
> Thanks, it works here.

  This is an updated version, applicable to the "master" branch.
  The earlier one only dealt with very limited cases, and I
  suspect it did not do the right thing when "--reset" is
  involved (it would probably had broken "git reset --hard").

  This is an RFC patch that I consider of somewhat dubious value.
  Not the implementation quality, but the semantic change it
  implies.  Before, we could freely work in a working tree, which
  is just a scratch area to build the index, littered with
  throw-away files, knowing they would not prevent merge between
  our HEAD and other branch from happenning even if a merge needs
  to blow them away.  With this change, it is not a case anymore.
  Your merge will fail to proceed and you have to remove those
  throw-away files yourself and retry the merge.  The extent of
  the damage can be seen by the change to t1002 test this commit
  contains.  It arguably is making things much safer by refusing to
  proceed, so this might be a desirable change.  I am still
  undecided.

435ac2bfd2ef29c584531cd4a29c1f018f9aea13
 read-tree.c                   |   43 ++++++++++++++++++++++++---
 t/t1002-read-tree-m-u-2way.sh |   66 +++++++++++++++++++++++++++--------------
 t/t3500-cherry.sh             |    1 +
 t/t4002-diff-basic.sh         |    6 ++--
 t/t6022-merge-rename.sh       |    1 +
 5 files changed, 87 insertions(+), 30 deletions(-)

435ac2bfd2ef29c584531cd4a29c1f018f9aea13
diff --git a/read-tree.c b/read-tree.c
index e16e91b..6dc95a3 100644
--- a/read-tree.c
+++ b/read-tree.c
@@ -408,7 +408,7 @@ static void verify_uptodate(struct cache
 {
 	struct stat st;
 
-	if (index_only)
+	if (index_only || reset)
 		return;
 
 	if (!lstat(ce->name, &st)) {
@@ -426,6 +426,21 @@ static void verify_uptodate(struct cache
 	die("Entry '%s' not uptodate. Cannot merge.", ce->name);
 }
 
+/*
+ * We do not want to remove or overwrite a working tree file that
+ * is not tracked.
+ */
+static void verify_absent(const char *path, const char *action)
+{
+	struct stat st;
+
+	if (index_only || reset || !update)
+		return;
+	if (!lstat(path, &st))
+		die("Untracked working tree file '%s' "
+		    "would be %s by merge.", path, action);
+}
+
 static int merged_entry(struct cache_entry *merge, struct cache_entry *old)
 {
 	merge->ce_flags |= htons(CE_UPDATE);
@@ -443,6 +458,9 @@ static int merged_entry(struct cache_ent
 			verify_uptodate(old);
 		}
 	}
+	else
+		verify_absent(merge->name, "overwritten");
+
 	merge->ce_flags &= ~htons(CE_STAGEMASK);
 	add_cache_entry(merge, ADD_CACHE_OK_TO_ADD);
 	return 1;
@@ -452,6 +470,8 @@ static int deleted_entry(struct cache_en
 {
 	if (old)
 		verify_uptodate(old);
+	else
+		verify_absent(ce->name, "removed");
 	ce->ce_mode = 0;
 	add_cache_entry(ce, ADD_CACHE_OK_TO_ADD);
 	return 1;
@@ -487,6 +507,7 @@ static int threeway_merge(struct cache_e
 	int count;
 	int head_match = 0;
 	int remote_match = 0;
+	const char *path = NULL;
 
 	int df_conflict_head = 0;
 	int df_conflict_remote = 0;
@@ -498,8 +519,11 @@ static int threeway_merge(struct cache_e
 	for (i = 1; i < head_idx; i++) {
 		if (!stages[i])
 			any_anc_missing = 1;
-		else
+		else {
+			if (!path)
+				path = stages[i]->name;
 			no_anc_exists = 0;
+		}
 	}
 
 	index = stages[0];
@@ -515,6 +539,13 @@ static int threeway_merge(struct cache_e
 		remote = NULL;
 	}
 
+	if (!path && index)
+		path = index->name;
+	if (!path && head)
+		path = head->name;
+	if (!path && remote)
+		path = remote->name;
+
 	/* First, if there's a #16 situation, note that to prevent #13
 	 * and #14. 
 	 */
@@ -575,6 +606,8 @@ static int threeway_merge(struct cache_e
 		    (remote_deleted && head && head_match)) {
 			if (index)
 				return deleted_entry(index, index);
+			else if (path)
+				verify_absent(path, "removed");
 			return 0;
 		}
 		/*
@@ -592,6 +625,8 @@ static int threeway_merge(struct cache_e
 	if (index) {
 		verify_uptodate(index);
 	}
+	else if (path)
+		verify_absent(path, "overwritten");
 
 	nontrivial_merge = 1;
 
@@ -689,7 +724,7 @@ static int oneway_merge(struct cache_ent
 			     merge_size);
 
 	if (!a)
-		return deleted_entry(old, NULL);
+		return deleted_entry(old, old);
 	if (old && same(old, a)) {
 		if (reset) {
 			struct stat st;
@@ -699,7 +734,7 @@ static int oneway_merge(struct cache_ent
 		}
 		return keep_entry(old);
 	}
-	return merged_entry(a, NULL);
+	return merged_entry(a, old);
 }
 
 static int read_cache_unmerged(void)
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
index 4d175d8..8335a63 100755
--- a/t/t1002-read-tree-m-u-2way.sh
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -39,7 +39,6 @@ test_expect_success \
      echo nitfol >nitfol &&
      echo bozbar >bozbar &&
      echo rezrov >rezrov &&
-     echo yomin >yomin &&
      git-update-index --add nitfol bozbar rezrov &&
      treeH=`git-write-tree` &&
      echo treeH $treeH &&
@@ -56,7 +55,8 @@ test_expect_success \
 
 test_expect_success \
     '1, 2, 3 - no carry forward' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
      git-read-tree -m -u $treeH $treeM &&
      git-ls-files --stage >1-3.out &&
      cmp M.out 1-3.out &&
@@ -66,11 +66,12 @@ test_expect_success \
      check_cache_at frotz clean &&
      check_cache_at nitfol clean'
 
-echo '+100644 X 0	yomin' >expected
-
 test_expect_success \
     '4 - carry forward local addition.' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
+     echo "+100644 X 0	yomin" >expected &&
+     echo yomin >yomin &&
      git-update-index --add yomin &&
      git-read-tree -m -u $treeH $treeM &&
      git-ls-files --stage >4.out || return 1
@@ -85,7 +86,9 @@ test_expect_success \
 
 test_expect_success \
     '5 - carry forward local addition.' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
+     git-read-tree -m -u $treeH &&
      echo yomin >yomin &&
      git-update-index --add yomin &&
      echo yomin yomin >yomin &&
@@ -103,7 +106,9 @@ test_expect_success \
 
 test_expect_success \
     '6 - local addition already has the same.' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
+     echo frotz >frotz &&
      git-update-index --add frotz &&
      git-read-tree -m -u $treeH $treeM &&
      git-ls-files --stage >6.out &&
@@ -117,7 +122,8 @@ test_expect_success \
 
 test_expect_success \
     '7 - local addition already has the same.' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
      echo frotz >frotz &&
      git-update-index --add frotz &&
      echo frotz frotz >frotz &&
@@ -134,14 +140,16 @@ test_expect_success \
 
 test_expect_success \
     '8 - conflicting addition.' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
      echo frotz frotz >frotz &&
      git-update-index --add frotz &&
      if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
 
 test_expect_success \
     '9 - conflicting addition.' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
      echo frotz frotz >frotz &&
      git-update-index --add frotz &&
      echo frotz >frotz &&
@@ -149,7 +157,8 @@ test_expect_success \
 
 test_expect_success \
     '10 - path removed.' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
      echo rezrov >rezrov &&
      git-update-index --add rezrov &&
      git-read-tree -m -u $treeH $treeM &&
@@ -160,7 +169,8 @@ test_expect_success \
 
 test_expect_success \
     '11 - dirty path removed.' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
      echo rezrov >rezrov &&
      git-update-index --add rezrov &&
      echo rezrov rezrov >rezrov &&
@@ -168,14 +178,16 @@ test_expect_success \
 
 test_expect_success \
     '12 - unmatching local changes being removed.' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
      echo rezrov rezrov >rezrov &&
      git-update-index --add rezrov &&
      if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
 
 test_expect_success \
     '13 - unmatching local changes being removed.' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
      echo rezrov rezrov >rezrov &&
      git-update-index --add rezrov &&
      echo rezrov >rezrov &&
@@ -188,7 +200,8 @@ EOF
 
 test_expect_success \
     '14 - unchanged in two heads.' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
      echo nitfol nitfol >nitfol &&
      git-update-index --add nitfol &&
      git-read-tree -m -u $treeH $treeM &&
@@ -207,7 +220,8 @@ test_expect_success \
 
 test_expect_success \
     '15 - unchanged in two heads.' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
      echo nitfol nitfol >nitfol &&
      git-update-index --add nitfol &&
      echo nitfol nitfol nitfol >nitfol &&
@@ -227,14 +241,16 @@ test_expect_success \
 
 test_expect_success \
     '16 - conflicting local change.' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
      echo bozbar bozbar >bozbar &&
      git-update-index --add bozbar &&
      if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
 
 test_expect_success \
     '17 - conflicting local change.' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
      echo bozbar bozbar >bozbar &&
      git-update-index --add bozbar &&
      echo bozbar bozbar bozbar >bozbar &&
@@ -242,7 +258,8 @@ test_expect_success \
 
 test_expect_success \
     '18 - local change already having a good result.' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
      echo gnusto >bozbar &&
      git-update-index --add bozbar &&
      git-read-tree -m -u $treeH $treeM &&
@@ -254,7 +271,8 @@ test_expect_success \
 
 test_expect_success \
     '19 - local change already having a good result, further modified.' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
      echo gnusto >bozbar &&
      git-update-index --add bozbar &&
      echo gnusto gnusto >bozbar &&
@@ -273,7 +291,8 @@ test_expect_success \
 
 test_expect_success \
     '20 - no local change, use new tree.' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
      echo bozbar >bozbar &&
      git-update-index --add bozbar &&
      git-read-tree -m -u $treeH $treeM &&
@@ -285,7 +304,8 @@ test_expect_success \
 
 test_expect_success \
     '21 - no local change, dirty cache.' \
-    'rm -f .git/index &&
+    'rm -f .git/index nitfol bozbar rezrov frotz &&
+     git-read-tree --reset -u $treeH &&
      echo bozbar >bozbar &&
      git-update-index --add bozbar &&
      echo gnusto gnusto >bozbar &&
@@ -294,7 +314,7 @@ test_expect_success \
 # Also make sure we did not break DF vs DF/DF case.
 test_expect_success \
     'DF vs DF/DF case setup.' \
-    'rm -f .git/index &&
+    'rm -f .git/index
      echo DF >DF &&
      git-update-index --add DF &&
      treeDF=`git-write-tree` &&
diff --git a/t/t3500-cherry.sh b/t/t3500-cherry.sh
index b141f89..e83bbee 100755
--- a/t/t3500-cherry.sh
+++ b/t/t3500-cherry.sh
@@ -30,6 +30,7 @@ test_expect_success \
      git-commit -m "Add C." &&
 
      git-checkout -f master &&
+     rm -f B C &&
 
      echo Third >> A &&
      git-update-index A &&
diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh
index 769274a..56eda63 100755
--- a/t/t4002-diff-basic.sh
+++ b/t/t4002-diff-basic.sh
@@ -191,7 +191,7 @@ test_expect_success \
     'rm -fr Z [A-Z][A-Z] &&
      git-read-tree $tree_A &&
      git-checkout-index -f -a &&
-     git-read-tree -m $tree_O || return 1
+     git-read-tree --reset $tree_O || return 1
      git-update-index --refresh >/dev/null ;# this can exit non-zero
      git-diff-files >.test-a &&
      cmp_diff_files_output .test-a .test-recursive-OA'
@@ -201,7 +201,7 @@ test_expect_success \
     'rm -fr Z [A-Z][A-Z] &&
      git-read-tree $tree_B &&
      git-checkout-index -f -a &&
-     git-read-tree -m $tree_O || return 1
+     git-read-tree --reset $tree_O || return 1
      git-update-index --refresh >/dev/null ;# this can exit non-zero
      git-diff-files >.test-a &&
      cmp_diff_files_output .test-a .test-recursive-OB'
@@ -211,7 +211,7 @@ test_expect_success \
     'rm -fr Z [A-Z][A-Z] &&
      git-read-tree $tree_B &&
      git-checkout-index -f -a &&
-     git-read-tree -m $tree_A || return 1
+     git-read-tree --reset $tree_A || return 1
      git-update-index --refresh >/dev/null ;# this can exit non-zero
      git-diff-files >.test-a &&
      cmp_diff_files_output .test-a .test-recursive-AB'
diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
index a2d24b5..5ac2564 100755
--- a/t/t6022-merge-rename.sh
+++ b/t/t6022-merge-rename.sh
@@ -111,6 +111,7 @@ test_expect_success 'pull renaming branc
 
 test_expect_success 'pull renaming branch into another renaming one' \
 '
+	rm -f B
 	git reset --hard
 	git checkout red
 	git pull . white && {
-- 
1.3.3.g8a24

^ permalink raw reply related

* Re: Merge with local conflicts in new files
From: Junio C Hamano @ 2006-05-17  8:20 UTC (permalink / raw)
  To: Pavel Roskin; +Cc: git
In-Reply-To: <1147852052.31879.50.camel@dv>

Pavel Roskin <proski@gnu.org> writes:

> I'm afraid this approach, while understandable from the technical
> standpoint, could prevent git from ever becoming a version control
> system that "just works" without any porcelains.
>
> I know a person who refuses to use any version control.  If he
> encountered this situation, he would never try any version control
> again.
>
> After all, we are talking about files in the _working_ directory.  It's
> not merely a transient appendix to the repository.  git is not the only
> player here.  If a file doesn't "belong" to git, it belongs to its
> "supreme commander", i.e. the user, and should be approached with utmost
> care.

I am biased ;-) and appreciate corrections like this.  How does
the updated patch I just sent out look?

^ permalink raw reply

* Re: Git 1.3.2 on Solaris
From: Junio C Hamano @ 2006-05-17  8:22 UTC (permalink / raw)
  To: Ryan Anderson; +Cc: git
In-Reply-To: <20060517051505.GD31164@h4x0r5.com>

Ryan Anderson <ryan@michonline.com> writes:

> The only major tool I can think of that has embedded Perl in the shell
> script is format-patch.  That could probably be redone in pure Perl if
> it would help.

Actually, that one is in the process of migrating all C.

^ permalink raw reply

* Re: [PATCH/RFC] read-tree -m -u: do not overwrite or remove untracked working tree files.
From: Jakub Narebski @ 2006-05-17  8:24 UTC (permalink / raw)
  To: git
In-Reply-To: <7v8xp1jc9h.fsf_-_@assigned-by-dhcp.cox.net>

Junio C Hamano wrote:

>   This is an RFC patch that I consider of somewhat dubious value.
>   Not the implementation quality, but the semantic change it
>   implies.  Before, we could freely work in a working tree, which
>   is just a scratch area to build the index, littered with
>   throw-away files, knowing they would not prevent merge between
>   our HEAD and other branch from happenning even if a merge needs
>   to blow them away.  With this change, it is not a case anymore.
>   Your merge will fail to proceed and you have to remove those
>   throw-away files yourself and retry the merge.  The extent of
>   the damage can be seen by the change to t1002 test this commit
>   contains.  It arguably is making things much safer by refusing to
>   proceed, so this might be a desirable change.  I am still
>   undecided.

Perhaps the behavior should be decided by the config option, e.g.
core.preserveWorkingTree; of course that leave us with the problem what
value should be the default: the one preserving backward compatibility, or
the more safe one.

And of course --force to blow away changes anyway...

-- 
Jakub Narebski
Warsaw, Poland

^ permalink raw reply

* Re: Git 1.3.2 on Solaris
From: Junio C Hamano @ 2006-05-17  8:28 UTC (permalink / raw)
  To: Stefan Pfetzing; +Cc: git
In-Reply-To: <f3d7535d0605161652n3b2ec033r874336082755e728@mail.gmail.com>

"Stefan Pfetzing" <stefan.pfetzing@gmail.com> writes:

> 1.  fix every single shellscript automatically during the build phase
> 2.  setup a dir which contains symlinks to the "right" binaries and
> put that dir into PATH.

You forgot 3.

  3.  rewrite scripts so that they would require only POSIX;
      for ones that do need GNU extended coreutils to do in
      shell, find other ways, perhaps rewriting the stuff in C.

I am not looking forward to do the g- prefix in the main
Makefile.  The approach to have symlink forest under gitexecdir
(<Pine.LNX.4.64.0605162047380.10823@g5.osdl.org> by Linus) is
more palatable, and I am not opposed to host a script to do so
under contrib/notgnu perhaps.

^ permalink raw reply

* Re: Fwd: [OT] Re: Git via a proxy server?
From: Jan-Benedict Glaw @ 2006-05-17  8:38 UTC (permalink / raw)
  To: Sam Song; +Cc: Petr Vandrovec, git
In-Reply-To: <20060517035639.40450.qmail@web32004.mail.mud.yahoo.com>

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

On Tue, 2006-05-16 20:56:39 -0700, Sam Song <samlinuxkernel@yahoo.com> wrote:
> Petr Vandrovec <petr@vmware.com> wrote:
> > Best to test this is to start 'socket 192.168.40.99
> > 80' from command line and 
> > then type these two lines above, plus one empty
> > line.  You should get back '200 
> > OK', empty line, and then you can start
> > communicating using git protocol - if 
> > you can do that...
> 
> I cannot run "socket" and "CONNECT" on Fedora Core 3.
> It simply told me that no such command. How could I 
> do this task in my case?

Well, install some package to have `socket' available? Debian calls
the packet `socket', too, so I guess Fedora may have something
similar.

MfG, JBG

-- 
Jan-Benedict Glaw       jbglaw@lug-owl.de    . +49-172-7608481             _ O _
"Eine Freie Meinung in  einem Freien Kopf    | Gegen Zensur | Gegen Krieg  _ _ O
 für einen Freien Staat voll Freier Bürger"  | im Internet! |   im Irak!   O O O
ret = do_actions((curr | FREE_SPEECH) & ~(NEW_COPYRIGHT_LAW | DRM | TCPA));

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

^ permalink raw reply

* Re: Merge with local conflicts in new files
From: Martin Langhoff @ 2006-05-17  8:50 UTC (permalink / raw)
  To: Pavel Roskin; +Cc: Junio C Hamano, Santi, git
In-Reply-To: <1147852052.31879.50.camel@dv>

On 5/17/06, Pavel Roskin <proski@gnu.org> wrote:
> If a file doesn't "belong" to git, it belongs to its
> "supreme commander", i.e. the user, and should be approached with utmost
> care.

+1 here. Unknown files are precious (to take an Arch term) until git
is told otherwise.

> special options (e.g. --force or --hard), and for the files explicitly
> marked as transient (e.g. in .gitignore).

I think that if we turn into clobbering files listed in .gitignore
users will probably be screaming bloody murder. No git op should
clobber untracked files...

Arch has this strange concept of allowing you to list 'junk' files. I
could never figure out why it would want my authorization to remove
files randomly. For all its faults, cvs does the right thing -- it
will say 'checkout/update of foo.c blocked by foo.c in directory'. And
if you force it with -C it will rename the local file to
.#originalname-local or something like that.

Even the files I think of as junk are actually useful and should not
be messed up with. Editor temp files, for instance, are often listed
in .gitignore, and if you ask me, they are junk. Except while I am
working with my editor! ;-)

Another case is .project files from IDEs like Eclipse. People list
them in .cvsignore so that they are not committed, and yet preserved.
The user probably has a lot of personal settings there.

cheers,


martin

^ permalink raw reply

* Re: Git 1.3.2 on Solaris
From: Junio C Hamano @ 2006-05-17  9:03 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0605161904260.16475@g5.osdl.org>

Linus Torvalds <torvalds@osdl.org> writes:

> [ Junio - see the "grep" issue ]
> ...
> Of course, I don't think anybody tried the new "git grep" on Solaris,...

I haven't tried the new grep on Solaris myself, as the Solaris
box I have easy access is badly maintained (unmaintained is
probably a better wording).

> ...and 
> I think the solaris "grep" lacks the "-H" flag, for example. But that 
> should be easy to fix (for example, replace the use of "--" and "-H" with 
> putting a "/dev/null" as the first filename).

You mean like this, I presume.

But I think this approach breaks -L; I do not think Solaris
supports -L, so it does not matter there, but on platforms that
knows how to do -L it does.

-- >8 --
[PATCH] builtin-grep: give /dev/null at the beginning instead of -H

---
diff --git a/builtin-grep.c b/builtin-grep.c
index 66111de..ff3c1f7 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -453,7 +453,6 @@ static int external_grep(struct grep_opt
 
 	len = nr = 0;
 	push_arg("grep");
-	push_arg("-H");
 	if (opt->fixed)
 		push_arg("-F");
 	if (opt->linenum)
@@ -503,7 +502,7 @@ static int external_grep(struct grep_opt
 		push_arg("-e");
 		push_arg(p->pattern);
 	}
-	push_arg("--");
+	push_arg("/dev/null");
 
 	hit = 0;
 	argc = nr;

^ permalink raw reply related

* Re: Git 1.3.2 on Solaris
From: Stefan Pfetzing @ 2006-05-17  9:06 UTC (permalink / raw)
  To: Git Mailing List
In-Reply-To: <7vpsidhx79.fsf@assigned-by-dhcp.cox.net>

Hi Junio,

2006/5/17, Junio C Hamano <junkio@cox.net>:
> "Stefan Pfetzing" <stefan.pfetzing@gmail.com> writes:
>
> > 1.  fix every single shellscript automatically during the build phase
> > 2.  setup a dir which contains symlinks to the "right" binaries and
> > put that dir into PATH.
>
> You forgot 3.
>
>   3.  rewrite scripts so that they would require only POSIX;
>       for ones that do need GNU extended coreutils to do in
>       shell, find other ways, perhaps rewriting the stuff in C.

Yes, thats right, but this can only be a long term goal, because I guess
this will take significantly longer. - even "tr" and "diff" behave different
on Solaris.

> I am not looking forward to do the g- prefix in the main
> Makefile.  The approach to have symlink forest under gitexecdir
> (<Pine.LNX.4.64.0605162047380.10823@g5.osdl.org> by Linus) is
> more palatable, and I am not opposed to host a script to do so
> under contrib/notgnu perhaps.

Hm, gitexecdir is also the path where git is installed, right? So if I'd
install git with pkgsrc it will be /usr/pkg/bin, right? - If so,
putting symlinks
there _will_ break pkgsrc.

bye

Stefan
-- 
       http://www.dreamind.de/
Oroborus and Debian GNU/Linux Developer.

^ permalink raw reply

* Re: Git 1.3.2 on Solaris
From: Junio C Hamano @ 2006-05-17  9:22 UTC (permalink / raw)
  To: Stefan Pfetzing; +Cc: git
In-Reply-To: <f3d7535d0605170206y76e24f25w305a688d32f4a0a1@mail.gmail.com>

"Stefan Pfetzing" <stefan.pfetzing@gmail.com> writes:

> Hm, gitexecdir is also the path where git is installed, right? So if I'd
> install git with pkgsrc it will be /usr/pkg/bin, right? - If so,
> putting symlinks
> there _will_ break pkgsrc.

If you look at our Makefile, you will see bindir does not have
to be gitexecdir.  The suggestion by Linus is that you set
bindir to /usr/local/bin or whereever your distribution's
packaging scheme wants the locally installed software to be that
is on user's PATH, and gitexecdir to /usr/local/libexec/git
(again, whereever), _and_ have:

	ln -s /usr/bin/gtr /usr/local/libexec/git/tr
	ln -s /usr/bin/gxargs /usr/local/libexec/git/xargs
        ...

Then:

	(1) git and gitk are available in /usr/local/bin;

        (2) while git and gitk runs, /usr/local/libexec/git will
            be prepended to the PATH, so when they want xargs,
            they will get gxargs;

        (3) but your users will _not_ have /usr/local/libexec/git
            on their PATH, so when they type xargs they will get
            the one that barfs on -0 option.

and train your users and user's scripts to use the officially
sanctioned way to refer to git subprograms.  From interactive
sessions, say "git foo", not "git-foo".  If your script _really_
cares about extra exec git wrapper does, use "git --exec-path"
upfront in the script to obtain correct gitexecpath, export
GIT_EXEC_PATH environment variable with that value, and prepend
it to PATH so that it can find "git-foo" executable (you would
probably need to do both, so that git-foo can find git-bar and
its friends).


	

^ permalink raw reply

* git-add + git-reset --hard = Arrrggh!
From: Shawn Pearce @ 2006-05-17  9:45 UTC (permalink / raw)
  To: git

After spending an hour writing and testing a new test case for GIT
I do the foolish:

	$ git add t/t1400-update-ref.sh
	# Hmm, maybe I should amend this into the prior commit.
	$ git format-patch -o .. next
	$ git reset --hard
	$ git update-ref HEAD~1
	# Uhhohh...
	$ ls t/t1400-update-ref.sh

All I can say is I'm very happy that update-index does a lot more
than just update the index.  I was easily able to find the deleted
test by finding the most recently modified object in my .git/objects
directory and pulling it back out with git cat-file.  :-)

Oh, and I totally agree with that discussion about GIT not clobbering
files the user is working on which the user can't easily recover.
I just wish recovery from the above stupidity didn't require going
through .git/objects looking for the newest file.  :-)

Yes, I know that git reset --hard was brutal and yes, I didn't
really need to use git-update-ref when git-reset would have also
done the job for me.  Arrgh.  Its early and I wasn't thinking.

-- 
Shawn.

^ permalink raw reply

* [RFC 0/5] Log history of a ref
From: Shawn Pearce @ 2006-05-17  9:54 UTC (permalink / raw)
  To: git

The following 5 [RFC] patches from me are all related to logging
changes made to a ref.  These patches contain the same basic idea
as the two patches I floated earlier this week.  Log files reside
in .git/logs<ref> and logging is enabled either by creating the
log file or by setting 'core.logAllRefUpdates' to true.

Summary is:

 * [RFC 1/5] Remove unnecessary local in get_ref_sha1.

	A minor code cleanup.

 * [RFC 2/5] Improve abstraction of ref lock/write.

	A major reorg of the write_ref_sha1 APIs.  This reorg allows
	all internal code to use the same logic for updating any ref,
	which makes it much easier to hook logging in.
	
 * [RFC 3/5] Convert update-ref to use ref_lock API.

 	Modify update-ref to use the reorg'd write_ref_sha1 API.

 * [RFC 4/5] Log ref updates to logs/refs/<ref>

	Add logging of refs.  This is the pretty much my latest
	logging patch but cleaned up and built on the above.

 * [RFC 5/5] Support 'master@2 hours ago' syntax

	Extend the SHA1 syntax to search the log for the SHA1 which
	was valid at the given point in time.

I still need to fix the receive-pack code to log ref changes prior
to running the update hook.  I'll probably look at that later this
week.  I also need to edit the ~20 sites which call 'git-update-ref'
to make use of the new "-m <reason>" flag.

-- 
Shawn.

^ permalink raw reply

* [PATCH] builtin-grep: workaround for non GNU grep.
From: Junio C Hamano @ 2006-05-17  9:54 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git
In-Reply-To: <7vejythvkr.fsf@assigned-by-dhcp.cox.net>

Some implementations do not know what to do with -H; define
NO_H_OPTION_IN_GREP when you build git if your grep lacks -H.

Most of the time, it can be worked around by prepending
/dev/null to the argument list, but that causes -L and -c to
slightly misbehave (they both expose /dev/null is given), so
when these options are given, do not run external grep that does
not understand -H.

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

 Junio C Hamano <junkio@cox.net> writes:

 > But I think this approach breaks -L; I do not think Solaris
 > supports -L, so it does not matter there, but on platforms that
 > knows how to do -L it does.

 So this is an updated version.  I am not proud of the handling
 of the new Makefile variable, although I like the C code that
 does not need #ifdef thanks to it.

 Makefile       |   11 +++++++++++
 builtin-grep.c |   22 +++++++++++++++++++---
 2 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/Makefile b/Makefile
index 9ba608c..c67108d 100644
--- a/Makefile
+++ b/Makefile
@@ -46,6 +46,8 @@ # Patrick Mauritz).
 #
 # Define NO_MMAP if you want to avoid mmap.
 #
+# Define NO_H_OPTION_IN_GREP if your grep does not understand -H.
+#
 # Define WITH_OWN_SUBPROCESS_PY if you want to use with python 2.3.
 #
 # Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
@@ -444,6 +446,12 @@ ifdef NO_ACCURATE_DIFF
 	ALL_CFLAGS += -DNO_ACCURATE_DIFF
 endif
 
+ifdef NO_H_OPTION_IN_GREP
+	NO_H_OPTION_IN_GREP=1
+else
+	NO_H_OPTION_IN_GREP=0
+endif
+
 # Shell quote (do not use $(call) to accomodate ancient setups);
 
 SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
@@ -526,6 +534,9 @@ git$X git.spec \
 %.o: %.S
 	$(CC) -o $*.o -c $(ALL_CFLAGS) $<
 
+builtin-grep.o: builtin-grep.c
+	$(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_H_OPTION_IN_GREP=$(NO_H_OPTION_IN_GREP) $<
+
 exec_cmd.o: exec_cmd.c
 	$(CC) -o $*.o -c $(ALL_CFLAGS) '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' $<
 
diff --git a/builtin-grep.c b/builtin-grep.c
index 66111de..36512d8 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -453,7 +453,6 @@ static int external_grep(struct grep_opt
 
 	len = nr = 0;
 	push_arg("grep");
-	push_arg("-H");
 	if (opt->fixed)
 		push_arg("-F");
 	if (opt->linenum)
@@ -503,7 +502,13 @@ static int external_grep(struct grep_opt
 		push_arg("-e");
 		push_arg(p->pattern);
 	}
-	push_arg("--");
+
+	if (NO_H_OPTION_IN_GREP)
+		push_arg("/dev/null");
+	else {
+		push_arg("-H");
+		push_arg("--");
+	}
 
 	hit = 0;
 	argc = nr;
@@ -535,8 +540,19 @@ #ifdef __unix__
 	 * Use the external "grep" command for the case where
 	 * we grep through the checked-out files. It tends to
 	 * be a lot more optimized
+	 *
+	 * Some grep implementations do not understand -H nor --
+	 * but /dev/null can be used as a substitution in most
+	 * cases.
+	 *
+	 * However -L and -c would slightly misbehave (-L would
+	 * list /dev/null as a hit, and -c would report 0 hits
+	 * from /dev/null); so do not use the external one on
+	 * such platforms.
 	 */
-	if (!cached) {
+	if (!cached &&
+	    (!NO_H_OPTION_IN_GREP ||
+	     (!opt->count && !opt->unmatch_name_only))) {
 		hit = external_grep(opt, paths, cached);
 		if (hit >= 0)
 			return hit;
-- 
1.3.3.g8a24

^ permalink raw reply related

* [RFC 1/5] Remove unnecessary local in get_ref_sha1.
From: Shawn Pearce @ 2006-05-17  9:54 UTC (permalink / raw)
  To: git

Remove unnecessary local in get_ref_sha1.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>

---

 refs.c |    5 +----
 1 files changed, 1 insertions(+), 4 deletions(-)

339dddea51656af46f6c8a2fba7f50d96ae2a434
diff --git a/refs.c b/refs.c
index 6c91ae6..0f3491f 100644
--- a/refs.c
+++ b/refs.c
@@ -220,12 +220,9 @@ static char *ref_lock_file_name(const ch
 
 int get_ref_sha1(const char *ref, unsigned char *sha1)
 {
-	const char *filename;
-
 	if (check_ref_format(ref))
 		return -1;
-	filename = git_path("refs/%s", ref);
-	return read_ref(filename, sha1);
+	return read_ref(git_path("refs/%s", ref), sha1);
 }
 
 static int lock_ref_file(const char *filename, const char *lock_filename,
-- 
1.3.2.g7278

^ permalink raw reply related

* [RFC 2/5] Improve abstraction of ref lock/write.
From: Shawn Pearce @ 2006-05-17  9:55 UTC (permalink / raw)
  To: git

Created 'struct ref_lock' to contain the data necessary to perform
a ref update.  This change improves writing a ref as the file names
are generated only once (rather than twice) and supports following
symrefs (up to the maximum depth).  Further the ref_lock structure
provides room to extend the update API with ref logging.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>

---

 fetch.c |   32 +++++----
 refs.c  |  236 ++++++++++++++++++++++++++++++---------------------------------
 refs.h  |   24 ++++--
 3 files changed, 144 insertions(+), 148 deletions(-)

d38945e0c759f8739da4c24485578a8193d54c59
diff --git a/fetch.c b/fetch.c
index 73bde07..8bdaacb 100644
--- a/fetch.c
+++ b/fetch.c
@@ -204,14 +204,14 @@ static int mark_complete(const char *pat
 
 int pull(char *target)
 {
+	struct ref_lock *lock;
 	unsigned char sha1[20];
-	int fd = -1;
 
 	save_commit_buffer = 0;
 	track_object_refs = 0;
-	if (write_ref && current_ref) {
-		fd = lock_ref_sha1(write_ref, current_ref);
-		if (fd < 0)
+	if (write_ref) {
+		lock = lock_ref_sha1(write_ref, current_ref, 1);
+		if (!lock)
 			return -1;
 	}
 
@@ -219,20 +219,22 @@ int pull(char *target)
 		for_each_ref(mark_complete);
 	}
 
-	if (interpret_target(target, sha1))
-		return error("Could not interpret %s as something to pull",
-			     target);
-	if (process(lookup_unknown_object(sha1)))
+	if (interpret_target(target, sha1)) {
+		error("Could not interpret %s as something to pull", target);
+		unlock_ref(lock);
+		return -1;
+	}
+	if (process(lookup_unknown_object(sha1))) {
+		unlock_ref(lock);
 		return -1;
-	if (loop())
+	}
+	if (loop()) {
+		unlock_ref(lock);
 		return -1;
-	
+	}
+
 	if (write_ref) {
-		if (current_ref) {
-			write_ref_sha1(write_ref, fd, sha1);
-		} else {
-			write_ref_sha1_unlocked(write_ref, sha1);
-		}
+		return write_ref_sha1(lock, sha1, "git fetch");
 	}
 	return 0;
 }
diff --git a/refs.c b/refs.c
index 0f3491f..91c8c44 100644
--- a/refs.c
+++ b/refs.c
@@ -198,26 +198,6 @@ int for_each_remote_ref(int (*fn)(const 
 	return do_for_each_ref("refs/remotes", fn, 13);
 }
 
-static char *ref_file_name(const char *ref)
-{
-	char *base = get_refs_directory();
-	int baselen = strlen(base);
-	int reflen = strlen(ref);
-	char *ret = xmalloc(baselen + 2 + reflen);
-	sprintf(ret, "%s/%s", base, ref);
-	return ret;
-}
-
-static char *ref_lock_file_name(const char *ref)
-{
-	char *base = get_refs_directory();
-	int baselen = strlen(base);
-	int reflen = strlen(ref);
-	char *ret = xmalloc(baselen + 7 + reflen);
-	sprintf(ret, "%s/%s.lock", base, ref);
-	return ret;
-}
-
 int get_ref_sha1(const char *ref, unsigned char *sha1)
 {
 	if (check_ref_format(ref))
@@ -225,94 +205,6 @@ int get_ref_sha1(const char *ref, unsign
 	return read_ref(git_path("refs/%s", ref), sha1);
 }
 
-static int lock_ref_file(const char *filename, const char *lock_filename,
-			 const unsigned char *old_sha1)
-{
-	int fd = open(lock_filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
-	unsigned char current_sha1[20];
-	int retval;
-	if (fd < 0) {
-		return error("Couldn't open lock file for %s: %s",
-			     filename, strerror(errno));
-	}
-	retval = read_ref(filename, current_sha1);
-	if (old_sha1) {
-		if (retval) {
-			close(fd);
-			unlink(lock_filename);
-			return error("Could not read the current value of %s",
-				     filename);
-		}
-		if (memcmp(current_sha1, old_sha1, 20)) {
-			close(fd);
-			unlink(lock_filename);
-			error("The current value of %s is %s",
-			      filename, sha1_to_hex(current_sha1));
-			return error("Expected %s",
-				     sha1_to_hex(old_sha1));
-		}
-	} else {
-		if (!retval) {
-			close(fd);
-			unlink(lock_filename);
-			return error("Unexpectedly found a value of %s for %s",
-				     sha1_to_hex(current_sha1), filename);
-		}
-	}
-	return fd;
-}
-
-int lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
-{
-	char *filename;
-	char *lock_filename;
-	int retval;
-	if (check_ref_format(ref))
-		return -1;
-	filename = ref_file_name(ref);
-	lock_filename = ref_lock_file_name(ref);
-	retval = lock_ref_file(filename, lock_filename, old_sha1);
-	free(filename);
-	free(lock_filename);
-	return retval;
-}
-
-static int write_ref_file(const char *filename,
-			  const char *lock_filename, int fd,
-			  const unsigned char *sha1)
-{
-	char *hex = sha1_to_hex(sha1);
-	char term = '\n';
-	if (write(fd, hex, 40) < 40 ||
-	    write(fd, &term, 1) < 1) {
-		error("Couldn't write %s", filename);
-		close(fd);
-		return -1;
-	}
-	close(fd);
-	rename(lock_filename, filename);
-	return 0;
-}
-
-int write_ref_sha1(const char *ref, int fd, const unsigned char *sha1)
-{
-	char *filename;
-	char *lock_filename;
-	int retval;
-	if (fd < 0)
-		return -1;
-	if (check_ref_format(ref))
-		return -1;
-	filename = ref_file_name(ref);
-	lock_filename = ref_lock_file_name(ref);
-	if (safe_create_leading_directories(filename))
-		die("unable to create leading directory for %s", filename);
-	retval = write_ref_file(filename, lock_filename, fd, sha1);
-	free(filename);
-	free(lock_filename);
-	return retval;
-}
-
 /*
  * Make sure "ref" is something reasonable to have under ".git/refs/";
  * We do not like it if:
@@ -365,25 +257,119 @@ int check_ref_format(const char *ref)
 	}
 }
 
-int write_ref_sha1_unlocked(const char *ref, const unsigned char *sha1)
+static struct ref_lock* verify_lock(struct ref_lock *lock,
+	const unsigned char *old_sha1, int mustexist)
+{
+	char buf[40];
+	int nr, fd = open(lock->ref_file, O_RDONLY);
+	if (fd < 0 && (mustexist || errno != ENOENT)) {
+		error("Can't verify ref %s", lock->ref_file);
+		unlock_ref(lock);
+		return NULL;
+	}
+	nr = read(fd, buf, 40);
+	close(fd);
+	if (nr != 40 || get_sha1_hex(buf, lock->old_sha1) < 0) {
+		error("Can't verify ref %s", lock->ref_file);
+		unlock_ref(lock);
+		return NULL;
+	}
+	if (memcmp(lock->old_sha1, old_sha1, 20)) {
+		error("Ref %s is at %s but expected %s", lock->ref_file,
+			sha1_to_hex(lock->old_sha1), sha1_to_hex(old_sha1));
+		unlock_ref(lock);
+		return NULL;
+	}
+	return lock;
+}
+
+static struct ref_lock* lock_ref_sha1_basic(const char *path,
+	int plen,
+	const unsigned char *old_sha1, int mustexist)
+{
+	struct ref_lock *lock;
+
+	lock = xcalloc(1, sizeof(struct ref_lock));
+	lock->lock_fd = -1;
+
+	plen = strlen(path) - plen;
+	path = resolve_ref(path, lock->old_sha1, mustexist);
+	if (!path) {
+		error("Can't read ref %s", path);
+		unlock_ref(lock);
+		return NULL;
+	}
+
+	lock->ref_file = strdup(path);
+	lock->lock_file = strdup(mkpath("%s.lock", lock->ref_file));
+
+	if (safe_create_leading_directories(lock->lock_file))
+		die("unable to create directory for %s", lock->lock_file);
+	lock->lock_fd = open(lock->lock_file,
+		O_WRONLY | O_CREAT | O_EXCL, 0666);
+	if (lock->lock_fd < 0) {
+		error("Couldn't open lock file %s: %s",
+			lock->lock_file, strerror(errno));
+		unlock_ref(lock);
+		return NULL;
+	}
+
+	return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
+}
+
+struct ref_lock* lock_ref_sha1(const char *ref,
+	const unsigned char *old_sha1, int mustexist)
 {
-	char *filename;
-	char *lock_filename;
-	int fd;
-	int retval;
 	if (check_ref_format(ref))
+		return NULL;
+	return lock_ref_sha1_basic(git_path("refs/%s", ref),
+		strlen(ref), old_sha1, mustexist);
+}
+
+struct ref_lock* lock_any_ref_for_update(const char *ref,
+	const unsigned char *old_sha1, int mustexist)
+{
+	return lock_ref_sha1_basic(git_path("%s", ref),
+		strlen(ref), old_sha1, mustexist);
+}
+
+void unlock_ref (struct ref_lock *lock)
+{
+	if (lock->lock_fd >= 0) {
+		close(lock->lock_fd);
+		unlink(lock->lock_file);
+	}
+	if (lock->ref_file)
+		free(lock->ref_file);
+	if (lock->lock_file)
+		free(lock->lock_file);
+	free(lock);
+}
+
+int write_ref_sha1(struct ref_lock *lock,
+	const unsigned char *sha1, const char *logmsg)
+{
+	static char term = '\n';
+
+	if (!lock)
 		return -1;
-	filename = ref_file_name(ref);
-	lock_filename = ref_lock_file_name(ref);
-	if (safe_create_leading_directories(filename))
-		die("unable to create leading directory for %s", filename);
-	fd = open(lock_filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
-	if (fd < 0) {
-		error("Writing %s", lock_filename);
-		perror("Open");
+	if (!memcmp(lock->old_sha1, sha1, 20)) {
+		unlock_ref(lock);
+		return 0;
 	}
-	retval = write_ref_file(filename, lock_filename, fd, sha1);
-	free(filename);
-	free(lock_filename);
-	return retval;
+	if (write(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
+	    write(lock->lock_fd, &term, 1) != 1
+		|| close(lock->lock_fd) < 0) {
+		error("Couldn't write %s", lock->lock_file);
+		unlock_ref(lock);
+		return -1;
+	}
+	if (rename(lock->lock_file, lock->ref_file) < 0) {
+		error("Couldn't set %s", lock->ref_file);
+		unlock_ref(lock);
+		return -1;
+	}
+	lock->lock_fd = -1;
+	unlock_ref(lock);
+	return 0;
 }
diff --git a/refs.h b/refs.h
index fa816c1..b7e9df2 100644
--- a/refs.h
+++ b/refs.h
@@ -1,6 +1,13 @@
 #ifndef REFS_H
 #define REFS_H
 
+struct ref_lock {
+	char *ref_file;
+	char *lock_file;
+	unsigned char old_sha1[20];
+	int lock_fd;
+};
+
 /*
  * Calls the specified function for each ref file until it returns nonzero,
  * and returns the value
@@ -14,16 +21,17 @@ extern int for_each_remote_ref(int (*fn)
 /** Reads the refs file specified into sha1 **/
 extern int get_ref_sha1(const char *ref, unsigned char *sha1);
 
-/** Locks ref and returns the fd to give to write_ref_sha1() if the ref
- * has the given value currently; otherwise, returns -1.
- **/
-extern int lock_ref_sha1(const char *ref, const unsigned char *old_sha1);
+/** Locks a "refs/" ref returning the lock on success and NULL on failure. **/
+extern struct ref_lock* lock_ref_sha1(const char *ref, const unsigned char *old_sha1, int mustexist);
+
+/** Locks any ref (for 'HEAD' type refs). */
+extern struct ref_lock* lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int mustexist);
 
-/** Writes sha1 into the refs file specified, locked with the given fd. **/
-extern int write_ref_sha1(const char *ref, int fd, const unsigned char *sha1);
+/** Release any lock taken but not written. **/
+extern void unlock_ref (struct ref_lock *lock);
 
-/** Writes sha1 into the refs file specified. **/
-extern int write_ref_sha1_unlocked(const char *ref, const unsigned char *sha1);
+/** Writes sha1 into the ref specified by the lock. **/
+extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
 
 /** Returns 0 if target has the right format for a ref. **/
 extern int check_ref_format(const char *target);
-- 
1.3.2.g7278

^ permalink raw reply related

* [RFC 3/5] Convert update-ref to use ref_lock API.
From: Shawn Pearce @ 2006-05-17  9:55 UTC (permalink / raw)
  To: git

This conversion also adds the '-m' switch to update-ref allowing
the caller to record why the ref is changing.  At present this is
merely copied down into the ref_lock API.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>

---

 Documentation/git-update-ref.txt |    2 -
 update-ref.c                     |  103 ++++++++++++++------------------------
 2 files changed, 38 insertions(+), 67 deletions(-)

4f017fce63b3cb56065731967a9684cb36ddd12e
diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt
index 475237f..f0e710a 100644
--- a/Documentation/git-update-ref.txt
+++ b/Documentation/git-update-ref.txt
@@ -7,7 +7,7 @@ git-update-ref - update the object name 
 
 SYNOPSIS
 --------
-'git-update-ref' <ref> <newvalue> [<oldvalue>]
+'git-update-ref' <ref> <newvalue> [<oldvalue>] [-m <reason>]
 
 DESCRIPTION
 -----------
diff --git a/update-ref.c b/update-ref.c
index fd48742..a1e6bb9 100644
--- a/update-ref.c
+++ b/update-ref.c
@@ -1,85 +1,56 @@
 #include "cache.h"
 #include "refs.h"
 
-static const char git_update_ref_usage[] = "git-update-ref <refname> <value> [<oldval>]";
-
-static int re_verify(const char *path, unsigned char *oldsha1, unsigned char *currsha1)
-{
-	char buf[40];
-	int fd = open(path, O_RDONLY), nr;
-	if (fd < 0)
-		return -1;
-	nr = read(fd, buf, 40);
-	close(fd);
-	if (nr != 40 || get_sha1_hex(buf, currsha1) < 0)
-		return -1;
-	return memcmp(oldsha1, currsha1, 20) ? -1 : 0;
-}
+static const char git_update_ref_usage[] =
+"git-update-ref <refname> <value> [<oldval>] [-m <reason>]";
 
 int main(int argc, char **argv)
 {
-	char *hex;
-	const char *refname, *value, *oldval, *path;
-	char *lockpath;
-	unsigned char sha1[20], oldsha1[20], currsha1[20];
-	int fd, written;
+	const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
+	struct ref_lock *lock;
+	unsigned char sha1[20], oldsha1[20];
+	int i;
 
 	setup_git_directory();
 	git_config(git_default_config);
-	if (argc < 3 || argc > 4)
+
+	for (i = 1; i < argc; i++) {
+		if (!strcmp("-m", argv[i])) {
+			if (i+1 >= argc)
+				usage(git_update_ref_usage);
+			msg = argv[++i];
+			if (!*msg)
+				die("Refusing to perform update with empty message.");
+			if (strchr(msg, '\n'))
+				die("Refusing to perform update with \\n in message.");
+			continue;
+		}
+		if (!refname) {
+			refname = argv[i];
+			continue;
+		}
+		if (!value) {
+			value = argv[i];
+			continue;
+		}
+		if (!oldval) {
+			oldval = argv[i];
+			continue;
+		}
+	}
+	if (!refname || !value)
 		usage(git_update_ref_usage);
 
-	refname = argv[1];
-	value = argv[2];
-	oldval = argv[3];
 	if (get_sha1(value, sha1))
 		die("%s: not a valid SHA1", value);
 	memset(oldsha1, 0, 20);
 	if (oldval && get_sha1(oldval, oldsha1))
 		die("%s: not a valid old SHA1", oldval);
 
-	path = resolve_ref(git_path("%s", refname), currsha1, !!oldval);
-	if (!path)
-		die("No such ref: %s", refname);
-
-	if (oldval) {
-		if (memcmp(currsha1, oldsha1, 20))
-			die("Ref %s is at %s but expected %s", refname, sha1_to_hex(currsha1), sha1_to_hex(oldsha1));
-		/* Nothing to do? */
-		if (!memcmp(oldsha1, sha1, 20))
-			exit(0);
-	}
-	path = strdup(path);
-	lockpath = mkpath("%s.lock", path);
-	if (safe_create_leading_directories(lockpath) < 0)
-		die("Unable to create all of %s", lockpath);
-
-	fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
-	if (fd < 0)
-		die("Unable to create %s", lockpath);
-	hex = sha1_to_hex(sha1);
-	hex[40] = '\n';
-	written = write(fd, hex, 41);
-	close(fd);
-	if (written != 41) {
-		unlink(lockpath);
-		die("Unable to write to %s", lockpath);
-	}
-
-	/*
-	 * Re-read the ref after getting the lock to verify
-	 */
-	if (oldval && re_verify(path, oldsha1, currsha1) < 0) {
-		unlink(lockpath);
-		die("Ref lock failed");
-	}
-
-	/*
-	 * Finally, replace the old ref with the new one
-	 */
-	if (rename(lockpath, path) < 0) {
-		unlink(lockpath);
-		die("Unable to create %s", path);
-	}
+	lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, 0);
+	if (!lock)
+		return 1;
+	if (write_ref_sha1(lock, sha1, msg) < 0)
+		return 1;
 	return 0;
 }
-- 
1.3.2.g7278

^ permalink raw reply related

* [RFC 4/5] Log ref updates to logs/refs/<ref>
From: Shawn Pearce @ 2006-05-17  9:55 UTC (permalink / raw)
  To: git

If config parameter core.logAllRefUpdates is true or the log
file already exists then append a line to ".git/logs/refs/<ref>"
whenever git-update-ref <ref> is executed.  Each log line contains
the following information:

  oldsha1 <SP> newsha1 <SP> committer <LF>

where committer is the current user, date, time and timezone in
the standard GIT ident format.  If the caller is unable to append
to the log file then git-update-ref will fail without updating <ref>.

An optional message may be included in the log line with the -m flag.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>

---

 Documentation/config.txt         |    8 +++
 Documentation/git-update-ref.txt |   26 +++++++++
 cache.h                          |    1 
 config.c                         |    5 ++
 environment.c                    |    1 
 refs.c                           |   56 +++++++++++++++++++
 refs.h                           |    1 
 t/t1400-update-ref.sh            |  112 ++++++++++++++++++++++++++++++++++++++
 8 files changed, 210 insertions(+), 0 deletions(-)
 create mode 100644 t/t1400-update-ref.sh

1b91f255386d1120bdcb434603993156c9fdde71
diff --git a/Documentation/config.txt b/Documentation/config.txt
index d1a4bec..e178ee2 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -70,6 +70,14 @@ core.preferSymlinkRefs::
 	This is sometimes needed to work with old scripts that
 	expect HEAD to be a symbolic link.
 
+core.logAllRefUpdates::
+	If true, `git-update-ref` will append a line to
+	"$GIT_DIR/logs/<ref>" listing the new SHA1 and the date/time
+	of the update.	If the file does not exist it will be
+	created automatically.	This information can be used to
+	determine what commit was the tip of a branch "2 days ago".
+	This value is false by default (no logging).
+
 core.repositoryFormatVersion::
 	Internal variable identifying the repository format and layout
 	version.
diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt
index f0e710a..dfbd886 100644
--- a/Documentation/git-update-ref.txt
+++ b/Documentation/git-update-ref.txt
@@ -49,6 +49,32 @@ for reading but not for writing (so we'l
 ref symlink to some other tree, if you have copied a whole
 archive by creating a symlink tree).
 
+Logging Updates
+---------------
+If config parameter "core.logAllRefUpdates" is true or the file
+"$GIT_DIR/logs/<ref>" exists then `git-update-ref` will append
+a line to the log file "$GIT_DIR/logs/<ref>" (dereferencing all
+symbolic refs before creating the log name) describing the change
+in ref value.  Log lines are formatted as:
+
+    . oldsha1 SP newsha1 SP committer LF
++
+Where "oldsha1" is the 40 character hexadecimal value previously
+stored in <ref>, "newsha1" is the 40 character hexadecimal value of
+<newvalue> and "committer" is the committer's name, email address
+and date in the standard GIT committer ident format.
+
+Optionally with -m:
+
+    . oldsha1 SP newsha1 SP committer TAB message LF
++
+Where all fields are as described above and "message" is the
+value supplied to the -m option.
+
+An update will fail (without changing <ref>) if the current user is
+unable to create a new log file, append to the existing log file
+or does not have committer information available.
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>.
diff --git a/cache.h b/cache.h
index b1300cd..82adbba 100644
--- a/cache.h
+++ b/cache.h
@@ -171,6 +171,7 @@ extern void rollback_index_file(struct c
 extern int trust_executable_bit;
 extern int assume_unchanged;
 extern int prefer_symlink_refs;
+extern int log_all_ref_updates;
 extern int warn_ambiguous_refs;
 extern int diff_rename_limit_default;
 extern int shared_repository;
diff --git a/config.c b/config.c
index 0248c6d..2ae6153 100644
--- a/config.c
+++ b/config.c
@@ -269,6 +269,11 @@ int git_default_config(const char *var, 
 		return 0;
 	}
 
+	if (!strcmp(var, "core.logallrefupdates")) {
+		log_all_ref_updates = git_config_bool(var, value);
+		return 0;
+	}
+
 	if (!strcmp(var, "core.warnambiguousrefs")) {
 		warn_ambiguous_refs = git_config_bool(var, value);
 		return 0;
diff --git a/environment.c b/environment.c
index 444c99e..2e79eab 100644
--- a/environment.c
+++ b/environment.c
@@ -14,6 +14,7 @@ char git_default_name[MAX_GITNAME];
 int trust_executable_bit = 1;
 int assume_unchanged = 0;
 int prefer_symlink_refs = 0;
+int log_all_ref_updates = 0;
 int warn_ambiguous_refs = 1;
 int repository_format_version = 0;
 char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
diff --git a/refs.c b/refs.c
index 91c8c44..4be75a5 100644
--- a/refs.c
+++ b/refs.c
@@ -302,6 +302,7 @@ static struct ref_lock* lock_ref_sha1_ba
 
 	lock->ref_file = strdup(path);
 	lock->lock_file = strdup(mkpath("%s.lock", lock->ref_file));
+	lock->log_file = strdup(git_path("logs/%s", lock->ref_file + plen));
 
 	if (safe_create_leading_directories(lock->lock_file))
 		die("unable to create directory for %s", lock->lock_file);
@@ -343,9 +344,60 @@ void unlock_ref (struct ref_lock *lock)
 		free(lock->ref_file);
 	if (lock->lock_file)
 		free(lock->lock_file);
+	if (lock->log_file)
+		free(lock->log_file);
 	free(lock);
 }
 
+static int log_ref_write(struct ref_lock *lock,
+	const unsigned char *sha1, const char *msg)
+{
+	int logfd, written, oflags = O_APPEND | O_WRONLY;
+	unsigned maxlen, len;
+	char *logrec;
+	const char *comitter;
+
+	if (log_all_ref_updates) {
+		if (safe_create_leading_directories(lock->log_file) < 0)
+			return error("unable to create directory for %s",
+				lock->log_file);
+		oflags |= O_CREAT;
+	}
+
+	logfd = open(lock->log_file, oflags, 0666);
+	if (logfd < 0) {
+		if (!log_all_ref_updates && errno == ENOENT)
+			return 0;
+		return error("Unable to append to %s: %s",
+			lock->log_file, strerror(errno));
+	}
+
+	setup_ident();
+	comitter = git_committer_info(1);
+	if (msg) {
+		maxlen = strlen(comitter) + strlen(msg) + 2*40 + 5;
+		logrec = xmalloc(maxlen);
+		len = snprintf(logrec, maxlen, "%s %s %s\t%s\n",
+			sha1_to_hex(lock->old_sha1),
+			sha1_to_hex(sha1),
+			comitter,
+			msg);
+	} else {
+		maxlen = strlen(comitter) + 2*40 + 4;
+		logrec = xmalloc(maxlen);
+		len = snprintf(logrec, maxlen, "%s %s %s\n",
+			sha1_to_hex(lock->old_sha1),
+			sha1_to_hex(sha1),
+			comitter);
+	}
+	written = len <= maxlen ? write(logfd, logrec, len) : -1;
+	free(logrec);
+	close(logfd);
+	if (written != len)
+		return error("Unable to append to %s", lock->log_file);
+	return 0;
+}
+
 int write_ref_sha1(struct ref_lock *lock,
 	const unsigned char *sha1, const char *logmsg)
 {
@@ -364,6 +416,10 @@ int write_ref_sha1(struct ref_lock *lock
 		unlock_ref(lock);
 		return -1;
 	}
+	if (log_ref_write(lock, sha1, logmsg) < 0) {
+		unlock_ref(lock);
+		return -1;
+	}
 	if (rename(lock->lock_file, lock->ref_file) < 0) {
 		error("Couldn't set %s", lock->ref_file);
 		unlock_ref(lock);
diff --git a/refs.h b/refs.h
index b7e9df2..43831e9 100644
--- a/refs.h
+++ b/refs.h
@@ -4,6 +4,7 @@ #define REFS_H
 struct ref_lock {
 	char *ref_file;
 	char *lock_file;
+	char *log_file;
 	unsigned char old_sha1[20];
 	int lock_fd;
 };
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
new file mode 100644
index 0000000..f338c53
--- /dev/null
+++ b/t/t1400-update-ref.sh
@@ -0,0 +1,112 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Shawn Pearce
+#
+
+test_description='Test git-update-ref and basic ref logging'
+. ./test-lib.sh
+
+Z=0000000000000000000000000000000000000000
+A=1111111111111111111111111111111111111111
+B=2222222222222222222222222222222222222222
+m=refs/heads/master
+
+test_expect_success \
+	"create $m" \
+	'git-update-ref $m $A &&
+	 test $A = $(cat .git/$m)'
+test_expect_success \
+	"create $m" \
+	'git-update-ref $m $B $A &&
+	 test $B = $(cat .git/$m)'
+rm -f .git/$m
+
+test_expect_success \
+	"create $m (by HEAD)" \
+	'git-update-ref HEAD $A &&
+	 test $A = $(cat .git/$m)'
+test_expect_success \
+	"create $m (by HEAD)" \
+	'git-update-ref HEAD $B $A &&
+	 test $B = $(cat .git/$m)'
+rm -f .git/$m
+
+test_expect_failure \
+	'(not) create HEAD with old sha1' \
+	'git-update-ref HEAD $A $B'
+test_expect_failure \
+	"(not) prior created .git/$m" \
+	'test -f .git/$m'
+rm -f .git/$m
+
+test_expect_success \
+	"create HEAD" \
+	'git-update-ref HEAD $A'
+test_expect_failure \
+	'(not) change HEAD with wrong SHA1' \
+	'git-update-ref HEAD $B $Z'
+test_expect_failure \
+	"(not) changed .git/$m" \
+	'test $B = $(cat .git/$m)'
+rm -f .git/$m
+
+mkdir -p .git/logs/refs/heads
+touch .git/logs/refs/heads/master
+test_expect_success \
+	"create $m (logged by touch)" \
+	'GIT_COMMITTER_DATE="2005-05-26 23:30" \
+	 git-update-ref HEAD $A -m "Initial Creation" &&
+	 test $A = $(cat .git/$m)'
+test_expect_success \
+	"update $m (logged by touch)" \
+	'GIT_COMMITTER_DATE="2005-05-26 23:31" \
+	 git-update-ref HEAD $B $A -m "Switch" &&
+	 test $B = $(cat .git/$m)'
+test_expect_success \
+	"set $m (logged by touch)" \
+	'GIT_COMMITTER_DATE="2005-05-26 23:41" \
+	 git-update-ref HEAD $A &&
+	 test $A = $(cat .git/$m)'
+
+cat >expect <<EOF
+$Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	Initial Creation
+$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000	Switch
+$B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000
+EOF
+test_expect_success \
+	"verifying $m's log" \
+	'diff expect .git/logs/$m'
+rm -rf .git/$m .git/logs expect
+
+test_expect_success \
+	'enable core.logAllRefUpdates' \
+	'git-repo-config core.logAllRefUpdates true &&
+	 test true = $(git-repo-config --bool --get core.logAllRefUpdates)'
+
+test_expect_success \
+	"create $m (logged by config)" \
+	'GIT_COMMITTER_DATE="2005-05-26 23:32" \
+	 git-update-ref HEAD $A -m "Initial Creation" &&
+	 test $A = $(cat .git/$m)'
+test_expect_success \
+	"update $m (logged by config)" \
+	'GIT_COMMITTER_DATE="2005-05-26 23:33" \
+	 git-update-ref HEAD $B $A -m "Switch" &&
+	 test $B = $(cat .git/$m)'
+test_expect_success \
+	"set $m (logged by config)" \
+	'GIT_COMMITTER_DATE="2005-05-26 23:43" \
+	 git-update-ref HEAD $A &&
+	 test $A = $(cat .git/$m)'
+
+cat >expect <<EOF
+$Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 +0000	Initial Creation
+$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 +0000	Switch
+$B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000
+EOF
+test_expect_success \
+	"verifying $m's log" \
+	'diff expect .git/logs/$m'
+rm -f .git/$m .git/logs/$m expect
+
+test_done
-- 
1.3.2.g7278

^ permalink raw reply related

* [RFC 5/5] Support 'master@2 hours ago' syntax
From: Shawn Pearce @ 2006-05-17  9:56 UTC (permalink / raw)
  To: git

Extended sha1 expressions may now include date specifications
which indicate a point in time within the local repository's
history.  If the ref indicated to the left of '@' has a log in
$GIT_DIR/logs/<ref> then the value of the ref at the time indicated
by the specification is obtained from the ref's log.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>

---

 Documentation/git-rev-parse.txt |    6 ++++
 refs.c                          |   53 +++++++++++++++++++++++++++++++++++
 refs.h                          |    3 ++
 sha1_name.c                     |   59 ++++++++++++++++++++++++++++-----------
 4 files changed, 104 insertions(+), 17 deletions(-)

efffa32e4896acd7978767b6a856fc7421060040
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index ab896fc..df308c3 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -124,6 +124,12 @@ syntax.
   happen to have both heads/master and tags/master, you can
   explicitly say 'heads/master' to tell git which one you mean.
 
+* A suffix '@' followed by a date specification such as 'yesterday'
+  (24 hours ago) or '1 month 2 weeks 3 days 1 hour 1 second ago'
+  to specify the value of the ref at a prior point in time.
+  This suffix may only be used immediately following a ref name
+  and the ref must have an existing log ($GIT_DIR/logs/<ref>).
+
 * A suffix '{caret}' to a revision parameter means the first parent of
   that commit object.  '{caret}<n>' means the <n>th parent (i.e.
   'rev{caret}'
diff --git a/refs.c b/refs.c
index 4be75a5..4c99e37 100644
--- a/refs.c
+++ b/refs.c
@@ -429,3 +429,56 @@ int write_ref_sha1(struct ref_lock *lock
 	unlock_ref(lock);
 	return 0;
 }
+
+int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1)
+{
+	const char *logfile, *logdata, *logend, *rec, *c;
+	char *tz_c;
+	int logfd, tz;
+	struct stat st;
+	unsigned long date;
+
+	logfile = git_path("logs/%s", ref);
+	logfd = open(logfile, O_RDONLY, 0);
+	if (logfd < 0)
+		die("Unable to read log %s: %s", logfile, strerror(errno));
+	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);
+	close(logfd);
+
+	rec = logend = logdata + st.st_size;
+	while (logdata < rec) {
+		if (logdata < rec && *(rec-1) == '\n')
+			rec--;
+		while (logdata < rec && *(rec-1) != '\n')
+			rec--;
+		c = rec;
+		while (c < logend && *c != '>' && *c != '\n')
+			c++;
+		if (c == logend || *c == '\n')
+			die("Log %s is corrupt.", logfile);
+		date = strtoul(c, NULL, 10);
+		if (date <= at_time) {
+			if (get_sha1_hex(rec + 41, sha1))
+				die("Log %s is corrupt.", logfile);
+			munmap((void*)logdata, st.st_size);
+			return 0;
+		}
+	}
+
+	c = logdata;
+	while (c < logend && *c != '>' && *c != '\n')
+		c++;
+	if (c == logend || *c == '\n')
+		die("Log %s is corrupt.", logfile);
+	date = strtoul(c, &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));
+	return 0;
+}
diff --git a/refs.h b/refs.h
index 43831e9..2c854de 100644
--- a/refs.h
+++ b/refs.h
@@ -34,6 +34,9 @@ extern void unlock_ref (struct ref_lock 
 /** Writes sha1 into the ref specified by the lock. **/
 extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
 
+/** Reads log for the value of ref during at_time. **/
+extern int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1);
+
 /** Returns 0 if target has the right format for a ref. **/
 extern int check_ref_format(const char *target);
 
diff --git a/sha1_name.c b/sha1_name.c
index dc68355..3ac3ab4 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -4,6 +4,7 @@ #include "commit.h"
 #include "tree.h"
 #include "blob.h"
 #include "tree-walk.h"
+#include "refs.h"
 
 static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
 {
@@ -245,36 +246,60 @@ static int get_sha1_basic(const char *st
 		"refs/remotes/%.*s/HEAD",
 		NULL
 	};
-	const char **p;
-	const char *warning = "warning: refname '%.*s' is ambiguous.\n";
-	char *pathname;
-	int already_found = 0;
+	static const char *warning = "warning: refname '%.*s' is ambiguous.\n";
+	const char **p, *pathname;
+	char *real_path = NULL;
+	int refs_found = 0, at_mark;
+	unsigned long at_time = (unsigned long)-1;
 	unsigned char *this_result;
 	unsigned char sha1_from_ref[20];
 
 	if (len == 40 && !get_sha1_hex(str, sha1))
 		return 0;
 
+	/* At a given period of time? "@2 hours ago" */
+	for (at_mark = 1; at_mark < len; at_mark++) {
+		if (str[at_mark] == '@') {
+			int date_len = len - at_mark - 1;
+			char *date_spec = xmalloc(date_len + 1);
+			strncpy(date_spec, str + at_mark + 1, date_len);
+			date_spec[date_len] = 0;
+			at_time = approxidate(date_spec);
+			free(date_spec);
+			len = at_mark;
+		}
+	}
+
 	/* Accept only unambiguous ref paths. */
 	if (ambiguous_path(str, len))
 		return -1;
 
 	for (p = fmt; *p; p++) {
-		this_result = already_found ? sha1_from_ref : sha1;
-		pathname = git_path(*p, len, str);
-		if (!read_ref(pathname, this_result)) {
-			if (warn_ambiguous_refs) {
-				if (already_found)
-					fprintf(stderr, warning, len, str);
-				already_found++;
-			}
-			else
-				return 0;
+		this_result = refs_found ? sha1_from_ref : sha1;
+		pathname = resolve_ref(git_path(*p, len, str), this_result, 1);
+		if (pathname) {
+			if (!refs_found++)
+				real_path = strdup(pathname);
+			if (!warn_ambiguous_refs)
+				break;
 		}
 	}
-	if (already_found)
-		return 0;
-	return -1;
+
+	if (!refs_found)
+		return -1;
+
+	if (warn_ambiguous_refs && refs_found > 1)
+		fprintf(stderr, warning, len, str);
+
+	if (at_time != (unsigned long)-1) {
+		read_ref_at(
+			real_path + strlen(git_path(".")) - 1,
+			at_time,
+			sha1);
+	}
+
+	free(real_path);
+	return 0;
 }
 
 static int get_sha1_1(const char *name, int len, unsigned char *sha1);
-- 
1.3.2.g7278

^ permalink raw reply related

* Re: Git 1.3.2 on Solaris
From: Stefan Pfetzing @ 2006-05-17 10:41 UTC (permalink / raw)
  To: Git Mailing List
In-Reply-To: <7v7j4lhup3.fsf@assigned-by-dhcp.cox.net>

Hi Junio,

2006/5/17, Junio C Hamano <junkio@cox.net>:
> If you look at our Makefile, you will see bindir does not have
> to be gitexecdir.  The suggestion by Linus is that you set
> bindir to /usr/local/bin or whereever your distribution's
> packaging scheme wants the locally installed software to be that
> is on user's PATH, and gitexecdir to /usr/local/libexec/git
> (again, whereever), _and_ have:
>
>         ln -s /usr/bin/gtr /usr/local/libexec/git/tr
>         ln -s /usr/bin/gxargs /usr/local/libexec/git/xargs
>         ...

Nice, that looks like a solution, but will this also "fix" the tr usage for
the git tests? If so, I'll write a small shellscript to create the links and
so on, and test it on Solaris later today.

bye

dreamind
-- 
       http://www.dreamind.de/
Oroborus and Debian GNU/Linux Developer.

^ permalink raw reply

* Re: [RFC 1/5] Remove unnecessary local in get_ref_sha1.
From: Junio C Hamano @ 2006-05-17 10:43 UTC (permalink / raw)
  To: Shawn Pearce; +Cc: git
In-Reply-To: <20060517095446.GB28529@spearce.org>

Shawn Pearce <spearce@spearce.org> writes:

> Remove unnecessary local in get_ref_sha1.
>
> Signed-off-by: Shawn O. Pearce <spearce@spearce.org>

Ack.

^ permalink raw reply

* Re: [RFC 2/5] Improve abstraction of ref lock/write.
From: Junio C Hamano @ 2006-05-17 10:46 UTC (permalink / raw)
  To: Shawn Pearce; +Cc: git
In-Reply-To: <20060517095502.GC28529@spearce.org>

Shawn Pearce <spearce@spearce.org> writes:

> Created 'struct ref_lock' to contain the data necessary to perform
> a ref update...
>
>  fetch.c |   32 +++++----
>  refs.c  |  236 ++++++++++++++++++++++++++++++---------------------------------
>  refs.h  |   24 ++++--
>  3 files changed, 144 insertions(+), 148 deletions(-)

Looks a bit intrusive probably necessary so not necessarily bad
but I'll postpone looking at this for now; maybe tomorrow or
more likely over the weekend.

Eyeballs from people touched the affected files in the past are
greatly appreciated.

^ permalink raw reply

* Re: [RFC 3/5] Convert update-ref to use ref_lock API.
From: Junio C Hamano @ 2006-05-17 10:49 UTC (permalink / raw)
  To: Shawn Pearce; +Cc: git
In-Reply-To: <20060517095519.GD28529@spearce.org>

Shawn Pearce <spearce@spearce.org> writes:

> -'git-update-ref' <ref> <newvalue> [<oldvalue>]
> +'git-update-ref' <ref> <newvalue> [<oldvalue>] [-m <reason>]

Is it just me who feels "cmd [-m <reason>] <ref> <new> [<old>]" would
look more natural?

^ permalink raw reply

* Re: Fwd: [OT] Re: Git via a proxy server?
From: Petr Vandrovec @ 2006-05-17 10:54 UTC (permalink / raw)
  To: Sam Song; +Cc: Jan-Benedict Glaw, git
In-Reply-To: <20060517083845.GC23642@lug-owl.de>

Jan-Benedict Glaw wrote:
> On Tue, 2006-05-16 20:56:39 -0700, Sam Song <samlinuxkernel@yahoo.com> wrote:
> 
>>Petr Vandrovec <petr@vmware.com> wrote:
>>
>>>Best to test this is to start 'socket 192.168.40.99
>>>80' from command line and 
>>>then type these two lines above, plus one empty
>>>line.  You should get back '200 
>>>OK', empty line, and then you can start
>>>communicating using git protocol - if 
>>>you can do that...
>>
>>I cannot run "socket" and "CONNECT" on Fedora Core 3.
>>It simply told me that no such command. How could I 
>>do this task in my case?
> 
> 
> Well, install some package to have `socket' available? Debian calls
> the packet `socket', too, so I guess Fedora may have something
> similar.

Surprisingly they do not...  You should be able to replace 'socket' with 
'netcat' - and I believe that netcat/nc package is available for Fedora.  For 
this purpose they have same command line & behavior.
							Petr

^ permalink raw reply


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