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:
commit
cadd8a7d4d
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
155
builtin-branch.c
155
builtin-branch.c
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
/*
|
||||
|
@ -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));
|
||||
|
47
connect.c
47
connect.c
@ -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)
|
||||
|
18
git-clone.sh
18
git-clone.sh
@ -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"
|
||||
|
@ -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;
|
||||
|
11
git-fetch.sh
11
git-fetch.sh
@ -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
|
||||
|
||||
|
271
git-svn.perl
271
git-svn.perl
@ -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};
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
'
|
||||
|
101
upload-pack.c
101
upload-pack.c
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user