Merge branch 'master' into jc/globfetch

This is to pick up the fix made on master:

  git-fetch: exit with non-zero status when fast-forward check fails
This commit is contained in:
Junio C Hamano 2006-11-25 01:04:54 -08:00
commit cadd8a7d4d
18 changed files with 1015 additions and 312 deletions

View File

@ -219,6 +219,12 @@ i18n.commitEncoding::
browser (and possibly at other places in the future or in other
porcelains). See e.g. gitlink:git-mailinfo[1]. Defaults to 'utf-8'.
log.showroot::
If true, the initial commit will be shown as a big creation event.
This is equivalent to a diff against an empty tree.
Tools like gitlink:git-log[1] or gitlink:git-whatchanged[1], which
normally hide the root commit will now show it. True by default.
merge.summary::
Whether to include summaries of merged commits in newly created
merge commit messages. False by default.

View File

@ -8,14 +8,16 @@ git-branch - List, create, or delete branches.
SYNOPSIS
--------
[verse]
'git-branch' [-r]
'git-branch' [-r] [-a] [-v] [--abbrev=<length>]
'git-branch' [-l] [-f] <branchname> [<start-point>]
'git-branch' (-d | -D) <branchname>...
DESCRIPTION
-----------
With no arguments given (or just `-r`) a list of available branches
With no arguments given a list of existing branches
will be shown, the current branch will be highlighted with an asterisk.
Option `-r` causes the remote-tracking branches to be listed,
and option `-a` shows both.
In its second form, a new branch named <branchname> will be created.
It will start out with a head equal to the one given as <start-point>.
@ -45,7 +47,17 @@ OPTIONS
a branch that already exists with the same name.
-r::
List only the "remote" branches.
List the remote-tracking branches.
-a::
List both remote-tracking branches and local branches.
-v::
Show sha1 and subject message for each head.
--abbrev=<length>::
Alter minimum display length for sha1 in output listing,
default value is 7.
<branchname>::
The name of the branch to create or delete.

View File

@ -11,7 +11,8 @@ SYNOPSIS
[verse]
'git-clone' [--template=<template_directory>] [-l [-s]] [-q] [-n] [--bare]
[-o <name>] [-u <upload-pack>] [--reference <repository>]
[--use-separate-remote] <repository> [<directory>]
[--use-separate-remote | --use-immingled-remote] <repository>
[<directory>]
DESCRIPTION
-----------
@ -71,9 +72,13 @@ OPTIONS
Make a 'bare' GIT repository. That is, instead of
creating `<directory>` and placing the administrative
files in `<directory>/.git`, make the `<directory>`
itself the `$GIT_DIR`. This implies `-n` option. When
this option is used, neither the `origin` branch nor the
default `remotes/origin` file is created.
itself the `$GIT_DIR`. This obviously implies the `-n`
because there is nowhere to check out the working tree.
Also the branch heads at the remote are copied directly
to corresponding local branch heads, without mapping
them to `refs/remotes/origin/`. When this option is
used, neither the `origin` branch nor the default
`remotes/origin` file is created.
--origin <name>::
-o <name>::
@ -97,8 +102,15 @@ OPTIONS
--use-separate-remote::
Save remotes heads under `$GIT_DIR/remotes/origin/` instead
of `$GIT_DIR/refs/heads/`. Only the master branch is saved
in the latter.
of `$GIT_DIR/refs/heads/`. Only the local master branch is
saved in the latter. This is the default.
--use-immingled-remote::
Save remotes heads in the same namespace as the local
heads, `$GIT_DIR/refs/heads/'. In regular repositories,
this is a legacy setup git-clone created by default in
older Git versions, and will be removed before the next
major release.
<repository>::
The (possibly remote) repository to clone from. It can

View File

@ -2119,7 +2119,11 @@ static void numstat_patch_list(struct patch *patch)
for ( ; patch; patch = patch->next) {
const char *name;
name = patch->new_name ? patch->new_name : patch->old_name;
printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
if (patch->is_binary)
printf("-\t-\t");
else
printf("%d\t%d\t",
patch->lines_added, patch->lines_deleted);
if (line_termination && quote_c_style(name, NULL, NULL, 0))
quote_c_style(name, NULL, stdout, 0);
else

View File

@ -11,7 +11,7 @@
#include "builtin.h"
static const char builtin_branch_usage[] =
"git-branch (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | [-r]";
"git-branch (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | [-r | -a] [-v] [--abbrev=<length>] ";
static const char *head;
@ -38,12 +38,16 @@ static int in_merge_bases(const unsigned char *sha1,
static void delete_branches(int argc, const char **argv, int force)
{
struct commit *rev, *head_rev;
struct commit *rev, *head_rev = head_rev;
unsigned char sha1[20];
char *name;
int i;
head_rev = lookup_commit_reference(head_sha1);
if (!force) {
head_rev = lookup_commit_reference(head_sha1);
if (!head_rev)
die("Couldn't look up commit object for HEAD");
}
for (i = 0; i < argc; i++) {
if (!strcmp(head, argv[i]))
die("Cannot delete the branch you are currently on.");
@ -53,8 +57,8 @@ static void delete_branches(int argc, const char **argv, int force)
die("Branch '%s' not found.", argv[i]);
rev = lookup_commit_reference(sha1);
if (!rev || !head_rev)
die("Couldn't look up commit objects.");
if (!rev)
die("Couldn't look up commit object for '%s'", name);
/* This checks whether the merge bases of branch and
* HEAD contains branch -- which means that the HEAD
@ -79,46 +83,129 @@ static void delete_branches(int argc, const char **argv, int force)
}
}
static int ref_index, ref_alloc;
static char **ref_list;
#define REF_UNKNOWN_TYPE 0x00
#define REF_LOCAL_BRANCH 0x01
#define REF_REMOTE_BRANCH 0x02
#define REF_TAG 0x04
static int append_ref(const char *refname, const unsigned char *sha1, int flags,
void *cb_data)
struct ref_item {
char *name;
unsigned int kind;
unsigned char sha1[20];
};
struct ref_list {
int index, alloc, maxwidth;
struct ref_item *list;
int kinds;
};
static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
{
if (ref_index >= ref_alloc) {
ref_alloc = alloc_nr(ref_alloc);
ref_list = xrealloc(ref_list, ref_alloc * sizeof(char *));
struct ref_list *ref_list = (struct ref_list*)(cb_data);
struct ref_item *newitem;
int kind = REF_UNKNOWN_TYPE;
int len;
/* Detect kind */
if (!strncmp(refname, "refs/heads/", 11)) {
kind = REF_LOCAL_BRANCH;
refname += 11;
} else if (!strncmp(refname, "refs/remotes/", 13)) {
kind = REF_REMOTE_BRANCH;
refname += 13;
} else if (!strncmp(refname, "refs/tags/", 10)) {
kind = REF_TAG;
refname += 10;
}
ref_list[ref_index++] = xstrdup(refname);
/* Don't add types the caller doesn't want */
if ((kind & ref_list->kinds) == 0)
return 0;
/* Resize buffer */
if (ref_list->index >= ref_list->alloc) {
ref_list->alloc = alloc_nr(ref_list->alloc);
ref_list->list = xrealloc(ref_list->list,
ref_list->alloc * sizeof(struct ref_item));
}
/* Record the new item */
newitem = &(ref_list->list[ref_list->index++]);
newitem->name = xstrdup(refname);
newitem->kind = kind;
hashcpy(newitem->sha1, sha1);
len = strlen(newitem->name);
if (len > ref_list->maxwidth)
ref_list->maxwidth = len;
return 0;
}
static int ref_cmp(const void *r1, const void *r2)
static void free_ref_list(struct ref_list *ref_list)
{
return strcmp(*(char **)r1, *(char **)r2);
int i;
for (i = 0; i < ref_list->index; i++)
free(ref_list->list[i].name);
free(ref_list->list);
}
static void print_ref_list(int remote_only)
static int ref_cmp(const void *r1, const void *r2)
{
struct ref_item *c1 = (struct ref_item *)(r1);
struct ref_item *c2 = (struct ref_item *)(r2);
if (c1->kind != c2->kind)
return c1->kind - c2->kind;
return strcmp(c1->name, c2->name);
}
static void print_ref_info(const unsigned char *sha1, int abbrev)
{
struct commit *commit;
char subject[256];
commit = lookup_commit(sha1);
if (commit && !parse_commit(commit))
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
subject, sizeof(subject), 0,
NULL, NULL, 0);
else
strcpy(subject, " **** invalid ref ****");
printf(" %s %s\n", find_unique_abbrev(sha1, abbrev), subject);
}
static void print_ref_list(int kinds, int verbose, int abbrev)
{
int i;
char c;
struct ref_list ref_list;
if (remote_only)
for_each_remote_ref(append_ref, NULL);
else
for_each_branch_ref(append_ref, NULL);
memset(&ref_list, 0, sizeof(ref_list));
ref_list.kinds = kinds;
for_each_ref(append_ref, &ref_list);
qsort(ref_list, ref_index, sizeof(char *), ref_cmp);
qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
for (i = 0; i < ref_index; i++) {
for (i = 0; i < ref_list.index; i++) {
c = ' ';
if (!strcmp(ref_list[i], head))
if (ref_list.list[i].kind == REF_LOCAL_BRANCH &&
!strcmp(ref_list.list[i].name, head))
c = '*';
printf("%c %s\n", c, ref_list[i]);
if (verbose) {
printf("%c %-*s", c, ref_list.maxwidth,
ref_list.list[i].name);
print_ref_info(ref_list.list[i].sha1, abbrev);
}
else
printf("%c %s\n", c, ref_list.list[i].name);
}
free_ref_list(&ref_list);
}
static void create_branch(const char *name, const char *start,
@ -160,8 +247,10 @@ static void create_branch(const char *name, const char *start,
int cmd_branch(int argc, const char **argv, const char *prefix)
{
int delete = 0, force_delete = 0, force_create = 0, remote_only = 0;
int delete = 0, force_delete = 0, force_create = 0;
int verbose = 0, abbrev = DEFAULT_ABBREV;
int reflog = 0;
int kinds = REF_LOCAL_BRANCH;
int i;
git_config(git_default_config);
@ -189,13 +278,25 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
continue;
}
if (!strcmp(arg, "-r")) {
remote_only = 1;
kinds = REF_REMOTE_BRANCH;
continue;
}
if (!strcmp(arg, "-a")) {
kinds = REF_REMOTE_BRANCH | REF_LOCAL_BRANCH;
continue;
}
if (!strcmp(arg, "-l")) {
reflog = 1;
continue;
}
if (!strncmp(arg, "--abbrev=", 9)) {
abbrev = atoi(arg+9);
continue;
}
if (!strcmp(arg, "-v")) {
verbose = 1;
continue;
}
usage(builtin_branch_usage);
}
@ -209,7 +310,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (delete)
delete_branches(argc - i, argv + i, force_delete);
else if (i == argc)
print_ref_list(remote_only);
print_ref_list(kinds, verbose, abbrev);
else if (i == argc - 1)
create_branch(argv[i], head, force_create, reflog);
else if (i == argc - 2)

View File

@ -13,6 +13,8 @@
#include <time.h>
#include <sys/time.h>
static int default_show_root = 1;
/* this is in builtin-diff.c */
void add_head(struct rev_info *revs);
@ -22,6 +24,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
rev->abbrev = DEFAULT_ABBREV;
rev->commit_format = CMIT_FMT_DEFAULT;
rev->verbose_header = 1;
rev->show_root_diff = default_show_root;
argc = setup_revisions(argc, argv, rev, "HEAD");
if (rev->diffopt.pickaxe || rev->diffopt.filter)
rev->always_show_header = 0;
@ -44,11 +47,20 @@ static int cmd_log_walk(struct rev_info *rev)
return 0;
}
static int git_log_config(const char *var, const char *value)
{
if (!strcmp(var, "log.showroot")) {
default_show_root = git_config_bool(var, value);
return 0;
}
return git_diff_ui_config(var, value);
}
int cmd_whatchanged(int argc, const char **argv, const char *prefix)
{
struct rev_info rev;
git_config(git_diff_ui_config);
git_config(git_log_config);
init_revisions(&rev, prefix);
rev.diff = 1;
rev.diffopt.recursive = 1;
@ -63,7 +75,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
{
struct rev_info rev;
git_config(git_diff_ui_config);
git_config(git_log_config);
init_revisions(&rev, prefix);
rev.diff = 1;
rev.diffopt.recursive = 1;
@ -80,7 +92,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)
{
struct rev_info rev;
git_config(git_diff_ui_config);
git_config(git_log_config);
init_revisions(&rev, prefix);
rev.always_show_header = 1;
cmd_log_init(argc, argv, prefix, &rev);
@ -109,7 +121,7 @@ static int git_format_config(const char *var, const char *value)
if (!strcmp(var, "diff.color")) {
return 0;
}
return git_diff_ui_config(var, value);
return git_log_config(var, value);
}

View File

@ -1176,7 +1176,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
* on an earlier try, but only when reusing delta data.
*/
if (!no_reuse_delta && trg_entry->in_pack &&
trg_entry->in_pack == src_entry->in_pack)
trg_entry->in_pack == src_entry->in_pack &&
trg_entry->in_pack_type != OBJ_REF_DELTA &&
trg_entry->in_pack_type != OBJ_OFS_DELTA)
return 0;
/*

View File

@ -16,8 +16,15 @@ static struct rev_info revs;
static int prune_object(char *path, const char *filename, const unsigned char *sha1)
{
char buf[20];
const char *type;
if (show_only) {
printf("would prune %s/%s\n", path, filename);
if (sha1_object_info(sha1, buf, NULL))
type = "unknown";
else
type = buf;
printf("%s %s\n", sha1_to_hex(sha1), type);
return 0;
}
unlink(mkpath("%s/%s", path, filename));

View File

@ -174,21 +174,58 @@ static int count_refspec_match(const char *pattern,
struct ref *refs,
struct ref **matched_ref)
{
int match;
int patlen = strlen(pattern);
struct ref *matched_weak = NULL;
struct ref *matched = NULL;
int weak_match = 0;
int match = 0;
for (match = 0; refs; refs = refs->next) {
for (weak_match = match = 0; refs; refs = refs->next) {
char *name = refs->name;
int namelen = strlen(name);
int weak_match;
if (namelen < patlen ||
memcmp(name + namelen - patlen, pattern, patlen))
continue;
if (namelen != patlen && name[namelen - patlen - 1] != '/')
continue;
match++;
*matched_ref = refs;
/* A match is "weak" if it is with refs outside
* heads or tags, and did not specify the pattern
* in full (e.g. "refs/remotes/origin/master") or at
* least from the toplevel (e.g. "remotes/origin/master");
* otherwise "git push $URL master" would result in
* ambiguity between remotes/origin/master and heads/master
* at the remote site.
*/
if (namelen != patlen &&
patlen != namelen - 5 &&
strncmp(name, "refs/heads/", 11) &&
strncmp(name, "refs/tags/", 10)) {
/* We want to catch the case where only weak
* matches are found and there are multiple
* matches, and where more than one strong
* matches are found, as ambiguous. One
* strong match with zero or more weak matches
* are acceptable as a unique match.
*/
matched_weak = refs;
weak_match++;
}
else {
matched = refs;
match++;
}
}
if (!matched) {
*matched_ref = matched_weak;
return weak_match;
}
else {
*matched_ref = matched;
return match;
}
return match;
}
static void link_dst_tail(struct ref *ref, struct ref ***tail)

View File

@ -14,7 +14,7 @@ die() {
}
usage() {
die "Usage: $0 [--template=<template_directory>] [--use-separate-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
die "Usage: $0 [--template=<template_directory>] [--use-immingled-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
}
get_repo_base() {
@ -48,6 +48,10 @@ Perhaps git-update-server-info needs to be run there?"
case "$name" in
*^*) continue;;
esac
case "$bare,$name" in
yes,* | ,heads/* | ,tags/*) ;;
*) continue ;;
esac
if test -n "$use_separate_remote" &&
branch_name=`expr "z$name" : 'zheads/\(.*\)'`
then
@ -115,7 +119,7 @@ bare=
reference=
origin=
origin_override=
use_separate_remote=
use_separate_remote=t
while
case "$#,$1" in
0,*) break ;;
@ -134,7 +138,10 @@ while
template="$1" ;;
*,-q|*,--quiet) quiet=-q ;;
*,--use-separate-remote)
# default
use_separate_remote=t ;;
*,--use-immingled-remote)
use_separate_remote= ;;
1,--reference) usage ;;
*,--reference)
shift; reference="$1" ;;
@ -169,18 +176,15 @@ repo="$1"
test -n "$repo" ||
die 'you must specify a repository to clone.'
# --bare implies --no-checkout
# --bare implies --no-checkout and --use-immingled-remote
if test yes = "$bare"
then
if test yes = "$origin_override"
then
die '--bare and --origin $origin options are incompatible.'
fi
if test t = "$use_separate_remote"
then
die '--bare and --use-separate-remote options are incompatible.'
fi
no_checkout=yes
use_separate_remote=
fi
if test -z "$origin"

View File

@ -161,8 +161,22 @@ sub new {
sub conn {
my $self = shift;
my $repo = $self->{'fullrep'};
if($repo =~ s/^:pserver:(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?//) {
my($user,$pass,$serv,$port) = ($1,$2,$3,$4);
if($repo =~ s/^:pserver(?:([^:]*)):(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?//) {
my($param,$user,$pass,$serv,$port) = ($1,$2,$3,$4,$5);
my($proxyhost,$proxyport);
if($param && ($param =~ m/proxy=([^;]+)/)) {
$proxyhost = $1;
# Default proxyport, if not specified, is 8080.
$proxyport = 8080;
if($ENV{"CVS_PROXY_PORT"}) {
$proxyport = $ENV{"CVS_PROXY_PORT"};
}
if($param =~ m/proxyport=([^;]+)/){
$proxyport = $1;
}
}
$user="anonymous" unless defined $user;
my $rr2 = "-";
unless($port) {
@ -187,13 +201,43 @@ sub conn {
}
$pass="A" unless $pass;
my $s = IO::Socket::INET->new(PeerHost => $serv, PeerPort => $port);
die "Socket to $serv: $!\n" unless defined $s;
my ($s, $rep);
if($proxyhost) {
# Use a HTTP Proxy. Only works for HTTP proxies that
# don't require user authentication
#
# See: http://www.ietf.org/rfc/rfc2817.txt
$s = IO::Socket::INET->new(PeerHost => $proxyhost, PeerPort => $proxyport);
die "Socket to $proxyhost: $!\n" unless defined $s;
$s->write("CONNECT $serv:$port HTTP/1.1\r\nHost: $serv:$port\r\n\r\n")
or die "Write to $proxyhost: $!\n";
$s->flush();
$rep = <$s>;
# The answer should look like 'HTTP/1.x 2yy ....'
if(!($rep =~ m#^HTTP/1\.. 2[0-9][0-9]#)) {
die "Proxy connect: $rep\n";
}
# Skip up to the empty line of the proxy server output
# including the response headers.
while ($rep = <$s>) {
last if (!defined $rep ||
$rep eq "\n" ||
$rep eq "\r\n");
}
} else {
$s = IO::Socket::INET->new(PeerHost => $serv, PeerPort => $port);
die "Socket to $serv: $!\n" unless defined $s;
}
$s->write("BEGIN AUTH REQUEST\n$repo\n$user\n$pass\nEND AUTH REQUEST\n")
or die "Write to $serv: $!\n";
$s->flush();
my $rep = <$s>;
$rep = <$s>;
if($rep ne "I LOVE YOU\n") {
$rep="<unknown>" unless $rep;

View File

@ -360,7 +360,7 @@ fetch_main () {
esac
append_fetch_head "$head" "$remote" \
"$remote_name" "$remote_nick" "$local_name" "$not_for_merge"
"$remote_name" "$remote_nick" "$local_name" "$not_for_merge" || exit
done
@ -414,15 +414,16 @@ fetch_main () {
done
local_name=$(expr "z$found" : 'z[^:]*:\(.*\)')
append_fetch_head "$sha1" "$remote" \
"$remote_name" "$remote_nick" "$local_name" "$not_for_merge"
done
"$remote_name" "$remote_nick" "$local_name" \
"$not_for_merge" || exit
done &&
if [ "$pack_lockfile" ]; then rm -f "$pack_lockfile"; fi
) || exit ;;
esac
}
fetch_main "$reflist"
fetch_main "$reflist" || exit
# automated tag following
case "$no_tags$tags" in
@ -450,7 +451,7 @@ case "$no_tags$tags" in
case "$taglist" in
'') ;;
?*)
fetch_main "$taglist" ;;
fetch_main "$taglist" || exit ;;
esac
esac

View File

@ -21,6 +21,7 @@ $ENV{TZ} = 'UTC';
$ENV{LC_ALL} = 'C';
$| = 1; # unbuffer STDOUT
sub fatal (@) { print STDERR $@; exit 1 }
# If SVN:: library support is added, please make the dependencies
# optional and preserve the capability to use the command-line client.
# use eval { require SVN::... } to make it lazy load
@ -39,7 +40,7 @@ memoize('revisions_eq');
memoize('cmt_metadata');
memoize('get_commit_time');
my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib);
my ($SVN, $_use_lib);
sub nag_lib {
print STDERR <<EOF;
@ -66,7 +67,8 @@ my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
$_template, $_shared, $_no_default_regex, $_no_graft_copy,
$_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,
$_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m,
$_merge, $_strategy, $_dry_run, $_ignore_nodate, $_non_recursive);
$_merge, $_strategy, $_dry_run, $_ignore_nodate, $_non_recursive,
$_username, $_config_dir, $_no_auth_cache);
my (@_branch_from, %tree_map, %users, %rusers, %equiv);
my ($_svn_co_url_revs, $_svn_pg_peg_revs);
my @repo_path_split_cache;
@ -79,6 +81,9 @@ my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
'repack:i' => \$_repack,
'no-metadata' => \$_no_metadata,
'quiet|q' => \$_q,
'username=s' => \$_username,
'config-dir=s' => \$_config_dir,
'no-auth-cache' => \$_no_auth_cache,
'ignore-nodate' => \$_ignore_nodate,
'repack-flags|repack-args|repack-opts=s' => \$_repack_flags);
@ -377,10 +382,7 @@ sub fetch_cmd {
sub fetch_lib {
my (@parents) = @_;
$SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
my $repo;
($repo, $SVN_PATH) = repo_path_split($SVN_URL);
$SVN_LOG ||= libsvn_connect($repo);
$SVN ||= libsvn_connect($repo);
$SVN ||= libsvn_connect($SVN_URL);
my ($last_rev, $last_commit) = svn_grab_base_rev();
my ($base, $head) = libsvn_parse_revision($last_rev);
if ($base > $head) {
@ -422,7 +424,7 @@ sub fetch_lib {
# performance sucks with it enabled, so it's much
# faster to fetch revision ranges instead of relying
# on the limiter.
libsvn_get_log($SVN_LOG, '/'.$SVN_PATH,
libsvn_get_log(libsvn_dup_ra($SVN), [''],
$min, $max, 0, 1, 1,
sub {
my $log_msg;
@ -524,7 +526,6 @@ sub commit_lib {
my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$";
my $repo;
($repo, $SVN_PATH) = repo_path_split($SVN_URL);
set_svn_commit_env();
foreach my $c (@revs) {
my $log_msg = get_commit_message($c, $commit_msg);
@ -533,13 +534,11 @@ sub commit_lib {
# can't track down... (it's probably in the SVN code)
defined(my $pid = open my $fh, '-|') or croak $!;
if (!$pid) {
$SVN_LOG = libsvn_connect($repo);
$SVN = libsvn_connect($repo);
my $ed = SVN::Git::Editor->new(
{ r => $r_last,
ra => $SVN_LOG,
ra => libsvn_dup_ra($SVN),
c => $c,
svn_path => $SVN_PATH
svn_path => $SVN->{svn_path},
},
$SVN->get_commit_editor(
$log_msg->{msg},
@ -571,7 +570,7 @@ sub commit_lib {
$no = 1;
}
}
close $fh or croak $?;
close $fh or exit 1;
if (! defined $r_new && ! defined $cmt_new) {
unless ($no) {
die "Failed to parse revision information\n";
@ -657,10 +656,9 @@ sub show_ignore_cmd {
sub show_ignore_lib {
my $repo;
($repo, $SVN_PATH) = repo_path_split($SVN_URL);
$SVN ||= libsvn_connect($repo);
$SVN ||= libsvn_connect($SVN_URL);
my $r = defined $_revision ? $_revision : $SVN->get_latest_revnum;
libsvn_traverse_ignore(\*STDOUT, $SVN_PATH, $r);
libsvn_traverse_ignore(\*STDOUT, $SVN->{svn_path}, $r);
}
sub graft_branches {
@ -786,7 +784,7 @@ sub show_log {
} elsif (/^:\d{6} \d{6} $sha1_short/o) {
push @{$c->{raw}}, $_;
} elsif (/^[ACRMDT]\t/) {
# we could add $SVN_PATH here, but that requires
# we could add $SVN->{svn_path} here, but that requires
# remote access at the moment (repo_path_split)...
s#^([ACRMDT])\t# $1 #;
push @{$c->{changed}}, $_;
@ -852,10 +850,7 @@ sub commit_diff {
$_message ||= get_commit_message($tb,
"$GIT_DIR/.svn-commit.tmp.$$")->{msg};
}
my $repo;
($repo, $SVN_PATH) = repo_path_split($SVN_URL);
$SVN_LOG ||= libsvn_connect($repo);
$SVN ||= libsvn_connect($repo);
$SVN ||= libsvn_connect($SVN_URL);
if ($r eq 'HEAD') {
$r = $SVN->get_latest_revnum;
} elsif ($r !~ /^\d+$/) {
@ -864,8 +859,9 @@ sub commit_diff {
my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : ();
my $rev_committed;
my $ed = SVN::Git::Editor->new({ r => $r,
ra => $SVN_LOG, c => $tb,
svn_path => $SVN_PATH
ra => libsvn_dup_ra($SVN),
c => $tb,
svn_path => $SVN->{svn_path}
},
$SVN->get_commit_editor($_message,
sub {
@ -873,13 +869,16 @@ sub commit_diff {
print "Committed $_[0]\n";
}, @lock)
);
my $mods = libsvn_checkout_tree($ta, $tb, $ed);
if (@$mods == 0) {
print "No changes\n$ta == $tb\n";
$ed->abort_edit;
} else {
$ed->close_edit;
}
eval {
my $mods = libsvn_checkout_tree($ta, $tb, $ed);
if (@$mods == 0) {
print "No changes\n$ta == $tb\n";
$ed->abort_edit;
} else {
$ed->close_edit;
}
};
fatal "$@\n" if $@;
$_message = $_file = undef;
return $rev_committed;
}
@ -1143,8 +1142,7 @@ sub graft_file_copy_lib {
my $tree_paths = $l_map->{$u};
my $pfx = common_prefix([keys %$tree_paths]);
my ($repo, $path) = repo_path_split($u.$pfx);
$SVN_LOG ||= libsvn_connect($repo);
$SVN ||= libsvn_connect($repo);
$SVN = libsvn_connect($repo);
my ($base, $head) = libsvn_parse_revision();
my $inc = 1000;
@ -1153,7 +1151,8 @@ sub graft_file_copy_lib {
$SVN::Error::handler = \&libsvn_skip_unknown_revs;
while (1) {
my $pool = SVN::Pool->new;
libsvn_get_log($SVN_LOG, "/$path", $min, $max, 0, 1, 1,
libsvn_get_log(libsvn_dup_ra($SVN), [$path],
$min, $max, 0, 1, 1,
sub {
libsvn_graft_file_copies($grafts, $tree_paths,
$path, @_);
@ -1263,13 +1262,9 @@ sub repo_path_split {
return ($u, $full_url);
}
}
if ($_use_lib) {
my $tmp = libsvn_connect($full_url);
my $url = $tmp->get_repos_root;
$full_url =~ s#^\Q$url\E/*##;
push @repo_path_split_cache, qr/^(\Q$url\E)/;
return ($url, $full_url);
return ($tmp->{repos_root}, $tmp->{svn_path});
} else {
my ($url, $path) = ($full_url =~ m!^([a-z\+]+://[^/]*)(.*)$!i);
$path =~ s#^/+##;
@ -2683,26 +2678,169 @@ sub libsvn_load {
my $kill_stupid_warnings = $SVN::Node::none.$SVN::Node::file.
$SVN::Node::dir.$SVN::Node::unknown.
$SVN::Node::none.$SVN::Node::file.
$SVN::Node::dir.$SVN::Node::unknown;
$SVN::Node::dir.$SVN::Node::unknown.
$SVN::Auth::SSL::CNMISMATCH.
$SVN::Auth::SSL::NOTYETVALID.
$SVN::Auth::SSL::EXPIRED.
$SVN::Auth::SSL::UNKNOWNCA.
$SVN::Auth::SSL::OTHER;
1;
};
}
sub _simple_prompt {
my ($cred, $realm, $default_username, $may_save, $pool) = @_;
$may_save = undef if $_no_auth_cache;
$default_username = $_username if defined $_username;
if (defined $default_username && length $default_username) {
if (defined $realm && length $realm) {
print "Authentication realm: $realm\n";
}
$cred->username($default_username);
} else {
_username_prompt($cred, $realm, $may_save, $pool);
}
$cred->password(_read_password("Password for '" .
$cred->username . "': ", $realm));
$cred->may_save($may_save);
$SVN::_Core::SVN_NO_ERROR;
}
sub _ssl_server_trust_prompt {
my ($cred, $realm, $failures, $cert_info, $may_save, $pool) = @_;
$may_save = undef if $_no_auth_cache;
print "Error validating server certificate for '$realm':\n";
if ($failures & $SVN::Auth::SSL::UNKNOWNCA) {
print " - The certificate is not issued by a trusted ",
"authority. Use the\n",
" fingerprint to validate the certificate manually!\n";
}
if ($failures & $SVN::Auth::SSL::CNMISMATCH) {
print " - The certificate hostname does not match.\n";
}
if ($failures & $SVN::Auth::SSL::NOTYETVALID) {
print " - The certificate is not yet valid.\n";
}
if ($failures & $SVN::Auth::SSL::EXPIRED) {
print " - The certificate has expired.\n";
}
if ($failures & $SVN::Auth::SSL::OTHER) {
print " - The certificate has an unknown error.\n";
}
printf( "Certificate information:\n".
" - Hostname: %s\n".
" - Valid: from %s until %s\n".
" - Issuer: %s\n".
" - Fingerprint: %s\n",
map $cert_info->$_, qw(hostname valid_from valid_until
issuer_dname fingerprint) );
my $choice;
prompt:
print $may_save ?
"(R)eject, accept (t)emporarily or accept (p)ermanently? " :
"(R)eject or accept (t)emporarily? ";
$choice = lc(substr(<STDIN> || 'R', 0, 1));
if ($choice =~ /^t$/i) {
$cred->may_save(undef);
} elsif ($choice =~ /^r$/i) {
return -1;
} elsif ($may_save && $choice =~ /^p$/i) {
$cred->may_save($may_save);
} else {
goto prompt;
}
$cred->accepted_failures($failures);
$SVN::_Core::SVN_NO_ERROR;
}
sub _ssl_client_cert_prompt {
my ($cred, $realm, $may_save, $pool) = @_;
$may_save = undef if $_no_auth_cache;
print "Client certificate filename: ";
chomp(my $filename = <STDIN>);
$cred->cert_file($filename);
$cred->may_save($may_save);
$SVN::_Core::SVN_NO_ERROR;
}
sub _ssl_client_cert_pw_prompt {
my ($cred, $realm, $may_save, $pool) = @_;
$may_save = undef if $_no_auth_cache;
$cred->password(_read_password("Password: ", $realm));
$cred->may_save($may_save);
$SVN::_Core::SVN_NO_ERROR;
}
sub _username_prompt {
my ($cred, $realm, $may_save, $pool) = @_;
$may_save = undef if $_no_auth_cache;
if (defined $realm && length $realm) {
print "Authentication realm: $realm\n";
}
my $username;
if (defined $_username) {
$username = $_username;
} else {
print "Username: ";
chomp($username = <STDIN>);
}
$cred->username($username);
$cred->may_save($may_save);
$SVN::_Core::SVN_NO_ERROR;
}
sub _read_password {
my ($prompt, $realm) = @_;
print $prompt;
require Term::ReadKey;
Term::ReadKey::ReadMode('noecho');
my $password = '';
while (defined(my $key = Term::ReadKey::ReadKey(0))) {
last if $key =~ /[\012\015]/; # \n\r
$password .= $key;
}
Term::ReadKey::ReadMode('restore');
print "\n";
$password;
}
sub libsvn_connect {
my ($url) = @_;
my $auth = SVN::Core::auth_open([SVN::Client::get_simple_provider(),
SVN::Client::get_ssl_server_trust_file_provider(),
SVN::Client::get_username_provider()]);
my $s = eval { SVN::Ra->new(url => $url, auth => $auth) };
return $s;
SVN::_Core::svn_config_ensure($_config_dir, undef);
my ($baton, $callbacks) = SVN::Core::auth_open_helper([
SVN::Client::get_simple_provider(),
SVN::Client::get_ssl_server_trust_file_provider(),
SVN::Client::get_simple_prompt_provider(
\&_simple_prompt, 2),
SVN::Client::get_ssl_client_cert_prompt_provider(
\&_ssl_client_cert_prompt, 2),
SVN::Client::get_ssl_client_cert_pw_prompt_provider(
\&_ssl_client_cert_pw_prompt, 2),
SVN::Client::get_username_provider(),
SVN::Client::get_ssl_server_trust_prompt_provider(
\&_ssl_server_trust_prompt),
SVN::Client::get_username_prompt_provider(
\&_username_prompt, 2),
]);
my $ra = SVN::Ra->new(url => $url, auth => $baton,
pool => SVN::Pool->new,
auth_provider_callbacks => $callbacks);
$ra->{svn_path} = $url;
$ra->{repos_root} = $ra->get_repos_root;
$ra->{svn_path} =~ s#^\Q$ra->{repos_root}\E/*##;
push @repo_path_split_cache, qr/^(\Q$ra->{repos_root}\E)/;
return $ra;
}
sub libsvn_dup_ra {
my ($ra) = @_;
SVN::Ra->new(map { $_ => $ra->{$_} }
qw/url auth auth_provider_callbacks repos_root svn_path/);
}
sub libsvn_get_file {
my ($gui, $f, $rev, $chg) = @_;
my $p = $f;
if (length $SVN_PATH > 0) {
return unless ($p =~ s#^\Q$SVN_PATH\E/##);
}
$f =~ s#^/##;
print "\t$chg\t$f\n" unless $_q;
my ($hash, $pid, $in, $out);
@ -2739,7 +2877,7 @@ sub libsvn_get_file {
waitpid $pid, 0;
$hash =~ /^$sha1$/o or die "not a sha1: $hash\n";
}
print $gui $mode,' ',$hash,"\t",$p,"\0" or croak $!;
print $gui $mode,' ',$hash,"\t",$f,"\0" or croak $!;
}
sub libsvn_log_entry {
@ -2757,7 +2895,6 @@ sub libsvn_log_entry {
sub process_rm {
my ($gui, $last_commit, $f) = @_;
$f =~ s#^\Q$SVN_PATH\E/?## or return;
# remove entire directories.
if (safe_qx('git-ls-tree',$last_commit,'--',$f) =~ /^040000 tree/) {
defined(my $pid = open my $ls, '-|') or croak $!;
@ -2779,9 +2916,11 @@ sub libsvn_fetch {
my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
open my $gui, '| git-update-index -z --index-info' or croak $!;
my @amr;
my $p = $SVN->{svn_path};
foreach my $f (keys %$paths) {
my $m = $paths->{$f}->action();
$f =~ s#^/+##;
$f =~ s#^/\Q$p\E/##;
next if $f =~ m#^/#;
if ($m =~ /^[DR]$/) {
print "\t$m\t$f\n" unless $_q;
process_rm($gui, $last_commit, $f);
@ -2871,9 +3010,9 @@ sub libsvn_parse_revision {
sub libsvn_traverse {
my ($gui, $pfx, $path, $rev, $files) = @_;
my $cwd = "$pfx/$path";
my $cwd = length $pfx ? "$pfx/$path" : $path;
my $pool = SVN::Pool->new;
$cwd =~ s#^/+##g;
$cwd =~ s#^\Q$SVN->{svn_path}\E##;
my ($dirent, $r, $props) = $SVN->get_dir($cwd, $rev, $pool);
foreach my $d (keys %$dirent) {
my $t = $dirent->{$d}->kind;
@ -2897,7 +3036,7 @@ sub libsvn_traverse_ignore {
my $pool = SVN::Pool->new;
my ($dirent, undef, $props) = $SVN->get_dir($path, $r, $pool);
my $p = $path;
$p =~ s#^\Q$SVN_PATH\E/?##;
$p =~ s#^\Q$SVN->{svn_path}\E/##;
print $fh length $p ? "\n# $p\n" : "\n# /\n";
if (my $s = $props->{'svn:ignore'}) {
$s =~ s/[\r\n]+/\n/g;
@ -2924,7 +3063,7 @@ sub revisions_eq {
if ($_use_lib) {
# should be OK to use Pool here (r1 - r0) should be small
my $pool = SVN::Pool->new;
libsvn_get_log($SVN, "/$path", $r0, $r1,
libsvn_get_log($SVN, [$path], $r0, $r1,
0, 1, 1, sub {$nr++}, $pool);
$pool->clear;
} else {
@ -2939,7 +3078,7 @@ sub revisions_eq {
sub libsvn_find_parent_branch {
my ($paths, $rev, $author, $date, $msg) = @_;
my $svn_path = '/'.$SVN_PATH;
my $svn_path = '/'.$SVN->{svn_path};
# look for a parent from another branch:
my $i = $paths->{$svn_path} or return;
@ -2950,7 +3089,7 @@ sub libsvn_find_parent_branch {
$branch_from =~ s#^/##;
my $l_map = {};
read_url_paths_all($l_map, '', "$GIT_DIR/svn");
my $url = $SVN->{url};
my $url = $SVN->{repos_root};
defined $l_map->{$url} or return;
my $id = $l_map->{$url}->{$branch_from};
if (!defined $id && $_follow_parent) {
@ -2972,7 +3111,7 @@ sub libsvn_find_parent_branch {
$GIT_SVN = $ENV{GIT_SVN_ID} = $id;
init_vars();
$SVN_URL = "$url/$branch_from";
$SVN_LOG = $SVN = undef;
$SVN = undef;
setup_git_svn();
# we can't assume SVN_URL exists at r+1:
$_revision = "0:$r";
@ -3009,7 +3148,7 @@ sub libsvn_new_tree {
}
my ($paths, $rev, $author, $date, $msg) = @_;
open my $gui, '| git-update-index -z --index-info' or croak $!;
libsvn_traverse($gui, '', $SVN_PATH, $rev);
libsvn_traverse($gui, '', $SVN->{svn_path}, $rev);
close $gui or croak $?;
return libsvn_log_entry($rev, $author, $date, $msg);
}
@ -3094,11 +3233,10 @@ sub libsvn_commit_cb {
sub libsvn_ls_fullurl {
my $fullurl = shift;
my ($repo, $path) = repo_path_split($fullurl);
$SVN ||= libsvn_connect($repo);
$SVN ||= libsvn_connect($fullurl);
my @ret;
my $pool = SVN::Pool->new;
my ($dirent, undef, undef) = $SVN->get_dir($path,
my ($dirent, undef, undef) = $SVN->get_dir($SVN->{svn_path},
$SVN->get_latest_revnum, $pool);
foreach my $d (keys %$dirent) {
if ($dirent->{$d}->kind == $SVN::Node::dir) {
@ -3120,8 +3258,9 @@ sub libsvn_skip_unknown_revs {
# Wonderfully consistent library, eh?
# 160013 - svn:// and file://
# 175002 - http(s)://
# 175007 - http(s):// (this repo required authorization, too...)
# More codes may be discovered later...
if ($errno == 175002 || $errno == 160013) {
if ($errno == 175007 || $errno == 175002 || $errno == 160013) {
return;
}
croak "Error from SVN, ($errno): ", $err->expanded_message,"\n";
@ -3209,8 +3348,7 @@ sub split_path {
}
sub repo_path {
(defined $_[1] && length $_[1]) ? "$_[0]->{svn_path}/$_[1]"
: $_[0]->{svn_path}
(defined $_[1] && length $_[1]) ? $_[1] : ''
}
sub url_path {
@ -3242,10 +3380,9 @@ sub rmdirs {
exec qw/git-ls-tree --name-only -r -z/, $self->{c} or croak $!;
}
local $/ = "\0";
my @svn_path = split m#/#, $self->{svn_path};
while (<$fh>) {
chomp;
my @dn = (@svn_path, (split m#/#, $_));
my @dn = split m#/#, $_;
while (pop @dn) {
delete $rm->{join '/', @dn};
}

View File

@ -334,11 +334,13 @@ div.diff.extended_header {
padding: 2px 0px 2px 0px;
}
div.diff a.list,
div.diff a.path,
div.diff a.hash {
text-decoration: none;
}
div.diff a.list:hover,
div.diff a.path:hover,
div.diff a.hash:hover {
text-decoration: underline;
@ -362,14 +364,25 @@ div.diff.rem {
color: #cc0000;
}
div.diff.chunk_header a,
div.diff.chunk_header {
color: #990099;
}
div.diff.chunk_header {
border: dotted #ffe0ff;
border-width: 1px 0px 0px 0px;
margin-top: 2px;
}
div.diff.chunk_header span.chunk_info {
background-color: #ffeeff;
}
div.diff.chunk_header span.section {
color: #aa22aa;
}
div.diff.incomplete {
color: #cccccc;
}

View File

@ -425,6 +425,7 @@ my %actions = (
"history" => \&git_history,
"log" => \&git_log,
"rss" => \&git_rss,
"atom" => \&git_atom,
"search" => \&git_search,
"search_help" => \&git_search_help,
"shortlog" => \&git_shortlog,
@ -459,7 +460,8 @@ exit;
sub href(%) {
my %params = @_;
my $href = $my_uri;
# default is to use -absolute url() i.e. $my_uri
my $href = $params{-full} ? $my_url : $my_uri;
# XXX: Warning: If you touch this, check the search form for updating,
# too.
@ -874,8 +876,10 @@ sub format_subject_html {
}
}
# format patch (diff) line (rather not to be used for diff headers)
sub format_diff_line {
my $line = shift;
my ($from, $to) = @_;
my $char = substr($line, 0, 1);
my $diff_class = "";
@ -891,6 +895,25 @@ sub format_diff_line {
$diff_class = " incomplete";
}
$line = untabify($line);
if ($from && $to && $line =~ m/^\@{2} /) {
my ($from_text, $from_start, $from_lines, $to_text, $to_start, $to_lines, $section) =
$line =~ m/^\@{2} (-(\d+)(?:,(\d+))?) (\+(\d+)(?:,(\d+))?) \@{2}(.*)$/;
$from_lines = 0 unless defined $from_lines;
$to_lines = 0 unless defined $to_lines;
if ($from->{'href'}) {
$from_text = $cgi->a({-href=>"$from->{'href'}#l$from_start",
-class=>"list"}, $from_text);
}
if ($to->{'href'}) {
$to_text = $cgi->a({-href=>"$to->{'href'}#l$to_start",
-class=>"list"}, $to_text);
}
$line = "<span class=\"chunk_info\">@@ $from_text $to_text @@</span>" .
"<span class=\"section\">" . esc_html($section, -nbsp=>1) . "</span>";
return "<div class=\"diff$diff_class\">$line</div>\n";
}
return "<div class=\"diff$diff_class\">" . esc_html($line, -nbsp=>1) . "</div>\n";
}
@ -1176,10 +1199,12 @@ sub parse_date {
$date{'mday'} = $mday;
$date{'day'} = $days[$wday];
$date{'month'} = $months[$mon];
$date{'rfc2822'} = sprintf "%s, %d %s %4d %02d:%02d:%02d +0000",
$days[$wday], $mday, $months[$mon], 1900+$year, $hour ,$min, $sec;
$date{'rfc2822'} = sprintf "%s, %d %s %4d %02d:%02d:%02d +0000",
$days[$wday], $mday, $months[$mon], 1900+$year, $hour ,$min, $sec;
$date{'mday-time'} = sprintf "%d %s %02d:%02d",
$mday, $months[$mon], $hour ,$min;
$date{'iso-8601'} = sprintf "%04d-%02d-%02dT%02d:%02d:%02dZ",
1900+$year, $mon, $mday, $hour ,$min, $sec;
$tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/;
my $local = $epoch + ((int $1 + ($2/60)) * 3600);
@ -1187,9 +1212,9 @@ sub parse_date {
$date{'hour_local'} = $hour;
$date{'minute_local'} = $min;
$date{'tz_local'} = $tz;
$date{'iso-tz'} = sprintf ("%04d-%02d-%02d %02d:%02d:%02d %s",
1900+$year, $mon+1, $mday,
$hour, $min, $sec, $tz);
$date{'iso-tz'} = sprintf("%04d-%02d-%02d %02d:%02d:%02d %s",
1900+$year, $mon+1, $mday,
$hour, $min, $sec, $tz);
return %date;
}
@ -1650,14 +1675,17 @@ EOF
}
}
if (defined $project) {
printf('<link rel="alternate" title="%s log" '.
'href="%s" type="application/rss+xml"/>'."\n",
printf('<link rel="alternate" title="%s log RSS feed" '.
'href="%s" type="application/rss+xml" />'."\n",
esc_param($project), href(action=>"rss"));
printf('<link rel="alternate" title="%s log Atom feed" '.
'href="%s" type="application/atom+xml" />'."\n",
esc_param($project), href(action=>"atom"));
} else {
printf('<link rel="alternate" title="%s projects list" '.
'href="%s" type="text/plain; charset=utf-8"/>'."\n",
$site_name, href(project=>undef, action=>"project_index"));
printf('<link rel="alternate" title="%s projects logs" '.
printf('<link rel="alternate" title="%s projects feeds" '.
'href="%s" type="text/x-opml"/>'."\n",
$site_name, href(project=>undef, action=>"opml"));
}
@ -1723,7 +1751,9 @@ sub git_footer_html {
print "<div class=\"page_footer_text\">" . esc_html($descr) . "</div>\n";
}
print $cgi->a({-href => href(action=>"rss"),
-class => "rss_logo"}, "RSS") . "\n";
-class => "rss_logo"}, "RSS") . " ";
print $cgi->a({-href => href(action=>"atom"),
-class => "rss_logo"}, "Atom") . "\n";
} else {
print $cgi->a({-href => href(project=>undef, action=>"opml"),
-class => "rss_logo"}, "OPML") . " ";
@ -2062,7 +2092,11 @@ sub git_difftree_body {
# link to patch
$patchno++;
print $cgi->a({-href => "#patch$patchno"}, "patch");
print " | ";
}
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
hash_base=>$hash, file_name=>$diff{'file'})},
"blob") . " | ";
print "</td>\n";
} elsif ($diff{'status'} eq "D") { # deleted
@ -2082,13 +2116,11 @@ sub git_difftree_body {
}
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
hash_base=>$parent, file_name=>$diff{'file'})},
"blob") . " | ";
"blob") . " | ";
if ($have_blame) {
print $cgi->a({-href =>
href(action=>"blame",
hash_base=>$parent,
file_name=>$diff{'file'})},
"blame") . " | ";
print $cgi->a({-href => href(action=>"blame", hash_base=>$parent,
file_name=>$diff{'file'})},
"blame") . " | ";
}
print $cgi->a({-href => href(action=>"history", hash_base=>$parent,
file_name=>$diff{'file'})},
@ -2133,13 +2165,12 @@ sub git_difftree_body {
" | ";
}
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
hash_base=>$hash, file_name=>$diff{'file'})},
"blob") . " | ";
hash_base=>$hash, file_name=>$diff{'file'})},
"blob") . " | ";
if ($have_blame) {
print $cgi->a({-href => href(action=>"blame",
hash_base=>$hash,
file_name=>$diff{'file'})},
"blame") . " | ";
print $cgi->a({-href => href(action=>"blame", hash_base=>$hash,
file_name=>$diff{'file'})},
"blame") . " | ";
}
print $cgi->a({-href => href(action=>"history", hash_base=>$hash,
file_name=>$diff{'file'})},
@ -2178,17 +2209,16 @@ sub git_difftree_body {
"diff") .
" | ";
}
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
hash_base=>$parent, file_name=>$diff{'from_file'})},
"blob") . " | ";
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
hash_base=>$parent, file_name=>$diff{'to_file'})},
"blob") . " | ";
if ($have_blame) {
print $cgi->a({-href => href(action=>"blame",
hash_base=>$hash,
file_name=>$diff{'to_file'})},
"blame") . " | ";
print $cgi->a({-href => href(action=>"blame", hash_base=>$hash,
file_name=>$diff{'to_file'})},
"blame") . " | ";
}
print $cgi->a({-href => href(action=>"history", hash_base=>$parent,
file_name=>$diff{'from_file'})},
print $cgi->a({-href => href(action=>"history", hash_base=>$hash,
file_name=>$diff{'to_file'})},
"history");
print "</td>\n";
@ -2202,31 +2232,56 @@ sub git_patchset_body {
my ($fd, $difftree, $hash, $hash_parent) = @_;
my $patch_idx = 0;
my $in_header = 0;
my $patch_found = 0;
my $patch_line;
my $diffinfo;
my (%from, %to);
my ($from_id, $to_id);
print "<div class=\"patchset\">\n";
LINE:
while (my $patch_line = <$fd>) {
# skip to first patch
while ($patch_line = <$fd>) {
chomp $patch_line;
if ($patch_line =~ m/^diff /) { # "git diff" header
# beginning of patch (in patchset)
if ($patch_found) {
# close extended header for previous empty patch
if ($in_header) {
print "</div>\n" # class="diff extended_header"
}
# close previous patch
print "</div>\n"; # class="patch"
} else {
# first patch in patchset
$patch_found = 1;
last if ($patch_line =~ m/^diff /);
}
PATCH:
while ($patch_line) {
my @diff_header;
# git diff header
#assert($patch_line =~ m/^diff /) if DEBUG;
#assert($patch_line !~ m!$/$!) if DEBUG; # is chomp-ed
push @diff_header, $patch_line;
# extended diff header
EXTENDED_HEADER:
while ($patch_line = <$fd>) {
chomp $patch_line;
last EXTENDED_HEADER if ($patch_line =~ m/^--- /);
if ($patch_line =~ m/^index ([0-9a-fA-F]{40})..([0-9a-fA-F]{40})/) {
$from_id = $1;
$to_id = $2;
}
print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n";
push @diff_header, $patch_line;
}
#last PATCH unless $patch_line;
my $last_patch_line = $patch_line;
# check if current patch belong to current raw line
# and parse raw git-diff line if needed
if (defined $diffinfo &&
$diffinfo->{'from_id'} eq $from_id &&
$diffinfo->{'to_id'} eq $to_id) {
# this is split patch
print "<div class=\"patch cont\">\n";
} else {
# advance raw git-diff output if needed
$patch_idx++ if defined $diffinfo;
# read and prepare patch information
if (ref($difftree->[$patch_idx]) eq "HASH") {
@ -2247,100 +2302,112 @@ sub git_patchset_body {
hash=>$diffinfo->{'to_id'},
file_name=>$to{'file'});
}
$patch_idx++;
# print "git diff" header
$patch_line =~ s!^(diff (.*?) )"?a/.*$!$1!;
if ($from{'href'}) {
$patch_line .= $cgi->a({-href => $from{'href'}, -class => "path"},
'a/' . esc_path($from{'file'}));
} else { # file was added
$patch_line .= 'a/' . esc_path($from{'file'});
}
$patch_line .= ' ';
if ($to{'href'}) {
$patch_line .= $cgi->a({-href => $to{'href'}, -class => "path"},
'b/' . esc_path($to{'file'}));
} else { # file was deleted
$patch_line .= 'b/' . esc_path($to{'file'});
}
print "<div class=\"diff header\">$patch_line</div>\n";
print "<div class=\"diff extended_header\">\n";
$in_header = 1;
next LINE;
# this is first patch for raw difftree line with $patch_idx index
# we index @$difftree array from 0, but number patches from 1
print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n";
}
if ($in_header) {
if ($patch_line !~ m/^---/) {
# match <path>
if ($patch_line =~ s!^((copy|rename) from ).*$!$1! && $from{'href'}) {
$patch_line .= $cgi->a({-href=>$from{'href'}, -class=>"path"},
esc_path($from{'file'}));
}
if ($patch_line =~ s!^((copy|rename) to ).*$!$1! && $to{'href'}) {
$patch_line = $cgi->a({-href=>$to{'href'}, -class=>"path"},
esc_path($to{'file'}));
}
# match <mode>
if ($patch_line =~ m/\s(\d{6})$/) {
$patch_line .= '<span class="info"> (' .
file_type_long($1) .
')</span>';
}
# match <hash>
if ($patch_line =~ m/^index/) {
my ($from_link, $to_link);
if ($from{'href'}) {
$from_link = $cgi->a({-href=>$from{'href'}, -class=>"hash"},
substr($diffinfo->{'from_id'},0,7));
} else {
$from_link = '0' x 7;
}
if ($to{'href'}) {
$to_link = $cgi->a({-href=>$to{'href'}, -class=>"hash"},
substr($diffinfo->{'to_id'},0,7));
} else {
$to_link = '0' x 7;
}
my ($from_id, $to_id) = ($diffinfo->{'from_id'}, $diffinfo->{'to_id'});
$patch_line =~ s!$from_id\.\.$to_id!$from_link..$to_link!;
}
print $patch_line . "<br/>\n";
} else {
#$in_header && $patch_line =~ m/^---/;
print "</div>\n"; # class="diff extended_header"
$in_header = 0;
# print "git diff" header
$patch_line = shift @diff_header;
$patch_line =~ s!^(diff (.*?) )"?a/.*$!$1!;
if ($from{'href'}) {
$patch_line .= $cgi->a({-href => $from{'href'}, -class => "path"},
'a/' . esc_path($from{'file'}));
} else { # file was added
$patch_line .= 'a/' . esc_path($from{'file'});
}
$patch_line .= ' ';
if ($to{'href'}) {
$patch_line .= $cgi->a({-href => $to{'href'}, -class => "path"},
'b/' . esc_path($to{'file'}));
} else { # file was deleted
$patch_line .= 'b/' . esc_path($to{'file'});
}
print "<div class=\"diff header\">$patch_line</div>\n";
# print extended diff header
print "<div class=\"diff extended_header\">\n" if (@diff_header > 0);
EXTENDED_HEADER:
foreach $patch_line (@diff_header) {
# match <path>
if ($patch_line =~ s!^((copy|rename) from ).*$!$1! && $from{'href'}) {
$patch_line .= $cgi->a({-href=>$from{'href'}, -class=>"path"},
esc_path($from{'file'}));
}
if ($patch_line =~ s!^((copy|rename) to ).*$!$1! && $to{'href'}) {
$patch_line = $cgi->a({-href=>$to{'href'}, -class=>"path"},
esc_path($to{'file'}));
}
# match <mode>
if ($patch_line =~ m/\s(\d{6})$/) {
$patch_line .= '<span class="info"> (' .
file_type_long($1) .
')</span>';
}
# match <hash>
if ($patch_line =~ m/^index/) {
my ($from_link, $to_link);
if ($from{'href'}) {
$patch_line = '--- a/' .
$cgi->a({-href=>$from{'href'}, -class=>"path"},
esc_path($from{'file'}));
$from_link = $cgi->a({-href=>$from{'href'}, -class=>"hash"},
substr($diffinfo->{'from_id'},0,7));
} else {
$from_link = '0' x 7;
}
print "<div class=\"diff from_file\">$patch_line</div>\n";
$patch_line = <$fd>;
chomp $patch_line;
#$patch_line =~ m/^+++/;
if ($to{'href'}) {
$patch_line = '+++ b/' .
$cgi->a({-href=>$to{'href'}, -class=>"path"},
esc_path($to{'file'}));
$to_link = $cgi->a({-href=>$to{'href'}, -class=>"hash"},
substr($diffinfo->{'to_id'},0,7));
} else {
$to_link = '0' x 7;
}
print "<div class=\"diff to_file\">$patch_line</div>\n";
#affirm {
# my ($from_hash, $to_hash) =
# ($patch_line =~ m/^index ([0-9a-fA-F]{40})..([0-9a-fA-F]{40})/);
# my ($from_id, $to_id) =
# ($diffinfo->{'from_id'}, $diffinfo->{'to_id'});
# ($from_hash eq $from_id) && ($to_hash eq $to_id);
#} if DEBUG;
my ($from_id, $to_id) = ($diffinfo->{'from_id'}, $diffinfo->{'to_id'});
$patch_line =~ s!$from_id\.\.$to_id!$from_link..$to_link!;
}
print $patch_line . "<br/>\n";
}
print "</div>\n" if (@diff_header > 0); # class="diff extended_header"
next LINE;
# from-file/to-file diff header
$patch_line = $last_patch_line;
#assert($patch_line =~ m/^---/) if DEBUG;
if ($from{'href'}) {
$patch_line = '--- a/' .
$cgi->a({-href=>$from{'href'}, -class=>"path"},
esc_path($from{'file'}));
}
print "<div class=\"diff from_file\">$patch_line</div>\n";
$patch_line = <$fd>;
#last PATCH unless $patch_line;
chomp $patch_line;
#assert($patch_line =~ m/^+++/) if DEBUG;
if ($to{'href'}) {
$patch_line = '+++ b/' .
$cgi->a({-href=>$to{'href'}, -class=>"path"},
esc_path($to{'file'}));
}
print "<div class=\"diff to_file\">$patch_line</div>\n";
# the patch itself
LINE:
while ($patch_line = <$fd>) {
chomp $patch_line;
next PATCH if ($patch_line =~ m/^diff /);
print format_diff_line($patch_line, \%from, \%to);
}
print format_diff_line($patch_line);
} continue {
print "</div>\n"; # class="patch"
}
print "</div>\n" if $in_header; # extended header
print "</div>\n" if $patch_found; # class="patch"
print "</div>\n"; # class="patchset"
}
@ -2851,8 +2918,8 @@ sub git_tag {
print "<div class=\"page_body\">";
my $comment = $tag{'comment'};
foreach my $line (@$comment) {
chomp($line);
print esc_html($line) . "<br/>\n";
chomp $line;
print esc_html($line, -nbsp=>1) . "<br/>\n";
}
print "</div>\n";
git_footer_html();
@ -2921,7 +2988,7 @@ HTML
}
}
my $data = $_;
chomp($data);
chomp $data;
my $rev = substr($full_rev, 0, 8);
my $author = $meta->{'author'};
my %date = parse_date($meta->{'author-time'},
@ -3392,6 +3459,7 @@ sub git_log {
}
sub git_commit {
$hash ||= $hash_base || "HEAD";
my %co = parse_commit($hash);
if (!%co) {
die_error(undef, "Unknown commit object");
@ -3669,6 +3737,7 @@ sub git_blobdiff_plain {
sub git_commitdiff {
my $format = shift || 'html';
$hash ||= $hash_base || "HEAD";
my %co = parse_commit($hash);
if (!%co) {
die_error(undef, "Unknown commit object");
@ -3731,7 +3800,8 @@ sub git_commitdiff {
$hash_parent, $hash, "--"
or die_error(undef, "Open git-diff-tree failed");
while (chomp(my $line = <$fd>)) {
while (my $line = <$fd>) {
chomp $line;
# empty line ends raw part of diff-tree output
last unless $line;
push @difftree, $line;
@ -4088,26 +4158,125 @@ sub git_shortlog {
}
## ......................................................................
## feeds (RSS, OPML)
## feeds (RSS, Atom; OPML)
sub git_rss {
# http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
sub git_feed {
my $format = shift || 'atom';
my ($have_blame) = gitweb_check_feature('blame');
# Atom: http://www.atomenabled.org/developers/syndication/
# RSS: http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
if ($format ne 'rss' && $format ne 'atom') {
die_error(undef, "Unknown web feed format");
}
# log/feed of current (HEAD) branch, log of given branch, history of file/directory
my $head = $hash || 'HEAD';
open my $fd, "-|", git_cmd(), "rev-list", "--max-count=150",
git_get_head_hash($project), "--"
$head, "--", (defined $file_name ? $file_name : ())
or die_error(undef, "Open git-rev-list failed");
my @revlist = map { chomp; $_ } <$fd>;
close $fd or die_error(undef, "Reading git-rev-list failed");
print $cgi->header(-type => 'text/xml', -charset => 'utf-8');
print <<XML;
<?xml version="1.0" encoding="utf-8"?>
my %latest_commit;
my %latest_date;
my $content_type = "application/$format+xml";
if (defined $cgi->http('HTTP_ACCEPT') &&
$cgi->Accept('text/xml') > $cgi->Accept($content_type)) {
# browser (feed reader) prefers text/xml
$content_type = 'text/xml';
}
if (defined($revlist[0])) {
%latest_commit = parse_commit($revlist[0]);
%latest_date = parse_date($latest_commit{'committer_epoch'});
print $cgi->header(
-type => $content_type,
-charset => 'utf-8',
-last_modified => $latest_date{'rfc2822'});
} else {
print $cgi->header(
-type => $content_type,
-charset => 'utf-8');
}
# Optimization: skip generating the body if client asks only
# for Last-Modified date.
return if ($cgi->request_method() eq 'HEAD');
# header variables
my $title = "$site_name - $project/$action";
my $feed_type = 'log';
if (defined $hash) {
$title .= " - '$hash'";
$feed_type = 'branch log';
if (defined $file_name) {
$title .= " :: $file_name";
$feed_type = 'history';
}
} elsif (defined $file_name) {
$title .= " - $file_name";
$feed_type = 'history';
}
$title .= " $feed_type";
my $descr = git_get_project_description($project);
if (defined $descr) {
$descr = esc_html($descr);
} else {
$descr = "$project " .
($format eq 'rss' ? 'RSS' : 'Atom') .
" feed";
}
my $owner = git_get_project_owner($project);
$owner = esc_html($owner);
#header
my $alt_url;
if (defined $file_name) {
$alt_url = href(-full=>1, action=>"history", hash=>$hash, file_name=>$file_name);
} elsif (defined $hash) {
$alt_url = href(-full=>1, action=>"log", hash=>$hash);
} else {
$alt_url = href(-full=>1, action=>"summary");
}
print qq!<?xml version="1.0" encoding="utf-8"?>\n!;
if ($format eq 'rss') {
print <<XML;
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
<title>$project $my_uri $my_url</title>
<link>${\esc_html("$my_url?p=$project;a=summary")}</link>
<description>$project log</description>
<language>en</language>
XML
print "<title>$title</title>\n" .
"<link>$alt_url</link>\n" .
"<description>$descr</description>\n" .
"<language>en</language>\n";
} elsif ($format eq 'atom') {
print <<XML;
<feed xmlns="http://www.w3.org/2005/Atom">
XML
print "<title>$title</title>\n" .
"<subtitle>$descr</subtitle>\n" .
'<link rel="alternate" type="text/html" href="' .
$alt_url . '" />' . "\n" .
'<link rel="self" type="' . $content_type . '" href="' .
$cgi->self_url() . '" />' . "\n" .
"<id>" . href(-full=>1) . "</id>\n" .
# use project owner for feed author
"<author><name>$owner</name></author>\n";
if (defined $favicon) {
print "<icon>" . esc_url($favicon) . "</icon>\n";
}
if (defined $logo_url) {
# not twice as wide as tall: 72 x 27 pixels
print "<logo>" . esc_url($logo_url) . "</logo>\n";
}
if (! %latest_date) {
# dummy date to keep the feed valid until commits trickle in:
print "<updated>1970-01-01T00:00:00Z</updated>\n";
} else {
print "<updated>$latest_date{'iso-8601'}</updated>\n";
}
}
# contents
for (my $i = 0; $i <= $#revlist; $i++) {
my $commit = $revlist[$i];
my %co = parse_commit($commit);
@ -4116,42 +4285,100 @@ XML
last;
}
my %cd = parse_date($co{'committer_epoch'});
# get list of changed files
open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
$co{'parent'}, $co{'id'}, "--"
$co{'parent'}, $co{'id'}, "--", (defined $file_name ? $file_name : ())
or next;
my @difftree = map { chomp; $_ } <$fd>;
close $fd
or next;
print "<item>\n" .
"<title>" .
sprintf("%d %s %02d:%02d", $cd{'mday'}, $cd{'month'}, $cd{'hour'}, $cd{'minute'}) . " - " . esc_html($co{'title'}) .
"</title>\n" .
"<author>" . esc_html($co{'author'}) . "</author>\n" .
"<pubDate>$cd{'rfc2822'}</pubDate>\n" .
"<guid isPermaLink=\"true\">" . esc_html("$my_url?p=$project;a=commit;h=$commit") . "</guid>\n" .
"<link>" . esc_html("$my_url?p=$project;a=commit;h=$commit") . "</link>\n" .
"<description>" . esc_html($co{'title'}) . "</description>\n" .
"<content:encoded>" .
"<![CDATA[\n";
# print element (entry, item)
my $co_url = href(-full=>1, action=>"commit", hash=>$commit);
if ($format eq 'rss') {
print "<item>\n" .
"<title>" . esc_html($co{'title'}) . "</title>\n" .
"<author>" . esc_html($co{'author'}) . "</author>\n" .
"<pubDate>$cd{'rfc2822'}</pubDate>\n" .
"<guid isPermaLink=\"true\">$co_url</guid>\n" .
"<link>$co_url</link>\n" .
"<description>" . esc_html($co{'title'}) . "</description>\n" .
"<content:encoded>" .
"<![CDATA[\n";
} elsif ($format eq 'atom') {
print "<entry>\n" .
"<title type=\"html\">" . esc_html($co{'title'}) . "</title>\n" .
"<updated>$cd{'iso-8601'}</updated>\n" .
"<author><name>" . esc_html($co{'author_name'}) . "</name></author>\n" .
# use committer for contributor
"<contributor><name>" . esc_html($co{'committer_name'}) . "</name></contributor>\n" .
"<published>$cd{'iso-8601'}</published>\n" .
"<link rel=\"alternate\" type=\"text/html\" href=\"$co_url\" />\n" .
"<id>$co_url</id>\n" .
"<content type=\"xhtml\" xml:base=\"" . esc_url($my_url) . "\">\n" .
"<div xmlns=\"http://www.w3.org/1999/xhtml\">\n";
}
my $comment = $co{'comment'};
print "<pre>\n";
foreach my $line (@$comment) {
$line = to_utf8($line);
print "$line<br/>\n";
$line = esc_html($line);
print "$line\n";
}
print "<br/>\n";
foreach my $line (@difftree) {
if (!($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/)) {
next;
print "</pre><ul>\n";
foreach my $difftree_line (@difftree) {
my %difftree = parse_difftree_raw_line($difftree_line);
next if !$difftree{'from_id'};
my $file = $difftree{'file'} || $difftree{'to_file'};
print "<li>" .
"[" .
$cgi->a({-href => href(-full=>1, action=>"blobdiff",
hash=>$difftree{'to_id'}, hash_parent=>$difftree{'from_id'},
hash_base=>$co{'id'}, hash_parent_base=>$co{'parent'},
file_name=>$file, file_parent=>$difftree{'from_file'}),
-title => "diff"}, 'D');
if ($have_blame) {
print $cgi->a({-href => href(-full=>1, action=>"blame",
file_name=>$file, hash_base=>$commit),
-title => "blame"}, 'B');
}
my $file = esc_path(unquote($7));
$file = to_utf8($file);
print "$file<br/>\n";
# if this is not a feed of a file history
if (!defined $file_name || $file_name ne $file) {
print $cgi->a({-href => href(-full=>1, action=>"history",
file_name=>$file, hash=>$commit),
-title => "history"}, 'H');
}
$file = esc_path($file);
print "] ".
"$file</li>\n";
}
if ($format eq 'rss') {
print "</ul>]]>\n" .
"</content:encoded>\n" .
"</item>\n";
} elsif ($format eq 'atom') {
print "</ul>\n</div>\n" .
"</content>\n" .
"</entry>\n";
}
print "]]>\n" .
"</content:encoded>\n" .
"</item>\n";
}
print "</channel></rss>";
# end of feed
if ($format eq 'rss') {
print "</channel>\n</rss>\n";
} elsif ($format eq 'atom') {
print "</feed>\n";
}
}
sub git_rss {
git_feed('rss');
}
sub git_atom {
git_feed('atom');
}
sub git_opml {

View File

@ -73,6 +73,7 @@ test_expect_success setup '
for i in 1 2; do echo $i; done >>dir/sub &&
git update-index file0 dir/sub &&
git repo-config log.showroot false &&
git commit --amend &&
git show-branch
'

View File

@ -12,9 +12,15 @@
static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>";
#define THEY_HAVE (1U << 0)
#define OUR_REF (1U << 1)
#define WANTED (1U << 2)
/* bits #0..7 in revision.h, #8..10 in commit.c */
#define THEY_HAVE (1u << 11)
#define OUR_REF (1u << 12)
#define WANTED (1u << 13)
#define COMMON_KNOWN (1u << 14)
#define REACHABLE (1u << 15)
static unsigned long oldest_have;
static int multi_ack, nr_our_refs;
static int use_thin_pack, use_ofs_delta;
static struct object_array have_obj;
@ -303,11 +309,12 @@ static void create_pack_file(void)
static int got_sha1(char *hex, unsigned char *sha1)
{
struct object *o;
int we_knew_they_have = 0;
if (get_sha1_hex(hex, sha1))
die("git-upload-pack: expected SHA1 object, got '%s'", hex);
if (!has_sha1_file(sha1))
return 0;
return -1;
o = lookup_object(sha1);
if (!(o && o->parsed))
@ -316,15 +323,84 @@ static int got_sha1(char *hex, unsigned char *sha1)
die("oops (%s)", sha1_to_hex(sha1));
if (o->type == OBJ_COMMIT) {
struct commit_list *parents;
struct commit *commit = (struct commit *)o;
if (o->flags & THEY_HAVE)
return 0;
o->flags |= THEY_HAVE;
for (parents = ((struct commit*)o)->parents;
we_knew_they_have = 1;
else
o->flags |= THEY_HAVE;
if (!oldest_have || (commit->date < oldest_have))
oldest_have = commit->date;
for (parents = commit->parents;
parents;
parents = parents->next)
parents->item->object.flags |= THEY_HAVE;
}
add_object_array(o, NULL, &have_obj);
if (!we_knew_they_have) {
add_object_array(o, NULL, &have_obj);
return 1;
}
return 0;
}
static int reachable(struct commit *want)
{
struct commit_list *work = NULL;
insert_by_date(want, &work);
while (work) {
struct commit_list *list = work->next;
struct commit *commit = work->item;
free(work);
work = list;
if (commit->object.flags & THEY_HAVE) {
want->object.flags |= COMMON_KNOWN;
break;
}
if (!commit->object.parsed)
parse_object(commit->object.sha1);
if (commit->object.flags & REACHABLE)
continue;
commit->object.flags |= REACHABLE;
if (commit->date < oldest_have)
continue;
for (list = commit->parents; list; list = list->next) {
struct commit *parent = list->item;
if (!(parent->object.flags & REACHABLE))
insert_by_date(parent, &work);
}
}
want->object.flags |= REACHABLE;
clear_commit_marks(want, REACHABLE);
free_commit_list(work);
return (want->object.flags & COMMON_KNOWN);
}
static int ok_to_give_up(void)
{
int i;
if (!have_obj.nr)
return 0;
for (i = 0; i < want_obj.nr; i++) {
struct object *want = want_obj.objects[i].item;
if (want->flags & COMMON_KNOWN)
continue;
want = deref_tag(want, "a want line", 0);
if (!want || want->type != OBJ_COMMIT) {
/* no way to tell if this is reachable by
* looking at the ancestry chain alone, so
* leave a note to ourselves not to worry about
* this object anymore.
*/
want_obj.objects[i].item->flags |= COMMON_KNOWN;
continue;
}
if (!reachable((struct commit *)want))
return 0;
}
return 1;
}
@ -349,7 +425,13 @@ static int get_common_commits(void)
}
len = strip(line, len);
if (!strncmp(line, "have ", 5)) {
if (got_sha1(line+5, sha1)) {
switch (got_sha1(line+5, sha1)) {
case -1: /* they have what we do not */
if (multi_ack && ok_to_give_up())
packet_write(1, "ACK %s continue\n",
sha1_to_hex(sha1));
break;
default:
memcpy(hex, sha1_to_hex(sha1), 41);
if (multi_ack) {
const char *msg = "ACK %s continue\n";
@ -358,6 +440,7 @@ static int get_common_commits(void)
}
else if (have_obj.nr == 1)
packet_write(1, "ACK %s\n", hex);
break;
}
continue;
}

View File

@ -118,7 +118,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
xdemitconf_t const *xecfg) {
long s1, s2, e1, e2, lctx;
xdchange_t *xch, *xche;
char funcbuf[40];
char funcbuf[80];
long funclen = 0;
if (xecfg->flags & XDL_EMIT_COMMON)