diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 0fe66f2d0c..517a86b238 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -15,9 +15,9 @@ SYNOPSIS
DESCRIPTION
-----------
Updates the index file for given paths, or all modified files if
-'-a' is specified, and makes a commit object. The command
-VISUAL and EDITOR environment variables to edit the commit log
-message.
+'-a' is specified, and makes a commit object. The command specified
+by either the VISUAL or EDITOR environment variables are used to edit
+the commit log message.
Several environment variable are used during commits. They are
documented in gitlink:git-commit-tree[1].
diff --git a/connect.c b/connect.c
index cb4656d79d..9a87bd999a 100644
--- a/connect.c
+++ b/connect.c
@@ -328,7 +328,7 @@ static enum protocol get_protocol(const char *name)
*/
static int git_tcp_connect_sock(char *host)
{
- int sockfd = -1;
+ int sockfd = -1, saved_errno = 0;
char *colon, *end;
const char *port = STR(DEFAULT_GIT_PORT);
struct addrinfo hints, *ai0, *ai;
@@ -362,9 +362,12 @@ static int git_tcp_connect_sock(char *host)
for (ai0 = ai; ai; ai = ai->ai_next) {
sockfd = socket(ai->ai_family,
ai->ai_socktype, ai->ai_protocol);
- if (sockfd < 0)
+ if (sockfd < 0) {
+ saved_errno = errno;
continue;
+ }
if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
+ saved_errno = errno;
close(sockfd);
sockfd = -1;
continue;
@@ -375,7 +378,7 @@ static int git_tcp_connect_sock(char *host)
freeaddrinfo(ai0);
if (sockfd < 0)
- die("unable to connect a socket (%s)", strerror(errno));
+ die("unable to connect a socket (%s)", strerror(saved_errno));
return sockfd;
}
@@ -387,7 +390,7 @@ static int git_tcp_connect_sock(char *host)
*/
static int git_tcp_connect_sock(char *host)
{
- int sockfd = -1;
+ int sockfd = -1, saved_errno = 0;
char *colon, *end;
char *port = STR(DEFAULT_GIT_PORT), *ep;
struct hostent *he;
@@ -426,8 +429,10 @@ static int git_tcp_connect_sock(char *host)
for (ap = he->h_addr_list; *ap; ap++) {
sockfd = socket(he->h_addrtype, SOCK_STREAM, 0);
- if (sockfd < 0)
+ if (sockfd < 0) {
+ saved_errno = errno;
continue;
+ }
memset(&sa, 0, sizeof sa);
sa.sin_family = he->h_addrtype;
@@ -435,6 +440,7 @@ static int git_tcp_connect_sock(char *host)
memcpy(&sa.sin_addr, *ap, he->h_length);
if (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {
+ saved_errno = errno;
close(sockfd);
sockfd = -1;
continue;
@@ -443,7 +449,7 @@ static int git_tcp_connect_sock(char *host)
}
if (sockfd < 0)
- die("unable to connect a socket (%s)", strerror(errno));
+ die("unable to connect a socket (%s)", strerror(saved_errno));
return sockfd;
}
diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl
index b3d3f479da..1e19aa19b2 100755
--- a/contrib/git-svn/git-svn.perl
+++ b/contrib/git-svn/git-svn.perl
@@ -264,9 +264,19 @@ when you have upgraded your tools and habits to use refs/remotes/$GIT_SVN
}
sub init {
- $SVN_URL = shift or die "SVN repository location required " .
+ my $url = shift or die "SVN repository location required " .
"as a command-line argument\n";
- $SVN_URL =~ s!/+$!!; # strip trailing slash
+ $url =~ s!/+$!!; # strip trailing slash
+
+ if (my $repo_path = shift) {
+ unless (-d $repo_path) {
+ mkpath([$repo_path]);
+ }
+ $GIT_DIR = $ENV{GIT_DIR} = $repo_path . "/.git";
+ init_vars();
+ }
+
+ $SVN_URL = $url;
unless (-d $GIT_DIR) {
my @init_db = ('git-init-db');
push @init_db, "--template=$_template" if defined $_template;
diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi
index 035e76d0a3..3e2790c5d6 100755
--- a/gitweb/gitweb.cgi
+++ b/gitweb/gitweb.cgi
@@ -16,21 +16,21 @@ use Encode;
use Fcntl ':mode';
binmode STDOUT, ':utf8';
-my $cgi = new CGI;
-my $version = "267";
-my $my_url = $cgi->url();
-my $my_uri = $cgi->url(-absolute => 1);
-my $rss_link = "";
+our $cgi = new CGI;
+our $version = "267";
+our $my_url = $cgi->url();
+our $my_uri = $cgi->url(-absolute => 1);
+our $rss_link = "";
# location of the git-core binaries
-my $gitbin = "/usr/bin";
+our $gitbin = "/usr/bin";
# absolute fs-path which will be prepended to the project path
-#my $projectroot = "/pub/scm";
-my $projectroot = "/home/kay/public_html/pub/scm";
+#our $projectroot = "/pub/scm";
+our $projectroot = "/home/kay/public_html/pub/scm";
# version of the git-core binaries
-my $git_version = qx($gitbin/git --version);
+our $git_version = qx($gitbin/git --version);
if ($git_version =~ m/git version (.*)$/) {
$git_version = $1;
} else {
@@ -38,32 +38,31 @@ if ($git_version =~ m/git version (.*)$/) {
}
# location for temporary files needed for diffs
-my $git_temp = "/tmp/gitweb";
+our $git_temp = "/tmp/gitweb";
# target of the home link on top of all pages
-my $home_link = $my_uri;
+our $home_link = $my_uri;
# html text to include at home page
-my $home_text = "indextext.html";
+our $home_text = "indextext.html";
# URI of default stylesheet
-my $stylesheet = "gitweb.css";
+our $stylesheet = "gitweb.css";
# source of projects list
-#my $projects_list = $projectroot;
-my $projects_list = "index/index.aux";
+#our $projects_list = $projectroot;
+our $projects_list = "index/index.aux";
# default blob_plain mimetype and default charset for text/plain blob
-my $default_blob_plain_mimetype = 'text/plain';
-my $default_text_plain_charset = undef;
+our $default_blob_plain_mimetype = 'text/plain';
+our $default_text_plain_charset = undef;
# file to use for guessing MIME types before trying /etc/mime.types
# (relative to the current git repository)
-my $mimetypes_file = undef;
-
+our $mimetypes_file = undef;
# input validation and dispatch
-my $action = $cgi->param('a');
+our $action = $cgi->param('a');
if (defined $action) {
if ($action =~ m/[^0-9a-zA-Z\.\-_]/) {
undef $action;
@@ -78,7 +77,7 @@ if (defined $action) {
}
}
-my $order = $cgi->param('o');
+our $order = $cgi->param('o');
if (defined $order) {
if ($order =~ m/[^0-9a-zA-Z_]/) {
undef $order;
@@ -86,7 +85,7 @@ if (defined $order) {
}
}
-my $project = ($cgi->param('p') || $ENV{'PATH_INFO'});
+our $project = ($cgi->param('p') || $ENV{'PATH_INFO'});
if (defined $project) {
$project =~ s|^/||; $project =~ s|/$||;
$project = validate_input($project);
@@ -109,7 +108,7 @@ if (defined $project) {
exit;
}
-my $file_name = $cgi->param('f');
+our $file_name = $cgi->param('f');
if (defined $file_name) {
$file_name = validate_input($file_name);
if (!defined($file_name)) {
@@ -117,7 +116,7 @@ if (defined $file_name) {
}
}
-my $hash = $cgi->param('h');
+our $hash = $cgi->param('h');
if (defined $hash) {
$hash = validate_input($hash);
if (!defined($hash)) {
@@ -125,7 +124,7 @@ if (defined $hash) {
}
}
-my $hash_parent = $cgi->param('hp');
+our $hash_parent = $cgi->param('hp');
if (defined $hash_parent) {
$hash_parent = validate_input($hash_parent);
if (!defined($hash_parent)) {
@@ -133,7 +132,7 @@ if (defined $hash_parent) {
}
}
-my $hash_base = $cgi->param('hb');
+our $hash_base = $cgi->param('hb');
if (defined $hash_base) {
$hash_base = validate_input($hash_base);
if (!defined($hash_base)) {
@@ -141,7 +140,7 @@ if (defined $hash_base) {
}
}
-my $page = $cgi->param('pg');
+our $page = $cgi->param('pg');
if (defined $page) {
if ($page =~ m/[^0-9]$/) {
undef $page;
@@ -149,7 +148,7 @@ if (defined $page) {
}
}
-my $searchtext = $cgi->param('s');
+our $searchtext = $cgi->param('s');
if (defined $searchtext) {
if ($searchtext =~ m/[^a-zA-Z0-9_\.\/\-\+\:\@ ]/) {
undef $searchtext;
@@ -1676,6 +1675,7 @@ sub git_tree {
"\n" .
"
" .
$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$t_hash$base_key;f=$base$t_name")}, "tree") .
+ " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$hash_base;f=$base$t_name")}, "history") .
" | \n";
}
print "\n";
@@ -2295,16 +2295,13 @@ sub git_history {
"\n";
print "/" . esc_html($file_name) . "
\n";
- open my $fd, "-|", "$gitbin/git-rev-list $hash | $gitbin/git-diff-tree -r --stdin -- \'$file_name\'";
- my $commit;
+ open my $fd, "-|",
+ "$gitbin/git-rev-list --full-history $hash -- \'$file_name\'";
print "\n";
my $alternate = 0;
while (my $line = <$fd>) {
if ($line =~ m/^([0-9a-fA-F]{40})/){
- $commit = $1;
- next;
- }
- if ($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/ && (defined $commit)) {
+ my $commit = $1;
my %co = git_read_commit($commit);
if (!%co) {
next;
@@ -2336,7 +2333,6 @@ sub git_history {
}
print "\n" .
"\n";
- undef $commit;
}
}
print "
\n";
diff --git a/object.c b/object.c
index 31c77ea03a..37277f9438 100644
--- a/object.c
+++ b/object.c
@@ -5,88 +5,97 @@
#include "commit.h"
#include "tag.h"
-static struct object **objs;
-static int nr_objs, obj_allocs;
+static struct object **obj_hash;
+static int nr_objs, obj_hash_size;
unsigned int get_max_object_index(void)
{
- return obj_allocs;
+ return obj_hash_size;
}
struct object *get_indexed_object(unsigned int idx)
{
- return objs[idx];
+ return obj_hash[idx];
}
const char *type_names[] = {
"none", "blob", "tree", "commit", "bad"
};
+static unsigned int hash_obj(struct object *obj, unsigned int n)
+{
+ unsigned int hash = *(unsigned int *)obj->sha1;
+ return hash % n;
+}
+
+static void insert_obj_hash(struct object *obj, struct object **hash, unsigned int size)
+{
+ int j = hash_obj(obj, size);
+
+ while (hash[j]) {
+ j++;
+ if (j >= size)
+ j = 0;
+ }
+ hash[j] = obj;
+}
+
static int hashtable_index(const unsigned char *sha1)
{
unsigned int i;
memcpy(&i, sha1, sizeof(unsigned int));
- return (int)(i % obj_allocs);
-}
-
-static int find_object(const unsigned char *sha1)
-{
- int i;
-
- if (!objs)
- return -1;
-
- i = hashtable_index(sha1);
- while (objs[i]) {
- if (memcmp(sha1, objs[i]->sha1, 20) == 0)
- return i;
- i++;
- if (i == obj_allocs)
- i = 0;
- }
- return -1 - i;
+ return (int)(i % obj_hash_size);
}
struct object *lookup_object(const unsigned char *sha1)
{
- int pos = find_object(sha1);
- if (pos >= 0)
- return objs[pos];
- return NULL;
+ int i;
+ struct object *obj;
+
+ if (!obj_hash)
+ return NULL;
+
+ i = hashtable_index(sha1);
+ while ((obj = obj_hash[i]) != NULL) {
+ if (!memcmp(sha1, obj->sha1, 20))
+ break;
+ i++;
+ if (i == obj_hash_size)
+ i = 0;
+ }
+ return obj;
+}
+
+static void grow_object_hash(void)
+{
+ int i;
+ int new_hash_size = obj_hash_size < 32 ? 32 : 2 * obj_hash_size;
+ struct object **new_hash;
+
+ new_hash = calloc(new_hash_size, sizeof(struct object *));
+ for (i = 0; i < obj_hash_size; i++) {
+ struct object *obj = obj_hash[i];
+ if (!obj)
+ continue;
+ insert_obj_hash(obj, new_hash, new_hash_size);
+ }
+ free(obj_hash);
+ obj_hash = new_hash;
+ obj_hash_size = new_hash_size;
}
void created_object(const unsigned char *sha1, struct object *obj)
{
- int pos;
-
obj->parsed = 0;
- memcpy(obj->sha1, sha1, 20);
- obj->type = TYPE_NONE;
obj->used = 0;
+ obj->type = TYPE_NONE;
+ obj->flags = 0;
+ memcpy(obj->sha1, sha1, 20);
- if (obj_allocs - 1 <= nr_objs * 2) {
- int i, count = obj_allocs;
- obj_allocs = (obj_allocs < 32 ? 32 : 2 * obj_allocs);
- objs = xrealloc(objs, obj_allocs * sizeof(struct object *));
- memset(objs + count, 0, (obj_allocs - count)
- * sizeof(struct object *));
- for (i = 0; i < obj_allocs; i++)
- if (objs[i]) {
- int j = find_object(objs[i]->sha1);
- if (j != i) {
- j = -1 - j;
- objs[j] = objs[i];
- objs[i] = NULL;
- }
- }
- }
+ if (obj_hash_size - 1 <= nr_objs * 2)
+ grow_object_hash();
- pos = find_object(sha1);
- if (pos >= 0)
- die("Inserting %s twice\n", sha1_to_hex(sha1));
- pos = -pos-1;
-
- objs[pos] = obj;
+ insert_obj_hash(obj, obj_hash, obj_hash_size);
nr_objs++;
}
diff --git a/pack-objects.c b/pack-objects.c
index 47da33baa3..b486ea528a 100644
--- a/pack-objects.c
+++ b/pack-objects.c
@@ -970,11 +970,12 @@ struct unpacked {
* one.
*/
static int try_delta(struct unpacked *trg, struct unpacked *src,
- struct delta_index *src_index, unsigned max_depth)
+ unsigned max_depth)
{
struct object_entry *trg_entry = trg->entry;
struct object_entry *src_entry = src->entry;
- unsigned long size, src_size, delta_size, sizediff, max_size;
+ unsigned long trg_size, src_size, delta_size, sizediff, max_size, sz;
+ char type[10];
void *delta_buf;
/* Don't bother doing diffs between different types */
@@ -1009,19 +1010,38 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
return 0;
/* Now some size filtering heuristics. */
- size = trg_entry->size;
- max_size = size/2 - 20;
+ trg_size = trg_entry->size;
+ max_size = trg_size/2 - 20;
max_size = max_size * (max_depth - src_entry->depth) / max_depth;
if (max_size == 0)
return 0;
if (trg_entry->delta && trg_entry->delta_size <= max_size)
max_size = trg_entry->delta_size-1;
src_size = src_entry->size;
- sizediff = src_size < size ? size - src_size : 0;
+ sizediff = src_size < trg_size ? trg_size - src_size : 0;
if (sizediff >= max_size)
return 0;
- delta_buf = create_delta(src_index, trg->data, size, &delta_size, max_size);
+ /* Load data if not already done */
+ if (!trg->data) {
+ trg->data = read_sha1_file(trg_entry->sha1, type, &sz);
+ if (sz != trg_size)
+ die("object %s inconsistent object length (%lu vs %lu)",
+ sha1_to_hex(trg_entry->sha1), sz, trg_size);
+ }
+ if (!src->data) {
+ src->data = read_sha1_file(src_entry->sha1, type, &sz);
+ if (sz != src_size)
+ die("object %s inconsistent object length (%lu vs %lu)",
+ sha1_to_hex(src_entry->sha1), sz, src_size);
+ }
+ if (!src->index) {
+ src->index = create_delta_index(src->data, src_size);
+ if (!src->index)
+ die("out of memory");
+ }
+
+ delta_buf = create_delta(src->index, trg->data, trg_size, &delta_size, max_size);
if (!delta_buf)
return 0;
@@ -1054,8 +1074,6 @@ static void find_deltas(struct object_entry **list, int window, int depth)
while (--i >= 0) {
struct object_entry *entry = list[i];
struct unpacked *n = array + idx;
- unsigned long size;
- char type[10];
int j;
if (!entry->preferred_base)
@@ -1082,11 +1100,8 @@ static void find_deltas(struct object_entry **list, int window, int depth)
free_delta_index(n->index);
n->index = NULL;
free(n->data);
+ n->data = NULL;
n->entry = entry;
- n->data = read_sha1_file(entry->sha1, type, &size);
- if (size != entry->size)
- die("object %s inconsistent object length (%lu vs %lu)",
- sha1_to_hex(entry->sha1), size, entry->size);
j = window;
while (--j > 0) {
@@ -1097,7 +1112,7 @@ static void find_deltas(struct object_entry **list, int window, int depth)
m = array + other_idx;
if (!m->entry)
break;
- if (try_delta(n, m, m->index, depth) < 0)
+ if (try_delta(n, m, depth) < 0)
break;
}
/* if we made n a delta, and if n is already at max
@@ -1107,10 +1122,6 @@ static void find_deltas(struct object_entry **list, int window, int depth)
if (entry->delta && depth <= entry->depth)
continue;
- n->index = create_delta_index(n->data, size);
- if (!n->index)
- die("out of memory");
-
idx++;
if (idx >= window)
idx = 0;
diff --git a/revision.c b/revision.c
index ae4ca82003..ebee05a698 100644
--- a/revision.c
+++ b/revision.c
@@ -280,7 +280,7 @@ int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1)
static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
{
struct commit_list **pp, *parent;
- int tree_changed = 0;
+ int tree_changed = 0, tree_same = 0;
if (!commit->tree)
return;
@@ -298,6 +298,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
parse_commit(p);
switch (rev_compare_tree(revs, p->tree, commit->tree)) {
case REV_TREE_SAME:
+ tree_same = 1;
if (!revs->simplify_history || (p->object.flags & UNINTERESTING)) {
/* Even if a merge with an uninteresting
* side branch brought the entire change
@@ -334,7 +335,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
}
die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1));
}
- if (tree_changed)
+ if (tree_changed && !tree_same)
commit->object.flags |= TREECHANGE;
}
@@ -895,6 +896,8 @@ static int rewrite_one(struct rev_info *revs, struct commit **pp)
struct commit *p = *pp;
if (!revs->limited)
add_parents_to_list(revs, p, &revs->commits);
+ if (p->parents && p->parents->next)
+ return 0;
if (p->object.flags & (TREECHANGE | UNINTERESTING))
return 0;
if (!p->parents)
@@ -987,8 +990,15 @@ struct commit *get_revision(struct rev_info *revs)
commit->parents && commit->parents->next)
continue;
if (revs->prune_fn && revs->dense) {
- if (!(commit->object.flags & TREECHANGE))
- continue;
+ /* Commit without changes? */
+ if (!(commit->object.flags & TREECHANGE)) {
+ /* drop merges unless we want parenthood */
+ if (!revs->parents)
+ continue;
+ /* non-merge - always ignore it */
+ if (commit->parents && !commit->parents->next)
+ continue;
+ }
if (revs->parents)
rewrite_parents(revs, commit);
}