summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/cvs-migration.txt24
-rw-r--r--Documentation/diff-options.txt16
-rw-r--r--Documentation/git-clone.txt4
-rw-r--r--Documentation/git-diff.txt6
-rw-r--r--Documentation/git-repo-config.txt6
-rw-r--r--Documentation/git-svn.txt40
-rw-r--r--Documentation/git-symbolic-ref.txt29
-rw-r--r--Documentation/tutorial.txt29
-rw-r--r--Makefile22
-rw-r--r--builtin-blame.c11
-rw-r--r--builtin-diff.c2
-rw-r--r--builtin-ls-files.c6
-rw-r--r--builtin-mv.c11
-rw-r--r--builtin-pack-objects.c6
-rw-r--r--builtin-shortlog.c4
-rwxr-xr-xcontrib/completion/git-completion.bash53
-rw-r--r--fetch-pack.c25
-rwxr-xr-xgit-clone.sh6
-rwxr-xr-xgit-cvsexportcommit.perl13
-rwxr-xr-xgit-fetch.sh41
-rwxr-xr-xgit-merge.sh10
-rwxr-xr-xgit-parse-remote.sh39
-rwxr-xr-xgit-request-pull.sh2
-rwxr-xr-xgit-reset.sh3
-rwxr-xr-xgit-svn.perl294
-rwxr-xr-xgitk20
-rwxr-xr-xgitweb/gitweb.perl2
-rw-r--r--ident.c15
-rw-r--r--perl/.gitignore3
-rw-r--r--perl/Makefile39
-rw-r--r--receive-pack.c3
-rw-r--r--t/Makefile3
-rw-r--r--t/lib-git-svn.sh2
-rwxr-xr-xt/t4015-diff-whitespace.sh6
-rwxr-xr-xt/t7001-mv.sh13
-rwxr-xr-xt/t9100-git-svn-basic.sh5
-rwxr-xr-xt/t9103-git-svn-graft-branches.sh2
-rwxr-xr-xt/t9200-git-cvsexportcommit.sh16
-rw-r--r--unpack-trees.c8
-rw-r--r--xdiff/xutils.c3
40 files changed, 651 insertions, 191 deletions
diff --git a/Documentation/cvs-migration.txt b/Documentation/cvs-migration.txt
index 6812683a16..a436180dd4 100644
--- a/Documentation/cvs-migration.txt
+++ b/Documentation/cvs-migration.txt
@@ -24,6 +24,11 @@ First, note some ways that git differs from CVS:
single shared repository which people can synchronize with; see below
for details.
+ * Since every working tree contains a repository, a commit in your
+ private repository will not publish your changes; it will only create
+ a revision. You have to "push" your changes to a public repository to
+ make them visible to others.
+
Importing a CVS archive
-----------------------
@@ -76,8 +81,8 @@ variants of this model.
With a small group, developers may just pull changes from each other's
repositories without the need for a central maintainer.
-Emulating the CVS Development Model
------------------------------------
+Creating a Shared Repository
+----------------------------
Start with an ordinary git working directory containing the project, and
remove the checked-out files, keeping just the bare .git directory:
@@ -105,7 +110,10 @@ $ GIT_DIR=repo.git git repo-config core.sharedrepository true
Make sure committers have a umask of at most 027, so that the directories
they create are writable and searchable by other group members.
-Suppose this repository is now set up in /pub/repo.git on the host
+Performing Development on a Shared Repository
+---------------------------------------------
+
+Suppose a repository is now set up in /pub/repo.git on the host
foo.com. Then as an individual committer you can clone the shared
repository:
@@ -134,15 +142,17 @@ Pull: master:origin
------------
================================
-You can update the shared repository with your changes using:
+You can update the shared repository with your changes by first commiting
+your changes, and then using:
------------------------------------------------
$ git push origin master
------------------------------------------------
-If someone else has updated the repository more recently, `git push`, like
-`cvs commit`, will complain, in which case you must pull any changes
-before attempting the push again.
+to "push" those commits to the shared repository. If someone else has
+updated the repository more recently, `git push`, like `cvs commit`, will
+complain, in which case you must pull any changes before attempting the
+push again.
In the `git push` command above we specify the name of the remote branch
to update (`master`). If we leave that out, `git push` tries to update
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index e112172ca5..9cdd171af7 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -129,5 +129,21 @@
-a::
Shorthand for "--text".
+--ignore-space-change::
+ Ignore changes in amount of white space. This ignores white
+ space at line end, and consider all other sequences of one or
+ more white space characters to be equivalent.
+
+-b::
+ Shorthand for "--ignore-space-change".
+
+--ignore-all-space::
+ Ignore white space when comparing lines. This ignores
+ difference even if one line has white space where the other
+ line has none.
+
+-w::
+ Shorthand for "--ignore-all-space".
+
For more detailed explanation on these common options, see also
link:diffcore.html[diffcore documentation].
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 4cb42237b5..d5efa00dea 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -11,7 +11,7 @@ SYNOPSIS
[verse]
'git-clone' [--template=<template_directory>] [-l [-s]] [-q] [-n] [--bare]
[-o <name>] [-u <upload-pack>] [--reference <repository>]
- [--use-separate-remote | --use-immingled-remote] <repository>
+ [--use-separate-remote | --no-separate-remote] <repository>
[<directory>]
DESCRIPTION
@@ -105,7 +105,7 @@ OPTIONS
of `$GIT_DIR/refs/heads/`. Only the local master branch is
saved in the latter. This is the default.
---use-immingled-remote::
+--no-separate-remote::
Save remotes heads in the same namespace as the local
heads, `$GIT_DIR/refs/heads/'. In regular repositories,
this is a legacy setup git-clone created by default in
diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index 228c4d95bd..3144864d85 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -22,8 +22,10 @@ the number of trees given to the command.
* When one <tree-ish> is given, the working tree and the named
tree are compared, using `git-diff-index`. The option
- `--cached` can be given to compare the index file and
+ `--index` can be given to compare the index file and
the named tree.
+ `--cached` is a deprecated alias for `--index`. It's use is
+ discouraged.
* When two <tree-ish>s are given, these two trees are compared
using `git-diff-tree`.
@@ -47,7 +49,7 @@ Various ways to check your working tree::
+
------------
$ git diff <1>
-$ git diff --cached <2>
+$ git diff --index <2>
$ git diff HEAD <3>
------------
+
diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt
index 8199615dde..5bede9ac22 100644
--- a/Documentation/git-repo-config.txt
+++ b/Documentation/git-repo-config.txt
@@ -77,6 +77,12 @@ OPTIONS
-l, --list::
List all variables set in config file.
+--bool::
+ git-repo-config will ensure that the output is "true" or "false"
+
+--int::
+ git-repo-config will ensure that the output is a simple decimal number
+
ENVIRONMENT
-----------
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index a764d1f8ee..a45067e164 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -49,7 +49,7 @@ latest revision.
Note: You should never attempt to modify the remotes/git-svn
branch outside of git-svn. Instead, create a branch from
-remotes/git-svn and work on that branch. Use the 'commit'
+remotes/git-svn and work on that branch. Use the 'dcommit'
command (see below) to write git commits back to
remotes/git-svn.
@@ -274,7 +274,7 @@ ADVANCED OPTIONS
-b<refname>::
--branch <refname>::
-Used with 'fetch' or 'commit'.
+Used with 'fetch', 'dcommit' or 'commit'.
This can be used to join arbitrary git branches to remotes/git-svn
on new commits where the tree object is equivalent.
@@ -368,7 +368,7 @@ SVN was very wrong.
Basic Examples
~~~~~~~~~~~~~~
-Tracking and contributing to an Subversion managed-project:
+Tracking and contributing to a Subversion-managed project:
------------------------------------------------------------------------
# Initialize a repo (like git init-db):
@@ -377,10 +377,9 @@ Tracking and contributing to an Subversion managed-project:
git-svn fetch
# Create your own branch to hack on:
git checkout -b my-branch remotes/git-svn
-# Commit only the git commits you want to SVN:
- git-svn commit <tree-ish> [<tree-ish_2> ...]
-# Commit all the git commits from my-branch that don't exist in SVN:
- git-svn commit remotes/git-svn..my-branch
+# Do some work, and then commit your new changes to SVN, as well as
+# automatically updating your working HEAD:
+ git-svn dcommit
# Something is committed to SVN, rebase the latest into your branch:
git-svn fetch && git rebase remotes/git-svn
# Append svn:ignore settings to the default git exclude file:
@@ -404,26 +403,24 @@ which can lead to merge commits reversing previous commits in SVN.
DESIGN PHILOSOPHY
-----------------
Merge tracking in Subversion is lacking and doing branched development
-with Subversion is cumbersome as a result. git-svn completely forgoes
-any automated merge/branch tracking on the Subversion side and leaves it
-entirely up to the user on the git side. It's simply not worth it to do
-a useful translation when the original signal is weak.
+with Subversion is cumbersome as a result. git-svn does not do
+automated merge/branch tracking by default and leaves it entirely up to
+the user on the git side.
[[tracking-multiple-repos]]
TRACKING MULTIPLE REPOSITORIES OR BRANCHES
------------------------------------------
-This is for advanced users, most users should ignore this section.
-
Because git-svn does not care about relationships between different
branches or directories in a Subversion repository, git-svn has a simple
hack to allow it to track an arbitrary number of related _or_ unrelated
-SVN repositories via one git repository. Simply set the GIT_SVN_ID
-environment variable to a name other other than "git-svn" (the default)
-and git-svn will ignore the contents of the $GIT_DIR/svn/git-svn directory
-and instead do all of its work in $GIT_DIR/svn/$GIT_SVN_ID for that
-invocation. The interface branch will be remotes/$GIT_SVN_ID, instead of
-remotes/git-svn. Any remotes/$GIT_SVN_ID branch should never be modified
-by the user outside of git-svn commands.
+SVN repositories via one git repository. Simply use the --id/-i flag or
+set the GIT_SVN_ID environment variable to a name other other than
+"git-svn" (the default) and git-svn will ignore the contents of the
+$GIT_DIR/svn/git-svn directory and instead do all of its work in
+$GIT_DIR/svn/$GIT_SVN_ID for that invocation. The interface branch will
+be remotes/$GIT_SVN_ID, instead of remotes/git-svn. Any
+remotes/$GIT_SVN_ID branch should never be modified by the user outside
+of git-svn commands.
[[fetch-args]]
ADDITIONAL FETCH ARGUMENTS
@@ -486,7 +483,8 @@ If you are not using the SVN::* Perl libraries and somebody commits a
conflicting changeset to SVN at a bad moment (right before you commit)
causing a conflict and your commit to fail, your svn working tree
($GIT_DIR/git-svn/tree) may be dirtied. The easiest thing to do is
-probably just to rm -rf $GIT_DIR/git-svn/tree and run 'rebuild'.
+probably just to rm -rf $GIT_DIR/git-svn/tree and run 'rebuild'. You
+can avoid this problem entirely by using 'dcommit'.
We ignore all SVN properties except svn:executable. Too difficult to
map them since we rely heavily on git write-tree being _exactly_ the
diff --git a/Documentation/git-symbolic-ref.txt b/Documentation/git-symbolic-ref.txt
index 68ac6a65df..4bc35a1d4b 100644
--- a/Documentation/git-symbolic-ref.txt
+++ b/Documentation/git-symbolic-ref.txt
@@ -19,29 +19,22 @@ argument to see on which branch your working tree is on.
Give two arguments, create or update a symbolic ref <name> to
point at the given branch <ref>.
-Traditionally, `.git/HEAD` is a symlink pointing at
-`refs/heads/master`. When we want to switch to another branch,
-we did `ln -sf refs/heads/newbranch .git/HEAD`, and when we want
+A symbolic ref is a regular file that stores a string that
+begins with `ref: refs/`. For example, your `.git/HEAD` is
+a regular file whose contents is `ref: refs/heads/master`.
+
+NOTES
+-----
+In the past, `.git/HEAD` was a symbolic link pointing at
+`refs/heads/master`. When we wanted to switch to another branch,
+we did `ln -sf refs/heads/newbranch .git/HEAD`, and when we wanted
to find out which branch we are on, we did `readlink .git/HEAD`.
This was fine, and internally that is what still happens by
default, but on platforms that do not have working symlinks,
or that do not have the `readlink(1)` command, this was a bit
cumbersome. On some platforms, `ln -sf` does not even work as
-advertised (horrors).
-
-A symbolic ref can be a regular file that stores a string that
-begins with `ref: refs/`. For example, your `.git/HEAD` *can*
-be a regular file whose contents is `ref: refs/heads/master`.
-This can be used on a filesystem that does not support symbolic
-links. Instead of doing `readlink .git/HEAD`, `git-symbolic-ref
-HEAD` can be used to find out which branch we are on. To point
-the HEAD to `newbranch`, instead of `ln -sf refs/heads/newbranch
-.git/HEAD`, `git-symbolic-ref HEAD refs/heads/newbranch` can be
-used.
-
-Currently, .git/HEAD uses a regular file symbolic ref on Cygwin,
-and everywhere else it is implemented as a symlink. This can be
-changed at compilation time.
+advertised (horrors). Therefore symbolic links are now deprecated
+and symbolic refs are used by default.
Author
------
diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt
index 35af81a3de..fe4491de41 100644
--- a/Documentation/tutorial.txt
+++ b/Documentation/tutorial.txt
@@ -11,6 +11,18 @@ diff" with:
$ man git-diff
------------------------------------------------
+It is a good idea to introduce yourself to git before doing any
+operation. The easiest way to do so is:
+
+------------------------------------------------
+$ cat >~/.gitconfig <<\EOF
+[user]
+ name = Your Name Comes Here
+ email = you@yourdomain.example.com
+EOF
+------------------------------------------------
+
+
Importing a new project
-----------------------
@@ -31,7 +43,8 @@ defaulting to local storage area
You've now initialized the working directory--you may notice a new
directory created, named ".git". Tell git that you want it to track
-every file under the current directory with
+every file under the current directory with (notice the dot '.'
+that means the current directory):
------------------------------------------------
$ git add .
@@ -40,7 +53,7 @@ $ git add .
Finally,
------------------------------------------------
-$ git commit -a
+$ git commit
------------------------------------------------
will prompt you for a commit message, then record the current state
@@ -55,11 +68,17 @@ $ git diff
to review your changes. When you're done,
------------------------------------------------
-$ git commit -a
+$ git commit file1 file2...
------------------------------------------------
will again prompt your for a message describing the change, and then
-record the new versions of the modified files.
+record the new versions of the files you listed. It is cumbersome
+to list all files and you can say `-a` (which stands for 'all')
+instead.
+
+------------------------------------------------
+$ git commit -a
+------------------------------------------------
A note on commit messages: Though not required, it's a good idea to
begin the commit message with a single short (less than 50 character)
@@ -75,7 +94,7 @@ $ git add path/to/new/file
------------------------------------------------
then commit as usual. No special command is required when removing a
-file; just remove it, then commit.
+file; just remove it, then tell `commit` about the file as usual.
At any point you can view the history of your changes using
diff --git a/Makefile b/Makefile
index de3e9f38ec..a1861de2cc 100644
--- a/Makefile
+++ b/Makefile
@@ -91,6 +91,10 @@ all:
#
# Define USE_STDEV below if you want git to care about the underlying device
# change being considered an inode change from the update-cache perspective.
+#
+# Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
+# MakeMaker (e.g. using ActiveState under Cygwin).
+#
GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
@$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -539,6 +543,9 @@ endif
ifdef NO_ACCURATE_DIFF
BASIC_CFLAGS += -DNO_ACCURATE_DIFF
endif
+ifdef NO_PERL_MAKEMAKER
+ export NO_PERL_MAKEMAKER
+endif
# Shell quote (do not use $(call) to accommodate ancient setups);
@@ -568,8 +575,8 @@ export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi
-all: perl/Makefile
- $(MAKE) -C perl
+all:
+ $(MAKE) -C perl PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
$(MAKE) -C templates
strip: $(PROGRAMS) git$X
@@ -602,7 +609,11 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
chmod +x $@+
mv $@+ $@
-$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/Makefile
+$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
+
+perl/perl.mak: GIT-CFLAGS
+ $(MAKE) -C perl PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
+
$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
rm -f $@ $@+
INSTLIBDIR=`$(MAKE) -C perl -s --no-print-directory instlibdir` && \
@@ -796,7 +807,7 @@ install: all
$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
$(INSTALL) git$X gitk '$(DESTDIR_SQ)$(bindir_SQ)'
$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
- $(MAKE) -C perl install
+ $(MAKE) -C perl prefix='$(prefix_SQ)' install
if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \
then \
ln -f '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \
@@ -866,8 +877,7 @@ clean:
rm -f $(htmldocs).tar.gz $(manpages).tar.gz
rm -f gitweb/gitweb.cgi
$(MAKE) -C Documentation/ clean
- [ ! -f perl/Makefile ] || $(MAKE) -C perl/ clean || $(MAKE) -C perl/ clean
- rm -f perl/ppport.h perl/Makefile.old
+ $(MAKE) -C perl clean
$(MAKE) -C templates/ clean
$(MAKE) -C t/ clean
rm -f GIT-VERSION-FILE GIT-CFLAGS
diff --git a/builtin-blame.c b/builtin-blame.c
index 066dee743e..dc3ffeaff8 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -1435,14 +1435,14 @@ static void find_alignment(struct scoreboard *sb, int *option)
struct commit_info ci;
int num;
+ if (strcmp(suspect->path, sb->path))
+ *option |= OUTPUT_SHOW_NAME;
+ num = strlen(suspect->path);
+ if (longest_file < num)
+ longest_file = num;
if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
suspect->commit->object.flags |= METAINFO_SHOWN;
get_commit_info(suspect->commit, &ci, 1);
- if (strcmp(suspect->path, sb->path))
- *option |= OUTPUT_SHOW_NAME;
- num = strlen(suspect->path);
- if (longest_file < num)
- longest_file = num;
num = strlen(ci.author);
if (longest_author < num)
longest_author = num;
@@ -1787,6 +1787,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
/* Now we got rev and path. We do not want the path pruning
* but we may want "bottom" processing.
*/
+ argv[unk++] = "--"; /* terminate the rev name */
argv[unk] = NULL;
init_revisions(&revs, NULL);
diff --git a/builtin-diff.c b/builtin-diff.c
index a6590205e8..1c535b1dd6 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -137,7 +137,7 @@ static int builtin_diff_index(struct rev_info *revs,
int cached = 0;
while (1 < argc) {
const char *arg = argv[1];
- if (!strcmp(arg, "--cached"))
+ if (!strcmp(arg, "--index") || !strcmp(arg, "--cached"))
cached = 1;
else
usage(builtin_diff_usage);
diff --git a/builtin-ls-files.c b/builtin-ls-files.c
index ad8c41e731..bc79ce40fc 100644
--- a/builtin-ls-files.c
+++ b/builtin-ls-files.c
@@ -487,10 +487,14 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
for (num = 0; pathspec[num]; num++) {
if (ps_matched[num])
continue;
- error("pathspec '%s' did not match any.",
+ error("pathspec '%s' did not match any file(s) known to git.",
pathspec[num] + prefix_offset);
errors++;
}
+
+ if (errors)
+ fprintf(stderr, "Did you forget to 'git add'?\n");
+
return errors ? 1 : 0;
}
diff --git a/builtin-mv.c b/builtin-mv.c
index 54dd3bfe8a..d14a4a7f5c 100644
--- a/builtin-mv.c
+++ b/builtin-mv.c
@@ -146,21 +146,24 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
&& lstat(dst, &st) == 0)
bad = "cannot move directory over file";
else if (src_is_dir) {
+ const char *src_w_slash = add_slash(src);
+ int len_w_slash = length + 1;
int first, last;
modes[i] = WORKING_DIRECTORY;
- first = cache_name_pos(src, length);
+ first = cache_name_pos(src_w_slash, len_w_slash);
if (first >= 0)
- die ("Huh? %s/ is in index?", src);
+ die ("Huh? %.*s is in index?",
+ len_w_slash, src_w_slash);
first = -1 - first;
for (last = first; last < active_nr; last++) {
const char *path = active_cache[last]->name;
- if (strncmp(path, src, length)
- || path[length] != '/')
+ if (strncmp(path, src_w_slash, len_w_slash))
break;
}
+ free((char *)src_w_slash);
if (last - first < 1)
bad = "source directory is empty";
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 753bcd57b0..a2dc7d1d9d 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -514,6 +514,8 @@ static void write_pack_file(void)
if (do_progress)
fputc('\n', stderr);
done:
+ if (written != nr_result)
+ die("wrote %d objects while expecting %d", written, nr_result);
sha1close(f, pack_file_sha1, 1);
}
@@ -1662,7 +1664,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
}
}
if (progress)
- fprintf(stderr, "Total %d, written %d (delta %d), reused %d (delta %d)\n",
- nr_result, written, written_delta, reused, reused_delta);
+ fprintf(stderr, "Total %d (delta %d), reused %d (delta %d)\n",
+ written, written_delta, reused, reused_delta);
return 0;
}
diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index b5b13dee3b..f1124e261b 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -298,9 +298,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
if (!access(".mailmap", R_OK))
read_mailmap(".mailmap");
- if (rev.pending.nr == 1)
- die ("Need a range!");
- else if (rev.pending.nr == 0)
+ if (rev.pending.nr == 0)
read_from_stdin(&list);
else
get_from_rev(&rev, &list);
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index d8ae4d7886..447ec20467 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -34,7 +34,19 @@
__gitdir ()
{
- echo "${__git_dir:-$(git rev-parse --git-dir 2>/dev/null)}"
+ if [ -z "$1" ]; then
+ if [ -n "$__git_dir" ]; then
+ echo "$__git_dir"
+ elif [ -d .git ]; then
+ echo .git
+ else
+ git rev-parse --git-dir 2>/dev/null
+ fi
+ elif [ -d "$1/.git" ]; then
+ echo "$1/.git"
+ else
+ echo "$1"
+ fi
}
__git_ps1 ()
@@ -51,7 +63,7 @@ __git_ps1 ()
__git_heads ()
{
- local cmd i is_hash=y dir="${1:-$(__gitdir)}"
+ local cmd i is_hash=y dir="$(__gitdir "$1")"
if [ -d "$dir" ]; then
for i in $(git --git-dir="$dir" \
for-each-ref --format='%(refname)' \
@@ -60,7 +72,7 @@ __git_heads ()
done
return
fi
- for i in $(git-ls-remote "$dir" 2>/dev/null); do
+ for i in $(git-ls-remote "$1" 2>/dev/null); do
case "$is_hash,$i" in
y,*) is_hash=n ;;
n,*^{}) is_hash=y ;;
@@ -72,7 +84,7 @@ __git_heads ()
__git_refs ()
{
- local cmd i is_hash=y dir="${1:-$(__gitdir)}"
+ local cmd i is_hash=y dir="$(__gitdir "$1")"
if [ -d "$dir" ]; then
if [ -e "$dir/HEAD" ]; then echo HEAD; fi
for i in $(git --git-dir="$dir" \
@@ -101,20 +113,9 @@ __git_refs ()
__git_refs2 ()
{
- local cmd i is_hash=y dir="${1:-$(__gitdir)}"
- if [ -d "$dir" ]; then
- cmd=git-peek-remote
- else
- cmd=git-ls-remote
- fi
- for i in $($cmd "$dir" 2>/dev/null); do
- case "$is_hash,$i" in
- y,*) is_hash=n ;;
- n,*^{}) is_hash=y ;;
- n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}:${i#refs/tags/}" ;;
- n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}:${i#refs/heads/}" ;;
- n,*) is_hash=y; echo "$i:$i" ;;
- esac
+ local i
+ for i in $(__git_refs "$1"); do
+ echo "$i:$i"
done
}
@@ -398,6 +399,20 @@ _git_cherry_pick ()
esac
}
+_git_commit ()
+{
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ case "$cur" in
+ --*)
+ COMPREPLY=($(compgen -W "
+ --all --author= --signoff --verify --no-verify
+ --edit --amend --include --only
+ " -- "$cur"))
+ return
+ esac
+ COMPREPLY=()
+}
+
_git_diff ()
{
__git_complete_file
@@ -768,6 +783,7 @@ _git ()
cat-file) _git_cat_file ;;
checkout) _git_checkout ;;
cherry-pick) _git_cherry_pick ;;
+ commit) _git_commit ;;
diff) _git_diff ;;
diff-tree) _git_diff_tree ;;
fetch) _git_fetch ;;
@@ -804,6 +820,7 @@ complete -o default -F _git_branch git-branch
complete -o default -o nospace -F _git_cat_file git-cat-file
complete -o default -F _git_checkout git-checkout
complete -o default -F _git_cherry_pick git-cherry-pick
+complete -o default -F _git_commit git-commit
complete -o default -o nospace -F _git_diff git-diff
complete -o default -F _git_diff_tree git-diff-tree
complete -o default -o nospace -F _git_fetch git-fetch
diff --git a/fetch-pack.c b/fetch-pack.c
index 0a169dce85..743eab7efa 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -566,6 +566,29 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
return 0;
}
+static int remove_duplicates(int nr_heads, char **heads)
+{
+ int src, dst;
+
+ for (src = dst = 0; src < nr_heads; src++) {
+ /* If heads[src] is different from any of
+ * heads[0..dst], push it in.
+ */
+ int i;
+ for (i = 0; i < dst; i++) {
+ if (!strcmp(heads[i], heads[src]))
+ break;
+ }
+ if (i < dst)
+ continue;
+ if (src != dst)
+ heads[dst] = heads[src];
+ dst++;
+ }
+ heads[dst] = 0;
+ return dst;
+}
+
int main(int argc, char **argv)
{
int i, ret, nr_heads;
@@ -617,6 +640,8 @@ int main(int argc, char **argv)
pid = git_connect(fd, dest, exec);
if (pid < 0)
return 1;
+ if (heads && nr_heads)
+ nr_heads = remove_duplicates(nr_heads, heads);
ret = fetch_pack(fd, nr_heads, heads);
close(fd[0]);
close(fd[1]);
diff --git a/git-clone.sh b/git-clone.sh
index b2d0f08e67..0ace989fde 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -14,7 +14,7 @@ die() {
}
usage() {
- die "Usage: $0 [--template=<template_directory>] [--use-immingled-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
+ die "Usage: $0 [--template=<template_directory>] [--no-separate-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
}
get_repo_base() {
@@ -140,7 +140,7 @@ while
*,--use-separate-remote)
# default
use_separate_remote=t ;;
- *,--use-immingled-remote)
+ *,--no-separate-remote)
use_separate_remote= ;;
1,--reference) usage ;;
*,--reference)
@@ -176,7 +176,7 @@ repo="$1"
test -n "$repo" ||
die 'you must specify a repository to clone.'
-# --bare implies --no-checkout and --use-immingled-remote
+# --bare implies --no-checkout and --no-separate-remote
if test yes = "$bare"
then
if test yes = "$origin_override"
diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl
index 7bac16e946..c9d1d88f2e 100755
--- a/git-cvsexportcommit.perl
+++ b/git-cvsexportcommit.perl
@@ -116,6 +116,7 @@ if ($opt_a) {
close MSG;
my (@afiles, @dfiles, @mfiles, @dirs);
+my %amodes;
my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit);
#print @files;
$? && die "Error in git-diff-tree";
@@ -124,6 +125,7 @@ foreach my $f (@files) {
my @fields = split(m!\s+!, $f);
if ($fields[4] eq 'A') {
my $path = $fields[5];
+ $amodes{$path} = $fields[1];
push @afiles, $path;
# add any needed parent directories
$path = dirname $path;
@@ -268,6 +270,7 @@ if (($? >> 8) == 2) {
}
foreach my $f (@afiles) {
+ set_new_file_permissions($f, $amodes{$f});
if (grep { $_ eq $f } @bfiles) {
system('cvs', 'add','-kb',$f);
} else {
@@ -342,3 +345,13 @@ sub safe_pipe_capture {
}
return wantarray ? @output : join('',@output);
}
+
+# For any file we want to add to cvs, we must first set its permissions
+# properly, *before* the "cvs add ..." command. Otherwise, it is impossible
+# to change the permission of the file in the CVS repository using only cvs
+# commands. This should be fixed in cvs-1.12.14.
+sub set_new_file_permissions {
+ my ($file, $perm) = @_;
+ chmod oct($perm), $file
+ or die "failed to set permissions of \"$file\": $!\n";
+}
diff --git a/git-fetch.sh b/git-fetch.sh
index 4425562098..4eecf148ea 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -88,6 +88,10 @@ then
: >"$GIT_DIR/FETCH_HEAD"
fi
+# Global that is reused later
+ls_remote_result=$(git ls-remote $upload_pack "$remote") ||
+ die "Cannot find the reflist at $remote"
+
append_fetch_head () {
head_="$1"
remote_="$2"
@@ -233,10 +237,7 @@ reflist=$(get_remote_refs_for_fetch "$@")
if test "$tags"
then
taglist=`IFS=" " &&
- (
- git-ls-remote $upload_pack --tags "$remote" ||
- echo fail ouch
- ) |
+ echo "$ls_remote_result" |
while read sha1 name
do
case "$sha1" in
@@ -245,6 +246,8 @@ then
esac
case "$name" in
*^*) continue ;;
+ refs/tags/*) ;;
+ *) continue ;;
esac
if git-check-ref-format "$name"
then
@@ -304,22 +307,20 @@ fetch_main () {
"`git-repo-config --bool http.noEPSV`" = true ]; then
noepsv_opt="--disable-epsv"
fi
- max_depth=5
- depth=0
- head="ref: $remote_name"
- while (expr "z$head" : "zref:" && expr $depth \< $max_depth) >/dev/null
- do
- remote_name_quoted=$(@@PERL@@ -e '
- my $u = $ARGV[0];
- $u =~ s/^ref:\s*//;
- $u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg;
- print "$u";
- ' "$head")
- head=$(curl -nsfL $curl_extra_args $noepsv_opt "$remote/$remote_name_quoted")
- depth=$( expr \( $depth + 1 \) )
- done
+
+ # Find $remote_name from ls-remote output.
+ head=$(
+ IFS=' '
+ echo "$ls_remote_result" |
+ while read sha1 name
+ do
+ test "z$name" = "z$remote_name" || continue
+ echo "$sha1"
+ break
+ done
+ )
expr "z$head" : "z$_x40\$" >/dev/null ||
- die "Failed to fetch $remote_name from $remote"
+ die "No such ref $remote_name at $remote"
echo >&2 "Fetching $remote_name from $remote using $proto"
git-http-fetch -v -a "$head" "$remote/" || exit
;;
@@ -432,7 +433,7 @@ case "$no_tags$tags" in
# effective only when we are following remote branch
# using local tracking branch.
taglist=$(IFS=" " &&
- git-ls-remote $upload_pack --tags "$remote" |
+ echo "$ls_remote_result" |
sed -n -e 's|^\('"$_x40"'\) \(refs/tags/.*\)^{}$|\1 \2|p' \
-e 's|^\('"$_x40"'\) \(refs/tags/.*\)$|\1 \2|p' |
while read sha1 name
diff --git a/git-merge.sh b/git-merge.sh
index 75af10d3e4..272f004622 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -189,13 +189,13 @@ else
merge_name=$(for remote
do
rh=$(git-rev-parse --verify "$remote"^0 2>/dev/null) &&
- if git show-ref -q --verify "refs/heads/$remote"
+ bh=$(git show-ref -s --verify "refs/heads/$remote") &&
+ if test "$rh" = "$bh"
then
- what=branch
+ echo "$rh branch '$remote' of ."
else
- what=commit
- fi &&
- echo "$rh $what '$remote'"
+ echo "$rh commit '$remote'"
+ fi
done | git-fmt-merge-msg
)
merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name"
diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index c325ef761e..da064a53f6 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -90,6 +90,43 @@ get_remote_default_refs_for_push () {
esac
}
+# Called from canon_refs_list_for_fetch -d "$remote", which
+# is called from get_remote_default_refs_for_fetch to grok
+# refspecs that are retrieved from the configuration, but not
+# from get_remote_refs_for_fetch when it deals with refspecs
+# supplied on the command line. $ls_remote_result has the list
+# of refs available at remote.
+expand_refs_wildcard () {
+ for ref
+ do
+ lref=${ref#'+'}
+ # a non glob pattern is given back as-is.
+ expr "z$lref" : 'zrefs/.*/\*:refs/.*/\*$' >/dev/null || {
+ echo "$ref"
+ continue
+ }
+
+ from=`expr "z$lref" : 'z\(refs/.*/\)\*:refs/.*/\*$'`
+ to=`expr "z$lref" : 'zrefs/.*/\*:\(refs/.*/\)\*$'`
+ local_force=
+ test "z$lref" = "z$ref" || local_force='+'
+ echo "$ls_remote_result" |
+ (
+ IFS=' '
+ while read sha1 name
+ do
+ mapped=${name#"$from"}
+ if test "z$name" != "z${name%'^{}'}" ||
+ test "z$name" = "z$mapped"
+ then
+ continue
+ fi
+ echo "${local_force}${name}:${to}${mapped}"
+ done
+ )
+ done
+}
+
# Subroutine to canonicalize remote:local notation.
canon_refs_list_for_fetch () {
# If called from get_remote_default_refs_for_fetch
@@ -107,6 +144,8 @@ canon_refs_list_for_fetch () {
merge_branches=$(git-repo-config \
--get-all "branch.${curr_branch}.merge")
fi
+ set x $(expand_refs_wildcard "$@")
+ shift
fi
for ref
do
diff --git a/git-request-pull.sh b/git-request-pull.sh
index 4319e35c62..4eacc3a059 100755
--- a/git-request-pull.sh
+++ b/git-request-pull.sh
@@ -30,4 +30,4 @@ echo " $url"
echo
git log $baserev..$headrev | git-shortlog ;
-git diff --stat --summary $baserev..$headrev
+git diff -M --stat --summary $baserev..$headrev
diff --git a/git-reset.sh b/git-reset.sh
index 3133b5bd25..c0feb4435d 100755
--- a/git-reset.sh
+++ b/git-reset.sh
@@ -63,6 +63,7 @@ case "$reset_type" in
;;
esac
-rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR" "$GIT_DIR/SQUASH_MSG"
+rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR" \
+ "$GIT_DIR/SQUASH_MSG" "$GIT_DIR/MERGE_MSG"
exit $update_ref_status
diff --git a/git-svn.perl b/git-svn.perl
index d5d9c49fd6..d0bd0bdeb8 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -60,6 +60,7 @@ nag_lib() unless $_use_lib;
my $_optimize_commits = 1 unless $ENV{GIT_SVN_NO_OPTIMIZE_COMMITS};
my $sha1 = qr/[a-f\d]{40}/;
my $sha1_short = qr/[a-f\d]{4,40}/;
+my $_esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/;
my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
$_find_copies_harder, $_l, $_cp_similarity, $_cp_remote,
$_repack, $_repack_nr, $_repack_flags, $_q,
@@ -68,7 +69,8 @@ my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
$_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,
$_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m,
$_merge, $_strategy, $_dry_run, $_ignore_nodate, $_non_recursive,
- $_username, $_config_dir, $_no_auth_cache);
+ $_username, $_config_dir, $_no_auth_cache, $_xfer_delta,
+ $_pager, $_color);
my (@_branch_from, %tree_map, %users, %rusers, %equiv);
my ($_svn_co_url_revs, $_svn_pg_peg_revs);
my @repo_path_split_cache;
@@ -122,7 +124,12 @@ my %cmd = (
'no-graft-copy' => \$_no_graft_copy } ],
'multi-init' => [ \&multi_init,
'Initialize multiple trees (like git-svnimport)',
- { %multi_opts, %fc_opts } ],
+ { %multi_opts, %init_opts,
+ 'revision|r=i' => \$_revision,
+ 'username=s' => \$_username,
+ 'config-dir=s' => \$_config_dir,
+ 'no-auth-cache' => \$_no_auth_cache,
+ } ],
'multi-fetch' => [ \&multi_fetch,
'Fetch multiple trees (like git-svnimport)',
\%fc_opts ],
@@ -135,6 +142,8 @@ my %cmd = (
'show-commit' => \$_show_commit,
'non-recursive' => \$_non_recursive,
'authors-file|A=s' => \$_authors,
+ 'color' => \$_color,
+ 'pager=s' => \$_pager,
} ],
'commit-diff' => [ \&commit_diff, 'Commit a diff between two trees',
{ 'message|m=s' => \$_message,
@@ -759,16 +768,17 @@ sub show_log {
}
}
+ config_pager();
my $pid = open(my $log,'-|');
defined $pid or croak $!;
if (!$pid) {
exec(git_svn_log_cmd($r_min,$r_max), @args) or croak $!;
}
- setup_pager();
+ run_pager();
my (@k, $c, $d);
while (<$log>) {
- if (/^commit ($sha1_short)/o) {
+ if (/^${_esc_color}commit ($sha1_short)/o) {
my $cmt = $1;
if ($c && cmt_showable($c) && $c->{r} != $r_last) {
$r_last = $c->{r};
@@ -777,25 +787,25 @@ sub show_log {
}
$d = undef;
$c = { c => $cmt };
- } elsif (/^author (.+) (\d+) ([\-\+]?\d+)$/) {
+ } elsif (/^${_esc_color}author (.+) (\d+) ([\-\+]?\d+)$/) {
get_author_info($c, $1, $2, $3);
- } elsif (/^(?:tree|parent|committer) /) {
+ } elsif (/^${_esc_color}(?:tree|parent|committer) /) {
# ignore
- } elsif (/^:\d{6} \d{6} $sha1_short/o) {
+ } elsif (/^${_esc_color}:\d{6} \d{6} $sha1_short/o) {
push @{$c->{raw}}, $_;
- } elsif (/^[ACRMDT]\t/) {
+ } elsif (/^${_esc_color}[ACRMDT]\t/) {
# we could add $SVN->{svn_path} here, but that requires
# remote access at the moment (repo_path_split)...
- s#^([ACRMDT])\t# $1 #;
+ s#^(${_esc_color})([ACRMDT])\t#$1 $2 #;
push @{$c->{changed}}, $_;
- } elsif (/^diff /) {
+ } elsif (/^${_esc_color}diff /) {
$d = 1;
push @{$c->{diff}}, $_;
} elsif ($d) {
push @{$c->{diff}}, $_;
- } elsif (/^ (git-svn-id:.+)$/) {
+ } elsif (/^${_esc_color} (git-svn-id:.+)$/) {
($c->{url}, $c->{r}, undef) = extract_metadata($1);
- } elsif (s/^ //) {
+ } elsif (s/^${_esc_color} //) {
push @{$c->{l}}, $_;
}
}
@@ -901,12 +911,30 @@ sub cmt_showable {
return defined $c->{r};
}
+sub log_use_color {
+ return 1 if $_color;
+ my $dc;
+ chomp($dc = `git-repo-config --get diff.color`);
+ if ($dc eq 'auto') {
+ if (-t *STDOUT || (defined $_pager &&
+ `git-repo-config --bool --get pager.color` !~ /^false/)) {
+ return ($ENV{TERM} && $ENV{TERM} ne 'dumb');
+ }
+ return 0;
+ }
+ return 0 if $dc eq 'never';
+ return 1 if $dc eq 'always';
+ chomp($dc = `git-repo-config --bool --get diff.color`);
+ $dc eq 'true';
+}
+
sub git_svn_log_cmd {
my ($r_min, $r_max) = @_;
my @cmd = (qw/git-log --abbrev-commit --pretty=raw
--default/, "refs/remotes/$GIT_SVN");
push @cmd, '-r' unless $_non_recursive;
push @cmd, qw/--raw --name-status/ if $_verbose;
+ push @cmd, '--color' if log_use_color();
return @cmd unless defined $r_max;
if ($r_max == $r_min) {
push @cmd, '--max-count=1';
@@ -1152,7 +1180,7 @@ sub graft_file_copy_lib {
while (1) {
my $pool = SVN::Pool->new;
libsvn_get_log(libsvn_dup_ra($SVN), [$path],
- $min, $max, 0, 1, 1,
+ $min, $max, 0, 2, 1,
sub {
libsvn_graft_file_copies($grafts, $tree_paths,
$path, @_);
@@ -2533,14 +2561,18 @@ sub tz_to_s_offset {
return ($1 * 60) + ($tz * 3600);
}
-sub setup_pager { # translated to Perl from pager.c
- return unless (-t *STDOUT);
- my $pager = $ENV{PAGER};
- if (!defined $pager) {
- $pager = 'less';
- } elsif (length $pager == 0 || $pager eq 'cat') {
- return;
+# adapted from pager.c
+sub config_pager {
+ $_pager ||= $ENV{GIT_PAGER} || $ENV{PAGER};
+ if (!defined $_pager) {
+ $_pager = 'less';
+ } elsif (length $_pager == 0 || $_pager eq 'cat') {
+ $_pager = undef;
}
+}
+
+sub run_pager {
+ return unless -t *STDOUT;
pipe my $rfd, my $wfd or return;
defined(my $pid = fork) or croak $!;
if (!$pid) {
@@ -2548,8 +2580,8 @@ sub setup_pager { # translated to Perl from pager.c
return;
}
open STDIN, '<&', $rfd or croak $!;
- $ENV{LESS} ||= '-S';
- exec $pager or croak "Can't run pager: $!\n";;
+ $ENV{LESS} ||= 'FRSX';
+ exec $_pager or croak "Can't run pager: $! ($_pager)\n";
}
sub get_author_info {
@@ -2675,6 +2707,9 @@ sub libsvn_load {
require SVN::Ra;
require SVN::Delta;
push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor';
+ push @SVN::Git::Fetcher::ISA, 'SVN::Delta::Editor';
+ *SVN::Git::Fetcher::process_rm = *process_rm;
+ *SVN::Git::Fetcher::safe_qx = *safe_qx;
my $kill_stupid_warnings = $SVN::Node::none.$SVN::Node::file.
$SVN::Node::dir.$SVN::Node::unknown.
$SVN::Node::none.$SVN::Node::file.
@@ -2827,6 +2862,13 @@ sub libsvn_connect {
config => $config,
pool => SVN::Pool->new,
auth_provider_callbacks => $callbacks);
+
+ my $df = $ENV{GIT_SVN_DELTA_FETCH};
+ if (defined $df) {
+ $_xfer_delta = $df;
+ } else {
+ $_xfer_delta = ($url =~ m#^file://#) ? undef : 1;
+ }
$ra->{svn_path} = $url;
$ra->{repos_root} = $ra->get_repos_root;
$ra->{svn_path} =~ s#^\Q$ra->{repos_root}\E/*##;
@@ -2896,7 +2938,7 @@ sub libsvn_log_entry {
}
sub process_rm {
- my ($gui, $last_commit, $f) = @_;
+ my ($gui, $last_commit, $f, $q) = @_;
# remove entire directories.
if (safe_qx('git-ls-tree',$last_commit,'--',$f) =~ /^040000 tree/) {
defined(my $pid = open my $ls, '-|') or croak $!;
@@ -2907,17 +2949,40 @@ sub process_rm {
local $/ = "\0";
while (<$ls>) {
print $gui '0 ',0 x 40,"\t",$_ or croak $!;
+ print "\tD\t$_\n" unless $q;
}
+ print "\tD\t$f/\n" unless $q;
close $ls or croak $?;
} else {
print $gui '0 ',0 x 40,"\t",$f,"\0" or croak $!;
+ print "\tD\t$f\n" unless $q;
}
}
sub libsvn_fetch {
+ $_xfer_delta ? libsvn_fetch_delta(@_) : libsvn_fetch_full(@_);
+}
+
+sub libsvn_fetch_delta {
+ my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
+ my $pool = SVN::Pool->new;
+ my $ed = SVN::Git::Fetcher->new({ c => $last_commit, q => $_q });
+ my $reporter = $SVN->do_update($rev, '', 1, $ed, $pool);
+ my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
+ my (undef, $last_rev, undef) = cmt_metadata($last_commit);
+ $reporter->set_path('', $last_rev, 0, @lock, $pool);
+ $reporter->finish_report($pool);
+ $pool->clear;
+ unless ($ed->{git_commit_ok}) {
+ die "SVN connection failed somewhere...\n";
+ }
+ libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]);
+}
+
+sub libsvn_fetch_full {
my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
open my $gui, '| git-update-index -z --index-info' or croak $!;
- my @amr;
+ my %amr;
my $p = $SVN->{svn_path};
foreach my $f (keys %$paths) {
my $m = $paths->{$f}->action();
@@ -2928,8 +2993,7 @@ sub libsvn_fetch {
$f =~ s#^/##;
}
if ($m =~ /^[DR]$/) {
- print "\t$m\t$f\n" unless $_q;
- process_rm($gui, $last_commit, $f);
+ process_rm($gui, $last_commit, $f, $_q);
next if $m eq 'D';
# 'R' can be file replacements, too, right?
}
@@ -2937,7 +3001,7 @@ sub libsvn_fetch {
my $t = $SVN->check_path($f, $rev, $pool);
if ($t == $SVN::Node::file) {
if ($m =~ /^[AMR]$/) {
- push @amr, [ $m, $f ];
+ $amr{$f} = $m;
} else {
die "Unrecognized action: $m, ($f r$rev)\n";
}
@@ -2945,13 +3009,13 @@ sub libsvn_fetch {
my @traversed = ();
libsvn_traverse($gui, '', $f, $rev, \@traversed);
foreach (@traversed) {
- push @amr, [ $m, $_ ]
+ $amr{$_} = $m;
}
}
$pool->clear;
}
- foreach (@amr) {
- libsvn_get_file($gui, $_->[1], $rev, $_->[0]);
+ foreach (keys %amr) {
+ libsvn_get_file($gui, $_, $rev, $amr{$_});
}
close $gui or croak $?;
return libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]);
@@ -3070,7 +3134,7 @@ sub revisions_eq {
# should be OK to use Pool here (r1 - r0) should be small
my $pool = SVN::Pool->new;
libsvn_get_log($SVN, [$path], $r0, $r1,
- 0, 1, 1, sub {$nr++}, $pool);
+ 0, 0, 1, sub {$nr++}, $pool);
$pool->clear;
} else {
my ($url, undef) = repo_path_split($SVN_URL);
@@ -3133,7 +3197,11 @@ sub libsvn_find_parent_branch {
unlink $GIT_SVN_INDEX;
print STDERR "Found branch parent: ($GIT_SVN) $parent\n";
sys(qw/git-read-tree/, $parent);
- return libsvn_fetch($parent, $paths, $rev,
+ # I can't seem to get do_switch() to work correctly with
+ # the SWIG interface (TypeError when passing switch_url...),
+ # so we'll unconditionally bypass the delta interface here
+ # for now
+ return libsvn_fetch_full($parent, $paths, $rev,
$author, $date, $msg);
}
print STDERR "Nope, branch point not imported or unknown\n";
@@ -3142,6 +3210,7 @@ sub libsvn_find_parent_branch {
sub libsvn_get_log {
my ($ra, @args) = @_;
+ $args[4]-- if $args[4] && $_xfer_delta && ! $_follow_parent;
if ($SVN::Core::VERSION le '1.2.0') {
splice(@args, 3, 1);
}
@@ -3153,9 +3222,22 @@ sub libsvn_new_tree {
return $log_entry;
}
my ($paths, $rev, $author, $date, $msg) = @_;
- open my $gui, '| git-update-index -z --index-info' or croak $!;
- libsvn_traverse($gui, '', $SVN->{svn_path}, $rev);
- close $gui or croak $?;
+ if ($_xfer_delta) {
+ my $pool = SVN::Pool->new;
+ my $ed = SVN::Git::Fetcher->new({q => $_q});
+ my $reporter = $SVN->do_update($rev, '', 1, $ed, $pool);
+ my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
+ $reporter->set_path('', $rev, 1, @lock, $pool);
+ $reporter->finish_report($pool);
+ $pool->clear;
+ unless ($ed->{git_commit_ok}) {
+ die "SVN connection failed somewhere...\n";
+ }
+ } else {
+ open my $gui, '| git-update-index -z --index-info' or croak $!;
+ libsvn_traverse($gui, '', $SVN->{svn_path}, $rev);
+ close $gui or croak $?;
+ }
return libsvn_log_entry($rev, $author, $date, $msg);
}
@@ -3239,11 +3321,11 @@ sub libsvn_commit_cb {
sub libsvn_ls_fullurl {
my $fullurl = shift;
- $SVN ||= libsvn_connect($fullurl);
+ my $ra = libsvn_connect($fullurl);
my @ret;
my $pool = SVN::Pool->new;
- my ($dirent, undef, undef) = $SVN->get_dir($SVN->{svn_path},
- $SVN->get_latest_revnum, $pool);
+ my $r = defined $_revision ? $_revision : $ra->get_latest_revnum;
+ my ($dirent, undef, undef) = $ra->get_dir('', $r, $pool);
foreach my $d (keys %$dirent) {
if ($dirent->{$d}->kind == $SVN::Node::dir) {
push @ret, "$d/"; # add '/' for compat with cli svn
@@ -3325,6 +3407,142 @@ sub copy_remote_ref {
"refs/remotes/$GIT_SVN on $origin\n";
}
}
+package SVN::Git::Fetcher;
+use vars qw/@ISA/;
+use strict;
+use warnings;
+use Carp qw/croak/;
+use IO::File qw//;
+
+# file baton members: path, mode_a, mode_b, pool, fh, blob, base
+sub new {
+ my ($class, $git_svn) = @_;
+ my $self = SVN::Delta::Editor->new;
+ bless $self, $class;
+ open my $gui, '| git-update-index -z --index-info' or croak $!;
+ $self->{gui} = $gui;
+ $self->{c} = $git_svn->{c} if exists $git_svn->{c};
+ $self->{q} = $git_svn->{q};
+ require Digest::MD5;
+ $self;
+}
+
+sub delete_entry {
+ my ($self, $path, $rev, $pb) = @_;
+ process_rm($self->{gui}, $self->{c}, $path, $self->{q});
+ undef;
+}
+
+sub open_file {
+ my ($self, $path, $pb, $rev) = @_;
+ my ($mode, $blob) = (safe_qx('git-ls-tree',$self->{c},'--',$path)
+ =~ /^(\d{6}) blob ([a-f\d]{40})\t/);
+ { path => $path, mode_a => $mode, mode_b => $mode, blob => $blob,
+ pool => SVN::Pool->new, action => 'M' };
+}
+
+sub add_file {
+ my ($self, $path, $pb, $cp_path, $cp_rev) = @_;
+ { path => $path, mode_a => 100644, mode_b => 100644,
+ pool => SVN::Pool->new, action => 'A' };
+}
+
+sub change_file_prop {
+ my ($self, $fb, $prop, $value) = @_;
+ if ($prop eq 'svn:executable') {
+ if ($fb->{mode_b} != 120000) {
+ $fb->{mode_b} = defined $value ? 100755 : 100644;
+ }
+ } elsif ($prop eq 'svn:special') {
+ $fb->{mode_b} = defined $value ? 120000 : 100644;
+ }
+ undef;
+}
+
+sub apply_textdelta {
+ my ($self, $fb, $exp) = @_;
+ my $fh = IO::File->new_tmpfile;
+ $fh->autoflush(1);
+ # $fh gets auto-closed() by SVN::TxDelta::apply(),
+ # (but $base does not,) so dup() it for reading in close_file
+ open my $dup, '<&', $fh or croak $!;
+ my $base = IO::File->new_tmpfile;
+ $base->autoflush(1);
+ if ($fb->{blob}) {
+ defined (my $pid = fork) or croak $!;
+ if (!$pid) {
+ open STDOUT, '>&', $base or croak $!;
+ print STDOUT 'link ' if ($fb->{mode_a} == 120000);
+ exec qw/git-cat-file blob/, $fb->{blob} or croak $!;
+ }
+ waitpid $pid, 0;
+ croak $? if $?;
+
+ if (defined $exp) {
+ seek $base, 0, 0 or croak $!;
+ my $md5 = Digest::MD5->new;
+ $md5->addfile($base);
+ my $got = $md5->hexdigest;
+ die "Checksum mismatch: $fb->{path} $fb->{blob}\n",
+ "expected: $exp\n",
+ " got: $got\n" if ($got ne $exp);
+ }
+ }
+ seek $base, 0, 0 or croak $!;
+ $fb->{fh} = $dup;
+ $fb->{base} = $base;
+ [ SVN::TxDelta::apply($base, $fh, undef, $fb->{path}, $fb->{pool}) ];
+}
+
+sub close_file {
+ my ($self, $fb, $exp) = @_;
+ my $hash;
+ my $path = $fb->{path};
+ if (my $fh = $fb->{fh}) {
+ seek($fh, 0, 0) or croak $!;
+ my $md5 = Digest::MD5->new;
+ $md5->addfile($fh);
+ my $got = $md5->hexdigest;
+ die "Checksum mismatch: $path\n",
+ "expected: $exp\n got: $got\n" if ($got ne $exp);
+ seek($fh, 0, 0) or croak $!;
+ if ($fb->{mode_b} == 120000) {
+ read($fh, my $buf, 5) == 5 or croak $!;
+ $buf eq 'link ' or die "$path has mode 120000",
+ "but is not a link\n";
+ }
+ defined(my $pid = open my $out,'-|') or die "Can't fork: $!\n";
+ if (!$pid) {
+ open STDIN, '<&', $fh or croak $!;
+ exec qw/git-hash-object -w --stdin/ or croak $!;
+ }
+ chomp($hash = do { local $/; <$out> });
+ close $out or croak $!;
+ close $fh or croak $!;
+ $hash =~ /^[a-f\d]{40}$/ or die "not a sha1: $hash\n";
+ close $fb->{base} or croak $!;
+ } else {
+ $hash = $fb->{blob} or die "no blob information\n";
+ }
+ $fb->{pool}->clear;
+ my $gui = $self->{gui};
+ print $gui "$fb->{mode_b} $hash\t$path\0" or croak $!;
+ print "\t$fb->{action}\t$path\n" if $fb->{action} && ! $self->{q};
+ undef;
+}
+
+sub abort_edit {
+ my $self = shift;
+ close $self->{gui};
+ $self->SUPER::abort_edit(@_);
+}
+
+sub close_edit {
+ my $self = shift;
+ close $self->{gui} or croak $!;
+ $self->{git_commit_ok} = 1;
+ $self->SUPER::close_edit(@_);
+}
package SVN::Git::Editor;
use vars qw/@ISA/;
diff --git a/gitk b/gitk
index ab383b3ad2..3dabc69516 100755
--- a/gitk
+++ b/gitk
@@ -554,7 +554,7 @@ proc makewindow {} {
pack .ctop.top.lbar.vlabel -side left -fill y
global viewhlmenu selectedhlview
set viewhlmenu [tk_optionMenu .ctop.top.lbar.vhl selectedhlview None]
- $viewhlmenu entryconf 0 -command delvhighlight
+ $viewhlmenu entryconf None -command delvhighlight
$viewhlmenu conf -font $uifont
.ctop.top.lbar.vhl conf -font $uifont
pack .ctop.top.lbar.vhl -side left -fill y
@@ -1474,7 +1474,7 @@ proc doviewmenu {m first cmd op argv} {
proc allviewmenus {n op args} {
global viewhlmenu
- doviewmenu .bar.view 7 [list showview $n] $op $args
+ doviewmenu .bar.view 5 [list showview $n] $op $args
doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
}
@@ -1516,7 +1516,7 @@ proc newviewok {top n} {
set viewperm($n) $newviewperm($n)
if {$newviewname($n) ne $viewname($n)} {
set viewname($n) $newviewname($n)
- doviewmenu .bar.view 7 [list showview $n] \
+ doviewmenu .bar.view 5 [list showview $n] \
entryconf [list -label $viewname($n)]
doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
entryconf [list -label $viewname($n) -value $viewname($n)]
@@ -1632,8 +1632,8 @@ proc showview {n} {
set curview $n
set selectedview $n
- .bar.view entryconf 2 -state [expr {$n == 0? "disabled": "normal"}]
- .bar.view entryconf 3 -state [expr {$n == 0? "disabled": "normal"}]
+ .bar.view entryconf Edit* -state [expr {$n == 0? "disabled": "normal"}]
+ .bar.view entryconf Delete* -state [expr {$n == 0? "disabled": "normal"}]
if {![info exists viewdata($n)]} {
set pending_select $selid
@@ -4899,9 +4899,9 @@ proc rowmenu {x y id} {
} else {
set state normal
}
- $rowctxmenu entryconfigure 0 -state $state
- $rowctxmenu entryconfigure 1 -state $state
- $rowctxmenu entryconfigure 2 -state $state
+ $rowctxmenu entryconfigure "Diff this*" -state $state
+ $rowctxmenu entryconfigure "Diff selected*" -state $state
+ $rowctxmenu entryconfigure "Make patch" -state $state
set rowmenuid $id
tk_popup $rowctxmenu $x $y
}
@@ -6305,8 +6305,8 @@ if {$cmdline_files ne {} || $revtreeargs ne {}} {
set viewargs(1) $revtreeargs
set viewperm(1) 0
addviewmenu 1
- .bar.view entryconf 2 -state normal
- .bar.view entryconf 3 -state normal
+ .bar.view entryconf Edit* -state normal
+ .bar.view entryconf Delete* -state normal
}
if {[info exists permviews]} {
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 093bd72058..ffe8ce13ff 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -4282,7 +4282,7 @@ XML
}
if (defined $logo_url) {
# not twice as wide as tall: 72 x 27 pixels
- print "<logo>" . esc_url($logo_url) . "</logo>\n";
+ print "<logo>" . esc_url($logo) . "</logo>\n";
}
if (! %latest_date) {
# dummy date to keep the feed valid until commits trickle in:
diff --git a/ident.c b/ident.c
index efec97ffd0..e415fd3588 100644
--- a/ident.c
+++ b/ident.c
@@ -158,12 +158,17 @@ static int copy(char *buf, int size, int offset, const char *src)
static const char au_env[] = "GIT_AUTHOR_NAME";
static const char co_env[] = "GIT_COMMITTER_NAME";
static const char *env_hint =
-"\n*** Environment problem:\n"
+"\n"
"*** Your name cannot be determined from your system services (gecos).\n"
-"*** You would need to set %s and %s\n"
-"*** environment variables; otherwise you won't be able to perform\n"
-"*** certain operations because of \"empty ident\" errors.\n"
-"*** Alternatively, you can use user.name configuration variable.\n\n";
+"\n"
+"Run\n"
+"\n"
+" git repo-config user.email \"you@email.com\"\n"
+" git repo-config user.name \"Your Name\"\n"
+"\n"
+"To set the identity in this repository.\n"
+"Add --global to set your account\'s default\n"
+"\n";
static const char *get_ident(const char *name, const char *email,
const char *date_str, int error_on_no_name)
diff --git a/perl/.gitignore b/perl/.gitignore
index e990caeea7..98b24772c7 100644
--- a/perl/.gitignore
+++ b/perl/.gitignore
@@ -1,4 +1,5 @@
-Makefile
+perl.mak
+perl.mak.old
blib
blibdirs
pm_to_blib
diff --git a/perl/Makefile b/perl/Makefile
new file mode 100644
index 0000000000..bd483b0997
--- /dev/null
+++ b/perl/Makefile
@@ -0,0 +1,39 @@
+#
+# Makefile for perl support modules and routine
+#
+makfile:=perl.mak
+
+PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
+prefix_SQ = $(subst ','\'',$(prefix))
+
+all install instlibdir: $(makfile)
+ $(MAKE) -f $(makfile) $@
+
+clean:
+ test -f $(makfile) && $(MAKE) -f $(makfile) $@ || exit 0
+ $(RM) ppport.h
+ $(RM) $(makfile)
+ $(RM) $(makfile).old
+
+ifdef NO_PERL_MAKEMAKER
+instdir_SQ = $(subst ','\'',$(prefix)/lib)
+$(makfile): ../GIT-CFLAGS Makefile
+ echo all: > $@
+ echo ' :' >> $@
+ echo install: >> $@
+ echo ' mkdir -p $(instdir_SQ)' >> $@
+ echo ' $(RM) $(instdir_SQ)/Git.pm; cp Git.pm $(instdir_SQ)' >> $@
+ echo ' $(RM) $(instdir_SQ)/Error.pm; \
+ cp private-Error.pm $(instdir_SQ)/Error.pm' >> $@
+ echo instlibdir: >> $@
+ echo ' echo $(instdir_SQ)' >> $@
+else
+$(makfile): Makefile.PL ../GIT-CFLAGS
+ '$(PERL_PATH_SQ)' $< FIRST_MAKEFILE='$@' PREFIX='$(prefix_SQ)'
+endif
+
+# this is just added comfort for calling make directly in perl dir
+# (even though GIT-CFLAGS aren't used yet. If ever)
+../GIT-CFLAGS:
+ $(MAKE) -C .. GIT-CFLAGS
+
diff --git a/receive-pack.c b/receive-pack.c
index 1a141dc1e5..a20bc924d6 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -120,7 +120,8 @@ static int update(struct command *cmd)
"but I can't find it!", new_hex);
}
if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
- !is_null_sha1(old_sha1)) {
+ !is_null_sha1(old_sha1) &&
+ !strncmp(name, "refs/heads/", 11)) {
struct commit *old_commit, *new_commit;
struct commit_list *bases, *ent;
diff --git a/t/Makefile b/t/Makefile
index e1c6c92d9c..c9bd9a4690 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -23,8 +23,9 @@ clean:
# we can test NO_OPTIMIZE_COMMITS independently of LC_ALL
full-svn-test:
+ $(MAKE) $(TSVN) GIT_SVN_NO_LIB=0 GIT_SVN_DELTA_FETCH=1 \
+ GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
$(MAKE) $(TSVN) GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
- $(MAKE) $(TSVN) GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
$(MAKE) $(TSVN) GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \
LC_ALL=en_US.UTF-8
$(MAKE) $(TSVN) GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 29a1e72c61..63c670304f 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -45,6 +45,6 @@ else
svnadmin create "$svnrepo"
fi
-svnrepo="file://$svnrepo/test-git-svn"
+svnrepo="file://$svnrepo"
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 1bc5b7a412..adf4993bac 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -109,12 +109,10 @@ index d99af23..8b32fb5 100644
+ whitespace at beginning
whitespace change
-whitespace in the middle
--whitespace at end
+white space in the middle
-+whitespace at end
+ whitespace at end
unchanged line
--CR at endQ
-+CR at end
+ CR at endQ
EOF
git-diff -b > out
test_expect_success 'another test, with -b' 'diff -u expect out'
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 23a1eff3bb..2f4ff82e14 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -105,4 +105,17 @@ test_expect_success "Michael Cassar's test case" '
}
'
+rm -fr papers partA path?
+
+test_expect_success "Sergey Vlasov's test case" '
+ rm -fr .git &&
+ git init-db &&
+ mkdir ab &&
+ date >ab.c &&
+ date >ab/d &&
+ git add ab.c ab &&
+ git commit -m 'initial' &&
+ git mv ab a
+'
+
test_done
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index 34a3ccd31c..f9de232366 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -228,6 +228,11 @@ tree 56a30b966619b863674f5978696f4a3594f2fca9
tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e
tree 8f51f74cf0163afc9ad68a4b1537288c4558b5a4
EOF
+
+if test -z "$GIT_SVN_NO_LIB" || test "$GIT_SVN_NO_LIB" -eq 0; then
+ echo tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 >> expected
+fi
+
test_expect_success "$name" "diff -u a expected"
test_done
diff --git a/t/t9103-git-svn-graft-branches.sh b/t/t9103-git-svn-graft-branches.sh
index cc62d4ece8..293b98f928 100755
--- a/t/t9103-git-svn-graft-branches.sh
+++ b/t/t9103-git-svn-graft-branches.sh
@@ -1,6 +1,8 @@
test_description='git-svn graft-branches'
. ./lib-git-svn.sh
+svnrepo="$svnrepo/test-git-svn"
+
test_expect_success 'initialize repo' "
mkdir import &&
cd import &&
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index 6e566d4409..c1024790e4 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -142,4 +142,20 @@ test_expect_success \
diff F/newfile6.png ../F/newfile6.png
)'
+test_expect_success 'Retain execute bit' '
+ mkdir G &&
+ echo executeon >G/on &&
+ chmod +x G/on &&
+ echo executeoff >G/off &&
+ git add G/on &&
+ git add G/off &&
+ git commit -a -m "Execute test" &&
+ (
+ cd "$CVSWORK" &&
+ git-cvsexportcommit -c HEAD
+ test -x G/on &&
+ ! test -x G/off
+ )
+'
+
test_done
diff --git a/unpack-trees.c b/unpack-trees.c
index 7cfd628d8e..47aa804a86 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -370,7 +370,7 @@ int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)
int i;
struct object_list *posn = trees;
struct tree_entry_list df_conflict_list;
- struct cache_entry df_conflict_entry;
+ static struct cache_entry *dfc;
memset(&df_conflict_list, 0, sizeof(df_conflict_list));
df_conflict_list.next = &df_conflict_list;
@@ -381,8 +381,10 @@ int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)
state.refresh_cache = 1;
o->merge_size = len;
- memset(&df_conflict_entry, 0, sizeof(df_conflict_entry));
- o->df_conflict_entry = &df_conflict_entry;
+
+ if (!dfc)
+ dfc = xcalloc(1, sizeof(struct cache_entry) + 1);
+ o->df_conflict_entry = dfc;
if (len) {
posns = xmalloc(len * sizeof(struct tree_entry_list *));
diff --git a/xdiff/xutils.c b/xdiff/xutils.c
index 9e4bb47ee9..1b899f32c4 100644
--- a/xdiff/xutils.c
+++ b/xdiff/xutils.c
@@ -230,7 +230,8 @@ unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
while (ptr + 1 < top && isspace(ptr[1])
&& ptr[1] != '\n')
ptr++;
- if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
+ if (flags & XDF_IGNORE_WHITESPACE_CHANGE
+ && ptr[1] != '\n') {
ha += (ha << 5);
ha ^= (unsigned long) ' ';
}