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
|
browser (and possibly at other places in the future or in other
|
||||||
porcelains). See e.g. gitlink:git-mailinfo[1]. Defaults to 'utf-8'.
|
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::
|
merge.summary::
|
||||||
Whether to include summaries of merged commits in newly created
|
Whether to include summaries of merged commits in newly created
|
||||||
merge commit messages. False by default.
|
merge commit messages. False by default.
|
||||||
|
@ -8,14 +8,16 @@ git-branch - List, create, or delete branches.
|
|||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
[verse]
|
[verse]
|
||||||
'git-branch' [-r]
|
'git-branch' [-r] [-a] [-v] [--abbrev=<length>]
|
||||||
'git-branch' [-l] [-f] <branchname> [<start-point>]
|
'git-branch' [-l] [-f] <branchname> [<start-point>]
|
||||||
'git-branch' (-d | -D) <branchname>...
|
'git-branch' (-d | -D) <branchname>...
|
||||||
|
|
||||||
DESCRIPTION
|
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.
|
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.
|
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>.
|
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.
|
a branch that already exists with the same name.
|
||||||
|
|
||||||
-r::
|
-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>::
|
<branchname>::
|
||||||
The name of the branch to create or delete.
|
The name of the branch to create or delete.
|
||||||
|
@ -11,7 +11,8 @@ SYNOPSIS
|
|||||||
[verse]
|
[verse]
|
||||||
'git-clone' [--template=<template_directory>] [-l [-s]] [-q] [-n] [--bare]
|
'git-clone' [--template=<template_directory>] [-l [-s]] [-q] [-n] [--bare]
|
||||||
[-o <name>] [-u <upload-pack>] [--reference <repository>]
|
[-o <name>] [-u <upload-pack>] [--reference <repository>]
|
||||||
[--use-separate-remote] <repository> [<directory>]
|
[--use-separate-remote | --use-immingled-remote] <repository>
|
||||||
|
[<directory>]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
@ -71,9 +72,13 @@ OPTIONS
|
|||||||
Make a 'bare' GIT repository. That is, instead of
|
Make a 'bare' GIT repository. That is, instead of
|
||||||
creating `<directory>` and placing the administrative
|
creating `<directory>` and placing the administrative
|
||||||
files in `<directory>/.git`, make the `<directory>`
|
files in `<directory>/.git`, make the `<directory>`
|
||||||
itself the `$GIT_DIR`. This implies `-n` option. When
|
itself the `$GIT_DIR`. This obviously implies the `-n`
|
||||||
this option is used, neither the `origin` branch nor the
|
because there is nowhere to check out the working tree.
|
||||||
default `remotes/origin` file is created.
|
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>::
|
--origin <name>::
|
||||||
-o <name>::
|
-o <name>::
|
||||||
@ -97,8 +102,15 @@ OPTIONS
|
|||||||
|
|
||||||
--use-separate-remote::
|
--use-separate-remote::
|
||||||
Save remotes heads under `$GIT_DIR/remotes/origin/` instead
|
Save remotes heads under `$GIT_DIR/remotes/origin/` instead
|
||||||
of `$GIT_DIR/refs/heads/`. Only the master branch is saved
|
of `$GIT_DIR/refs/heads/`. Only the local master branch is
|
||||||
in the latter.
|
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>::
|
<repository>::
|
||||||
The (possibly remote) repository to clone from. It can
|
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) {
|
for ( ; patch; patch = patch->next) {
|
||||||
const char *name;
|
const char *name;
|
||||||
name = patch->new_name ? patch->new_name : patch->old_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))
|
if (line_termination && quote_c_style(name, NULL, NULL, 0))
|
||||||
quote_c_style(name, NULL, stdout, 0);
|
quote_c_style(name, NULL, stdout, 0);
|
||||||
else
|
else
|
||||||
|
153
builtin-branch.c
153
builtin-branch.c
@ -11,7 +11,7 @@
|
|||||||
#include "builtin.h"
|
#include "builtin.h"
|
||||||
|
|
||||||
static const char builtin_branch_usage[] =
|
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;
|
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)
|
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];
|
unsigned char sha1[20];
|
||||||
char *name;
|
char *name;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (!force) {
|
||||||
head_rev = lookup_commit_reference(head_sha1);
|
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++) {
|
for (i = 0; i < argc; i++) {
|
||||||
if (!strcmp(head, argv[i]))
|
if (!strcmp(head, argv[i]))
|
||||||
die("Cannot delete the branch you are currently on.");
|
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]);
|
die("Branch '%s' not found.", argv[i]);
|
||||||
|
|
||||||
rev = lookup_commit_reference(sha1);
|
rev = lookup_commit_reference(sha1);
|
||||||
if (!rev || !head_rev)
|
if (!rev)
|
||||||
die("Couldn't look up commit objects.");
|
die("Couldn't look up commit object for '%s'", name);
|
||||||
|
|
||||||
/* This checks whether the merge bases of branch and
|
/* This checks whether the merge bases of branch and
|
||||||
* HEAD contains branch -- which means that the HEAD
|
* 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;
|
#define REF_UNKNOWN_TYPE 0x00
|
||||||
static char **ref_list;
|
#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,
|
struct ref_item {
|
||||||
void *cb_data)
|
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) {
|
struct ref_list *ref_list = (struct ref_list*)(cb_data);
|
||||||
ref_alloc = alloc_nr(ref_alloc);
|
struct ref_item *newitem;
|
||||||
ref_list = xrealloc(ref_list, ref_alloc * sizeof(char *));
|
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;
|
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;
|
int i;
|
||||||
char c;
|
char c;
|
||||||
|
struct ref_list ref_list;
|
||||||
|
|
||||||
if (remote_only)
|
memset(&ref_list, 0, sizeof(ref_list));
|
||||||
for_each_remote_ref(append_ref, NULL);
|
ref_list.kinds = kinds;
|
||||||
else
|
for_each_ref(append_ref, &ref_list);
|
||||||
for_each_branch_ref(append_ref, NULL);
|
|
||||||
|
|
||||||
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 = ' ';
|
c = ' ';
|
||||||
if (!strcmp(ref_list[i], head))
|
if (ref_list.list[i].kind == REF_LOCAL_BRANCH &&
|
||||||
|
!strcmp(ref_list.list[i].name, head))
|
||||||
c = '*';
|
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,
|
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 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 reflog = 0;
|
||||||
|
int kinds = REF_LOCAL_BRANCH;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
git_config(git_default_config);
|
git_config(git_default_config);
|
||||||
@ -189,13 +278,25 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strcmp(arg, "-r")) {
|
if (!strcmp(arg, "-r")) {
|
||||||
remote_only = 1;
|
kinds = REF_REMOTE_BRANCH;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(arg, "-a")) {
|
||||||
|
kinds = REF_REMOTE_BRANCH | REF_LOCAL_BRANCH;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strcmp(arg, "-l")) {
|
if (!strcmp(arg, "-l")) {
|
||||||
reflog = 1;
|
reflog = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!strncmp(arg, "--abbrev=", 9)) {
|
||||||
|
abbrev = atoi(arg+9);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(arg, "-v")) {
|
||||||
|
verbose = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
usage(builtin_branch_usage);
|
usage(builtin_branch_usage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,7 +310,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
|||||||
if (delete)
|
if (delete)
|
||||||
delete_branches(argc - i, argv + i, force_delete);
|
delete_branches(argc - i, argv + i, force_delete);
|
||||||
else if (i == argc)
|
else if (i == argc)
|
||||||
print_ref_list(remote_only);
|
print_ref_list(kinds, verbose, abbrev);
|
||||||
else if (i == argc - 1)
|
else if (i == argc - 1)
|
||||||
create_branch(argv[i], head, force_create, reflog);
|
create_branch(argv[i], head, force_create, reflog);
|
||||||
else if (i == argc - 2)
|
else if (i == argc - 2)
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
static int default_show_root = 1;
|
||||||
|
|
||||||
/* this is in builtin-diff.c */
|
/* this is in builtin-diff.c */
|
||||||
void add_head(struct rev_info *revs);
|
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->abbrev = DEFAULT_ABBREV;
|
||||||
rev->commit_format = CMIT_FMT_DEFAULT;
|
rev->commit_format = CMIT_FMT_DEFAULT;
|
||||||
rev->verbose_header = 1;
|
rev->verbose_header = 1;
|
||||||
|
rev->show_root_diff = default_show_root;
|
||||||
argc = setup_revisions(argc, argv, rev, "HEAD");
|
argc = setup_revisions(argc, argv, rev, "HEAD");
|
||||||
if (rev->diffopt.pickaxe || rev->diffopt.filter)
|
if (rev->diffopt.pickaxe || rev->diffopt.filter)
|
||||||
rev->always_show_header = 0;
|
rev->always_show_header = 0;
|
||||||
@ -44,11 +47,20 @@ static int cmd_log_walk(struct rev_info *rev)
|
|||||||
return 0;
|
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)
|
int cmd_whatchanged(int argc, const char **argv, const char *prefix)
|
||||||
{
|
{
|
||||||
struct rev_info rev;
|
struct rev_info rev;
|
||||||
|
|
||||||
git_config(git_diff_ui_config);
|
git_config(git_log_config);
|
||||||
init_revisions(&rev, prefix);
|
init_revisions(&rev, prefix);
|
||||||
rev.diff = 1;
|
rev.diff = 1;
|
||||||
rev.diffopt.recursive = 1;
|
rev.diffopt.recursive = 1;
|
||||||
@ -63,7 +75,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
|
|||||||
{
|
{
|
||||||
struct rev_info rev;
|
struct rev_info rev;
|
||||||
|
|
||||||
git_config(git_diff_ui_config);
|
git_config(git_log_config);
|
||||||
init_revisions(&rev, prefix);
|
init_revisions(&rev, prefix);
|
||||||
rev.diff = 1;
|
rev.diff = 1;
|
||||||
rev.diffopt.recursive = 1;
|
rev.diffopt.recursive = 1;
|
||||||
@ -80,7 +92,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)
|
|||||||
{
|
{
|
||||||
struct rev_info rev;
|
struct rev_info rev;
|
||||||
|
|
||||||
git_config(git_diff_ui_config);
|
git_config(git_log_config);
|
||||||
init_revisions(&rev, prefix);
|
init_revisions(&rev, prefix);
|
||||||
rev.always_show_header = 1;
|
rev.always_show_header = 1;
|
||||||
cmd_log_init(argc, argv, prefix, &rev);
|
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")) {
|
if (!strcmp(var, "diff.color")) {
|
||||||
return 0;
|
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.
|
* on an earlier try, but only when reusing delta data.
|
||||||
*/
|
*/
|
||||||
if (!no_reuse_delta && trg_entry->in_pack &&
|
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;
|
return 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -16,8 +16,15 @@ static struct rev_info revs;
|
|||||||
|
|
||||||
static int prune_object(char *path, const char *filename, const unsigned char *sha1)
|
static int prune_object(char *path, const char *filename, const unsigned char *sha1)
|
||||||
{
|
{
|
||||||
|
char buf[20];
|
||||||
|
const char *type;
|
||||||
|
|
||||||
if (show_only) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
unlink(mkpath("%s/%s", path, filename));
|
unlink(mkpath("%s/%s", path, filename));
|
||||||
|
45
connect.c
45
connect.c
@ -174,22 +174,59 @@ static int count_refspec_match(const char *pattern,
|
|||||||
struct ref *refs,
|
struct ref *refs,
|
||||||
struct ref **matched_ref)
|
struct ref **matched_ref)
|
||||||
{
|
{
|
||||||
int match;
|
|
||||||
int patlen = strlen(pattern);
|
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;
|
char *name = refs->name;
|
||||||
int namelen = strlen(name);
|
int namelen = strlen(name);
|
||||||
|
int weak_match;
|
||||||
|
|
||||||
if (namelen < patlen ||
|
if (namelen < patlen ||
|
||||||
memcmp(name + namelen - patlen, pattern, patlen))
|
memcmp(name + namelen - patlen, pattern, patlen))
|
||||||
continue;
|
continue;
|
||||||
if (namelen != patlen && name[namelen - patlen - 1] != '/')
|
if (namelen != patlen && name[namelen - patlen - 1] != '/')
|
||||||
continue;
|
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)
|
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() {
|
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() {
|
get_repo_base() {
|
||||||
@ -48,6 +48,10 @@ Perhaps git-update-server-info needs to be run there?"
|
|||||||
case "$name" in
|
case "$name" in
|
||||||
*^*) continue;;
|
*^*) continue;;
|
||||||
esac
|
esac
|
||||||
|
case "$bare,$name" in
|
||||||
|
yes,* | ,heads/* | ,tags/*) ;;
|
||||||
|
*) continue ;;
|
||||||
|
esac
|
||||||
if test -n "$use_separate_remote" &&
|
if test -n "$use_separate_remote" &&
|
||||||
branch_name=`expr "z$name" : 'zheads/\(.*\)'`
|
branch_name=`expr "z$name" : 'zheads/\(.*\)'`
|
||||||
then
|
then
|
||||||
@ -115,7 +119,7 @@ bare=
|
|||||||
reference=
|
reference=
|
||||||
origin=
|
origin=
|
||||||
origin_override=
|
origin_override=
|
||||||
use_separate_remote=
|
use_separate_remote=t
|
||||||
while
|
while
|
||||||
case "$#,$1" in
|
case "$#,$1" in
|
||||||
0,*) break ;;
|
0,*) break ;;
|
||||||
@ -134,7 +138,10 @@ while
|
|||||||
template="$1" ;;
|
template="$1" ;;
|
||||||
*,-q|*,--quiet) quiet=-q ;;
|
*,-q|*,--quiet) quiet=-q ;;
|
||||||
*,--use-separate-remote)
|
*,--use-separate-remote)
|
||||||
|
# default
|
||||||
use_separate_remote=t ;;
|
use_separate_remote=t ;;
|
||||||
|
*,--use-immingled-remote)
|
||||||
|
use_separate_remote= ;;
|
||||||
1,--reference) usage ;;
|
1,--reference) usage ;;
|
||||||
*,--reference)
|
*,--reference)
|
||||||
shift; reference="$1" ;;
|
shift; reference="$1" ;;
|
||||||
@ -169,18 +176,15 @@ repo="$1"
|
|||||||
test -n "$repo" ||
|
test -n "$repo" ||
|
||||||
die 'you must specify a repository to clone.'
|
die 'you must specify a repository to clone.'
|
||||||
|
|
||||||
# --bare implies --no-checkout
|
# --bare implies --no-checkout and --use-immingled-remote
|
||||||
if test yes = "$bare"
|
if test yes = "$bare"
|
||||||
then
|
then
|
||||||
if test yes = "$origin_override"
|
if test yes = "$origin_override"
|
||||||
then
|
then
|
||||||
die '--bare and --origin $origin options are incompatible.'
|
die '--bare and --origin $origin options are incompatible.'
|
||||||
fi
|
fi
|
||||||
if test t = "$use_separate_remote"
|
|
||||||
then
|
|
||||||
die '--bare and --use-separate-remote options are incompatible.'
|
|
||||||
fi
|
|
||||||
no_checkout=yes
|
no_checkout=yes
|
||||||
|
use_separate_remote=
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test -z "$origin"
|
if test -z "$origin"
|
||||||
|
@ -161,8 +161,22 @@ sub new {
|
|||||||
sub conn {
|
sub conn {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $repo = $self->{'fullrep'};
|
my $repo = $self->{'fullrep'};
|
||||||
if($repo =~ s/^:pserver:(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?//) {
|
if($repo =~ s/^:pserver(?:([^:]*)):(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?//) {
|
||||||
my($user,$pass,$serv,$port) = ($1,$2,$3,$4);
|
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;
|
$user="anonymous" unless defined $user;
|
||||||
my $rr2 = "-";
|
my $rr2 = "-";
|
||||||
unless($port) {
|
unless($port) {
|
||||||
@ -187,13 +201,43 @@ sub conn {
|
|||||||
}
|
}
|
||||||
$pass="A" unless $pass;
|
$pass="A" unless $pass;
|
||||||
|
|
||||||
my $s = IO::Socket::INET->new(PeerHost => $serv, PeerPort => $port);
|
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;
|
die "Socket to $serv: $!\n" unless defined $s;
|
||||||
|
}
|
||||||
|
|
||||||
$s->write("BEGIN AUTH REQUEST\n$repo\n$user\n$pass\nEND AUTH REQUEST\n")
|
$s->write("BEGIN AUTH REQUEST\n$repo\n$user\n$pass\nEND AUTH REQUEST\n")
|
||||||
or die "Write to $serv: $!\n";
|
or die "Write to $serv: $!\n";
|
||||||
$s->flush();
|
$s->flush();
|
||||||
|
|
||||||
my $rep = <$s>;
|
$rep = <$s>;
|
||||||
|
|
||||||
if($rep ne "I LOVE YOU\n") {
|
if($rep ne "I LOVE YOU\n") {
|
||||||
$rep="<unknown>" unless $rep;
|
$rep="<unknown>" unless $rep;
|
||||||
|
11
git-fetch.sh
11
git-fetch.sh
@ -360,7 +360,7 @@ fetch_main () {
|
|||||||
esac
|
esac
|
||||||
|
|
||||||
append_fetch_head "$head" "$remote" \
|
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
|
done
|
||||||
|
|
||||||
@ -414,15 +414,16 @@ fetch_main () {
|
|||||||
done
|
done
|
||||||
local_name=$(expr "z$found" : 'z[^:]*:\(.*\)')
|
local_name=$(expr "z$found" : 'z[^:]*:\(.*\)')
|
||||||
append_fetch_head "$sha1" "$remote" \
|
append_fetch_head "$sha1" "$remote" \
|
||||||
"$remote_name" "$remote_nick" "$local_name" "$not_for_merge"
|
"$remote_name" "$remote_nick" "$local_name" \
|
||||||
done
|
"$not_for_merge" || exit
|
||||||
|
done &&
|
||||||
if [ "$pack_lockfile" ]; then rm -f "$pack_lockfile"; fi
|
if [ "$pack_lockfile" ]; then rm -f "$pack_lockfile"; fi
|
||||||
) || exit ;;
|
) || exit ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch_main "$reflist"
|
fetch_main "$reflist" || exit
|
||||||
|
|
||||||
# automated tag following
|
# automated tag following
|
||||||
case "$no_tags$tags" in
|
case "$no_tags$tags" in
|
||||||
@ -450,7 +451,7 @@ case "$no_tags$tags" in
|
|||||||
case "$taglist" in
|
case "$taglist" in
|
||||||
'') ;;
|
'') ;;
|
||||||
?*)
|
?*)
|
||||||
fetch_main "$taglist" ;;
|
fetch_main "$taglist" || exit ;;
|
||||||
esac
|
esac
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
255
git-svn.perl
255
git-svn.perl
@ -21,6 +21,7 @@ $ENV{TZ} = 'UTC';
|
|||||||
$ENV{LC_ALL} = 'C';
|
$ENV{LC_ALL} = 'C';
|
||||||
$| = 1; # unbuffer STDOUT
|
$| = 1; # unbuffer STDOUT
|
||||||
|
|
||||||
|
sub fatal (@) { print STDERR $@; exit 1 }
|
||||||
# If SVN:: library support is added, please make the dependencies
|
# If SVN:: library support is added, please make the dependencies
|
||||||
# optional and preserve the capability to use the command-line client.
|
# optional and preserve the capability to use the command-line client.
|
||||||
# use eval { require SVN::... } to make it lazy load
|
# use eval { require SVN::... } to make it lazy load
|
||||||
@ -39,7 +40,7 @@ memoize('revisions_eq');
|
|||||||
memoize('cmt_metadata');
|
memoize('cmt_metadata');
|
||||||
memoize('get_commit_time');
|
memoize('get_commit_time');
|
||||||
|
|
||||||
my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib);
|
my ($SVN, $_use_lib);
|
||||||
|
|
||||||
sub nag_lib {
|
sub nag_lib {
|
||||||
print STDERR <<EOF;
|
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,
|
$_template, $_shared, $_no_default_regex, $_no_graft_copy,
|
||||||
$_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,
|
$_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,
|
||||||
$_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m,
|
$_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 (@_branch_from, %tree_map, %users, %rusers, %equiv);
|
||||||
my ($_svn_co_url_revs, $_svn_pg_peg_revs);
|
my ($_svn_co_url_revs, $_svn_pg_peg_revs);
|
||||||
my @repo_path_split_cache;
|
my @repo_path_split_cache;
|
||||||
@ -79,6 +81,9 @@ my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
|
|||||||
'repack:i' => \$_repack,
|
'repack:i' => \$_repack,
|
||||||
'no-metadata' => \$_no_metadata,
|
'no-metadata' => \$_no_metadata,
|
||||||
'quiet|q' => \$_q,
|
'quiet|q' => \$_q,
|
||||||
|
'username=s' => \$_username,
|
||||||
|
'config-dir=s' => \$_config_dir,
|
||||||
|
'no-auth-cache' => \$_no_auth_cache,
|
||||||
'ignore-nodate' => \$_ignore_nodate,
|
'ignore-nodate' => \$_ignore_nodate,
|
||||||
'repack-flags|repack-args|repack-opts=s' => \$_repack_flags);
|
'repack-flags|repack-args|repack-opts=s' => \$_repack_flags);
|
||||||
|
|
||||||
@ -377,10 +382,7 @@ sub fetch_cmd {
|
|||||||
sub fetch_lib {
|
sub fetch_lib {
|
||||||
my (@parents) = @_;
|
my (@parents) = @_;
|
||||||
$SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
|
$SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
|
||||||
my $repo;
|
$SVN ||= libsvn_connect($SVN_URL);
|
||||||
($repo, $SVN_PATH) = repo_path_split($SVN_URL);
|
|
||||||
$SVN_LOG ||= libsvn_connect($repo);
|
|
||||||
$SVN ||= libsvn_connect($repo);
|
|
||||||
my ($last_rev, $last_commit) = svn_grab_base_rev();
|
my ($last_rev, $last_commit) = svn_grab_base_rev();
|
||||||
my ($base, $head) = libsvn_parse_revision($last_rev);
|
my ($base, $head) = libsvn_parse_revision($last_rev);
|
||||||
if ($base > $head) {
|
if ($base > $head) {
|
||||||
@ -422,7 +424,7 @@ sub fetch_lib {
|
|||||||
# performance sucks with it enabled, so it's much
|
# performance sucks with it enabled, so it's much
|
||||||
# faster to fetch revision ranges instead of relying
|
# faster to fetch revision ranges instead of relying
|
||||||
# on the limiter.
|
# on the limiter.
|
||||||
libsvn_get_log($SVN_LOG, '/'.$SVN_PATH,
|
libsvn_get_log(libsvn_dup_ra($SVN), [''],
|
||||||
$min, $max, 0, 1, 1,
|
$min, $max, 0, 1, 1,
|
||||||
sub {
|
sub {
|
||||||
my $log_msg;
|
my $log_msg;
|
||||||
@ -524,7 +526,6 @@ sub commit_lib {
|
|||||||
my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$";
|
my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$";
|
||||||
|
|
||||||
my $repo;
|
my $repo;
|
||||||
($repo, $SVN_PATH) = repo_path_split($SVN_URL);
|
|
||||||
set_svn_commit_env();
|
set_svn_commit_env();
|
||||||
foreach my $c (@revs) {
|
foreach my $c (@revs) {
|
||||||
my $log_msg = get_commit_message($c, $commit_msg);
|
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)
|
# can't track down... (it's probably in the SVN code)
|
||||||
defined(my $pid = open my $fh, '-|') or croak $!;
|
defined(my $pid = open my $fh, '-|') or croak $!;
|
||||||
if (!$pid) {
|
if (!$pid) {
|
||||||
$SVN_LOG = libsvn_connect($repo);
|
|
||||||
$SVN = libsvn_connect($repo);
|
|
||||||
my $ed = SVN::Git::Editor->new(
|
my $ed = SVN::Git::Editor->new(
|
||||||
{ r => $r_last,
|
{ r => $r_last,
|
||||||
ra => $SVN_LOG,
|
ra => libsvn_dup_ra($SVN),
|
||||||
c => $c,
|
c => $c,
|
||||||
svn_path => $SVN_PATH
|
svn_path => $SVN->{svn_path},
|
||||||
},
|
},
|
||||||
$SVN->get_commit_editor(
|
$SVN->get_commit_editor(
|
||||||
$log_msg->{msg},
|
$log_msg->{msg},
|
||||||
@ -571,7 +570,7 @@ sub commit_lib {
|
|||||||
$no = 1;
|
$no = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close $fh or croak $?;
|
close $fh or exit 1;
|
||||||
if (! defined $r_new && ! defined $cmt_new) {
|
if (! defined $r_new && ! defined $cmt_new) {
|
||||||
unless ($no) {
|
unless ($no) {
|
||||||
die "Failed to parse revision information\n";
|
die "Failed to parse revision information\n";
|
||||||
@ -657,10 +656,9 @@ sub show_ignore_cmd {
|
|||||||
|
|
||||||
sub show_ignore_lib {
|
sub show_ignore_lib {
|
||||||
my $repo;
|
my $repo;
|
||||||
($repo, $SVN_PATH) = repo_path_split($SVN_URL);
|
$SVN ||= libsvn_connect($SVN_URL);
|
||||||
$SVN ||= libsvn_connect($repo);
|
|
||||||
my $r = defined $_revision ? $_revision : $SVN->get_latest_revnum;
|
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 {
|
sub graft_branches {
|
||||||
@ -786,7 +784,7 @@ sub show_log {
|
|||||||
} elsif (/^:\d{6} \d{6} $sha1_short/o) {
|
} elsif (/^:\d{6} \d{6} $sha1_short/o) {
|
||||||
push @{$c->{raw}}, $_;
|
push @{$c->{raw}}, $_;
|
||||||
} elsif (/^[ACRMDT]\t/) {
|
} 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)...
|
# remote access at the moment (repo_path_split)...
|
||||||
s#^([ACRMDT])\t# $1 #;
|
s#^([ACRMDT])\t# $1 #;
|
||||||
push @{$c->{changed}}, $_;
|
push @{$c->{changed}}, $_;
|
||||||
@ -852,10 +850,7 @@ sub commit_diff {
|
|||||||
$_message ||= get_commit_message($tb,
|
$_message ||= get_commit_message($tb,
|
||||||
"$GIT_DIR/.svn-commit.tmp.$$")->{msg};
|
"$GIT_DIR/.svn-commit.tmp.$$")->{msg};
|
||||||
}
|
}
|
||||||
my $repo;
|
$SVN ||= libsvn_connect($SVN_URL);
|
||||||
($repo, $SVN_PATH) = repo_path_split($SVN_URL);
|
|
||||||
$SVN_LOG ||= libsvn_connect($repo);
|
|
||||||
$SVN ||= libsvn_connect($repo);
|
|
||||||
if ($r eq 'HEAD') {
|
if ($r eq 'HEAD') {
|
||||||
$r = $SVN->get_latest_revnum;
|
$r = $SVN->get_latest_revnum;
|
||||||
} elsif ($r !~ /^\d+$/) {
|
} elsif ($r !~ /^\d+$/) {
|
||||||
@ -864,8 +859,9 @@ sub commit_diff {
|
|||||||
my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : ();
|
my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : ();
|
||||||
my $rev_committed;
|
my $rev_committed;
|
||||||
my $ed = SVN::Git::Editor->new({ r => $r,
|
my $ed = SVN::Git::Editor->new({ r => $r,
|
||||||
ra => $SVN_LOG, c => $tb,
|
ra => libsvn_dup_ra($SVN),
|
||||||
svn_path => $SVN_PATH
|
c => $tb,
|
||||||
|
svn_path => $SVN->{svn_path}
|
||||||
},
|
},
|
||||||
$SVN->get_commit_editor($_message,
|
$SVN->get_commit_editor($_message,
|
||||||
sub {
|
sub {
|
||||||
@ -873,6 +869,7 @@ sub commit_diff {
|
|||||||
print "Committed $_[0]\n";
|
print "Committed $_[0]\n";
|
||||||
}, @lock)
|
}, @lock)
|
||||||
);
|
);
|
||||||
|
eval {
|
||||||
my $mods = libsvn_checkout_tree($ta, $tb, $ed);
|
my $mods = libsvn_checkout_tree($ta, $tb, $ed);
|
||||||
if (@$mods == 0) {
|
if (@$mods == 0) {
|
||||||
print "No changes\n$ta == $tb\n";
|
print "No changes\n$ta == $tb\n";
|
||||||
@ -880,6 +877,8 @@ sub commit_diff {
|
|||||||
} else {
|
} else {
|
||||||
$ed->close_edit;
|
$ed->close_edit;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
fatal "$@\n" if $@;
|
||||||
$_message = $_file = undef;
|
$_message = $_file = undef;
|
||||||
return $rev_committed;
|
return $rev_committed;
|
||||||
}
|
}
|
||||||
@ -1143,8 +1142,7 @@ sub graft_file_copy_lib {
|
|||||||
my $tree_paths = $l_map->{$u};
|
my $tree_paths = $l_map->{$u};
|
||||||
my $pfx = common_prefix([keys %$tree_paths]);
|
my $pfx = common_prefix([keys %$tree_paths]);
|
||||||
my ($repo, $path) = repo_path_split($u.$pfx);
|
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 ($base, $head) = libsvn_parse_revision();
|
||||||
my $inc = 1000;
|
my $inc = 1000;
|
||||||
@ -1153,7 +1151,8 @@ sub graft_file_copy_lib {
|
|||||||
$SVN::Error::handler = \&libsvn_skip_unknown_revs;
|
$SVN::Error::handler = \&libsvn_skip_unknown_revs;
|
||||||
while (1) {
|
while (1) {
|
||||||
my $pool = SVN::Pool->new;
|
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 {
|
sub {
|
||||||
libsvn_graft_file_copies($grafts, $tree_paths,
|
libsvn_graft_file_copies($grafts, $tree_paths,
|
||||||
$path, @_);
|
$path, @_);
|
||||||
@ -1263,13 +1262,9 @@ sub repo_path_split {
|
|||||||
return ($u, $full_url);
|
return ($u, $full_url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($_use_lib) {
|
if ($_use_lib) {
|
||||||
my $tmp = libsvn_connect($full_url);
|
my $tmp = libsvn_connect($full_url);
|
||||||
my $url = $tmp->get_repos_root;
|
return ($tmp->{repos_root}, $tmp->{svn_path});
|
||||||
$full_url =~ s#^\Q$url\E/*##;
|
|
||||||
push @repo_path_split_cache, qr/^(\Q$url\E)/;
|
|
||||||
return ($url, $full_url);
|
|
||||||
} else {
|
} else {
|
||||||
my ($url, $path) = ($full_url =~ m!^([a-z\+]+://[^/]*)(.*)$!i);
|
my ($url, $path) = ($full_url =~ m!^([a-z\+]+://[^/]*)(.*)$!i);
|
||||||
$path =~ s#^/+##;
|
$path =~ s#^/+##;
|
||||||
@ -2683,26 +2678,169 @@ sub libsvn_load {
|
|||||||
my $kill_stupid_warnings = $SVN::Node::none.$SVN::Node::file.
|
my $kill_stupid_warnings = $SVN::Node::none.$SVN::Node::file.
|
||||||
$SVN::Node::dir.$SVN::Node::unknown.
|
$SVN::Node::dir.$SVN::Node::unknown.
|
||||||
$SVN::Node::none.$SVN::Node::file.
|
$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;
|
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 {
|
sub libsvn_connect {
|
||||||
my ($url) = @_;
|
my ($url) = @_;
|
||||||
my $auth = SVN::Core::auth_open([SVN::Client::get_simple_provider(),
|
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_ssl_server_trust_file_provider(),
|
||||||
SVN::Client::get_username_provider()]);
|
SVN::Client::get_simple_prompt_provider(
|
||||||
my $s = eval { SVN::Ra->new(url => $url, auth => $auth) };
|
\&_simple_prompt, 2),
|
||||||
return $s;
|
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 {
|
sub libsvn_get_file {
|
||||||
my ($gui, $f, $rev, $chg) = @_;
|
my ($gui, $f, $rev, $chg) = @_;
|
||||||
my $p = $f;
|
$f =~ s#^/##;
|
||||||
if (length $SVN_PATH > 0) {
|
|
||||||
return unless ($p =~ s#^\Q$SVN_PATH\E/##);
|
|
||||||
}
|
|
||||||
print "\t$chg\t$f\n" unless $_q;
|
print "\t$chg\t$f\n" unless $_q;
|
||||||
|
|
||||||
my ($hash, $pid, $in, $out);
|
my ($hash, $pid, $in, $out);
|
||||||
@ -2739,7 +2877,7 @@ sub libsvn_get_file {
|
|||||||
waitpid $pid, 0;
|
waitpid $pid, 0;
|
||||||
$hash =~ /^$sha1$/o or die "not a sha1: $hash\n";
|
$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 {
|
sub libsvn_log_entry {
|
||||||
@ -2757,7 +2895,6 @@ sub libsvn_log_entry {
|
|||||||
|
|
||||||
sub process_rm {
|
sub process_rm {
|
||||||
my ($gui, $last_commit, $f) = @_;
|
my ($gui, $last_commit, $f) = @_;
|
||||||
$f =~ s#^\Q$SVN_PATH\E/?## or return;
|
|
||||||
# remove entire directories.
|
# remove entire directories.
|
||||||
if (safe_qx('git-ls-tree',$last_commit,'--',$f) =~ /^040000 tree/) {
|
if (safe_qx('git-ls-tree',$last_commit,'--',$f) =~ /^040000 tree/) {
|
||||||
defined(my $pid = open my $ls, '-|') or croak $!;
|
defined(my $pid = open my $ls, '-|') or croak $!;
|
||||||
@ -2779,9 +2916,11 @@ sub libsvn_fetch {
|
|||||||
my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
|
my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
|
||||||
open my $gui, '| git-update-index -z --index-info' or croak $!;
|
open my $gui, '| git-update-index -z --index-info' or croak $!;
|
||||||
my @amr;
|
my @amr;
|
||||||
|
my $p = $SVN->{svn_path};
|
||||||
foreach my $f (keys %$paths) {
|
foreach my $f (keys %$paths) {
|
||||||
my $m = $paths->{$f}->action();
|
my $m = $paths->{$f}->action();
|
||||||
$f =~ s#^/+##;
|
$f =~ s#^/\Q$p\E/##;
|
||||||
|
next if $f =~ m#^/#;
|
||||||
if ($m =~ /^[DR]$/) {
|
if ($m =~ /^[DR]$/) {
|
||||||
print "\t$m\t$f\n" unless $_q;
|
print "\t$m\t$f\n" unless $_q;
|
||||||
process_rm($gui, $last_commit, $f);
|
process_rm($gui, $last_commit, $f);
|
||||||
@ -2871,9 +3010,9 @@ sub libsvn_parse_revision {
|
|||||||
|
|
||||||
sub libsvn_traverse {
|
sub libsvn_traverse {
|
||||||
my ($gui, $pfx, $path, $rev, $files) = @_;
|
my ($gui, $pfx, $path, $rev, $files) = @_;
|
||||||
my $cwd = "$pfx/$path";
|
my $cwd = length $pfx ? "$pfx/$path" : $path;
|
||||||
my $pool = SVN::Pool->new;
|
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);
|
my ($dirent, $r, $props) = $SVN->get_dir($cwd, $rev, $pool);
|
||||||
foreach my $d (keys %$dirent) {
|
foreach my $d (keys %$dirent) {
|
||||||
my $t = $dirent->{$d}->kind;
|
my $t = $dirent->{$d}->kind;
|
||||||
@ -2897,7 +3036,7 @@ sub libsvn_traverse_ignore {
|
|||||||
my $pool = SVN::Pool->new;
|
my $pool = SVN::Pool->new;
|
||||||
my ($dirent, undef, $props) = $SVN->get_dir($path, $r, $pool);
|
my ($dirent, undef, $props) = $SVN->get_dir($path, $r, $pool);
|
||||||
my $p = $path;
|
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";
|
print $fh length $p ? "\n# $p\n" : "\n# /\n";
|
||||||
if (my $s = $props->{'svn:ignore'}) {
|
if (my $s = $props->{'svn:ignore'}) {
|
||||||
$s =~ s/[\r\n]+/\n/g;
|
$s =~ s/[\r\n]+/\n/g;
|
||||||
@ -2924,7 +3063,7 @@ sub revisions_eq {
|
|||||||
if ($_use_lib) {
|
if ($_use_lib) {
|
||||||
# should be OK to use Pool here (r1 - r0) should be small
|
# should be OK to use Pool here (r1 - r0) should be small
|
||||||
my $pool = SVN::Pool->new;
|
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);
|
0, 1, 1, sub {$nr++}, $pool);
|
||||||
$pool->clear;
|
$pool->clear;
|
||||||
} else {
|
} else {
|
||||||
@ -2939,7 +3078,7 @@ sub revisions_eq {
|
|||||||
|
|
||||||
sub libsvn_find_parent_branch {
|
sub libsvn_find_parent_branch {
|
||||||
my ($paths, $rev, $author, $date, $msg) = @_;
|
my ($paths, $rev, $author, $date, $msg) = @_;
|
||||||
my $svn_path = '/'.$SVN_PATH;
|
my $svn_path = '/'.$SVN->{svn_path};
|
||||||
|
|
||||||
# look for a parent from another branch:
|
# look for a parent from another branch:
|
||||||
my $i = $paths->{$svn_path} or return;
|
my $i = $paths->{$svn_path} or return;
|
||||||
@ -2950,7 +3089,7 @@ sub libsvn_find_parent_branch {
|
|||||||
$branch_from =~ s#^/##;
|
$branch_from =~ s#^/##;
|
||||||
my $l_map = {};
|
my $l_map = {};
|
||||||
read_url_paths_all($l_map, '', "$GIT_DIR/svn");
|
read_url_paths_all($l_map, '', "$GIT_DIR/svn");
|
||||||
my $url = $SVN->{url};
|
my $url = $SVN->{repos_root};
|
||||||
defined $l_map->{$url} or return;
|
defined $l_map->{$url} or return;
|
||||||
my $id = $l_map->{$url}->{$branch_from};
|
my $id = $l_map->{$url}->{$branch_from};
|
||||||
if (!defined $id && $_follow_parent) {
|
if (!defined $id && $_follow_parent) {
|
||||||
@ -2972,7 +3111,7 @@ sub libsvn_find_parent_branch {
|
|||||||
$GIT_SVN = $ENV{GIT_SVN_ID} = $id;
|
$GIT_SVN = $ENV{GIT_SVN_ID} = $id;
|
||||||
init_vars();
|
init_vars();
|
||||||
$SVN_URL = "$url/$branch_from";
|
$SVN_URL = "$url/$branch_from";
|
||||||
$SVN_LOG = $SVN = undef;
|
$SVN = undef;
|
||||||
setup_git_svn();
|
setup_git_svn();
|
||||||
# we can't assume SVN_URL exists at r+1:
|
# we can't assume SVN_URL exists at r+1:
|
||||||
$_revision = "0:$r";
|
$_revision = "0:$r";
|
||||||
@ -3009,7 +3148,7 @@ sub libsvn_new_tree {
|
|||||||
}
|
}
|
||||||
my ($paths, $rev, $author, $date, $msg) = @_;
|
my ($paths, $rev, $author, $date, $msg) = @_;
|
||||||
open my $gui, '| git-update-index -z --index-info' or croak $!;
|
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 $?;
|
close $gui or croak $?;
|
||||||
return libsvn_log_entry($rev, $author, $date, $msg);
|
return libsvn_log_entry($rev, $author, $date, $msg);
|
||||||
}
|
}
|
||||||
@ -3094,11 +3233,10 @@ sub libsvn_commit_cb {
|
|||||||
|
|
||||||
sub libsvn_ls_fullurl {
|
sub libsvn_ls_fullurl {
|
||||||
my $fullurl = shift;
|
my $fullurl = shift;
|
||||||
my ($repo, $path) = repo_path_split($fullurl);
|
$SVN ||= libsvn_connect($fullurl);
|
||||||
$SVN ||= libsvn_connect($repo);
|
|
||||||
my @ret;
|
my @ret;
|
||||||
my $pool = SVN::Pool->new;
|
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);
|
$SVN->get_latest_revnum, $pool);
|
||||||
foreach my $d (keys %$dirent) {
|
foreach my $d (keys %$dirent) {
|
||||||
if ($dirent->{$d}->kind == $SVN::Node::dir) {
|
if ($dirent->{$d}->kind == $SVN::Node::dir) {
|
||||||
@ -3120,8 +3258,9 @@ sub libsvn_skip_unknown_revs {
|
|||||||
# Wonderfully consistent library, eh?
|
# Wonderfully consistent library, eh?
|
||||||
# 160013 - svn:// and file://
|
# 160013 - svn:// and file://
|
||||||
# 175002 - http(s)://
|
# 175002 - http(s)://
|
||||||
|
# 175007 - http(s):// (this repo required authorization, too...)
|
||||||
# More codes may be discovered later...
|
# More codes may be discovered later...
|
||||||
if ($errno == 175002 || $errno == 160013) {
|
if ($errno == 175007 || $errno == 175002 || $errno == 160013) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
croak "Error from SVN, ($errno): ", $err->expanded_message,"\n";
|
croak "Error from SVN, ($errno): ", $err->expanded_message,"\n";
|
||||||
@ -3209,8 +3348,7 @@ sub split_path {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub repo_path {
|
sub repo_path {
|
||||||
(defined $_[1] && length $_[1]) ? "$_[0]->{svn_path}/$_[1]"
|
(defined $_[1] && length $_[1]) ? $_[1] : ''
|
||||||
: $_[0]->{svn_path}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub url_path {
|
sub url_path {
|
||||||
@ -3242,10 +3380,9 @@ sub rmdirs {
|
|||||||
exec qw/git-ls-tree --name-only -r -z/, $self->{c} or croak $!;
|
exec qw/git-ls-tree --name-only -r -z/, $self->{c} or croak $!;
|
||||||
}
|
}
|
||||||
local $/ = "\0";
|
local $/ = "\0";
|
||||||
my @svn_path = split m#/#, $self->{svn_path};
|
|
||||||
while (<$fh>) {
|
while (<$fh>) {
|
||||||
chomp;
|
chomp;
|
||||||
my @dn = (@svn_path, (split m#/#, $_));
|
my @dn = split m#/#, $_;
|
||||||
while (pop @dn) {
|
while (pop @dn) {
|
||||||
delete $rm->{join '/', @dn};
|
delete $rm->{join '/', @dn};
|
||||||
}
|
}
|
||||||
|
@ -334,11 +334,13 @@ div.diff.extended_header {
|
|||||||
padding: 2px 0px 2px 0px;
|
padding: 2px 0px 2px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.diff a.list,
|
||||||
div.diff a.path,
|
div.diff a.path,
|
||||||
div.diff a.hash {
|
div.diff a.hash {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.diff a.list:hover,
|
||||||
div.diff a.path:hover,
|
div.diff a.path:hover,
|
||||||
div.diff a.hash:hover {
|
div.diff a.hash:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
@ -362,14 +364,25 @@ div.diff.rem {
|
|||||||
color: #cc0000;
|
color: #cc0000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.diff.chunk_header a,
|
||||||
div.diff.chunk_header {
|
div.diff.chunk_header {
|
||||||
color: #990099;
|
color: #990099;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.diff.chunk_header {
|
||||||
border: dotted #ffe0ff;
|
border: dotted #ffe0ff;
|
||||||
border-width: 1px 0px 0px 0px;
|
border-width: 1px 0px 0px 0px;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.diff.chunk_header span.chunk_info {
|
||||||
|
background-color: #ffeeff;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.diff.chunk_header span.section {
|
||||||
|
color: #aa22aa;
|
||||||
|
}
|
||||||
|
|
||||||
div.diff.incomplete {
|
div.diff.incomplete {
|
||||||
color: #cccccc;
|
color: #cccccc;
|
||||||
}
|
}
|
||||||
|
@ -425,6 +425,7 @@ my %actions = (
|
|||||||
"history" => \&git_history,
|
"history" => \&git_history,
|
||||||
"log" => \&git_log,
|
"log" => \&git_log,
|
||||||
"rss" => \&git_rss,
|
"rss" => \&git_rss,
|
||||||
|
"atom" => \&git_atom,
|
||||||
"search" => \&git_search,
|
"search" => \&git_search,
|
||||||
"search_help" => \&git_search_help,
|
"search_help" => \&git_search_help,
|
||||||
"shortlog" => \&git_shortlog,
|
"shortlog" => \&git_shortlog,
|
||||||
@ -459,7 +460,8 @@ exit;
|
|||||||
|
|
||||||
sub href(%) {
|
sub href(%) {
|
||||||
my %params = @_;
|
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,
|
# XXX: Warning: If you touch this, check the search form for updating,
|
||||||
# too.
|
# 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 {
|
sub format_diff_line {
|
||||||
my $line = shift;
|
my $line = shift;
|
||||||
|
my ($from, $to) = @_;
|
||||||
my $char = substr($line, 0, 1);
|
my $char = substr($line, 0, 1);
|
||||||
my $diff_class = "";
|
my $diff_class = "";
|
||||||
|
|
||||||
@ -891,6 +895,25 @@ sub format_diff_line {
|
|||||||
$diff_class = " incomplete";
|
$diff_class = " incomplete";
|
||||||
}
|
}
|
||||||
$line = untabify($line);
|
$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";
|
return "<div class=\"diff$diff_class\">" . esc_html($line, -nbsp=>1) . "</div>\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1180,6 +1203,8 @@ sub parse_date {
|
|||||||
$days[$wday], $mday, $months[$mon], 1900+$year, $hour ,$min, $sec;
|
$days[$wday], $mday, $months[$mon], 1900+$year, $hour ,$min, $sec;
|
||||||
$date{'mday-time'} = sprintf "%d %s %02d:%02d",
|
$date{'mday-time'} = sprintf "%d %s %02d:%02d",
|
||||||
$mday, $months[$mon], $hour ,$min;
|
$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])$/;
|
$tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/;
|
||||||
my $local = $epoch + ((int $1 + ($2/60)) * 3600);
|
my $local = $epoch + ((int $1 + ($2/60)) * 3600);
|
||||||
@ -1650,14 +1675,17 @@ EOF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (defined $project) {
|
if (defined $project) {
|
||||||
printf('<link rel="alternate" title="%s log" '.
|
printf('<link rel="alternate" title="%s log RSS feed" '.
|
||||||
'href="%s" type="application/rss+xml" />'."\n",
|
'href="%s" type="application/rss+xml" />'."\n",
|
||||||
esc_param($project), href(action=>"rss"));
|
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 {
|
} else {
|
||||||
printf('<link rel="alternate" title="%s projects list" '.
|
printf('<link rel="alternate" title="%s projects list" '.
|
||||||
'href="%s" type="text/plain; charset=utf-8"/>'."\n",
|
'href="%s" type="text/plain; charset=utf-8"/>'."\n",
|
||||||
$site_name, href(project=>undef, action=>"project_index"));
|
$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",
|
'href="%s" type="text/x-opml"/>'."\n",
|
||||||
$site_name, href(project=>undef, action=>"opml"));
|
$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 "<div class=\"page_footer_text\">" . esc_html($descr) . "</div>\n";
|
||||||
}
|
}
|
||||||
print $cgi->a({-href => href(action=>"rss"),
|
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 {
|
} else {
|
||||||
print $cgi->a({-href => href(project=>undef, action=>"opml"),
|
print $cgi->a({-href => href(project=>undef, action=>"opml"),
|
||||||
-class => "rss_logo"}, "OPML") . " ";
|
-class => "rss_logo"}, "OPML") . " ";
|
||||||
@ -2062,7 +2092,11 @@ sub git_difftree_body {
|
|||||||
# link to patch
|
# link to patch
|
||||||
$patchno++;
|
$patchno++;
|
||||||
print $cgi->a({-href => "#patch$patchno"}, "patch");
|
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";
|
print "</td>\n";
|
||||||
|
|
||||||
} elsif ($diff{'status'} eq "D") { # deleted
|
} elsif ($diff{'status'} eq "D") { # deleted
|
||||||
@ -2084,9 +2118,7 @@ sub git_difftree_body {
|
|||||||
hash_base=>$parent, file_name=>$diff{'file'})},
|
hash_base=>$parent, file_name=>$diff{'file'})},
|
||||||
"blob") . " | ";
|
"blob") . " | ";
|
||||||
if ($have_blame) {
|
if ($have_blame) {
|
||||||
print $cgi->a({-href =>
|
print $cgi->a({-href => href(action=>"blame", hash_base=>$parent,
|
||||||
href(action=>"blame",
|
|
||||||
hash_base=>$parent,
|
|
||||||
file_name=>$diff{'file'})},
|
file_name=>$diff{'file'})},
|
||||||
"blame") . " | ";
|
"blame") . " | ";
|
||||||
}
|
}
|
||||||
@ -2136,8 +2168,7 @@ sub git_difftree_body {
|
|||||||
hash_base=>$hash, file_name=>$diff{'file'})},
|
hash_base=>$hash, file_name=>$diff{'file'})},
|
||||||
"blob") . " | ";
|
"blob") . " | ";
|
||||||
if ($have_blame) {
|
if ($have_blame) {
|
||||||
print $cgi->a({-href => href(action=>"blame",
|
print $cgi->a({-href => href(action=>"blame", hash_base=>$hash,
|
||||||
hash_base=>$hash,
|
|
||||||
file_name=>$diff{'file'})},
|
file_name=>$diff{'file'})},
|
||||||
"blame") . " | ";
|
"blame") . " | ";
|
||||||
}
|
}
|
||||||
@ -2178,17 +2209,16 @@ sub git_difftree_body {
|
|||||||
"diff") .
|
"diff") .
|
||||||
" | ";
|
" | ";
|
||||||
}
|
}
|
||||||
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
|
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
|
||||||
hash_base=>$parent, file_name=>$diff{'from_file'})},
|
hash_base=>$parent, file_name=>$diff{'to_file'})},
|
||||||
"blob") . " | ";
|
"blob") . " | ";
|
||||||
if ($have_blame) {
|
if ($have_blame) {
|
||||||
print $cgi->a({-href => href(action=>"blame",
|
print $cgi->a({-href => href(action=>"blame", hash_base=>$hash,
|
||||||
hash_base=>$hash,
|
|
||||||
file_name=>$diff{'to_file'})},
|
file_name=>$diff{'to_file'})},
|
||||||
"blame") . " | ";
|
"blame") . " | ";
|
||||||
}
|
}
|
||||||
print $cgi->a({-href => href(action=>"history", hash_base=>$parent,
|
print $cgi->a({-href => href(action=>"history", hash_base=>$hash,
|
||||||
file_name=>$diff{'from_file'})},
|
file_name=>$diff{'to_file'})},
|
||||||
"history");
|
"history");
|
||||||
print "</td>\n";
|
print "</td>\n";
|
||||||
|
|
||||||
@ -2202,31 +2232,56 @@ sub git_patchset_body {
|
|||||||
my ($fd, $difftree, $hash, $hash_parent) = @_;
|
my ($fd, $difftree, $hash, $hash_parent) = @_;
|
||||||
|
|
||||||
my $patch_idx = 0;
|
my $patch_idx = 0;
|
||||||
my $in_header = 0;
|
my $patch_line;
|
||||||
my $patch_found = 0;
|
|
||||||
my $diffinfo;
|
my $diffinfo;
|
||||||
my (%from, %to);
|
my (%from, %to);
|
||||||
|
my ($from_id, $to_id);
|
||||||
|
|
||||||
print "<div class=\"patchset\">\n";
|
print "<div class=\"patchset\">\n";
|
||||||
|
|
||||||
LINE:
|
# skip to first patch
|
||||||
while (my $patch_line = <$fd>) {
|
while ($patch_line = <$fd>) {
|
||||||
chomp $patch_line;
|
chomp $patch_line;
|
||||||
|
|
||||||
if ($patch_line =~ m/^diff /) { # "git diff" header
|
last if ($patch_line =~ m/^diff /);
|
||||||
# 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"
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
} else {
|
||||||
# first patch in patchset
|
# advance raw git-diff output if needed
|
||||||
$patch_found = 1;
|
$patch_idx++ if defined $diffinfo;
|
||||||
}
|
|
||||||
print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n";
|
|
||||||
|
|
||||||
# read and prepare patch information
|
# read and prepare patch information
|
||||||
if (ref($difftree->[$patch_idx]) eq "HASH") {
|
if (ref($difftree->[$patch_idx]) eq "HASH") {
|
||||||
@ -2247,9 +2302,13 @@ sub git_patchset_body {
|
|||||||
hash=>$diffinfo->{'to_id'},
|
hash=>$diffinfo->{'to_id'},
|
||||||
file_name=>$to{'file'});
|
file_name=>$to{'file'});
|
||||||
}
|
}
|
||||||
$patch_idx++;
|
# 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";
|
||||||
|
}
|
||||||
|
|
||||||
# print "git diff" header
|
# print "git diff" header
|
||||||
|
$patch_line = shift @diff_header;
|
||||||
$patch_line =~ s!^(diff (.*?) )"?a/.*$!$1!;
|
$patch_line =~ s!^(diff (.*?) )"?a/.*$!$1!;
|
||||||
if ($from{'href'}) {
|
if ($from{'href'}) {
|
||||||
$patch_line .= $cgi->a({-href => $from{'href'}, -class => "path"},
|
$patch_line .= $cgi->a({-href => $from{'href'}, -class => "path"},
|
||||||
@ -2264,15 +2323,12 @@ sub git_patchset_body {
|
|||||||
} else { # file was deleted
|
} else { # file was deleted
|
||||||
$patch_line .= 'b/' . esc_path($to{'file'});
|
$patch_line .= 'b/' . esc_path($to{'file'});
|
||||||
}
|
}
|
||||||
|
|
||||||
print "<div class=\"diff header\">$patch_line</div>\n";
|
print "<div class=\"diff header\">$patch_line</div>\n";
|
||||||
print "<div class=\"diff extended_header\">\n";
|
|
||||||
$in_header = 1;
|
|
||||||
next LINE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($in_header) {
|
# print extended diff header
|
||||||
if ($patch_line !~ m/^---/) {
|
print "<div class=\"diff extended_header\">\n" if (@diff_header > 0);
|
||||||
|
EXTENDED_HEADER:
|
||||||
|
foreach $patch_line (@diff_header) {
|
||||||
# match <path>
|
# match <path>
|
||||||
if ($patch_line =~ s!^((copy|rename) from ).*$!$1! && $from{'href'}) {
|
if ($patch_line =~ s!^((copy|rename) from ).*$!$1! && $from{'href'}) {
|
||||||
$patch_line .= $cgi->a({-href=>$from{'href'}, -class=>"path"},
|
$patch_line .= $cgi->a({-href=>$from{'href'}, -class=>"path"},
|
||||||
@ -2303,16 +2359,23 @@ sub git_patchset_body {
|
|||||||
} else {
|
} else {
|
||||||
$to_link = '0' x 7;
|
$to_link = '0' x 7;
|
||||||
}
|
}
|
||||||
|
#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'});
|
my ($from_id, $to_id) = ($diffinfo->{'from_id'}, $diffinfo->{'to_id'});
|
||||||
$patch_line =~ s!$from_id\.\.$to_id!$from_link..$to_link!;
|
$patch_line =~ s!$from_id\.\.$to_id!$from_link..$to_link!;
|
||||||
}
|
}
|
||||||
print $patch_line . "<br/>\n";
|
print $patch_line . "<br/>\n";
|
||||||
|
}
|
||||||
|
print "</div>\n" if (@diff_header > 0); # class="diff extended_header"
|
||||||
|
|
||||||
} else {
|
# from-file/to-file diff header
|
||||||
#$in_header && $patch_line =~ m/^---/;
|
$patch_line = $last_patch_line;
|
||||||
print "</div>\n"; # class="diff extended_header"
|
#assert($patch_line =~ m/^---/) if DEBUG;
|
||||||
$in_header = 0;
|
|
||||||
|
|
||||||
if ($from{'href'}) {
|
if ($from{'href'}) {
|
||||||
$patch_line = '--- a/' .
|
$patch_line = '--- a/' .
|
||||||
$cgi->a({-href=>$from{'href'}, -class=>"path"},
|
$cgi->a({-href=>$from{'href'}, -class=>"path"},
|
||||||
@ -2321,9 +2384,10 @@ sub git_patchset_body {
|
|||||||
print "<div class=\"diff from_file\">$patch_line</div>\n";
|
print "<div class=\"diff from_file\">$patch_line</div>\n";
|
||||||
|
|
||||||
$patch_line = <$fd>;
|
$patch_line = <$fd>;
|
||||||
|
#last PATCH unless $patch_line;
|
||||||
chomp $patch_line;
|
chomp $patch_line;
|
||||||
|
|
||||||
#$patch_line =~ m/^+++/;
|
#assert($patch_line =~ m/^+++/) if DEBUG;
|
||||||
if ($to{'href'}) {
|
if ($to{'href'}) {
|
||||||
$patch_line = '+++ b/' .
|
$patch_line = '+++ b/' .
|
||||||
$cgi->a({-href=>$to{'href'}, -class=>"path"},
|
$cgi->a({-href=>$to{'href'}, -class=>"path"},
|
||||||
@ -2331,17 +2395,20 @@ sub git_patchset_body {
|
|||||||
}
|
}
|
||||||
print "<div class=\"diff to_file\">$patch_line</div>\n";
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
next LINE;
|
} continue {
|
||||||
|
print "</div>\n"; # class="patch"
|
||||||
}
|
}
|
||||||
|
|
||||||
print format_diff_line($patch_line);
|
|
||||||
}
|
|
||||||
print "</div>\n" if $in_header; # extended header
|
|
||||||
|
|
||||||
print "</div>\n" if $patch_found; # class="patch"
|
|
||||||
|
|
||||||
print "</div>\n"; # class="patchset"
|
print "</div>\n"; # class="patchset"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2851,8 +2918,8 @@ sub git_tag {
|
|||||||
print "<div class=\"page_body\">";
|
print "<div class=\"page_body\">";
|
||||||
my $comment = $tag{'comment'};
|
my $comment = $tag{'comment'};
|
||||||
foreach my $line (@$comment) {
|
foreach my $line (@$comment) {
|
||||||
chomp($line);
|
chomp $line;
|
||||||
print esc_html($line) . "<br/>\n";
|
print esc_html($line, -nbsp=>1) . "<br/>\n";
|
||||||
}
|
}
|
||||||
print "</div>\n";
|
print "</div>\n";
|
||||||
git_footer_html();
|
git_footer_html();
|
||||||
@ -2921,7 +2988,7 @@ HTML
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
my $data = $_;
|
my $data = $_;
|
||||||
chomp($data);
|
chomp $data;
|
||||||
my $rev = substr($full_rev, 0, 8);
|
my $rev = substr($full_rev, 0, 8);
|
||||||
my $author = $meta->{'author'};
|
my $author = $meta->{'author'};
|
||||||
my %date = parse_date($meta->{'author-time'},
|
my %date = parse_date($meta->{'author-time'},
|
||||||
@ -3392,6 +3459,7 @@ sub git_log {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub git_commit {
|
sub git_commit {
|
||||||
|
$hash ||= $hash_base || "HEAD";
|
||||||
my %co = parse_commit($hash);
|
my %co = parse_commit($hash);
|
||||||
if (!%co) {
|
if (!%co) {
|
||||||
die_error(undef, "Unknown commit object");
|
die_error(undef, "Unknown commit object");
|
||||||
@ -3669,6 +3737,7 @@ sub git_blobdiff_plain {
|
|||||||
|
|
||||||
sub git_commitdiff {
|
sub git_commitdiff {
|
||||||
my $format = shift || 'html';
|
my $format = shift || 'html';
|
||||||
|
$hash ||= $hash_base || "HEAD";
|
||||||
my %co = parse_commit($hash);
|
my %co = parse_commit($hash);
|
||||||
if (!%co) {
|
if (!%co) {
|
||||||
die_error(undef, "Unknown commit object");
|
die_error(undef, "Unknown commit object");
|
||||||
@ -3731,7 +3800,8 @@ sub git_commitdiff {
|
|||||||
$hash_parent, $hash, "--"
|
$hash_parent, $hash, "--"
|
||||||
or die_error(undef, "Open git-diff-tree failed");
|
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
|
# empty line ends raw part of diff-tree output
|
||||||
last unless $line;
|
last unless $line;
|
||||||
push @difftree, $line;
|
push @difftree, $line;
|
||||||
@ -4088,26 +4158,125 @@ sub git_shortlog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
## ......................................................................
|
## ......................................................................
|
||||||
## feeds (RSS, OPML)
|
## feeds (RSS, Atom; OPML)
|
||||||
|
|
||||||
sub git_rss {
|
sub git_feed {
|
||||||
# http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
|
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",
|
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");
|
or die_error(undef, "Open git-rev-list failed");
|
||||||
my @revlist = map { chomp; $_ } <$fd>;
|
my @revlist = map { chomp; $_ } <$fd>;
|
||||||
close $fd or die_error(undef, "Reading git-rev-list failed");
|
close $fd or die_error(undef, "Reading git-rev-list failed");
|
||||||
print $cgi->header(-type => 'text/xml', -charset => '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;
|
print <<XML;
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
|
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
|
||||||
<channel>
|
<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
|
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++) {
|
for (my $i = 0; $i <= $#revlist; $i++) {
|
||||||
my $commit = $revlist[$i];
|
my $commit = $revlist[$i];
|
||||||
my %co = parse_commit($commit);
|
my %co = parse_commit($commit);
|
||||||
@ -4116,42 +4285,100 @@ XML
|
|||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
my %cd = parse_date($co{'committer_epoch'});
|
my %cd = parse_date($co{'committer_epoch'});
|
||||||
|
|
||||||
|
# get list of changed files
|
||||||
open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
|
open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
|
||||||
$co{'parent'}, $co{'id'}, "--"
|
$co{'parent'}, $co{'id'}, "--", (defined $file_name ? $file_name : ())
|
||||||
or next;
|
or next;
|
||||||
my @difftree = map { chomp; $_ } <$fd>;
|
my @difftree = map { chomp; $_ } <$fd>;
|
||||||
close $fd
|
close $fd
|
||||||
or next;
|
or next;
|
||||||
|
|
||||||
|
# print element (entry, item)
|
||||||
|
my $co_url = href(-full=>1, action=>"commit", hash=>$commit);
|
||||||
|
if ($format eq 'rss') {
|
||||||
print "<item>\n" .
|
print "<item>\n" .
|
||||||
"<title>" .
|
"<title>" . esc_html($co{'title'}) . "</title>\n" .
|
||||||
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" .
|
"<author>" . esc_html($co{'author'}) . "</author>\n" .
|
||||||
"<pubDate>$cd{'rfc2822'}</pubDate>\n" .
|
"<pubDate>$cd{'rfc2822'}</pubDate>\n" .
|
||||||
"<guid isPermaLink=\"true\">" . esc_html("$my_url?p=$project;a=commit;h=$commit") . "</guid>\n" .
|
"<guid isPermaLink=\"true\">$co_url</guid>\n" .
|
||||||
"<link>" . esc_html("$my_url?p=$project;a=commit;h=$commit") . "</link>\n" .
|
"<link>$co_url</link>\n" .
|
||||||
"<description>" . esc_html($co{'title'}) . "</description>\n" .
|
"<description>" . esc_html($co{'title'}) . "</description>\n" .
|
||||||
"<content:encoded>" .
|
"<content:encoded>" .
|
||||||
"<![CDATA[\n";
|
"<![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'};
|
my $comment = $co{'comment'};
|
||||||
|
print "<pre>\n";
|
||||||
foreach my $line (@$comment) {
|
foreach my $line (@$comment) {
|
||||||
$line = to_utf8($line);
|
$line = esc_html($line);
|
||||||
print "$line<br/>\n";
|
print "$line\n";
|
||||||
}
|
}
|
||||||
print "<br/>\n";
|
print "</pre><ul>\n";
|
||||||
foreach my $line (@difftree) {
|
foreach my $difftree_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(.*)$/)) {
|
my %difftree = parse_difftree_raw_line($difftree_line);
|
||||||
next;
|
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));
|
# if this is not a feed of a file history
|
||||||
$file = to_utf8($file);
|
if (!defined $file_name || $file_name ne $file) {
|
||||||
print "$file<br/>\n";
|
print $cgi->a({-href => href(-full=>1, action=>"history",
|
||||||
|
file_name=>$file, hash=>$commit),
|
||||||
|
-title => "history"}, 'H');
|
||||||
}
|
}
|
||||||
print "]]>\n" .
|
$file = esc_path($file);
|
||||||
|
print "] ".
|
||||||
|
"$file</li>\n";
|
||||||
|
}
|
||||||
|
if ($format eq 'rss') {
|
||||||
|
print "</ul>]]>\n" .
|
||||||
"</content:encoded>\n" .
|
"</content:encoded>\n" .
|
||||||
"</item>\n";
|
"</item>\n";
|
||||||
|
} elsif ($format eq 'atom') {
|
||||||
|
print "</ul>\n</div>\n" .
|
||||||
|
"</content>\n" .
|
||||||
|
"</entry>\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 {
|
sub git_opml {
|
||||||
|
@ -73,6 +73,7 @@ test_expect_success setup '
|
|||||||
for i in 1 2; do echo $i; done >>dir/sub &&
|
for i in 1 2; do echo $i; done >>dir/sub &&
|
||||||
git update-index file0 dir/sub &&
|
git update-index file0 dir/sub &&
|
||||||
|
|
||||||
|
git repo-config log.showroot false &&
|
||||||
git commit --amend &&
|
git commit --amend &&
|
||||||
git show-branch
|
git show-branch
|
||||||
'
|
'
|
||||||
|
@ -12,9 +12,15 @@
|
|||||||
|
|
||||||
static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>";
|
static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>";
|
||||||
|
|
||||||
#define THEY_HAVE (1U << 0)
|
/* bits #0..7 in revision.h, #8..10 in commit.c */
|
||||||
#define OUR_REF (1U << 1)
|
#define THEY_HAVE (1u << 11)
|
||||||
#define WANTED (1U << 2)
|
#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 multi_ack, nr_our_refs;
|
||||||
static int use_thin_pack, use_ofs_delta;
|
static int use_thin_pack, use_ofs_delta;
|
||||||
static struct object_array have_obj;
|
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)
|
static int got_sha1(char *hex, unsigned char *sha1)
|
||||||
{
|
{
|
||||||
struct object *o;
|
struct object *o;
|
||||||
|
int we_knew_they_have = 0;
|
||||||
|
|
||||||
if (get_sha1_hex(hex, sha1))
|
if (get_sha1_hex(hex, sha1))
|
||||||
die("git-upload-pack: expected SHA1 object, got '%s'", hex);
|
die("git-upload-pack: expected SHA1 object, got '%s'", hex);
|
||||||
if (!has_sha1_file(sha1))
|
if (!has_sha1_file(sha1))
|
||||||
return 0;
|
return -1;
|
||||||
|
|
||||||
o = lookup_object(sha1);
|
o = lookup_object(sha1);
|
||||||
if (!(o && o->parsed))
|
if (!(o && o->parsed))
|
||||||
@ -316,17 +323,86 @@ static int got_sha1(char *hex, unsigned char *sha1)
|
|||||||
die("oops (%s)", sha1_to_hex(sha1));
|
die("oops (%s)", sha1_to_hex(sha1));
|
||||||
if (o->type == OBJ_COMMIT) {
|
if (o->type == OBJ_COMMIT) {
|
||||||
struct commit_list *parents;
|
struct commit_list *parents;
|
||||||
|
struct commit *commit = (struct commit *)o;
|
||||||
if (o->flags & THEY_HAVE)
|
if (o->flags & THEY_HAVE)
|
||||||
return 0;
|
we_knew_they_have = 1;
|
||||||
|
else
|
||||||
o->flags |= THEY_HAVE;
|
o->flags |= THEY_HAVE;
|
||||||
for (parents = ((struct commit*)o)->parents;
|
if (!oldest_have || (commit->date < oldest_have))
|
||||||
|
oldest_have = commit->date;
|
||||||
|
for (parents = commit->parents;
|
||||||
parents;
|
parents;
|
||||||
parents = parents->next)
|
parents = parents->next)
|
||||||
parents->item->object.flags |= THEY_HAVE;
|
parents->item->object.flags |= THEY_HAVE;
|
||||||
}
|
}
|
||||||
|
if (!we_knew_they_have) {
|
||||||
add_object_array(o, NULL, &have_obj);
|
add_object_array(o, NULL, &have_obj);
|
||||||
return 1;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
static int get_common_commits(void)
|
static int get_common_commits(void)
|
||||||
{
|
{
|
||||||
@ -349,7 +425,13 @@ static int get_common_commits(void)
|
|||||||
}
|
}
|
||||||
len = strip(line, len);
|
len = strip(line, len);
|
||||||
if (!strncmp(line, "have ", 5)) {
|
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);
|
memcpy(hex, sha1_to_hex(sha1), 41);
|
||||||
if (multi_ack) {
|
if (multi_ack) {
|
||||||
const char *msg = "ACK %s continue\n";
|
const char *msg = "ACK %s continue\n";
|
||||||
@ -358,6 +440,7 @@ static int get_common_commits(void)
|
|||||||
}
|
}
|
||||||
else if (have_obj.nr == 1)
|
else if (have_obj.nr == 1)
|
||||||
packet_write(1, "ACK %s\n", hex);
|
packet_write(1, "ACK %s\n", hex);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
|
|||||||
xdemitconf_t const *xecfg) {
|
xdemitconf_t const *xecfg) {
|
||||||
long s1, s2, e1, e2, lctx;
|
long s1, s2, e1, e2, lctx;
|
||||||
xdchange_t *xch, *xche;
|
xdchange_t *xch, *xche;
|
||||||
char funcbuf[40];
|
char funcbuf[80];
|
||||||
long funclen = 0;
|
long funclen = 0;
|
||||||
|
|
||||||
if (xecfg->flags & XDL_EMIT_COMMON)
|
if (xecfg->flags & XDL_EMIT_COMMON)
|
||||||
|
Loading…
Reference in New Issue
Block a user