summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/git-merge-file.txt4
-rw-r--r--Documentation/git-svnimport.txt13
-rw-r--r--builtin-show-ref.c77
-rw-r--r--cache.h1
-rwxr-xr-xgit-fetch.sh12
-rwxr-xr-xgitweb/gitweb.perl216
-rw-r--r--ident.c15
-rw-r--r--read-cache.c6
-rw-r--r--receive-pack.c2
-rwxr-xr-xt/t0000-basic.sh9
10 files changed, 305 insertions, 50 deletions
diff --git a/Documentation/git-merge-file.txt b/Documentation/git-merge-file.txt
index 0b41d66a70..29d3faa556 100644
--- a/Documentation/git-merge-file.txt
+++ b/Documentation/git-merge-file.txt
@@ -1,9 +1,9 @@
git-merge-file(1)
-============
+=================
NAME
----
-git-merge-file - threeway file merge
+git-merge-file - three-way file merge
SYNOPSIS
diff --git a/Documentation/git-svnimport.txt b/Documentation/git-svnimport.txt
index b1b87c2fcd..2c7c7dad54 100644
--- a/Documentation/git-svnimport.txt
+++ b/Documentation/git-svnimport.txt
@@ -15,6 +15,7 @@ SYNOPSIS
[ -b branch_subdir ] [ -T trunk_subdir ] [ -t tag_subdir ]
[ -s start_chg ] [ -m ] [ -r ] [ -M regex ]
[ -I <ignorefile_name> ] [ -A <author_file> ]
+ [ -P <path_from_trunk> ]
<SVN_repository_URL> [ <path> ]
@@ -103,9 +104,17 @@ repository without -A.
-l <max_rev>::
Specify a maximum revision number to pull.
++
+Formerly, this option controlled how many revisions to pull,
+due to SVN memory leaks. (These have been worked around.)
- Formerly, this option controlled how many revisions to pull,
- due to SVN memory leaks. (These have been worked around.)
+-P <path_from_trunk>::
+ Partial import of the SVN tree.
++
+By default, the whole tree on the SVN trunk (/trunk) is imported.
+'-P my/proj' will import starting only from '/trunk/my/proj'.
+This option is useful when you want to import one project from a
+svn repo which hosts multiple projects under the same trunk.
-v::
Verbosity: let 'svnimport' report what it is doing.
diff --git a/builtin-show-ref.c b/builtin-show-ref.c
index 073979855b..23e0ff8fbf 100644
--- a/builtin-show-ref.c
+++ b/builtin-show-ref.c
@@ -2,8 +2,9 @@
#include "refs.h"
#include "object.h"
#include "tag.h"
+#include "path-list.h"
-static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<length>]] [--abbrev[=<length>]] [--tags] [--heads] [--] [pattern*]";
+static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<length>]] [--abbrev[=<length>]] [--tags] [--heads] [--] [pattern*] | --filter-invalid < ref-list";
static int deref_tags = 0, show_head = 0, tags_only = 0, heads_only = 0,
found_match = 0, verify = 0, quiet = 0, hash_only = 0, abbrev = 0;
@@ -86,6 +87,59 @@ match:
return 0;
}
+static int add_existing(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
+{
+ struct path_list *list = (struct path_list *)cbdata;
+ path_list_insert(refname, list);
+ return 0;
+}
+
+/*
+ * read "^(?:<anything>\s)?<refname>(?:\^\{\})?$" from the standard input,
+ * and
+ * (1) strip "^{}" at the end of line if any;
+ * (2) ignore if match is provided and does not head-match refname;
+ * (3) warn if refname is not a well-formed refname and skip;
+ * (4) ignore if refname is a ref that exists in the local repository;
+ * (5) otherwise output the line.
+ */
+static int exclude_existing(const char *match)
+{
+ static struct path_list existing_refs = { NULL, 0, 0, 0 };
+ char buf[1024];
+ int matchlen = match ? strlen(match) : 0;
+
+ for_each_ref(add_existing, &existing_refs);
+ while (fgets(buf, sizeof(buf), stdin)) {
+ int len = strlen(buf);
+ char *ref;
+ if (len > 0 && buf[len - 1] == '\n')
+ buf[--len] = '\0';
+ if (!strcmp(buf + len - 3, "^{}")) {
+ len -= 3;
+ buf[len] = '\0';
+ }
+ for (ref = buf + len; buf < ref; ref--)
+ if (isspace(ref[-1]))
+ break;
+ if (match) {
+ int reflen = buf + len - ref;
+ if (reflen < matchlen)
+ continue;
+ if (strncmp(ref, match, matchlen))
+ continue;
+ }
+ if (check_ref_format(ref)) {
+ fprintf(stderr, "warning: ref '%s' ignored\n", ref);
+ continue;
+ }
+ if (!path_list_has_path(&existing_refs, ref)) {
+ printf("%s\n", buf);
+ }
+ }
+ return 0;
+}
+
int cmd_show_ref(int argc, const char **argv, const char *prefix)
{
int i;
@@ -153,8 +207,29 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
heads_only = 1;
continue;
}
+ if (!strcmp(arg, "--exclude-existing"))
+ return exclude_existing(NULL);
+ if (!strncmp(arg, "--exclude-existing=", 19))
+ return exclude_existing(arg + 19);
usage(show_ref_usage);
}
+
+ if (verify) {
+ unsigned char sha1[20];
+
+ while (*pattern) {
+ if (resolve_ref(*pattern, sha1, 1, NULL))
+ printf("%s %s\n", sha1_to_hex(sha1),
+ *pattern);
+ else if (!quiet)
+ die("'%s' - not a valid ref", *pattern);
+ else
+ return 1;
+ pattern++;
+ }
+ return 0;
+ }
+
if (show_head)
head_ref(show_ref, NULL);
for_each_ref(show_ref, NULL);
diff --git a/cache.h b/cache.h
index bfab4f9752..8ad5920d2b 100644
--- a/cache.h
+++ b/cache.h
@@ -309,6 +309,7 @@ void datestamp(char *buf, int bufsize);
unsigned long approxidate(const char *);
extern int setup_ident(void);
+extern void ignore_missing_committer_name();
extern const char *git_author_info(int);
extern const char *git_committer_info(int);
diff --git a/git-fetch.sh b/git-fetch.sh
index fb35815a5f..38101a6ace 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -242,7 +242,7 @@ esac
reflist=$(get_remote_refs_for_fetch "$@")
if test "$tags"
then
- taglist=`IFS=" " &&
+ taglist=`IFS=' ' &&
echo "$ls_remote_result" |
while read sha1 name
do
@@ -438,17 +438,11 @@ case "$no_tags$tags" in
*:refs/*)
# effective only when we are following remote branch
# using local tracking branch.
- taglist=$(IFS=" " &&
+ taglist=$(IFS=' ' &&
echo "$ls_remote_result" |
- sed -n -e 's|^\('"$_x40"'\) \(refs/tags/.*\)^{}$|\1 \2|p' \
- -e 's|^\('"$_x40"'\) \(refs/tags/.*\)$|\1 \2|p' |
+ git-show-ref --exclude-existing=refs/tags/ |
while read sha1 name
do
- git-show-ref --verify --quiet -- "$name" && continue
- git-check-ref-format "$name" || {
- echo >&2 "warning: tag ${name} ignored"
- continue
- }
git-cat-file -t "$sha1" >/dev/null 2>&1 || continue
echo >&2 "Auto-following $name"
echo ".${name}:${name}"
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 5ea3fda540..4059894e0b 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -434,6 +434,7 @@ my %actions = (
"tags" => \&git_tags,
"tree" => \&git_tree,
"snapshot" => \&git_snapshot,
+ "object" => \&git_object,
# those below don't need $project
"opml" => \&git_opml,
"project_list" => \&git_project_list,
@@ -827,14 +828,12 @@ sub format_log_line_html {
my $line = shift;
$line = esc_html($line, -nbsp=>1);
- if ($line =~ m/([0-9a-fA-F]{40})/) {
+ if ($line =~ m/([0-9a-fA-F]{8,40})/) {
my $hash_text = $1;
- if (git_get_type($hash_text) eq "commit") {
- my $link =
- $cgi->a({-href => href(action=>"commit", hash=>$hash_text),
- -class => "text"}, $hash_text);
- $line =~ s/$hash_text/$link/;
- }
+ my $link =
+ $cgi->a({-href => href(action=>"object", hash=>$hash_text),
+ -class => "text"}, $hash_text);
+ $line =~ s/$hash_text/$link/;
}
return $line;
}
@@ -856,7 +855,8 @@ sub format_ref_marker {
$name = $ref;
}
- $markers .= " <span class=\"$type\">" . esc_html($name) . "</span>";
+ $markers .= " <span class=\"$type\" title=\"$ref\">" .
+ esc_html($name) . "</span>";
}
}
@@ -1989,12 +1989,73 @@ sub git_print_log ($;%) {
}
}
+# return link target (what link points to)
+sub git_get_link_target {
+ my $hash = shift;
+ my $link_target;
+
+ # read link
+ open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
+ or return;
+ {
+ local $/;
+ $link_target = <$fd>;
+ }
+ close $fd
+ or return;
+
+ return $link_target;
+}
+
+# given link target, and the directory (basedir) the link is in,
+# return target of link relative to top directory (top tree);
+# return undef if it is not possible (including absolute links).
+sub normalize_link_target {
+ my ($link_target, $basedir, $hash_base) = @_;
+
+ # we can normalize symlink target only if $hash_base is provided
+ return unless $hash_base;
+
+ # absolute symlinks (beginning with '/') cannot be normalized
+ return if (substr($link_target, 0, 1) eq '/');
+
+ # normalize link target to path from top (root) tree (dir)
+ my $path;
+ if ($basedir) {
+ $path = $basedir . '/' . $link_target;
+ } else {
+ # we are in top (root) tree (dir)
+ $path = $link_target;
+ }
+
+ # remove //, /./, and /../
+ my @path_parts;
+ foreach my $part (split('/', $path)) {
+ # discard '.' and ''
+ next if (!$part || $part eq '.');
+ # handle '..'
+ if ($part eq '..') {
+ if (@path_parts) {
+ pop @path_parts;
+ } else {
+ # link leads outside repository (outside top dir)
+ return;
+ }
+ } else {
+ push @path_parts, $part;
+ }
+ }
+ $path = join('/', @path_parts);
+
+ return $path;
+}
+
# print tree entry (row of git_tree), but without encompassing <tr> element
sub git_print_tree_entry {
my ($t, $basedir, $hash_base, $have_blame) = @_;
my %base_key = ();
- $base_key{hash_base} = $hash_base if defined $hash_base;
+ $base_key{'hash_base'} = $hash_base if defined $hash_base;
# The format of a table row is: mode list link. Where mode is
# the mode of the entry, list is the name of the entry, an href,
@@ -2005,16 +2066,31 @@ sub git_print_tree_entry {
print "<td class=\"list\">" .
$cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
file_name=>"$basedir$t->{'name'}", %base_key),
- -class => "list"}, esc_path($t->{'name'})) . "</td>\n";
+ -class => "list"}, esc_path($t->{'name'}));
+ if (S_ISLNK(oct $t->{'mode'})) {
+ my $link_target = git_get_link_target($t->{'hash'});
+ if ($link_target) {
+ my $norm_target = normalize_link_target($link_target, $basedir, $hash_base);
+ if (defined $norm_target) {
+ print " -> " .
+ $cgi->a({-href => href(action=>"object", hash_base=>$hash_base,
+ file_name=>$norm_target),
+ -title => $norm_target}, esc_path($link_target));
+ } else {
+ print " -> " . esc_path($link_target);
+ }
+ }
+ }
+ print "</td>\n";
print "<td class=\"link\">";
print $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
- file_name=>"$basedir$t->{'name'}", %base_key)},
- "blob");
+ file_name=>"$basedir$t->{'name'}", %base_key)},
+ "blob");
if ($have_blame) {
print " | " .
$cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'},
- file_name=>"$basedir$t->{'name'}", %base_key)},
- "blame");
+ file_name=>"$basedir$t->{'name'}", %base_key)},
+ "blame");
}
if (defined $hash_base) {
print " | " .
@@ -2036,8 +2112,8 @@ sub git_print_tree_entry {
print "</td>\n";
print "<td class=\"link\">";
print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
- file_name=>"$basedir$t->{'name'}", %base_key)},
- "tree");
+ file_name=>"$basedir$t->{'name'}", %base_key)},
+ "tree");
if (defined $hash_base) {
print " | " .
$cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
@@ -3414,8 +3490,7 @@ sub git_snapshot {
my $filename = basename($project) . "-$hash.tar.$suffix";
print $cgi->header(
- -type => 'application/x-tar',
- -content_encoding => $ctype,
+ -type => "application/$ctype",
-content_disposition => 'inline; filename="' . "$filename" . '"',
-status => '200 OK');
@@ -3497,15 +3572,46 @@ sub git_commit {
my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'});
my %cd = parse_date($co{'committer_epoch'}, $co{'committer_tz'});
- my $parent = $co{'parent'};
+ my $parent = $co{'parent'};
+ my $parents = $co{'parents'}; # listref
+
+ # we need to prepare $formats_nav before any parameter munging
+ my $formats_nav;
+ if (!defined $parent) {
+ # --root commitdiff
+ $formats_nav .= '(initial)';
+ } elsif (@$parents == 1) {
+ # single parent commit
+ $formats_nav .=
+ '(parent: ' .
+ $cgi->a({-href => href(action=>"commit",
+ hash=>$parent)},
+ esc_html(substr($parent, 0, 7))) .
+ ')';
+ } else {
+ # merge commit
+ $formats_nav .=
+ '(merge: ' .
+ join(' ', map {
+ $cgi->a({-href => href(action=>"commitdiff",
+ hash=>$_)},
+ esc_html(substr($_, 0, 7)));
+ } @$parents ) .
+ ')';
+ }
+
if (!defined $parent) {
$parent = "--root";
}
- open my $fd, "-|", git_cmd(), "diff-tree", '-r', "--no-commit-id",
- @diff_opts, $parent, $hash, "--"
- or die_error(undef, "Open git-diff-tree failed");
- my @difftree = map { chomp; $_ } <$fd>;
- close $fd or die_error(undef, "Reading git-diff-tree failed");
+ my @difftree;
+ if (@$parents <= 1) {
+ # difftree output is not printed for merges
+ open my $fd, "-|", git_cmd(), "diff-tree", '-r', "--no-commit-id",
+ @diff_opts, $parent, $hash, "--"
+ or die_error(undef, "Open git-diff-tree failed");
+ @difftree = map { chomp; $_ } <$fd>;
+ close $fd or die_error(undef, "Reading git-diff-tree failed");
+ }
# non-textual hash id's can be cached
my $expires;
@@ -3517,16 +3623,10 @@ sub git_commit {
my $have_snapshot = gitweb_have_snapshot();
- my @views_nav = ();
- if (defined $file_name && defined $co{'parent'}) {
- push @views_nav,
- $cgi->a({-href => href(action=>"blame", hash_parent=>$parent, file_name=>$file_name)},
- "blame");
- }
git_header_html(undef, $expires);
git_print_page_nav('commit', '',
$hash, $co{'tree'}, $hash,
- join (' | ', @views_nav));
+ $formats_nav);
if (defined $co{'parent'}) {
git_print_header_div('commitdiff', esc_html($co{'title'}) . $ref, $hash);
@@ -3567,7 +3667,7 @@ sub git_commit {
}
print "</td>" .
"</tr>\n";
- my $parents = $co{'parents'};
+
foreach my $par (@$parents) {
print "<tr>" .
"<td>parent</td>" .
@@ -3589,11 +3689,61 @@ sub git_commit {
git_print_log($co{'comment'});
print "</div>\n";
- git_difftree_body(\@difftree, $hash, $parent);
+ if (@$parents <= 1) {
+ # do not output difftree/whatchanged for merges
+ git_difftree_body(\@difftree, $hash, $parent);
+ }
git_footer_html();
}
+sub git_object {
+ # object is defined by:
+ # - hash or hash_base alone
+ # - hash_base and file_name
+ my $type;
+
+ # - hash or hash_base alone
+ if ($hash || ($hash_base && !defined $file_name)) {
+ my $object_id = $hash || $hash_base;
+
+ my $git_command = git_cmd_str();
+ open my $fd, "-|", "$git_command cat-file -t $object_id 2>/dev/null"
+ or die_error('404 Not Found', "Object does not exist");
+ $type = <$fd>;
+ chomp $type;
+ close $fd
+ or die_error('404 Not Found', "Object does not exist");
+
+ # - hash_base and file_name
+ } elsif ($hash_base && defined $file_name) {
+ $file_name =~ s,/+$,,;
+
+ system(git_cmd(), "cat-file", '-e', $hash_base) == 0
+ or die_error('404 Not Found', "Base object does not exist");
+
+ # here errors should not hapen
+ open my $fd, "-|", git_cmd(), "ls-tree", $hash_base, "--", $file_name
+ or die_error(undef, "Open git-ls-tree failed");
+ my $line = <$fd>;
+ close $fd;
+
+ #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
+ unless ($line && $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/) {
+ die_error('404 Not Found', "File or directory for given base does not exist");
+ }
+ $type = $2;
+ $hash = $3;
+ } else {
+ die_error('404 Not Found', "Not enough information to find object");
+ }
+
+ print $cgi->redirect(-uri => href(action=>$type, -full=>1,
+ hash=>$hash, hash_base=>$hash_base,
+ file_name=>$file_name),
+ -status => '302 Found');
+}
+
sub git_blobdiff {
my $format = shift || 'html';
diff --git a/ident.c b/ident.c
index e415fd3588..d7faba6a70 100644
--- a/ident.c
+++ b/ident.c
@@ -221,3 +221,18 @@ const char *git_committer_info(int error_on_no_name)
getenv("GIT_COMMITTER_DATE"),
error_on_no_name);
}
+
+void ignore_missing_committer_name()
+{
+ /* If we did not get a name from the user's gecos entry then
+ * git_default_name is empty; so instead load the username
+ * into it as a 'good enough for now' approximation of who
+ * this user is.
+ */
+ if (!*git_default_name) {
+ struct passwd *pw = getpwuid(getuid());
+ if (!pw)
+ die("You don't exist. Go away!");
+ strlcpy(git_default_name, pw->pw_name, sizeof(git_default_name));
+ }
+}
diff --git a/read-cache.c b/read-cache.c
index eae4745d28..b8d83ccd9f 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -358,7 +358,7 @@ int add_file_to_index(const char *path, int verbose)
if (index_path(ce->sha1, path, &st, 1))
die("unable to index file %s", path);
- if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD))
+ if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
die("unable to add %s to index",path);
if (verbose)
printf("add '%s'\n", path);
@@ -517,7 +517,7 @@ static int has_dir_name(const struct cache_entry *ce, int pos, int ok_to_replace
pos = cache_name_pos(name, ntohs(create_ce_flags(len, stage)));
if (pos >= 0) {
retval = -1;
- if (ok_to_replace)
+ if (!ok_to_replace)
break;
remove_cache_entry_at(pos);
continue;
@@ -609,7 +609,7 @@ int add_cache_entry(struct cache_entry *ce, int option)
if (!skip_df_check &&
check_file_directory_conflict(ce, pos, ok_to_replace)) {
if (!ok_to_replace)
- return -1;
+ return error("'%s' appears as both a file and as a directory", ce->name);
pos = cache_name_pos(ce->name, ntohs(ce->ce_flags));
pos = -pos-1;
}
diff --git a/receive-pack.c b/receive-pack.c
index e76d9aea31..5e5510bc3d 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -420,6 +420,8 @@ int main(int argc, char **argv)
die("'%s': unable to chdir or not a git archive", dir);
setup_ident();
+ /* don't die if gecos is empty */
+ ignore_missing_committer_name();
git_config(receive_pack_config);
write_head_info();
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 3260d1d7a7..0cd1c41866 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -272,4 +272,13 @@ test_expect_success \
wc -l) &&
test $numparent = 1'
+test_expect_success 'update-index D/F conflict' '
+ mv path0 tmp &&
+ mv path2 path0 &&
+ mv tmp path2 &&
+ git update-index --add --replace path2 path0/file2 &&
+ numpath0=$(git ls-files path0 | wc -l) &&
+ test $numpath0 = 1
+'
+
test_done