Git development
 help / color / mirror / Atom feed
* Re: 'git commit' ignoring args?
From: Jeff Garzik @ 2005-11-29 11:03 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vzmnotwyr.fsf@assigned-by-dhcp.cox.net>

Junio C Hamano wrote:
> Could it be that at some point after touching book.xml before
> running git commit you did update-index on it?

Not according to scrollback, no.


> -- >8 --
> Here is what I did to reproduce.

Unfortunately, I cannot reproduce the problem either :(

Oh well, as long as the facility works in general, I'll live to fight 
another day :)

	Jeff

^ permalink raw reply

* Re: git-name-rev off-by-one bug
From: Petr Baudis @ 2005-11-29 10:31 UTC (permalink / raw)
  To: linux; +Cc: junkio, git
In-Reply-To: <20051129080529.20705.qmail@science.horizon.com>

Dear diary, on Tue, Nov 29, 2005 at 09:05:29AM CET, I got a letter
where linux@horizon.com said that...
> > *2* Cogito (things that start with cg-*) seems to try to be
> > cleverer.  Pasky might want to brag about the rules in Cogito
> > land.

> In fact, he might want to explain what the difference is between cogito
> and git.  Most particularly, are there any restrictions on mixing cg-*
> and git-* operations from within the same directory?

  Nope, except:

Cogito vs. other GIT tools
~~~~~~~~~~~~~~~~~~~~~~~~~~

You can *MOSTLY* use Cogito in parallel with other GIT frontends (e.g.
StGIT), as well as the GIT plumbing and core GIT tools - the tools only
need to keep HEAD in place and follow the standardized `refs/`
hierarchy. The only notable exception is that you should stick with a
single toolkit during a merge.

	(-- Cogito README)

  So exactly during a merge, things might not blend well, since Cogito
does things a bit differently. It knows of no MERGE_HEAD, MERGE_MSG and
such, and instead passes stuff over different channels or computes/asks
it at different times. Historically, cg-merge and git-merge evolution
has been almost entirely separate.


  From the user POV, the main difference between Cogito and GIT merging
is that:

  (i) Cogito tries to never leave the index "dirty" (i.e. containing
unmerged entries), and instead all conflicts should propagate to the
working tree, so that the user can resolve them without any further
special tools. (What is lacking here is that Cogito won't proofcheck
that you really resolved them all during a commit. That's a big TODO.
But core GIT won't you warn about committing the classical << >> ==
conflicts either.)

  (ii) Cogito will handle trees with some local modifications better -
basically any local modifications git-read-tree -m won't care about.
I didn't read the whole conversation, so to reiterate: git-read-tree
will complain when the index does not match the HEAD, but won't
complain about modified files in the working tree if the merge is not
going to touch them. Now, let's say you do this (output is visually
only roughly or not at all resembling what would real tools tell you):

	$ ls
	a b c
	$ echo 'somelocalhack' >>a
	$ git merge "blah" HEAD remotehead
	File-level merge of 'b' and 'c'...
	Oops, 'b' contained local conflicts.
	Automatic merge aborted, fix up by hand.
	$ fixup b
	$ git commit
	Committed files 'a', 'b', 'c'.

Oops. It grabbed your local hack and committed it along the merge.
Cogito won't do this, it will hold 'a' back when doing the merge commit
(if it works right; in the past, there were several bugs related to
this, but hopefully they are all fixed by now):

	$ ls
	a b c
	$ echo 'somelocalhack' >>a
	$ cg-merge remotehead
	... Merging c
	... Merging b
	Conflicts during merge of 'b'.

		Fix up the conflicts, then kindly do cg-commit.
	$ fixup b
	$ cg-commit -m"blah"
	Committed files 'b', 'c'.

Also note that the cg-merge usage is simpler and you give the "blah"
message only to cg-commit, when it's for sure you are going to use it.

  (iii) Cogito does not support the smart recursive merging strategy.
That means it won't follow renames, and in case of multiple merge bases,
it will not merge them recursively, but it will just ask you to choose
one manually, or suggest you the most conservative merge base (where you
should get no false clean merges, but you will probably have to deal
with a lot of conflicts).

-- 
				Petr "Pasky" Baudis
Stuff: http://pasky.or.cz/
VI has two modes: the one in which it beeps and the one in which
it doesn't.

^ permalink raw reply

* Re: 'git commit' ignoring args?
From: Andreas Ericsson @ 2005-11-29 10:38 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <loom.20051129T003332-60@post.gmane.org>

Junio C Hamano wrote:
> Andreas Ericsson <ae <at> op5.se> writes:
> 
> 
>>The index has been updated to match the file on disk somehow (perhaps 
>>you did 'git add book.xml'?).
> 
> 
> book.xml is known to the index file at this point.  "git add book.xml" will
> *not* run update-index on that path, so that is not it.
> 

At this point, yes, but if it was just added it wouldn't have been 
before it was git-add'ed.

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231

^ permalink raw reply

* Re: use binmode(STDOUT) in git-status
From: Tim O'Callaghan @ 2005-11-29 10:05 UTC (permalink / raw)
  To: git
In-Reply-To: <7vd5kkvd3q.fsf@assigned-by-dhcp.cox.net>

On Mon, Nov 28, 2005 at 10:31:05AM -0800, Junio C Hamano wrote:
> Alex Riesen <raa.lkml@gmail.com> writes:
> 
> > Activision's Perl generates CRLF unless STDOUT is binmoded, which is
> > inconsistent with other output of git-status.
> 
> I do not think this is a kind of patch that I should accept to
> apply to the generic part of the codepath, even if on sane
> platforms binmode() could be a no-op.
> 
> You should not have to say binmode() when you are emitting plain
> text (otherwise you have to say that everywhere which is
> madness).  I presume the Cygwin version uses Perl from Cygwin
> and would not have this problem?
> 
> If that is the case, maybe this patch should be maintained out
> of tree by the maintainer of Windows port of git that does _not_
> use Cygwin but ActiveState.
>

You could also check `perl -v` for the phrase "built for cygwin". I am not
sure how much of an issue this is though, because native windows support is
going to be a bit tricky without Cygwin as you also need (ba)sh, sed, grep,
etc.

Tim.

^ permalink raw reply

* Re: git-name-rev off-by-one bug
From: Junio C Hamano @ 2005-11-29  9:29 UTC (permalink / raw)
  To: linux; +Cc: junkio, git
In-Reply-To: <20051129080529.20705.qmail@science.horizon.com>

linux@horizon.com writes:

> (In particular, and unlike other SCMs, "push" and "pull" are
> based on merging!  So I can't even understand what pulling from
> Linus's tree does until I understand merging.)

If you do "git pull git://.../linux-2.6/", it fetches the
objects and stores Linus "master" into .git/FETCH_HEAD, and then
merges that into whatever branch you happen to be on (but you
already know that).

> BUT... what's the second argument to git-read-tree for, if it
> always has to be HEAD?

git-read-tree is purely "a building block to be scripted" in
your second mail (private?).  In my message you are replying to,
I outlined what the end-user level tool, git-pull (and git-merge
it uses) does, which is built using git-read-tree, and we happen
to always pass HEAD as its second argument.  But it does not
have to be that way.  One thing planned in the future is to do a
merge in a temporary working directory, not in your primary
working tree, and when we implement that, the second argument
will be whatever branch head you are pulling into, not
necessarily your current HEAD.

> BTW, I'd change the description of git-read-tree from

Thanks.  I am a bit too tired tonight so I may send you a
correction later if what I am going to comment here turns out to
be incorrect, but a quick glance tells me this is a good
clarification.

> ...  I was wondering why the
> hell a 2-way merge looked more complex than a 3-way.

2-way is called merge but it is not about the merge at all
(git-merge is mostly about 3-way merge).  It is more about
checkout.  Suppose you have checked out branch A, and have local
modificiations (both in index and in working tree).  You would
want to switch to branch B by "git checkout B" (without -f).
2-way "read-tree -m -u" is used to ensure that you take your
local modifications with you while checking out branch B into
your working tree, meaning:

 (1) local changes already registered in the index stays in the
     index file; obviously this can be done only if A and B are
     the same at such paths.

 (2) local changes not registered in the index stays in the
     working tree; similar restriction applies but the rules are
     more involved.

Similar to 3-way case, 2-way will refuse to lose your local
modifications, and that is the 2-way case table in
Documentation/git-read-tree.txt is about.

> This is git-merge, as opposed to the more primitive git-read-tree -m
> plus git-merge-index, right?

Everything I wrote in the previous message was about the
end-user tools git-pull/git-merge. 

> - Doesn't any non-trivial merge or invocation of git-update-index
>   produce blob objects in the database that become garbage if you
>   do this?

We produce garbage blobs all the time, and we do not care.  Even
the following sequence that does not involve any merge produces
a garbage blob for the first version of A that was faulty:

        $ git checkout
        $ edit A
        $ git update-index A
        $ make ;# oops, there is a mistake.
        $ edit A
        $ make ;# this time it is good.
        $ git commit -a -m 'Finally compiles.'

Occasional fsck-objects, prune and repack are your friends.

> - Is there any difference between "git-reset --hard" and "git-checkout -f"?

"reset --hard" does more thorough job removing unwanted files
from your working tree.  It looks at your current HEAD, the
commit you are resetting to (when you say "reset --hard
<commit>"), and your index file, and paths mentioned by any of
these three that should not remain (that is, not in the commit
you are resetting to) are removed from your working tree.  In
addition, "reset --hard <commit>" updates the branch head and
can be used to rewind it.  On the other hand, "checkout -f"
tells git to *ignore* what is in the index, so any file in the
working tree that used to be in the index (or old branch you
were working on) that does not exist in the branch you are
checking out is not removed.

On a related topic of removing unwanted paths, earlier I said
2-way is used to make sure "git checkout" takes your changes
with you when you switch branches.  As a natural consequence of
this, if you do not have any local changes, "git checkout"
without "-f" does the right thing -- it removes unwanted paths
that existed in the original branch but not in the branch you
are switching to.

> Okay, so git-update-index will overwrite a staged file with a
> fresh stage-0 copy.  And git-commit will refuse to commit
> (to be precise, it'll stop at the git-write-tree stage) if there
> are unresolved conflicts.

Sorry, I was unclear that I was talking about end-user level
tool.  The update-index here is not about the conflict
resolution in the index file read-tree documentation talks
about.  That has already been done when "merge" ran in the
conflicting case.  In the conflicting case, the working tree
holds 3-way merge conflicting result, and the index holds HEAD
version at stage0 for such a path.  Hand resolving after
update-index is to record what you eventually want to commit
(i.e. you are not replacing higher stage entry in the index with
stage0 entry -- you are replacing stage0 entry with another).

> If you want to see the unmodified input files, you can find their
> IDs with "git-ls-files -u" and then get a copy with "git-cat-file blob"
> or "git-unpack-file".  git-merge-index is basically a different way to
> process the output of git-ls-files -u.

Yes, in principle.  But in practice you usually do not use these
low level tools yourself.  When git-merge returns with
conflicting paths, most of them have already been collapsed into
stage0 and git-ls-files --unmerged would not show.  The only
case I know of that you may still see higher stage entries in
the index these days is merging paths with different mode bits.
We used to leave higher stage entries when both sides added new
file at the same path, but even that we show as merge from
common these days.

^ permalink raw reply

* [PATCH] SVN import: Use one log call
From: Matthias Urlichs @ 2005-11-29  7:13 UTC (permalink / raw)
  To: git

One "svn log" (or its equivalent) per revision adds delay and server load.
Instead, open two SVN connections -- one for the log, and one for the files.

Positive side effect: Only those log entries which actually contain data
are committed => no more empty commits.

Also, change the "-l" option to set the maximum revision to be pulled,
not the number of revisions.

---

... or pull from http://netz.smurf.noris.de/git/git.git#svnup.

 Documentation/git-svnimport.txt |   15 ++++++---------
 git-svnimport.perl              |   37 ++++++++++++++++---------------------
 2 files changed, 22 insertions(+), 30 deletions(-)

applies-to: 744acb3ef516240503d9a13cf1adb6a9670f905e
81da2a98762ef4965e6410c1c83600f2d80dc67a
diff --git a/Documentation/git-svnimport.txt b/Documentation/git-svnimport.txt
index fcc79fa..f8dbee7 100644
--- a/Documentation/git-svnimport.txt
+++ b/Documentation/git-svnimport.txt
@@ -10,7 +10,7 @@ git-svnimport - Import a SVN repository 
 SYNOPSIS
 --------
 'git-svnimport' [ -o <branch-for-HEAD> ] [ -h ] [ -v ] [ -d | -D ]
-			[ -C <GIT_repository> ] [ -i ] [ -u ] [-l limit_nr_changes]
+			[ -C <GIT_repository> ] [ -i ] [ -u ] [-l limit_rev]
 			[ -b branch_subdir ] [ -t trunk_subdir ] [ -T tag_subdir ]
 			[ -s start_chg ] [ -m ] [ -M regex ]
 			<SVN_repository_URL> [ <path> ]
@@ -71,14 +71,11 @@ When importing incementally, you might n
 	regex. It can be used with -m to also see the default regexes.
 	You must escape forward slashes.
 
--l <max_num_changes>::
-	Limit the number of SVN changesets we pull before quitting.
-	This option is necessary because the SVN library has serious memory
-	leaks; the recommended value for nontrivial imports is 100.
-
-	git-svnimport will still exit with a zero exit code. You can check
-	the size of the file ".git/svn2git" to determine whether to call
-	the importer again.
+-l <max_rev>::
+	Specify a maximum revision number to pull.
+
+	Formerly, this option controlled how many revisions to pull, due to
+	SVN memory leaks. (These have been worked around.)
 
 -v::
 	Verbosity: let 'svnimport' report what it is doing.
diff --git a/git-svnimport.perl b/git-svnimport.perl
index af13fdd..640d675 100755
--- a/git-svnimport.perl
+++ b/git-svnimport.perl
@@ -35,7 +35,7 @@ our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$
 sub usage() {
 	print STDERR <<END;
 Usage: ${\basename $0}     # fetch/update GIT from SVN
-       [-o branch-for-HEAD] [-h] [-v] [-l max_num_changes]
+       [-o branch-for-HEAD] [-h] [-v] [-l max_rev]
        [-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname]
        [-d|-D] [-i] [-u] [-s start_chg] [-m] [-M regex] [SVN_URL]
 END
@@ -126,8 +126,9 @@ sub file {
 package main;
 use URI;
 
-my $svn = $svn_url;
+our $svn = $svn_url;
 $svn .= "/$svn_dir" if defined $svn_dir;
+my $svn2 = SVNconn->new($svn);
 $svn = SVNconn->new($svn);
 
 my $lwp_ua;
@@ -198,7 +199,7 @@ $ENV{GIT_INDEX_FILE} = $git_index;
 my $maxnum = 0;
 my $last_rev = "";
 my $last_branch;
-my $current_rev = $opt_s-1;
+my $current_rev = $opt_s || 1;
 unless(-d $git_dir) {
 	system("git-init-db");
 	die "Cannot init the GIT db at $git_tree: $?\n" if $?;
@@ -250,7 +251,7 @@ EOM
 		my($num,$branch,$ref) = split;
 		$branches{$branch}{$num} = $ref;
 		$branches{$branch}{"LAST"} = $ref;
-		$current_rev = $num if $current_rev < $num;
+		$current_rev = $num+1 if $current_rev <= $num;
 	}
 	close($B);
 }
@@ -704,17 +705,17 @@ sub commit {
 	print "DONE: $revision $dest $cid\n" if $opt_v;
 }
 
-my ($changed_paths, $revision, $author, $date, $message, $pool) = @_;
-sub _commit_all {
-	($changed_paths, $revision, $author, $date, $message, $pool) = @_;
+sub commit_all {
+	# Recursive use of the SVN connection does not work
+	local $svn = $svn2;
+
+	my ($changed_paths, $revision, $author, $date, $message, $pool) = @_;
 	my %p;
 	while(my($path,$action) = each %$changed_paths) {
 		$p{$path} = [ $action->action,$action->copyfrom_path, $action->copyfrom_rev, $path ];
 	}
 	$changed_paths = \%p;
-}
 
-sub commit_all {
 	my %done;
 	my @col;
 	my $pref;
@@ -730,18 +731,12 @@ sub commit_all {
 	}
 }
 
-while(++$current_rev <= $svn->{'maxrev'}) {
-	if (defined $opt_l) {
-		$opt_l--;
-		if ($opt_l < 0) {
-			last;
-		}
-	}
-	my $pool=SVN::Pool->new;
-	$svn->{'svn'}->get_log("/",$current_rev,$current_rev,1,1,1,\&_commit_all,$pool);
-	$pool->clear;
-	commit_all();
-}
+$opt_l = $svn->{'maxrev'} if not defined $opt_l or $opt_l > $svn->{'maxrev'};
+print "Fetching from $current_rev to $opt_l ...\n" if $opt_v;
+
+my $pool=SVN::Pool->new;
+$svn->{'svn'}->get_log("/",$current_rev,$opt_l,0,1,1,\&commit_all,$pool);
+$pool->clear;
 
 
 unlink($git_index);
---


-- 
Matthias Urlichs   |   {M:U} IT Design @ m-u-it.de   |  smurf@smurf.noris.de
Disclaimer: The quote was selected randomly. Really. | http://smurf.noris.de
 - -
There was a young peasant named Gorse Who fell madly in love with his horse.
Said his wife, "You rapscallion, That horse is a stallion -- This constitutes
grounds for divorce."

^ permalink raw reply related

* Re: [PATCH] Fix typo in http-push.c
From: Johannes Schindelin @ 2005-11-29  8:24 UTC (permalink / raw)
  To: Jan Andres; +Cc: git
In-Reply-To: <20051129005154.GA3076@pitr.home.jan>

Hi,

On Tue, 29 Nov 2005, Jan Andres wrote:

> -				lock->token = xmalloc(strlen(ctx->cdata - 15));
> +				lock->token = xmalloc(strlen(ctx->cdata) - 15);
>  				strcpy(lock->token, ctx->cdata + 16);

Why not

+				lock->token = xmalloc(strlen(ctx->cdata + 16));

Ciao,
Dscho

^ permalink raw reply

* Re: git-name-rev off-by-one bug
From: linux @ 2005-11-29  8:05 UTC (permalink / raw)
  To: junkio; +Cc: git, linux
In-Reply-To: <7vhd9wngn6.fsf@assigned-by-dhcp.cox.net>

> Thanks for the complaints.  No sarcasm intended.  Yours is
> exactly the kind of message we (people who've been around here
> for too long) need to hear.

Thanks for taking it so well!  I'm trying to really *understand* git,
so I can predict its behaviour, but I've been coming to the conclusion
that the only way to do that is by re-reading the mailing list from day 1.

And to understand git at all, you have to understand merging, since
doing merging fast and well is the central reason for git's entire
existence.  Single-developer porcelain like "cvs annotate" is
noticeably lacking, but branching and merging is great.

(In particular, and unlike other SCMs, "push" and "pull" are
based on merging!  So I can't even understand what pulling from
Linus's tree does until I understand merging.)


I'm working on some notes to explain git to myself and people
I work with, which I'll post when they're vaguely complete.

> To the git barebone Porcelain layer (things that start with
> git-*, not with cg-*) [*2*], a merge is always between the
> current HEAD and one or more remote branch heads, and the index
> file must exactly match the tree of HEAD commit (i.e. the
> contents of the last commit) when it happens.  In other words,
> "git-diff --cached HEAD" must report no changes [*3*].  So
> HEAD+1 must be HEAD in your above notation, or merge will refuse
> to do any harm to your repository (that is, it may fetch the
> objects from remote, and it may even update the local branch
> used to keep track of the remote branch with "git pull remote
> rbranch:lbranch", but your working tree, .git/HEAD pointer and
> index file are left intact).

Right!  Since the object database is strictly append-only, it's easy to
see how such changes are quite harmless, and updating a tracking branch
is hardly a big nasty surprise.

It's the index and working directory that are volatile, and that I was
worried about.

BUT... what's the second argument to git-read-tree for, if it
always has to be HEAD?

BTW, I'd change the description of git-read-tree from

>     Reads the tree information  given  by  <tree-ish>  into  the directory
>     cache, but does not actually update any of the files it "caches". (see:
>     git-checkout-index)

to

      Reads the tree information given by <tree-ish> into the index.
      The working directory is not modified in any way (unless -u
      is used).  Use git-checkout-index to do that.

      In addition to the simple one-tree case, this can (with the
      -m flag) merge 2 or 3 trees into the index.  When used with
      -m, the -u flag causes it to also update the files in the
      working directory.

      Trivial merges are done by "git-read-tree" itself.  Conflicts
      are left in an unmerged state for git-merge-index to resolve.

> You may have local modifications in the working tree files.  In
> other words, "git-diff" is allowed to report changes (the
> difference between HEAD+2 and HEAD+1 in your notation).
> However, the merge uses your working tree as the working area,
> and in order to prevent the merge operation from losing such
> changes, it makes sure that they do not interfere with the
> merge.  Those complex tables in read-tree documentation define
> what it means for a path to "interfere with the merge".  And if
> your local modifications interfere with the merge, again, it
> stops before touching anything.

THANK YOU for finally making this clear!  I was wondering why the
hell a 2-way merge looked more complex than a 3-way.  (Although,
admittedly, I'm *still* not clear on what the difference is.  It
seems like a 2-way just picks the origin commit automatically.)
So it goes like this:

- git-merge will refuse to do anything if there are any uncommitted changes
  in the index.  [footnote about harmless exceptions]
- git-merge will refuse to do anything if there are changes in the
  working directory to a file that would be affected by the merge.
  You CAN, however, have unindexed changes to files that are unchanged
  by the merge.


The description of a 1-way merge in git-read tree is quite confusing.
Here's a rephrasing; do I understand it?

READING

    Without -m, git-read-tree pulls a specified tree into the index.
    Any file whose underlying blob is changed by the update will have
    its cached stat data invalidated, so it will appear in the output
    of git-diff-files, and git-checkout-index -f will overwrite it.

    A single-tree merge is a slightly nicer variant on this.

MERGING
    ...


Single Tree Merge
    If only one tree is specfied, any file whose blob changes as a
    result of the update is compared with the working directory file,
    and the cached stat data updated if the working directory file
    matches the new blob.  Thus, working directory files which happen to
    match the contents of the new tree will be excluded from the outout
    of git-diff-files, and git-checkout-index -f will not update their
    file modification times.

    This is basically the effect of git-update-index --refresh.

    This is actually usually preferable to the non-merge case, but
    does do extra work.

I haven't found the code yet, but obviously if the working directory
file is clean relative to the previous blob, it's dirty relative to
the changed blob, so there's no need to actually read the file.

Um... I just tried it, and it appears that things do NOT work as
I just described.  Time to read the code...


BTW, an even cuter way of writing same() in read_tree.c would be:

static int same(struct cache_entry *a, struct cache_entry *b)
{
	if (!a || !b)
		return a == b;
        return a->ce_mode == b->ce_mode &&
                !memcmp(a->sha1, b->sha1, 20);
}

(I presume that
return a&&b ? a->ce_mode == b->ce_mode && !memcmp(a->sha1, b->sha1, 20) : a==b;
would be taking it too far...)

Oh, and the git-read-tree man page fails to mention --reset and --trivial.
And the usage message should have <sha> changed to <tree>.

> So in the above two "failed merge" case, you do not have to
> worry about lossage of data --- you simply were not ready to do
> a merge, so no merge happened at all.  You may want to finish
> whatever you were in the middle of doing, and retry the same
> pull after you are done and ready.

I feel about a thousand percent better about git already.

> When things cleanly merge, these things happen:
>
> (1) the results are updated both in the index file and in your
>     working tree,
> (2) index file is written out as a tree,
> (3) the tree gets committed, and 
> (4) the HEAD pointer gets advanced.

This is git-merge, as opposed to the more primitive git-read-tree -m
plus git-merge-index, right?

(Aside, you should document that got-merge --no-commit saves the <msg>
in .git/MERGE_MSG, and git-commit uses it as the default message.
Otherwise, users wonder why the hell it asks for a commit message it
knows won't be used.)

> Because of (2), we require that the original state of the index
> file to match exactly the current HEAD commit; otherwise we will
> write out your local changes already registered in your index
> file (the difference between HEAD+1 and HEAD in your notation)
> along with the merge result, which is not good.

> *3* This is a bit of lie.  In certain special cases, your index
> are allowed to be different from the tree of HEAD commit;
> basically your index entries are allowed to match the result of
> trivial merge already (e.g. you received the same patch from
> external source to produce the same result as what you are
> merging).  For example, if a path did not exist in the common
> ancestor and your head commit but exists in the tree you are
> merging into your repository, and if you already happen to have
> that path exactly in your index, the merge does not have to
> fail.  This is case #2 in the 3-way read-tree table in t/t1000.

Ah, yes, the light dawns!  So it *is* okay if you have an uncommitted
change in the index which exactly matches what git-read-tree -m would
have done anyway.  It doesn't make a difference to the final state
of the index, and forcing the poor user to undo it just so the merge
can redo it is simply annoying.

But it's NOT okay if you have any other change in the index, even in a
file unaffected by the merge, because then the final state would not
be the result of the merge, and that could be confusing.

> Because (1)
> involves only the paths different between your branch and the
> remote branch you are pulling from during the merge (which is
> typically a fraction of the whole tree), you can have local
> modifications in your working tree as long as they do not
> overlap with what the merge updates.

Wonderfully clear.  Such changes must not exist at the git-read-tree
level, since there's not even a way to represent them to
git-merge-index.  Still, this can save considerable bother when
trying to track someone else's tree.

> When there are conflicts, these things happen:
>
>  (0) HEAD stays the same.
>
>  (1) Cleanly merged paths are updated both in the index file and
>      in your working tree.
> 
>  (2) For conflicting paths, the index file records the version
>      from HEAD. The working tree files have the result of
>      "merge" program; i.e. 3-way merge result with familiar
>      conflict markers <<< === >>>.
> 
>  (3) No other changes are done.  In particular, the local
>      modifications you had before you started merge will stay the
>      same and the index entries for them stay as they were,
>      i.e. matching HEAD.

So this is a lot like what CVS does, but neater because all the
merge sources are available unmodified in the index.  Excellent.

> After seeing a conflict, you can do two things:
>
> * Decide not to merge.  The only clean-up you need are to reset
>   the index file to the HEAD commit to reverse (1) and to clean
>   up working tree changes made by (1) and (2); "git-reset" can
>   be used for this.

Cool.  Two minor questions:

- Doesn't any non-trivial merge or invocation of git-update-index
  produce blob objects in the database that become garbage if you
  do this?  Or are they somehow kept separate until a tree object
  is created to point to them?

  (You could have an "unreferenced" bit in the index, indicating that
  the blobs in question could be found in .git/pending rather than
  .git/objects, until git-write-tree moved them into the database.
  But I don't see mention of any such scheme.)

- Is there any difference between "git-reset --hard" and "git-checkout -f"?

> * Resolve the conflicts.  "git-diff" would report only the
>   conflicting paths because of the above (1) and (2).  Edit the
>   working tree files into a desirable shape, git-update-index
>   them, to make the index file contain what the merge result
>   should be, and run "git-commit" to commit the result.

Okay, so git-update-index will overwrite a staged file with a
fresh stage-0 copy.  And git-commit will refuse to commit
(to be precise, it'll stop at the git-write-tree stage) if there
are unresolved conflicts.

If you want to see the unmodified input files, you can find their
IDs with "git-ls-files -u" and then get a copy with "git-cat-file blob"
or "git-unpack-file".  git-merge-index is basically a different way to
process the output of git-ls-files -u.


> *1* It is a shame that the most comprehensive definition of
> 3-way read-tree semantics is in t/t1000-read-tree-m-3way.sh test
> script.

Thanks for the pointer; I'll go and read it!

> *2* Cogito (things that start with cg-*) seems to try to be
> cleverer.  Pasky might want to brag about the rules in Cogito
> land.

In fact, he might want to explain what the difference is between cogito
and git.  Most particularly, are there any restrictions on mixing cg-*
and git-* operations from within the same directory?

I've been assuming that cogito is just series of friendlier
utilities, in much the same way that a text editor is friendlier
than "cat > kernel/sched.c", so I'll study it after I understand
core git.


One more question to be sure I understand merging... AFAICT, it would
be theoretically possible to stop distinguishing "stage 2" and "stage 0".
If there is only a stage 2 file, then in-index will immediately
collapse it to stage 0, and if there are any other stage files,
you know there's an incomplete merge.

(Alternatively, you could collapse "stage 3" and "stage 0", since
stages 2 and 3 are treated identically, but traditionally, stage 2 is
the "trunk" and state 3 is the "branch" being merged in.)

^ permalink raw reply

* Re: [PATCH] git-clone --shared should imply --local
From: Junio C Hamano @ 2005-11-29  7:52 UTC (permalink / raw)
  To: Pavel Roskin; +Cc: git
In-Reply-To: <1133248651.27750.82.camel@dv>

Pavel Roskin <proski@gnu.org> writes:

> On Mon, 2005-11-28 at 23:08 -0800, Junio C Hamano wrote:
>> Pavel Roskin <proski@gnu.org> writes:
>> 
>> > The "--shared" option to git-clone is silently ignored if "--local" is
>> > not specified.  The manual doesn't mention such dependency.  Make
>> > "--shared" imply "--local".
>> >
>> > Signed-off-by: Pavel Roskin <proski@gnu.org>
>> 
>> Hmph, then probably the manual should say it implies it...
>
> Actually, from the user point of view --local and --shared are
> alternatives to each other and to the default behavior.  --local copies
> files, --shared makes a reference.
>
> Saying that --shared implies --local could sound like git-clone would
> still copy all objects.
>
> My description was from the implementation point of view.

Ah.  Makes sense.

^ permalink raw reply

* Re: [PATCH] Re: keeping remote repo checked out?
From: Junio C Hamano @ 2005-11-29  7:52 UTC (permalink / raw)
  To: Daniel Barkalow; +Cc: git, Petr Baudis, Linus Torvalds
In-Reply-To: <Pine.LNX.4.64.0511290141220.25300@iabervon.org>

Daniel Barkalow <barkalow@iabervon.org> writes:

>>     server$ git checkout production
>>     work$ git checkout master
>>     work$ git pull server production ;# merge into work's master
>>     work$ git push server master:receive_from_work
>>     server$ git pull . receive_from_work ;# merge into server's production
>> 
>> and you make sure receive_from_work is not checked out on server
>> (or production is never pushed into) and always do fast forward
>> and nothing else.
>
> That's what I'm doing currently, actually (with work pushing to 
> "production", but server having checked out "deploy"), and I find it 
> annoying to have to do the pull each time and have a separate head. It 
> also means that, if the stuff on the server is set up as a hook, it'll 
> have to do the locking against concurrent changes to the working tree in 
> pull, which is much more complicated than checkout.

This depends on what is on the checked-out working tree and how
it is used, but one use pattern I was imagining was actually
much more elaborate.  I would set up two non-naked repositories
on the server; let's call them treeR and treeP (reception and
publish).

Each work machine push into its own reception branch of treeR
repository (so you have N reception branches for N workers).  On
the server machine, a daemon monitors the reception branch heads
(inotify or idle poll loop looking at mtime; the details do not
matter), and run the git pull into "master" of treeR.  My daemon
might want to forbid anything but fast forward for this pull, or
might allow any clean merge.  It compiles and runs testsuites
next.  If anything fails during the above process, the owner of
the reception branch is notified about the failure; I might even
want to forcibly rewind her reception branch in this case to
"master" when this happens.

When everything goes well, the daemon goes to treeP and pulls
from treeR's master into "deploy", which is checked out.  This
procedure is the only thing that touches treeP, so this pull is
guaranteed to be a fast forward.

The serialization happens by having a single such daemon
running; no need to worry about locking anymore.

^ permalink raw reply

* Re: [PATCH] git-clone --shared should imply --local
From: Pavel Roskin @ 2005-11-29  7:17 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7viruclymo.fsf@assigned-by-dhcp.cox.net>

On Mon, 2005-11-28 at 23:08 -0800, Junio C Hamano wrote:
> Pavel Roskin <proski@gnu.org> writes:
> 
> > The "--shared" option to git-clone is silently ignored if "--local" is
> > not specified.  The manual doesn't mention such dependency.  Make
> > "--shared" imply "--local".
> >
> > Signed-off-by: Pavel Roskin <proski@gnu.org>
> 
> Hmph, then probably the manual should say it implies it...

Actually, from the user point of view --local and --shared are
alternatives to each other and to the default behavior.  --local copies
files, --shared makes a reference.

Saying that --shared implies --local could sound like git-clone would
still copy all objects.

My description was from the implementation point of view.

-- 
Regards,
Pavel Roskin

^ permalink raw reply

* Re: [PATCH] git-clone --shared should imply --local
From: Junio C Hamano @ 2005-11-29  7:08 UTC (permalink / raw)
  To: Pavel Roskin; +Cc: git
In-Reply-To: <1133245249.27750.77.camel@dv>

Pavel Roskin <proski@gnu.org> writes:

> The "--shared" option to git-clone is silently ignored if "--local" is
> not specified.  The manual doesn't mention such dependency.  Make
> "--shared" imply "--local".
>
> Signed-off-by: Pavel Roskin <proski@gnu.org>

Hmph, then probably the manual should say it implies it...

^ permalink raw reply

* Re: [PATCH] Re: keeping remote repo checked out?
From: Daniel Barkalow @ 2005-11-29  6:57 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Petr Baudis, Linus Torvalds
In-Reply-To: <7vmzjom00m.fsf@assigned-by-dhcp.cox.net>

On Mon, 28 Nov 2005, Junio C Hamano wrote:

> Daniel Barkalow <barkalow@iabervon.org> writes:
> 
> > the two-way merge. That is, what I want to have work is:
> >
> >   server$ git checkout production
> >   work$ git push server:production
> >   server$ git checkout
> >
> > and this should leave the working tree on server with the changes pushed 
> > from work.
> 
> I think the approach Linus was suggesting before he suggested
> the funny refs/heads/../.. symlink idea (honestly, I think he
> was half joking) is cleaner, easier to understand, easier to
> implement and generally makes more sense.  Your work cycle would
> become like this:
> 
>     server$ git checkout production
>     work$ git checkout master
>     work$ git pull server production ;# merge into work's master
>     work$ git push server master:receive_from_work
>     server$ git pull . receive_from_work ;# merge into server's production
> 
> and you make sure receive_from_work is not checked out on server
> (or production is never pushed into) and always do fast forward
> and nothing else.

That's what I'm doing currently, actually (with work pushing to 
"production", but server having checked out "deploy"), and I find it 
annoying to have to do the pull each time and have a separate head. It 
also means that, if the stuff on the server is set up as a hook, it'll 
have to do the locking against concurrent changes to the working tree in 
pull, which is much more complicated than checkout.

It's also somewhat non-intuitive, and leaves a surprising way for the user 
to confuse the system in a way that it can't detect.

	-Daniel
*This .sig left intentionally blank*

^ permalink raw reply

* Re: [PATCH 0/4] Repository format version check series.
From: Martin Atukunda @ 2005-11-29  6:42 UTC (permalink / raw)
  To: git
In-Reply-To: <7voe48gqg9.fsf@assigned-by-dhcp.cox.net>

On Fri, Nov 25, 2005 at 05:15:02PM -0800, Junio C Hamano wrote:
> This series is what I've been cooking for the past several days,
> partly based on patch from Martin Atukunda but with hopefully
> smaller impact.
> 
> [PATCH 1/4] Repository format version check.
> [PATCH 2/4] Check repository format version in enter_repo().
> [PATCH 3/4] init-db: check template and repository format.
> [PATCH 4/4] setup_git_directory(): check repository format version.

This is just to confirm that this patch series passed the tests on my
end. Good work.

- Martin -

-- 
Due to a shortage of devoted followers, the production of great leaders has been discontinued.

^ permalink raw reply

* Re: [PATCH] Re: keeping remote repo checked out?
From: Junio C Hamano @ 2005-11-29  6:38 UTC (permalink / raw)
  To: Daniel Barkalow; +Cc: git, Petr Baudis, Linus Torvalds
In-Reply-To: <Pine.LNX.4.64.0511282337170.25300@iabervon.org>

Daniel Barkalow <barkalow@iabervon.org> writes:

> the two-way merge. That is, what I want to have work is:
>
>   server$ git checkout production
>   work$ git push server:production
>   server$ git checkout
>
> and this should leave the working tree on server with the changes pushed 
> from work.

I think the approach Linus was suggesting before he suggested
the funny refs/heads/../.. symlink idea (honestly, I think he
was half joking) is cleaner, easier to understand, easier to
implement and generally makes more sense.  Your work cycle would
become like this:

    server$ git checkout production
    work$ git checkout master
    work$ git pull server production ;# merge into work's master
    work$ git push server master:receive_from_work
    server$ git pull . receive_from_work ;# merge into server's production

and you make sure receive_from_work is not checked out on server
(or production is never pushed into) and always do fast forward
and nothing else.

^ permalink raw reply

* [PATCH] git-clone --shared should imply --local
From: Pavel Roskin @ 2005-11-29  6:20 UTC (permalink / raw)
  To: git

The "--shared" option to git-clone is silently ignored if "--local" is
not specified.  The manual doesn't mention such dependency.  Make
"--shared" imply "--local".

Signed-off-by: Pavel Roskin <proski@gnu.org>

diff --git a/git-clone.sh b/git-clone.sh
index c09979a..699205e 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -73,7 +73,7 @@ while
 	*,-n) no_checkout=yes ;;
 	*,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local) use_local=yes ;;
         *,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared) 
-          local_shared=yes ;;
+          local_shared=yes; use_local=yes ;;
 	*,-q|*,--quiet) quiet=-q ;;
 	1,-u|1,--upload-pack) usage ;;
 	*,-u|*,--upload-pack)


-- 
Regards,
Pavel Roskin

^ permalink raw reply related

* Re: [RFC 2/2] Automatically transform .git/{branches,remotes} into .git/config
From: Junio C Hamano @ 2005-11-29  6:04 UTC (permalink / raw)
  To: Josef Weidendorfer; +Cc: git
In-Reply-To: <200511281359.04741.Josef.Weidendorfer@gmx.de>

Josef Weidendorfer <Josef.Weidendorfer@gmx.de> writes:

>> >  - we will add new ones to config, now we have it;
>
> It is not really fitting for all purposes (eg. syntax restrictions).

OK; please read it as "when able".

^ permalink raw reply

* Re: [PATCH] Fix typo in http-push.c
From: Junio C Hamano @ 2005-11-29  6:03 UTC (permalink / raw)
  To: Jan Andres; +Cc: git
In-Reply-To: <20051129005154.GA3076@pitr.home.jan>

Jan Andres <jandres@gmx.net> writes:

> Please find below the patch for a typo in http-push.c (in the maint
> branch), which caused git-http-push to segfault on my Linux i386 box.

Thanks.  Next time around please sign-off your patches (see
Documentation/SubmittingPatches).

^ permalink raw reply

* Re: [PATCH] Re: keeping remote repo checked out?
From: Daniel Barkalow @ 2005-11-29  6:02 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Petr Baudis, Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.64.0511282029290.3177@g5.osdl.org>

On Mon, 28 Nov 2005, Linus Torvalds wrote:

> On Mon, 28 Nov 2005, Daniel Barkalow wrote:
> > 
> > I was planning to keep it in .git (instead of .git/refs/...), because it's 
> > information about the working tree, not properly part of the repository. 
> > It's like MERGE_HEAD in being about what the user is in the middle of.
> 
> That's fine.
> 
> However, your patch isn't.
> 
> Your patch just creates this eternal confusion of "why is HEAD different 
> from CHECKED_OUT". 
> 
> It's really easy to do what you want to do _without_ any tool changes:
> 
> 	git-rev-parse HEAD > .git/CHECKED_OUT
> 	rm .git/HEAD
> 	ln -s refs/heads/../../CHECKED_OUT .git/HEAD
> 
> and you're done. No tool changes necessary.
> 
> Now "HEAD" always points to the checked-out thing, but a "git clone" won't 
> ever actually clone your CHECKED_OUT branch.

But CHECKED_OUT isn't a branch. The whole point of it is that you stay on 
some real branch, so you can actually use the tools to affect the 
repository (i.e., the things under refs/). The reason to keep CHECKED_OUT 
is so that you can tell if the branch you're on changes out from under 
you.

For example, I want to have a repository where I push into the 
"production" branch, and I want to be able to get these changes in the 
working tree that the repository is in. If I have HEAD point to 
refs/heads/production, git doesn't know any more what's in the working 
tree, so it can't do the two-way merge. If I have HEAD point to 
CHECKED_OUT, git doesn't know any more what I want to get, so it can't do 
the two-way merge. That is, what I want to have work is:

  server$ git checkout production
  work$ git push server:production
  server$ git checkout

and this should leave the working tree on server with the changes pushed 
from work. Currently, I have to make sure server *isn't* tracking the 
branch I care about when the push happens, so that it doesn't lose track 
of its state, and then switch to the branch to get the changes, and copy 
the branch to a new branch, and switch to that so the next branch won't 
mess up the state.

HEAD is a symbolic ref to the ref in the repository that is the current 
branch. CHECKED_OUT is a ref to the hash that's in the working tree. 
Usually they match, because usually nothing is changing current branch 
without changing the working tree (or making the current branch match 
changes in the working tree), but the point of the patch is to respond 
sensibly to cases where this stops being true, because those can actually 
be part of desirable usage patterns.

	-Daniel
*This .sig left intentionally blank*

^ permalink raw reply

* Re: git-name-rev off-by-one bug
From: Junio C Hamano @ 2005-11-29  5:54 UTC (permalink / raw)
  To: linux; +Cc: git
In-Reply-To: <20051128234256.1508.qmail@science.horizon.com>

linux@horizon.com writes:

> (Being able to back up the object database is obviously simple, but what
> happens if the index holds HEAD+1, the working directory holds HEAD+2,
> and I try to mere the latest changes from origin?  Are either HEAD+1 or
> HEAD+2 in danger of being lost, or will checking them in later overwrite
> the merge, or what?)

Thanks for the complaints.  No sarcasm intended.  Yours is
exactly the kind of message we (people who've been around here
for too long) need to hear.

Although the technical details are hidden in the documentation
which needs reorganization to make them easier to find [*1*] as
you point out, the guiding principle for merge is quite simple.

To the git barebone Porcelain layer (things that start with
git-*, not with cg-*) [*2*], a merge is always between the
current HEAD and one or more remote branch heads, and the index
file must exactly match the tree of HEAD commit (i.e. the
contents of the last commit) when it happens.  In other words,
"git-diff --cached HEAD" must report no changes [*3*].  So
HEAD+1 must be HEAD in your above notation, or merge will refuse
to do any harm to your repository (that is, it may fetch the
objects from remote, and it may even update the local branch
used to keep track of the remote branch with "git pull remote
rbranch:lbranch", but your working tree, .git/HEAD pointer and
index file are left intact).

You may have local modifications in the working tree files.  In
other words, "git-diff" is allowed to report changes (the
difference between HEAD+2 and HEAD+1 in your notation).
However, the merge uses your working tree as the working area,
and in order to prevent the merge operation from losing such
changes, it makes sure that they do not interfere with the
merge. Those complex tables in read-tree documentation define
what it means for a path to "interfere with the merge".  And if
your local modifications interfere with the merge, again, it
stops before touching anything.

So in the above two "failed merge" case, you do not have to
worry about lossage of data --- you simply were not ready to do
a merge, so no merge happened at all.  You may want to finish
whatever you were in the middle of doing, and retry the same
pull after you are done and ready.

When things cleanly merge, these things happen:

 (1) the results are updated both in the index file and in your
     working tree,
 (2) index file is written out as a tree,
 (3) the tree gets committed, and 
 (4) the HEAD pointer gets advanced.

Because of (2), we require that the original state of the index
file to match exactly the current HEAD commit; otherwise we will
write out your local changes already registered in your index
file (the difference between HEAD+1 and HEAD in your notation)
along with the merge result, which is not good.  Because (1)
involves only the paths different between your branch and the
remote branch you are pulling from during the merge (which is
typically a fraction of the whole tree), you can have local
modifications in your working tree as long as they do not
overlap with what the merge updates.

When there are conflicts, these things happen:

 (0) HEAD stays the same.

 (1) Cleanly merged paths are updated both in the index file and
     in your working tree.

 (2) For conflicting paths, the index file records the version
     from HEAD. The working tree files have the result of
     "merge" program; i.e. 3-way merge result with familiar
     conflict markers <<< === >>>.

 (3) No other changes are done.  In particular, the local
     modifications you had before you started merge will stay the
     same and the index entries for them stay as they were,
     i.e. matching HEAD.

After seeing a conflict, you can do two things:

 * Decide not to merge.  The only clean-up you need are to reset
   the index file to the HEAD commit to reverse (1) and to clean
   up working tree changes made by (1) and (2); "git-reset" can
   be used for this.

 * Resolve the conflicts.  "git-diff" would report only the
   conflicting paths because of the above (1) and (2).  Edit the
   working tree files into a desirable shape, git-update-index
   them, to make the index file contain what the merge result
   should be, and run "git-commit" to commit the result.


[Footnotes]

*1* It is a shame that the most comprehensive definition of
3-way read-tree semantics is in t/t1000-read-tree-m-3way.sh test
script.

*2* Cogito (things that start with cg-*) seems to try to be
cleverer.  Pasky might want to brag about the rules in Cogito
land.

*3* This is a bit of lie.  In certain special cases, your index
are allowed to be different from the tree of HEAD commit;
basically your index entries are allowed to match the result of
trivial merge already (e.g. you received the same patch from
external source to produce the same result as what you are
merging).  For example, if a path did not exist in the common
ancestor and your head commit but exists in the tree you are
merging into your repository, and if you already happen to have
that path exactly in your index, the merge does not have to
fail.  This is case #2 in the 3-way read-tree table in t/t1000.

^ permalink raw reply

* Re: [PATCH] Re: keeping remote repo checked out?
From: Linus Torvalds @ 2005-11-29  4:34 UTC (permalink / raw)
  To: Daniel Barkalow; +Cc: Petr Baudis, Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.64.0511282208050.25300@iabervon.org>



On Mon, 28 Nov 2005, Daniel Barkalow wrote:
> 
> I was planning to keep it in .git (instead of .git/refs/...), because it's 
> information about the working tree, not properly part of the repository. 
> It's like MERGE_HEAD in being about what the user is in the middle of.

That's fine.

However, your patch isn't.

Your patch just creates this eternal confusion of "why is HEAD different 
from CHECKED_OUT". 

It's really easy to do what you want to do _without_ any tool changes:

	git-rev-parse HEAD > .git/CHECKED_OUT
	rm .git/HEAD
	ln -s refs/heads/../../CHECKED_OUT .git/HEAD

and you're done. No tool changes necessary.

Now "HEAD" always points to the checked-out thing, but a "git clone" won't 
ever actually clone your CHECKED_OUT branch.

		Linus

^ permalink raw reply

* git-name-rev off-by-one bug
From: linux @ 2005-11-28 23:42 UTC (permalink / raw)
  To: git; +Cc: linux

I've been trying to wrap my head around git for a while now, and finding
things a bit confusing.  Basically, the reason that I'm scared to trust
it with my code is that all sharing is done via push and pull, and they
are done by merging, and merging isn't described very well anywhere.

There's lots of intimate *detail* of merge algorithms (hiding in, of all
places, the git-read-tree documentation, which is not the obvious place
for a beginner to look), but the important high-level questions like "what
happens to all my hard work if there's a merge conflict?" or "what if I
forget to git-update-index before doing the merge?" are not really clear.
I don't like to go ahead if I'm not confident I can get back.

(Being able to back up the object database is obviously simple, but what
happens if the index holds HEAD+1, the working directory holds HEAD+2,
and I try to mere the latest changes from origin?  Are either HEAD+1 or
HEAD+2 in danger of being lost, or will checking them in later overwrite
the merge, or what?)

Anyway, I'm doing some experiments and trying to understand it, and writing
what I learn as I go, which will hopefully be useful to someone.


Another very confusing thing is the ref syntax with all those ~12^3^22^2
suffixes.  The git tutorial uses "master^" and "master^2" syntax, but
doesn't actually explain it.

The meaning can be found on the second page of the git-rev-parse manual.
If, that is, you think to read that man page, and if you don't stop
reading after the first page tells you that it's a helper for scripts
not meant to be invoked directly by the end-user.

Trying to see if I understood what was going on, I picked a random rev out of
git-show-branch output and tried git-name-rev:

> $ git-name-rev 365a00a3f280f8697e4735e1ac5b42a1c50f7887
> 365a00a3f280f8697e4735e1ac5b42a1c50f7887 maint~404^1~7

(If you care, maint=93dcab2937624ebb97f91807576cddb242a55a46)

And was very confused when git-rev-parse didn't invert the operation:

> $ git-rev-parse maint~404^1~7
> f69714c38c6f3296a4bfba0d057e0f1605373f49

I spent a while verifying that I understood that ^1 == ^ == ~1, so
~404^1~7 = ~412, and that gave the same unwanted result:

> $ git-rev-parse maint~412
> f69714c38c6f3296a4bfba0d057e0f1605373f49

After confusing myself for a while, I looked to see why git-name-rev
would output such a redundant name and found that it was simply
wrong.  Fixing the symbolic name worked:

> $ git-rev-parse maint~404^2~7
> 365a00a3f280f8697e4735e1ac5b42a1c50f7887

You can either go with a minimal fix:
diff --git a/name-rev.c b/name-rev.c
index 7d89401..f7fa18c 100644
--- a/name-rev.c
+++ b/name-rev.c
@@ -61,9 +61,10 @@ copy_data:
 
 			if (generation > 0)
 				sprintf(new_name, "%s~%d^%d", tip_name,
-						generation, parent_number);
+						generation, parent_number+1);
 			else
-				sprintf(new_name, "%s^%d", tip_name, parent_number);
+				sprintf(new_name, "%s^%d", tip_name,
+						parent_number+1);
 
 			name_rev(parents->item, new_name,
 				merge_traversals + 1 , 0, 0);


Or you can get a bit more ambitious and write ~1 as ^:

diff --git a/name-rev.c b/name-rev.c
index 7d89401..82053c8 100644
--- a/name-rev.c
+++ b/name-rev.c
@@ -57,13 +57,17 @@ copy_data:
 			parents;
 			parents = parents->next, parent_number++) {
 		if (parent_number > 0) {
-			char *new_name = xmalloc(strlen(tip_name)+8);
+			unsigned const len = strlen(tip_name);
+			char *new_name = xmalloc(len+8);
 
-			if (generation > 0)
-				sprintf(new_name, "%s~%d^%d", tip_name,
-						generation, parent_number);
-			else
-				sprintf(new_name, "%s^%d", tip_name, parent_number);
+			memcpy(new_name, tip_name, len);
+
+			if (generation == 1)
+				new_name[len++] = '^';
+			else if (generation > 1)
+				len += sprintf(new_name+len, "~%d", generation);
+
+			sprintf(new_name+len, "^%d", parent_number+1);
 
 			name_rev(parents->item, new_name,
 				merge_traversals + 1 , 0, 0);


While I'm at it, I notice some unnecessary invocations of expr in some
of the shell scripts.  You can do it far more simply using the ${var#pat}
and ${var%pat} expansions to strip off leading and trailing patterns.
For example:

diff --git a/git-cherry.sh b/git-cherry.sh
index 867522b..c653a6a 100755
--- a/git-cherry.sh
+++ b/git-cherry.sh
@@ -23,8 +23,7 @@ case "$1" in -v) verbose=t; shift ;; esa
 
 case "$#,$1" in
 1,*..*)
-    upstream=$(expr "$1" : '\(.*\)\.\.') ours=$(expr "$1" : '.*\.\.\(.*\)$')
-    set x "$upstream" "$ours"
+    set x "${1%..*}" "${1#*..}"
     shift ;;
 esac
 
This works in dash and is in the POSIX spec.  It doesn't work in some
very old /bin/sh implementations (such as Solaris still ships), but I'm
pretty sure it was introduced at the same time as $(), and the scripts
use *that* all over the place.

% sh
$ uname -s -r
SunOS 5.9
$ foo=bar
$ echo ${foo#b}
bad substitution
$ echo `echo $foo`
bar
$ echo $(echo $foo)
syntax error: `(' unexpected

Anyway, if it's portable enough, it's faster.  Ah... I just found discussion
of this in late September, but it's not clear what the resolution was.
http://marc.theaimsgroup.com/?t=112746188000003


(Oh, yes: all of the above patches are released into the public domain.
Copyright abandoned.  Have fun.)

^ permalink raw reply related

* [PATCH] Re: keeping remote repo checked out?
From: Daniel Barkalow @ 2005-11-29  3:40 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Petr Baudis, Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.64.0511281837040.3177@g5.osdl.org>

On Mon, 28 Nov 2005, Linus Torvalds wrote:

> On Mon, 28 Nov 2005, Daniel Barkalow wrote:
> > > 
> > > but if you use a special git branch for the checked-out state, then you 
> > > have to realize that nobody can push directly to it.
> > 
> > The idea is to have something which is private, to match the working tree, 
> > which is obviously private (because it doesn't even exist from the point 
> > of view of the database).
> 
> Sure. It's private because it has its own name, and you can make _sure_ 
> that nobody tries to push to it by having a trigger that refuses to update 
> that branch.
> 
> Now, if you want to make it also be _invisible_ (so that when others don't 
> clone, they don't see what the checked-out state was when they cloned), 
> you'd have to make some new rules to git. We could have rules like "branch 
> names that start with '.' aren't listed", which would effectively do 
> exactly that..

I was planning to keep it in .git (instead of .git/refs/...), because it's 
information about the working tree, not properly part of the repository. 
It's like MERGE_HEAD in being about what the user is in the middle of.

I actually have a bit of a patch now, which only updates checkout and 
commit (anything that updates HEAD should also update CHECKED_OUT, and 
some things probably ought to read CHECKED_OUT instead of HEAD), but 
should serve to give an idea of what I mean. Essentially, if somebody's 
messed with your HEAD, than "git checkout" will update to that, and "git 
commit" will give an error message until you do (I added a check so it 
happens before you write a message, but the third argument to 
"git-update-ref HEAD" would also complain).

Obviously, not for application, since it breaks things like "git resolve" 
which are supposed to change what your working tree contains and aren't 
updated to make CHECKED_OUT match.

---

Keep a private copy of the commit whose tree corresponds to the
git-provided state of the working tree, in case somebody changes the
branch we're on behind our backs.

Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>

---

 git-checkout.sh |    5 ++++-
 git-commit.sh   |   10 +++++++++-
 2 files changed, 13 insertions(+), 2 deletions(-)

applies-to: fe523a4df93cce3e5c5b0266b9d3f1cbea009afa
8831e2595f29c90a259586250d5c6d044e9d7c8c
diff --git a/git-checkout.sh b/git-checkout.sh
index 4cf30e2..503b34f 100755
--- a/git-checkout.sh
+++ b/git-checkout.sh
@@ -5,7 +5,8 @@ usage () {
     die "usage: git checkout [-f] [-b <new_branch>] [<branch>] [<paths>...]"
 }
 
-old=$(git-rev-parse HEAD)
+old=$(git-rev-parse --revs-only CHECKED_OUT)
+[ -z $old ] && old=$(git-rev-parse HEAD)
 new=
 force=
 branch=
@@ -117,6 +118,8 @@ else
     git-read-tree -m -u $old $new
 fi
 
+git-update-ref CHECKED_OUT $new
+
 # 
 # Switch the HEAD pointer to the new branch if it we
 # checked out a branch head, and remove any potential
diff --git a/git-commit.sh b/git-commit.sh
index 3d250ec..e73003b 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -108,6 +108,13 @@ t,*)
 esac || exit 1
 git-update-index -q --refresh || exit 1
 
+current=$(git-rev-parse --revs-only CHECKED_OUT)
+if test ! -z "$current" -a "$current" != "$(git-rev-parse HEAD)"
+then
+	echo "Changes have been pushed to this branch since you checked it out"
+	exit 1
+fi
+
 case "$verify" in
 t)
 	if test -x "$GIT_DIR"/hooks/pre-commit
@@ -232,7 +239,8 @@ then
 	tree=$(git-write-tree) &&
 	commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) &&
 	git-update-ref HEAD $commit $current &&
-	rm -f -- "$GIT_DIR/MERGE_HEAD"
+	rm -f -- "$GIT_DIR/MERGE_HEAD" &&
+	git-update-ref CHECKED_OUT $commit
 else
 	echo >&2 "* no commit message?  aborting commit."
 	false
---
0.99.9.GIT

^ permalink raw reply related

* Re: keeping remote repo checked out?
From: Linus Torvalds @ 2005-11-29  2:39 UTC (permalink / raw)
  To: Daniel Barkalow; +Cc: Petr Baudis, Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.64.0511282027360.25300@iabervon.org>



On Mon, 28 Nov 2005, Daniel Barkalow wrote:
> > 
> > but if you use a special git branch for the checked-out state, then you 
> > have to realize that nobody can push directly to it.
> 
> The idea is to have something which is private, to match the working tree, 
> which is obviously private (because it doesn't even exist from the point 
> of view of the database).

Sure. It's private because it has its own name, and you can make _sure_ 
that nobody tries to push to it by having a trigger that refuses to update 
that branch.

Now, if you want to make it also be _invisible_ (so that when others don't 
clone, they don't see what the checked-out state was when they cloned), 
you'd have to make some new rules to git. We could have rules like "branch 
names that start with '.' aren't listed", which would effectively do 
exactly that..

			Linus

^ permalink raw reply

* Re: keeping remote repo checked out?
From: Daniel Barkalow @ 2005-11-29  2:35 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Petr Baudis, Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.64.0511281637480.3177@g5.osdl.org>

On Mon, 28 Nov 2005, Linus Torvalds wrote:

> On Mon, 28 Nov 2005, Daniel Barkalow wrote:
> > 
> > We really ought to keep track of what the current checked out tree is, 
> > independant of the head that it's supposed to match. Then it would be safe 
> > to push to a head that's checked out (it wouldn't update it, but the 
> > system would understand what's going on).
> 
> Well, that's what branches are all about. 
> 
> You really have two choices:
>  - do it all yourself
>  - use git branches
> 
> but if you use a special git branch for the checked-out state, then you 
> have to realize that nobody can push directly to it.

The idea is to have something which is private, to match the working tree, 
which is obviously private (because it doesn't even exist from the point 
of view of the database). Of course, you don't want most of the branch 
mechanism, with HEAD pointing to a file, because when you commit, you want 
to update something else, and when you check out, you want to check out 
something else. You want to use a normal git branch, but still have git 
keep track of what it thinks should be in your working tree, regardless of 
what the branch you're on does, so that checkout doesn't get confused if 
somebody's pushed to the branch you're on.

Then, if someone pushes to a branch that's checked out, git doesn't lose 
track of what it put in the working tree, so it can handle the "git 
checkout" that you do afterwards to update the working tree. Making 
everything work okay if multiple "git checkout"s happen at the same time 
is future work, and will definitely involve locking.

	-Daniel
*This .sig left intentionally blank*

^ 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