提交 ddb95de3 编写于 作者: J Junio C Hamano

Merge branch 'master' into ph/strbuf

* master:
  archive - leakfix for format_subst()
  Make --no-thin the default in git-push to save server resources
  fix doc for --compression argument to pack-objects
  git-tag -s must fail if gpg cannot sign the tag.
  git-svn: understand grafts when doing dcommit
  git-diff: don't squelch the new SHA1 in submodule diffs
  Define NO_MEMMEM on Darwin as it lacks the function
  git-svn: fix "Malformed network data" with svn:// servers
  (cvs|svn)import: Ask git-tag to overwrite old tags.
  git-rebase: fix -C option
  git-rebase: support --whitespace=<option>
  Documentation / grammer nit
  archive: rename attribute specfile to export-subst
  archive: specfile syntax change: "$Format:%PLCHLDR$" instead of just "%PLCHLDR" (take 2)
  add memmem()
  Remove unused function convert_sha1_file()
  archive: specfile support (--pretty=format: in archive files)
  Export format_commit_message()
......@@ -68,7 +68,7 @@ OPTIONS
automatically setup .git/objects/info/alternates to
obtain objects from the reference repository. Using
an already existing repository as an alternate will
require less objects to be copied from the repository
require fewer objects to be copied from the repository
being cloned, reducing network and local storage costs.
--quiet::
......
......@@ -155,12 +155,8 @@ base-name::
generated pack. If not specified, pack compression level is
determined first by pack.compression, then by core.compression,
and defaults to -1, the zlib default, if neither is set.
Data copied from loose objects will be recompressed
if core.legacyheaders was true when they were created or if
the loose compression level (see core.loosecompression and
core.compression) is now a different value than the pack
compression level. Add --no-reuse-object if you want to force
a uniform compression level on all data no matter the source.
Add \--no-reuse-object if you want to force a uniform compression
level on all data no matter the source.
--delta-base-offset::
A packed archive can express base object of a delta as
......
......@@ -8,8 +8,9 @@ git-rebase - Forward-port local commits to the updated upstream head
SYNOPSIS
--------
[verse]
'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge] [-C<n>]
[-p | --preserve-merges] [--onto <newbase>] <upstream> [<branch>]
'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge]
[-C<n>] [ --whitespace=<option>] [-p | --preserve-merges]
[--onto <newbase>] <upstream> [<branch>]
'git-rebase' --continue | --skip | --abort
DESCRIPTION
......@@ -209,6 +210,10 @@ OPTIONS
context exist they all must match. By default no context is
ever ignored.
--whitespace=<nowarn|warn|error|error-all|strip>::
This flag is passed to the `git-apply` program
(see gitlink:git-apply[1]) that applies the patch.
-i, \--interactive::
Make a list of the commits which are about to be rebased. Let the
user edit that list before rebasing. This mode can also be used to
......
......@@ -421,6 +421,23 @@ frotz unspecified
----------------------------------------------------------------
Creating an archive
~~~~~~~~~~~~~~~~~~~
`export-subst`
^^^^^^^^^^^^^^
If the attribute `export-subst` is set for a file then git will expand
several placeholders when adding this file to an archive. The
expansion depends on the availability of a commit ID, i.e. if
gitlink:git-archive[1] has been given a tree instead of a commit or a
tag then no replacement will be done. The placeholders are the same
as those for the option `--pretty=format:` of gitlink:git-log[1],
except that they need to be wrapped like this: `$Format:PLACEHOLDERS$`
in the file. E.g. the string `$Format:%H$` will be replaced by the
commit hash.
GIT
---
Part of the gitlink:git[7] suite
......@@ -28,6 +28,8 @@ all::
#
# Define NO_STRCASESTR if you don't have strcasestr.
#
# Define NO_MEMMEM if you don't have memmem.
#
# Define NO_STRLCPY if you don't have strlcpy.
#
# Define NO_STRTOUMAX if you don't have strtoumax in the C library.
......@@ -396,12 +398,14 @@ ifeq ($(uname_S),Darwin)
NEEDS_LIBICONV = YesPlease
OLD_ICONV = UnfortunatelyYes
NO_STRLCPY = YesPlease
NO_MEMMEM = YesPlease
endif
ifeq ($(uname_S),SunOS)
NEEDS_SOCKET = YesPlease
NEEDS_NSL = YesPlease
SHELL_PATH = /bin/bash
NO_STRCASESTR = YesPlease
NO_MEMMEM = YesPlease
NO_HSTRERROR = YesPlease
ifeq ($(uname_R),5.8)
NEEDS_LIBICONV = YesPlease
......@@ -424,6 +428,7 @@ ifeq ($(uname_O),Cygwin)
NO_D_TYPE_IN_DIRENT = YesPlease
NO_D_INO_IN_DIRENT = YesPlease
NO_STRCASESTR = YesPlease
NO_MEMMEM = YesPlease
NO_SYMLINK_HEAD = YesPlease
NEEDS_LIBICONV = YesPlease
NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
......@@ -442,6 +447,7 @@ ifeq ($(uname_S),FreeBSD)
endif
ifeq ($(uname_S),OpenBSD)
NO_STRCASESTR = YesPlease
NO_MEMMEM = YesPlease
NEEDS_LIBICONV = YesPlease
BASIC_CFLAGS += -I/usr/local/include
BASIC_LDFLAGS += -L/usr/local/lib
......@@ -456,6 +462,7 @@ ifeq ($(uname_S),NetBSD)
endif
ifeq ($(uname_S),AIX)
NO_STRCASESTR=YesPlease
NO_MEMMEM = YesPlease
NO_STRLCPY = YesPlease
NEEDS_LIBICONV=YesPlease
endif
......@@ -467,6 +474,7 @@ ifeq ($(uname_S),IRIX64)
NO_IPV6=YesPlease
NO_SETENV=YesPlease
NO_STRCASESTR=YesPlease
NO_MEMMEM = YesPlease
NO_STRLCPY = YesPlease
NO_SOCKADDR_STORAGE=YesPlease
SHELL_PATH=/usr/gnu/bin/bash
......@@ -661,6 +669,10 @@ ifdef NO_HSTRERROR
COMPAT_CFLAGS += -DNO_HSTRERROR
COMPAT_OBJS += compat/hstrerror.o
endif
ifdef NO_MEMMEM
COMPAT_CFLAGS += -DNO_MEMMEM
COMPAT_OBJS += compat/memmem.o
endif
ifeq ($(TCLTK_PATH),)
NO_TCLTK=NoThanks
......
......@@ -17,6 +17,7 @@ static unsigned long offset;
static time_t archive_time;
static int tar_umask = 002;
static int verbose;
static const struct commit *commit;
/* writes out the whole block, but only if it is full */
static void write_if_needed(void)
......@@ -252,7 +253,8 @@ static int write_tar_entry(const unsigned char *sha1,
buffer = NULL;
size = 0;
} else {
buffer = convert_sha1_file(path.buf, sha1, mode, &type, &size);
buffer = sha1_file_to_archive(path.buf, sha1, mode, &type,
&size, commit);
if (!buffer)
die("cannot read %s", sha1_to_hex(sha1));
}
......@@ -271,6 +273,7 @@ int write_tar_archive(struct archiver_args *args)
archive_time = args->time;
verbose = args->verbose;
commit = args->commit;
if (args->commit_sha1)
write_global_extended_header(args->commit_sha1);
......
......@@ -12,6 +12,7 @@
static int verbose;
static int zip_date;
static int zip_time;
static const struct commit *commit;
static unsigned char *zip_dir;
static unsigned int zip_dir_size;
......@@ -195,7 +196,8 @@ static int write_zip_entry(const unsigned char *sha1,
if (S_ISREG(mode) && zlib_compression_level != 0)
method = 8;
result = 0;
buffer = convert_sha1_file(path, sha1, mode, &type, &size);
buffer = sha1_file_to_archive(path, sha1, mode, &type, &size,
commit);
if (!buffer)
die("cannot read %s", sha1_to_hex(sha1));
crc = crc32(crc, buffer, size);
......@@ -316,6 +318,7 @@ int write_zip_archive(struct archiver_args *args)
zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);
zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
verbose = args->verbose;
commit = args->commit;
if (args->base && plen > 0 && args->base[plen - 1] == '/') {
char *base = xstrdup(args->base);
......
......@@ -8,6 +8,7 @@ struct archiver_args {
const char *base;
struct tree *tree;
const unsigned char *commit_sha1;
const struct commit *commit;
time_t time;
const char **pathspec;
unsigned int verbose : 1;
......@@ -42,4 +43,6 @@ extern int write_tar_archive(struct archiver_args *);
extern int write_zip_archive(struct archiver_args *);
extern void *parse_extra_zip_args(int argc, const char **argv);
extern void *sha1_file_to_archive(const char *path, const unsigned char *sha1, unsigned int mode, enum object_type *type, unsigned long *size, const struct commit *commit);
#endif /* ARCHIVE_H */
......@@ -10,6 +10,7 @@
#include "exec_cmd.h"
#include "pkt-line.h"
#include "sideband.h"
#include "attr.h"
static const char archive_usage[] = \
"git-archive --format=<fmt> [--prefix=<prefix>/] [--verbose] [<extra>] <tree-ish> [path...]";
......@@ -80,6 +81,100 @@ static int run_remote_archiver(const char *remote, int argc,
return !!rv;
}
static void *format_subst(const struct commit *commit, const char *format,
unsigned long *sizep)
{
unsigned long len = *sizep, result_len = 0;
const char *a = format;
char *result = NULL;
for (;;) {
const char *b, *c;
char *fmt, *formatted = NULL;
unsigned long a_len, fmt_len, formatted_len, allocated = 0;
b = memmem(a, len, "$Format:", 8);
if (!b || a + len < b + 9)
break;
c = memchr(b + 8, '$', len - 8);
if (!c)
break;
a_len = b - a;
fmt_len = c - b - 8;
fmt = xmalloc(fmt_len + 1);
memcpy(fmt, b + 8, fmt_len);
fmt[fmt_len] = '\0';
formatted_len = format_commit_message(commit, fmt, &formatted,
&allocated);
free(fmt);
result = xrealloc(result, result_len + a_len + formatted_len);
memcpy(result + result_len, a, a_len);
memcpy(result + result_len + a_len, formatted, formatted_len);
result_len += a_len + formatted_len;
len -= c + 1 - a;
a = c + 1;
}
if (result && len) {
result = xrealloc(result, result_len + len);
memcpy(result + result_len, a, len);
result_len += len;
}
*sizep = result_len;
return result;
}
static void *convert_to_archive(const char *path,
const void *src, unsigned long *sizep,
const struct commit *commit)
{
static struct git_attr *attr_export_subst;
struct git_attr_check check[1];
if (!commit)
return NULL;
if (!attr_export_subst)
attr_export_subst = git_attr("export-subst", 12);
check[0].attr = attr_export_subst;
if (git_checkattr(path, ARRAY_SIZE(check), check))
return NULL;
if (!ATTR_TRUE(check[0].value))
return NULL;
return format_subst(commit, src, sizep);
}
void *sha1_file_to_archive(const char *path, const unsigned char *sha1,
unsigned int mode, enum object_type *type,
unsigned long *size,
const struct commit *commit)
{
void *buffer, *converted;
buffer = read_sha1_file(sha1, type, size);
if (buffer && S_ISREG(mode)) {
converted = convert_to_working_tree(path, buffer, size);
if (converted) {
free(buffer);
buffer = converted;
}
converted = convert_to_archive(path, buffer, size, commit);
if (converted) {
free(buffer);
buffer = converted;
}
}
return buffer;
}
static int init_archiver(const char *name, struct archiver *ar)
{
int rv = -1, i;
......@@ -109,7 +204,7 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
const unsigned char *commit_sha1;
time_t archive_time;
struct tree *tree;
struct commit *commit;
const struct commit *commit;
unsigned char sha1[20];
if (get_sha1(name, sha1))
......@@ -142,6 +237,7 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
}
ar_args->tree = tree;
ar_args->commit_sha1 = commit_sha1;
ar_args->commit = commit;
ar_args->time = archive_time;
}
......
......@@ -9,7 +9,7 @@
static const char push_usage[] = "git-push [--all] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]";
static int all, force, thin = 1, verbose;
static int all, force, thin, verbose;
static const char *receivepack;
static const char **refspec;
......
......@@ -200,6 +200,10 @@ static ssize_t do_sign(char *buffer, size_t size, size_t max)
bracket[1] = '\0';
}
/* When the username signingkey is bad, program could be terminated
* because gpg exits without reading and then write gets SIGPIPE. */
signal(SIGPIPE, SIG_IGN);
memset(&gpg, 0, sizeof(gpg));
gpg.argv = args;
gpg.in = -1;
......@@ -212,12 +216,17 @@ static ssize_t do_sign(char *buffer, size_t size, size_t max)
if (start_command(&gpg))
return error("could not run gpg.");
write_or_die(gpg.in, buffer, size);
if (write_in_full(gpg.in, buffer, size) != size) {
close(gpg.in);
finish_command(&gpg);
return error("gpg did not accept the tag data");
}
close(gpg.in);
gpg.close_in = 0;
len = read_in_full(gpg.out, buffer + size, max - size);
finish_command(&gpg);
if (finish_command(&gpg) || !len || len < 0)
return error("gpg failed to sign the tag");
if (len == max - size)
return error("could not read the entire signature from gpg.");
......@@ -310,9 +319,10 @@ static void create_tag(const unsigned char *object, const char *tag,
size += header_len;
if (sign) {
size = do_sign(buffer, size, max_size);
if (size < 0)
ssize_t r = do_sign(buffer, size, max_size);
if (r < 0)
die("unable to sign the tag");
size = r;
}
if (write_sha1_file(buffer, size, tag_type, result) < 0)
......
......@@ -592,7 +592,6 @@ extern void trace_argv_printf(const char **argv, int count, const char *format,
/* convert.c */
extern char *convert_to_git(const char *path, const char *src, unsigned long *sizep);
extern char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep);
extern void *convert_sha1_file(const char *path, const unsigned char *sha1, unsigned int mode, enum object_type *type, unsigned long *size);
/* diff.c */
extern int diff_auto_refresh_index;
......
......@@ -787,8 +787,8 @@ static void fill_person(struct interp *table, const char *msg, int len)
interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601));
}
static long format_commit_message(const struct commit *commit,
const char *msg, char **buf_p, unsigned long *space_p)
long format_commit_message(const struct commit *commit, const void *format,
char **buf_p, unsigned long *space_p)
{
struct interp table[] = {
{ "%H" }, /* commit hash */
......@@ -843,6 +843,7 @@ static long format_commit_message(const struct commit *commit,
char parents[1024];
int i;
enum { HEADER, SUBJECT, BODY } state;
const char *msg = commit->buffer;
if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table))
die("invalid interp table!");
......@@ -924,7 +925,7 @@ static long format_commit_message(const struct commit *commit,
char *buf = *buf_p;
unsigned long space = *space_p;
space = interpolate(buf, space, user_format,
space = interpolate(buf, space, format,
table, ARRAY_SIZE(table));
if (!space)
break;
......@@ -1165,7 +1166,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
char *buf;
if (fmt == CMIT_FMT_USERFORMAT)
return format_commit_message(commit, msg, buf_p, space_p);
return format_commit_message(commit, user_format, buf_p, space_p);
encoding = (git_log_output_encoding
? git_log_output_encoding
......
......@@ -61,6 +61,7 @@ enum cmit_fmt {
};
extern enum cmit_fmt get_commit_format(const char *arg);
extern long format_commit_message(const struct commit *commit, const void *template, char **buf_p, unsigned long *space_p);
extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char **buf_p, unsigned long *space_p, int abbrev, const char *subject, const char *after_subject, enum date_mode dmode);
/** Removes the first commit from a list sorted by date, and adds all
......
#include "../git-compat-util.h"
void *gitmemmem(const void *haystack, size_t haystack_len,
const void *needle, size_t needle_len)
{
const char *begin = haystack;
const char *last_possible = begin + haystack_len - needle_len;
/*
* The first occurrence of the empty string is deemed to occur at
* the beginning of the string.
*/
if (needle_len == 0)
return (void *)begin;
/*
* Sanity check, otherwise the loop might search through the whole
* memory.
*/
if (haystack_len < needle_len)
return NULL;
for (; begin <= last_possible; begin++) {
if (!memcmp(begin, needle, needle_len))
return (void *)begin;
}
return NULL;
}
......@@ -687,18 +687,3 @@ char *convert_to_working_tree(const char *path, const char *src, unsigned long *
return buf;
}
void *convert_sha1_file(const char *path, const unsigned char *sha1,
unsigned int mode, enum object_type *type,
unsigned long *size)
{
void *buffer = read_sha1_file(sha1, type, size);
if (S_ISREG(mode) && buffer) {
void *converted = convert_to_working_tree(path, buffer, size);
if (converted) {
free(buffer);
buffer = converted;
}
}
return buffer;
}
......@@ -3135,6 +3135,22 @@ static void diffcore_apply_filter(const char *filter)
*q = outq;
}
/* Check whether two filespecs with the same mode and size are identical */
static int diff_filespec_is_identical(struct diff_filespec *one,
struct diff_filespec *two)
{
if (S_ISGITLINK(one->mode)) {
diff_fill_sha1_info(one);
diff_fill_sha1_info(two);
return !hashcmp(one->sha1, two->sha1);
}
if (diff_populate_filespec(one, 0))
return 0;
if (diff_populate_filespec(two, 0))
return 0;
return !memcmp(one->data, two->data, one->size);
}
static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
{
int i;
......@@ -3166,10 +3182,7 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
diff_populate_filespec(p->one, 1) ||
diff_populate_filespec(p->two, 1) ||
(p->one->size != p->two->size) ||
diff_populate_filespec(p->one, 0) || /* (2) */
diff_populate_filespec(p->two, 0) ||
memcmp(p->one->data, p->two->data, p->one->size))
!diff_filespec_is_identical(p->one, p->two)) /* (2) */
diff_q(&outq, p);
else {
/*
......
......@@ -172,6 +172,12 @@ extern uintmax_t gitstrtoumax(const char *, char **, int);
extern const char *githstrerror(int herror);
#endif
#ifdef NO_MEMMEM
#define memmem gitmemmem
void *gitmemmem(const void *haystack, size_t haystacklen,
const void *needle, size_t needlelen);
#endif
extern void release_pack_memory(size_t, int);
static inline char* xstrdup(const char *str)
......
......@@ -779,7 +779,7 @@ sub commit {
$xtag =~ tr/_/\./ if ( $opt_u );
$xtag =~ s/[\/]/$opt_s/g;
system('git-tag', $xtag, $cid) == 0
system('git-tag', '-f', $xtag, $cid) == 0
or die "Cannot create tag $xtag: $!\n";
print "Created tag '$xtag' on '$branch'\n" if $opt_v;
......
......@@ -216,9 +216,11 @@ do
-v|--verbose)
verbose=t
;;
--whitespace=*)
git_am_opt="$git_am_opt $1"
;;
-C*)
git_am_opt=$1
shift
git_am_opt="$git_am_opt $1"
;;
-*)
usage
......
......@@ -841,14 +841,9 @@ sub working_head_info {
sub read_commit_parents {
my ($parents, $c) = @_;
my ($fh, $ctx) = command_output_pipe(qw/cat-file commit/, $c);
while (<$fh>) {
chomp;
last if '';
/^parent ($sha1)/ or next;
push @{$parents->{$c}}, $1;
}
close $fh; # break the pipe
chomp(my $p = command_oneline(qw/rev-list --parents -1/, $c));
$p =~ s/^($c)\s*// or die "rev-list --parents -1 $c failed!\n";
@{$parents->{$c}} = split(/ /, $p);
}
sub linearize_history {
......@@ -3013,7 +3008,7 @@ package Git::SVN::Ra;
use vars qw/@ISA $config_dir $_log_window_size/;
use strict;
use warnings;
my ($can_do_switch, %ignored_err, $RA);
my ($ra_invalid, $can_do_switch, %ignored_err, $RA);
BEGIN {
# enforce temporary pool usage for some simple functions
......@@ -3174,7 +3169,11 @@ sub gs_do_switch {
$self->{url} = $full_url;
$reparented = 1;
} else {
$_[0] = undef;
$self = undef;
$RA = undef;
$ra = Git::SVN::Ra->new($full_url);
$ra_invalid = 1;
}
}
$ra ||= $self;
......@@ -3234,6 +3233,7 @@ sub gs_fetch_loop_common {
my $inc = $_log_window_size;
my ($min, $max) = ($base, $head < $base + $inc ? $head : $base + $inc);
my $longest_path = longest_common_path($gsv, $globs);
my $ra_url = $self->{url};
while (1) {
my %revs;
my $err;
......@@ -3295,6 +3295,13 @@ sub gs_fetch_loop_common {
"$g->{t}-maxRev";
Git::SVN::tmp_config($k, $r);
}
if ($ra_invalid) {
$_[0] = undef;
$self = undef;
$RA = undef;
$self = Git::SVN::Ra->new($ra_url);
$ra_invalid = undef;
}
}
# pre-fill the .rev_db since it'll eventually get filled in
# with '0' x40 if something new gets committed
......
......@@ -873,7 +873,7 @@ sub commit {
$dest =~ tr/_/\./ if $opt_u;
system('git-tag', $dest, $cid) == 0
system('git-tag', '-f', $dest, $cid) == 0
or die "Cannot create tag $dest: $!\n";
print "Created tag '$dest' on '$branch'\n" if $opt_v;
......
......@@ -28,12 +28,15 @@ commit id embedding:
TAR=${TAR:-tar}
UNZIP=${UNZIP:-unzip}
SUBSTFORMAT=%H%n
test_expect_success \
'populate workdir' \
'mkdir a b c &&
echo simple textfile >a/a &&
mkdir a/bin &&
cp /bin/sh a/bin &&
printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile &&
ln -s a a/l1 &&
(p=long_path_to_a_file && cd a &&
for depth in 1 2 3 4 5; do mkdir $p && cd $p; done &&
......@@ -104,6 +107,22 @@ test_expect_success \
'validate file contents with prefix' \
'diff -r a c/prefix/a'
test_expect_success \
'create an archive with a substfile' \
'echo substfile export-subst >a/.gitattributes &&
git archive HEAD >f.tar &&
rm a/.gitattributes'
test_expect_success \
'extract substfile' \
'(mkdir f && cd f && $TAR xf -) <f.tar'
test_expect_success \
'validate substfile contents' \
'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
>f/a/substfile.expected &&
diff f/a/substfile.expected f/a/substfile'
test_expect_success \
'git archive --format=zip' \
'git archive --format=zip HEAD >d.zip'
......
......@@ -990,6 +990,13 @@ test_expect_success \
git diff expect actual
'
# try to sign with bad user.signingkey
git config user.signingkey BobTheMouse
test_expect_failure \
'git-tag -s fails if gpg is misconfigured' \
'git tag -s -m tail tag-gpg-failure'
git config --unset user.signingkey
# try to verify without gpg:
rm -rf gpghome
......
......@@ -152,6 +152,10 @@ test_expect_success 'the --cached sha1 should be rev1' '
git-submodule --cached status | grep "^+$rev1"
'
test_expect_success 'git diff should report the SHA1 of the new submodule commit' '
git-diff | grep "^+Subproject commit $rev2"
'
test_expect_success 'update should checkout rev1' '
git-submodule update &&
head=$(cd lib && git rev-parse HEAD) &&
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册