summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/RelNotes-1.5.3.1.txt10
-rw-r--r--Documentation/RelNotes-1.5.3.txt2
-rw-r--r--Documentation/RelNotes-1.5.4.txt14
-rw-r--r--Documentation/git-archive.txt5
-rw-r--r--Documentation/git-clone.txt2
-rw-r--r--Documentation/git-config.txt2
-rw-r--r--Documentation/git-filter-branch.txt5
-rw-r--r--Documentation/git-ls-files.txt9
-rw-r--r--Documentation/git-pack-objects.txt8
-rw-r--r--Documentation/git-push.txt6
-rw-r--r--Documentation/git-rebase.txt9
-rw-r--r--Documentation/git-send-email.txt35
-rw-r--r--Documentation/git.txt5
-rw-r--r--Documentation/gitattributes.txt17
-rw-r--r--Makefile15
l---------RelNotes2
-rw-r--r--archive-tar.c5
-rw-r--r--archive-zip.c5
-rw-r--r--archive.h3
-rw-r--r--builtin-add.c3
-rw-r--r--builtin-apply.c3
-rw-r--r--builtin-archive.c98
-rw-r--r--builtin-fetch--tool.c21
-rw-r--r--builtin-grep.c90
-rw-r--r--builtin-log.c31
-rw-r--r--builtin-ls-files.c78
-rw-r--r--builtin-push.c2
-rw-r--r--builtin-tag.c18
-rw-r--r--builtin-update-ref.c9
-rw-r--r--cache.h1
-rw-r--r--commit.c9
-rw-r--r--commit.h1
-rw-r--r--compat/memmem.c29
-rw-r--r--contrib/emacs/git.el197
-rwxr-xr-xcontrib/fast-import/git-p4342
-rwxr-xr-xcontrib/workdir/git-new-workdir6
-rw-r--r--convert.c15
-rw-r--r--diff-delta.c109
-rw-r--r--diff-lib.c2
-rw-r--r--diff.c23
-rw-r--r--diffcore-rename.c19
-rwxr-xr-xgit-clone.sh6
-rwxr-xr-xgit-commit.sh9
-rw-r--r--git-compat-util.h6
-rwxr-xr-xgit-cvsimport.perl2
-rwxr-xr-xgit-rebase.sh6
-rwxr-xr-xgit-send-email.perl104
-rwxr-xr-xgit-stash.sh2
-rwxr-xr-xgit-svn.perl25
-rwxr-xr-xgit-svnimport.perl2
-rw-r--r--git.spec.in1
-rw-r--r--refs.c27
-rw-r--r--refs.h6
-rw-r--r--send-pack.c12
-rwxr-xr-xt/t1400-update-ref.sh2
-rwxr-xr-xt/t2200-add-update.sh11
-rwxr-xr-xt/t4123-apply-shrink.sh58
-rwxr-xr-xt/t5000-tar-tree.sh19
-rwxr-xr-xt/t7002-grep.sh4
-rwxr-xr-xt/t7004-tag.sh7
-rwxr-xr-xt/t7400-submodule-basic.sh4
-rw-r--r--t/t7501-commit.sh32
-rwxr-xr-xt/t9500-gitweb-standalone-no-errors.sh18
-rw-r--r--templates/hooks--update2
-rw-r--r--wt-status.c1
65 files changed, 1266 insertions, 365 deletions
diff --git a/Documentation/RelNotes-1.5.3.1.txt b/Documentation/RelNotes-1.5.3.1.txt
new file mode 100644
index 0000000000..7ff546c743
--- /dev/null
+++ b/Documentation/RelNotes-1.5.3.1.txt
@@ -0,0 +1,10 @@
+GIT v1.5.3.1 Release Notes
+==========================
+
+Fixes since v1.5.3
+------------------
+
+This is solely to fix the generated RPM's dependencies. We used
+to have git-p4 package but we do not anymore. As suggested on
+the mailing list, this release makes git-core "Obsolete" git-p4,
+so that yum update would not complain.
diff --git a/Documentation/RelNotes-1.5.3.txt b/Documentation/RelNotes-1.5.3.txt
index 317c8b9db6..d03894b926 100644
--- a/Documentation/RelNotes-1.5.3.txt
+++ b/Documentation/RelNotes-1.5.3.txt
@@ -152,7 +152,7 @@ Updates since v1.5.2
cloning locally.
- URL used for "git clone" and friends can specify nonstandard SSH port
- by using sh://host:port/path/to/repo syntax.
+ by using ssh://host:port/path/to/repo syntax.
- "git bundle create" can now create a bundle without negative refs,
i.e. "everything since the beginning up to certain points".
diff --git a/Documentation/RelNotes-1.5.4.txt b/Documentation/RelNotes-1.5.4.txt
new file mode 100644
index 0000000000..1df66af9ce
--- /dev/null
+++ b/Documentation/RelNotes-1.5.4.txt
@@ -0,0 +1,14 @@
+GIT v1.5.4 Release Notes
+========================
+
+Updates since v1.5.3
+--------------------
+
+
+
+Fixes since v1.5.3
+------------------
+
+All of the fixes in v1.5.3 maintenance series are included in
+this release, unless otherwise noted.
+
diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt
index f2080eb6ad..e1e2d60fef 100644
--- a/Documentation/git-archive.txt
+++ b/Documentation/git-archive.txt
@@ -15,7 +15,8 @@ SYNOPSIS
DESCRIPTION
-----------
Creates an archive of the specified format containing the tree
-structure for the named tree. If <prefix> is specified it is
+structure for the named tree, and writes it out to the standard
+output. If <prefix> is specified it is
prepended to the filenames in the archive.
'git-archive' behaves differently when given a tree ID versus when
@@ -31,7 +32,7 @@ OPTIONS
-------
--format=<fmt>::
- Format of the resulting archive: 'tar', 'zip'... The default
+ Format of the resulting archive: 'tar' or 'zip'. The default
is 'tar'.
--list, -l::
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 227f092e26..253f4f03c5 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -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::
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 5b794f4399..a592b61e2f 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -142,7 +142,7 @@ FILES
If not set explicitly with '--file', there are three files where
git-config will search for configuration options:
-.git/config::
+$GIT_DIR/config::
Repository specific configuration file. (The filename is
of course relative to the repository root, not the working
directory.)
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index 29bb8cec0c..c878ed395e 100644
--- a/Documentation/git-filter-branch.txt
+++ b/Documentation/git-filter-branch.txt
@@ -220,11 +220,6 @@ git filter-branch --commit-filter '
fi' HEAD
------------------------------------------------------------------------------
-Note that the changes introduced by the commits, and not reverted by
-subsequent commits, will still be in the rewritten branch. If you want
-to throw out _changes_ together with the commits, you should use the
-interactive mode of gitlink:git-rebase[1].
-
The function 'skip_commits' is defined as follows:
--------------------------
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 997594549f..9e454f0a4d 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -15,7 +15,7 @@ SYNOPSIS
[-x <pattern>|--exclude=<pattern>]
[-X <file>|--exclude-from=<file>]
[--exclude-per-directory=<file>]
- [--error-unmatch]
+ [--error-unmatch] [--with-tree=<tree-ish>]
[--full-name] [--abbrev] [--] [<file>]\*
DESCRIPTION
@@ -81,6 +81,13 @@ OPTIONS
If any <file> does not appear in the index, treat this as an
error (return 1).
+--with-tree=<tree-ish>::
+ When using --error-unmatch to expand the user supplied
+ <file> (i.e. path pattern) arguments to paths, pretend
+ that paths which were removed in the index since the
+ named <tree-ish> are still present. Using this option
+ with `-s` or `-u` options does not make any sense.
+
-t::
Identify the file status with the following tags (followed by
a space) at the start of each line:
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index f9b97956e3..628f296ce1 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -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
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 0dd9caf867..7b8e075c42 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -117,6 +117,12 @@ git push origin master:satellite/master::
the ref that matches `satellite/master` (most likely, it would
be `refs/remotes/satellite/master`) in `origin` repository with it.
+git push origin master:refs/heads/experimental::
+ Create the branch `experimental` in the `origin` repository
+ by copying the current `master` branch. This form is usually
+ needed to create a new branch in the remote repository as
+ there is no `experimental` branch to match.
+
Author
------
Written by Junio C Hamano <junkio@cox.net>, later rewritten in C
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 61b1810dba..0858fa8a63 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -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
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 16bfd7be22..1ec61affab 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -75,6 +75,12 @@ The --cc option must be repeated for each user you want on the cc list.
Make git-send-email less verbose. One line per email should be
all that is output.
+--identity::
+ A configuration identity. When given, causes values in the
+ 'sendemail.<identity>' subsection to take precedence over
+ values in the 'sendemail' section. The default identity is
+ the value of 'sendemail.identity'.
+
--smtp-server::
If set, specifies the outgoing SMTP server to use (e.g.
`smtp.example.com` or a raw IP address). Alternatively it can
@@ -85,6 +91,17 @@ The --cc option must be repeated for each user you want on the cc list.
`/usr/lib/sendmail` if such program is available, or
`localhost` otherwise.
+--smtp-user, --smtp-pass::
+ Username and password for SMTP-AUTH. Defaults are the values of
+ the configuration values 'sendemail.smtpuser' and
+ 'sendemail.smtppass', but see also 'sendemail.identity'.
+ If not set, authentication is not attempted.
+
+--smtp-ssl::
+ If set, connects to the SMTP server using SSL.
+ Default is the value of the 'sendemail.smtpssl' configuration value;
+ if that is unspecified, does not use SSL.
+
--subject::
Specify the initial subject of the email thread.
Only necessary if --compose is also set. If --compose
@@ -122,6 +139,13 @@ The --to option must be repeated for each user you want on the to list.
CONFIGURATION
-------------
+sendemail.identity::
+ The default configuration identity. When specified,
+ 'sendemail.<identity>.<item>' will have higher precedence than
+ 'sendemail.<item>'. This is useful to declare multiple SMTP
+ identities and to hoist sensitive authentication information
+ out of the repository and into the global configuation file.
+
sendemail.aliasesfile::
To avoid typing long email addresses, point this to one or more
email aliases files. You must also supply 'sendemail.aliasfiletype'.
@@ -141,7 +165,16 @@ sendemail.chainreplyto::
parameter.
sendemail.smtpserver::
- Default smtp server to use.
+ Default SMTP server to use.
+
+sendemail.smtpuser::
+ Default SMTP-AUTH username.
+
+sendemail.smtppass::
+ Default SMTP-AUTH password.
+
+sendemail.smtpssl::
+ Boolean value specifying the default to the '--smtp-ssl' parameter.
Author
------
diff --git a/Documentation/git.txt b/Documentation/git.txt
index ceca892a0d..6f7db2935b 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -43,7 +43,10 @@ unreleased) version of git, that is available from 'master'
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.5.2.5/git.html[documentation for release 1.5.2.5]
+* link:v1.5.3/git.html[documentation for release 1.5.3]
+
+* release notes for
+ link:RelNotes-1.5.3.1.txt[1.5.3.1].
* release notes for
link:RelNotes-1.5.2.5.txt[1.5.2.5],
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 46f9d591aa..d0e951ee6f 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -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
diff --git a/Makefile b/Makefile
index a92fb31695..40567ece14 100644
--- a/Makefile
+++ b/Makefile
@@ -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.
@@ -284,7 +286,7 @@ LIB_FILE=libgit.a
XDIFF_LIB=xdiff/lib.a
LIB_H = \
- archive.h blob.h cache.h commit.h csum-file.h delta.h grep.h \
+ archive.h blob.h cache.h cache-tree.h commit.h csum-file.h delta.h grep.h \
diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
@@ -399,12 +401,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
@@ -427,6 +431,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
@@ -440,11 +445,13 @@ ifeq ($(uname_O),Cygwin)
endif
ifeq ($(uname_S),FreeBSD)
NEEDS_LIBICONV = YesPlease
+ NO_MEMMEM = YesPlease
BASIC_CFLAGS += -I/usr/local/include
BASIC_LDFLAGS += -L/usr/local/lib
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
@@ -459,6 +466,7 @@ ifeq ($(uname_S),NetBSD)
endif
ifeq ($(uname_S),AIX)
NO_STRCASESTR=YesPlease
+ NO_MEMMEM = YesPlease
NO_STRLCPY = YesPlease
NEEDS_LIBICONV=YesPlease
endif
@@ -470,6 +478,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
@@ -664,6 +673,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
ifdef THREADED_DELTA_SEARCH
BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH
diff --git a/RelNotes b/RelNotes
index 0de5e663a2..46308cee0b 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.5.3.txt \ No newline at end of file
+Documentation/RelNotes-1.5.4.txt \ No newline at end of file
diff --git a/archive-tar.c b/archive-tar.c
index 66fe3e375b..c0d95dab0d 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -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)
@@ -285,7 +286,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));
}
@@ -304,6 +306,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);
diff --git a/archive-zip.c b/archive-zip.c
index 444e1623db..f63dff3834 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -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);
diff --git a/archive.h b/archive.h
index 6838dc788f..5791e657e9 100644
--- a/archive.h
+++ b/archive.h
@@ -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 */
diff --git a/builtin-add.c b/builtin-add.c
index 105a9f0e1f..3d8b8b4f89 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -95,9 +95,10 @@ static void update_callback(struct diff_queue_struct *q,
const char *path = p->one->path;
switch (p->status) {
default:
- die("unexpacted diff status %c", p->status);
+ die("unexpected diff status %c", p->status);
case DIFF_STATUS_UNMERGED:
case DIFF_STATUS_MODIFIED:
+ case DIFF_STATUS_TYPE_CHANGED:
add_file_to_cache(path, verbose);
break;
case DIFF_STATUS_DELETED:
diff --git a/builtin-apply.c b/builtin-apply.c
index 25b1447901..976ec77041 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -1514,7 +1514,8 @@ static int find_offset(const char *buf, unsigned long size, const char *fragment
}
/* Exact line number? */
- if (!memcmp(buf + start, fragment, fragsize))
+ if ((start + fragsize <= size) &&
+ !memcmp(buf + start, fragment, fragsize))
return start;
/*
diff --git a/builtin-archive.c b/builtin-archive.c
index 187491bc17..a90c65ce54 100644
--- a/builtin-archive.c
+++ b/builtin-archive.c
@@ -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;
}
diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c
index e2f8ede9ae..24c7e6f7db 100644
--- a/builtin-fetch--tool.c
+++ b/builtin-fetch--tool.c
@@ -31,24 +31,19 @@ static void show_new(enum object_type type, unsigned char *sha1_new)
find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
}
-static int update_ref(const char *action,
+static int update_ref_env(const char *action,
const char *refname,
unsigned char *sha1,
unsigned char *oldval)
{
char msg[1024];
char *rla = getenv("GIT_REFLOG_ACTION");
- static struct ref_lock *lock;
if (!rla)
rla = "(reflog update)";
- snprintf(msg, sizeof(msg), "%s: %s", rla, action);
- lock = lock_any_ref_for_update(refname, oldval, 0);
- if (!lock)
- return 1;
- if (write_ref_sha1(lock, sha1, msg) < 0)
- return 1;
- return 0;
+ if (snprintf(msg, sizeof(msg), "%s: %s", rla, action) >= sizeof(msg))
+ warning("reflog message too long: %.*s...", 50, msg);
+ return update_ref(msg, refname, sha1, oldval, 0, QUIET_ON_ERR);
}
static int update_local_ref(const char *name,
@@ -88,7 +83,7 @@ static int update_local_ref(const char *name,
fprintf(stderr, "* %s: storing %s\n",
name, note);
show_new(type, sha1_new);
- return update_ref(msg, name, sha1_new, NULL);
+ return update_ref_env(msg, name, sha1_new, NULL);
}
if (!hashcmp(sha1_old, sha1_new)) {
@@ -102,7 +97,7 @@ static int update_local_ref(const char *name,
if (!strncmp(name, "refs/tags/", 10)) {
fprintf(stderr, "* %s: updating with %s\n", name, note);
show_new(type, sha1_new);
- return update_ref("updating tag", name, sha1_new, NULL);
+ return update_ref_env("updating tag", name, sha1_new, NULL);
}
current = lookup_commit_reference(sha1_old);
@@ -117,7 +112,7 @@ static int update_local_ref(const char *name,
fprintf(stderr, "* %s: fast forward to %s\n",
name, note);
fprintf(stderr, " old..new: %s..%s\n", oldh, newh);
- return update_ref("fast forward", name, sha1_new, sha1_old);
+ return update_ref_env("fast forward", name, sha1_new, sha1_old);
}
if (!force) {
fprintf(stderr,
@@ -131,7 +126,7 @@ static int update_local_ref(const char *name,
"* %s: forcing update to non-fast forward %s\n",
name, note);
fprintf(stderr, " old...new: %s...%s\n", oldh, newh);
- return update_ref("forced-update", name, sha1_new, sha1_old);
+ return update_ref_env("forced-update", name, sha1_new, sha1_old);
}
static int append_fetch_head(FILE *fp,
diff --git a/builtin-grep.c b/builtin-grep.c
index e13cb31f2b..c7b45c4d58 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -187,6 +187,78 @@ static int exec_grep(int argc, const char **argv)
else die("maximum number of args exceeded"); \
} while (0)
+/*
+ * If you send a singleton filename to grep, it does not give
+ * the name of the file. GNU grep has "-H" but we would want
+ * that behaviour in a portable way.
+ *
+ * So we keep two pathnames in argv buffer unsent to grep in
+ * the main loop if we need to do more than one grep.
+ */
+static int flush_grep(struct grep_opt *opt,
+ int argc, int arg0, const char **argv, int *kept)
+{
+ int status;
+ int count = argc - arg0;
+ const char *kept_0 = NULL;
+
+ if (count <= 2) {
+ /*
+ * Because we keep at least 2 paths in the call from
+ * the main loop (i.e. kept != NULL), and MAXARGS is
+ * far greater than 2, this usually is a call to
+ * conclude the grep. However, the user could attempt
+ * to overflow the argv buffer by giving too many
+ * options to leave very small number of real
+ * arguments even for the call in the main loop.
+ */
+ if (kept)
+ die("insanely many options to grep");
+
+ /*
+ * If we have two or more paths, we do not have to do
+ * anything special, but we need to push /dev/null to
+ * get "-H" behaviour of GNU grep portably but when we
+ * are not doing "-l" nor "-L" nor "-c".
+ */
+ if (count == 1 &&
+ !opt->name_only &&
+ !opt->unmatch_name_only &&
+ !opt->count) {
+ argv[argc++] = "/dev/null";
+ argv[argc] = NULL;
+ }
+ }
+
+ else if (kept) {
+ /*
+ * Called because we found many paths and haven't finished
+ * iterating over the cache yet. We keep two paths
+ * for the concluding call. argv[argc-2] and argv[argc-1]
+ * has the last two paths, so save the first one away,
+ * replace it with NULL while sending the list to grep,
+ * and recover them after we are done.
+ */
+ *kept = 2;
+ kept_0 = argv[argc-2];
+ argv[argc-2] = NULL;
+ argc -= 2;
+ }
+
+ status = exec_grep(argc, argv);
+
+ if (kept_0) {
+ /*
+ * Then recover them. Now the last arg is beyond the
+ * terminating NULL which is at argc, and the second
+ * from the last is what we saved away in kept_0
+ */
+ argv[arg0++] = kept_0;
+ argv[arg0] = argv[argc+1];
+ }
+ return status;
+}
+
static int external_grep(struct grep_opt *opt, const char **paths, int cached)
{
int i, nr, argc, hit, len, status;
@@ -253,22 +325,12 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
push_arg(p->pattern);
}
- /*
- * To make sure we get the header printed out when we want it,
- * add /dev/null to the paths to grep. This is unnecessary
- * (and wrong) with "-l" or "-L", which always print out the
- * name anyway.
- *
- * GNU grep has "-H", but this is portable.
- */
- if (!opt->name_only && !opt->unmatch_name_only)
- push_arg("/dev/null");
-
hit = 0;
argc = nr;
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
char *name;
+ int kept;
if (!S_ISREG(ntohl(ce->ce_mode)))
continue;
if (!pathspec_matches(paths, ce->name))
@@ -283,10 +345,10 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
argv[argc++] = name;
if (argc < MAXARGS && !ce_stage(ce))
continue;
- status = exec_grep(argc, argv);
+ status = flush_grep(opt, argc, nr, argv, &kept);
if (0 < status)
hit = 1;
- argc = nr;
+ argc = nr + kept;
if (ce_stage(ce)) {
do {
i++;
@@ -296,7 +358,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
}
}
if (argc > nr) {
- status = exec_grep(argc, argv);
+ status = flush_grep(opt, argc, nr, argv, NULL);
if (0 < status)
hit = 1;
}
diff --git a/builtin-log.c b/builtin-log.c
index fa81c25920..c6cc3aef52 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -437,6 +437,34 @@ static void gen_message_id(char *dest, unsigned int length, char *base)
(int)(email_end - email_start - 1), email_start + 1);
}
+static const char *clean_message_id(const char *msg_id)
+{
+ char ch;
+ const char *a, *z, *m;
+ char *n;
+ size_t len;
+
+ m = msg_id;
+ while ((ch = *m) && (isspace(ch) || (ch == '<')))
+ m++;
+ a = m;
+ z = NULL;
+ while ((ch = *m)) {
+ if (!isspace(ch) && (ch != '>'))
+ z = m;
+ m++;
+ }
+ if (!z)
+ die("insane in-reply-to: %s", msg_id);
+ if (++z == m)
+ return a;
+ len = z - a;
+ n = xmalloc(len + 1);
+ memcpy(n, a, len);
+ n[len] = 0;
+ return n;
+}
+
int cmd_format_patch(int argc, const char **argv, const char *prefix)
{
struct commit *commit;
@@ -625,7 +653,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (numbered)
rev.total = total + start_number - 1;
rev.add_signoff = add_signoff;
- rev.ref_message_id = in_reply_to;
+ if (in_reply_to)
+ rev.ref_message_id = clean_message_id(in_reply_to);
while (0 <= --nr) {
int shown;
commit = list[nr];
diff --git a/builtin-ls-files.c b/builtin-ls-files.c
index cce17b5ced..6c1db86e80 100644
--- a/builtin-ls-files.c
+++ b/builtin-ls-files.c
@@ -9,6 +9,7 @@
#include "quote.h"
#include "dir.h"
#include "builtin.h"
+#include "tree.h"
static int abbrev;
static int show_deleted;
@@ -26,6 +27,7 @@ static int prefix_offset;
static const char **pathspec;
static int error_unmatch;
static char *ps_matched;
+static const char *with_tree;
static const char *tag_cached = "";
static const char *tag_unmerged = "";
@@ -247,6 +249,8 @@ static void show_files(struct dir_struct *dir, const char *prefix)
continue;
if (show_unmerged && !ce_stage(ce))
continue;
+ if (ce->ce_flags & htons(CE_UPDATE))
+ continue;
show_ce_entry(ce_stage(ce) ? tag_unmerged : tag_cached, ce);
}
}
@@ -332,6 +336,67 @@ static const char *verify_pathspec(const char *prefix)
return real_prefix;
}
+/*
+ * Read the tree specified with --with-tree option
+ * (typically, HEAD) into stage #1 and then
+ * squash them down to stage #0. This is used for
+ * --error-unmatch to list and check the path patterns
+ * that were given from the command line. We are not
+ * going to write this index out.
+ */
+static void overlay_tree(const char *tree_name, const char *prefix)
+{
+ struct tree *tree;
+ unsigned char sha1[20];
+ const char **match;
+ struct cache_entry *last_stage0 = NULL;
+ int i;
+
+ if (get_sha1(tree_name, sha1))
+ die("tree-ish %s not found.", tree_name);
+ tree = parse_tree_indirect(sha1);
+ if (!tree)
+ die("bad tree-ish %s", tree_name);
+
+ /* Hoist the unmerged entries up to stage #3 to make room */
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ if (!ce_stage(ce))
+ continue;
+ ce->ce_flags |= htons(CE_STAGEMASK);
+ }
+
+ if (prefix) {
+ static const char *(matchbuf[2]);
+ matchbuf[0] = prefix;
+ matchbuf [1] = NULL;
+ match = matchbuf;
+ } else
+ match = NULL;
+ if (read_tree(tree, 1, match))
+ die("unable to read tree entries %s", tree_name);
+
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ switch (ce_stage(ce)) {
+ case 0:
+ last_stage0 = ce;
+ /* fallthru */
+ default:
+ continue;
+ case 1:
+ /*
+ * If there is stage #0 entry for this, we do not
+ * need to show it. We use CE_UPDATE bit to mark
+ * such an entry.
+ */
+ if (last_stage0 &&
+ !strcmp(last_stage0->name, ce->name))
+ ce->ce_flags |= htons(CE_UPDATE);
+ }
+ }
+}
+
static const char ls_files_usage[] =
"git-ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
"[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
@@ -452,6 +517,10 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
error_unmatch = 1;
continue;
}
+ if (!prefixcmp(arg, "--with-tree=")) {
+ with_tree = arg + 12;
+ continue;
+ }
if (!prefixcmp(arg, "--abbrev=")) {
abbrev = strtoul(arg+9, NULL, 10);
if (abbrev && abbrev < MINIMUM_ABBREV)
@@ -503,6 +572,15 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
read_cache();
if (prefix)
prune_cache(prefix);
+ if (with_tree) {
+ /*
+ * Basic sanity check; show-stages and show-unmerged
+ * would not make any sense with this option.
+ */
+ if (show_stage || show_unmerged)
+ die("ls-files --with-tree is incompatible with -s or -u");
+ overlay_tree(with_tree, prefix);
+ }
show_files(&dir, prefix);
if (ps_matched) {
diff --git a/builtin-push.c b/builtin-push.c
index 2612f07f74..88c5024da7 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -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;
diff --git a/builtin-tag.c b/builtin-tag.c
index 348919cff8..3a9d2eea71 100644
--- a/builtin-tag.c
+++ b/builtin-tag.c
@@ -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)
diff --git a/builtin-update-ref.c b/builtin-update-ref.c
index 8339cf19e2..fe1f74c9f3 100644
--- a/builtin-update-ref.c
+++ b/builtin-update-ref.c
@@ -8,7 +8,6 @@ static const char git_update_ref_usage[] =
int cmd_update_ref(int argc, const char **argv, const char *prefix)
{
const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
- struct ref_lock *lock;
unsigned char sha1[20], oldsha1[20];
int i, delete, ref_flags;
@@ -62,10 +61,6 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
if (oldval && *oldval && get_sha1(oldval, oldsha1))
die("%s: not a valid old SHA1", oldval);
- lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, ref_flags);
- if (!lock)
- die("%s: cannot lock the ref", refname);
- if (write_ref_sha1(lock, sha1, msg) < 0)
- die("%s: cannot update the ref", refname);
- return 0;
+ return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
+ ref_flags, DIE_ON_ERR);
}
diff --git a/cache.h b/cache.h
index 70abbd59bf..493983cbae 100644
--- a/cache.h
+++ b/cache.h
@@ -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;
diff --git a/commit.c b/commit.c
index dc5a0643f3..99f65cee0e 100644
--- a/commit.c
+++ b/commit.c
@@ -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
diff --git a/commit.h b/commit.h
index 467872eeca..a8d76616d2 100644
--- a/commit.h
+++ b/commit.h
@@ -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
diff --git a/compat/memmem.c b/compat/memmem.c
new file mode 100644
index 0000000000..cd0d877364
--- /dev/null
+++ b/compat/memmem.c
@@ -0,0 +1,29 @@
+#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;
+}
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index 280557ecd4..2d77fd47ec 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -97,6 +97,21 @@ if there is already one that displays the same directory."
:group 'git
:type 'string)
+(defcustom git-show-uptodate nil
+ "Whether to display up-to-date files."
+ :group 'git
+ :type 'boolean)
+
+(defcustom git-show-ignored nil
+ "Whether to display ignored files."
+ :group 'git
+ :type 'boolean)
+
+(defcustom git-show-unknown t
+ "Whether to display unknown files."
+ :group 'git
+ :type 'boolean)
+
(defface git-status-face
'((((class color) (background light)) (:foreground "purple"))
@@ -479,6 +494,27 @@ and returns the process output as a string."
(setf (git-fileinfo->orig-name info) nil)
(setf (git-fileinfo->needs-refresh info) t))))
+(defun git-set-filenames-state (status files state)
+ "Set the state of a list of named files."
+ (when files
+ (setq files (sort files #'string-lessp))
+ (let ((file (pop files))
+ (node (ewoc-nth status 0)))
+ (while (and file node)
+ (let ((info (ewoc-data node)))
+ (cond ((string-lessp (git-fileinfo->name info) file)
+ (setq node (ewoc-next status node)))
+ ((string-equal (git-fileinfo->name info) file)
+ (unless (eq (git-fileinfo->state info) state)
+ (setf (git-fileinfo->state info) state)
+ (setf (git-fileinfo->rename-state info) nil)
+ (setf (git-fileinfo->orig-name info) nil)
+ (setf (git-fileinfo->needs-refresh info) t))
+ (setq file (pop files)))
+ (t (setq file (pop files)))))))
+ (unless state ;; delete files whose state has been set to nil
+ (ewoc-filter status (lambda (info) (git-fileinfo->state info))))))
+
(defun git-state-code (code)
"Convert from a string to a added/deleted/modified state."
(case (string-to-char code)
@@ -532,19 +568,36 @@ and returns the process output as a string."
" " (git-escape-file-name (git-fileinfo->name info))
(git-rename-as-string info))))
-(defun git-insert-fileinfo (status info &optional refresh)
- "Insert INFO in the status buffer, optionally refreshing an existing one."
- (let ((node (and refresh
- (git-find-status-file status (git-fileinfo->name info)))))
- (setf (git-fileinfo->needs-refresh info) t)
- (when node ;preserve the marked flag
- (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node))))
- (if node (setf (ewoc-data node) info) (ewoc-enter-last status info))))
+(defun git-insert-info-list (status infolist)
+ "Insert a list of file infos in the status buffer, replacing existing ones if any."
+ (setq infolist (sort infolist
+ (lambda (info1 info2)
+ (string-lessp (git-fileinfo->name info1)
+ (git-fileinfo->name info2)))))
+ (let ((info (pop infolist))
+ (node (ewoc-nth status 0)))
+ (while info
+ (setf (git-fileinfo->needs-refresh info) t)
+ (cond ((not node)
+ (ewoc-enter-last status info)
+ (setq info (pop infolist)))
+ ((string-lessp (git-fileinfo->name (ewoc-data node))
+ (git-fileinfo->name info))
+ (setq node (ewoc-next status node)))
+ ((string-equal (git-fileinfo->name (ewoc-data node))
+ (git-fileinfo->name info))
+ ;; preserve the marked flag
+ (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node)))
+ (setf (ewoc-data node) info)
+ (setq info (pop infolist)))
+ (t
+ (ewoc-enter-before status node info)
+ (setq info (pop infolist)))))))
(defun git-run-diff-index (status files)
"Run git-diff-index on FILES and parse the results into STATUS.
Return the list of files that haven't been handled."
- (let ((refresh files))
+ (let (infolist)
(with-temp-buffer
(apply #'git-run-command t nil "diff-index" "-z" "-M" "HEAD" "--" files)
(goto-char (point-min))
@@ -558,13 +611,14 @@ Return the list of files that haven't been handled."
(new-name (match-string 8)))
(if new-name ; copy or rename
(if (eq ?C (string-to-char state))
- (git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) refresh)
- (git-insert-fileinfo status (git-create-fileinfo 'deleted name 0 0 'rename new-name) refresh)
- (git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'rename name)) refresh)
- (git-insert-fileinfo status (git-create-fileinfo (git-state-code state) name old-perm new-perm) refresh))
+ (push (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) infolist)
+ (push (git-create-fileinfo 'deleted name 0 0 'rename new-name) infolist)
+ (push (git-create-fileinfo 'added new-name old-perm new-perm 'rename name) infolist))
+ (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist))
(setq files (delete name files))
- (when new-name (setq files (delete new-name files)))))))
- files)
+ (when new-name (setq files (delete new-name files))))))
+ (git-insert-info-list status infolist)
+ files))
(defun git-find-status-file (status file)
"Find a given file in the status ewoc and return its node."
@@ -576,16 +630,16 @@ Return the list of files that haven't been handled."
(defun git-run-ls-files (status files default-state &rest options)
"Run git-ls-files on FILES and parse the results into STATUS.
Return the list of files that haven't been handled."
- (let ((refresh files))
+ (let (infolist)
(with-temp-buffer
- (apply #'git-run-command t nil "ls-files" "-z" "-t" (append options (list "--") files))
+ (apply #'git-run-command t nil "ls-files" "-z" (append options (list "--") files))
(goto-char (point-min))
- (while (re-search-forward "\\([HMRCK?]\\) \\([^\0]*\\)\0" nil t 1)
- (let ((state (match-string 1))
- (name (match-string 2)))
- (git-insert-fileinfo status (git-create-fileinfo (or (git-state-code state) default-state) name) refresh)
- (setq files (delete name files))))))
- files)
+ (while (re-search-forward "\\([^\0]*\\)\0" nil t 1)
+ (let ((name (match-string 1)))
+ (push (git-create-fileinfo default-state name) infolist)
+ (setq files (delete name files)))))
+ (git-insert-info-list status infolist)
+ files))
(defun git-run-ls-unmerged (status files)
"Run git-ls-files -u on FILES and parse the results into STATUS."
@@ -594,9 +648,8 @@ Return the list of files that haven't been handled."
(goto-char (point-min))
(let (unmerged-files)
(while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t)
- (let ((node (git-find-status-file status (match-string 1))))
- (when node (push (ewoc-data node) unmerged-files))))
- (git-set-files-state unmerged-files 'unmerged))))
+ (push (match-string 1) unmerged-files))
+ (git-set-filenames-state status unmerged-files 'unmerged))))
(defun git-get-exclude-files ()
"Get the list of exclude files to pass to git-ls-files."
@@ -608,34 +661,30 @@ Return the list of files that haven't been handled."
(push config files))
files))
+(defun git-run-ls-files-with-excludes (status files default-state &rest options)
+ "Run git-ls-files on FILES with appropriate --exclude-from options."
+ (let ((exclude-files (git-get-exclude-files)))
+ (apply #'git-run-ls-files status files default-state
+ (concat "--exclude-per-directory=" git-per-dir-ignore-file)
+ (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files)))))
+
(defun git-update-status-files (files &optional default-state)
"Update the status of FILES from the index."
(unless git-status (error "Not in git-status buffer."))
- (let* ((status git-status)
- (remaining-files
+ (unless files
+ (when git-show-uptodate (git-run-ls-files git-status nil 'uptodate "-c")))
+ (let* ((remaining-files
(if (git-empty-db-p) ; we need some special handling for an empty db
- (git-run-ls-files status files 'added "-c")
- (git-run-diff-index status files))))
- (git-run-ls-unmerged status files)
- (when (or (not files) remaining-files)
- (let ((exclude-files (git-get-exclude-files)))
- (setq remaining-files (apply #'git-run-ls-files status remaining-files 'unknown "-o"
- (concat "--exclude-per-directory=" git-per-dir-ignore-file)
- (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files)))))
- ; mark remaining files with the default state (or remove them if nil)
- (when remaining-files
- (if default-state
- (ewoc-map (lambda (info)
- (when (member (git-fileinfo->name info) remaining-files)
- (git-set-files-state (list info) default-state))
- nil)
- status)
- (ewoc-filter status
- (lambda (info files)
- (not (member (git-fileinfo->name info) files)))
- remaining-files)))
+ (git-run-ls-files git-status files 'added "-c")
+ (git-run-diff-index git-status files))))
+ (git-run-ls-unmerged git-status files)
+ (when (or remaining-files (and git-show-unknown (not files)))
+ (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'unknown "-o")))
+ (when (or remaining-files (and git-show-ignored (not files)))
+ (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'ignored "-o" "-i")))
+ (git-set-filenames-state git-status remaining-files default-state)
(git-refresh-files)
- (git-refresh-ewoc-hf status)))
+ (git-refresh-ewoc-hf git-status)))
(defun git-marked-files ()
"Return a list of all marked files, or if none a list containing just the file at cursor position."
@@ -853,7 +902,7 @@ Return the list of files that haven't been handled."
(defun git-add-file ()
"Add marked file(s) to the index cache."
(interactive)
- (let ((files (git-get-filenames (git-marked-files-state 'unknown))))
+ (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored))))
(unless files
(push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
(apply #'git-run-command nil nil "update-index" "--add" "--" files)
@@ -871,7 +920,7 @@ Return the list of files that haven't been handled."
(defun git-remove-file ()
"Remove the marked file(s)."
(interactive)
- (let ((files (git-get-filenames (git-marked-files-state 'added 'modified 'unknown 'uptodate))))
+ (let ((files (git-get-filenames (git-marked-files-state 'added 'modified 'unknown 'uptodate 'ignored))))
(unless files
(push (file-relative-name (read-file-name "File to remove: " nil nil t)) files))
(if (yes-or-no-p
@@ -916,11 +965,41 @@ Return the list of files that haven't been handled."
(interactive)
(ewoc-filter git-status
(lambda (info)
- (not (or (eq (git-fileinfo->state info) 'ignored)
- (eq (git-fileinfo->state info) 'uptodate)))))
+ (case (git-fileinfo->state info)
+ ('ignored git-show-ignored)
+ ('uptodate git-show-uptodate)
+ ('unknown git-show-unknown)
+ (t t))))
(unless (ewoc-nth git-status 0) ; refresh header if list is empty
(git-refresh-ewoc-hf git-status)))
+(defun git-toggle-show-uptodate ()
+ "Toogle the option for showing up-to-date files."
+ (interactive)
+ (if (setq git-show-uptodate (not git-show-uptodate))
+ (git-refresh-status)
+ (git-remove-handled)))
+
+(defun git-toggle-show-ignored ()
+ "Toogle the option for showing ignored files."
+ (interactive)
+ (if (setq git-show-ignored (not git-show-ignored))
+ (progn
+ (git-run-ls-files-with-excludes git-status nil 'ignored "-o" "-i")
+ (git-refresh-files)
+ (git-refresh-ewoc-hf git-status))
+ (git-remove-handled)))
+
+(defun git-toggle-show-unknown ()
+ "Toogle the option for showing unknown files."
+ (interactive)
+ (if (setq git-show-unknown (not git-show-unknown))
+ (progn
+ (git-run-ls-files-with-excludes git-status nil 'unknown "-o")
+ (git-refresh-files)
+ (git-refresh-ewoc-hf git-status))
+ (git-remove-handled)))
+
(defun git-setup-diff-buffer (buffer)
"Setup a buffer for displaying a diff."
(let ((dir default-directory))
@@ -1146,7 +1225,8 @@ Return the list of files that haven't been handled."
(unless git-status-mode-map
(let ((map (make-keymap))
- (diff-map (make-sparse-keymap)))
+ (diff-map (make-sparse-keymap))
+ (toggle-map (make-sparse-keymap)))
(suppress-keymap map)
(define-key map "?" 'git-help)
(define-key map "h" 'git-help)
@@ -1170,6 +1250,7 @@ Return the list of files that haven't been handled."
(define-key map "q" 'git-status-quit)
(define-key map "r" 'git-remove-file)
(define-key map "R" 'git-resolve-file)
+ (define-key map "t" toggle-map)
(define-key map "T" 'git-toggle-all-marks)
(define-key map "u" 'git-unmark-file)
(define-key map "U" 'git-revert-file)
@@ -1186,6 +1267,11 @@ Return the list of files that haven't been handled."
(define-key diff-map "h" 'git-diff-file-merge-head)
(define-key diff-map "m" 'git-diff-file-mine)
(define-key diff-map "o" 'git-diff-file-other)
+ ; the toggle submap
+ (define-key toggle-map "u" 'git-toggle-show-uptodate)
+ (define-key toggle-map "i" 'git-toggle-show-ignored)
+ (define-key toggle-map "k" 'git-toggle-show-unknown)
+ (define-key toggle-map "m" 'git-toggle-all-marks)
(setq git-status-mode-map map)))
;; git mode should only run in the *git status* buffer
@@ -1207,6 +1293,9 @@ Commands:
(let ((status (ewoc-create 'git-fileinfo-prettyprint "" "")))
(set (make-local-variable 'git-status) status))
(set (make-local-variable 'list-buffers-directory) default-directory)
+ (make-local-variable 'git-show-uptodate)
+ (make-local-variable 'git-show-ignored)
+ (make-local-variable 'git-show-unknown)
(run-hooks 'git-status-mode-hook)))
(defun git-find-status-buffer (dir)
diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index 55778c5775..adaaae6633 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -281,6 +281,19 @@ def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent
def originP4BranchesExist():
return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master")
+def p4ChangesForPaths(depotPaths, changeRange):
+ assert depotPaths
+ output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, changeRange)
+ for p in depotPaths]))
+
+ changes = []
+ for line in output:
+ changeNum = line.split(" ")[1]
+ changes.append(int(changeNum))
+
+ changes.sort()
+ return changes
+
class Command:
def __init__(self):
self.usage = "usage: %prog [options]"
@@ -664,9 +677,8 @@ class P4Submit(Command):
f.close();
os.chdir(self.clientPath)
- response = raw_input("Do you want to sync %s with p4 sync? [y]es/[n]o " % self.clientPath)
- if response == "y" or response == "yes":
- system("p4 sync ...")
+ print "Syncronizing p4 checkout..."
+ system("p4 sync ...")
if self.reset:
self.firstTime = True
@@ -705,10 +717,14 @@ class P4Submit(Command):
else:
print "All changes applied!"
os.chdir(self.oldWorkingDirectory)
- response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ")
+
+ sync = P4Sync()
+ sync.run([])
+
+ response = raw_input("Do you want to rebase current HEAD from Perforce now using git-p4 rebase? [y]es/[n]o ")
if response == "y" or response == "yes":
rebase = P4Rebase()
- rebase.run([])
+ rebase.rebase()
os.remove(self.configFile)
return True
@@ -1102,6 +1118,186 @@ class P4Sync(Command):
self.keepRepoPath = (d.has_key('options')
and ('keepRepoPath' in d['options']))
+ def gitRefForBranch(self, branch):
+ if branch == "main":
+ return self.refPrefix + "master"
+
+ if len(branch) <= 0:
+ return branch
+
+ return self.refPrefix + self.projectName + branch
+
+ def gitCommitByP4Change(self, ref, change):
+ if self.verbose:
+ print "looking in ref " + ref + " for change %s using bisect..." % change
+
+ earliestCommit = ""
+ latestCommit = parseRevision(ref)
+
+ while True:
+ if self.verbose:
+ print "trying: earliest %s latest %s" % (earliestCommit, latestCommit)
+ next = read_pipe("git rev-list --bisect %s %s" % (latestCommit, earliestCommit)).strip()
+ if len(next) == 0:
+ if self.verbose:
+ print "argh"
+ return ""
+ log = extractLogMessageFromGitCommit(next)
+ settings = extractSettingsGitLog(log)
+ currentChange = int(settings['change'])
+ if self.verbose:
+ print "current change %s" % currentChange
+
+ if currentChange == change:
+ if self.verbose:
+ print "found %s" % next
+ return next
+
+ if currentChange < change:
+ earliestCommit = "^%s" % next
+ else:
+ latestCommit = "%s" % next
+
+ return ""
+
+ def importNewBranch(self, branch, maxChange):
+ # make fast-import flush all changes to disk and update the refs using the checkpoint
+ # command so that we can try to find the branch parent in the git history
+ self.gitStream.write("checkpoint\n\n");
+ self.gitStream.flush();
+ branchPrefix = self.depotPaths[0] + branch + "/"
+ range = "@1,%s" % maxChange
+ #print "prefix" + branchPrefix
+ changes = p4ChangesForPaths([branchPrefix], range)
+ if len(changes) <= 0:
+ return False
+ firstChange = changes[0]
+ #print "first change in branch: %s" % firstChange
+ sourceBranch = self.knownBranches[branch]
+ sourceDepotPath = self.depotPaths[0] + sourceBranch
+ sourceRef = self.gitRefForBranch(sourceBranch)
+ #print "source " + sourceBranch
+
+ branchParentChange = int(p4Cmd("changes -m 1 %s...@1,%s" % (sourceDepotPath, firstChange))["change"])
+ #print "branch parent: %s" % branchParentChange
+ gitParent = self.gitCommitByP4Change(sourceRef, branchParentChange)
+ if len(gitParent) > 0:
+ self.initialParents[self.gitRefForBranch(branch)] = gitParent
+ #print "parent git commit: %s" % gitParent
+
+ self.importChanges(changes)
+ return True
+
+ def importChanges(self, changes):
+ cnt = 1
+ for change in changes:
+ description = p4Cmd("describe %s" % change)
+ self.updateOptionDict(description)
+
+ if not self.silent:
+ sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
+ sys.stdout.flush()
+ cnt = cnt + 1
+
+ try:
+ if self.detectBranches:
+ branches = self.splitFilesIntoBranches(description)
+ for branch in branches.keys():
+ ## HACK --hwn
+ branchPrefix = self.depotPaths[0] + branch + "/"
+
+ parent = ""
+
+ filesForCommit = branches[branch]
+
+ if self.verbose:
+ print "branch is %s" % branch
+
+ self.updatedBranches.add(branch)
+
+ if branch not in self.createdBranches:
+ self.createdBranches.add(branch)
+ parent = self.knownBranches[branch]
+ if parent == branch:
+ parent = ""
+ else:
+ fullBranch = self.projectName + branch
+ if fullBranch not in self.p4BranchesInGit:
+ if not self.silent:
+ print("\n Importing new branch %s" % fullBranch);
+ if self.importNewBranch(branch, change - 1):
+ parent = ""
+ self.p4BranchesInGit.append(fullBranch)
+ if not self.silent:
+ print("\n Resuming with change %s" % change);
+
+ if self.verbose:
+ print "parent determined through known branches: %s" % parent
+
+ branch = self.gitRefForBranch(branch)
+ parent = self.gitRefForBranch(parent)
+
+ if self.verbose:
+ print "looking for initial parent for %s; current parent is %s" % (branch, parent)
+
+ if len(parent) == 0 and branch in self.initialParents:
+ parent = self.initialParents[branch]
+ del self.initialParents[branch]
+
+ self.commit(description, filesForCommit, branch, [branchPrefix], parent)
+ else:
+ files = self.extractFilesFromCommit(description)
+ self.commit(description, files, self.branch, self.depotPaths,
+ self.initialParent)
+ self.initialParent = ""
+ except IOError:
+ print self.gitError.read()
+ sys.exit(1)
+
+ def importHeadRevision(self, revision):
+ print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch)
+
+ details = { "user" : "git perforce import user", "time" : int(time.time()) }
+ details["desc"] = ("Initial import of %s from the state at revision %s"
+ % (' '.join(self.depotPaths), revision))
+ details["change"] = revision
+ newestRevision = 0
+
+ fileCnt = 0
+ for info in p4CmdList("files "
+ + ' '.join(["%s...%s"
+ % (p, revision)
+ for p in self.depotPaths])):
+
+ if info['code'] == 'error':
+ sys.stderr.write("p4 returned an error: %s\n"
+ % info['data'])
+ sys.exit(1)
+
+
+ change = int(info["change"])
+ if change > newestRevision:
+ newestRevision = change
+
+ if info["action"] == "delete":
+ # don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
+ #fileCnt = fileCnt + 1
+ continue
+
+ for prop in ["depotFile", "rev", "action", "type" ]:
+ details["%s%s" % (prop, fileCnt)] = info[prop]
+
+ fileCnt = fileCnt + 1
+
+ details["change"] = newestRevision
+ self.updateOptionDict(details)
+ try:
+ self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths)
+ except IOError:
+ print "IO error with git fast-import. Is your git version recent enough?"
+ print self.gitError.read()
+
+
def run(self, args):
self.depotPaths = []
self.changeRange = ""
@@ -1199,7 +1395,7 @@ class P4Sync(Command):
self.depotPaths = sorted(args)
- self.revision = ""
+ revision = ""
self.users = {}
newPaths = []
@@ -1210,15 +1406,15 @@ class P4Sync(Command):
if self.changeRange == "@all":
self.changeRange = ""
elif ',' not in self.changeRange:
- self.revision = self.changeRange
+ revision = self.changeRange
self.changeRange = ""
p = p[:atIdx]
elif p.find("#") != -1:
hashIdx = p.index("#")
- self.revision = p[hashIdx:]
+ revision = p[hashIdx:]
p = p[:hashIdx]
elif self.previousDepotPaths == []:
- self.revision = "#head"
+ revision = "#head"
p = re.sub ("\.\.\.$", "", p)
if not p.endswith("/"):
@@ -1259,49 +1455,8 @@ class P4Sync(Command):
self.gitStream = importProcess.stdin
self.gitError = importProcess.stderr
- if self.revision:
- print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), self.revision, self.branch)
-
- details = { "user" : "git perforce import user", "time" : int(time.time()) }
- details["desc"] = ("Initial import of %s from the state at revision %s"
- % (' '.join(self.depotPaths), self.revision))
- details["change"] = self.revision
- newestRevision = 0
-
- fileCnt = 0
- for info in p4CmdList("files "
- + ' '.join(["%s...%s"
- % (p, self.revision)
- for p in self.depotPaths])):
-
- if info['code'] == 'error':
- sys.stderr.write("p4 returned an error: %s\n"
- % info['data'])
- sys.exit(1)
-
-
- change = int(info["change"])
- if change > newestRevision:
- newestRevision = change
-
- if info["action"] == "delete":
- # don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
- #fileCnt = fileCnt + 1
- continue
-
- for prop in ["depotFile", "rev", "action", "type" ]:
- details["%s%s" % (prop, fileCnt)] = info[prop]
-
- fileCnt = fileCnt + 1
-
- details["change"] = newestRevision
- self.updateOptionDict(details)
- try:
- self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths)
- except IOError:
- print "IO error with git fast-import. Is your git version recent enough?"
- print self.gitError.read()
-
+ if revision:
+ self.importHeadRevision(revision)
else:
changes = []
@@ -1319,15 +1474,7 @@ class P4Sync(Command):
if self.verbose:
print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
self.changeRange)
- assert self.depotPaths
- output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, self.changeRange)
- for p in self.depotPaths]))
-
- for line in output:
- changeNum = line.split(" ")[1]
- changes.append(int(changeNum))
-
- changes.sort()
+ changes = p4ChangesForPaths(self.depotPaths, self.changeRange)
if len(self.maxChanges) > 0:
changes = changes[:min(int(self.maxChanges), len(changes))]
@@ -1342,74 +1489,7 @@ class P4Sync(Command):
self.updatedBranches = set()
- cnt = 1
- for change in changes:
- description = p4Cmd("describe %s" % change)
- self.updateOptionDict(description)
-
- if not self.silent:
- sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
- sys.stdout.flush()
- cnt = cnt + 1
-
- try:
- if self.detectBranches:
- branches = self.splitFilesIntoBranches(description)
- for branch in branches.keys():
- ## HACK --hwn
- branchPrefix = self.depotPaths[0] + branch + "/"
-
- parent = ""
-
- filesForCommit = branches[branch]
-
- if self.verbose:
- print "branch is %s" % branch
-
- self.updatedBranches.add(branch)
-
- if branch not in self.createdBranches:
- self.createdBranches.add(branch)
- parent = self.knownBranches[branch]
- if parent == branch:
- parent = ""
- elif self.verbose:
- print "parent determined through known branches: %s" % parent
-
- # main branch? use master
- if branch == "main":
- branch = "master"
- else:
-
- ## FIXME
- branch = self.projectName + branch
-
- if parent == "main":
- parent = "master"
- elif len(parent) > 0:
- ## FIXME
- parent = self.projectName + parent
-
- branch = self.refPrefix + branch
- if len(parent) > 0:
- parent = self.refPrefix + parent
-
- if self.verbose:
- print "looking for initial parent for %s; current parent is %s" % (branch, parent)
-
- if len(parent) == 0 and branch in self.initialParents:
- parent = self.initialParents[branch]
- del self.initialParents[branch]
-
- self.commit(description, filesForCommit, branch, [branchPrefix], parent)
- else:
- files = self.extractFilesFromCommit(description)
- self.commit(description, files, self.branch, self.depotPaths,
- self.initialParent)
- self.initialParent = ""
- except IOError:
- print self.gitError.read()
- sys.exit(1)
+ self.importChanges(changes)
if not self.silent:
print ""
@@ -1419,7 +1499,6 @@ class P4Sync(Command):
sys.stdout.write("%s " % b)
sys.stdout.write("\n")
-
self.gitStream.close()
if importProcess.wait() != 0:
die("fast-import failed: %s" % self.gitError.read())
@@ -1440,6 +1519,9 @@ class P4Rebase(Command):
sync = P4Sync()
sync.run([])
+ return self.rebase()
+
+ def rebase(self):
[upstream, settings] = findUpstreamBranchPoint()
if len(upstream) == 0:
die("Cannot find upstream branchpoint for rebase")
diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir
index c6e154a84f..2838546d16 100755
--- a/contrib/workdir/git-new-workdir
+++ b/contrib/workdir/git-new-workdir
@@ -48,6 +48,12 @@ then
"a complete repository."
fi
+# don't recreate a workdir over an existing repository
+if test -e "$new_workdir"
+then
+ die "destination directory '$new_workdir' already exists."
+fi
+
# make sure the the links use full paths
git_dir=$(cd "$git_dir"; pwd)
diff --git a/convert.c b/convert.c
index 21908b1039..d77c8eb8b2 100644
--- a/convert.c
+++ b/convert.c
@@ -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;
-}
diff --git a/diff-delta.c b/diff-delta.c
index 0dde2f2dc0..9e440a9299 100644
--- a/diff-delta.c
+++ b/diff-delta.c
@@ -115,7 +115,11 @@ static const unsigned int U[256] = {
struct index_entry {
const unsigned char *ptr;
unsigned int val;
- struct index_entry *next;
+};
+
+struct unpacked_index_entry {
+ struct index_entry entry;
+ struct unpacked_index_entry *next;
};
struct delta_index {
@@ -131,7 +135,8 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
unsigned int i, hsize, hmask, entries, prev_val, *hash_count;
const unsigned char *data, *buffer = buf;
struct delta_index *index;
- struct index_entry *entry, **hash;
+ struct unpacked_index_entry *entry, **hash;
+ struct index_entry *packed_entry, **packed_hash;
void *mem;
unsigned long memsize;
@@ -148,28 +153,21 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
hmask = hsize - 1;
/* allocate lookup index */
- memsize = sizeof(*index) +
- sizeof(*hash) * hsize +
+ memsize = sizeof(*hash) * hsize +
sizeof(*entry) * entries;
mem = malloc(memsize);
if (!mem)
return NULL;
- index = mem;
- mem = index + 1;
hash = mem;
mem = hash + hsize;
entry = mem;
- index->memsize = memsize;
- index->src_buf = buf;
- index->src_size = bufsize;
- index->hash_mask = hmask;
memset(hash, 0, hsize * sizeof(*hash));
/* allocate an array to count hash entries */
hash_count = calloc(hsize, sizeof(*hash_count));
if (!hash_count) {
- free(index);
+ free(hash);
return NULL;
}
@@ -183,12 +181,13 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
val = ((val << 8) | data[i]) ^ T[val >> RABIN_SHIFT];
if (val == prev_val) {
/* keep the lowest of consecutive identical blocks */
- entry[-1].ptr = data + RABIN_WINDOW;
+ entry[-1].entry.ptr = data + RABIN_WINDOW;
+ --entries;
} else {
prev_val = val;
i = val & hmask;
- entry->ptr = data + RABIN_WINDOW;
- entry->val = val;
+ entry->entry.ptr = data + RABIN_WINDOW;
+ entry->entry.val = val;
entry->next = hash[i];
hash[i] = entry++;
hash_count[i]++;
@@ -208,20 +207,84 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
* the reference buffer.
*/
for (i = 0; i < hsize; i++) {
- if (hash_count[i] < HASH_LIMIT)
+ int acc;
+
+ if (hash_count[i] <= HASH_LIMIT)
continue;
+
+ entries -= hash_count[i] - HASH_LIMIT;
+ /* We leave exactly HASH_LIMIT entries in the bucket */
+
entry = hash[i];
+ acc = 0;
do {
- struct index_entry *keep = entry;
- int skip = hash_count[i] / HASH_LIMIT;
- do {
- entry = entry->next;
- } while(--skip && entry);
- keep->next = entry;
- } while(entry);
+ acc += hash_count[i] - HASH_LIMIT;
+ if (acc > 0) {
+ struct unpacked_index_entry *keep = entry;
+ do {
+ entry = entry->next;
+ acc -= HASH_LIMIT;
+ } while (acc > 0);
+ keep->next = entry->next;
+ }
+ entry = entry->next;
+ } while (entry);
+
+ /* Assume that this loop is gone through exactly
+ * HASH_LIMIT times and is entered and left with
+ * acc==0. So the first statement in the loop
+ * contributes (hash_count[i]-HASH_LIMIT)*HASH_LIMIT
+ * to the accumulator, and the inner loop consequently
+ * is run (hash_count[i]-HASH_LIMIT) times, removing
+ * one element from the list each time. Since acc
+ * balances out to 0 at the final run, the inner loop
+ * body can't be left with entry==NULL. So we indeed
+ * encounter entry==NULL in the outer loop only.
+ */
}
free(hash_count);
+ /* Now create the packed index in array form rather than
+ * linked lists */
+
+ memsize = sizeof(*index)
+ + sizeof(*packed_hash) * (hsize+1)
+ + sizeof(*packed_entry) * entries;
+
+ mem = malloc(memsize);
+
+ if (!mem) {
+ free(hash);
+ return NULL;
+ }
+
+ index = mem;
+ index->memsize = memsize;
+ index->src_buf = buf;
+ index->src_size = bufsize;
+ index->hash_mask = hmask;
+
+ mem = index + 1;
+ packed_hash = mem;
+ mem = packed_hash + (hsize+1);
+ packed_entry = mem;
+
+ /* Coalesce all entries belonging to one linked list into
+ * consecutive array entries */
+
+ for (i = 0; i < hsize; i++) {
+ packed_hash[i] = packed_entry;
+ for (entry = hash[i]; entry; entry = entry->next)
+ *packed_entry++ = entry->entry;
+ }
+
+ /* Sentinel value to indicate the length of the last hash
+ * bucket */
+
+ packed_hash[hsize] = packed_entry;
+ assert(packed_entry - (struct index_entry *)mem == entries);
+ free(hash);
+
return index;
}
@@ -302,7 +365,7 @@ create_delta(const struct delta_index *index,
val ^= U[data[-RABIN_WINDOW]];
val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
i = val & index->hash_mask;
- for (entry = index->hash[i]; entry; entry = entry->next) {
+ for (entry = index->hash[i]; entry < index->hash[i+1]; entry++) {
const unsigned char *ref = entry->ptr;
const unsigned char *src = data;
unsigned int ref_size = ref_top - ref;
diff --git a/diff-lib.c b/diff-lib.c
index f5568c3b36..da5571302d 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -298,6 +298,8 @@ int setup_diff_no_index(struct rev_info *revs,
revs->diffopt.nr_paths = 2;
revs->diffopt.no_index = 1;
revs->max_count = -2;
+ if (diff_setup_done(&revs->diffopt) < 0)
+ die("diff_setup_done failed");
return 0;
}
diff --git a/diff.c b/diff.c
index 0d30d05263..0ee9ea1c1b 100644
--- a/diff.c
+++ b/diff.c
@@ -17,7 +17,7 @@
#endif
static int diff_detect_rename_default;
-static int diff_rename_limit_default = -1;
+static int diff_rename_limit_default = 100;
static int diff_use_color_default;
int diff_auto_refresh_index = 1;
@@ -3144,6 +3144,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;
@@ -3175,10 +3191,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 {
/*
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 6bde4396f2..41b35c3a9e 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -298,10 +298,25 @@ void diffcore_rename(struct diff_options *options)
else if (detect_rename == DIFF_DETECT_COPY)
register_rename_src(p->one, 1, p->score);
}
- if (rename_dst_nr == 0 || rename_src_nr == 0 ||
- (0 < rename_limit && rename_limit < rename_dst_nr))
+ if (rename_dst_nr == 0 || rename_src_nr == 0)
goto cleanup; /* nothing to do */
+ /*
+ * This basically does a test for the rename matrix not
+ * growing larger than a "rename_limit" square matrix, ie:
+ *
+ * rename_dst_nr * rename_src_nr > rename_limit * rename_limit
+ *
+ * but handles the potential overflow case specially (and we
+ * assume at least 32-bit integers)
+ */
+ if (rename_limit <= 0 || rename_limit > 32767)
+ rename_limit = 32767;
+ if (rename_dst_nr > rename_limit && rename_src_nr > rename_limit)
+ goto cleanup;
+ if (rename_dst_nr * rename_src_nr > rename_limit * rename_limit)
+ goto cleanup;
+
/* We really want to cull the candidates list early
* with cheap tests in order to avoid doing deltas.
* The first round matches up the up-to-date entries,
diff --git a/git-clone.sh b/git-clone.sh
index 18003ab4b3..5e582fe247 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -34,7 +34,11 @@ fi
http_fetch () {
# $1 = Remote, $2 = Local
- curl -nsfL $curl_extra_args "$1" >"$2"
+ curl -nsfL $curl_extra_args "$1" >"$2" ||
+ case $? in
+ 126|127) exit ;;
+ *) return $? ;;
+ esac
}
clone_dumb_http () {
diff --git a/git-commit.sh b/git-commit.sh
index 1d04f1ff31..bb113e858b 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -379,8 +379,11 @@ t,)
then
refuse_partial "Cannot do a partial commit during a merge."
fi
+
TMP_INDEX="$GIT_DIR/tmp-index$$"
- commit_only=`git ls-files --error-unmatch -- "$@"` || exit
+ W=
+ test -z "$initial_commit" && W=--with-tree=HEAD
+ commit_only=`git ls-files --error-unmatch $W -- "$@"` || exit
# Build a temporary index and update the real index
# the same way.
@@ -401,7 +404,7 @@ t,)
(
GIT_INDEX_FILE="$NEXT_INDEX"
export GIT_INDEX_FILE
- git update-index --remove --stdin
+ git update-index --add --remove --stdin
) || exit
;;
esac
@@ -554,7 +557,7 @@ else
# we need to check if there is anything to commit
run_status >/dev/null
fi
-if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" -a -z "$amend" ]
+if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ]
then
rm -f "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
use_status_color=t
diff --git a/git-compat-util.h b/git-compat-util.h
index ca0a597a28..1bfbdeb94f 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -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)
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index ba23eb8eeb..2954fb846e 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -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;
diff --git a/git-rebase.sh b/git-rebase.sh
index 3bd66b0a04..c9942f2400 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -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
diff --git a/git-send-email.perl b/git-send-email.perl
index e0b7d1245e..d8319d45fb 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -73,9 +73,18 @@ Options:
--signed-off-cc Automatically add email addresses that appear in
Signed-off-by: or Cc: lines to the cc: list. Defaults to on.
+ --identity The configuration identity, a subsection to prioritise over
+ the default section.
+
--smtp-server If set, specifies the outgoing SMTP server to use.
Defaults to localhost.
+ --smtp-user The username for SMTP-AUTH.
+
+ --smtp-pass The password for SMTP-AUTH.
+
+ --smtp-ssl If set, connects to the SMTP server using SSL.
+
--suppress-from Suppress sending emails to yourself if your address
appears in a From: line. Defaults to off.
@@ -145,7 +154,6 @@ my $compose_filename = ".msg.$$";
my (@to,@cc,@initial_cc,@bcclist,@xh,
$initial_reply_to,$initial_subject,@files,$author,$sender,$compose,$time);
-my $smtp_server;
my $envelope_sender;
# Example reply to:
@@ -164,24 +172,26 @@ my ($quiet, $dry_run) = (0, 0);
# Variables with corresponding config settings
my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc, $cc_cmd);
+my ($smtp_server, $smtp_authuser, $smtp_authpass, $smtp_ssl);
+my ($identity, $aliasfiletype, @alias_files);
-my %config_settings = (
+my %config_bool_settings = (
"thread" => [\$thread, 1],
"chainreplyto" => [\$chain_reply_to, 1],
"suppressfrom" => [\$suppress_from, 0],
"signedoffcc" => [\$signed_off_cc, 1],
- "cccmd" => [\$cc_cmd, ""],
+ "smtpssl" => [\$smtp_ssl, 0],
);
-foreach my $setting (keys %config_settings) {
- my $config = $repo->config_bool("sendemail.$setting");
- ${$config_settings{$setting}->[0]} = (defined $config) ? $config : $config_settings{$setting}->[1];
-}
-
-@bcclist = $repo->config('sendemail.bcc');
-if (!@bcclist or !$bcclist[0]) {
- @bcclist = ();
-}
+my %config_settings = (
+ "smtpserver" => \$smtp_server,
+ "smtpuser" => \$smtp_authuser,
+ "smtppass" => \$smtp_authpass,
+ "cccmd" => \$cc_cmd,
+ "aliasfiletype" => \$aliasfiletype,
+ "bcc" => \@bcclist,
+ "aliasesfile" => \@alias_files,
+);
# Begin by accumulating all the variables (defined above), that we will end up
# needing, first, from the command line:
@@ -194,6 +204,10 @@ my $rc = GetOptions("sender|from=s" => \$sender,
"bcc=s" => \@bcclist,
"chain-reply-to!" => \$chain_reply_to,
"smtp-server=s" => \$smtp_server,
+ "smtp-user=s" => \$smtp_authuser,
+ "smtp-pass=s" => \$smtp_authpass,
+ "smtp-ssl!" => \$smtp_ssl,
+ "identity=s" => \$identity,
"compose" => \$compose,
"quiet" => \$quiet,
"cc-cmd=s" => \$cc_cmd,
@@ -208,6 +222,43 @@ unless ($rc) {
usage();
}
+# Now, let's fill any that aren't set in with defaults:
+
+sub read_config {
+ my ($prefix) = @_;
+
+ foreach my $setting (keys %config_bool_settings) {
+ my $target = $config_bool_settings{$setting}->[0];
+ $$target = $repo->config_bool("$prefix.$setting") unless (defined $$target);
+ }
+
+ foreach my $setting (keys %config_settings) {
+ my $target = $config_settings{$setting};
+ if (ref($target) eq "ARRAY") {
+ unless (@$target) {
+ my @values = $repo->config("$prefix.$setting");
+ @$target = @values if (@values && defined $values[0]);
+ }
+ }
+ else {
+ $$target = $repo->config("$prefix.$setting") unless (defined $$target);
+ }
+ }
+}
+
+# read configuration from [sendemail "$identity"], fall back on [sendemail]
+$identity = $repo->config("sendemail.identity") unless (defined $identity);
+read_config("sendemail.$identity") if (defined $identity);
+read_config("sendemail");
+
+# fall back on builtin bool defaults
+foreach my $setting (values %config_bool_settings) {
+ ${$setting->[0]} = $setting->[1] unless (defined (${$setting->[0]}));
+}
+
+my ($repoauthor) = $repo->ident_person('author');
+my ($repocommitter) = $repo->ident_person('committer');
+
# Verify the user input
foreach my $entry (@to) {
@@ -222,14 +273,7 @@ foreach my $entry (@bcclist) {
die "Comma in --bcclist entry: $entry'\n" unless $entry !~ m/,/;
}
-# Now, let's fill any that aren't set in with defaults:
-
-my ($repoauthor) = $repo->ident_person('author');
-my ($repocommitter) = $repo->ident_person('committer');
-
my %aliases;
-my @alias_files = $repo->config('sendemail.aliasesfile');
-my $aliasfiletype = $repo->config('sendemail.aliasfiletype');
my %parse_alias = (
# multiline formats can be supported in the future
mutt => sub { my $fh = shift; while (<$fh>) {
@@ -317,13 +361,11 @@ if ($thread && !defined $initial_reply_to && $prompting) {
} while (!defined $_);
$initial_reply_to = $_;
- $initial_reply_to =~ s/(^\s+|\s+$)//g;
+ $initial_reply_to =~ s/^\s+<?/</;
+ $initial_reply_to =~ s/>?\s+$/>/;
}
-if (!$smtp_server) {
- $smtp_server = $repo->config('sendemail.smtpserver');
-}
-if (!$smtp_server) {
+if (!defined $smtp_server) {
foreach (qw( /usr/sbin/sendmail /usr/lib/sendmail )) {
if (-x $_) {
$smtp_server = $_;
@@ -553,8 +595,16 @@ X-Mailer: git-send-email $gitversion
print $sm "$header\n$message";
close $sm or die $?;
} else {
- require Net::SMTP;
- $smtp ||= Net::SMTP->new( $smtp_server );
+ if ($smtp_ssl) {
+ require Net::SMTP::SSL;
+ $smtp ||= Net::SMTP::SSL->new( $smtp_server, Port => 465 );
+ }
+ else {
+ require Net::SMTP;
+ $smtp ||= Net::SMTP->new( $smtp_server );
+ }
+ $smtp->auth( $smtp_authuser, $smtp_authpass )
+ or die $smtp->message if (defined $smtp_authuser);
$smtp->mail( $raw_from ) or die $smtp->message;
$smtp->to( @recipients ) or die $smtp->message;
$smtp->data or die $smtp->message;
@@ -661,7 +711,7 @@ foreach my $t (@files) {
}
close F;
- if ($cc_cmd ne "") {
+ if (defined $cc_cmd) {
open(F, "$cc_cmd $t |")
or die "(cc-cmd) Could not execute '$cc_cmd'";
while(<F>) {
diff --git a/git-stash.sh b/git-stash.sh
index 30425ce6df..7ba61625ba 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -57,7 +57,7 @@ save_stash () {
# state of the index
i_tree=$(git write-tree) &&
- i_commit=$(printf 'index on %s' "$msg" |
+ i_commit=$(printf 'index on %s\n' "$msg" |
git commit-tree $i_tree -p $b_commit) ||
die "Cannot save the current index state"
diff --git a/git-svn.perl b/git-svn.perl
index d3c8cd0b8e..f8181609f9 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -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
diff --git a/git-svnimport.perl b/git-svnimport.perl
index 8c17fb5ae2..d3ad5b904f 100755
--- a/git-svnimport.perl
+++ b/git-svnimport.perl
@@ -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;
diff --git a/git.spec.in b/git.spec.in
index fe7b3d8215..bdb293d990 100644
--- a/git.spec.in
+++ b/git.spec.in
@@ -25,6 +25,7 @@ This is a dummy package which brings in all subpackages.
Summary: Core git tools
Group: Development/Tools
Requires: zlib >= 1.2, rsync, curl, less, openssh-clients, expat
+Obsoletes: git-p4
%description core
Git is a fast, scalable, distributed revision control system with an
unusually rich command set that provides both high-level operations
diff --git a/refs.c b/refs.c
index 09a2c87fc2..7fb3350789 100644
--- a/refs.c
+++ b/refs.c
@@ -1455,3 +1455,30 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)
{
return do_for_each_reflog("", fn, cb_data);
}
+
+int update_ref(const char *action, const char *refname,
+ const unsigned char *sha1, const unsigned char *oldval,
+ int flags, enum action_on_err onerr)
+{
+ static struct ref_lock *lock;
+ lock = lock_any_ref_for_update(refname, oldval, flags);
+ if (!lock) {
+ const char *str = "Cannot lock the ref '%s'.";
+ switch (onerr) {
+ case MSG_ON_ERR: error(str, refname); break;
+ case DIE_ON_ERR: die(str, refname); break;
+ case QUIET_ON_ERR: break;
+ }
+ return 1;
+ }
+ if (write_ref_sha1(lock, sha1, action) < 0) {
+ const char *str = "Cannot update the ref '%s'.";
+ switch (onerr) {
+ case MSG_ON_ERR: error(str, refname); break;
+ case DIE_ON_ERR: die(str, refname); break;
+ case QUIET_ON_ERR: break;
+ }
+ return 1;
+ }
+ return 0;
+}
diff --git a/refs.h b/refs.h
index f234eb76ba..6eb98a4caf 100644
--- a/refs.h
+++ b/refs.h
@@ -64,4 +64,10 @@ extern int rename_ref(const char *oldref, const char *newref, const char *logmsg
/** resolve ref in nested "gitlink" repository */
extern int resolve_gitlink_ref(const char *name, const char *refname, unsigned char *result);
+/** lock a ref and then write its file */
+enum action_on_err { MSG_ON_ERR, DIE_ON_ERR, QUIET_ON_ERR };
+int update_ref(const char *action, const char *refname,
+ const unsigned char *sha1, const unsigned char *oldval,
+ int flags, enum action_on_err onerr);
+
#endif /* REFS_H */
diff --git a/send-pack.c b/send-pack.c
index 9fc8a812f4..f74e66a8ba 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -307,20 +307,14 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha
rs.src = ref->name;
rs.dst = NULL;
if (!remote_find_tracking(remote, &rs)) {
- struct ref_lock *lock;
fprintf(stderr, " Also local %s\n", rs.dst);
if (will_delete_ref) {
if (delete_ref(rs.dst, NULL)) {
error("Failed to delete");
}
- } else {
- lock = lock_any_ref_for_update(rs.dst, NULL, 0);
- if (!lock)
- error("Failed to lock");
- else
- write_ref_sha1(lock, ref->new_sha1,
- "update by push");
- }
+ } else
+ update_ref("update by push", rs.dst,
+ ref->new_sha1, NULL, 0, 0);
free(rs.dst);
}
}
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index c4c0dfaab1..ce045b2a57 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -198,11 +198,9 @@ test_expect_success \
GIT_AUTHOR_DATE="2005-05-26 23:41" \
GIT_COMMITTER_DATE="2005-05-26 23:41" git-commit -F M -a &&
h_OTHER=$(git rev-parse --verify HEAD) &&
- echo FIXED >F &&
GIT_AUTHOR_DATE="2005-05-26 23:44" \
GIT_COMMITTER_DATE="2005-05-26 23:44" git-commit --amend &&
h_FIXED=$(git rev-parse --verify HEAD) &&
- echo TEST+FIXED >F &&
echo Merged initial commit and a later commit. >M &&
echo $h_TEST >.git/MERGE_HEAD &&
GIT_AUTHOR_DATE="2005-05-26 23:45" \
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index 61d08bb431..eb1ced3c37 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -16,11 +16,12 @@ only the updates to dir/sub.'
test_expect_success setup '
echo initial >check &&
echo initial >top &&
+ echo initial >foo &&
mkdir dir1 dir2 &&
echo initial >dir1/sub1 &&
echo initial >dir1/sub2 &&
echo initial >dir2/sub3 &&
- git add check dir1 dir2 top &&
+ git add check dir1 dir2 top foo &&
test_tick
git-commit -m initial &&
@@ -76,4 +77,12 @@ test_expect_success 'change gets noticed' '
'
+test_expect_success 'replace a file with a symlink' '
+
+ rm foo &&
+ ln -s top foo &&
+ git add -u -- foo
+
+'
+
test_done
diff --git a/t/t4123-apply-shrink.sh b/t/t4123-apply-shrink.sh
new file mode 100755
index 0000000000..984157f03b
--- /dev/null
+++ b/t/t4123-apply-shrink.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+test_description='apply a patch that is larger than the preimage'
+
+. ./test-lib.sh
+
+cat >F <<\EOF
+1
+2
+3
+4
+5
+6
+7
+8
+999999
+A
+B
+C
+D
+E
+F
+G
+H
+I
+J
+
+EOF
+
+test_expect_success setup '
+
+ git add F &&
+ mv F G &&
+ sed -e "s/1/11/" -e "s/999999/9/" -e "s/H/HH/" <G >F &&
+ git diff >patch &&
+ sed -e "/^\$/d" <G >F &&
+ git add F
+
+'
+
+test_expect_success 'apply should fail gracefully' '
+
+ if git apply --index patch
+ then
+ echo Oops, should not have succeeded
+ false
+ else
+ status=$?
+ echo "Status was $status"
+ if test -f .git/index.lock
+ then
+ echo Oops, should not have crashed
+ false
+ fi
+ fi
+'
+
+test_done
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 1a4c53a031..42e28ab758 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -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 &&
@@ -105,6 +108,22 @@ test_expect_success \
'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'
diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh
index 6bfb899ed1..68b2b92879 100755
--- a/t/t7002-grep.sh
+++ b/t/t7002-grep.sh
@@ -107,6 +107,10 @@ do
diff expected actual
'
+ test_expect_failure "grep -c $L (no /dev/null)" '
+ git grep -c test $H | grep -q "/dev/null"
+ '
+
done
test_done
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 606d4f2a2c..0d07bc39c7 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -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
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index 9d142ed649..4fe3a41f07 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -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) &&
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 6bd3c9e3e0..b151b51a34 100644
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -131,4 +131,36 @@ test_expect_success \
'validate git-rev-list output.' \
'diff current expected'
+test_expect_success 'partial commit that involves removal (1)' '
+
+ git rm --cached file &&
+ mv file elif &&
+ git add elif &&
+ git commit -m "Partial: add elif" elif &&
+ git diff-tree --name-status HEAD^ HEAD >current &&
+ echo "A elif" >expected &&
+ diff expected current
+
+'
+
+test_expect_success 'partial commit that involves removal (2)' '
+
+ git commit -m "Partial: remove file" file &&
+ git diff-tree --name-status HEAD^ HEAD >current &&
+ echo "D file" >expected &&
+ diff expected current
+
+'
+
+test_expect_success 'partial commit that involves removal (3)' '
+
+ git rm --cached elif &&
+ echo elif >elif &&
+ git commit -m "Partial: modify elif" elif &&
+ git diff-tree --name-status HEAD^ HEAD >current &&
+ echo "M elif" >expected &&
+ diff expected current
+
+'
+
test_done
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index fa32598b0c..642b836d64 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -58,6 +58,14 @@ gitweb_run () {
# gitweb.log is left for debugging
}
+safe_chmod () {
+ chmod "$1" "$2" &&
+ if [ "$(git config --get core.filemode)" = false ]
+ then
+ git update-index --chmod="$1" "$2"
+ fi
+}
+
. ./test-lib.sh
perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
@@ -229,7 +237,7 @@ test_debug 'cat gitweb.log'
test_expect_success \
'commitdiff(0): mode change' \
- 'chmod a+x new_file &&
+ 'safe_chmod +x new_file &&
git commit -a -m "Mode changed." &&
gitweb_run "p=.git;a=commitdiff"'
test_debug 'cat gitweb.log'
@@ -268,7 +276,7 @@ test_debug 'cat gitweb.log'
test_expect_success \
'commitdiff(0): mode change and modified' \
'echo "New line" >> file2 &&
- chmod a+x file2 &&
+ safe_chmod +x file2 &&
git commit -a -m "Mode change and modification." &&
gitweb_run "p=.git;a=commitdiff"'
test_debug 'cat gitweb.log'
@@ -295,7 +303,7 @@ test_expect_success \
'commitdiff(0): renamed, mode change and modified' \
'git mv file3 file2 &&
echo "Propter nomen suum." >> file2 &&
- chmod a+x file2 &&
+ safe_chmod +x file2 &&
git commit -a -m "File rename, mode change and modification." &&
gitweb_run "p=.git;a=commitdiff"'
test_debug 'cat gitweb.log'
@@ -412,10 +420,10 @@ test_expect_success \
git add 03-new &&
git mv 04-rename-from 04-rename-to &&
echo "Changed" >> 04-rename-to &&
- chmod a+x 05-mode-change &&
+ safe_chmod +x 05-mode-change &&
rm -f 06-file-or-symlink && ln -s 01-change 06-file-or-symlink &&
echo "Changed and have mode changed" > 07-change-mode-change &&
- chmod a+x 07-change-mode-change &&
+ safe_chmod +x 07-change-mode-change &&
git commit -a -m "Large commit" &&
git checkout master'
diff --git a/templates/hooks--update b/templates/hooks--update
index 9d3795c6d0..d8c76264be 100644
--- a/templates/hooks--update
+++ b/templates/hooks--update
@@ -42,7 +42,7 @@ fi
# --- Check types
# if $newrev is 0000...0000, it's a commit to delete a branch
-if [ -z "${newrev##0*}" ]; then
+if [ "$newrev" = "0000000000000000000000000000000000000000" ]; then
newrev_type=commit
else
newrev_type=$(git-cat-file -t $newrev)
diff --git a/wt-status.c b/wt-status.c
index 52054201c2..10ce6eedc7 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -227,6 +227,7 @@ static void wt_status_print_updated(struct wt_status *s)
rev.diffopt.format_callback = wt_status_print_updated_cb;
rev.diffopt.format_callback_data = s;
rev.diffopt.detect_rename = 1;
+ rev.diffopt.rename_limit = 100;
wt_read_cache(s);
run_diff_index(&rev, 1);
}