* Re: Status of Mac OS/X ports of git and cogito?
From: Junio C Hamano @ 2005-08-27 3:50 UTC (permalink / raw)
To: John Ellson; +Cc: git
In-Reply-To: <deofnh$jl0$1@sea.gmane.org>
John Ellson <ellson@research.att.com> writes:
> Do git and cogito build easily on Mac OS/X now? Are there
> binaries available anywhere?
Not that I know of. We used to get portability patches from
Darwin folks, but I haven't seen any lately.
I am somewhat interested in what portability glitches we still
have, but not having an access to a machine, it is more of a
curiosity rather than a necessity for me.
Are Darwin folks on the list happy with the current codebase, or
have you given up because it is too GNU/Linux specific?
^ permalink raw reply
* Comments in read-tree about #nALT
From: Daniel Barkalow @ 2005-08-27 6:07 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
I've gotten to the point of having all of the entries for a given path
ready to put into the cache at the same, and now I want to convert the
merge functions to take their data directly, rather than in the cache, so
that they can take extra entries for extra ancestors.
Part of threeway_merge, however, wants to search the rest of the cache for
interfering entries in some cases, which would have to happen differently,
because I won't have the cache completely filled out beforehand. I'm
trying to figure out what the comments are talking about, and they seem to
refer to a list of the possible cases. Is that list somewhere convenient?
-Daniel
*This .sig left intentionally blank*
^ permalink raw reply
* Re: Merges without bases
From: Martin Langhoff @ 2005-08-27 6:48 UTC (permalink / raw)
To: Daniel Barkalow; +Cc: Junio C Hamano, Darrin Thompson, git
In-Reply-To: <Pine.LNX.4.63.0508261150320.23242@iabervon.org>
On 8/27/05, Daniel Barkalow <barkalow@iabervon.org> wrote:
> The problem with both of these (and doing it in the build system) is that,
> when a project includes another project, you generally don't want whatever
> revision of the included project happens to be the latest; you want the
> revision of the included project that the revision of the including
> project you're looking at matches. That is, if App includes Lib, and
Exactly - so you do it on a tag, or a commit date with cvs. With Arch,
GIT and others that have a stable id for each commit, you can use that
or the more user-friendly tags.
The project pulling the libs has the makefile, and the makefile says
'pull library-foo revision xxx'. If a later revision yyy is known to
work well, you update the makefile and commit it. Perfectly version
controlled, no need for special purpose machinery ;)
The good thing here is that a makefile will know how to handle the
situation if the external lib is hosted in Arch, in SVN, or Visual
SourceSafe. If your external lib is only available as a tarball in a
url, you can fetch that and uncompress it too. Arch configurations are
_cute_ but useless in any but the most narrow cases.
I want my SCM to be a good SCM, but this kind of interop is better
left to general purpose languages. Letting the build system do it
seems to be 'best-practice' and the right thing to do.
cheers,
martin
^ permalink raw reply
* Re: Status of Mac OS/X ports of git and cogito?
From: Martin Langhoff @ 2005-08-27 6:59 UTC (permalink / raw)
To: John Ellson; +Cc: git
In-Reply-To: <deofnh$jl0$1@sea.gmane.org>
I am running git/cogito on MacOSX 10.3.x mostly as a client, and it
works pretty well. My main dev machines are Debian boxes, so the OSX
build is most often just fetching commits and running cg-log. So it's
not very intensive ;)
I get all my build dependencies from Fink, and also get diffutils from
Fink, as the diff included in MacOSX is old/buggy. The one in Fink's
diffutils matches GIT's expectations ;)
There is _one_ bug I've seen with cg-diff and I haven't had time to
fix. On OSX, running cg-diff with no parameters doesn't show anything.
For some reason, it fails to list the files. If you give it the paths
explicitly, it'll give you the appropriate diff.
And there is a problem with gitk too: the tk that I get from fink gets
the panels sizes all wrong for gitk. I doubt this is a bug in gitk
though.
cheers,
martin
On 8/27/05, John Ellson <ellson@research.att.com> wrote:
> Do git and cogito build easily on Mac OS/X now? Are there binaries available
> anywhere?
>
>
> John
>
> -
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* Re: Comments in read-tree about #nALT
From: Junio C Hamano @ 2005-08-27 7:08 UTC (permalink / raw)
To: Daniel Barkalow; +Cc: git
In-Reply-To: <Pine.LNX.4.63.0508270151590.23242@iabervon.org>
Daniel Barkalow <barkalow@iabervon.org> writes:
> Part of threeway_merge, however, wants to search the rest of the cache for
> interfering entries in some cases, which would have to happen differently,
> because I won't have the cache completely filled out beforehand. I'm
> trying to figure out what the comments are talking about, and they seem to
> refer to a list of the possible cases. Is that list somewhere convenient?
Please look for END_OF_CASE_TABLE in t/t1000-read-tree-m-3way.sh;
the table talks about some of the (ALT) not implemented, but
some of them are ("git whatchanged t/t1000-read-tree-m-3way"
would tell you which).
Two way cases are described in Documentation/git-read-tree.txt,
if you care. If you were not touching the three-way case right
now, I'd move/copy the three way cases there as well, but that
can wait until after your changes.
^ permalink raw reply
* [PATCH] git-rev-list: Don't die on a bad tag
From: Frank Sorenson @ 2005-08-27 8:17 UTC (permalink / raw)
To: Git Mailing List, Junio C Hamano
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
The current cogito tree contains an invalid tag
(junio-gpg-pub => 0918385dbd9656cab0d1d81ba7453d49bbc16250), and
git-repack-script (and probably others) will die when git-rev-list
tries to dump the objects. This patch changes it to complain, but
continue.
Signed-off-by: Frank Sorenson <frank@tuxrocks.com>
diff --git a/rev-list.c b/rev-list.c
- --- a/rev-list.c
+++ b/rev-list.c
@@ -418,8 +418,10 @@ static struct commit *get_commit_referen
if (tag_objects && !(object->flags & UNINTERESTING))
add_pending_object(object, tag->tag);
object = parse_object(tag->tagged->sha1);
- - if (!object)
- - die("bad object %s", sha1_to_hex(tag->tagged->sha1));
+ if (!object) {
+ fprintf(stderr, "Bad tag: %s\n", sha1_to_hex(tag->tagged->sha1));
+ return NULL;
+ }
}
/*
Frank
- --
Frank Sorenson - KD7TZK
Systems Manager, Computer Science Department
Brigham Young University
frank@tuxrocks.com
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org
iD8DBQFDECF9aI0dwg4A47wRAncdAKCIcNFzihLm9cy7RWRKakM6INikigCfcCvu
R/XG+XlMsbgWesEvZcFBmQY=
=OY7G
-----END PGP SIGNATURE-----
^ permalink raw reply
* [PATCH] git-repack-script: Add option to repack all objects
From: Frank Sorenson @ 2005-08-27 8:41 UTC (permalink / raw)
To: Git Mailing List, Junio C Hamano
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
This patch adds an option to git-repack-script to repack all objects,
including both packed and unpacked. This allows a full repack of
a git archive (current cogito packs from 39MB to 4.5MB, and git packs
from 4.4MB to 3.8MB).
Signed-off-by: Frank Sorenson <frank@tuxrocks.com>
diff --git a/git-repack-script b/git-repack-script
- --- a/git-repack-script
+++ b/git-repack-script
@@ -5,10 +5,12 @@
. git-sh-setup-script || die "Not a git archive"
+repack_all=
no_update_info=
while case "$#" in 0) break ;; esac
do
case "$1" in
+ --all) repack_all=t ;;
-n) no_update_info=t ;;
*) break ;;
esac
@@ -16,13 +18,22 @@ do
done
rm -f .tmp-pack-*
- -packname=$(git-rev-list --unpacked --objects $(git-rev-parse --all) |
- - git-pack-objects --non-empty --incremental .tmp-pack) ||
- - exit 1
- -if [ -z "$packname" ]; then
- - echo Nothing new to pack
- - exit 0
- -fi
+case "$repack_all" in
+t) packname=$(git-rev-list --objects $(git-rev-parse --all) |
+ git-pack-objects .tmp-pack) ||
+ exit 1
+ find "$GIT_OBJECT_DIRECTORY/"?? -type f | xargs rm -f
+ find "$GIT_OBJECT_DIRECTORY/pack" -type f | xargs rm -f
+ ;;
+*) packname=$(git-rev-list --unpacked --objects $(git-rev-parse --all) |
+ git-pack-objects --non-empty --incremental .tmp-pack) ||
+ exit 1
+ if [ -z "$packname" ]; then
+ echo Nothing new to pack
+ exit 0
+ fi
+ ;;
+esac
mkdir -p "$GIT_OBJECT_DIRECTORY/pack" &&
mv .tmp-pack-$packname.pack "$GIT_OBJECT_DIRECTORY/pack/pack-$packname.pack" &&
Frank
- --
Frank Sorenson - KD7TZK
Systems Manager, Computer Science Department
Brigham Young University
frank@tuxrocks.com
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org
iD8DBQFDECcnaI0dwg4A47wRAsigAKDEItbKTKAeO+PO8VV0dtMvFl0qfgCffyDc
hL0nAUB0HxeDlDoh9fv2m4o=
=r4gM
-----END PGP SIGNATURE-----
^ permalink raw reply
* Re: Comments in read-tree about #nALT
From: Junio C Hamano @ 2005-08-27 8:59 UTC (permalink / raw)
To: Daniel Barkalow; +Cc: git
In-Reply-To: <7vwtm726xq.fsf@assigned-by-dhcp.cox.net>
Junio C Hamano <junkio@cox.net> writes:
> Daniel Barkalow <barkalow@iabervon.org> writes:
>
>> Part of threeway_merge, however, wants to search the rest of the cache for
>> interfering entries in some cases, which would have to happen differently,
>> because I won't have the cache completely filled out beforehand. I'm
>> trying to figure out what the comments are talking about, and they seem to
>> refer to a list of the possible cases. Is that list somewhere convenient?
>
> Please look for END_OF_CASE_TABLE in t/t1000-read-tree-m-3way.sh;
> the table talks about some of the (ALT) not implemented, but
> some of them are ("git whatchanged t/t1000-read-tree-m-3way"
> would tell you which).
>
> Two way cases are described in Documentation/git-read-tree.txt,
> if you care. If you were not touching the three-way case right
> now, I'd move/copy the three way cases there as well, but that
> can wait until after your changes.
The comments like /* 4 and 5 */ in twoway_merge refer to the
case table number in the Documentation/git-read-tree.txt
Just in case you are wondering what all this index constraints
are about. It used to be that the order of stages given to
"read-tree -m O A B" did not have any inherent meaning, but it
is not anymore, especially after the "-u" flag was introduced
and the "new world order" was introduced to the merge operation.
Before that, we tried to do everything in the index file, and
even merge-one-file-script tried not to touch the files in the
working tree at all while it worked. But that has changed, and
these days I believe the code assumes that A corresponds to the
commit the current working tree is based upon, and tries to make
sure that the index file does not have a local modification that
would be lost by the result of "read-tree -m -u O A B" (that is,
roughly speaking, read-tree chose B and the working tree was
modified since A).
If you need more information, probably the first thing to do
would be to run "git whatchanged" on read-tree, its tests and
documentations to find the rough timescale of those "new world
order" changes, and check the list archives.
As a software archaeologist, I've done some of that for you and
for myself. I hope these would help you.
Gee, up until now I thought my only significant contribution was
around diff, but going through 5000+ messages made me realize I
was all over the place ;-).
------------------------------------------------
This message talked about Linus' desire not to touch working
tree at all.
Date: Sun, 1 May 2005 13:29:59 -0700 (PDT)
From: Linus Torvalds <torvalds@osdl.org>
Subject: Re: [PATCH] Really fix git-merge-one-file-script this time.
Message-ID: <Pine.LNX.4.58.0505011312080.2296@ppc970.osdl.org>
This was a question to Linus after the "new merge world order"
happened.
Message-ID: <7v64wrvpt4.fsf@assigned-by-dhcp.cox.net>
From: Junio C Hamano <junkio@cox.net>
Subject: new read-tree questions.
Date: Mon, 06 Jun 2005 01:43:03 -0700
And these three discusses the issues. The first two are the
must-read responses from Linus.
Message-ID: <Pine.LNX.4.58.0506060748390.1876@ppc970.osdl.org>
From: Linus Torvalds <torvalds@osdl.org>
Subject: Re: new read-tree questions.
Date: Mon, 6 Jun 2005 08:04:22 -0700 (PDT)
Message-ID: <Pine.LNX.4.58.0506060738170.1876@ppc970.osdl.org>
From: Linus Torvalds <torvalds@osdl.org>
Subject: Re: Last mile for 1.0
Date: Mon, 6 Jun 2005 07:47:52 -0700 (PDT)
Message-ID: <7voeajp875.fsf@assigned-by-dhcp.cox.net>
From: Junio C Hamano <junkio@cox.net>
Subject: Re: clarifying two tree merge semantics
Date: Mon, 06 Jun 2005 12:59:58 -0700
The thread starting at this message is about emu23, which may
not affect directly what you are doing, but if you are
interesting the issues there are interesting.
Message-ID: <Pine.LNX.4.58.0506081336080.2286@ppc970.osdl.org>
From: Linus Torvalds <torvalds@osdl.org>
Subject: Handling merge conflicts a bit more gracefully..
Date: Wed, 8 Jun 2005 13:55:23 -0700 (PDT)
I've reproduced the whole message for this one, which is what
your question was about.
Message-ID: <7voeagrp11.fsf_-_@assigned-by-dhcp.cox.net>
From: Junio C Hamano <junkio@cox.net>
Subject: [PATCH 0/3] Handling merge conflicts a bit more gracefully
Date: Thu, 09 Jun 2005 00:02:34 -0700
This series consists of three patches.
[PATCH 1/3] read-tree.c: rename local variables used in 3-way merge code.
[PATCH 2/3] read-tree -m 3-way: loosen index requirements that is too strict.
[PATCH 3/3] read-tree -m 3-way: handle more trivial merges internally
You may have noticed that I already described some "alternative
semantics" in the 3-way merge test script t1000. This set of
patches implements some of them, namely the following 5 cases:
O A B result index requirements
-------------------------------------------------------------------
5 missing exists A==B take A must match A, if exists.
------------------------------------------------------------------
6 exists missing missing remove must not exist.
------------------------------------------------------------------
8 exists missing O==B remove must not exist.
------------------------------------------------------------------
10 exists O==A missing remove must match A and be
up-to-date, if exists.
------------------------------------------------------------------
14 exists O==A O!=B take B if exists, must either (1)
match A and be up-to-date,
or (2) match B.
-------------------------------------------------------------------
The first patch is to match the local variable names used in the
functions involved to the names used in the case matrix.
Case #14 is resolved identically as the old code does, but the
index requirement old code placed on this case was stricter than
necessary. In this case, satisfying the usual rule of "match A
and be up-to-date if exists" is certainly OK, but additionally,
if the original index matches the tree being merged (without
even being up-to-date) is also permissible, because there would
be no information loss or work-tree clobbering if we allowed it.
The second patch in the series corrects this.
Case #5, #6, #8, and #10 were traditionally kept unmerged in the
index file when read-tree is done, and resolving them was left
to the script. By resolving these internally, we can loosen the
index requirements without compromising correctness for case #5.
Other three cases could still be left for the "script policy"
because this change does not affect the index requirements for
these cases, but it was simple enough to implement them and this
would not be too controversial a change. The third patch in the
series implements these changes.
^ permalink raw reply
* Re: [PATCH] git-rev-list: Don't die on a bad tag
From: Junio C Hamano @ 2005-08-27 9:14 UTC (permalink / raw)
To: Frank Sorenson; +Cc: Git Mailing List
In-Reply-To: <4310217D.80408@tuxrocks.com>
Frank Sorenson <frank@tuxrocks.com> writes:
> The current cogito tree contains an invalid tag
> (junio-gpg-pub => 0918385dbd9656cab0d1d81ba7453d49bbc16250), and
> git-repack-script (and probably others) will die when git-rev-list
> tries to dump the objects. This patch changes it to complain, but
> continue.
>
> Signed-off-by: Frank Sorenson <frank@tuxrocks.com>
I think this patch is wrong.
The check you are disabling is to see if the repository contains
an object that a tag refers to, and the user wanted to slurp the
tag, along with whatever thing it refers to, so barfing and
dying is the only right thing to do.
The repository does not have the object the tag points at. IOW,
the repository is not fsck clean.
If it bothers you either you could drop that tag from cogito
repository (because there would not be anything that is signed
by _my_ public key anyway), or borrow the object from git.git
repository.
^ permalink raw reply
* cvsimport error: need a valid pathname
From: Kalle Valo @ 2005-08-27 14:48 UTC (permalink / raw)
To: git
I have already imported a full CVS repository using this command:
git cvsimport -v -d :pserver:anonymous@hostap.epitest.fi:/cvs hostap
It completed without errors and I could use git to access the hostap
history, just like I wanted. But now, when I was trying to get updates
from the CVS repository, I got an error running the command in the
same directory:
$ git cvsimport -v -d :pserver:anonymous@hostap.epitest.fi:/cvs hostap
fileparse(): need a valid pathname at /usr/bin/git-cvsimport-script line 435
$
The documentation says that it should be possible to update
incrementally from the CVS repository. Am I doing something wrong or
is this a bug?
I'm using git updated today from the master branch on Debian unstable.
--
Kalle Valo
^ permalink raw reply
* Re: cvsimport error: need a valid pathname
From: Martin Langhoff @ 2005-08-27 15:37 UTC (permalink / raw)
To: Kalle Valo; +Cc: git
In-Reply-To: <874q9bcu6d.fsf@litku.valo.iki.fi>
On 8/28/05, Kalle Valo <Kalle.Valo@iki.fi> wrote:
> The documentation says that it should be possible to update
> incrementally from the CVS repository. Am I doing something wrong or
> is this a bug?
It _should_ work the way you are running it, so consider it a bug. Do
you think you can do some tinkering/debugging to tell us some more?
Otherwise. how large is the cvs repo?
(Debugging this kind of stuff, when it only fails on a specific repo,
is tricky and time-consuming. Any help we get is _really_ appreciated.
I have imported many trees, and several of them I am tracking -
importing new commits on a daily basis - without much trouble, except
for invalid/force-moved tags.)
cheers,
martin
^ permalink raw reply
* Re: cvsimport error: need a valid pathname
From: Kalle Valo @ 2005-08-27 17:44 UTC (permalink / raw)
To: Martin Langhoff; +Cc: git
In-Reply-To: <46a038f905082708371719121c@mail.gmail.com>
Martin Langhoff <martin.langhoff@gmail.com> writes:
> On 8/28/05, Kalle Valo <Kalle.Valo@iki.fi> wrote:
>> The documentation says that it should be possible to update
>> incrementally from the CVS repository. Am I doing something wrong or
>> is this a bug?
>
> It _should_ work the way you are running it, so consider it a bug. Do
> you think you can do some tinkering/debugging to tell us some more?
I investigated it and realized that this was my mistake. I had copied
the imported git repository from my laptop to my desktop using 'scp
-r' and it changed .git/HEAD to a file, not a link as it should have
been. I copied it again, this time with tar to preserve symbolic
links, and cvsimport started to work again. So this was just a PEBCAK.
Thanks for your help.
> Otherwise. how large is the cvs repo?
'git log | grep commit | wc -l' says 2704 commits. I have to say that
git has made my life a lot easier. It was really easy to import the
CVS history to git and now I can read the history properly with gitk.
With CVS I would have been banging my head to the wall all the time.
Now all I want is svnimport and darcsimport :)
--
Kalle Valo
^ permalink raw reply
* Re: Merges without bases
From: Daniel Barkalow @ 2005-08-27 20:49 UTC (permalink / raw)
To: Martin Langhoff; +Cc: Junio C Hamano, Darrin Thompson, git
In-Reply-To: <46a038f90508262348b25d1c8@mail.gmail.com>
On Sat, 27 Aug 2005, Martin Langhoff wrote:
> On 8/27/05, Daniel Barkalow <barkalow@iabervon.org> wrote:
> > The problem with both of these (and doing it in the build system) is that,
> > when a project includes another project, you generally don't want whatever
> > revision of the included project happens to be the latest; you want the
> > revision of the included project that the revision of the including
> > project you're looking at matches. That is, if App includes Lib, and
>
> Exactly - so you do it on a tag, or a commit date with cvs. With Arch,
> GIT and others that have a stable id for each commit, you can use that
> or the more user-friendly tags.
I'm thinking of cases like openssl, openssh, and libcrypto. Openssl and
openssh both use libcrypto but not each other (looking at the ldd output,
rather than packaging). However, it would be too much of a pain to work
directly on libcrypto without working through some other package, because
the library doesn't have its own applications. Furthermore, if you're
doing much to libcrypto, you're likely doing it in the context of a
particular application (say, for example, ssh needs a new cipher that
isn't supported for SSL at the time). You'd want to make simultaneous
changes to libcrypto to implement the new feature and to openssh to use
it; neither can be validated until the other is written, which means that
you'll have both projects checked out and dirty (in the cache sense) at
the same time, and be building the using project.
It would also be good to be able to check in this whole thing through the
version control system, rather than partially through a change to the
build system. That is, if I change the included libcrypto, commit it, and
commit the including openssh, the system as a whole should understand that
I want to change which commit of libcrypto gets used. Similarly, it would
be good to merge changes into the libcrypto used by openssh with the same
procedure used to merge changes to openssh itself, including supporting
non-fast-forward when there's a local version in use.
(Of course, currently, libcrypto is strictly part of openssl, because it
would be too much of a pain with the present version control to make it
independant, and openssh depends on openssl, despite not even linking
against -lssl, because openssl got libcrypto first.)
> The good thing here is that a makefile will know how to handle the
> situation if the external lib is hosted in Arch, in SVN, or Visual
> SourceSafe. If your external lib is only available as a tarball in a
> url, you can fetch that and uncompress it too. Arch configurations are
> _cute_ but useless in any but the most narrow cases.
Certainly, if it's sufficiently external to be in a different SCM it
should be handled by the build system. Actually, if it's even nearly that
external, it's probably going to be handled best by requiring people to go
get it themselves.
I find it odd that you say that the standard approach is to have the build
system fetch a version of the included package; my experience is that
projects either just report (or fail to report) a dependancy on having the
other package or they copy the project into their project. The former
means they can't change it (which is generally good, unless it becomes
necessary), while the latter causes update problems (c.f. zlib).
I think that Arch configurations and the CVS equivalent are, in fact,
useless, but that this is only due to implementation being insufficiently
clever, not due to the concept being inherently bad; I feel the same way
about distributed development under Arch, which is really nice under git,
so I have hope that something better could be done.
-Daniel
*This .sig left intentionally blank*
^ permalink raw reply
* [PATCH] A new merge algorithm, take 2
From: Fredrik Kuivinen @ 2005-08-27 20:51 UTC (permalink / raw)
To: git
Hi,
I have cleaned up the code for the merge algorithm quite a lot. It is
in a much better shape now.
The main changes compared to the previous version are:
* Lots of clean up.
* Some of the scripts have been renamed to better match the naming
convention used in Git.
* A new option ('-s') has been added to git-merge-cache
* Unclean merges are detected and reported
* Clean merges are committed
The user interface has been changed to:
git-merge-script <branch> <message>
HEAD will be merged with <branch>. If the merge turns out to be clean
then it will be committed with the message <message>. The working
directory will always be updated to reflect the result of the merge.
git-merge-cache has a new flag, '-s'. When this flag is given
git-merge-cache will only run the merge-script once, instead of once
per cache entry. The needed information is written to the
merge-scripts stdin in the following form:
<oSha> SP <aSha> SP <bSha> SP <oMode> SP <aMode> SP <bMode> SP <path> NUL
where
<oSha> - original file SHA1 (or empty)
<aSha> - file in branch1 SHA1 (or empty)
<bSha> - file in branch2 SHA1 (or empty)
<oMode> - orignal file mode (or empty)
<aMode> - file in branch1 mode (or empty)
<bMode> - file in branch2 mode (or empty)
<path> - pathname in repository
SP is a single space and NUL is \0. An empty SHA1 is represented as
'0000000000000000000000000000000000000000' and an empty mode is
represented with '0'.
This flag is nice to have when you have a merge-script which needs to
keep some state between different cache entries.
- Fredrik
Signed-off-by: Fredrik Kuivinen <freku045@student.liu.se>
---
Makefile | 3 -
git-merge-files-script | 188 ++++++++++++++++++++++++++++++++
git-merge-script | 94 ++++++++++++++++
gitMergeCommon.py | 86 +++++++++++++++
gitMergeCore.py | 281 ++++++++++++++++++++++++++++++++++++++++++++++++
merge-cache.c | 78 +++++++++++++
read-tree.c | 13 ++
7 files changed, 737 insertions(+), 6 deletions(-)
create mode 100755 git-merge-files-script
create mode 100755 git-merge-script
create mode 100644 gitMergeCommon.py
create mode 100644 gitMergeCore.py
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -66,7 +66,8 @@ SCRIPTS=git git-apply-patch-script git-m
git-format-patch-script git-sh-setup-script git-push-script \
git-branch-script git-parse-remote-script git-verify-tag-script \
git-ls-remote-script git-clone-dumb-http git-rename-script \
- git-request-pull-script git-bisect-script
+ git-request-pull-script git-bisect-script git-merge-script \
+ gitMergeCommon.py gitMergeCore.py git-merge-files-script
SCRIPTS += git-count-objects-script
SCRIPTS += git-revert-script
diff --git a/git-merge-files-script b/git-merge-files-script
new file mode 100755
--- /dev/null
+++ b/git-merge-files-script
@@ -0,0 +1,188 @@
+#!/usr/bin/env python
+#
+# This script is based on 'git-merge-one-file-script' in the Git
+# distribution.
+#
+# This script is supposed to be executed by git-merge-cache. When
+# git-merge-cache is used with the '-s' flag it will write entries to
+# the executed programs stdin. The syntax of one entry is:
+#
+# <oSha> SP <aSha> SP <bSha> SP <oMode> SP <aMode> SP <bMode> SP <path> NUL
+#
+# <oSha> - original file SHA1 (or empty)
+# <aSha> - file in branch1 SHA1 (or empty)
+# <bSha> - file in branch2 SHA1 (or empty)
+# <oMode> - orignal file mode (or empty)
+# <aMode> - file in branch1 mode (or empty)
+# <bMode> - file in branch2 mode (or empty)
+# <path> - pathname in repository
+#
+# SP is a single space and NUL is \0. An empty SHA1 is represented as
+# '0000000000000000000000000000000000000000' and an empty mode is
+# represented with '0'.
+
+import sys, os, re
+from gitMergeCommon import *
+from sets import Set
+
+# TODO
+# Generate a unique file name when we get a name conflict.
+
+# x <==> sha != '', - <==> sha == ''
+#
+# Case o a b
+# D x x x
+# A x x -
+# A x - x
+# A x - -
+# C - x x
+# B - x -
+# B - - x
+# - - - Doesn't happen
+def processEntry(oSha, aSha, bSha, oMode, aMode, bMode, path):
+ global cleanMerge
+ debug('processing', path)
+ if oSha == '0'*40:
+ oSha = ''
+ if aSha == '0'*40:
+ aSha = ''
+ if bSha == '0'*40:
+ bSha = ''
+ if oMode == '0':
+ oMode = ''
+ if aMode == '0':
+ aMode = ''
+ if bMode == '0':
+ bMode = ''
+
+ if (oSha != '' and (aSha == '' or bSha == '')):
+ #
+ # Case A: Deleted in one
+ #
+ if (aSha == '' and bSha == '') or \
+ (aSha == oSha and bSha == '') or \
+ (aSha == '' and bSha == oSha):
+ # Deleted in both or deleted in one and unchanged in the other
+ print 'Removing ' + path
+ runProgram(['git-update-cache', '--force-remove', '--', path])
+ else:
+ # Deleted in one and changed in the other
+ cleanMerge = False
+ if aSha == '':
+ print 'CONFLICT (del/mod): "' + path + '" deleted in', \
+ branch1, 'and modified in', branch2, '. Version', branch2, ' of "' + path + '" left in tree'
+ mode = bMode
+ sha = bSha
+ else:
+ print 'CONFLICT (mod/del): "' + path + '" deleted in', branch2, 'and modified in', branch1, \
+ '. Version', branch1, 'of "' + path + '" left in tree'
+ mode = aMode
+ sha = aSha
+ runProgram(['git-update-cache', '--cacheinfo', mode, sha, path])
+
+ elif (oSha == '' and aSha != '' and bSha == '') or \
+ (oSha == '' and aSha == '' and bSha != ''):
+ #
+ # Case B: Added in one.
+ #
+ if aSha != '':
+ addBranch = branch1
+ otherBranch = branch2
+ conf = 'file/dir'
+ else:
+ addBranch = branch2
+ otherBranch = branch1
+ conf = 'dir/file'
+
+ if path in dirs:
+ cleanMerge = False
+ newPath = path + '_' + addBranch
+ print 'CONFLICT (' + conf + '): There is a directory with name "' + path + '" in', otherBranch, \
+ '. Adding "' + path + '" as "' + newPath + '"'
+ runProgram(['git-update-cache', '--force-remove', '--', path])
+ path = newPath
+ else:
+ print 'Adding "' + path + '"'
+ runProgram(['git-update-cache', '--add', '--cacheinfo', aMode+bMode, aSha+bSha, path])
+
+ elif oSha == '' and aSha != '' and bSha != '':
+ #
+ # Case C: Added in both (check for same permissions).
+ #
+ if aSha == bSha:
+ if aMode != bMode:
+ cleanMerge = False
+ print 'CONFLICT: File "' + path + '" added identically in both branches,'
+ print 'CONFLICT: but permissions conflict', aMode, '->', bMode
+ print 'CONFLICT: adding with permission:', aMode
+ runProgram(['git-update-cache', '--add', '--cacheinfo', aMode, aSha, path])
+ else:
+ # This case is handled by git-read-tree
+ print 'Adding', path
+ runProgram(['git-update-cache', '--add', '--cacheinfo', aMode, aSha, path])
+ else:
+ cleanMerge = False
+ newPath1 = path + '_' + branch1
+ newPath2 = path + '_' + branch2
+ print 'CONFLICT (add/add): File "' + path + '" added non-identically in both branches.', \
+ 'Adding "' + newPath1 + '" and "' + newPath2 + '" instead.'
+ runProgram(['git-update-cache', '--force-remove', '--', path])
+ runProgram(['git-update-cache', '--add',
+ '--cacheinfo', aMode, aSha, newPath1,
+ '--cacheinfo', bMode, bSha, newPath2])
+
+ elif oSha != '' and aSha != '' and bSha != '':
+ #
+ # case D: Modified in both, but differently.
+ #
+ print 'Auto-merging', path
+ orig = runProgram(['git-unpack-file', oSha]).rstrip()
+ src1 = runProgram(['git-unpack-file', aSha]).rstrip()
+ src2 = runProgram(['git-unpack-file', bSha]).rstrip()
+ [out, ret] = runProgram(['merge',
+ '-L', os.environ['GIT_MERGE_BRANCH_1'] + '/' + path,
+ '-L', 'orig/' + path,
+ '-L', os.environ['GIT_MERGE_BRANCH_2'] + '/' + path,
+ src1, orig, src2], returnCode=True)
+
+ if ret != 0:
+ cleanMerge = False
+ print 'CONFLICT (content): Merge conflict in "' + path + '".'
+
+ if aMode == oMode:
+ mode = bMode
+ else:
+ mode = aMode
+
+ sha = runProgram(['git-hash-object', '-t', 'blob', '-w', src1])
+ runProgram(['git-update-cache', '--cacheinfo', aMode, sha, path])
+ os.unlink(orig)
+ os.unlink(src1)
+ os.unlink(src2)
+ else:
+ print 'ERROR: Fatal merge failure.'
+ print "ERROR: Shouldn't happen"
+ sys.exit(1)
+
+[files, dirs] = eval(open(os.environ['GIT_MERGE_DIRS']).read())
+branch1 = os.environ['GIT_MERGE_BRANCH_1']
+branch2 = os.environ['GIT_MERGE_BRANCH_2']
+cleanMerge = True
+
+input = sys.stdin.read()
+entries = input.split("\0")
+entries.pop() # remove last entry (which is '')
+entryRE = re.compile('^([0-9a-f]{40}) ([0-9a-f]{40}) ([0-9a-f]{40}) ([0-9]+) ([0-9]+) ([0-9]+) (.*)$')
+debug('starting processing...', repr(entries))
+for entry in entries:
+ m = entryRE.match(entry)
+ if m:
+ processEntry(m.group(1), m.group(2), m.group(3), m.group(4),
+ m.group(5), m.group(6), m.group(7))
+ else:
+ print sys.argv[0], 'Invalid input from merge program:', entry
+ sys.exit(1)
+
+if not cleanMerge:
+ sys.exit(1)
+
diff --git a/git-merge-script b/git-merge-script
new file mode 100755
--- /dev/null
+++ b/git-merge-script
@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+
+from gitMergeCommon import *
+from gitMergeCore import buildGraph, merge
+import sys
+
+def dropHeads():
+ try:
+ os.unlink(os.environ['GIT_DIR'] + '/MERGE_HEAD')
+ except OSError:
+ pass
+
+ try:
+ os.unlink(os.environ['GIT_DIR'] + '/LAST_MERGE')
+ except OSError:
+ pass
+
+def writeHead(head, sha):
+ if sha[-1] != '\n':
+ sha += '\n'
+
+ try:
+ f = open(os.environ['GIT_DIR'] + '/' + head, 'w')
+ f.write(sha)
+ f.close()
+ except IOError, e:
+ print 'Failed to write to', os.environ['GIT_DIR'] + '/' + head + ':', e.strerror
+ sys.exit(1)
+ return True
+
+def checkCleanTree():
+ [out, code] = runProgram(['git-update-cache', '--refresh'], returnCode = True)
+
+ if code != 0:
+ return False
+ out = runProgram(['git-diff-cache', '--name-only', '--cached', 'HEAD'])
+ if len(out) > 0:
+ return False
+
+ return True
+
+def doCommit(msg, p1, p2):
+ tree = runProgram(['git-write-tree'])
+ tree = tree.rstrip()
+
+ commit = runProgram(['git-commit-tree', tree, '-p', p1, '-p', p2], msg + '\n')
+ writeHead('HEAD', commit)
+ return commit.rstrip()
+
+if len(sys.argv) < 2:
+ print 'Usage:', sys.argv[0], '<branch> <msg>'
+ sys.exit(1)
+
+setupEnvironment()
+repoValid()
+
+if not checkCleanTree():
+ print 'Either the cache is out of sync or the working tree does not match HEAD.'
+ print 'Aborting merge.'
+ sys.exit(1)
+
+h1 = firstBranch = 'HEAD'
+h2 = secondBranch = sys.argv[1]
+commitMessage = sys.argv[2]
+
+print 'Merging', h1, 'with', h2
+h1 = runProgram(['git-rev-parse', '--revs-only', h1]).rstrip()
+h2 = runProgram(['git-rev-parse', '--revs-only', h2]).rstrip()
+print 'Resolved heads: h1:', h1, 'h2:', h2
+
+writeHead('ORIG_HEAD', h1)
+writeHead('LAST_MERGE', h2)
+
+print 'Building graph...'
+graph = buildGraph([h1, h2])
+print 'graph done.'
+
+[res, clean] = merge(graph.shaMap[h1], graph.shaMap[h2], firstBranch, secondBranch, graph)
+print 'Merge result: (tree)', res.tree()
+
+# Checkout the merge results
+runProgram(['git-read-tree', 'HEAD'])
+runProgram(['git-update-cache', '--refresh'])
+runProgram(['git-read-tree', '-m', '-u', 'HEAD', res.tree()])
+
+if clean:
+ cmit = doCommit(commitMessage, h1, h2)
+ print 'Committed merge', cmit
+ print runProgram('git-diff-tree -p ORIG_HEAD ' + cmit + ' | git-apply --stat')
+ dropHeads()
+else:
+ print 'Automatic merge failed, fix up by hand'
+ writeHead('MERGE_HEAD', h2)
+ sys.exit(1)
diff --git a/gitMergeCommon.py b/gitMergeCommon.py
new file mode 100644
--- /dev/null
+++ b/gitMergeCommon.py
@@ -0,0 +1,86 @@
+import sys
+
+if sys.version_info[0] < 2 or (sys.version_info[0] == 2 and sys.version_info[1] < 4):
+ print 'Python version 2.4 required, found', \
+ str(sys.version_info[0])+'.'+str(sys.version_info[1])+'.'+str(sys.version_info[2])
+ sys.exit(1)
+
+import subprocess, os, sets, traceback
+
+DEBUG = 0
+
+functionsToDebug = sets.Set()
+
+def addDebug(func):
+ if type(func) == str:
+ functionsToDebug.add(func)
+ else:
+ functionsToDebug.add(func.func_name)
+
+def debug(*args):
+ if DEBUG:
+ funcName = traceback.extract_stack()[-2][2]
+ if funcName in functionsToDebug:
+ printList(args)
+
+def printList(list):
+ for x in list:
+ sys.stdout.write(str(x))
+ sys.stdout.write(' ')
+ sys.stdout.write('\n')
+
+class ProgramError(Exception):
+ def __init__(self, progStr, error):
+ self.progStr = progStr
+ self.error = error
+
+# addDebug('runProgram')
+def runProgram(prog, input=None, returnCode=False, env=None):
+ debug('runProgram prog:', str(prog), 'input:', str(input))
+ if type(prog) is str:
+ progStr = prog
+ else:
+ progStr = ' '.join(prog)
+
+ try:
+ pop = subprocess.Popen(prog,
+ shell = type(prog) is str,
+ stderr=subprocess.STDOUT,
+ stdout=subprocess.PIPE,
+ stdin=subprocess.PIPE,
+ env=env)
+ except OSError, e:
+ debug('strerror:', e.strerror)
+ raise ProgramError(progStr, e.strerror)
+
+ if input != None:
+ pop.stdin.write(input)
+ pop.stdin.close()
+
+ out = pop.stdout.read()
+ code = pop.wait()
+ if returnCode:
+ ret = [out, code]
+ else:
+ ret = out
+ if code != 0 and not returnCode:
+ debug('error output:', out)
+ debug('prog:', prog)
+ raise ProgramError(progStr, out)
+# debug('output:', out.replace('\0', '\n'))
+ return ret
+
+def setupEnvironment():
+ if 'GIT_DIR' not in os.environ:
+ os.environ['GIT_DIR'] = '.git'
+
+ if not os.environ.has_key('GIT_OBJECT_DIRECTORY'):
+ os.environ['GIT_OBJECT_DIRECTORY'] = os.environ['GIT_DIR'] + '/objects'
+
+def repoValid():
+ if not (os.path.exists(os.environ['GIT_DIR']) and
+ os.path.exists(os.environ['GIT_DIR'] + '/refs') and
+ os.path.exists(os.environ['GIT_OBJECT_DIRECTORY']) and
+ os.path.exists(os.environ['GIT_OBJECT_DIRECTORY'] + '/00')):
+ print "Not a Git archive"
+ sys.exit(1)
diff --git a/gitMergeCore.py b/gitMergeCore.py
new file mode 100644
--- /dev/null
+++ b/gitMergeCore.py
@@ -0,0 +1,281 @@
+import sys, math, random, os, re, signal, tempfile
+from gitMergeCommon import runProgram, debug, addDebug, printList
+from heapq import heappush, heappop
+from sets import Set
+
+currentId = 0
+def getUniqueId():
+ global currentId
+ currentId += 1
+ return currentId
+
+class Commit:
+ def __init__(self, sha, parents, tree=None):
+ self.parents = parents
+ self.firstLineMsg = None
+ self.children = []
+
+ if tree:
+ tree = tree.rstrip()
+ self._tree = tree
+
+ if not sha:
+ self.sha = getUniqueId()
+ self.virtual = True
+ self.firstLineMsg = 'virtual commit'
+ assert(tree != None)
+ else:
+ self.virtual = False
+ self.sha = sha.rstrip()
+ assert(isSha(self.sha))
+
+ def tree(self):
+ self.getInfo()
+ assert(self._tree != None)
+ return self._tree
+
+ def shortInfo(self):
+ self.getInfo()
+ return str(self.sha) + ' ' + self.firstLineMsg
+
+ def __str__(self):
+ return self.shortInfo()
+
+ def getInfo(self):
+ if self.virtual or self.firstLineMsg != None:
+ return
+ else:
+ info = runProgram(['git-cat-file', 'commit', self.sha])
+ info = info.split('\n')
+ msg = False
+ for l in info:
+ if msg:
+ self.firstLineMsg = l
+ break
+ else:
+ if l.startswith('tree'):
+ self._tree = l[5:].rstrip()
+ elif l == '':
+ msg = True
+
+class Graph:
+ def __init__(self):
+ self.commits = []
+ self.shaMap = {}
+
+ def addNode(self, node):
+ assert(isinstance(node, Commit))
+ self.shaMap[node.sha] = node
+ self.commits.append(node)
+ for p in node.parents:
+ p.children.append(node)
+ return node
+
+ def reachableNodes(self, n1, n2):
+ res = {}
+ def traverse(n):
+ res[n] = True
+ for p in n.parents:
+ traverse(p)
+
+ traverse(n1)
+ traverse(n2)
+ return res
+
+ def fixParents(self, node):
+ for x in range(0, len(node.parents)):
+ node.parents[x] = self.shaMap[node.parents[x]]
+
+# addDebug('buildGraph')
+def buildGraph(heads):
+ debug('buildGraph heads:', heads)
+ for h in heads:
+ assert(isSha(h))
+
+ g = Graph()
+
+ out = runProgram(['git-rev-list', '--parents'] + heads)
+ for l in out.split('\n'):
+ if l == '':
+ continue
+ shas = l.split(' ')
+
+ # This is a hack, we temporarily use the 'parents' attribute
+ # to contain a list of SHA1:s. They are later replaced by proper
+ # Commit objects.
+ c = Commit(shas[0], shas[1:])
+
+ g.commits.append(c)
+ g.shaMap[c.sha] = c
+
+ for c in g.commits:
+ g.fixParents(c)
+
+ for c in g.commits:
+ for p in c.parents:
+ p.children.append(c)
+ return g
+
+# Write the empty tree to the object database and return its SHA1
+def writeEmptyTree():
+ tmpIndex = os.environ['GIT_DIR'] + '/merge-tmp-index'
+ def delTmpIndex():
+ try:
+ os.unlink(tmpIndex)
+ except OSError:
+ pass
+ delTmpIndex()
+ newEnv = os.environ.copy()
+ newEnv['GIT_INDEX_FILE'] = tmpIndex
+ res = runProgram(['git-write-tree'], env=newEnv).rstrip()
+ delTmpIndex()
+ return res
+
+def addCommonRoot(graph):
+ roots = []
+ for c in graph.commits:
+ if len(c.parents) == 0:
+ roots.append(c)
+
+ superRoot = Commit(sha=None, parents=[], tree=writeEmptyTree())
+ graph.addNode(superRoot)
+ for r in roots:
+ r.parents = [superRoot]
+ superRoot.children = roots
+ return superRoot
+
+def findShared(h1, h2):
+ def traverse(start, set):
+ stack = [start]
+ while len(stack) > 0:
+ el = stack.pop()
+ set.add(el)
+ for p in el.parents:
+ if p not in set:
+ stack.append(p)
+ h1Set = Set()
+ h2Set = Set()
+ traverse(h1, h1Set)
+ traverse(h2, h2Set)
+
+ return h1Set.intersection(h2Set)
+
+def sharedHeads(shared):
+ h = Set()
+
+ for s in shared:
+ if len([c for c in s.children if c in shared]) == 0:
+ h.add(s)
+ return list(h)
+
+getFilesRE = re.compile('([0-9]+) ([a-z0-9]+) ([0-9a-f]{40})\t(.*)')
+def getFilesAndDirs(tree1, tree2):
+ files = Set()
+ dirs = Set()
+ def addFilesDirs(tree):
+ out = runProgram(['git-ls-tree', '-r', '-z', tree])
+ for l in out.split('\0'):
+ m = getFilesRE.match(l)
+ if m:
+ if m.group(2) == 'tree':
+ dirs.add(m.group(4))
+ elif m.group(2) == 'blob':
+ files.add(m.group(4))
+
+ addFilesDirs(tree1)
+ addFilesDirs(tree2)
+ return [files, dirs]
+
+def merge(h1, h2, branch1Name, branch2Name, graph, indent=0, first=True):
+ def infoMsg(*args):
+ sys.stdout.write(' '*indent)
+ printList(args)
+ infoMsg('Merging:')
+ infoMsg(h1)
+ infoMsg(h2)
+ assert(isinstance(h1, Commit) and isinstance(h2, Commit))
+ assert(isinstance(graph, Graph))
+
+ s = findShared(h1, h2)
+ if len(s) == 0:
+ s = [addCommonRoot(graph)]
+
+ # infoMsg('found', len(s), 'shared commits.')
+ # if len(s) < 10:
+ # for x in s:
+ # infoMsg(x)
+ sys.stdout.flush()
+ s = sharedHeads(s)
+ infoMsg('found', len(s), 'shared head(s):')
+ for x in s :
+ infoMsg(x)
+ Ms = s[0]
+
+ for h in s[1:]:
+ [Ms, ignore] = merge(Ms, h, branch1Name, branch2Name, graph, indent+1, False)
+
+ filesDirs = getFilesAndDirs(h1.tree(), h2.tree())
+
+ infoMsg('running resolve on')
+ infoMsg(h1)
+ infoMsg(h2)
+
+ if first:
+ b1 = branch1Name
+ b2 = branch2Name
+ else:
+ b1 = 'Temporary shared merge branch 1'
+ b2 = 'Temporary shared merge branch 2'
+ [shaRes, clean] = resolve(h1.tree(), h2.tree(), Ms.tree(), b1, b2, filesDirs)
+
+ res = Commit(None, [h1, h2], tree=shaRes)
+ graph.addNode(res)
+ # infoMsg('merge returned: (tree)', res.tree())
+ return [res, clean]
+
+# The 'virtual' commit objects have SHAs which are integers
+shaRE = re.compile('^[0-9a-f]{40}$')
+def isSha(obj):
+ return (type(obj) is str and bool(shaRE.match(obj))) or \
+ (type(obj) is int and obj >= 1)
+
+def resolve(head, merge, common, b1, b2, filesDirs):
+ assert(isSha(head) and isSha(merge) and isSha(common))
+ head = runProgram(['git-rev-parse', '--revs-only', head]).rstrip()
+ merge = runProgram(['git-rev-parse', '--revs-only', merge]).rstrip()
+
+ if common == merge:
+ return head
+
+ if common == head:
+ return merge
+
+ runProgram(['git-read-tree', head])
+ runProgram(['git-read-tree', '-i', '-m', common, head, merge])
+ os.environ['GIT_MERGE_BRANCH_1'] = b1
+ os.environ['GIT_MERGE_BRANCH_2'] = b2
+ fdirs = tempfile.NamedTemporaryFile('w')
+ fdirs.write(repr(filesDirs))
+ fdirs.flush()
+ os.environ['GIT_MERGE_DIRS'] = fdirs.name
+ try:
+ [tree, code] = runProgram('git-write-tree', returnCode=True)
+ tree = tree.rstrip()
+ if code != 0:
+ [out, code] = runProgram(['git-merge-cache', '-s',
+ 'git-merge-files-script', '-a'],
+ returnCode=True)
+ print out
+ if code != 0:
+ # Non-clean merge
+ clean = False
+ else:
+ clean = True
+ tree = runProgram('git-write-tree').rstrip()
+ else:
+ clean = True
+ finally:
+ fdirs.close()
+
+ return [tree, clean]
+
diff --git a/merge-cache.c b/merge-cache.c
--- a/merge-cache.c
+++ b/merge-cache.c
@@ -5,7 +5,7 @@
static const char *pgm = NULL;
static const char *arguments[8];
-static int one_shot, quiet;
+static int one_shot, quiet, use_stdin, stdin_fd, pgm_pid;
static int err;
static void run_program(void)
@@ -37,6 +37,59 @@ static void run_program(void)
}
}
+static void start_program(void)
+{
+ int pid, fds[2];
+
+ if (pipe(fds) < 0)
+ die("pipe failed: %s", strerror(errno));
+
+ pid = fork();
+ if (pid < 0)
+ die("unable to fork");
+
+ if (!pid) {
+ close(fds[1]);
+ dup2(fds[0], 0);
+ close(fds[0]);
+ execlp(pgm, pgm, NULL);
+ die("unable to execute '%s': %s", pgm, strerror(errno));
+ }
+
+ pgm_pid = pid;
+ close(fds[0]);
+ stdin_fd = fds[1];
+}
+
+static void die_write(int fd, const char* buf, int len)
+{
+ if (write(fd, buf, len) != len)
+ die("unable to write: %s", strerror(errno));
+}
+
+static void write_to_program(void)
+{
+ int arg;
+ for(arg = 1; arg < 4; arg++) {
+ if(!strcmp("", arguments[arg]))
+ die_write(stdin_fd, "0000000000000000000000000000000000000000", 40);
+ else
+ die_write(stdin_fd, arguments[arg], 40);
+ die_write(stdin_fd, " ", 1);
+ }
+
+ for(arg = 5; arg < 8; arg++) {
+ if(!strcmp("", arguments[arg]))
+ die_write(stdin_fd, "0", 1);
+ else
+ die_write(stdin_fd, arguments[arg], strlen(arguments[arg]));
+ die_write(stdin_fd, " ", 1);
+ }
+
+ /* Make sure we write the terminating \0 too. */
+ die_write(stdin_fd, arguments[4], strlen(arguments[4])+1);
+}
+
static int merge_entry(int pos, const char *path)
{
int found;
@@ -68,7 +121,11 @@ static int merge_entry(int pos, const ch
} while (++pos < active_nr);
if (!found)
die("git-merge-cache: %s not in the cache", path);
- run_program();
+
+ if(use_stdin)
+ write_to_program();
+ else
+ run_program();
return found;
}
@@ -100,7 +157,7 @@ int main(int argc, char **argv)
int i, force_file = 0;
if (argc < 3)
- usage("git-merge-cache [-o] [-q] <merge-program> (-a | <filename>*)");
+ usage("git-merge-cache [-o] [-q] [-s] <merge-program> (-a | <filename>*)");
read_cache();
@@ -113,7 +170,14 @@ int main(int argc, char **argv)
quiet = 1;
i++;
}
+ if (!strcmp(argv[i], "-s")) {
+ use_stdin = 1;
+ i++;
+ }
pgm = argv[i++];
+ if(use_stdin)
+ start_program();
+
for (; i < argc; i++) {
char *arg = argv[i];
if (!force_file && *arg == '-') {
@@ -129,6 +193,14 @@ int main(int argc, char **argv)
}
merge_file(arg);
}
+
+ if(use_stdin) {
+ int status;
+ close(stdin_fd);
+ if (waitpid(pgm_pid, &status, 0) < 0 || !WIFEXITED(status) || WEXITSTATUS(status))
+ err = 1;
+ }
+
if (err && !quiet)
die("merge program failed");
return err;
diff --git a/read-tree.c b/read-tree.c
--- a/read-tree.c
+++ b/read-tree.c
@@ -7,6 +7,7 @@
static int stage = 0;
static int update = 0;
+static int ignore_working_dir = 0;
static int unpack_tree(unsigned char *sha1)
{
@@ -80,7 +81,10 @@ static void verify_uptodate(struct cache
{
struct stat st;
- if (!lstat(ce->name, &st)) {
+ if (ignore_working_dir)
+ return;
+
+ if (!lstat(ce->name, &st)) {
unsigned changed = ce_match_stat(ce, &st);
if (!changed)
return;
@@ -510,7 +514,7 @@ static int read_cache_unmerged(void)
return deleted;
}
-static const char read_tree_usage[] = "git-read-tree (<sha> | -m [-u] <sha1> [<sha2> [<sha3>]])";
+static const char read_tree_usage[] = "git-read-tree (<sha> | -m [-u] [-i] <sha1> [<sha2> [<sha3>]])";
static struct cache_file cache_file;
@@ -535,6 +539,11 @@ int main(int argc, char **argv)
continue;
}
+ if (!strcmp(arg, "-i")) {
+ ignore_working_dir = 1;
+ continue;
+ }
+
/* This differs from "-m" in that we'll silently ignore unmerged entries */
if (!strcmp(arg, "--reset")) {
if (stage || merge || emu23)
^ permalink raw reply
* Make .git directory validation code test HEAD
From: Linus Torvalds @ 2005-08-27 20:54 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Kalle Valo, Git Mailing List
In-Reply-To: <87zmr39svy.fsf@litku.valo.iki.fi>
Inspired by a report by Kalle Valo, this changes git-sh-setup-script and
the "setup_git_directory()" function to test that $GIT_DIR/HEAD is a
symlink, since a number of core git features depend on that these days.
We used to allow a regular file there, but git-fsck-cache has been
complaining about that for a while, and anything that uses branches
depends on the HEAD file being a symlink, so let's just encode that as a
fundamental requirement.
Before, a non-symlink HEAD file would appear to work, but have subtle bugs
like not having the HEAD show up as a valid reference (because it wasn't
under "refs"). Now, we will complain loudly, and the user can fix it up
trivially instead of getting strange behaviour.
This also removes the tests for "$GIT_DIR" and "$GIT_OBJECT_DIRECTORY"
being directories, since the other tests will implicitly test for that
anyway (ie the tests for HEAD, refs and 00 would fail).
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
---
On Sat, 27 Aug 2005, Kalle Valo wrote:
>
> I investigated it and realized that this was my mistake. I had copied
> the imported git repository from my laptop to my desktop using 'scp
> -r' and it changed .git/HEAD to a file, not a link as it should have
> been. I copied it again, this time with tar to preserve symbolic
> links, and cvsimport started to work again. So this was just a PEBCAK.
diff --git a/git-sh-setup-script b/git-sh-setup-script
--- a/git-sh-setup-script
+++ b/git-sh-setup-script
@@ -11,7 +11,6 @@ die() {
exit 1
}
-[ -d "$GIT_DIR" ] &&
+[ -h "$GIT_DIR/HEAD" ] &&
[ -d "$GIT_DIR/refs" ] &&
-[ -d "$GIT_OBJECT_DIRECTORY" ] &&
[ -d "$GIT_OBJECT_DIRECTORY/00" ]
diff --git a/setup.c b/setup.c
--- a/setup.c
+++ b/setup.c
@@ -72,6 +72,24 @@ const char **get_pathspec(const char *pr
return (const char **) pathspec;
}
+/*
+ * Test it it looks like we're at the top
+ * level git directory. We want to see a
+ *
+ * - a HEAD symlink and a refs/ directory under ".git"
+ * - either a .git/objects/ directory _or_ the proper
+ * GIT_OBJECT_DIRECTORY environment variable
+ */
+static int is_toplevel_directory(void)
+{
+ struct stat st;
+
+ return !lstat(".git/HEAD", &st) &&
+ S_ISLNK(st.st_mode) &&
+ !access(".git/refs/", X_OK) &&
+ (gitenv(DB_ENVIRONMENT) || !access(".git/objects/", X_OK));
+}
+
const char *setup_git_directory(void)
{
static char cwd[PATH_MAX+1];
@@ -89,17 +107,8 @@ const char *setup_git_directory(void)
offset = len = strlen(cwd);
for (;;) {
- /*
- * We always want to see a .git/refs/ subdirectory
- */
- if (!access(".git/refs/", X_OK)) {
- /*
- * Then we need either a GIT_OBJECT_DIRECTORY define
- * or a .git/objects/ directory
- */
- if (gitenv(DB_ENVIRONMENT) || !access(".git/objects/", X_OK))
- break;
- }
+ if (is_toplevel_directory())
+ break;
chdir("..");
do {
if (!offset)
^ permalink raw reply
* Re: Comments in read-tree about #nALT
From: Daniel Barkalow @ 2005-08-27 21:57 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vwtm726xq.fsf@assigned-by-dhcp.cox.net>
On Sat, 27 Aug 2005, Junio C Hamano wrote:
> Daniel Barkalow <barkalow@iabervon.org> writes:
>
> > Part of threeway_merge, however, wants to search the rest of the cache for
> > interfering entries in some cases, which would have to happen differently,
> > because I won't have the cache completely filled out beforehand. I'm
> > trying to figure out what the comments are talking about, and they seem to
> > refer to a list of the possible cases. Is that list somewhere convenient?
>
> Please look for END_OF_CASE_TABLE in t/t1000-read-tree-m-3way.sh;
> the table talks about some of the (ALT) not implemented, but
> some of them are ("git whatchanged t/t1000-read-tree-m-3way"
> would tell you which).
It looks like all of them are implemented:
#2ALT, #3ALT, #5ALT, and #14ALT, according to the commit comments, and the
others seem from the email you quote to have been done in the process of
getting #5ALT.
> Two way cases are described in Documentation/git-read-tree.txt,
> if you care. If you were not touching the three-way case right
> now, I'd move/copy the three way cases there as well, but that
> can wait until after your changes.
I'd actually like to introduce Documentation/technical/trivial-merge for
this stuff; I think it would be good to have documentation for people who
need to know how the stuff works, rather than just how to use it, so we
get a balance between reams of information that users don't want to wade
through and being too vague for future developers.
Okay, so it looks to me like the only cases that care about the contents
of the index, other than in stage 0 (which is effectively another tree,
but already in index-form rather than tree-form), are 2 and 3, and these
only care because read-tree modifies the stage of entries, rather
than removing them and adding a stage-0 replacement entry; if it went
through the add logic without SKIP_DFCHECK, that would reject the same
things that causes_df_conflict rejects (at the point where whichever is
second is done).
So if I do the merge on tree entries (plus a stage-0 ce for the input from
the index), and then add the result(s) to the cache, I can skip
causes_df_conflict() in favor of just not using SKIP_DFCHECK. Is this
right?
Also, there doesn't actually seem to be a DF test in t1000; I think the
t1005 DF test covers these cases (by the emu23 path into this code). Is
this right?
-Daniel
*This .sig left intentionally blank*
^ permalink raw reply
* Re: Comments in read-tree about #nALT
From: Daniel Barkalow @ 2005-08-27 23:15 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <Pine.LNX.4.63.0508271709530.23242@iabervon.org>
On Sat, 27 Aug 2005, Daniel Barkalow wrote:
> Okay, so it looks to me like the only cases that care about the contents
> of the index, other than in stage 0 (which is effectively another tree,
> but already in index-form rather than tree-form), are 2 and 3, and these
> only care because read-tree modifies the stage of entries, rather
> than removing them and adding a stage-0 replacement entry; if it went
> through the add logic without SKIP_DFCHECK, that would reject the same
> things that causes_df_conflict rejects (at the point where whichever is
> second is done).
>
> So if I do the merge on tree entries (plus a stage-0 ce for the input from
> the index), and then add the result(s) to the cache, I can skip
> causes_df_conflict() in favor of just not using SKIP_DFCHECK. Is this
> right?
What I missed was that the effect of causes_df_conflict is to give "no
merge" for the entry, rather than giving an error overall. So I do need an
equivalent.
> Also, there doesn't actually seem to be a DF test in t1000; I think the
> t1005 DF test covers these cases (by the emu23 path into this code). Is
> this right?
Looks like stuff all over the place fails if causes_df_conflict is messed
up, so I should be covered.
-Daniel
*This .sig left intentionally blank*
^ permalink raw reply
* Re: Comments in read-tree about #nALT
From: Linus Torvalds @ 2005-08-27 23:35 UTC (permalink / raw)
To: Daniel Barkalow; +Cc: Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.63.0508271855100.23242@iabervon.org>
On Sat, 27 Aug 2005, Daniel Barkalow wrote:
>
> What I missed was that the effect of causes_df_conflict is to give "no
> merge" for the entry, rather than giving an error overall. So I do need an
> equivalent.
Daniel,
I'm not 100% sure what you're trying to do, but one thing that might work
out is to just having multiple "stage 3" entries with the same pathname.
We current use 4 stages:
- stage 0 is "resolved"
- stage 1 is "original"
- stage 2 is "one branch"
- stage 3 is "another branch"
But if we allowed duplicate entries per stage, I think we could easily
just fold stage 2/3 into one stage, and just have <n> entries in stage 2.
That would immediately mean that a three-way merge could be <n> way.
The only rule would be that when you add a entry to stage 2, you must
always add it after any previous entry that is already in stage 2. That
should be easy.
In fact, this extension might even allow us to solve the "multiple merge
base" problem: we could allow multiple entries in "stage 1" too, ie one
entry per merge base (and just collapse identical entries - there's no
ordering involved in stage 1 entries).
So you could merge "n" trees with "m" bases, and all without really
changing the current logic much at all.
Maybe I'm missing something (like what you're trying to do in the first
place), but this _seems_ doable.
Of course, "git-merge-one-file" gets more interesting as a result.
Linus
^ permalink raw reply
* Re: Comments in read-tree about #nALT
From: Daniel Barkalow @ 2005-08-28 0:16 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.58.0508271621210.3317@g5.osdl.org>
On Sat, 27 Aug 2005, Linus Torvalds wrote:
> On Sat, 27 Aug 2005, Daniel Barkalow wrote:
> >
> > What I missed was that the effect of causes_df_conflict is to give "no
> > merge" for the entry, rather than giving an error overall. So I do need an
> > equivalent.
>
> Daniel,
> I'm not 100% sure what you're trying to do, but one thing that might work
> out is to just having multiple "stage 3" entries with the same pathname.
>
> We current use 4 stages:
> - stage 0 is "resolved"
> - stage 1 is "original"
> - stage 2 is "one branch"
> - stage 3 is "another branch"
>
> But if we allowed duplicate entries per stage, I think we could easily
> just fold stage 2/3 into one stage, and just have <n> entries in stage 2.
> That would immediately mean that a three-way merge could be <n> way.
>
> The only rule would be that when you add a entry to stage 2, you must
> always add it after any previous entry that is already in stage 2. That
> should be easy.
It looks like stage 2 is currently special as the stage that's similar to
the index/HEAD/working tree. However, I don't see any problem with <n>
entries in stage 3, except that, if you have a non-maximal number of them
for some reason, it'll be impossible to determine which came from which
tree.
> In fact, this extension might even allow us to solve the "multiple merge
> base" problem: we could allow multiple entries in "stage 1" too, ie one
> entry per merge base (and just collapse identical entries - there's no
> ordering involved in stage 1 entries).
That's actually the problem I was working on.
> So you could merge "n" trees with "m" bases, and all without really
> changing the current logic much at all.
>
> Maybe I'm missing something (like what you're trying to do in the first
> place), but this _seems_ doable.
I'd be afraid of confusing everything by removing the uniqueness
invariant, although I guess not too much does anything with entries in
stages other than 0. I probably just don't find the index as intuitive as
you do and as the struct tree representation.
I'm working on arranging the code to look at each path in sequence, with
the input trees as the inner loop, rather than with the loops in the other
order; using parse_tree to parse the objects instead of read_tree; and
doing trivial merges before putting things in the cache, rather than
after. I'd been thinking that this would avoid a limit on the number of
stages, since I hadn't considered whether multiple entries for the same
path and stage could be allowed.
I still think that my order is likely to be easier to understand and
involve read-tree relying less on tricky properties of the data
structures, but I'll have to get it done before I can say that for sure.
-Daniel
*This .sig left intentionally blank*
^ permalink raw reply
* Re: Comments in read-tree about #nALT
From: Linus Torvalds @ 2005-08-28 1:18 UTC (permalink / raw)
To: Daniel Barkalow; +Cc: Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.63.0508271941090.23242@iabervon.org>
On Sat, 27 Aug 2005, Daniel Barkalow wrote:
>
> It looks like stage 2 is currently special as the stage that's similar to
> the index/HEAD/working tree.
Ahh, yes. We do actually want that. So yes, we should make only stage 3
have the multiple entries.
> I'd be afraid of confusing everything by removing the uniqueness
> invariant, although I guess not too much does anything with entries in
> stages other than 0. I probably just don't find the index as intuitive as
> you do and as the struct tree representation.
Yes, I find the index to be very nice to work with - it basically allows
you to work with multiple trees in one go, instead of working one file at
a time. You just slurp in any number of trees (with some trivial rules
about which stage they go in), and then you can just walk through each
filename and efficiently handle one file at a time without re-doing the
tree thing. Very nice.
Linus
^ permalink raw reply
* Re: Make .git directory validation code test HEAD
From: Junio C Hamano @ 2005-08-28 4:56 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Kalle Valo, Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0508271334320.3317@g5.osdl.org>
Linus Torvalds <torvalds@osdl.org> writes:
> This also removes the tests for "$GIT_DIR" and "$GIT_OBJECT_DIRECTORY"
> being directories, since the other tests will implicitly test for that
> anyway (ie the tests for HEAD, refs and 00 would fail).
I've thought about it when you brought up the Andrew's naked git
repository detection issue, but one thing I was undecided was
that if we would want to forbid either of these "directories"
being a symlink to another directory. I think it would be OK;
admittedly "test -d" says OK for a symlink to a directory.
I accept the patch, but will not apply and push it out right
now; I am not in a shape to be operating heavy equipment ;-).
Please wait until tomorrow morning.
^ permalink raw reply
* Re: SVN import issue
From: James Cloos @ 2005-08-28 5:11 UTC (permalink / raw)
To: git
In-Reply-To: <pan.2005.08.26.10.40.38.616149@smurf.noris.de>
>>>>> "Matthias" == Matthias Urlichs <smurf@smurf.noris.de> writes:
Matthias> Paths in SVN are usually prefixed "/trunk/" (main branch) or
Matthias> "/branches/foo/" (branch foo); tagging is done by creating
Matthias> "/tags/bar", with the trunk (or branch) revision it is
Matthias> pointing to as its parent.
Anyone working on this should note that /usually/ is vital above.
Among other variations, using branch rather than branches is common.
-JimC
^ permalink raw reply
* Re: [PATCH] A new merge algorithm, take 2
From: Junio C Hamano @ 2005-08-28 5:23 UTC (permalink / raw)
To: Fredrik Kuivinen; +Cc: git
In-Reply-To: <20050827205135.GB16587@c165.ib.student.liu.se>
Fredrik Kuivinen <freku045@student.liu.se> writes:
> The main changes compared to the previous version are:
>
> * Lots of clean up.
> * Some of the scripts have been renamed to better match the naming
> convention used in Git.
> * A new option ('-s') has been added to git-merge-cache
> * Unclean merges are detected and reported
> * Clean merges are committed
I have not looked deeply into your Graph thing, so I'd comment
only on a couple of points in the external interfaces area.
I am not sure why you need to be touching merge-cache. I
suspect that reading directly from "ls-files --stage -z" might
be easier and certainly less intrusive. I do not have immediate
objections to "read-tree -i", though. I think it is useful.
When there is a merge conflict, the current code leaves the
index in unmerged state and intermediate merge result with
conflict marker is left in the working tree. It appears that
your code changes this and puts the blob with conflict markers
in the index file. Leaving things in unmerged state has two
advantages:
- you _could_ run git-diff-stages to see what the changes in
both sides are.
- you cannot accidentally make a commit from such an index file
state; in fact you cannot even write it out as a tree.
I understand why you do it this way, and I do not find your way
_too_ problematic, but we do need to realize that this is making
things somewhat more prone to human errors.
I have to admit that I find that "even when merging the
merge-base candidates results in a file with conflict markers in
it, the parts with the conflict markers often gets changed in
the heads being merged, and the conflict markers will be gone
from the result" trick very cute and interesting. By itself it
has certain amusement value, and if it works well in practice
that is great.
> +print 'Merging', h1, 'with', h2
> +h1 = runProgram(['git-rev-parse', '--revs-only', h1]).rstrip()
> +h2 = runProgram(['git-rev-parse', '--revs-only', h2]).rstrip()
Here, '--verify" would be the right flag to give to these. Perhaps:
h1 = runProgram(['git-rev-parse', '--verify', ('%s^0' % h1)]).rstrip()
^ permalink raw reply
* Re: Comments in read-tree about #nALT
From: Junio C Hamano @ 2005-08-28 5:45 UTC (permalink / raw)
To: Daniel Barkalow; +Cc: git
In-Reply-To: <Pine.LNX.4.63.0508271855100.23242@iabervon.org>
Daniel Barkalow <barkalow@iabervon.org> writes:
> What I missed was that the effect of causes_df_conflict is to give "no
> merge" for the entry, rather than giving an error overall. So I do need an
> equivalent.
Correct.
^ permalink raw reply
* Re: Comments in read-tree about #nALT
From: Junio C Hamano @ 2005-08-28 5:46 UTC (permalink / raw)
To: Daniel Barkalow; +Cc: git
In-Reply-To: <Pine.LNX.4.63.0508271709530.23242@iabervon.org>
Daniel Barkalow <barkalow@iabervon.org> writes:
> I'd actually like to introduce Documentation/technical/trivial-merge for
> this stuff; I think it would be good to have documentation for people who
> need to know how the stuff works, rather than just how to use it, so we
> get a balance between reams of information that users don't want to wade
> through and being too vague for future developers.
I think that is an excellent idea to have user manual vs
internals manual.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox