* Re: Does Git run on Windows ?
From: Johannes Schindelin @ 2006-07-08 2:48 UTC (permalink / raw)
To: Jakub Narebski; +Cc: git
In-Reply-To: <e8mb8r$t1u$2@sea.gmane.org>
Hi,
On Fri, 7 Jul 2006, Jakub Narebski wrote:
> Johannes Schindelin wrote:
>
> > On Thu, 6 Jul 2006, Aaron Gray wrote:
> >
> >> Its got lots of C code, and Bash scripts, with a couple of Perl scripts.
> >
> > And you completely forgot Python.
>
> If I remember correctly the only Python dependency was recursive merge
> strategy, currently being reworked in C.
I remember, too. Because Alex and me are working very hard to provide
something which is presentable to the list. We will release a WIP patch of
it, probably tomorrow.
Ciao,
Dscho
^ permalink raw reply
* Re: comparing file contents in is_exact_match?
From: Johannes Schindelin @ 2006-07-08 2:50 UTC (permalink / raw)
To: Florian Weimer; +Cc: git
In-Reply-To: <87k66p8jee.fsf@mid.deneb.enyo.de>
Hi,
On Fri, 7 Jul 2006, Florian Weimer wrote:
> - s->data = mmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
> + s->data = mmap(NULL, s->size, PROT_READ, MAP_SHARED, fd, 0);
Be advised that this breaks setups with NO_MMAP, in particular most (all)
cygwin setups I know of.
Hth,
Dscho
^ permalink raw reply
* [PATCH] gitweb.cgi: Use File::MMagic; "a=blob" action knows the blob/file type
From: Luben Tuikov @ 2006-07-08 4:10 UTC (permalink / raw)
To: git
[-- Attachment #1: Type: text/plain, Size: 775 bytes --]
Use File::MMagic to determine the MIME type of a blob/file.
The variable magic_mime_file holds the location of the
"magic.mime" file, usually "/usr/share/file/magic.mime".
If not defined, the magic numbers internally stored in the
File::MMagic module are used.
Action "blob" knows the file type: if the file type is
not "text/*" then action "blob" defaults to "blob_plain",
i.e. the file is downloaded raw for the browser to interpret.
If the file type is "text/*", then "blob" defaults to the
current "cat -n"-like output, from which you can click
"plain", to get the "blob_plain" output.
Signed-off-by: Luben Tuikov <ltuikov@yahoo.com>
---
gitweb/gitweb.cgi | 140 +++++++++++++++++++++--------------------------------
1 files changed, 56 insertions(+), 84 deletions(-)
[-- Attachment #2: pat785262450 --]
[-- Type: text/plain, Size: 5173 bytes --]
diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi
index cce0753..6798990 100755
--- a/gitweb/gitweb.cgi
+++ b/gitweb/gitweb.cgi
@@ -14,6 +14,8 @@ use CGI::Util qw(unescape);
use CGI::Carp qw(fatalsToBrowser);
use Encode;
use Fcntl ':mode';
+use File::MMagic;
+use FileHandle;
binmode STDOUT, ':utf8';
our $cgi = new CGI;
@@ -54,9 +56,15 @@ #our $projects_list = $projectroot;
our $projects_list = "index/index.aux";
# default blob_plain mimetype and default charset for text/plain blob
-our $default_blob_plain_mimetype = 'text/plain';
+our $default_blob_mimetype = 'text/plain';
our $default_text_plain_charset = undef;
+# magic_mime_file: if defined this file will be used by File::MMagic
+# to guess the file type, else the magic numbers stored internally
+# in File::MMagic will be used. Either relative or absolute name
+# can be given. E.g. "/usr/share/file/magic.mime".
+our $magic_mime_file = undef;
+
# file to use for guessing MIME types before trying /etc/mime.types
# (relative to the current git repository)
our $mimetypes_file = undef;
@@ -1455,11 +1463,58 @@ sub git_get_hash_by_path {
}
}
+#
+# Strangely enough the File::MMagic package, version 1.27, has a bug
+# whereby reading from a piped filehandle (e.g. STDIN, or "-|") always
+# returns 'text/plain', but reading from a file on a file system (as it
+# would be the case for the checktype_filename() method) properly
+# determines the file type.
+#
+sub get_blob_mimetype {
+ my $blob_file = "$git_temp/blob-$hash";
+ if (! -r $blob_file) {
+ open my $fd_in, "-|", "$gitbin/git-cat-file blob $hash" or return $default_blob_mimetype;
+ open my $fd_out, "> $blob_file";
+ my @file = <$fd_in>;
+ print $fd_out @file;
+ close $fd_out;
+ close $fd_in;
+ }
+ my $mm = $magic_mime_file ? File::MMagic->new($magic_mime_file) : new File::MMagic;
+ my $mime = $mm->checktype_filename($blob_file);
+ return $mime;
+}
+
+sub git_blob_plain {
+ open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or return;
+ my $mimetype = get_blob_mimetype();
+
+ # save as filename, even when no $file_name is given
+ my $save_as = "$hash";
+ if (defined $file_name) {
+ $save_as = $file_name;
+ } elsif ($mimetype =~ m/^text\//) {
+ $save_as .= '.txt';
+ }
+
+ print $cgi->header(-type => "$mimetype", '-content-disposition' => "inline; filename=\"$save_as\"");
+ undef $/;
+ binmode STDOUT, ':raw';
+ print <$fd>;
+ binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
+ $/ = "\n";
+ close $fd;
+}
+
sub git_blob {
if (!defined $hash && defined $file_name) {
my $base = $hash_base || git_read_head($project);
$hash = git_get_hash_by_path($base, $file_name, "blob") || die_error(undef, "Error lookup file.");
}
+ my $mimetype = get_blob_mimetype();
+ if ($mimetype !~ m/^text\//) {
+ return git_blob_plain();
+ }
my $have_blame = git_get_project_config_bool ('blame');
open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or die_error(undef, "Open failed.");
git_header_html();
@@ -1510,89 +1565,6 @@ sub git_blob {
git_footer_html();
}
-sub mimetype_guess_file {
- my $filename = shift;
- my $mimemap = shift;
- -r $mimemap or return undef;
-
- my %mimemap;
- open(MIME, $mimemap) or return undef;
- while (<MIME>) {
- my ($mime, $exts) = split(/\t+/);
- my @exts = split(/\s+/, $exts);
- foreach my $ext (@exts) {
- $mimemap{$ext} = $mime;
- }
- }
- close(MIME);
-
- $filename =~ /\.(.*?)$/;
- return $mimemap{$1};
-}
-
-sub mimetype_guess {
- my $filename = shift;
- my $mime;
- $filename =~ /\./ or return undef;
-
- if ($mimetypes_file) {
- my $file = $mimetypes_file;
- #$file =~ m#^/# or $file = "$projectroot/$path/$file";
- $mime = mimetype_guess_file($filename, $file);
- }
- $mime ||= mimetype_guess_file($filename, '/etc/mime.types');
- return $mime;
-}
-
-sub git_blob_plain_mimetype {
- my $fd = shift;
- my $filename = shift;
-
- # just in case
- return $default_blob_plain_mimetype unless $fd;
-
- if ($filename) {
- my $mime = mimetype_guess($filename);
- $mime and return $mime;
- }
-
- if (-T $fd) {
- return 'text/plain' .
- ($default_text_plain_charset ? '; charset='.$default_text_plain_charset : '');
- } elsif (! $filename) {
- return 'application/octet-stream';
- } elsif ($filename =~ m/\.png$/i) {
- return 'image/png';
- } elsif ($filename =~ m/\.gif$/i) {
- return 'image/gif';
- } elsif ($filename =~ m/\.jpe?g$/i) {
- return 'image/jpeg';
- } else {
- return 'application/octet-stream';
- }
-}
-
-sub git_blob_plain {
- open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or return;
- my $type = git_blob_plain_mimetype($fd, $file_name);
-
- # save as filename, even when no $file_name is given
- my $save_as = "$hash";
- if (defined $file_name) {
- $save_as = $file_name;
- } elsif ($type =~ m/^text\//) {
- $save_as .= '.txt';
- }
-
- print $cgi->header(-type => "$type", '-content-disposition' => "inline; filename=\"$save_as\"");
- undef $/;
- binmode STDOUT, ':raw';
- print <$fd>;
- binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
- $/ = "\n";
- close $fd;
-}
-
sub git_tree {
if (!defined $hash) {
$hash = git_read_head($project);
--
1.4.1.g2f3c
^ permalink raw reply related
* Re: [PATCH] gitweb.cgi: Use File::MMagic; "a=blob" action knows the blob/file type
From: Junio C Hamano @ 2006-07-08 6:18 UTC (permalink / raw)
To: ltuikov; +Cc: git
In-Reply-To: <20060708041021.24704.qmail@web31804.mail.mud.yahoo.com>
Luben Tuikov <ltuikov@yahoo.com> writes:
> Use File::MMagic to determine the MIME type of a blob/file.
> The variable magic_mime_file holds the location of the
> "magic.mime" file, usually "/usr/share/file/magic.mime".
> If not defined, the magic numbers internally stored in the
> File::MMagic module are used.
I am sorry to ask you this, but would you mind redoing this
patch without File::MMagic bits? I think giving "a=blob" an
ability to automatically switch to git_blob_plain is a good
addition (as is your earlier patch to give a direct link to
reach blob_plain from the list), so let's have that part in
first. I haven't applied your earlier one but it will appear in
"next" shortly.
Existing filename based mimetypes_guess should be a lot cheaper
than exploding a blob and feeding it to File::MMagic. I was
hoping File::MMagic to be used when we cannot guess the content
type that way (i.e. when mimetypes_guess returns undef or
application/octet-stream).
Since the repository owner can correct misidentification by the
standard /etc/mime.types by supplying a custom per-repository
$mimetypes_file (modulo that the current implementation of
mimetype_guess_file does not allow it if the file does not have
an extension that is specific enough), File::MMagic might be an
overkill, especially if used in the way this patch does. To
allow finer grained differentiation that cannot be done with
file extensions alone (e.g. some files may have .dat extension
but one can be VCD mpeg wrapped in RIFF, and another can be a
Z-machine story file), it might be simpler to allow the
repository owner to specify full $file_name for such an ambiguous
file in their custom $mimetypes_file, and try to match it in
mimetype_guess_file sub. That way we may not even need to use
File::MMagic.
Are there cases where only $hash is given without $file_name?
If so we may need to fall back on File::MMagic in such a case
after all, but get_blob_mimetype sub copies the whole blob to a
temporary file to work around a problem with version 1.27 you
state in the comment -- this is way too much (and nobody seems
to clean up the tempfile). Looking at magic.mime, I suspect we
might be able to get away with the first 4k bytes or so at most
(the largest offset except iso9660 image is "Biff5" appearing at
2114 to signal an Excel spreadsheet).
^ permalink raw reply
* Re: [RFC/PATCH 14] autoconf: Added --with/--without for openssl, curl, expat to ./configure
From: Pavel Roskin @ 2006-07-08 7:33 UTC (permalink / raw)
To: Jakub Narebski; +Cc: git
In-Reply-To: <200607011955.23908.jnareb@gmail.com>
Hello, Jakub!
On Sat, 2006-07-01 at 19:55 +0200, Jakub Narebski wrote:
> >> I suspect that AS_HELP_WITH does some strange quoting, or stripping. Both
> >> [=PATH] and [[=PATH]] produces =PATH in ./configure --help output.
> >> When using @<:@=PATH@:>@ I get [=PATH], but the description of option begins
> >> line below.
Sorry, I misunderstood the problem. I think it's pure cosmetics.
Please don't let it stop you.
> I guess I would just not use AS_HELP_STRING, and format help
> message "by hand".
Please don't wast time on such minor things. It's more important to get
the functionality implemented.
> By the way, if you know autoconf well, perhaps you could tell me how to write
> tests for the following programs: ar, tar, rpmbuild, how to write test for
> Python version (or rather for WITH_OWN_SUBPROCESS_PY) and other test autoconf.ac
> lacks now (NEEDS_SSL_WITH_CRYPTO, NEEDS_LIBICONV, NEEDS_SOCKET, NO_MMAP,
> NO_IPV6, NO_ICONV, NO_ACCURATE_DIFF unless that was removed or changed name).
Generally, see the Autoconf manual for the specific test first, then for
more common test.
For ar, use AC_CHECK_TOOL to allow cross-compilation. For tar and
rpmbiuld, use
AC_CHECK_PROG. Python will have to run to find the version, I'm afraid,
which would complicate cross builds. Fortunately, it's on the way out.
Tests for sockets are described in the Autoconf documentation. Other
tests should probably be implemented as test programs unless they can be
reduced to checking for a specific symbol in a specific library.
--
Regards,
Pavel Roskin
^ permalink raw reply
* Re: Quick merge status updates.
From: Pavel Roskin @ 2006-07-08 7:36 UTC (permalink / raw)
To: Petr Baudis; +Cc: Junio C Hamano, git
In-Reply-To: <20060702204906.GG29115@pasky.or.cz>
On Sun, 2006-07-02 at 22:49 +0200, Petr Baudis wrote:
> Dear diary, on Wed, Jun 28, 2006 at 12:05:03PM CEST, I got a letter
> where Pavel Roskin <proski@gnu.org> said that...
> I feel that it is time for another stupid question of mine - why can't
> you just use lib?
>
> use lib ('@@INSTLIBDIR@@');
>
> Looks a lot better than some @INC unshifting, and it should be
> equivalent.
I honestly have no idea. I think whatever gets you closer to the
satisfaction of the "three conditions" should be used.
--
Regards,
Pavel Roskin
^ permalink raw reply
* Re: git on HP-UX
From: Pavel Roskin @ 2006-07-08 7:38 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vfyhe465i.fsf@assigned-by-dhcp.cox.net>
On Thu, 2006-07-06 at 17:20 -0700, Junio C Hamano wrote:
> Pavel Roskin <proski@gnu.org> writes:
> > I hope the Autoconf based configure is on its way to git, but I don't
> > see in in the "pu" branch yet. I'm not very keen about reinventing
> > Autoconf and hacking a hand-made configure script.
>
> OK, you half-convinced me. The other half came from a recent
> series of patches to try using 'which' to detect executables,
> which is another common mistake handcrafted configure script
> makes, which autoconf people have solved for us long time ago.
I really appreciate it. I hope all the Autoconf patches in the queue
will land in "pu" shortly, and then I'm going to have a closer look at
it.
--
Regards,
Pavel Roskin
^ permalink raw reply
* Re: [PATCH] Fix several places where diff.renames in config can be problematic
From: Junio C Hamano @ 2006-07-08 8:05 UTC (permalink / raw)
To: Eric Wong; +Cc: git
In-Reply-To: <20060708015844.GA13769@soma>
Eric Wong <normalperson@yhbt.net> writes:
> -my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit);
> +my @files = safe_pipe_capture('git-diff-tree','--no-renames','-r',
> + $parent, $commit);
I changed my mind.
-- >8 --
diff: do not use configuration magic at the core-level
The Porcelainish has become so much usable as the UI that there
is not much reason people should be using the core programs by
hand anymore. At this point we are better off making the
behaviour of the core programs predictable by keeping them
unaffected by the configuration variables. Otherwise they will
become very hard to use as reliable building blocks.
For example, "git-commit -a" internally uses git-diff-files to
figure out the set of paths that need to be updated in the
index, and we should never allow diff.renames that happens to be
in the configuration to interfere (or slow down the process).
The UI level configuration such as showing renamed diff and
coloring are still honored by the Porcelainish ("git log" family
and "git diff"), but not by the core anymore.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
builtin-diff-files.c | 2 +-
builtin-diff-index.c | 2 +-
builtin-diff-stages.c | 2 +-
builtin-diff-tree.c | 2 +-
builtin-diff.c | 2 +-
builtin-log.c | 8 ++++----
diff.c | 8 +++++++-
diff.h | 2 +-
8 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/builtin-diff-files.c b/builtin-diff-files.c
index a655eea..81ac2fe 100644
--- a/builtin-diff-files.c
+++ b/builtin-diff-files.c
@@ -18,7 +18,7 @@ int cmd_diff_files(int argc, const char
struct rev_info rev;
int silent = 0;
- git_config(git_diff_config);
+ git_config(git_default_config); /* no "diff" UI options */
init_revisions(&rev);
rev.abbrev = 0;
diff --git a/builtin-diff-index.c b/builtin-diff-index.c
index b37c9e8..a1fa1b8 100644
--- a/builtin-diff-index.c
+++ b/builtin-diff-index.c
@@ -15,7 +15,7 @@ int cmd_diff_index(int argc, const char
int cached = 0;
int i;
- git_config(git_diff_config);
+ git_config(git_default_config); /* no "diff" UI options */
init_revisions(&rev);
rev.abbrev = 0;
diff --git a/builtin-diff-stages.c b/builtin-diff-stages.c
index 30931fe..9c62702 100644
--- a/builtin-diff-stages.c
+++ b/builtin-diff-stages.c
@@ -61,7 +61,7 @@ int cmd_diff_stages(int ac, const char *
const char *prefix = setup_git_directory();
const char **pathspec = NULL;
- git_config(git_diff_config);
+ git_config(git_default_config); /* no "diff" UI options */
read_cache();
diff_setup(&diff_options);
while (1 < ac && av[1][0] == '-') {
diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c
index ae1cde9..b610668 100644
--- a/builtin-diff-tree.c
+++ b/builtin-diff-tree.c
@@ -67,7 +67,7 @@ int cmd_diff_tree(int argc, const char *
static struct rev_info *opt = &log_tree_opt;
int read_stdin = 0;
- git_config(git_diff_config);
+ git_config(git_default_config); /* no "diff" UI options */
nr_sha1 = 0;
init_revisions(opt);
opt->abbrev = 0;
diff --git a/builtin-diff.c b/builtin-diff.c
index d520c7c..1df531b 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -250,7 +250,7 @@ int cmd_diff(int argc, const char **argv
* Other cases are errors.
*/
- git_config(git_diff_config);
+ git_config(git_diff_ui_config);
init_revisions(&rev);
argc = setup_revisions(argc, argv, &rev, NULL);
diff --git a/builtin-log.c b/builtin-log.c
index 698b71e..dd5a5a2 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -47,7 +47,7 @@ int cmd_whatchanged(int argc, const char
{
struct rev_info rev;
- git_config(git_diff_config);
+ git_config(git_diff_ui_config);
init_revisions(&rev);
rev.diff = 1;
rev.diffopt.recursive = 1;
@@ -62,7 +62,7 @@ int cmd_show(int argc, const char **argv
{
struct rev_info rev;
- git_config(git_diff_config);
+ git_config(git_diff_ui_config);
init_revisions(&rev);
rev.diff = 1;
rev.diffopt.recursive = 1;
@@ -79,7 +79,7 @@ int cmd_log(int argc, const char **argv,
{
struct rev_info rev;
- git_config(git_diff_config);
+ git_config(git_diff_ui_config);
init_revisions(&rev);
rev.always_show_header = 1;
cmd_log_init(argc, argv, envp, &rev);
@@ -105,7 +105,7 @@ static int git_format_config(const char
strcat(extra_headers, value);
return 0;
}
- return git_diff_config(var, value);
+ return git_diff_ui_config(var, value);
}
diff --git a/diff.c b/diff.c
index 1bf1ed0..493650c 100644
--- a/diff.c
+++ b/diff.c
@@ -102,7 +102,13 @@ static const char *parse_diff_color_valu
die("bad config value '%s' for variable '%s'", value, var);
}
-int git_diff_config(const char *var, const char *value)
+/*
+ * These are to give UI layer defaults.
+ * The core-level commands such as git-diff-files should
+ * never be affected by the setting of diff.renames
+ * the user happens to have in the configuration file.
+ */
+int git_diff_ui_config(const char *var, const char *value)
{
if (!strcmp(var, "diff.renamelimit")) {
diff_rename_limit_default = git_config_int(var, value);
diff --git a/diff.h b/diff.h
index 8ab0448..a06f959 100644
--- a/diff.h
+++ b/diff.h
@@ -123,7 +123,7 @@ #define DIFF_SETUP_REVERSE 1
#define DIFF_SETUP_USE_CACHE 2
#define DIFF_SETUP_USE_SIZE_CACHE 4
-extern int git_diff_config(const char *var, const char *value);
+extern int git_diff_ui_config(const char *var, const char *value);
extern void diff_setup(struct diff_options *);
extern int diff_opt_parse(struct diff_options *, const char **, int);
extern int diff_setup_done(struct diff_options *);
^ permalink raw reply related
* Re: [PATCH] Fix several places where diff.renames in config can be problematic
From: Eric Wong @ 2006-07-08 8:41 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vsllcr077.fsf@assigned-by-dhcp.cox.net>
Junio C Hamano <junkio@cox.net> wrote:
> Eric Wong <normalperson@yhbt.net> writes:
>
> > -my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit);
> > +my @files = safe_pipe_capture('git-diff-tree','--no-renames','-r',
> > + $parent, $commit);
>
> I changed my mind.
>
> -- >8 --
> diff: do not use configuration magic at the core-level
>
> The Porcelainish has become so much usable as the UI that there
> is not much reason people should be using the core programs by
> hand anymore. At this point we are better off making the
> behaviour of the core programs predictable by keeping them
> unaffected by the configuration variables. Otherwise they will
> become very hard to use as reliable building blocks.
>
> For example, "git-commit -a" internally uses git-diff-files to
> figure out the set of paths that need to be updated in the
> index, and we should never allow diff.renames that happens to be
> in the configuration to interfere (or slow down the process).
>
> The UI level configuration such as showing renamed diff and
> coloring are still honored by the Porcelainish ("git log" family
> and "git diff"), but not by the core anymore.
Full ack on this. I was ready to let my diff.renames patch drop
if there were too many potential incompatibilities/breakages,
but this should alleviate that.
I should work on breaking out of the habit of using git
diff-{index,tree} in my day-to-day use and finally start using git diff
more to save some keystrokes.
--
Eric Wong
^ permalink raw reply
* [PATCH] templates/hooks--update: replace diffstat calls with git diff --stat
From: Eric Wong @ 2006-07-08 8:50 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <20060708084121.GD29036@hand.yhbt.net>
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
This was part of the previous patch, but useful on its own since
we have our own internal diffstat
templates/hooks--update | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/templates/hooks--update b/templates/hooks--update
index d7a8f0a..76d5ac2 100644
--- a/templates/hooks--update
+++ b/templates/hooks--update
@@ -60,7 +60,7 @@ then
echo "Changes since $prev:"
git rev-list --pretty $prev..$3 | $short
echo ---
- git diff $prev..$3 | diffstat -p1
+ git diff --stat $prev..$3
echo ---
fi
;;
@@ -75,7 +75,7 @@ else
base=$(git-merge-base "$2" "$3")
case "$base" in
"$2")
- git diff "$3" "^$base" | diffstat -p1
+ git diff --stat "$3" "^$base"
echo
echo "New commits:"
;;
--
1.4.1.g6a62
^ permalink raw reply related
* [PATCH] Close the index file between writing and committing
From: Johannes Schindelin @ 2006-07-08 8:56 UTC (permalink / raw)
To: git, junkio
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
This is just playing it safe.
Alternatively, write_cache() could be taught to close the file
itself, but maybe there will be a user of write_cache() who wants
to write something after the cache data?
builtin-add.c | 2 +-
builtin-apply.c | 2 +-
builtin-read-tree.c | 2 +-
builtin-rm.c | 2 +-
builtin-update-index.c | 2 +-
builtin-write-tree.c | 3 ++-
checkout-index.c | 2 +-
7 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/builtin-add.c b/builtin-add.c
index bfbbb1b..2d25698 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -181,7 +181,7 @@ int cmd_add(int argc, const char **argv,
if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
- commit_lock_file(&lock_file))
+ close(newfd) || commit_lock_file(&lock_file))
die("Unable to write new index file");
}
diff --git a/builtin-apply.c b/builtin-apply.c
index e9ead00..c3af489 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -2323,7 +2323,7 @@ int cmd_apply(int argc, const char **arg
if (write_index) {
if (write_cache(newfd, active_cache, active_nr) ||
- commit_lock_file(&lock_file))
+ close(newfd) || commit_lock_file(&lock_file))
die("Unable to write new index file");
}
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 9a2099d..23a8d92 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -1038,7 +1038,7 @@ int cmd_read_tree(int argc, const char *
}
if (write_cache(newfd, active_cache, active_nr) ||
- commit_lock_file(&lock_file))
+ close(newfd) || commit_lock_file(&lock_file))
die("unable to write new index file");
return 0;
}
diff --git a/builtin-rm.c b/builtin-rm.c
index 4d56a1f..875d825 100644
--- a/builtin-rm.c
+++ b/builtin-rm.c
@@ -147,7 +147,7 @@ int cmd_rm(int argc, const char **argv,
if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
- commit_lock_file(&lock_file))
+ close(newfd) || commit_lock_file(&lock_file))
die("Unable to write new index file");
}
diff --git a/builtin-update-index.c b/builtin-update-index.c
index ef50243..1a4200d 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -648,7 +648,7 @@ int cmd_update_index(int argc, const cha
finish:
if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
- commit_lock_file(lock_file))
+ close(newfd) || commit_lock_file(lock_file))
die("Unable to write new index file");
}
diff --git a/builtin-write-tree.c b/builtin-write-tree.c
index 70e9b6f..449a4d1 100644
--- a/builtin-write-tree.c
+++ b/builtin-write-tree.c
@@ -35,7 +35,8 @@ int write_tree(unsigned char *sha1, int
missing_ok, 0) < 0)
die("git-write-tree: error building trees");
if (0 <= newfd) {
- if (!write_cache(newfd, active_cache, active_nr))
+ if (!write_cache(newfd, active_cache, active_nr)
+ && !close(newfd))
commit_lock_file(lock_file);
}
/* Not being able to write is fine -- we are only interested
diff --git a/checkout-index.c b/checkout-index.c
index ea40bc2..2927955 100644
--- a/checkout-index.c
+++ b/checkout-index.c
@@ -311,7 +311,7 @@ int main(int argc, char **argv)
if (0 <= newfd &&
(write_cache(newfd, active_cache, active_nr) ||
- commit_lock_file(&lock_file)))
+ close(newfd) || commit_lock_file(&lock_file)))
die("Unable to write new index file");
return 0;
}
--
1.4.1.rc2.g5b68a
^ permalink raw reply related
* Re: [PATCH] Close the index file between writing and committing
From: Junio C Hamano @ 2006-07-08 10:28 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git
In-Reply-To: <Pine.LNX.4.63.0607081055520.29667@wbgn013.biozentrum.uni-wuerzburg.de>
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Alternatively, write_cache() could be taught to close the file
> itself, but maybe there will be a user of write_cache() who wants
> to write something after the cache data?
Currently I suspect nobody would want to append stuff to the
index file, because anybody who wants to add something to the
cache can do so with the index extension mechanism.
However, it is conceivable that later we might invent a new
program that writes the index file and other things to a single
stream, and closing the fd in write_cache() would inconvenience
such a program.
^ permalink raw reply
* [PATH] sed -e '/RE/r rfile/' needs space in 'r rfile'
From: Michal Rokos @ 2006-07-08 15:27 UTC (permalink / raw)
To: git
Johannes,
in commit 07002287f3e219a16a948a8a6eca0a41162a491f
you cleaned up 'replace ugly and unportable sed invocation' as you said.
Please note, that some SEDs (like HP-UX one) mandate a space between 'r'
and 'rfile'.
Michal
Signed-off-by: Michal Rokos <michal.rokos@nextsoft.cz>
diff --git a/Makefile b/Makefile
index 202f261..8f9881f 100644
--- a/Makefile
+++ b/Makefile
@@ -552,9 +553,9 @@ git-instaweb: git-instaweb.sh gitweb/git
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
- -e '/@@GITWEB_CGI@@/rgitweb/gitweb.cgi' \
+ -e '/@@GITWEB_CGI@@/r gitweb/gitweb.cgi' \
-e '/@@GITWEB_CGI@@/d' \
- -e '/@@GITWEB_CSS@@/rgitweb/gitweb.css' \
+ -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \
-e '/@@GITWEB_CSS@@/d' \
$@.sh > $@+
chmod +x $@+
--
Michal Rokos
NextSoft s.r.o.
Vyskočilova 1/1410
140 21 Praha 4
phone: +420 267 224 311
fax: +420 267 224 307
mobile: +420 736 646 591
e-mail: michal.rokos@nextsoft.cz
^ permalink raw reply related
* [Patch] Using 'perl' in *.sh
From: Michal Rokos @ 2006-07-08 15:32 UTC (permalink / raw)
To: git
Hi,
some GIT's shell script are using bare 'perl' for perl invocation. It's
causing me problems... I compile git with PERL_PATH set and I'd suggest to
use it everywhere.
So @@PERL@@ would be replaced with PERL_PATH_SQ instead.
What do you think?
Michal
Signed-off-by: Michal Rokos <michal.rokos@nextsoft.cz>
diff --git a/Makefile b/Makefile
index 202f261..8f9881f 100644
--- a/Makefile
+++ b/Makefile
@@ -514,6 +514,7 @@ common-cmds.h: Documentation/git-*.txt
$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
rm -f $@ $@+
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+ -e 's!@@PERL@@!$(PERL_PATH_SQ)!g' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
diff --git a/git-bisect.sh b/git-bisect.sh
index 03df143..06a8d26 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -13,7 +13,7 @@ git bisect log show bisect log.'
. git-sh-setup
sq() {
- perl -e '
+ @@PERL@@ -e '
for (@ARGV) {
s/'\''/'\'\\\\\'\''/g;
print " '\''$_'\''";
diff --git a/git-clone.sh b/git-clone.sh
index 6a14b25..0368803 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -324,7 +324,7 @@ test -d "$GIT_DIR/refs/reference-tmp" &&
if test -f "$GIT_DIR/CLONE_HEAD"
then
# Read git-fetch-pack -k output and store the remote branches.
- perl -e "$copy_refs" "$GIT_DIR" "$use_separate_remote" "$origin"
+ @@PERL@@ -e "$copy_refs" "$GIT_DIR" "$use_separate_remote" "$origin"
fi
cd "$D" || exit
diff --git a/git-commit.sh b/git-commit.sh
index 22c4ce8..08d786d 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -147,7 +147,7 @@ #'
git-ls-files -z --others $option \
--exclude-per-directory=.gitignore
fi |
- perl -e '$/ = "\0";
+ @@PERL@@ -e '$/ = "\0";
my $shown = 0;
while (<>) {
chomp;
diff --git a/git-fetch.sh b/git-fetch.sh
index 48818f8..f80299d 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -278,7 +278,7 @@ fetch_main () {
head="ref: $remote_name"
while (expr "z$head" : "zref:" && expr $depth \< $max_depth) >/dev/null
do
- remote_name_quoted=$(perl -e '
+ remote_name_quoted=$(@@PERL@@ -e '
my $u = $ARGV[0];
$u =~ s/^ref:\s*//;
$u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg;
diff --git a/git-rebase.sh b/git-rebase.sh
index 3945e06..1b9e986 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -311,7 +311,7 @@ echo "$prev_head" > "$dotest/prev_head"
msgnum=0
for cmt in `git-rev-list --no-merges "$upstream"..ORIG_HEAD \
- | perl -e 'print reverse <>'`
+ | @@PERL@@ -e 'print reverse <>'`
do
msgnum=$(($msgnum + 1))
echo "$cmt" > "$dotest/cmt.$msgnum"
--
Michal Rokos
NextSoft s.r.o.
Vyskočilova 1/1410
140 21 Praha 4
phone: +420 267 224 311
fax: +420 267 224 307
mobile: +420 736 646 591
e-mail: michal.rokos@nextsoft.cz
^ permalink raw reply related
* [RFC] just an (stupid) idea when creating a new branch
From: moreau francis @ 2006-07-08 15:55 UTC (permalink / raw)
To: git
Hi GIT folks.
I'm a complete newbie on git development so excuse me if
this idea is completely stupid.
Would it be possible to let the user stick a short explanation
on what a branch is supposed to implement during its creation.
That is
$ git branch --topic "Implement a killer feature \
> This set of patches add the foo feature to GIT" mybranch master
Then it would be possible with another command to consult this topic
after a while. And even more useful, when generating the patch set
with the following git command
$ git-format-patch -n HEAD master
it uses the topic branch to generate as first patch a summary
of the patch serie. That is
"""
Subject: [PATCH 0/n] Implement a killer feature
This This set of patches add the foo feature to GIT.
"""
Is it useless ?
Thanks
Francis
^ permalink raw reply
* [WIP] Status update on merge-recursive in C
From: Johannes Schindelin @ 2006-07-08 16:42 UTC (permalink / raw)
To: git
This is just an update for people being interested. Alex and me were
busy with that project for a few days now. While it has progressed nicely,
there are quite a couple TODOs in merge-recursive.c, just search for "TODO".
For impatient people: yes, it passes all the tests, and yes, according
to the evil test Alex did, it is faster than the Python script.
But no, it is not yet finished. Biggest points are:
- there are still three external calls
- in the end, it should not be necessary to write the index more than once
(just before exiting)
- a lot of things can be refactored to make the code easier and shorter
BTW we cannot just plug in git-merge-tree yet, because git-merge-tree
does not handle renames at all.
This patch is meant for testing, and as such,
- it compile the program to git-merge-recur
- it adjusts the scripts and tests to use git-merge-recur instead of
git-merge-recursive
- it provides "TEST", a script to execute the tests regarding -recursive
- it inlines the changes to read-cache.c (read_cache_from(), discard_cache()
and refresh_cache_entry())
Brought to you by Alex Riesen and Dscho
---
Makefile | 8
TEST | 10
cache.h | 4
git-merge.sh | 6
git-rebase.sh | 4
merge-recursive.c | 1560 +++++++++++++++++++++++++++++++++++++++++++++++
path-list.c | 105 +++
path-list.h | 22 +
read-cache.c | 104 ++-
t/t3402-rebase-merge.sh | 2
10 files changed, 1773 insertions(+), 52 deletions(-)
diff --git a/Makefile b/Makefile
index 8d429a0..11feefe 100644
--- a/Makefile
+++ b/Makefile
@@ -179,7 +179,8 @@ PROGRAMS = \
git-upload-pack$X git-verify-pack$X \
git-symbolic-ref$X \
git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \
- git-describe$X git-merge-tree$X git-blame$X git-imap-send$X
+ git-describe$X git-merge-tree$X git-blame$X git-imap-send$X \
+ git-merge-recur$X
BUILT_INS = git-log$X git-whatchanged$X git-show$X git-update-ref$X \
git-count-objects$X git-diff$X git-push$X git-mailsplit$X \
@@ -647,6 +648,11 @@ git-http-push$X: revision.o http.o http-
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
+merge-recursive.o path-list.o: path-list.h
+git-merge-recur$X: merge-recursive.o path-list.o $(LIB_FILE)
+ $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
+ $(LIBS)
+
$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
$(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
$(DIFF_OBJS): diffcore.h
diff --git a/TEST b/TEST
new file mode 100755
index 0000000..d530983
--- /dev/null
+++ b/TEST
@@ -0,0 +1,10 @@
+#!/bin/sh -x
+cd t || exit
+./t3400-rebase.sh "$@" && \
+./t6020-merge-df.sh "$@" && \
+./t3401-rebase-partial.sh "$@" && \
+./t6021-merge-criss-cross.sh "$@" && \
+./t3402-rebase-merge.sh "$@" && \
+./t6022-merge-rename.sh "$@" && \
+./t6010-merge-base.sh "$@" && \
+:
diff --git a/cache.h b/cache.h
index 7b5c91c..08777ca 100644
--- a/cache.h
+++ b/cache.h
@@ -115,6 +115,7 @@ #define cache_entry_size(len) ((offsetof
extern struct cache_entry **active_cache;
extern unsigned int active_nr, active_alloc, active_cache_changed;
extern struct cache_tree *active_cache_tree;
+extern int cache_errno;
#define GIT_DIR_ENVIRONMENT "GIT_DIR"
#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
@@ -142,13 +143,16 @@ #define alloc_nr(x) (((x)+16)*3/2)
/* Initialize and use the cache information */
extern int read_cache(void);
+extern int read_cache_from(const char *path);
extern int write_cache(int newfd, struct cache_entry **cache, int entries);
+extern int discard_cache(void);
extern int verify_path(const char *path);
extern int cache_name_pos(const char *name, int namelen);
#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */
#define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */
#define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */
extern int add_cache_entry(struct cache_entry *ce, int option);
+extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
extern int remove_cache_entry_at(int pos);
extern int remove_file_from_cache(const char *path);
extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
diff --git a/git-merge.sh b/git-merge.sh
index 24e3b50..b26ca14 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -9,15 +9,15 @@ USAGE='[-n] [--no-commit] [--squash] [-s
LF='
'
-all_strategies='recursive octopus resolve stupid ours'
-default_twohead_strategies='recursive'
+all_strategies='recur recur octopus resolve stupid ours'
+default_twohead_strategies='recur'
default_octopus_strategies='octopus'
no_trivial_merge_strategies='ours'
use_strategies=
index_merge=t
if test "@@NO_PYTHON@@"; then
- all_strategies='resolve octopus stupid ours'
+ all_strategies='recur resolve octopus stupid ours'
default_twohead_strategies='resolve'
fi
diff --git a/git-rebase.sh b/git-rebase.sh
index 3945e06..5d4c7d2 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -35,7 +35,7 @@ If you would prefer to skip this patch,
To restore the original branch and stop rebasing run \"git rebase --abort\".
"
unset newbase
-strategy=recursive
+strategy=recur
do_merge=
dotest=$GIT_DIR/.dotest-merge
prec=4
@@ -292,7 +292,7 @@ then
exit $?
fi
-if test "@@NO_PYTHON@@" && test "$strategy" = "recursive"
+if test "@@NO_PYTHON@@" && test "$strategy" = "recur"
then
die 'The recursive merge strategy currently relies on Python,
which this installation of git was not configured with. Please consider
diff --git a/merge-recursive.c b/merge-recursive.c
new file mode 100644
index 0000000..07a2b38
--- /dev/null
+++ b/merge-recursive.c
@@ -0,0 +1,1560 @@
+/*
+ * Recursive Merge algorithm stolen from git-merge-recursive.py by
+ * Fredrik Kuivinen.
+ * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
+ */
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include "cache.h"
+#include "cache-tree.h"
+#include "commit.h"
+#include "blob.h"
+#include "tree-walk.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "run-command.h"
+#include "tag.h"
+
+#include "path-list.h"
+
+/*#define DEBUG*/
+
+#ifdef DEBUG
+#define debug(args, ...) fprintf(stderr, args, ## __VA_ARGS__)
+#else
+#define debug(args, ...)
+#endif
+
+#ifdef DEBUG
+#include "quote.h"
+static void show_ce_entry(const char *tag, struct cache_entry *ce)
+{
+ if (tag && *tag &&
+ (ce->ce_flags & htons(CE_VALID))) {
+ static char alttag[4];
+ memcpy(alttag, tag, 3);
+ if (isalpha(tag[0]))
+ alttag[0] = tolower(tag[0]);
+ else if (tag[0] == '?')
+ alttag[0] = '!';
+ else {
+ alttag[0] = 'v';
+ alttag[1] = tag[0];
+ alttag[2] = ' ';
+ alttag[3] = 0;
+ }
+ tag = alttag;
+ }
+
+ fprintf(stderr,"%s%06o %s %d\t",
+ tag,
+ ntohl(ce->ce_mode),
+ sha1_to_hex(ce->sha1),
+ ce_stage(ce));
+ write_name_quoted("", 0, ce->name,
+ '\n', stderr);
+ fputc('\n', stderr);
+}
+
+static void ls_files() {
+ int i;
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ show_ce_entry("", ce);
+ }
+ fprintf(stderr, "---\n");
+}
+#endif
+
+/*
+ * A virtual commit has
+ * - (const char *)commit->util set to the name, and
+ * - *(int *)commit->object.sha1 set to the virtual id.
+ */
+static const char *commit_title(struct commit *commit, int *len)
+{
+ const char *s = "(null commit)";
+ *len = strlen(s);
+
+ if ( commit->util ) {
+ s = commit->util;
+ *len = strlen(s);
+ } else {
+ if ( parse_commit(commit) != 0 ) {
+ s = "(bad commit)";
+ *len = strlen(s);
+ } else {
+ s = commit->buffer;
+ char prev = '\0';
+ while ( *s ) {
+ if ( '\n' == prev && '\n' == *s ) {
+ ++s;
+ break;
+ }
+ prev = *s++;
+ }
+ *len = 0;
+ while ( s[*len] && '\n' != s[*len] )
+ ++(*len);
+ }
+ }
+ return s;
+}
+
+static const char *commit_hex_sha1(const struct commit *commit)
+{
+ return commit->util ? "virtual" : commit ?
+ sha1_to_hex(commit->object.sha1) : "undefined";
+}
+
+static unsigned commit_list_count(const struct commit_list *l)
+{
+ unsigned c = 0;
+ for (; l; l = l->next )
+ c++;
+ return c;
+}
+
+static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
+{
+ struct commit *commit = xcalloc(1, sizeof(struct commit));
+ static unsigned virtual_id = 1;
+ commit->tree = tree;
+ commit->util = (void*)comment;
+ *(int*)commit->object.sha1 = virtual_id++;
+ return commit;
+}
+
+/*
+ * TODO: we should not have to copy the SHA1s around, but rather reference
+ * them. That way, sha_eq() is just sha1 == sha2.
+ */
+static int sha_eq(const unsigned char *a, const unsigned char *b)
+{
+ if ( !a && !b )
+ return 2;
+ return a && b && memcmp(a, b, 20) == 0;
+}
+
+static void memswp(void *p1, void *p2, unsigned n)
+{
+ unsigned char *a = p1, *b = p2;
+ while ( n-- ) {
+ *a ^= *b;
+ *b ^= *a;
+ *a ^= *b;
+ ++a;
+ ++b;
+ }
+}
+
+/*
+ * TODO: we should convert the merge_result users to
+ * int blabla(..., struct commit **result)
+ * like everywhere else in git.
+ * Same goes for merge_tree_result and merge_file_info.
+ */
+struct merge_result
+{
+ struct commit *commit;
+ unsigned clean:1;
+};
+
+struct merge_tree_result
+{
+ struct tree *tree;
+ unsigned clean:1;
+};
+
+/*
+ * TODO: check if we can just reuse the active_cache structure: it is already
+ * sorted (by name, stage).
+ * Only problem: do not write it when flushing the cache.
+ */
+struct stage_data
+{
+ struct
+ {
+ unsigned mode;
+ unsigned char sha[20];
+ } stages[4];
+ unsigned processed:1;
+};
+
+static struct path_list currentFileSet = {NULL, 0, 0, 1};
+static struct path_list currentDirectorySet = {NULL, 0, 0, 1};
+
+static int output_indent = 0;
+
+static void output(const char *fmt, ...)
+{
+ va_list args;
+ int i;
+ for ( i = output_indent; i--; )
+ fputs(" ", stdout);
+ va_start(args, fmt);
+ vfprintf(stdout, fmt, args);
+ va_end(args);
+ fputc('\n', stdout);
+}
+
+static const char *original_index_file;
+static const char *temporary_index_file;
+static int cache_dirty = 0;
+
+static int flush_cache()
+{
+ /* flush temporary index */
+ struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+ int fd = hold_lock_file_for_update(lock, getenv("GIT_INDEX_FILE"));
+ if (fd < 0)
+ die("could not lock %s", temporary_index_file);
+ if (write_cache(fd, active_cache, active_nr) ||
+ close(fd) || commit_lock_file(lock))
+ die ("unable to write %s", getenv("GIT_INDEX_FILE"));
+ discard_cache();
+ cache_dirty = 0;
+ return 0;
+}
+
+static void setup_index(int temp)
+{
+ const char *idx = temp ? temporary_index_file: original_index_file;
+ if (cache_dirty)
+ die("fatal: cache changed flush_cache();");
+ unlink(temporary_index_file);
+ setenv("GIT_INDEX_FILE", idx, 1);
+ discard_cache();
+}
+
+static struct cache_entry *make_cache_entry(unsigned int mode,
+ const unsigned char *sha1, const char *path, int stage, int refresh)
+{
+ int size, len;
+ struct cache_entry *ce;
+
+ if (!verify_path(path))
+ return NULL;
+
+ len = strlen(path);
+ size = cache_entry_size(len);
+ ce = xcalloc(1, size);
+
+ memcpy(ce->sha1, sha1, 20);
+ memcpy(ce->name, path, len);
+ ce->ce_flags = create_ce_flags(len, stage);
+ ce->ce_mode = create_ce_mode(mode);
+
+ if (refresh)
+ return refresh_cache_entry(ce, 0);
+
+ return ce;
+}
+
+static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
+ const char *path, int stage, int refresh, int options)
+{
+ struct cache_entry *ce;
+ if (!cache_dirty)
+ read_cache_from(getenv("GIT_INDEX_FILE"));
+ cache_dirty++;
+ ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh);
+ if (!ce)
+ return error("cache_addinfo failed: %s", strerror(cache_errno));
+ return add_cache_entry(ce, options);
+}
+
+/*
+ * This is a global variable which is used in a number of places but
+ * only written to in the 'merge' function.
+ *
+ * index_only == 1 => Don't leave any non-stage 0 entries in the cache and
+ * don't update the working directory.
+ * 0 => Leave unmerged entries in the cache and update
+ * the working directory.
+ */
+static int index_only = 0;
+
+/*
+ * TODO: this can be streamlined by refactoring builtin-read-tree.c
+ */
+static int git_read_tree(const struct tree *tree)
+{
+#if 0
+ fprintf(stderr, "GIT_INDEX_FILE='%s' git-read-tree %s\n",
+ getenv("GIT_INDEX_FILE"),
+ sha1_to_hex(tree->object.sha1));
+#endif
+ const char *argv[] = { "git-read-tree", NULL, NULL, };
+ if (cache_dirty)
+ die("read-tree with dirty cache");
+ argv[1] = sha1_to_hex(tree->object.sha1);
+ int rc = run_command_v(2, argv);
+ return rc < 0 ? -1: rc;
+}
+
+/*
+ * TODO: this can be streamlined by refactoring builtin-read-tree.c
+ */
+static int git_merge_trees(const char *update_arg,
+ struct tree *common,
+ struct tree *head,
+ struct tree *merge)
+{
+#if 0
+ fprintf(stderr, "GIT_INDEX_FILE='%s' git-read-tree %s -m %s %s %s\n",
+ getenv("GIT_INDEX_FILE"),
+ update_arg,
+ sha1_to_hex(common->object.sha1),
+ sha1_to_hex(head->object.sha1),
+ sha1_to_hex(merge->object.sha1));
+#endif
+ const char *argv[] = {
+ "git-read-tree", NULL, "-m", NULL, NULL, NULL,
+ NULL,
+ };
+ if (cache_dirty)
+ flush_cache();
+ argv[1] = update_arg;
+ argv[3] = sha1_to_hex(common->object.sha1);
+ argv[4] = sha1_to_hex(head->object.sha1);
+ argv[5] = sha1_to_hex(merge->object.sha1);
+ int rc = run_command_v(6, argv);
+ return rc < 0 ? -1: rc;
+}
+
+/*
+ * TODO: this can be streamlined by refactoring builtin-write-tree.c
+ */
+static struct tree *git_write_tree()
+{
+#if 0
+ fprintf(stderr, "GIT_INDEX_FILE='%s' git-write-tree\n",
+ getenv("GIT_INDEX_FILE"));
+#endif
+ if (cache_dirty)
+ flush_cache();
+ FILE *fp = popen("git-write-tree 2>/dev/null", "r");
+ char buf[41];
+ unsigned char sha1[20];
+ int ch;
+ unsigned i = 0;
+ while ( (ch = fgetc(fp)) != EOF )
+ if ( i < sizeof(buf)-1 && ch >= '0' && ch <= 'f' )
+ buf[i++] = ch;
+ else
+ break;
+ int rc = pclose(fp);
+ if ( rc == -1 || WEXITSTATUS(rc) )
+ return NULL;
+ buf[i] = '\0';
+ if ( get_sha1(buf, sha1) != 0 )
+ return NULL;
+ return lookup_tree(sha1);
+}
+
+/*
+ * TODO: get rid of files_and_dirs; we do not use it except for
+ * current_file_set and current_dir_set, which are global already.
+ */
+static struct
+{
+ struct path_list *files;
+ struct path_list *dirs;
+} files_and_dirs;
+
+static int save_files_dirs(const unsigned char *sha1,
+ const char *base, int baselen, const char *path,
+ unsigned int mode, int stage)
+{
+ int len = strlen(path);
+ char *newpath = malloc(baselen + len + 1);
+ memcpy(newpath, base, baselen);
+ memcpy(newpath + baselen, path, len);
+ newpath[baselen + len] = '\0';
+
+ if (S_ISDIR(mode))
+ path_list_insert(newpath, files_and_dirs.dirs);
+ else
+ path_list_insert(newpath, files_and_dirs.files);
+ free(newpath);
+
+ return READ_TREE_RECURSIVE;
+}
+
+static int get_files_dirs(struct tree *tree,
+ struct path_list *files,
+ struct path_list *dirs)
+{
+ int n;
+ files_and_dirs.files = files;
+ files_and_dirs.dirs = dirs;
+ debug("get_files_dirs ...\n");
+ if (read_tree_recursive(tree, "", 0, 0, NULL, save_files_dirs) != 0) {
+ debug(" get_files_dirs done (0)\n");
+ return 0;
+ }
+ n = files->nr + dirs->nr;
+ debug(" get_files_dirs done (%d)\n", n);
+ return n;
+}
+
+/*
+ * TODO: this wrapper is so small, we can use path_list_lookup directly.
+ * Same goes for index_entry_get(), free_index_entries(), find_rename_bysrc(),
+ * free_rename_entries().
+ */
+static struct stage_data *index_entry_find(struct path_list *ents,
+ const char *path)
+{
+ struct path_list_item *item = path_list_lookup(path, ents);
+ if (item)
+ return item->util;
+ return NULL;
+}
+
+static struct stage_data *index_entry_get(struct path_list *ents,
+ const char *path)
+{
+ struct path_list_item *item = path_list_lookup(path, ents);
+
+ if (item == NULL) {
+ item = path_list_insert(path, ents);
+ item->util = xcalloc(1, sizeof(struct stage_data));
+ }
+ return item->util;
+}
+
+/*
+ * TODO: since the result of index_entry_from_db() is tucked into a
+ * path_list anyway, this helper can do that already.
+ */
+/*
+ * Returns a index_entry instance which doesn't have to correspond to
+ * a real cache entry in Git's index.
+ */
+static struct stage_data *index_entry_from_db(const char *path,
+ struct tree *o,
+ struct tree *a,
+ struct tree *b)
+{
+ struct stage_data *e = xcalloc(1, sizeof(struct stage_data));
+ get_tree_entry(o->object.sha1, path,
+ e->stages[1].sha, &e->stages[1].mode);
+ get_tree_entry(a->object.sha1, path,
+ e->stages[2].sha, &e->stages[2].mode);
+ get_tree_entry(b->object.sha1, path,
+ e->stages[3].sha, &e->stages[3].mode);
+ return e;
+}
+
+static void free_index_entries(struct path_list **ents)
+{
+ if (!*ents)
+ return;
+
+ path_list_clear(*ents, 1);
+ free(*ents);
+ *ents = NULL;
+}
+
+/*
+ * Create a dictionary mapping file names to CacheEntry objects. The
+ * dictionary contains one entry for every path with a non-zero stage entry.
+ */
+static struct path_list *get_unmerged()
+{
+ struct path_list *unmerged = xcalloc(1, sizeof(struct path_list));
+ int i;
+
+ unmerged->strdup_paths = 1;
+ if (!cache_dirty) {
+ read_cache_from(getenv("GIT_INDEX_FILE"));
+ cache_dirty++;
+ }
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ if (!ce_stage(ce))
+ continue;
+
+ struct stage_data *e = index_entry_get(unmerged, ce->name);
+ e->stages[ce_stage(ce)].mode = ntohl(ce->ce_mode);
+ memcpy(e->stages[ce_stage(ce)].sha, ce->sha1, 20);
+ }
+
+ debug(" get_unmerged done\n");
+ return unmerged;
+}
+
+struct rename
+{
+ struct diff_filepair *pair;
+ struct stage_data *src_entry;
+ struct stage_data *dst_entry;
+ unsigned processed:1;
+};
+
+static struct rename *find_rename_bysrc(struct path_list *e,
+ const char *name)
+{
+ struct path_list_item *item = path_list_lookup(name, e);
+ if (item)
+ return item->util;
+ return NULL;
+}
+
+static void free_rename_entries(struct path_list **list)
+{
+ if (!*list)
+ return;
+
+ path_list_clear(*list, 0);
+ free(*list);
+ *list = NULL;
+}
+
+/*
+ * Get information of all renames which occured between 'oTree' and
+ * 'tree'. We need the three trees in the merge ('oTree', 'aTree' and
+ * 'bTree') to be able to associate the correct cache entries with
+ * the rename information. 'tree' is always equal to either aTree or bTree.
+ */
+static struct path_list *get_renames(struct tree *tree,
+ struct tree *oTree,
+ struct tree *aTree,
+ struct tree *bTree,
+ struct path_list *entries)
+{
+#ifdef DEBUG
+ time_t t = time(0);
+ debug("getRenames ...\n");
+#endif
+ int i;
+ struct path_list *renames = xcalloc(1, sizeof(struct path_list));
+ struct diff_options opts;
+ diff_setup(&opts);
+ opts.recursive = 1;
+ opts.detect_rename = DIFF_DETECT_RENAME;
+ opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+ if (diff_setup_done(&opts) < 0)
+ die("diff setup failed");
+ diff_tree_sha1(oTree->object.sha1, tree->object.sha1, "", &opts);
+ diffcore_std(&opts);
+ for (i = 0; i < diff_queued_diff.nr; ++i) {
+ struct rename *re;
+ struct diff_filepair *pair = diff_queued_diff.queue[i];
+ if (pair->status != 'R') {
+ diff_free_filepair(pair);
+ continue;
+ }
+ re = xmalloc(sizeof(*re));
+ re->processed = 0;
+ re->pair = pair;
+ re->src_entry = index_entry_find(entries, re->pair->one->path);
+ /* TODO: should it not be an error, if src_entry was found? */
+ if ( !re->src_entry ) {
+ re->src_entry = index_entry_from_db(re->pair->one->path,
+ oTree, aTree, bTree);
+ struct path_list_item *item =
+ path_list_insert(re->pair->one->path, entries);
+ item->util = re->src_entry;
+ }
+ re->dst_entry = index_entry_find(entries, re->pair->two->path);
+ if ( !re->dst_entry ) {
+ re->dst_entry = index_entry_from_db(re->pair->two->path,
+ oTree, aTree, bTree);
+ struct path_list_item *item =
+ path_list_insert(re->pair->two->path, entries);
+ item->util = re->dst_entry;
+ }
+ struct path_list_item *item = path_list_insert(pair->one->path, renames);
+ item->util = re;
+ }
+ opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+ diff_queued_diff.nr = 0;
+ diff_flush(&opts);
+ debug(" getRenames done in %ld\n", time(0)-t);
+ return renames;
+}
+
+/*
+ * TODO: the code would be way nicer, if we had a struct containing just sha1 and mode.
+ * In this particular case, we might get away reusing stage_data, no?
+ */
+int update_stages(const char *path,
+ unsigned char *osha, unsigned omode,
+ unsigned char *asha, unsigned amode,
+ unsigned char *bsha, unsigned bmode,
+ int clear /* =True */)
+{
+ int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
+ if ( clear )
+ if (add_cacheinfo(0, null_sha1, path, 0, 0, options))
+ return -1;
+ if ( omode )
+ if (add_cacheinfo(omode, osha, path, 1, 0, options))
+ return -1;
+ if ( amode )
+ if (add_cacheinfo(omode, osha, path, 2, 0, options))
+ return -1;
+ if ( bmode )
+ if (add_cacheinfo(omode, osha, path, 3, 0, options))
+ return -1;
+ return 0;
+}
+
+/*
+ * TODO: there has to be a function in libgit doing this exact thing.
+ */
+static int remove_path(const char *name)
+{
+ int ret;
+ char *slash;
+
+ ret = unlink(name);
+ if ( ret )
+ return ret;
+ int len = strlen(name);
+ char *dirs = malloc(len+1);
+ memcpy(dirs, name, len);
+ dirs[len] = '\0';
+ while ( (slash = strrchr(name, '/')) ) {
+ *slash = '\0';
+ len = slash - name;
+ if ( rmdir(name) != 0 )
+ break;
+ }
+ free(dirs);
+ return ret;
+}
+
+/* General TODO: unC99ify the code: no declaration after code */
+/* General TODO: no javaIfiCation: rename updateCache to update_cache */
+/*
+ * TODO: once we no longer call external programs, we'd probably be better of
+ * not setting / getting the environment variable GIT_INDEX_FILE all the time.
+ */
+int remove_file(int clean, const char *path)
+{
+ int updateCache = index_only || clean;
+ int updateWd = !index_only;
+
+ if ( updateCache ) {
+ if (!cache_dirty)
+ read_cache_from(getenv("GIT_INDEX_FILE"));
+ cache_dirty++;
+ if (remove_file_from_cache(path))
+ return -1;
+ }
+ if ( updateWd )
+ {
+ unlink(path);
+ if ( errno != ENOENT || errno != EISDIR )
+ return -1;
+ remove_path(path);
+ }
+ return 0;
+}
+
+static char *unique_path(const char *path, const char *branch)
+{
+ char *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1);
+ strcpy(newpath, path);
+ strcat(newpath, "~");
+ char *p = newpath + strlen(newpath);
+ strcpy(p, branch);
+ for ( ; *p; ++p )
+ if ( '/' == *p )
+ *p = '_';
+ int suffix = 0;
+ struct stat st;
+ while ( path_list_has_path(¤tFileSet, newpath) ||
+ path_list_has_path(¤tDirectorySet, newpath) ||
+ lstat(newpath, &st) == 0 ) {
+ sprintf(p, "_%d", suffix++);
+ }
+ path_list_insert(newpath, ¤tFileSet);
+ return newpath;
+}
+
+/*
+ * TODO: except for create_last, this so looks like
+ * safe_create_leading_directories().
+ */
+static int mkdir_p(const char *path, unsigned long mode, int create_last)
+{
+ char *buf = strdup(path);
+ char *p;
+
+ for ( p = buf; *p; ++p ) {
+ if ( *p != '/' )
+ continue;
+ *p = '\0';
+ if (mkdir(buf, mode)) {
+ int e = errno;
+ if ( e == EEXIST ) {
+ struct stat st;
+ if ( !stat(buf, &st) && S_ISDIR(st.st_mode) )
+ goto next; /* ok */
+ errno = e;
+ }
+ free(buf);
+ return -1;
+ }
+ next:
+ *p = '/';
+ }
+ free(buf);
+ if ( create_last && mkdir(path, mode) )
+ return -1;
+ return 0;
+}
+
+static void flush_buffer(int fd, const char *buf, unsigned long size)
+{
+ while (size > 0) {
+ long ret = xwrite(fd, buf, size);
+ if (ret < 0) {
+ /* Ignore epipe */
+ if (errno == EPIPE)
+ break;
+ die("merge-recursive: %s", strerror(errno));
+ } else if (!ret) {
+ die("merge-recursive: disk full?");
+ }
+ size -= ret;
+ buf += ret;
+ }
+}
+
+/* General TODO: reindent according to guide lines (no if ( blabla )) */
+void update_file_flags(const unsigned char *sha,
+ unsigned mode,
+ const char *path,
+ int updateCache,
+ int updateWd)
+{
+ if ( index_only )
+ updateWd = 0;
+
+ if ( updateWd ) {
+ char type[20];
+ void *buf;
+ unsigned long size;
+
+ buf = read_sha1_file(sha, type, &size);
+ if (!buf)
+ die("cannot read object %s '%s'", sha1_to_hex(sha), path);
+ if ( strcmp(type, blob_type) != 0 )
+ die("blob expected for %s '%s'", sha1_to_hex(sha), path);
+
+ if ( S_ISREG(mode) ) {
+ if ( mkdir_p(path, 0777, 0 /* don't create last element */) )
+ die("failed to create path %s: %s", path, strerror(errno));
+ unlink(path);
+ if ( mode & 0100 )
+ mode = 0777;
+ else
+ mode = 0666;
+ int fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
+ if ( fd < 0 )
+ die("failed to open %s: %s", path, strerror(errno));
+ flush_buffer(fd, buf, size);
+ close(fd);
+ } else if ( S_ISLNK(mode) ) {
+ char *linkTarget = malloc(size + 1);
+ memcpy(linkTarget, buf, size);
+ linkTarget[size] = '\0';
+ mkdir_p(path, 0777, 0);
+ symlink(linkTarget, path);
+ } else
+ die("do not know what to do with %06o %s '%s'",
+ mode, sha1_to_hex(sha), path);
+ }
+ if ( updateCache )
+ add_cacheinfo(mode, sha, path, 0, updateWd, ADD_CACHE_OK_TO_ADD);
+}
+
+/* TODO: is this often used? if not, do direct call */
+void update_file(int clean,
+ const unsigned char *sha,
+ unsigned mode,
+ const char *path)
+{
+ update_file_flags(sha, mode, path, index_only || clean, !index_only);
+}
+
+/* Low level file merging, update and removal */
+
+struct merge_file_info
+{
+ unsigned char sha[20];
+ unsigned mode;
+ unsigned clean:1,
+ merge:1;
+};
+
+static char *git_unpack_file(const unsigned char *sha1, char *path)
+{
+ void *buf;
+ char type[20];
+ unsigned long size;
+ int fd;
+
+ buf = read_sha1_file(sha1, type, &size);
+ if (!buf || strcmp(type, blob_type))
+ die("unable to read blob object %s", sha1_to_hex(sha1));
+
+ strcpy(path, ".merge_file_XXXXXX");
+ fd = mkstemp(path);
+ if (fd < 0)
+ die("unable to create temp-file");
+ flush_buffer(fd, buf, size);
+ close(fd);
+ return path;
+}
+
+/*
+ * TODO: the signature would be much more efficient using stage_data
+ */
+static struct merge_file_info merge_file(const char *oPath,
+ const unsigned char *oSha,
+ unsigned oMode,
+ const char *aPath,
+ const unsigned char *aSha,
+ unsigned aMode,
+ const char *bPath,
+ const unsigned char *bSha,
+ unsigned bMode,
+ const char *branch1Name,
+ const char *branch2Name)
+{
+ struct merge_file_info result;
+ result.merge = 0;
+ result.clean = 1;
+
+ if ( (S_IFMT & aMode) != (S_IFMT & bMode) ) {
+ result.clean = 0;
+ if ( S_ISREG(aMode) ) {
+ result.mode = aMode;
+ memcpy(result.sha, aSha, 20);
+ } else {
+ result.mode = bMode;
+ memcpy(result.sha, bSha, 20);
+ }
+ } else {
+ if ( memcmp(aSha, oSha, 20) != 0 && memcmp(bSha, oSha, 20) != 0 )
+ result.merge = 1;
+
+ result.mode = aMode == oMode ? bMode: aMode;
+
+ if ( memcmp(aSha, oSha, 20) == 0 )
+ memcpy(result.sha, bSha, 20);
+ else if ( memcmp(bSha, oSha, 20) == 0 )
+ memcpy(result.sha, aSha, 20);
+ else if ( S_ISREG(aMode) ) {
+
+ int code = 1;
+ char orig[PATH_MAX];
+ char src1[PATH_MAX];
+ char src2[PATH_MAX];
+
+ git_unpack_file(oSha, orig);
+ git_unpack_file(aSha, src1);
+ git_unpack_file(bSha, src2);
+
+ const char *argv[] = {
+ "merge", "-L", NULL, "-L", NULL, "-L", NULL,
+ src1, orig, src2,
+ NULL
+ };
+ char *la, *lb, *lo;
+ argv[2] = la = strdup(mkpath("%s/%s", branch1Name, aPath));
+ argv[6] = lb = strdup(mkpath("%s/%s", branch2Name, bPath));
+ argv[4] = lo = strdup(mkpath("orig/%s", oPath));
+
+#if 0
+ printf("%s %s %s %s %s %s %s %s %s %s\n",
+ argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6], argv[7], argv[8], argv[9]);
+#endif
+ code = run_command_v(10, argv);
+
+ free(la);
+ free(lb);
+ free(lo);
+ if ( code && code < -256 ) {
+ die("Failed to execute 'merge'. merge(1) is used as the "
+ "file-level merge tool. Is 'merge' in your path?");
+ }
+ struct stat st;
+ int fd = open(src1, O_RDONLY);
+ if (fd < 0 || fstat(fd, &st) < 0 ||
+ index_fd(result.sha, fd, &st, 1,
+ "blob"))
+ die("Unable to add %s to database", src1);
+ close(fd);
+
+ unlink(orig);
+ unlink(src1);
+ unlink(src2);
+
+ result.clean = WEXITSTATUS(code) == 0;
+ } else {
+ if ( !(S_ISLNK(aMode) || S_ISLNK(bMode)) )
+ die("cannot merge modes?");
+
+ memcpy(result.sha, aSha, 20);
+
+ if ( memcmp(aSha, bSha, 20) != 0 )
+ result.clean = 0;
+ }
+ }
+
+ return result;
+}
+
+static void conflict_rename_rename(struct rename *ren1,
+ const char *branch1,
+ struct rename *ren2,
+ const char *branch2)
+{
+ char *del[2];
+ int delp = 0;
+ const char *ren1_dst = ren1->pair->two->path;
+ const char *ren2_dst = ren2->pair->two->path;
+ const char *dstName1 = ren1_dst;
+ const char *dstName2 = ren2_dst;
+ if (path_list_has_path(¤tDirectorySet, ren1_dst)) {
+ dstName1 = del[delp++] = unique_path(ren1_dst, branch1);
+ output("%s is a directory in %s adding as %s instead",
+ ren1_dst, branch2, dstName1);
+ remove_file(0, ren1_dst);
+ }
+ if (path_list_has_path(¤tDirectorySet, ren2_dst)) {
+ dstName2 = del[delp++] = unique_path(ren2_dst, branch2);
+ output("%s is a directory in %s adding as %s instead",
+ ren2_dst, branch1, dstName2);
+ remove_file(0, ren2_dst);
+ }
+ update_stages(dstName1,
+ NULL, 0,
+ ren1->pair->two->sha1, ren1->pair->two->mode,
+ NULL, 0,
+ 1 /* clear */);
+ update_stages(dstName2,
+ NULL, 0,
+ NULL, 0,
+ ren2->pair->two->sha1, ren2->pair->two->mode,
+ 1 /* clear */);
+ while ( delp-- )
+ free(del[delp]);
+}
+
+static void conflict_rename_dir(struct rename *ren1,
+ const char *branch1)
+{
+ char *newPath = unique_path(ren1->pair->two->path, branch1);
+ output("Renaming %s to %s instead", ren1->pair->one->path, newPath);
+ remove_file(0, ren1->pair->two->path);
+ update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, newPath);
+ free(newPath);
+}
+
+static void conflict_rename_rename_2(struct rename *ren1,
+ const char *branch1,
+ struct rename *ren2,
+ const char *branch2)
+{
+ char *newPath1 = unique_path(ren1->pair->two->path, branch1);
+ char *newPath2 = unique_path(ren2->pair->two->path, branch2);
+ output("Renaming %s to %s and %s to %s instead",
+ ren1->pair->one->path, newPath1,
+ ren2->pair->one->path, newPath2);
+ remove_file(0, ren1->pair->two->path);
+ update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, newPath1);
+ update_file(0, ren2->pair->two->sha1, ren2->pair->two->mode, newPath2);
+ free(newPath2);
+ free(newPath1);
+}
+
+/* General TODO: get rid of all the debug messages */
+static int process_renames(struct path_list *renamesA,
+ struct path_list *renamesB,
+ const char *branchNameA,
+ const char *branchNameB)
+{
+ int cleanMerge = 1, i;
+ struct path_list srcNames = {NULL, 0, 0, 0}, byDstA = {NULL, 0, 0, 0}, byDstB = {NULL, 0, 0, 0};
+ const struct rename *sre;
+
+ /*
+ * TODO: think about a saner way to do this.
+ * Since both renamesA and renamesB are sorted, it should
+ * be much more efficient to traverse both simultaneously,
+ * only byDstA and byDstB should be needed.
+ */
+ debug("processRenames...\n");
+ for (i = 0; i < renamesA->nr; i++) {
+ sre = renamesA->items[i].util;
+ path_list_insert(sre->pair->one->path, &srcNames);
+ path_list_insert(sre->pair->two->path, &byDstA)->util
+ = sre->dst_entry;
+ }
+ for (i = 0; i < renamesB->nr; i++) {
+ sre = renamesB->items[i].util;
+ path_list_insert(sre->pair->one->path, &srcNames);
+ path_list_insert(sre->pair->two->path, &byDstB)->util
+ = sre->dst_entry;
+ }
+
+ for (i = 0; i < srcNames.nr; i++) {
+ char *src = srcNames.items[i].path;
+ struct path_list *renames1, *renames2, *renames2Dst;
+ struct rename *ren1, *ren2;
+ const char *branchName1, *branchName2;
+ ren1 = find_rename_bysrc(renamesA, src);
+ ren2 = find_rename_bysrc(renamesB, src);
+ /* TODO: refactor, so that 1/2 are not needed */
+ if ( ren1 ) {
+ renames1 = renamesA;
+ renames2 = renamesB;
+ renames2Dst = &byDstB;
+ branchName1 = branchNameA;
+ branchName2 = branchNameB;
+ } else {
+ renames1 = renamesB;
+ renames2 = renamesA;
+ renames2Dst = &byDstA;
+ branchName1 = branchNameB;
+ branchName2 = branchNameA;
+ struct rename *tmp = ren2;
+ ren2 = ren1;
+ ren1 = tmp;
+ }
+
+ ren1->dst_entry->processed = 1;
+ ren1->src_entry->processed = 1;
+
+ if ( ren1->processed )
+ continue;
+ ren1->processed = 1;
+
+ const char *ren1_src = ren1->pair->one->path;
+ const char *ren1_dst = ren1->pair->two->path;
+
+ if ( ren2 ) {
+ const char *ren2_src = ren2->pair->one->path;
+ const char *ren2_dst = ren2->pair->two->path;
+ /* Renamed in 1 and renamed in 2 */
+ if (strcmp(ren1_src, ren2_src) != 0)
+ die("ren1.src != ren2.src");
+ ren2->dst_entry->processed = 1;
+ ren2->processed = 1;
+ if (strcmp(ren1_dst, ren2_dst) != 0) {
+ cleanMerge = 0;
+ output("CONFLICT (rename/rename): "
+ "Rename %s->%s in branch %s "
+ "rename %s->%s in %s",
+ src, ren1_dst, branchName1,
+ src, ren2_dst, branchName2);
+ conflict_rename_rename(ren1, branchName1, ren2, branchName2);
+ } else {
+ remove_file(1, ren1_src);
+ struct merge_file_info mfi;
+ mfi = merge_file(ren1_src,
+ ren1->pair->one->sha1,
+ ren1->pair->one->mode,
+ ren1_dst,
+ ren1->pair->two->sha1,
+ ren1->pair->two->mode,
+ ren2_dst,
+ ren2->pair->two->sha1,
+ ren2->pair->two->mode,
+ branchName1,
+ branchName2);
+ if ( mfi.merge || !mfi.clean )
+ output("Renaming %s->%s", src, ren1_dst);
+
+ if ( mfi.merge )
+ output("Auto-merging %s", ren1_dst);
+
+ if ( !mfi.clean ) {
+ output("CONFLICT (content): merge conflict in %s",
+ ren1_dst);
+ cleanMerge = 0;
+
+ if ( !index_only )
+ update_stages(ren1_dst,
+ ren1->pair->one->sha1,
+ ren1->pair->one->mode,
+ ren1->pair->two->sha1,
+ ren1->pair->two->mode,
+ ren2->pair->two->sha1,
+ ren2->pair->two->mode,
+ 1 /* clear */);
+ }
+ update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst);
+ }
+ } else {
+ /* Renamed in 1, maybe changed in 2 */
+ remove_file(1, ren1_src);
+
+ unsigned char srcShaOtherBranch[20], dstShaOtherBranch[20];
+ unsigned srcModeOtherBranch, dstModeOtherBranch;
+
+ int stage = renamesA == renames1 ? 3: 2;
+
+ memcpy(srcShaOtherBranch, ren1->src_entry->stages[stage].sha, 20);
+ srcModeOtherBranch = ren1->src_entry->stages[stage].mode;
+
+ memcpy(dstShaOtherBranch, ren1->dst_entry->stages[stage].sha, 20);
+ dstModeOtherBranch = ren1->dst_entry->stages[stage].mode;
+
+ int tryMerge = 0;
+ char *newPath;
+
+ if (path_list_has_path(¤tDirectorySet, ren1_dst)) {
+ cleanMerge = 0;
+ output("CONFLICT (rename/directory): Rename %s->%s in %s "
+ " directory %s added in %s",
+ ren1_src, ren1_dst, branchName1,
+ ren1_dst, branchName2);
+ conflict_rename_dir(ren1, branchName1);
+ } else if ( memcmp(srcShaOtherBranch, null_sha1, 20) == 0 ) {
+ cleanMerge = 0;
+ output("CONFLICT (rename/delete): Rename %s->%s in %s "
+ "and deleted in %s",
+ ren1_src, ren1_dst, branchName1,
+ branchName2);
+ update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst);
+ } else if ( memcmp(dstShaOtherBranch, null_sha1, 20) != 0 ) {
+ cleanMerge = 0;
+ tryMerge = 1;
+ output("CONFLICT (rename/add): Rename %s->%s in %s. "
+ "%s added in %s",
+ ren1_src, ren1_dst, branchName1,
+ ren1_dst, branchName2);
+ newPath = unique_path(ren1_dst, branchName2);
+ output("Adding as %s instead", newPath);
+ update_file(0, dstShaOtherBranch, dstModeOtherBranch, newPath);
+ } else if ( (ren2 = find_rename_bysrc(renames2Dst, ren1_dst)) ) {
+ cleanMerge = 0;
+ ren2->processed = 1;
+ output("CONFLICT (rename/rename): Rename %s->%s in %s. "
+ "Rename %s->%s in %s",
+ ren1_src, ren1_dst, branchName1,
+ ren2->pair->one->path, ren2->pair->two->path, branchName2);
+ conflict_rename_rename_2(ren1, branchName1, ren2, branchName2);
+ } else
+ tryMerge = 1;
+
+ if ( tryMerge ) {
+ const char *oname = ren1_src;
+ const char *aname = ren1_dst;
+ const char *bname = ren1_src;
+ unsigned char osha[20], asha[20], bsha[20];
+ unsigned omode = ren1->pair->one->mode;
+ unsigned amode = ren1->pair->two->mode;
+ unsigned bmode = srcModeOtherBranch;
+ memcpy(osha, ren1->pair->one->sha1, 20);
+ memcpy(asha, ren1->pair->two->sha1, 20);
+ memcpy(bsha, srcShaOtherBranch, 20);
+ const char *aBranch = branchName1;
+ const char *bBranch = branchName2;
+
+ if ( renamesA != renames1 ) {
+ memswp(&aname, &bname, sizeof(aname));
+ memswp(asha, bsha, 20);
+ memswp(&aBranch, &bBranch, sizeof(aBranch));
+ }
+ struct merge_file_info mfi;
+ mfi = merge_file(oname, osha, omode,
+ aname, asha, amode,
+ bname, bsha, bmode,
+ aBranch, bBranch);
+
+ if ( mfi.merge || !mfi.clean )
+ output("Renaming %s => %s", ren1_src, ren1_dst);
+ if ( mfi.merge )
+ output("Auto-merging %s", ren1_dst);
+ if ( !mfi.clean ) {
+ output("CONFLICT (rename/modify): Merge conflict in %s",
+ ren1_dst);
+ cleanMerge = 0;
+
+ if ( !index_only )
+ update_stages(ren1_dst,
+ osha, omode,
+ asha, amode,
+ bsha, bmode,
+ 1 /* clear */);
+ }
+ update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst);
+ }
+ }
+ }
+ path_list_clear(&srcNames, 0);
+ debug(" processRenames done\n");
+
+ if (cache_dirty)
+ flush_cache();
+ return cleanMerge;
+}
+
+static unsigned char *has_sha(const unsigned char *sha)
+{
+ return memcmp(sha, null_sha1, 20) == 0 ? NULL: (unsigned char *)sha;
+}
+
+/* Per entry merge function */
+static int process_entry(const char *path, struct stage_data *entry,
+ const char *branch1Name,
+ const char *branch2Name)
+{
+ /*
+ printf("processing entry, clean cache: %s\n", index_only ? "yes": "no");
+ print_index_entry("\tpath: ", entry);
+ */
+ int cleanMerge = 1;
+ unsigned char *oSha = has_sha(entry->stages[1].sha);
+ unsigned char *aSha = has_sha(entry->stages[2].sha);
+ unsigned char *bSha = has_sha(entry->stages[3].sha);
+ unsigned oMode = entry->stages[1].mode;
+ unsigned aMode = entry->stages[2].mode;
+ unsigned bMode = entry->stages[3].mode;
+
+ if ( oSha && (!aSha || !bSha) ) {
+ /* Case A: Deleted in one */
+ if ( (!aSha && !bSha) ||
+ (sha_eq(aSha, oSha) && !bSha) ||
+ (!aSha && sha_eq(bSha, oSha)) ) {
+ /* Deleted in both or deleted in one and
+ * unchanged in the other */
+ if ( aSha )
+ output("Removing %s", path);
+ remove_file(1, path);
+ } else {
+ /* Deleted in one and changed in the other */
+ cleanMerge = 0;
+ if ( !aSha ) {
+ output("CONFLICT (delete/modify): %s deleted in %s "
+ "and modified in %s. Version %s of %s left in tree.",
+ path, branch1Name,
+ branch2Name, branch2Name, path);
+ update_file(0, bSha, bMode, path);
+ } else {
+ output("CONFLICT (delete/modify): %s deleted in %s "
+ "and modified in %s. Version %s of %s left in tree.",
+ path, branch2Name,
+ branch1Name, branch1Name, path);
+ update_file(0, aSha, aMode, path);
+ }
+ }
+
+ } else if ( (!oSha && aSha && !bSha) ||
+ (!oSha && !aSha && bSha) ) {
+ /* Case B: Added in one. */
+ const char *addBranch;
+ const char *otherBranch;
+ unsigned mode;
+ const unsigned char *sha;
+ const char *conf;
+
+ if ( aSha ) {
+ addBranch = branch1Name;
+ otherBranch = branch2Name;
+ mode = aMode;
+ sha = aSha;
+ conf = "file/directory";
+ } else {
+ addBranch = branch2Name;
+ otherBranch = branch1Name;
+ mode = bMode;
+ sha = bSha;
+ conf = "directory/file";
+ }
+ if ( path_list_has_path(¤tDirectorySet, path) ) {
+ cleanMerge = 0;
+ const char *newPath = unique_path(path, addBranch);
+ output("CONFLICT (%s): There is a directory with name %s in %s. "
+ "Adding %s as %s",
+ conf, path, otherBranch, path, newPath);
+ remove_file(0, path);
+ update_file(0, sha, mode, newPath);
+ } else {
+ output("Adding %s", path);
+ update_file(1, sha, mode, path);
+ }
+ } else if ( !oSha && aSha && bSha ) {
+ /* Case C: Added in both (check for same permissions). */
+ if ( sha_eq(aSha, bSha) ) {
+ if ( aMode != bMode ) {
+ cleanMerge = 0;
+ output("CONFLICT: File %s added identically in both branches, "
+ "but permissions conflict %06o->%06o",
+ path, aMode, bMode);
+ output("CONFLICT: adding with permission: %06o", aMode);
+ update_file(0, aSha, aMode, path);
+ } else {
+ /* This case is handled by git-read-tree */
+ assert(0 && "This case must be handled by git-read-tree");
+ }
+ } else {
+ cleanMerge = 0;
+ const char *newPath1 = unique_path(path, branch1Name);
+ const char *newPath2 = unique_path(path, branch2Name);
+ output("CONFLICT (add/add): File %s added non-identically "
+ "in both branches. Adding as %s and %s instead.",
+ path, newPath1, newPath2);
+ remove_file(0, path);
+ update_file(0, aSha, aMode, newPath1);
+ update_file(0, bSha, bMode, newPath2);
+ }
+
+ } else if ( oSha && aSha && bSha ) {
+ /* case D: Modified in both, but differently. */
+ output("Auto-merging %s", path);
+ struct merge_file_info mfi;
+ mfi = merge_file(path, oSha, oMode,
+ path, aSha, aMode,
+ path, bSha, bMode,
+ branch1Name, branch2Name);
+
+ if ( mfi.clean )
+ update_file(1, mfi.sha, mfi.mode, path);
+ else {
+ cleanMerge = 0;
+ output("CONFLICT (content): Merge conflict in %s", path);
+
+ if ( index_only )
+ update_file(0, mfi.sha, mfi.mode, path);
+ else
+ update_file_flags(mfi.sha, mfi.mode, path,
+ 0 /* updateCache */, 1 /* updateWd */);
+ }
+ } else
+ die("Fatal merge failure, shouldn't happen.");
+
+ if (cache_dirty)
+ flush_cache();
+
+ return cleanMerge;
+}
+
+static struct merge_tree_result merge_trees(struct tree *head,
+ struct tree *merge,
+ struct tree *common,
+ const char *branch1Name,
+ const char *branch2Name)
+{
+ int code;
+ struct merge_tree_result result = { NULL, 0 };
+ if ( !memcmp(common->object.sha1, merge->object.sha1, 20) ) {
+ output("Already uptodate!");
+ result.tree = head;
+ result.clean = 1;
+ return result;
+ }
+
+ debug("merge_trees ...\n");
+ code = git_merge_trees(index_only ? "-i": "-u", common, head, merge);
+
+ if ( code != 0 )
+ die("merging of trees %s and %s failed",
+ sha1_to_hex(head->object.sha1),
+ sha1_to_hex(merge->object.sha1));
+
+ result.tree = git_write_tree();
+
+ if ( !result.tree ) {
+ path_list_clear(¤tFileSet, 1);
+ path_list_clear(¤tDirectorySet, 1);
+ get_files_dirs(head, ¤tFileSet, ¤tDirectorySet);
+ get_files_dirs(merge, ¤tFileSet, ¤tDirectorySet);
+
+ struct path_list *entries = get_unmerged();
+ struct path_list *re_head, *re_merge;
+ re_head = get_renames(head, common, head, merge, entries);
+ re_merge = get_renames(merge, common, head, merge, entries);
+ result.clean = process_renames(re_head, re_merge,
+ branch1Name, branch2Name);
+ debug("\tprocessing entries...\n");
+ int i;
+ for (i = 0; i < entries->nr; i++) {
+ const char *path = entries->items[i].path;
+ struct stage_data *e = entries->items[i].util;
+ if (e->processed)
+ continue;
+ if (!process_entry(path, e, branch1Name, branch2Name))
+ result.clean = 0;
+ }
+
+ free_rename_entries(&re_merge);
+ free_rename_entries(&re_head);
+ free_index_entries(&entries);
+
+ if (result.clean || index_only)
+ result.tree = git_write_tree();
+ else
+ result.tree = NULL;
+ debug("\t processing entries done\n");
+ } else {
+ result.clean = 1;
+ printf("merging of trees %s and %s resulted in %s\n",
+ sha1_to_hex(head->object.sha1),
+ sha1_to_hex(merge->object.sha1),
+ sha1_to_hex(result.tree->object.sha1));
+ }
+
+ debug(" merge_trees done\n");
+ return result;
+}
+
+/*
+ * Merge the commits h1 and h2, return the resulting virtual
+ * commit object and a flag indicating the cleaness of the merge.
+ */
+static
+struct merge_result merge(struct commit *h1,
+ struct commit *h2,
+ const char *branch1Name,
+ const char *branch2Name,
+ int callDepth /* =0 */,
+ struct commit *ancestor /* =None */)
+{
+ struct merge_result result = { NULL, 0 };
+ const char *msg;
+ int msglen;
+ struct commit_list *ca = NULL, *iter;
+ struct commit *mergedCA;
+ struct merge_tree_result mtr;
+
+ output("Merging:");
+ msg = commit_title(h1, &msglen);
+ /* TODO: refactor. we always show the sha1 with the title */
+ output("%s %.*s", commit_hex_sha1(h1), msglen, msg);
+ msg = commit_title(h2, &msglen);
+ output("%s %.*s", commit_hex_sha1(h2), msglen, msg);
+
+ if ( ancestor )
+ commit_list_insert(ancestor, &ca);
+ else
+ ca = get_merge_bases(h1, h2, 1);
+
+ output("found %u common ancestor(s):", commit_list_count(ca));
+ for (iter = ca; iter; iter = iter->next) {
+ msg = commit_title(iter->item, &msglen);
+ output("%s %.*s", commit_hex_sha1(iter->item), msglen, msg);
+ }
+
+ mergedCA = pop_commit(&ca);
+
+ /* TODO: what happens when merge with virtual commits fails? */
+ for (iter = ca; iter; iter = iter->next) {
+ output_indent = callDepth + 1;
+ result = merge(mergedCA, iter->item,
+ "Temporary merge branch 1",
+ "Temporary merge branch 2",
+ callDepth + 1,
+ NULL);
+ mergedCA = result.commit;
+ output_indent = callDepth;
+
+ if ( !mergedCA )
+ die("merge returned no commit");
+ }
+
+ if ( callDepth == 0 ) {
+ setup_index(0);
+ index_only = 0;
+ } else {
+ setup_index(1);
+ git_read_tree(h1->tree);
+ index_only = 1;
+ }
+
+ mtr = merge_trees(h1->tree, h2->tree,
+ mergedCA->tree, branch1Name, branch2Name);
+
+ if ( !ancestor && (mtr.clean || index_only) ) {
+ result.commit = make_virtual_commit(mtr.tree, "merged tree");
+ commit_list_insert(h1, &result.commit->parents);
+ commit_list_insert(h2, &result.commit->parents->next);
+ } else
+ result.commit = NULL;
+
+ result.clean = mtr.clean;
+ return result;
+}
+
+static struct commit *get_ref(const char *ref)
+{
+ unsigned char sha1[20];
+ struct object *object;
+
+ if (get_sha1(ref, sha1))
+ die("Could not resolve ref '%s'", ref);
+ object = deref_tag(parse_object(sha1), ref, strlen(ref));
+ if (object->type != TYPE_COMMIT)
+ return NULL;
+ if (parse_commit((struct commit *)object))
+ die("Could not parse commit '%s'", sha1_to_hex(object->sha1));
+ return (struct commit *)object;
+}
+
+int main(int argc, char *argv[])
+{
+ static const char *bases[2];
+ static unsigned bases_count = 0;
+
+ original_index_file = getenv("GIT_INDEX_FILE");
+
+ if (!original_index_file)
+ original_index_file = strdup(git_path("index"));
+
+ temporary_index_file = strdup(git_path("mrg-rcrsv-tmp-idx"));
+
+ if (argc < 4)
+ die("Usage: %s <base>... -- <head> <remote> ...\n", argv[0]);
+
+ int i;
+ for (i = 1; i < argc; ++i) {
+ if (!strcmp(argv[i], "--"))
+ break;
+ if (bases_count < sizeof(bases)/sizeof(*bases))
+ bases[bases_count++] = argv[i];
+ }
+ if (argc - i != 3) /* "--" "<head>" "<remote>" */
+ die("Not handling anything other than two heads merge.");
+
+ const char *branch1, *branch2;
+
+ branch1 = argv[++i];
+ branch2 = argv[++i];
+ printf("Merging %s with %s\n", branch1, branch2);
+
+ struct merge_result result;
+ struct commit *h1 = get_ref(branch1);
+ struct commit *h2 = get_ref(branch2);
+
+ if (bases_count == 1) {
+ struct commit *ancestor = get_ref(bases[0]);
+ result = merge(h1, h2, branch1, branch2, 0, ancestor);
+ } else
+ result = merge(h1, h2, branch1, branch2, 0, NULL);
+
+ if (cache_dirty)
+ flush_cache();
+
+ return result.clean ? 0: 1;
+}
+
+/*
+vim: sw=8 noet
+*/
diff --git a/path-list.c b/path-list.c
new file mode 100644
index 0000000..f15a10d
--- /dev/null
+++ b/path-list.c
@@ -0,0 +1,105 @@
+#include <stdio.h>
+#include "cache.h"
+#include "path-list.h"
+
+/* if there is no exact match, point to the index where the entry could be
+ * inserted */
+static int get_entry_index(const struct path_list *list, const char *path,
+ int *exact_match)
+{
+ int left = -1, right = list->nr;
+
+ while (left + 1 < right) {
+ int middle = (left + right) / 2;
+ int compare = strcmp(path, list->items[middle].path);
+ if (compare < 0)
+ right = middle;
+ else if (compare > 0)
+ left = middle;
+ else {
+ *exact_match = 1;
+ return middle;
+ }
+ }
+
+ *exact_match = 0;
+ return right;
+}
+
+/* returns -1-index if already exists */
+static int add_entry(struct path_list *list, const char *path)
+{
+ int exact_match;
+ int index = get_entry_index(list, path, &exact_match);
+
+ if (exact_match)
+ return -1 - index;
+
+ if (list->nr + 1 >= list->alloc) {
+ list->alloc += 32;
+ list->items = xrealloc(list->items, list->alloc
+ * sizeof(struct path_list_item));
+ }
+ if (index < list->nr)
+ memmove(list->items + index + 1, list->items + index,
+ (list->nr - index)
+ * sizeof(struct path_list_item));
+ list->items[index].path = list->strdup_paths ?
+ strdup(path) : (char *)path;
+ list->items[index].util = NULL;
+ list->nr++;
+
+ return index;
+}
+
+struct path_list_item *path_list_insert(const char *path, struct path_list *list)
+{
+ int index = add_entry(list, path);
+
+ if (index < 0)
+ index = 1 - index;
+
+ return list->items + index;
+}
+
+int path_list_has_path(const struct path_list *list, const char *path)
+{
+ int exact_match;
+ get_entry_index(list, path, &exact_match);
+ return exact_match;
+}
+
+struct path_list_item *path_list_lookup(const char *path, struct path_list *list)
+{
+ int exact_match, i = get_entry_index(list, path, &exact_match);
+ if (!exact_match)
+ return NULL;
+ return list->items + i;
+}
+
+void path_list_clear(struct path_list *list, int free_items)
+{
+ if (list->items) {
+ int i;
+ if (free_items)
+ for (i = 0; i < list->nr; i++) {
+ if (list->strdup_paths)
+ free(list->items[i].path);
+ if (list->items[i].util)
+ free(list->items[i].util);
+ }
+ free(list->items);
+ }
+ list->items = NULL;
+ list->nr = list->alloc = 0;
+}
+
+void print_path_list(const char *text, const struct path_list *p)
+{
+ int i;
+ if ( text )
+ printf("%s\n", text);
+ for (i = 0; i < p->nr; i++)
+ printf("%s:%p\n", p->items[i].path, p->items[i].util);
+}
+
diff --git a/path-list.h b/path-list.h
new file mode 100644
index 0000000..d6401ea
--- /dev/null
+++ b/path-list.h
@@ -0,0 +1,22 @@
+#ifndef _PATH_LIST_H_
+#define _PATH_LIST_H_
+
+struct path_list_item {
+ char *path;
+ void *util;
+};
+struct path_list
+{
+ struct path_list_item *items;
+ unsigned int nr, alloc;
+ unsigned int strdup_paths:1;
+};
+
+void print_path_list(const char *text, const struct path_list *p);
+
+int path_list_has_path(const struct path_list *list, const char *path);
+void path_list_clear(struct path_list *list, int free_items);
+struct path_list_item *path_list_insert(const char *path, struct path_list *list);
+struct path_list_item *path_list_lookup(const char *path, struct path_list *list);
+
+#endif /* _PATH_LIST_H_ */
diff --git a/read-cache.c b/read-cache.c
index 3c32aae..f6e1b70 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -24,6 +24,11 @@ unsigned int active_nr = 0, active_alloc
struct cache_tree *active_cache_tree = NULL;
+int cache_errno = 0;
+
+static void *cache_mmap = NULL;
+static size_t cache_mmap_size = 0;
+
/*
* This only updates the "non-critical" parts of the directory
* cache, ie the parts that aren't tracked by GIT, and only used
@@ -577,22 +582,6 @@ int add_cache_entry(struct cache_entry *
return 0;
}
-/* Three functions to allow overloaded pointer return; see linux/err.h */
-static inline void *ERR_PTR(long error)
-{
- return (void *) error;
-}
-
-static inline long PTR_ERR(const void *ptr)
-{
- return (long) ptr;
-}
-
-static inline long IS_ERR(const void *ptr)
-{
- return (unsigned long)ptr > (unsigned long)-1000L;
-}
-
/*
* "refresh" does not calculate a new sha1 file or bring the
* cache up-to-date for mode/content changes. But what it
@@ -604,14 +593,16 @@ static inline long IS_ERR(const void *pt
* For example, you'd want to do this after doing a "git-read-tree",
* to link up the stat cache details with the proper files.
*/
-static struct cache_entry *refresh_entry(struct cache_entry *ce, int really)
+struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
{
struct stat st;
struct cache_entry *updated;
int changed, size;
- if (lstat(ce->name, &st) < 0)
- return ERR_PTR(-errno);
+ if (lstat(ce->name, &st) < 0) {
+ cache_errno = errno;
+ return NULL;
+ }
changed = ce_match_stat(ce, &st, really);
if (!changed) {
@@ -619,11 +610,13 @@ static struct cache_entry *refresh_entry
!(ce->ce_flags & htons(CE_VALID)))
; /* mark this one VALID again */
else
- return NULL;
+ return ce;
}
- if (ce_modified(ce, &st, really))
- return ERR_PTR(-EINVAL);
+ if (ce_modified(ce, &st, really)) {
+ cache_errno = EINVAL;
+ return NULL;
+ }
size = ce_size(ce);
updated = xmalloc(size);
@@ -666,13 +659,13 @@ int refresh_cache(unsigned int flags)
continue;
}
- new = refresh_entry(ce, really);
- if (!new)
+ new = refresh_cache_entry(ce, really);
+ if (new == ce)
continue;
- if (IS_ERR(new)) {
- if (not_new && PTR_ERR(new) == -ENOENT)
+ if (!new) {
+ if (not_new && cache_errno == ENOENT)
continue;
- if (really && PTR_ERR(new) == -EINVAL) {
+ if (really && cache_errno == EINVAL) {
/* If we are doing --really-refresh that
* means the index is not valid anymore.
*/
@@ -729,39 +722,43 @@ static int read_index_extension(const ch
int read_cache(void)
{
+ return read_cache_from(get_index_file());
+}
+
+/* remember to discard_cache() before reading a different cache! */
+int read_cache_from(const char *path)
+{
int fd, i;
struct stat st;
- unsigned long size, offset;
- void *map;
+ unsigned long offset;
struct cache_header *hdr;
errno = EBUSY;
- if (active_cache)
+ if (cache_mmap)
return active_nr;
errno = ENOENT;
index_file_timestamp = 0;
- fd = open(get_index_file(), O_RDONLY);
+ fd = open(path, O_RDONLY);
if (fd < 0) {
if (errno == ENOENT)
return 0;
die("index file open failed (%s)", strerror(errno));
}
- size = 0; // avoid gcc warning
- map = MAP_FAILED;
+ cache_mmap = MAP_FAILED;
if (!fstat(fd, &st)) {
- size = st.st_size;
+ cache_mmap_size = st.st_size;
errno = EINVAL;
- if (size >= sizeof(struct cache_header) + 20)
- map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+ if (cache_mmap_size >= sizeof(struct cache_header) + 20)
+ cache_mmap = mmap(NULL, cache_mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
}
close(fd);
- if (map == MAP_FAILED)
+ if (cache_mmap == MAP_FAILED)
die("index file mmap failed (%s)", strerror(errno));
- hdr = map;
- if (verify_hdr(hdr, size) < 0)
+ hdr = cache_mmap;
+ if (verify_hdr(hdr, cache_mmap_size) < 0)
goto unmap;
active_nr = ntohl(hdr->hdr_entries);
@@ -770,12 +767,12 @@ int read_cache(void)
offset = sizeof(*hdr);
for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = (struct cache_entry *) ((char *) map + offset);
+ struct cache_entry *ce = (struct cache_entry *) ((char *) cache_mmap + offset);
offset = offset + ce_size(ce);
active_cache[i] = ce;
}
index_file_timestamp = st.st_mtime;
- while (offset <= size - 20 - 8) {
+ while (offset <= cache_mmap_size - 20 - 8) {
/* After an array of active_nr index entries,
* there can be arbitrary number of extended
* sections, each of which is prefixed with
@@ -783,10 +780,10 @@ int read_cache(void)
* in 4-byte network byte order.
*/
unsigned long extsize;
- memcpy(&extsize, (char *) map + offset + 4, 4);
+ memcpy(&extsize, (char *) cache_mmap + offset + 4, 4);
extsize = ntohl(extsize);
- if (read_index_extension(((const char *) map) + offset,
- (char *) map + offset + 8,
+ if (read_index_extension(((const char *) cache_mmap) + offset,
+ (char *) cache_mmap + offset + 8,
extsize) < 0)
goto unmap;
offset += 8;
@@ -795,11 +792,28 @@ int read_cache(void)
return active_nr;
unmap:
- munmap(map, size);
+ munmap(cache_mmap, cache_mmap_size);
errno = EINVAL;
die("index file corrupt");
}
+int discard_cache()
+{
+ int ret;
+
+ if (cache_mmap == NULL)
+ return 0;
+ ret = munmap(cache_mmap, cache_mmap_size);
+ cache_mmap = NULL;
+ cache_mmap_size = 0;
+ active_nr = active_cache_changed = 0;
+ index_file_timestamp = 0;
+ cache_tree_free(&active_cache_tree);
+
+ /* no need to throw away allocated active_cache */
+ return ret;
+}
+
#define WRITE_BUFFER_SIZE 8192
static unsigned char write_buffer[WRITE_BUFFER_SIZE];
static unsigned long write_buffer_len;
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
index d34c6cf..b70e177 100755
--- a/t/t3402-rebase-merge.sh
+++ b/t/t3402-rebase-merge.sh
@@ -51,7 +51,7 @@ test_expect_success setup '
'
test_expect_success 'reference merge' '
- git merge -s recursive "reference merge" HEAD master
+ git merge -s recur "reference merge" HEAD master
'
test_expect_success rebase '
^ permalink raw reply related
* Re: [PATH] sed -e '/RE/r rfile/' needs space in 'r rfile'
From: Johannes Schindelin @ 2006-07-08 16:47 UTC (permalink / raw)
To: Michal Rokos; +Cc: git
In-Reply-To: <200607081727.10837.michal.rokos@nextsoft.cz>
Hi,
On Sat, 8 Jul 2006, Michal Rokos wrote:
> Johannes,
Could you Cc me next time you address me personally? There are times when
I am too busy to read all the mails of all the lists, and your mail would
have been not read by me at those occasions.
> in commit 07002287f3e219a16a948a8a6eca0a41162a491f
> you cleaned up 'replace ugly and unportable sed invocation' as you said.
> Please note, that some SEDs (like HP-UX one) mandate a space between 'r'
> and 'rfile'.
Ack. Note that this is yet-another-reason to step away from scripts.
Ciao,
Dscho
^ permalink raw reply
* Re: [PATH] sed -e '/RE/r rfile/' needs space in 'r rfile'
From: Junio C Hamano @ 2006-07-08 18:25 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git
In-Reply-To: <Pine.LNX.4.63.0607081844580.29667@wbgn013.biozentrum.uni-wuerzburg.de>
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> in commit 07002287f3e219a16a948a8a6eca0a41162a491f
>> you cleaned up 'replace ugly and unportable sed invocation' as you said.
>> Please note, that some SEDs (like HP-UX one) mandate a space between 'r'
>> and 'rfile'.
>
> Ack. Note that this is yet-another-reason to step away from scripts.
Are you talking about doing this part in the Makefile in C ;-)?
^ permalink raw reply
* Re: [Patch] Using 'perl' in *.sh
From: Junio C Hamano @ 2006-07-08 18:27 UTC (permalink / raw)
To: Michal Rokos; +Cc: git
In-Reply-To: <200607081732.04273.michal.rokos@nextsoft.cz>
Michal Rokos <michal.rokos@nextsoft.cz> writes:
> Hi,
>
> some GIT's shell script are using bare 'perl' for perl invocation. It's
> causing me problems... I compile git with PERL_PATH set and I'd suggest to
> use it everywhere.
>
> So @@PERL@@ would be replaced with PERL_PATH_SQ instead.
>
> What do you think?
Absolutely.
I think it was just sloppiness that we did not do so already.
Thanks.
^ permalink raw reply
* [PATCH] Remove more gcc extension usage.
From: Shawn Pearce @ 2006-07-08 18:34 UTC (permalink / raw)
To: Junio Hamano; +Cc: git
Removing these last remaining extension uses allows GIT to compile
with the Sun C compiler rather then gcc. This can be handy when
you are trying to compile GIT on a Solaris system that seems to
have a total lack of GNU utilities.
---
builtin-read-tree.c | 18 ++++++++----------
checkout-index.c | 10 ++--------
diff.c | 12 ++++++------
3 files changed, 16 insertions(+), 24 deletions(-)
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 9a2099d..33b2faf 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -43,10 +43,7 @@ struct tree_entry_list {
const unsigned char *sha1;
};
-static struct tree_entry_list df_conflict_list = {
- .name = NULL,
- .next = &df_conflict_list
-};
+static struct tree_entry_list df_conflict_list;
typedef int (*merge_fn_t)(struct cache_entry **src);
@@ -333,14 +330,9 @@ static void setup_progress_signal(void)
setitimer(ITIMER_REAL, &v, NULL);
}
+static struct checkout state;
static void check_updates(struct cache_entry **src, int nr)
{
- static struct checkout state = {
- .base_dir = "",
- .force = 1,
- .quiet = 1,
- .refresh_cache = 1,
- };
unsigned short mask = htons(CE_UPDATE);
unsigned last_percent = 200, cnt = 0, total = 0;
@@ -884,6 +876,12 @@ int cmd_read_tree(int argc, const char *
unsigned char sha1[20];
merge_fn_t fn = NULL;
+ df_conflict_list.next = &df_conflict_list;
+ state.base_dir = "";
+ state.force = 1;
+ state.quiet = 1;
+ state.refresh_cache = 1;
+
setup_git_directory();
git_config(git_default_config);
diff --git a/checkout-index.c b/checkout-index.c
index ea40bc2..88c21cb 100644
--- a/checkout-index.c
+++ b/checkout-index.c
@@ -49,14 +49,7 @@ static int checkout_stage; /* default to
static int to_tempfile;
static char topath[4][MAXPATHLEN+1];
-static struct checkout state = {
- .base_dir = "",
- .base_dir_len = 0,
- .force = 0,
- .quiet = 0,
- .not_new = 0,
- .refresh_cache = 0,
-};
+static struct checkout state;
static void write_tempfile_record (const char *name)
{
@@ -177,6 +170,7 @@ int main(int argc, char **argv)
int all = 0;
int read_from_stdin = 0;
+ state.base_dir = "";
prefix = setup_git_directory();
git_config(git_default_config);
prefix_length = prefix ? strlen(prefix) : 0;
diff --git a/diff.c b/diff.c
index f0450a8..4b389b1 100644
--- a/diff.c
+++ b/diff.c
@@ -43,12 +43,12 @@ #define COLOR_CYAN "\033[36m"
#define COLOR_WHITE "\033[37m"
static const char *diff_colors[] = {
- [DIFF_RESET] = COLOR_RESET,
- [DIFF_PLAIN] = COLOR_NORMAL,
- [DIFF_METAINFO] = COLOR_BOLD,
- [DIFF_FRAGINFO] = COLOR_CYAN,
- [DIFF_FILE_OLD] = COLOR_RED,
- [DIFF_FILE_NEW] = COLOR_GREEN,
+ COLOR_RESET,
+ COLOR_NORMAL,
+ COLOR_BOLD,
+ COLOR_CYAN,
+ COLOR_RED,
+ COLOR_GREEN
};
static int parse_diff_color_slot(const char *var, int ofs)
--
1.4.1.gbcf1
^ permalink raw reply related
* Re: [PATCH] Remove more gcc extension usage.
From: Junio C Hamano @ 2006-07-08 18:51 UTC (permalink / raw)
To: Shawn Pearce; +Cc: git
In-Reply-To: <20060708183402.GA17644@spearce.org>
Shawn Pearce <spearce@spearce.org> writes:
> Removing these last remaining extension uses allows GIT to compile
> with the Sun C compiler rather then gcc. This can be handy when
> you are trying to compile GIT on a Solaris system that seems to
> have a total lack of GNU utilities.
Two points.
- Aren't the constructs you ripped out not GCC extension,
rather proper ISO C99?
- Our Makefile is pretty GNU already. I think people have
pointed out and ripped out bashisms from our shell scripts,
but I would not be surprised if the default Sun /bin/sh does
not understand POSIXy features some of them use.
^ permalink raw reply
* gitweb/test/Marchen always untracked?
From: Shawn Pearce @ 2006-07-08 18:53 UTC (permalink / raw)
To: git
I'm not sure what is going on here but on my Mac OS X system
the file `gitweb/test/Marchen` is always untracked:
[spearce@spearce-pb15 git]$ git status
# On branch refs/heads/sp/work
#
# Untracked files:
# (use "git add" to add to commit)
#
# gitweb/test/MaÌrchen
nothing to commit
[spearce@spearce-pb15 git]$ git-ls-files | grep chen
"gitweb/test/M\303\244rchen"
[spearce@spearce-pb15 git]$ ls -l gitweb/test/Ma\314\210rchen
-rw-r--r-- 1 spearce spearce 17 Jul 8 14:38 gitweb/test/Ma??rchen
At first glance it would appear as though the file is encoded into
the tree (and thus the index) one way and the OS is reporting it
another way during a readdir. Which is leaving me scratching my
head...
--
Shawn.
^ permalink raw reply
* Re: [PATH] sed -e '/RE/r rfile/' needs space in 'r rfile'
From: Johannes Schindelin @ 2006-07-08 18:57 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7v7j2oq7h3.fsf@assigned-by-dhcp.cox.net>
Hi,
On Sat, 8 Jul 2006, Junio C Hamano wrote:
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>
> >> in commit 07002287f3e219a16a948a8a6eca0a41162a491f
> >> you cleaned up 'replace ugly and unportable sed invocation' as you said.
> >> Please note, that some SEDs (like HP-UX one) mandate a space between 'r'
> >> and 'rfile'.
> >
> > Ack. Note that this is yet-another-reason to step away from scripts.
>
> Are you talking about doing this part in the Makefile in C ;-)?
If it is not portable, then yes ;-)
But of course, you know the spirit this was written in: I had no way to
test with sed on HP-UX, so I do not know if my change works there.
However, I know that if it is C -- except for bugs in HP-UX's C compiler
-- then it works on HP-UX as expected.
Ciao,
Dscho
^ permalink raw reply
* Re: gitweb/test/Marchen always untracked?
From: Johannes Schindelin @ 2006-07-08 19:02 UTC (permalink / raw)
To: Shawn Pearce; +Cc: git
In-Reply-To: <20060708185322.GA17708@spearce.org>
[-- Attachment #1: Type: TEXT/PLAIN, Size: 496 bytes --]
Hi,
On Sat, 8 Jul 2006, Shawn Pearce wrote:
> I'm not sure what is going on here but on my Mac OS X system
> the file `gitweb/test/Marchen` is always untracked:
I noticed that problem also. It seems to be yet another encoding problem,
because you see the Latin1 interpretation of a UTF-8 encoded umlaut, ä.
What is fascinating:
> git-ls-files --cached|grep chen.$
"gitweb/test/M\303\244rchen"
but
> git-ls-files --others|grep chen.$
"gitweb/test/Ma\314\210rchen"
Will investigate,
Dscho
^ permalink raw reply
* Re: [PATCH] Remove more gcc extension usage.
From: Shawn Pearce @ 2006-07-08 19:03 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vy7v4orpt.fsf@assigned-by-dhcp.cox.net>
Junio C Hamano <junkio@cox.net> wrote:
> Shawn Pearce <spearce@spearce.org> writes:
>
> > Removing these last remaining extension uses allows GIT to compile
> > with the Sun C compiler rather then gcc. This can be handy when
> > you are trying to compile GIT on a Solaris system that seems to
> > have a total lack of GNU utilities.
>
> Two points.
>
> - Aren't the constructs you ripped out not GCC extension,
> rather proper ISO C99?
Hmm. I'm not sure actually. I don't do much C hacking these days
so I haven't kept current with what C99 has and doesn't. I just
know that these small changes made the core plumbing build and run
fine on Solaris with only GNU make being present. I didn't even
attempt to use the higher level Poreclainish shell script commands.
Monday I'll look to see if there's an option that can be given to
the Solaris compiler to make it accept these constructs. Maybe a
simple CFLAGS change in my config.mak would resolve what this patch
was trying to do.
> - Our Makefile is pretty GNU already. I think people have
> pointed out and ripped out bashisms from our shell scripts,
> but I would not be surprised if the default Sun /bin/sh does
> not understand POSIXy features some of them use.
I realize that. Asking someone to compile GNU make in their home
directory before they build GIT to their home directory isn't a
big deal (took me all of 30 seconds to download the latest and
./configure&&make install it). Asking someone to download and
compile binutils and gcc because their local site manager won't
install them for you is another matter entirely...
--
Shawn.
^ 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