summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Documentation/Makefile4
-rw-r--r--Documentation/git-apply.txt7
-rw-r--r--Documentation/git-applymbox.txt10
-rw-r--r--Documentation/git-format-patch.txt6
-rw-r--r--Documentation/git-mailsplit.txt13
-rw-r--r--Documentation/git-tag.txt22
-rw-r--r--Makefile122
-rw-r--r--apply.c127
-rw-r--r--cache.h17
-rw-r--r--checkout-index.c9
-rw-r--r--compat/mmap.c50
-rw-r--r--connect.c73
-rw-r--r--daemon.c94
-rw-r--r--diff.c77
-rw-r--r--entry.c2
-rw-r--r--fsck-objects.c5
-rwxr-xr-xgit-am.sh337
-rwxr-xr-xgit-applymbox.sh17
-rwxr-xr-xgit-applypatch.sh92
-rwxr-xr-xgit-commit.sh28
-rwxr-xr-xgit-format-patch.sh75
-rwxr-xr-xgit-ls-remote.sh2
-rwxr-xr-xgit-parse-remote.sh2
-rwxr-xr-xgit-rename.perl2
-rwxr-xr-xgit-sh-setup.sh2
-rwxr-xr-xgit-shortlog.perl143
-rwxr-xr-xgit-tag.sh18
-rwxr-xr-xgit.sh12
-rw-r--r--mailsplit.c171
-rw-r--r--prune-packed.c5
-rw-r--r--server-info.c10
-rw-r--r--sha1_file.c153
-rwxr-xr-xt/diff-lib.sh10
-rwxr-xr-xt/t0000-basic.sh2
-rwxr-xr-xt/t4000-diff-format.sh3
-rwxr-xr-xt/t4001-diff-rename.sh3
-rwxr-xr-xt/t4004-diff-rename-symlink.sh3
-rw-r--r--update-index.c86
-rw-r--r--update-server-info.c3
40 files changed, 1410 insertions, 408 deletions
diff --git a/.gitignore b/.gitignore
index e90e2c3503..d190c0ad07 100644
--- a/.gitignore
+++ b/.gitignore
@@ -101,3 +101,4 @@ git-core-*/?*
*.dsc
*.deb
git-core.spec
+*.exe
diff --git a/Documentation/Makefile b/Documentation/Makefile
index bb21d6af44..3cfa360a9e 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -17,14 +17,14 @@ DOC_HTML += $(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES))
DOC_MAN1=$(patsubst %.txt,%.1,$(MAN1_TXT))
DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT))
-prefix=$(HOME)
+prefix?=$(HOME)
bin=$(prefix)/bin
mandir=$(prefix)/man
man1=$(mandir)/man1
man7=$(mandir)/man7
# DESTDIR=
-INSTALL=install
+INSTALL?=install
#
# Please note that there is a minor bug in asciidoc.
diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
index 8cbbb4b853..e095f93c08 100644
--- a/Documentation/git-apply.txt
+++ b/Documentation/git-apply.txt
@@ -9,7 +9,7 @@ git-apply - Apply patch on a GIT index file and a work tree
SYNOPSIS
--------
-'git-apply' [--no-merge] [--stat] [--summary] [--check] [--index] [--show-files] [--apply] [<patch>...]
+'git-apply' [--stat] [--summary] [--check] [--index] [--show-files] [--apply] [<patch>...]
DESCRIPTION
-----------
@@ -22,11 +22,6 @@ OPTIONS
The files to read patch from. '-' can be used to read
from the standard input.
---no-merge::
- The default mode of operation is the merge behaviour
- which is not implemented yet. This flag explicitly
- tells the program not to use the merge behaviour.
-
--stat::
Instead of applying the patch, output diffstat for the
input. Turns off "apply".
diff --git a/Documentation/git-applymbox.txt b/Documentation/git-applymbox.txt
index bb543788c0..8f01ca6a16 100644
--- a/Documentation/git-applymbox.txt
+++ b/Documentation/git-applymbox.txt
@@ -8,7 +8,7 @@ git-applymbox - Apply a series of patches in a mailbox
SYNOPSIS
--------
-'git-applymbox' [-u] [-k] [-q] ( -c .dotest/<num> | <mbox> ) [ <signoff> ]
+'git-applymbox' [-u] [-k] [-q] [-m] ( -c .dotest/<num> | <mbox> ) [ <signoff> ]
DESCRIPTION
-----------
@@ -33,6 +33,14 @@ OPTIONS
munging, and is most useful when used to read back 'git
format-patch --mbox' output.
+-m::
+ Patches are applied with `git-apply` command, and unless
+ it cleanly applies without fuzz, the processing fails.
+ With this flag, if a tree that the patch applies cleanly
+ is found in a repository, the patch is applied to the
+ tree and then a 3-way merge between the resulting tree
+ and the current tree.
+
-u::
By default, the commit log message, author name and
author email are taken from the e-mail without any
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index a1483ffd0f..f3ef4c1e04 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -8,7 +8,7 @@ git-format-patch - Prepare patches for e-mail submission.
SYNOPSIS
--------
-'git-format-patch' [-n][-o <dir>][-k][--mbox][--diff-options] <his> [<mine>]
+'git-format-patch' [-n][-o <dir>|--stdout][-k][--mbox][--diff-options] <his> [<mine>]
DESCRIPTION
-----------
@@ -54,6 +54,10 @@ OPTIONS
concatenated together and fed to `git-applymbox`.
Implies --author and --date.
+--stdout::
+ This flag generates the mbox formatted output to the
+ standard output, instead of saving them into a file per
+ patch and implies --mbox.
Author
------
diff --git a/Documentation/git-mailsplit.txt b/Documentation/git-mailsplit.txt
index 557d2e9056..03a9477664 100644
--- a/Documentation/git-mailsplit.txt
+++ b/Documentation/git-mailsplit.txt
@@ -7,7 +7,7 @@ git-mailsplit - Totally braindamaged mbox splitter program.
SYNOPSIS
--------
-'git-mailsplit' <mbox> <directory>
+'git-mailsplit' [-d<prec>] [<mbox>] <directory>
DESCRIPTION
-----------
@@ -17,14 +17,23 @@ directory so you can process them further from there.
OPTIONS
-------
<mbox>::
- Mbox file to split.
+ Mbox file to split. If not given, the mbox is read from
+ the standard input.
<directory>::
Directory in which to place the individual messages.
+-d<prec>::
+ Instead of the default 4 digits with leading zeros,
+ different precision can be specified for the generated
+ filenames.
+
+
Author
------
Written by Linus Torvalds <torvalds@osdl.org>
+and Junio C Hamano <junkio@cox.net>
+
Documentation
--------------
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index e11f51c266..3984812cec 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -8,19 +8,27 @@ git-tag - Create a tag object signed with GPG
SYNOPSIS
--------
-'git-tag' [-a | -s] [-f] [-m <msg>] <name> [<head>]
+'git-tag' [-a | -s | -u <key-id>] [-f] [-m <msg>] <name> [<head>]
DESCRIPTION
-----------
-Adds a "tag" reference in .git/refs/tags/
+Adds a 'tag' reference in .git/refs/tags/
-Unless "-f" is given, the tag must not yet exist in ".git/refs/tags"
+Unless `-f` is given, the tag must not yet exist in
+`.git/refs/tags/` directory.
-If "-s" or "-a" is passed, the user will be prompted for a tag message.
-and a tag object is created. Otherwise just the SHA1 object
-name of the commit object is written.
+If one of `-a`, `-s`, or `-u <key-id>` is passed, the command
+creates a 'tag' object, and requires the tag message. Unless
+`-m <msg>` is given, an editor is started for the user to type
+in the tag message.
-A GnuPG signed tag object will be created when "-s" is used.
+Otherwise just the SHA1 object name of the commit object is
+written (i.e. an lightweight tag).
+
+A GnuPG signed tag object will be created when `-s` or `-u
+<key-id>` is used. When `-u <key-id>` is not used, the
+committer identity for the current user is used to find the
+GnuPG key for signing.
Author
diff --git a/Makefile b/Makefile
index b8ca504f09..ac384c7dc4 100644
--- a/Makefile
+++ b/Makefile
@@ -27,8 +27,12 @@
# Define NEEDS_SOCKET if linking with libc is not enough (SunOS,
# Patrick Mauritz).
#
+# Define NO_MMAP if you want to avoid mmap.
+#
# Define WITH_OWN_SUBPROCESS_PY if you want to use with python 2.3.
#
+# Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
+#
# Define COLLISION_CHECK below if you believe that SHA1's
# 1461501637330902918203684832716283019655932542976 hashes do not give you
# sufficient guarantee that no collisions between objects will ever happen.
@@ -48,7 +52,7 @@
# DEFINES += -DUSE_STDEV
-GIT_VERSION = 0.99.8
+GIT_VERSION = 0.99.8.GIT
CFLAGS = -g -O2 -Wall
ALL_CFLAGS = $(CFLAGS) $(PLATFORM_DEFINES) $(DEFINES)
@@ -83,7 +87,7 @@ SCRIPT_SH = \
git-repack.sh git-request-pull.sh git-reset.sh \
git-resolve.sh git-revert.sh git-sh-setup.sh git-status.sh \
git-tag.sh git-verify-tag.sh git-whatchanged.sh git.sh \
- git-applymbox.sh git-applypatch.sh \
+ git-applymbox.sh git-applypatch.sh git-am.sh \
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
git-merge-resolve.sh git-grep.sh
@@ -96,31 +100,31 @@ SCRIPT_PYTHON = \
# The ones that do not have to link with lcrypto nor lz.
SIMPLE_PROGRAMS = \
- git-get-tar-commit-id git-mailinfo git-mailsplit git-stripspace \
- git-daemon git-var
+ git-get-tar-commit-id$X git-mailinfo$X git-mailsplit$X \
+ git-stripspace$X git-var$X git-daemon$X
# ... and all the rest
PROGRAMS = \
- git-apply git-cat-file \
- git-checkout-index git-clone-pack git-commit-tree \
- git-convert-objects git-diff-files \
- git-diff-index git-diff-stages \
- git-diff-tree git-fetch-pack git-fsck-objects \
- git-hash-object git-init-db \
- git-local-fetch git-ls-files git-ls-tree git-merge-base \
- git-merge-index git-mktag git-pack-objects git-patch-id \
- git-peek-remote git-prune-packed git-read-tree \
- git-receive-pack git-rev-list git-rev-parse \
- git-send-pack git-show-branch \
- git-show-index git-ssh-fetch \
- git-ssh-upload git-tar-tree git-unpack-file \
- git-unpack-objects git-update-index git-update-server-info \
- git-upload-pack git-verify-pack git-write-tree \
- git-update-ref git-symbolic-ref \
+ git-apply$X git-cat-file$X \
+ git-checkout-index$X git-clone-pack$X git-commit-tree$X \
+ git-convert-objects$X git-diff-files$X \
+ git-diff-index$X git-diff-stages$X \
+ git-diff-tree$X git-fetch-pack$X git-fsck-objects$X \
+ git-hash-object$X git-init-db$X \
+ git-local-fetch$X git-ls-files$X git-ls-tree$X git-merge-base$X \
+ git-merge-index$X git-mktag$X git-pack-objects$X git-patch-id$X \
+ git-peek-remote$X git-prune-packed$X git-read-tree$X \
+ git-receive-pack$X git-rev-list$X git-rev-parse$X \
+ git-send-pack$X git-show-branch$X \
+ git-show-index$X git-ssh-fetch$X \
+ git-ssh-upload$X git-tar-tree$X git-unpack-file$X \
+ git-unpack-objects$X git-update-index$X git-update-server-info$X \
+ git-upload-pack$X git-verify-pack$X git-write-tree$X \
+ git-update-ref$X git-symbolic-ref$X \
$(SIMPLE_PROGRAMS)
# Backward compatibility -- to be removed after 1.0
-PROGRAMS += git-ssh-pull git-ssh-push
+PROGRAMS += git-ssh-pull$X git-ssh-push$X
GIT_LIST_TWEAK =
@@ -162,11 +166,19 @@ LIBS += -lz
#
# Platform specific tweaks
#
-ifeq ($(shell uname -s),Darwin)
+
+# We choose to avoid "if .. else if .. else .. endif endif"
+# because maintaining the nesting to match is a pain. If
+# we had "elif" things would have been much nicer...
+uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
+uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
+uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not')
+
+ifeq ($(uname_S),Darwin)
NEEDS_SSL_WITH_CRYPTO = YesPlease
NEEDS_LIBICONV = YesPlease
endif
-ifeq ($(shell uname -s),SunOS)
+ifeq ($(uname_S),SunOS)
NEEDS_SOCKET = YesPlease
NEEDS_NSL = YesPlease
SHELL_PATH = /bin/bash
@@ -176,13 +188,20 @@ ifeq ($(shell uname -s),SunOS)
TAR = gtar
PLATFORM_DEFINES += -D__EXTENSIONS__
endif
-ifneq (,$(findstring arm,$(shell uname -m)))
- ARM_SHA1 = YesPlease
+ifeq ($(uname_O),Cygwin)
+ NO_STRCASESTR = YesPlease
+ NEEDS_LIBICONV = YesPlease
+ NO_IPV6 = YesPlease
+ X = .exe
+ PLATFORM_DEFINES += -DUSE_SYMLINK_HEAD=0
endif
-ifeq ($(shell uname -s),OpenBSD)
+ifeq ($(uname_S),OpenBSD)
NEEDS_LIBICONV = YesPlease
PLATFORM_DEFINES += -I/usr/local/include -L/usr/local/lib
endif
+ifneq (,$(findstring arm,$(uname_M)))
+ ARM_SHA1 = YesPlease
+endif
ifndef NO_CURL
ifdef CURLDIR
@@ -192,7 +211,7 @@ ifndef NO_CURL
else
CURL_LIBCURL = -lcurl
endif
- PROGRAMS += git-http-fetch
+ PROGRAMS += git-http-fetch$X
endif
ifndef SHELL_PATH
@@ -249,6 +268,13 @@ ifdef NO_STRCASESTR
DEFINES += -Dstrcasestr=gitstrcasestr -DNO_STRCASESTR=1
LIB_OBJS += compat/strcasestr.o
endif
+ifdef NO_MMAP
+ DEFINES += -Dmmap=gitfakemmap -Dmunmap=gitfakemunmap -DNO_MMAP
+ LIB_OBJS += compat/mmap.o
+endif
+ifdef NO_IPV6
+ DEFINES += -DNO_IPV6 -Dsockaddr_storage=sockaddr_in
+endif
ifdef PPC_SHA1
SHA1_HEADER = "ppc/sha1.h"
@@ -275,7 +301,7 @@ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
$(patsubst %.py,%,$(SCRIPT_PYTHON)) \
gitk
-export TAR INSTALL DESTDIR SHELL_PATH
+export prefix TAR INSTALL DESTDIR SHELL_PATH
### Build rules
all: $(PROGRAMS) $(SCRIPTS)
@@ -287,25 +313,31 @@ git: git.sh Makefile
rm -f $@+ $@
sed -e '1s|#!.*/sh|#!$(SHELL_PATH)|' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+ -e 's/@@X@@/$(X)/g' \
$(GIT_LIST_TWEAK) <$@.sh >$@+
chmod +x $@+
mv $@+ $@
$(filter-out git,$(patsubst %.sh,%,$(SCRIPT_SH))) : % : %.sh
rm -f $@
- sed -e '1s|#!.*/sh|#!$(SHELL_PATH)|' $@.sh >$@
+ sed -e '1s|#!.*/sh|#!$(SHELL_PATH)|' \
+ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+ $@.sh >$@
chmod +x $@
$(patsubst %.perl,%,$(SCRIPT_PERL)) : % : %.perl
rm -f $@
- sed -e '1s|#!.*perl|#!$(PERL_PATH)|' $@.perl >$@
+ sed -e '1s|#!.*perl|#!$(PERL_PATH)|' \
+ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+ $@.perl >$@
chmod +x $@
$(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py
rm -f $@
sed -e '1s|#!.*python|#!$(PYTHON_PATH)|' \
-e 's|@@GIT_PYTHON_PATH@@|$(GIT_PYTHON_DIR)|g' \
- $@.py >$@
+ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+ $@.py >$@
chmod +x $@
%.o: %.c
@@ -313,30 +345,30 @@ $(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py
%.o: %.S
$(CC) -o $*.o -c $(ALL_CFLAGS) $<
-git-%: %.o $(LIB_FILE)
+git-%$X: %.o $(LIB_FILE)
$(CC) $(ALL_CFLAGS) -o $@ $(filter %.o,$^) $(LIBS)
-git-mailinfo : SIMPLE_LIB += $(LIB_4_ICONV)
+git-mailinfo$X : SIMPLE_LIB += $(LIB_4_ICONV)
$(SIMPLE_PROGRAMS) : $(LIB_FILE)
-$(SIMPLE_PROGRAMS) : git-% : %.o
+$(SIMPLE_PROGRAMS) : git-%$X : %.o
$(CC) $(ALL_CFLAGS) -o $@ $(filter %.o,$^) $(LIB_FILE) $(SIMPLE_LIB)
-git-http-fetch: fetch.o
-git-local-fetch: fetch.o
-git-ssh-fetch: rsh.o fetch.o
-git-ssh-upload: rsh.o
-git-ssh-pull: rsh.o fetch.o
-git-ssh-push: rsh.o
+git-http-fetch$X: fetch.o
+git-local-fetch$X: fetch.o
+git-ssh-fetch$X: rsh.o fetch.o
+git-ssh-upload$X: rsh.o
+git-ssh-pull$X: rsh.o fetch.o
+git-ssh-push$X: rsh.o
-git-http-fetch: LIBS += $(CURL_LIBCURL)
-git-rev-list: LIBS += $(OPENSSL_LIBSSL)
+git-http-fetch$X: LIBS += $(CURL_LIBCURL)
+git-rev-list$X: LIBS += $(OPENSSL_LIBSSL)
init-db.o: init-db.c
$(CC) -c $(ALL_CFLAGS) \
-DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir)"' $*.c
$(LIB_OBJS): $(LIB_H)
-$(patsubst git-%,%.o,$(PROGRAMS)): $(LIB_H)
+$(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H)
$(DIFF_OBJS): diffcore.h
$(LIB_FILE): $(LIB_OBJS)
@@ -351,10 +383,10 @@ doc:
test: all
$(MAKE) -C t/ all
-test-date: test-date.c date.o
+test-date$X: test-date.c date.o
$(CC) $(ALL_CFLAGS) -o $@ test-date.c date.o
-test-delta: test-delta.c diff-delta.o patch-delta.o
+test-delta$X: test-delta.c diff-delta.o patch-delta.o
$(CC) $(ALL_CFLAGS) -o $@ $^
check:
diff --git a/apply.c b/apply.c
index f8862722fd..155fbe84da 100644
--- a/apply.c
+++ b/apply.c
@@ -5,26 +5,17 @@
*
* This applies patches on top of some (arbitrary) version of the SCM.
*
- * NOTE! It does all its work in the index file, and only cares about
- * the files in the working directory if you tell it to "merge" the
- * patch apply.
- *
- * Even when merging it always takes the source from the index, and
- * uses the working tree as a "branch" for a 3-way merge.
*/
#include <ctype.h>
#include <fnmatch.h>
#include "cache.h"
-// We default to the merge behaviour, since that's what most people would
-// expect.
-//
// --check turns on checking that the working tree matches the
// files that are being modified, but doesn't apply the patch
// --stat does just a diffstat, and doesn't actually apply
// --show-files shows the directory changes
+// --show-index-info shows the old and new index info for paths if available.
//
-static int merge_patch = 1;
static int check_index = 0;
static int write_index = 0;
static int diffstat = 0;
@@ -32,8 +23,9 @@ static int summary = 0;
static int check = 0;
static int apply = 1;
static int show_files = 0;
+static int show_index_info = 0;
static const char apply_usage[] =
-"git-apply [--no-merge] [--stat] [--summary] [--check] [--index] [--apply] [--show-files] <patch>...";
+"git-apply [--stat] [--summary] [--check] [--index] [--apply] [--show-files] [--show-index-info] <patch>...";
/*
* For "diff-stat" like behaviour, we keep track of the biggest change
@@ -66,6 +58,8 @@ struct patch {
struct fragment *fragments;
char *result;
unsigned long resultsize;
+ char old_sha1_prefix[41];
+ char new_sha1_prefix[41];
struct patch *next;
};
@@ -344,6 +338,38 @@ static int gitdiff_dissimilarity(const char *line, struct patch *patch)
return 0;
}
+static int gitdiff_index(const char *line, struct patch *patch)
+{
+ /* index line is N hexadecimal, "..", N hexadecimal,
+ * and optional space with octal mode.
+ */
+ const char *ptr, *eol;
+ int len;
+
+ ptr = strchr(line, '.');
+ if (!ptr || ptr[1] != '.' || 40 <= ptr - line)
+ return 0;
+ len = ptr - line;
+ memcpy(patch->old_sha1_prefix, line, len);
+ patch->old_sha1_prefix[len] = 0;
+
+ line = ptr + 2;
+ ptr = strchr(line, ' ');
+ eol = strchr(line, '\n');
+
+ if (!ptr || eol < ptr)
+ ptr = eol;
+ len = ptr - line;
+
+ if (40 <= len)
+ return 0;
+ memcpy(patch->new_sha1_prefix, line, len);
+ patch->new_sha1_prefix[len] = 0;
+ if (*ptr == ' ')
+ patch->new_mode = patch->old_mode = strtoul(ptr+1, NULL, 8);
+ return 0;
+}
+
/*
* This is normal for a diff that doesn't change anything: we'll fall through
* into the next diff. Tell the parser to break out.
@@ -448,6 +474,7 @@ static int parse_git_header(char *line, int len, unsigned int size, struct patch
{ "rename to ", gitdiff_renamedst },
{ "similarity index ", gitdiff_similarity },
{ "dissimilarity index ", gitdiff_dissimilarity },
+ { "index ", gitdiff_index },
{ "", gitdiff_unrecognized },
};
int i;
@@ -676,7 +703,10 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
/* We allow "\ No newline at end of file". Depending
* on locale settings when the patch was produced we
* don't know what this line looks like. The only
- * thing we do know is that it begins with "\ ". */
+ * thing we do know is that it begins with "\ ".
+ * Checking for 12 is just for sanity check -- any
+ * l10n of "\ No newline..." is at least that long.
+ */
case '\\':
if (len < 12 || memcmp(line, "\\ ", 2))
return -1;
@@ -1030,17 +1060,39 @@ static int check_patch(struct patch *patch)
if (old_name) {
int changed;
+ int stat_ret = lstat(old_name, &st);
- if (lstat(old_name, &st) < 0)
- return error("%s: %s", old_name, strerror(errno));
if (check_index) {
int pos = cache_name_pos(old_name, strlen(old_name));
if (pos < 0)
- return error("%s: does not exist in index", old_name);
+ return error("%s: does not exist in index",
+ old_name);
+ if (stat_ret < 0) {
+ struct checkout costate;
+ if (errno != ENOENT)
+ return error("%s: %s", old_name,
+ strerror(errno));
+ /* checkout */
+ costate.base_dir = "";
+ costate.base_dir_len = 0;
+ costate.force = 0;
+ costate.quiet = 0;
+ costate.not_new = 0;
+ costate.refresh_cache = 1;
+ if (checkout_entry(active_cache[pos],
+ &costate) ||
+ lstat(old_name, &st))
+ return -1;
+ }
+
changed = ce_match_stat(active_cache[pos], &st);
if (changed)
- return error("%s: does not match index", old_name);
+ return error("%s: does not match index",
+ old_name);
}
+ else if (stat_ret < 0)
+ return error("%s: %s", old_name, strerror(errno));
+
if (patch->is_new < 0)
patch->is_new = 0;
st.st_mode = ntohl(create_ce_mode(st.st_mode));
@@ -1121,6 +1173,36 @@ static void show_file_list(struct patch *patch)
}
}
+static inline int is_null_sha1(const unsigned char *sha1)
+{
+ return !memcmp(sha1, null_sha1, 20);
+}
+
+static void show_index_list(struct patch *list)
+{
+ struct patch *patch;
+
+ /* Once we start supporting the reverse patch, it may be
+ * worth showing the new sha1 prefix, but until then...
+ */
+ for (patch = list; patch; patch = patch->next) {
+ const unsigned char *sha1_ptr;
+ unsigned char sha1[20];
+ const char *name;
+
+ name = patch->old_name ? patch->old_name : patch->new_name;
+ if (patch->is_new)
+ sha1_ptr = null_sha1;
+ else if (get_sha1(patch->old_sha1_prefix, sha1))
+ die("sha1 information is lacking or useless (%s).",
+ name);
+ else
+ sha1_ptr = sha1;
+ printf("%06o %s %s\n",patch->old_mode,
+ sha1_to_hex(sha1_ptr), name);
+ }
+}
+
static void stat_patch_list(struct patch *patch)
{
int files, adds, dels;
@@ -1461,6 +1543,9 @@ static int apply_patch(int fd)
if (show_files)
show_file_list(list);
+ if (show_index_info)
+ show_index_list(list);
+
if (diffstat)
stat_patch_list(list);
@@ -1492,11 +1577,6 @@ int main(int argc, char **argv)
excludes = x;
continue;
}
- /* NEEDSWORK: this does not do anything at this moment. */
- if (!strcmp(arg, "--no-merge")) {
- merge_patch = 0;
- continue;
- }
if (!strcmp(arg, "--stat")) {
apply = 0;
diffstat = 1;
@@ -1524,6 +1604,11 @@ int main(int argc, char **argv)
show_files = 1;
continue;
}
+ if (!strcmp(arg, "--show-index-info")) {
+ apply = 0;
+ show_index_info = 1;
+ continue;
+ }
fd = open(arg, O_RDONLY);
if (fd < 0)
usage(apply_usage);
diff --git a/cache.h b/cache.h
index ec2a1610b2..5987d4c125 100644
--- a/cache.h
+++ b/cache.h
@@ -11,7 +11,9 @@
#include <string.h>
#include <errno.h>
#include <limits.h>
+#ifndef NO_MMAP
#include <sys/mman.h>
+#endif
#include <sys/param.h>
#include <netinet/in.h>
#include <sys/types.h>
@@ -165,6 +167,7 @@ extern int ce_match_stat(struct cache_entry *ce, struct stat *st);
extern int ce_modified(struct cache_entry *ce, struct stat *st);
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type);
+extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
struct cache_file {
@@ -355,4 +358,18 @@ extern void packed_object_info_detail(struct pack_entry *, char *, unsigned long
/* Dumb servers support */
extern int update_server_info(int);
+#ifdef NO_MMAP
+
+#ifndef PROT_READ
+#define PROT_READ 1
+#define PROT_WRITE 2
+#define MAP_PRIVATE 1
+#define MAP_FAILED ((void*)-1)
+#endif
+
+extern void *gitfakemmap(void *start, size_t length, int prot , int flags, int fd, off_t offset);
+extern int gitfakemunmap(void *start, size_t length);
+
+#endif
+
#endif /* CACHE_H */
diff --git a/checkout-index.c b/checkout-index.c
index f32513c507..97845324be 100644
--- a/checkout-index.c
+++ b/checkout-index.c
@@ -63,15 +63,20 @@ static int checkout_file(const char *name)
static int checkout_all(void)
{
- int i;
+ int i, errs = 0;
for (i = 0; i < active_nr ; i++) {
struct cache_entry *ce = active_cache[i];
if (ce_stage(ce))
continue;
if (checkout_entry(ce, &state) < 0)
- return -1;
+ errs++;
}
+ if (errs)
+ /* we have already done our error reporting.
+ * exit with the same code as die().
+ */
+ exit(128);
return 0;
}
diff --git a/compat/mmap.c b/compat/mmap.c
new file mode 100644
index 0000000000..a051c4767d
--- /dev/null
+++ b/compat/mmap.c
@@ -0,0 +1,50 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include "../cache.h"
+
+void *gitfakemmap(void *start, size_t length, int prot , int flags, int fd, off_t offset)
+{
+ int n = 0;
+
+ if (start != NULL || !(flags & MAP_PRIVATE))
+ die("Invalid usage of gitfakemmap.");
+
+ if (lseek(fd, offset, SEEK_SET) < 0) {
+ errno = EINVAL;
+ return MAP_FAILED;
+ }
+
+ start = xmalloc(length);
+ if (start == NULL) {
+ errno = ENOMEM;
+ return MAP_FAILED;
+ }
+
+ while (n < length) {
+ int count = read(fd, start+n, length-n);
+
+ if (count == 0) {
+ memset(start+n, 0, length-n);
+ break;
+ }
+
+ if (count < 0) {
+ free(start);
+ errno = EACCES;
+ return MAP_FAILED;
+ }
+
+ n += count;
+ }
+
+ return start;
+}
+
+int gitfakemunmap(void *start, size_t length)
+{
+ free(start);
+ return 0;
+}
+
diff --git a/connect.c b/connect.c
index 825c439acc..b157cf1cc7 100644
--- a/connect.c
+++ b/connect.c
@@ -290,6 +290,8 @@ static enum protocol get_protocol(const char *name)
#define STR_(s) # s
#define STR(s) STR_(s)
+#ifndef NO_IPV6
+
static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path)
{
int sockfd = -1;
@@ -346,6 +348,77 @@ static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path)
return 0;
}
+#else /* NO_IPV6 */
+
+static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path)
+{
+ int sockfd = -1;
+ char *colon, *end;
+ char *port = STR(DEFAULT_GIT_PORT), *ep;
+ struct hostent *he;
+ struct sockaddr_in sa;
+ char **ap;
+ unsigned int nport;
+
+ if (host[0] == '[') {
+ end = strchr(host + 1, ']');
+ if (end) {
+ *end = 0;
+ end++;
+ host++;
+ } else
+ end = host;
+ } else
+ end = host;
+ colon = strchr(end, ':');
+
+ if (colon) {
+ *colon = 0;
+ port = colon + 1;
+ }
+
+
+ he = gethostbyname(host);
+ if (!he)
+ die("Unable to look up %s (%s)", host, hstrerror(h_errno));
+ nport = strtoul(port, &ep, 10);
+ if ( ep == port || *ep ) {
+ /* Not numeric */
+ struct servent *se = getservbyname(port,"tcp");
+ if ( !se )
+ die("Unknown port %s\n", port);
+ nport = se->s_port;
+ }
+
+ for (ap = he->h_addr_list; *ap; ap++) {
+ sockfd = socket(he->h_addrtype, SOCK_STREAM, 0);
+ if (sockfd < 0)
+ continue;
+
+ memset(&sa, 0, sizeof sa);
+ sa.sin_family = he->h_addrtype;
+ sa.sin_port = htons(nport);
+ memcpy(&sa.sin_addr, ap, he->h_length);
+
+ if (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {
+ close(sockfd);
+ sockfd = -1;
+ continue;
+ }
+ break;
+ }
+
+ if (sockfd < 0)
+ die("unable to connect a socket (%s)", strerror(errno));
+
+ fd[0] = sockfd;
+ fd[1] = sockfd;
+ packet_write(sockfd, "%s %s\n", prog, path);
+ return 0;
+}
+
+#endif /* NO_IPV6 */
+
/*
* Yeah, yeah, fixme. Need to pass in the heads etc.
*/
diff --git a/daemon.c b/daemon.c
index 65e62d7bb1..11fa3ed11f 100644
--- a/daemon.c
+++ b/daemon.c
@@ -4,6 +4,7 @@
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/time.h>
+#include <sys/poll.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
@@ -141,7 +142,7 @@ static int upload(char *dir, int dirlen)
* is ok with us doing this.
*/
if ((!export_all_trees && access("git-daemon-export-ok", F_OK)) ||
- access("objects/00", X_OK) ||
+ access("objects/", X_OK) ||
access("HEAD", R_OK)) {
logerror("Not a valid git-daemon-enabled repository: '%s'", dir);
return -1;
@@ -328,6 +329,7 @@ static void handle(int incoming, struct sockaddr *addr, int addrlen)
inet_ntop(AF_INET, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));
port = sin_addr->sin_port;
+#ifndef NO_IPV6
} else if (addr->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6_addr = (void *) addr;
@@ -337,6 +339,7 @@ static void handle(int incoming, struct sockaddr *addr, int addrlen)
strcat(buf, "]");
port = sin6_addr->sin6_port;
+#endif
}
loginfo("Connection from %s:%d", addrbuf, port);
@@ -369,16 +372,16 @@ static void child_handler(int signo)
}
}
-static int serve(int port)
+#ifndef NO_IPV6
+
+static int socksetup(int port, int **socklist_p)
{
- struct addrinfo hints, *ai0, *ai;
- int gai;
int socknum = 0, *socklist = NULL;
int maxfd = -1;
- fd_set fds_init, fds;
char pbuf[NI_MAXSERV];
- signal(SIGCHLD, child_handler);
+ struct addrinfo hints, *ai0, *ai;
+ int gai;
sprintf(pbuf, "%d", port);
memset(&hints, 0, sizeof(hints));
@@ -391,8 +394,6 @@ static int serve(int port)
if (gai)
die("getaddrinfo() failed: %s\n", gai_strerror(gai));
- FD_ZERO(&fds_init);
-
for (ai = ai0; ai; ai = ai->ai_next) {
int sockfd;
int *newlist;
@@ -431,23 +432,63 @@ static int serve(int port)
socklist = newlist;
socklist[socknum++] = sockfd;
- FD_SET(sockfd, &fds_init);
if (maxfd < sockfd)
maxfd = sockfd;
}
freeaddrinfo(ai0);
- if (socknum == 0)
- die("unable to allocate any listen sockets on port %u", port);
+ *socklist_p = socklist;
+ return socknum;
+}
+
+#else /* NO_IPV6 */
+
+static int socksetup(int port, int **socklist_p)
+{
+ struct sockaddr_in sin;
+ int sockfd;
+
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd < 0)
+ return 0;
+
+ memset(&sin, 0, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ sin.sin_port = htons(port);
+
+ if ( bind(sockfd, (struct sockaddr *)&sin, sizeof sin) < 0 ) {
+ close(sockfd);
+ return 0;
+ }
+ *socklist_p = xmalloc(sizeof(int));
+ **socklist_p = sockfd;
+}
+
+#endif
+
+static int service_loop(int socknum, int *socklist)
+{
+ struct pollfd *pfd;
+ int i;
+
+ pfd = xcalloc(socknum, sizeof(struct pollfd));
+
+ for (i = 0; i < socknum; i++) {
+ pfd[i].fd = socklist[i];
+ pfd[i].events = POLLIN;
+ }
+
+ signal(SIGCHLD, child_handler);
+
for (;;) {
int i;
- fds = fds_init;
-
- if (select(maxfd + 1, &fds, NULL, NULL, NULL) < 0) {
+
+ if (poll(pfd, socknum, 0) < 0) {
if (errno != EINTR) {
- error("select failed, resuming: %s",
+ error("poll failed, resuming: %s",
strerror(errno));
sleep(1);
}
@@ -455,12 +496,10 @@ static int serve(int port)
}
for (i = 0; i < socknum; i++) {
- int sockfd = socklist[i];
-
- if (FD_ISSET(sockfd, &fds)) {
+ if (pfd[i].revents & POLLIN) {
struct sockaddr_storage ss;
- int sslen = sizeof(ss);
- int incoming = accept(sockfd, (struct sockaddr *)&ss, &sslen);
+ unsigned int sslen = sizeof(ss);
+ int incoming = accept(pfd[i].fd, (struct sockaddr *)&ss, &sslen);
if (incoming < 0) {
switch (errno) {
case EAGAIN:
@@ -477,6 +516,17 @@ static int serve(int port)
}
}
+static int serve(int port)
+{
+ int socknum, *socklist;
+
+ socknum = socksetup(port, &socklist);
+ if (socknum == 0)
+ die("unable to allocate any listen sockets on port %u", port);
+
+ return service_loop(socknum, socklist);
+}
+
int main(int argc, char **argv)
{
int port = DEFAULT_GIT_PORT;
@@ -526,7 +576,7 @@ int main(int argc, char **argv)
if (inetd_mode) {
fclose(stderr); //FIXME: workaround
return execute();
+ } else {
+ return serve(port);
}
-
- return serve(port);
}
diff --git a/diff.c b/diff.c
index 7d06b035ae..cbb86320a6 100644
--- a/diff.c
+++ b/diff.c
@@ -596,15 +596,31 @@ static void run_external_diff(const char *pgm,
remove_tempfile();
}
+static void diff_fill_sha1_info(struct diff_filespec *one)
+{
+ if (DIFF_FILE_VALID(one)) {
+ if (!one->sha1_valid) {
+ struct stat st;
+ if (stat(one->path, &st) < 0)
+ die("stat %s", one->path);
+ if (index_path(one->sha1, one->path, &st, 0))
+ die("cannot hash %s\n", one->path);
+ }
+ }
+ else
+ memset(one->sha1, 0, 20);
+}
+
static void run_diff(struct diff_filepair *p)
{
const char *pgm = external_diff();
- char msg_[PATH_MAX*2+200], *xfrm_msg;
+ char msg[PATH_MAX*2+300], *xfrm_msg;
struct diff_filespec *one;
struct diff_filespec *two;
const char *name;
const char *other;
int complete_rewrite = 0;
+ int len;
if (DIFF_PAIR_UNMERGED(p)) {
/* unmerged */
@@ -616,39 +632,60 @@ static void run_diff(struct diff_filepair *p)
name = p->one->path;
other = (strcmp(name, p->two->path) ? p->two->path : NULL);
one = p->one; two = p->two;
+
+ diff_fill_sha1_info(one);
+ diff_fill_sha1_info(two);
+
+ len = 0;
switch (p->status) {
case DIFF_STATUS_COPIED:
- sprintf(msg_,
- "similarity index %d%%\n"
- "copy from %s\n"
- "copy to %s",
- (int)(0.5 + p->score * 100.0/MAX_SCORE),
- name, other);
- xfrm_msg = msg_;
+ len += snprintf(msg + len, sizeof(msg) - len,
+ "similarity index %d%%\n"
+ "copy from %s\n"
+ "copy to %s\n",
+ (int)(0.5 + p->score * 100.0/MAX_SCORE),
+ name, other);
break;
case DIFF_STATUS_RENAMED:
- sprintf(msg_,
- "similarity index %d%%\n"
- "rename from %s\n"
- "rename to %s",
- (int)(0.5 + p->score * 100.0/MAX_SCORE),
- name, other);
- xfrm_msg = msg_;
+ len += snprintf(msg + len, sizeof(msg) - len,
+ "similarity index %d%%\n"
+ "rename from %s\n"
+ "rename to %s\n",
+ (int)(0.5 + p->score * 100.0/MAX_SCORE),
+ name, other);
break;
case DIFF_STATUS_MODIFIED:
if (p->score) {
- sprintf(msg_,
- "dissimilarity index %d%%",
- (int)(0.5 + p->score * 100.0/MAX_SCORE));
- xfrm_msg = msg_;
+ len += snprintf(msg + len, sizeof(msg) - len,
+ "dissimilarity index %d%%\n",
+ (int)(0.5 + p->score *
+ 100.0/MAX_SCORE));
complete_rewrite = 1;
break;
}
/* fallthru */
default:
- xfrm_msg = NULL;
+ /* nothing */
+ ;
}
+ if (memcmp(one->sha1, two->sha1, 20)) {
+ char one_sha1[41];
+ memcpy(one_sha1, sha1_to_hex(one->sha1), 41);
+
+ len += snprintf(msg + len, sizeof(msg) - len,
+ "index %.7s..%.7s", one_sha1,
+ sha1_to_hex(two->sha1));
+ if (one->mode == two->mode)
+ len += snprintf(msg + len, sizeof(msg) - len,
+ " %06o", one->mode);
+ len += snprintf(msg + len, sizeof(msg) - len, "\n");
+ }
+
+ if (len)
+ msg[--len] = 0;
+ xfrm_msg = len ? msg : NULL;
+
if (!pgm &&
DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
(S_IFMT & one->mode) != (S_IFMT & two->mode)) {
diff --git a/entry.c b/entry.c
index b8426dbd0d..15b34eb6f9 100644
--- a/entry.c
+++ b/entry.c
@@ -132,7 +132,7 @@ int checkout_entry(struct cache_entry *ce, struct checkout *state)
if (!state->force) {
if (!state->quiet)
fprintf(stderr, "git-checkout-index: %s already exists\n", path);
- return 0;
+ return -1;
}
/*
diff --git a/fsck-objects.c b/fsck-objects.c
index 65cec7d12b..17d05363e0 100644
--- a/fsck-objects.c
+++ b/fsck-objects.c
@@ -329,9 +329,8 @@ static int fsck_dir(int i, char *path)
DIR *dir = opendir(path);
struct dirent *de;
- if (!dir) {
- return error("missing sha1 directory '%s'", path);
- }
+ if (!dir)
+ return 0;
while ((de = readdir(dir)) != NULL) {
char name[100];
diff --git a/git-am.sh b/git-am.sh
new file mode 100755
index 0000000000..9e41e70313
--- /dev/null
+++ b/git-am.sh
@@ -0,0 +1,337 @@
+#!/bin/sh
+#
+#
+. git-sh-setup || die "Not a git archive"
+
+files=$(git-diff-index --cached --name-only HEAD) || exit
+if [ "$files" ]; then
+ echo "Dirty index: cannot apply patches (dirty: $files)" >&2
+ exit 1
+fi
+
+usage () {
+ echo >&2 "usage: $0 [--signoff] [--dotest=<dir>] [--utf8] [--3way] <mbox>"
+ echo >&2 " or, when resuming"
+ echo >&2 " $0 [--skip]"
+ exit 1;
+}
+
+stop_here () {
+ echo "$1" >"$dotest/next"
+ exit 1
+}
+
+go_next () {
+ rm -f "$dotest/$msgnum" "$dotest/msg" "$dotest/msg-clean" \
+ "$dotest/patch" "$dotest/info"
+ echo "$next" >"$dotest/next"
+ this=$next
+}
+
+fall_back_3way () {
+ O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
+
+ rm -fr "$dotest"/patch-merge-*
+ mkdir "$dotest/patch-merge-tmp-dir"
+
+ # First see if the patch records the index info that we can use.
+ if git-apply --show-index-info "$dotest/patch" \
+ >"$dotest/patch-merge-index-info" 2>/dev/null &&
+ GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
+ git-update-index --index-info <"$dotest/patch-merge-index-info" &&
+ GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
+ git-write-tree >"$dotest/patch-merge-base+" &&
+ # index has the base tree now.
+ (
+ cd "$dotest/patch-merge-tmp-dir" &&
+ GIT_INDEX_FILE="../patch-merge-tmp-index" \
+ GIT_OBJECT_DIRECTORY="$O_OBJECT" \
+ git-apply --index <../patch
+ )
+ then
+ echo Using index info to reconstruct a base tree...
+ mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base"
+ mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index"
+ else
+ # Otherwise, try nearby trees that can be used to apply the
+ # patch.
+ (
+ N=10
+
+ # Hoping the patch is against our recent commits...
+ git-rev-list --max-count=$N HEAD
+
+ # or hoping the patch is against known tags...
+ git-ls-remote --tags .
+ ) |
+ while read base junk
+ do
+ # See if we have it as a tree...
+ git-cat-file tree "$base" >/dev/null 2>&1 || continue
+
+ rm -fr "$dotest"/patch-merge-* &&
+ mkdir "$dotest/patch-merge-tmp-dir" || break
+ (
+ cd "$dotest/patch-merge-tmp-dir" &&
+ GIT_INDEX_FILE=../patch-merge-tmp-index &&
+ GIT_OBJECT_DIRECTORY="$O_OBJECT" &&
+ export GIT_INDEX_FILE GIT_OBJECT_DIRECTORY &&
+ git-read-tree "$base" &&
+ git-apply --index &&
+ mv ../patch-merge-tmp-index ../patch-merge-index &&
+ echo "$base" >../patch-merge-base
+ ) <"$dotest/patch" 2>/dev/null && break
+ done
+ fi
+
+ test -f "$dotest/patch-merge-index" &&
+ his_tree=$(GIT_INDEX_FILE="$dotest/patch-merge-index" git-write-tree) &&
+ orig_tree=$(cat "$dotest/patch-merge-base") &&
+ rm -fr "$dotest"/patch-merge-* || exit 1
+
+ echo Falling back to patching base and 3-way merge...
+
+ # This is not so wrong. Depending on which base we picked,
+ # orig_tree may be wildly different from ours, but his_tree
+ # has the same set of wildly different changes in parts the
+ # patch did not touch, so resolve ends up cancelling them,
+ # saying that we reverted all those changes.
+
+ git-merge-resolve $orig_tree -- HEAD $his_tree || {
+ echo Failed to merge in the changes.
+ exit 1
+ }
+}
+
+prec=4
+dotest=.dotest sign= utf8= keep= skip= interactive=
+
+while case "$#" in 0) break;; esac
+do
+ case "$1" in
+ -d=*|--d=*|--do=*|--dot=*|--dote=*|--dotes=*|--dotest=*)
+ dotest=`expr "$1" : '-[^=]*=\(.*\)'`; shift ;;
+ -d|--d|--do|--dot|--dote|--dotes|--dotest)
+ case "$#" in 1) usage ;; esac; shift
+ dotest="$1"; shift;;
+
+ -i|--i|--in|--int|--inte|--inter|--intera|--interac|--interact|\
+ --interacti|--interactiv|--interactive)
+ interactive=t; shift ;;
+
+ -3|--3|--3w|--3wa|--3way)
+ threeway=t; shift ;;
+ -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
+ sign=t; shift ;;
+ -u|--u|--ut|--utf|--utf8)
+ utf8=t; shift ;;
+ -k|--k|--ke|--kee|--keep)
+ keep=t; shift ;;
+
+ --sk|--ski|--skip)
+ skip=t; shift ;;
+
+ --)
+ shift; break ;;
+ -*)
+ usage ;;
+ *)
+ break ;;
+ esac
+done
+
+if test -d "$dotest" &&
+ last=$(cat "$dotest/last") &&
+ next=$(cat "$dotest/next") &&
+ test $# != 0 &&
+ test "$next" -gt "$last"
+then
+ rm -fr "$dotest"
+fi
+
+if test -d "$dotest"
+then
+ test ",$#," = ",0," ||
+ die "previous dotest directory $dotest still exists but mbox given."
+else
+ # Make sure we are not given --skip
+ test ",$skip," = ,, ||
+ die "we are not resuming."
+
+ # Start afresh.
+ mkdir -p "$dotest" || exit
+
+ # cat does the right thing for us, including '-' to mean
+ # standard input.
+ cat "$@" |
+ git-mailsplit -d$prec "$dotest/" >"$dotest/last" || {
+ rm -fr "$dotest"
+ exit 1
+ }
+
+ echo "$sign" >"$dotest/sign"
+ echo "$utf8" >"$dotest/utf8"
+ echo "$keep" >"$dotest/keep"
+ echo "$threeway" >"$dotest/3way"
+ echo 1 >"$dotest/next"
+fi
+
+if test "$(cat "$dotest/utf8")" = t
+then
+ utf8=-u
+fi
+if test "$(cat "$dotest/keep")" = t
+then
+ keep=-k
+fi
+if test "$(cat "$dotest/sign")" = t
+then
+ SIGNOFF=`git-var GIT_COMMITTER_IDENT | sed -e '
+ s/>.*/>/
+ s/^/Signed-off-by: /'
+ `
+else
+ SIGNOFF=
+fi
+threeway=$(cat "$dotest/3way")
+
+last=`cat "$dotest/last"`
+this=`cat "$dotest/next"`
+if test "$skip" = t
+then
+ this=`expr "$this" + 1`
+fi
+
+if test "$this" -gt "$last"
+then
+ echo Nothing to do.
+ rm -fr "$dotest"
+ exit
+fi
+
+while test "$this" -le "$last"
+do
+ msgnum=`printf "%0${prec}d" $this`
+ next=`expr "$this" + 1`
+ test -f "$dotest/$msgnum" || {
+ go_next
+ continue
+ }
+ git-mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
+ <"$dotest/$msgnum" >"$dotest/info" ||
+ stop_here $this
+ git-stripspace < "$dotest/msg" > "$dotest/msg-clean"
+
+ GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")"
+ GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")"
+ GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")"
+ SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
+
+ case "$keep_subject" in -k) SUBJECT="[PATCH] $SUBJECT" ;; esac
+ if test '' != "$SIGNOFF"
+ then
+ LAST_SIGNED_OFF_BY=`
+ sed -ne '/^Signed-off-by: /p' "$dotest/msg-clean" |
+ tail -n 1
+ `
+ ADD_SIGNOFF=$(test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
+ test '' = "$LAST_SIGNED_OFF_BY" && echo
+ echo "$SIGNOFF"
+ })
+ else
+ ADD_SIGNOFF=
+ fi
+ {
+ echo "$SUBJECT"
+ if test -s "$dotest/msg-clean"
+ then
+ echo
+ cat "$dotest/msg-clean"
+ fi
+ if test '' != "$ADD_SIGNOFF"
+ then
+ echo "$ADD_SIGNOFF"
+ fi
+ } >"$dotest/final-commit"
+
+ if test "$interactive" = t
+ then
+ action=again
+ while test "$action" = again
+ do
+ echo "Commit Body is:"
+ echo "--------------------------"
+ cat "$dotest/final-commit"
+ echo "--------------------------"
+ echo -n "Apply? [y]es/[n]o/[e]dit/[a]ccept all "
+ read reply
+ case "$reply" in
+ y*|Y*) action=yes ;;
+ a*|A*) action=yes interactive= ;;
+ n*|N*) action=skip ;;
+ e*|E*) "${VISUAL:-${EDITOR:-vi}}" "$dotest/final-commit"
+ action=again ;;
+ esac
+ done
+ else
+ action=yes
+ fi
+
+ if test $action = skip
+ then
+ go_next
+ continue
+ fi
+
+ if test -x "$GIT_DIR"/hooks/applypatch-msg
+ then
+ "$GIT_DIR"/hooks/applypatch-msg "$dotest/final-commit" ||
+ stop_here $this
+ fi
+
+ echo
+ echo "Applying '$SUBJECT'"
+ echo
+
+ git-apply --index "$dotest/patch"; apply_status=$?
+ if test $apply_status = 1 && test "$threeway" = t
+ then
+ (fall_back_3way) || stop_here $this
+
+ # Applying the patch to an earlier tree and merging the
+ # result may have produced the same tree as ours.
+ if test '' = "$(git-diff-index --cached --name-only -z HEAD)"
+ then
+ echo No changes -- Patch already applied.
+ go_next
+ continue
+ fi
+ fi
+ if test $apply_status != 0
+ then
+ echo Patch failed at $msgnum.
+ stop_here $this
+ fi
+
+ if test -x "$GIT_DIR"/hooks/pre-applypatch
+ then
+ "$GIT_DIR"/hooks/pre-applypatch || stop_here $this
+ fi
+
+ tree=$(git-write-tree) &&
+ echo Wrote tree $tree &&
+ parent=$(git-rev-parse --verify HEAD) &&
+ commit=$(git-commit-tree $tree -p $parent <"$dotest/final-commit") &&
+ echo Committed: $commit &&
+ git-update-ref HEAD $commit $parent ||
+ stop_here $this
+
+ if test -x "$GIT_DIR"/hooks/post-applypatch
+ then
+ "$GIT_DIR"/hooks/post-applypatch
+ fi
+
+ go_next
+done
+
+rm -fr "$dotest"
diff --git a/git-applymbox.sh b/git-applymbox.sh
index e2bfd02870..4e77132ab5 100755
--- a/git-applymbox.sh
+++ b/git-applymbox.sh
@@ -9,8 +9,6 @@
## You give it a mbox-format collection of emails, and it will try to
## apply them to the kernel using "applypatch"
##
-## applymbox [-u] [-k] [-q] (-c .dotest/msg-number | mail_archive) [Signoff_file]"
-##
## The patch application may fail in the middle. In which case:
## (1) look at .dotest/patch and fix it up to apply
## (2) re-run applymbox with -c .dotest/msg-number for the current one.
@@ -21,7 +19,7 @@
. git-sh-setup || die "Not a git archive"
usage () {
- echo >&2 "applymbox [-u] [-k] [-q] (-c .dotest/<num> | mbox) [signoff]"
+ echo >&2 "applymbox [-u] [-k] [-q] [-m] (-c .dotest/<num> | mbox) [signoff]"
exit 1
}
@@ -33,6 +31,7 @@ do
-k) keep_subject=-k ;;
-q) query_apply=t ;;
-c) continue="$2"; resume=f; shift ;;
+ -m) fallback_3way=t ;;
-*) usage ;;
*) break ;;
esac
@@ -43,7 +42,8 @@ case "$continue" in
'')
rm -rf .dotest
mkdir .dotest
- git-mailsplit "$1" .dotest || exit 1
+ num_msgs=$(git-mailsplit "$1" .dotest) || exit 1
+ echo "$num_msgs patch(es) to process."
shift
esac
@@ -56,6 +56,9 @@ fi
case "$query_apply" in
t) touch .dotest/.query_apply
esac
+case "$fall_back_3way" in
+t) : >.dotest/.3way
+esac
case "$keep_subject" in
-k) : >.dotest/.keep_subject
esac
@@ -80,7 +83,11 @@ do
do
git-applypatch .dotest/msg-clean .dotest/patch .dotest/info "$signoff"
case "$?" in
- 0 | 2 )
+ 0)
+ # Remove the cleanly applied one to reduce clutter.
+ rm -f .dotest/$i
+ ;;
+ 2)
# 2 is a special exit code from applypatch to indicate that
# the patch wasn't applied, but continue anyway
;;
diff --git a/git-applypatch.sh b/git-applypatch.sh
index 9f5a45bb2b..66fd19ae2d 100755
--- a/git-applypatch.sh
+++ b/git-applypatch.sh
@@ -22,6 +22,8 @@ query_apply=.dotest/.query_apply
## if this file exists.
keep_subject=.dotest/.keep_subject
+## We do not attempt the 3-way merge fallback unless this file exists.
+fall_back_3way=.dotest/.3way
MSGFILE=$1
PATCHFILE=$2
@@ -29,10 +31,10 @@ INFO=$3
SIGNOFF=$4
EDIT=${VISUAL:-${EDITOR:-vi}}
-export GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' .dotest/info)"
-export GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' .dotest/info)"
-export GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' .dotest/info)"
-export SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' .dotest/info)"
+export GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$INFO")"
+export GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$INFO")"
+export GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$INFO")"
+export SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$INFO")"
if test '' != "$SIGNOFF"
then
@@ -54,8 +56,10 @@ then
sed -ne '/^Signed-off-by: /p' "$MSGFILE" |
tail -n 1
`
- test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" ||
- echo "$SIGNOFF" >>"$MSGFILE"
+ test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
+ test '' = "$LAST_SIGNED_OFF_BY" && echo
+ echo "$SIGNOFF"
+ } >>"$MSGFILE"
fi
fi
@@ -99,7 +103,81 @@ echo
echo Applying "'$SUBJECT'"
echo
-git-apply --index "$PATCHFILE" || exit 1
+git-apply --index "$PATCHFILE" || {
+
+ # git-apply exits with status 1 when the patch does not apply,
+ # but it die()s with other failures, most notably upon corrupt
+ # patch. In the latter case, there is no point to try applying
+ # it to another tree and do 3-way merge.
+ test $? = 1 || exit 1
+
+ test -f "$fall_back_3way" || exit 1
+
+ # Here if we know which revision the patch applies to,
+ # we create a temporary working tree and index, apply the
+ # patch, and attempt 3-way merge with the resulting tree.
+
+ O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
+ rm -fr .patch-merge-*
+
+ (
+ N=10
+
+ # if the patch records the base tree...
+ sed -ne '
+ /^diff /q
+ /^applies-to: \([0-9a-f]*\)$/{
+ s//\1/p
+ q
+ }
+ ' "$PATCHFILE"
+
+ # or hoping the patch is against our recent commits...
+ git-rev-list --max-count=$N HEAD
+
+ # or hoping the patch is against known tags...
+ git-ls-remote --tags .
+ ) |
+ while read base junk
+ do
+ # Try it if we have it as a tree.
+ git-cat-file tree "$base" >/dev/null 2>&1 || continue
+
+ rm -fr .patch-merge-tmp-* &&
+ mkdir .patch-merge-tmp-dir || break
+ (
+ cd .patch-merge-tmp-dir &&
+ GIT_INDEX_FILE=../.patch-merge-tmp-index &&
+ GIT_OBJECT_DIRECTORY="$O_OBJECT" &&
+ export GIT_INDEX_FILE GIT_OBJECT_DIRECTORY &&
+ git-read-tree "$base" &&
+ git-apply --index &&
+ mv ../.patch-merge-tmp-index ../.patch-merge-index &&
+ echo "$base" >../.patch-merge-base
+ ) <"$PATCHFILE" 2>/dev/null && break
+ done
+
+ test -f .patch-merge-index &&
+ his_tree=$(GIT_INDEX_FILE=.patch-merge-index git-write-tree) &&
+ orig_tree=$(cat .patch-merge-base) &&
+ rm -fr .patch-merge-* || exit 1
+
+ echo Falling back to patching base and 3-way merge using $orig_tree...
+
+ # This is not so wrong. Depending on which base we picked,
+ # orig_tree may be wildly different from ours, but his_tree
+ # has the same set of wildly different changes in parts the
+ # patch did not touch, so resolve ends up cancelling them,
+ # saying that we reverted all those changes.
+
+ if git-merge-resolve $orig_tree -- HEAD $his_tree
+ then
+ echo Done.
+ else
+ echo Failed to merge in the changes.
+ exit 1
+ fi
+}
if test -x "$GIT_DIR"/hooks/pre-applypatch
then
diff --git a/git-commit.sh b/git-commit.sh
index 591fcdceb1..5bdee54a46 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -129,7 +129,7 @@ then
elif test "$use_commit" != ""
then
git-cat-file commit "$use_commit" | sed -e '1,/^$/d'
-fi | git-stripspace >.editmsg
+fi | git-stripspace >"$GIT_DIR"/COMMIT_EDITMSG
case "$signoff" in
t)
@@ -139,7 +139,7 @@ t)
s/>.*/>/
s/^/Signed-off-by: /
'
- } >>.editmsg
+ } >>"$GIT_DIR"/COMMIT_EDITMSG
;;
esac
@@ -153,7 +153,7 @@ if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
echo "# $GIT_DIR/MERGE_HEAD"
echo "# and try again"
echo "#"
-fi >>.editmsg
+fi >>"$GIT_DIR"/COMMIT_EDITMSG
PARENTS="-p HEAD"
if GIT_DIR="$GIT_DIR" git-rev-parse --verify HEAD >/dev/null 2>&1
@@ -197,16 +197,16 @@ else
fi
PARENTS=""
fi
-git-status >>.editmsg
+git-status >>"$GIT_DIR"/COMMIT_EDITMSG
if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ]
then
- rm -f .editmsg
+ rm -f "$GIT_DIR/COMMIT_EDITMSG"
git-status
exit 1
fi
case "$no_edit" in
'')
- ${VISUAL:-${EDITOR:-vi}} .editmsg
+ ${VISUAL:-${EDITOR:-vi}} "$GIT_DIR/COMMIT_EDITMSG"
;;
esac
@@ -214,16 +214,20 @@ case "$verify" in
t)
if test -x "$GIT_DIR"/hooks/commit-msg
then
- "$GIT_DIR"/hooks/commit-msg .editmsg || exit
+ "$GIT_DIR"/hooks/commit-msg "$GIT_DIR"/COMMIT_EDITMSG || exit
fi
esac
-grep -v '^#' < .editmsg | git-stripspace > .cmitmsg
-grep -v -i '^Signed-off-by' .cmitmsg >.cmitchk
-if test -s .cmitchk
+grep -v '^#' < "$GIT_DIR"/COMMIT_EDITMSG |
+git-stripspace > "$GIT_DIR"/COMMIT_MSG
+
+if cnt=`grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
+ git-stripspace |
+ wc -l` &&
+ test 0 -lt $cnt
then
tree=$(git-write-tree) &&
- commit=$(cat .cmitmsg | git-commit-tree $tree $PARENTS) &&
+ commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) &&
git-update-ref HEAD $commit $current &&
rm -f -- "$GIT_DIR/MERGE_HEAD"
else
@@ -231,7 +235,7 @@ else
false
fi
ret="$?"
-rm -f .cmitmsg .editmsg .cmitchk
+rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG"
if test -x "$GIT_DIR"/hooks/post-commit && test "$ret" = 0
then
diff --git a/git-format-patch.sh b/git-format-patch.sh
index 2844799535..09b635e492 100755
--- a/git-format-patch.sh
+++ b/git-format-patch.sh
@@ -6,7 +6,7 @@
. git-sh-setup || die "Not a git archive."
usage () {
- echo >&2 "usage: $0"' [-n] [-o dir] [--keep-subject] [--mbox] [--check] [--signoff] [-<diff options>...] upstream [ our-head ]
+ echo >&2 "usage: $0"' [-n] [-o dir | --stdout] [--keep-subject] [--mbox] [--check] [--signoff] [-<diff options>...] upstream [ our-head ]
Prepare each commit with its patch since our-head forked from upstream,
one file per patch, for e-mail submission. Each output file is
@@ -49,6 +49,8 @@ do
numbered=t ;;
-s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
signoff=t ;;
+ --st|--std|--stdo|--stdou|--stdout)
+ stdout=t mbox=t date=t author=t ;;
-o=*|--o=*|--ou=*|--out=*|--outp=*|--outpu=*|--output=*|--output-=*|\
--output-d=*|--output-di=*|--output-dir=*|--output-dire=*|\
--output-direc=*|--output-direct=*|--output-directo=*|\
@@ -141,25 +143,7 @@ do
esac
done >$series
-total=`wc -l <$series | tr -dc "[0-9]"`
-i=1
-while read commit
-do
- git-cat-file commit "$commit" | git-stripspace >$commsg
- title=`sed -ne "$titleScript" <$commsg`
- case "$numbered" in
- '') num= ;;
- *)
- case $total in
- 1) num= ;;
- *) num=' '`printf "%d/%d" $i $total` ;;
- esac
- esac
-
- file=`printf '%04d-%stxt' $i "$title"`
- i=`expr "$i" + 1`
- echo "* $file"
- {
+process_one () {
mailScript='
/./d
/^$/n'
@@ -178,6 +162,7 @@ do
echo 'From nobody Mon Sep 17 00:00:00 2001' ;# UNIX "From" line
;;
esac
+
eval "$(sed -ne "$whosepatchScript" $commsg)"
test "$author,$au" = ",$me" || {
mailScript="$mailScript"'
@@ -196,7 +181,9 @@ Date: '"$ad"
n
b body'
- sed -ne "$mailScript" <$commsg
+ (cat $commsg ; echo; echo) |
+ sed -ne "$mailScript" |
+ git-stripspace
test "$signoff" = "t" && {
offsigner=`git-var GIT_COMMITTER_IDENT | sed -e 's/>.*/>/'`
@@ -212,21 +199,49 @@ Date: '"$ad"
echo
git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
echo
+ git-cat-file commit "$commit^" | sed -e 's/^tree /applies-to: /' -e q
git-diff-tree -p $diff_opts "$commit"
+ echo "---"
+ echo "@@GIT_VERSION@@"
case "$mbox" in
t)
echo
;;
esac
- } >"$outdir$file"
- case "$check" in
- t)
- # This is slightly modified from Andrew Morton's Perfect Patch.
- # Lines you introduce should not have trailing whitespace.
- # Also check for an indentation that has SP before a TAB.
- grep -n '^+\([ ]* .*\|.*[ ]\)$' "$outdir$file"
-
- : do not exit with non-zero because we saw no problem in the last one.
+}
+
+total=`wc -l <$series | tr -dc "[0-9]"`
+i=1
+while read commit
+do
+ git-cat-file commit "$commit" | git-stripspace >$commsg
+ title=`sed -ne "$titleScript" <$commsg`
+ case "$numbered" in
+ '') num= ;;
+ *)
+ case $total in
+ 1) num= ;;
+ *) num=' '`printf "%d/%d" $i $total` ;;
+ esac
esac
+
+ file=`printf '%04d-%stxt' $i "$title"`
+ if test '' = "$stdout"
+ then
+ echo "* $file"
+ process_one >"$outdir$file"
+ if test t = "$check"
+ then
+ # This is slightly modified from Andrew Morton's Perfect Patch.
+ # Lines you introduce should not have trailing whitespace.
+ # Also check for an indentation that has SP before a TAB.
+ grep -n '^+\([ ]* .*\|.*[ ]\)$' "$outdir$file"
+ :
+ fi
+ else
+ echo >&2 "* $file"
+ process_one
+ fi
+ i=`expr "$i" + 1`
done <$series
diff --git a/git-ls-remote.sh b/git-ls-remote.sh
index bfbd5a4d5a..f0f0b07f6f 100755
--- a/git-ls-remote.sh
+++ b/git-ls-remote.sh
@@ -1,6 +1,6 @@
#!/bin/sh
#
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
usage () {
echo >&2 "usage: $0 [--heads] [--tags] <repository> <refs>..."
diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index 4d8a572a99..5e75e15a7e 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
get_data_source () {
case "$1" in
diff --git a/git-rename.perl b/git-rename.perl
index a28c8c83bb..3b1127b1b2 100755
--- a/git-rename.perl
+++ b/git-rename.perl
@@ -15,7 +15,7 @@ sub usage($);
my $GIT_DIR = $ENV{'GIT_DIR'} || ".git";
unless ( -d $GIT_DIR && -d $GIT_DIR . "/objects" &&
- -d $GIT_DIR . "/objects/00" && -d $GIT_DIR . "/refs") {
+ -d $GIT_DIR . "/objects/" && -d $GIT_DIR . "/refs") {
usage("Git repository not found.");
}
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index a0172686a9..dbb98842bf 100755
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -22,4 +22,4 @@ refs/*) : ;;
*) false ;;
esac &&
[ -d "$GIT_DIR/refs" ] &&
-[ -d "$GIT_OBJECT_DIRECTORY/00" ]
+[ -d "$GIT_OBJECT_DIRECTORY/" ]
diff --git a/git-shortlog.perl b/git-shortlog.perl
index 8f0984be02..0b14f833ee 100755
--- a/git-shortlog.perl
+++ b/git-shortlog.perl
@@ -2,55 +2,13 @@
use strict;
-#
-# Even with git, we don't always have name translations.
-# So have an email->real name table to translate the
-# (hopefully few) missing names
-#
-my %mailmap = (
- 'R.Marek@sh.cvut.cz' => 'Rudolf Marek',
- 'Ralf.Wildenhues@gmx.de' => 'Ralf Wildenhues',
- 'aherrman@de.ibm.com' => 'Andreas Herrmann',
- 'akpm@osdl.org' => 'Andrew Morton',
- 'andrew.vasquez@qlogic.com' => 'Andrew Vasquez',
- 'aquynh@gmail.com' => 'Nguyen Anh Quynh',
- 'axboe@suse.de' => 'Jens Axboe',
- 'blaisorblade@yahoo.it' => 'Paolo \'Blaisorblade\' Giarrusso',
- 'bunk@stusta.de' => 'Adrian Bunk',
- 'domen@coderock.org' => 'Domen Puncer',
- 'dougg@torque.net' => 'Douglas Gilbert',
- 'dwmw2@shinybook.infradead.org' => 'David Woodhouse',
- 'ecashin@coraid.com' => 'Ed L Cashin',
- 'felix@derklecks.de' => 'Felix Moeller',
- 'fzago@systemfabricworks.com' => 'Frank Zago',
- 'gregkh@suse.de' => 'Greg Kroah-Hartman',
- 'hch@lst.de' => 'Christoph Hellwig',
- 'htejun@gmail.com' => 'Tejun Heo',
- 'jejb@mulgrave.(none)' => 'James Bottomley',
- 'jejb@titanic.il.steeleye.com' => 'James Bottomley',
- 'jgarzik@pretzel.yyz.us' => 'Jeff Garzik',
- 'johnpol@2ka.mipt.ru' => 'Evgeniy Polyakov',
- 'kay.sievers@vrfy.org' => 'Kay Sievers',
- 'minyard@acm.org' => 'Corey Minyard',
- 'mshah@teja.com' => 'Mitesh shah',
- 'pj@ludd.ltu.se' => 'Peter A Jonsson',
- 'rmps@joel.ist.utl.pt' => 'Rui Saraiva',
- 'santtu.hyrkko@gmail.com' => 'Santtu Hyrkkö',
- 'simon@thekelleys.org.uk' => 'Simon Kelley',
- 'ssant@in.ibm.com' => 'Sachin P Sant',
- 'terra@gnome.org' => 'Morten Welinder',
- 'tony.luck@intel.com' => 'Tony Luck',
- 'welinder@anemone.rentec.com' => 'Morten Welinder',
- 'welinder@darter.rentec.com' => 'Morten Welinder',
- 'welinder@troll.com' => 'Morten Welinder',
-);
-
+my (%mailmap);
+my (%email);
my (%map);
my $pstate = 1;
my $n_records = 0;
my $n_output = 0;
-
sub shortlog_entry($$) {
my ($name, $desc) = @_;
my $key = $name;
@@ -108,41 +66,35 @@ sub changelog_input {
if ($pstate == 1) {
my ($email);
- next unless /^[Aa]uthor:? (.*)<(.*)>.*$/;
-
+ next unless /^[Aa]uthor:?\s*(.*?)\s*<(.*)>/;
+
$n_records++;
-
+
$author = $1;
$email = $2;
$desc = undef;
- # trim trailing whitespace.
- # why doesn't chomp work?
- while ($author && ($author =~ /\s$/)) {
- chop $author;
- }
-
# cset author fixups
if (exists $mailmap{$email}) {
$author = $mailmap{$email};
} elsif (exists $mailmap{$author}) {
$author = $mailmap{$author};
- } elsif ((!$author) || ($author eq "")) {
+ } elsif (!$author) {
$author = $email;
}
-
+ $email{$author}{$email}++;
$pstate++;
}
-
+
# skip to blank line
elsif ($pstate == 2) {
next unless /^\s*$/;
$pstate++;
}
-
+
# skip to non-blank line
elsif ($pstate == 3) {
- next unless /^\s*(\S.*)$/;
+ next unless /^\s*?(.*)/;
# skip lines that are obviously not
# a 1-line cset description
@@ -150,9 +102,9 @@ sub changelog_input {
chomp;
$desc = $1;
-
+
&shortlog_entry($author, $desc);
-
+
$pstate = 1;
}
@@ -162,16 +114,87 @@ sub changelog_input {
}
}
+sub read_mailmap {
+ my ($fh, $mailmap) = @_;
+ while (<$fh>) {
+ chomp;
+ if (/^([^#].*?)\s*<(.*)>/) {
+ $mailmap->{$2} = $1;
+ }
+ }
+}
+
+sub setup_mailmap {
+ read_mailmap(\*DATA, \%mailmap);
+ if (-f '.mailmap') {
+ my $fh = undef;
+ open $fh, '<', '.mailmap';
+ read_mailmap($fh, \%mailmap);
+ close $fh;
+ }
+}
+
sub finalize {
#print "\n$n_records records parsed.\n";
if ($n_records != $n_output) {
die "parse error: input records != output records\n";
}
+ if (0) {
+ for my $author (sort keys %email) {
+ my $e = $email{$author};
+ for my $email (sort keys %$e) {
+ print STDERR "$author <$email>\n";
+ }
+ }
+ }
}
+&setup_mailmap;
&changelog_input;
&shortlog_output;
&finalize;
exit(0);
+
+__DATA__
+#
+# Even with git, we don't always have name translations.
+# So have an email->real name table to translate the
+# (hopefully few) missing names
+#
+Adrian Bunk <bunk@stusta.de>
+Andreas Herrmann <aherrman@de.ibm.com>
+Andrew Morton <akpm@osdl.org>
+Andrew Vasquez <andrew.vasquez@qlogic.com>
+Christoph Hellwig <hch@lst.de>
+Corey Minyard <minyard@acm.org>
+David Woodhouse <dwmw2@shinybook.infradead.org>
+Domen Puncer <domen@coderock.org>
+Douglas Gilbert <dougg@torque.net>
+Ed L Cashin <ecashin@coraid.com>
+Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+Felix Moeller <felix@derklecks.de>
+Frank Zago <fzago@systemfabricworks.com>
+Greg Kroah-Hartman <gregkh@suse.de>
+James Bottomley <jejb@mulgrave.(none)>
+James Bottomley <jejb@titanic.il.steeleye.com>
+Jeff Garzik <jgarzik@pretzel.yyz.us>
+Jens Axboe <axboe@suse.de>
+Kay Sievers <kay.sievers@vrfy.org>
+Mitesh shah <mshah@teja.com>
+Morten Welinder <terra@gnome.org>
+Morten Welinder <welinder@anemone.rentec.com>
+Morten Welinder <welinder@darter.rentec.com>
+Morten Welinder <welinder@troll.com>
+Nguyen Anh Quynh <aquynh@gmail.com>
+Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
+Peter A Jonsson <pj@ludd.ltu.se>
+Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
+Rudolf Marek <R.Marek@sh.cvut.cz>
+Rui Saraiva <rmps@joel.ist.utl.pt>
+Sachin P Sant <ssant@in.ibm.com>
+Santtu Hyrkk,Av(B <santtu.hyrkko@gmail.com>
+Simon Kelley <simon@thekelleys.org.uk>
+Tejun Heo <htejun@gmail.com>
+Tony Luck <tony.luck@intel.com>
diff --git a/git-tag.sh b/git-tag.sh
index 400bdb9843..25c1a0e88e 100755
--- a/git-tag.sh
+++ b/git-tag.sh
@@ -4,7 +4,7 @@
. git-sh-setup || die "Not a git archive"
usage () {
- echo >&2 "Usage: git-tag [-a | -s] [-f] [-m "tag message"] tagname [head]"
+ echo >&2 "Usage: git-tag [-a | -s | -u <key-id>] [-f] [-m <msg>] <tagname> [<head>]"
exit 1
}
@@ -12,6 +12,7 @@ annotate=
signed=
force=
message=
+username=
while case "$#" in 0) break ;; esac
do
case "$1" in
@@ -30,6 +31,12 @@ do
shift
message="$1"
;;
+ -u)
+ annotate=1
+ signed=1
+ shift
+ username="$1"
+ ;;
-*)
usage
;;
@@ -50,6 +57,7 @@ shift
object=$(git-rev-parse --verify --default HEAD "$@") || exit 1
type=$(git-cat-file -t $object) || exit 1
tagger=$(git-var GIT_COMMITTER_IDENT) || exit 1
+: ${username:=$(expr "$tagger" : '\(.*>\)')}
trap 'rm -f .tmp-tag* .tagmsg .editmsg' 0
@@ -65,13 +73,15 @@ if [ "$annotate" ]; then
grep -v '^#' < .editmsg | git-stripspace > .tagmsg
- [ -s .tagmsg ] || exit
+ [ -s .tagmsg ] || {
+ echo >&2 "No tag message?"
+ exit 1
+ }
( echo -e "object $object\ntype $type\ntag $name\ntagger $tagger\n"; cat .tagmsg ) > .tmp-tag
rm -f .tmp-tag.asc .tagmsg
if [ "$signed" ]; then
- me=$(expr "$tagger" : '\(.*>\)') &&
- gpg -bsa -u "$me" .tmp-tag &&
+ gpg -bsa -u "$username" .tmp-tag &&
cat .tmp-tag.asc >>.tmp-tag ||
die "failed to sign the tag with GPG."
fi
diff --git a/git.sh b/git.sh
index dc383eddea..7400c16257 100755
--- a/git.sh
+++ b/git.sh
@@ -11,7 +11,17 @@ case "$#" in
echo "git version @@GIT_VERSION@@"
exit 0 ;;
esac
- test -x $path/git-$cmd && exec $path/git-$cmd "$@" ;;
+
+ test -x $path/git-$cmd && exec $path/git-$cmd "$@"
+
+ case '@@X@@' in
+ '')
+ ;;
+ *)
+ test -x $path/git-$cmd@@X@@ && exec $path/git-$cmd@@X@@ "$@"
+ ;;
+ esac
+ ;;
esac
echo "Usage: git COMMAND [OPTIONS] [TARGET]"
diff --git a/mailsplit.c b/mailsplit.c
index 7afea1aaca..0f8100dcca 100644
--- a/mailsplit.c
+++ b/mailsplit.c
@@ -9,30 +9,14 @@
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
+#include "cache.h"
-static int usage(void)
-{
- fprintf(stderr, "mailsplit <mbox> <directory>\n");
- exit(1);
-}
-
-static int linelen(const char *map, unsigned long size)
-{
- int len = 0, c;
-
- do {
- c = *map;
- map++;
- size--;
- len++;
- } while (size && c != '\n');
- return len;
-}
+static const char git_mailsplit_usage[] =
+"git-mailsplit [-d<prec>] [<mbox>] <directory>";
static int is_from_line(const char *line, int len)
{
@@ -65,81 +49,110 @@ static int is_from_line(const char *line, int len)
return 1;
}
-static int parse_email(const void *map, unsigned long size)
+/* Could be as small as 64, enough to hold a Unix "From " line. */
+static char buf[4096];
+
+/* Called with the first line (potentially partial)
+ * already in buf[] -- normally that should begin with
+ * the Unix "From " line. Write it into the specified
+ * file.
+ */
+static int split_one(FILE *mbox, const char *name)
{
- unsigned long offset;
+ FILE *output = NULL;
+ int len = strlen(buf);
+ int fd;
+ int status = 0;
- if (size < 6 || memcmp("From ", map, 5))
+ if (!is_from_line(buf, len))
goto corrupt;
- /* Make sure we don't trigger on this first line */
- map++; size--; offset=1;
+ fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
+ if (fd < 0)
+ die("cannot open output file %s", name);
+ output = fdopen(fd, "w");
- /*
- * Search for a line beginning with "From ", and
- * having something that looks like a date format.
+ /* Copy it out, while searching for a line that begins with
+ * "From " and having something that looks like a date format.
*/
- do {
- int len = linelen(map, size);
- if (is_from_line(map, len))
- return offset;
- map += len;
- size -= len;
- offset += len;
- } while (size);
- return offset;
-
-corrupt:
+ for (;;) {
+ int is_partial = (buf[len-1] != '\n');
+
+ if (fputs(buf, output) == EOF)
+ die("cannot write output");
+
+ if (fgets(buf, sizeof(buf), mbox) == NULL) {
+ if (feof(mbox)) {
+ status = 1;
+ break;
+ }
+ die("cannot read mbox");
+ }
+ len = strlen(buf);
+ if (!is_partial && is_from_line(buf, len))
+ break; /* done with one message */
+ }
+ fclose(output);
+ return status;
+
+ corrupt:
+ if (output)
+ fclose(output);
+ unlink(name);
fprintf(stderr, "corrupt mailbox\n");
exit(1);
}
-int main(int argc, char **argv)
+int main(int argc, const char **argv)
{
- int fd, nr;
- struct stat st;
- unsigned long size;
- void *map;
-
- if (argc != 3)
- usage();
- fd = open(argv[1], O_RDONLY);
- if (fd < 0) {
- perror(argv[1]);
- exit(1);
- }
- if (chdir(argv[2]) < 0)
- usage();
- if (fstat(fd, &st) < 0) {
- perror("stat");
- exit(1);
+ int i, nr, nr_prec = 4;
+ FILE *mbox = NULL;
+
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+
+ if (arg[0] != '-')
+ break;
+ /* do flags here */
+ if (!strncmp(arg, "-d", 2)) {
+ nr_prec = strtol(arg + 2, NULL, 10);
+ if (nr_prec < 3 || 10 <= nr_prec)
+ usage(git_mailsplit_usage);
+ continue;
+ }
}
- size = st.st_size;
- map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (map == MAP_FAILED) {
- perror("mmap");
- close(fd);
- exit(1);
+
+ /* Either one remaining arg (dir), or two (mbox and dir) */
+ switch (argc - i) {
+ case 1:
+ mbox = stdin;
+ break;
+ case 2:
+ if ((mbox = fopen(argv[i], "r")) == NULL)
+ die("cannot open mbox %s for reading", argv[i]);
+ break;
+ default:
+ usage(git_mailsplit_usage);
}
- close(fd);
+ if (chdir(argv[argc - 1]) < 0)
+ usage(git_mailsplit_usage);
+
nr = 0;
- do {
+ if (fgets(buf, sizeof(buf), mbox) == NULL)
+ die("cannot read mbox");
+
+ for (;;) {
char name[10];
- unsigned long len = parse_email(map, size);
- assert(len <= size);
- sprintf(name, "%04d", ++nr);
- fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
- if (fd < 0) {
- perror(name);
- exit(1);
- }
- if (write(fd, map, len) != len) {
- perror("write");
+
+ sprintf(name, "%0*d", nr_prec, ++nr);
+ switch (split_one(mbox, name)) {
+ case 0:
+ break;
+ case 1:
+ printf("%d\n", nr);
+ return 0;
+ default:
exit(1);
}
- close(fd);
- map += len;
- size -= len;
- } while (size > 0);
- return 0;
+ }
}
diff --git a/prune-packed.c b/prune-packed.c
index 5306e8e5ef..73f0f3a462 100644
--- a/prune-packed.c
+++ b/prune-packed.c
@@ -26,6 +26,9 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len)
else if (unlink(pathname) < 0)
error("unable to unlink %s", pathname);
}
+ pathname[len] = 0;
+ if (rmdir(pathname))
+ mkdir(pathname, 0777);
}
static void prune_packed_objects(void)
@@ -46,7 +49,7 @@ static void prune_packed_objects(void)
sprintf(pathname + len, "%02x/", i);
d = opendir(pathname);
if (!d)
- die("unable to open %s", pathname);
+ continue;
prune_dir(i, d, pathname, len + 3);
closedir(d);
}
diff --git a/server-info.c b/server-info.c
index a9e5607f2f..3c08a288db 100644
--- a/server-info.c
+++ b/server-info.c
@@ -59,6 +59,16 @@ static struct object *parse_object_cheap(const unsigned char *sha1)
struct commit *commit = (struct commit *)o;
free(commit->buffer);
commit->buffer = NULL;
+ } else if (o->type == tree_type) {
+ struct tree *tree = (struct tree *)o;
+ struct tree_entry_list *e, *n;
+ for (e = tree->entries; e; e = n) {
+ free(e->name);
+ e->name = NULL;
+ n = e->next;
+ free(e);
+ }
+ tree->entries = NULL;
}
return o;
}
diff --git a/sha1_file.c b/sha1_file.c
index 895c1fab6f..baaa4c00da 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -1248,6 +1248,73 @@ char *write_sha1_file_prepare(void *buf,
return sha1_file_name(sha1);
}
+/*
+ * Link the tempfile to the final place, possibly creating the
+ * last directory level as you do so.
+ *
+ * Returns the errno on failure, 0 on success.
+ */
+static int link_temp_to_file(const char *tmpfile, char *filename)
+{
+ int ret;
+
+ if (!link(tmpfile, filename))
+ return 0;
+
+ /*
+ * Try to mkdir the last path component if that failed
+ * with an ENOENT.
+ *
+ * Re-try the "link()" regardless of whether the mkdir
+ * succeeds, since a race might mean that somebody
+ * else succeeded.
+ */
+ ret = errno;
+ if (ret == ENOENT) {
+ char *dir = strrchr(filename, '/');
+ if (dir) {
+ *dir = 0;
+ mkdir(filename, 0777);
+ *dir = '/';
+ if (!link(tmpfile, filename))
+ return 0;
+ ret = errno;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Move the just written object into its final resting place
+ */
+static int move_temp_to_file(const char *tmpfile, char *filename)
+{
+ int ret = link_temp_to_file(tmpfile, filename);
+ if (ret) {
+ /*
+ * Coda hack - coda doesn't like cross-directory links,
+ * so we fall back to a rename, which will mean that it
+ * won't be able to check collisions, but that's not a
+ * big deal.
+ *
+ * When this succeeds, we just return 0. We have nothing
+ * left to unlink.
+ */
+ if (ret == EXDEV && !rename(tmpfile, filename))
+ return 0;
+ }
+ unlink(tmpfile);
+ if (ret) {
+ if (ret != EEXIST) {
+ fprintf(stderr, "unable to write sha1 filename %s: %s", filename, strerror(ret));
+ return -1;
+ }
+ /* FIXME!!! Collision check here ? */
+ }
+
+ return 0;
+}
+
int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
{
int size;
@@ -1257,7 +1324,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
char *filename;
static char tmpfile[PATH_MAX];
unsigned char hdr[50];
- int fd, hdrlen, ret;
+ int fd, hdrlen;
/* Normally if we have it in the pack then we do not bother writing
* it out into .git/objects/??/?{38} file.
@@ -1320,32 +1387,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
close(fd);
free(compressed);
- ret = link(tmpfile, filename);
- if (ret < 0) {
- ret = errno;
-
- /*
- * Coda hack - coda doesn't like cross-directory links,
- * so we fall back to a rename, which will mean that it
- * won't be able to check collisions, but that's not a
- * big deal.
- *
- * When this succeeds, we just return 0. We have nothing
- * left to unlink.
- */
- if (ret == EXDEV && !rename(tmpfile, filename))
- return 0;
- }
- unlink(tmpfile);
- if (ret) {
- if (ret != EEXIST) {
- fprintf(stderr, "unable to write sha1 filename %s: %s", filename, strerror(ret));
- return -1;
- }
- /* FIXME!!! Collision check here ? */
- }
-
- return 0;
+ return move_temp_to_file(tmpfile, filename);
}
int write_sha1_to_fd(int fd, const unsigned char *sha1)
@@ -1420,8 +1462,7 @@ int write_sha1_to_fd(int fd, const unsigned char *sha1)
int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
size_t bufsize, size_t *bufposn)
{
- char *filename = sha1_file_name(sha1);
-
+ char tmpfile[PATH_MAX];
int local;
z_stream stream;
unsigned char real_sha1[20];
@@ -1429,10 +1470,11 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
int ret;
SHA_CTX c;
- local = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
+ snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", get_object_directory());
+ local = mkstemp(tmpfile);
if (local < 0)
- return error("Couldn't open %s\n", filename);
+ return error("Couldn't open %s for %s\n", tmpfile, sha1_to_hex(sha1));
memset(&stream, 0, sizeof(stream));
@@ -1462,7 +1504,7 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
size = read(fd, buffer + *bufposn, bufsize - *bufposn);
if (size <= 0) {
close(local);
- unlink(filename);
+ unlink(tmpfile);
if (!size)
return error("Connection closed?");
perror("Reading from connection");
@@ -1475,15 +1517,15 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
close(local);
SHA1_Final(real_sha1, &c);
if (ret != Z_STREAM_END) {
- unlink(filename);
+ unlink(tmpfile);
return error("File %s corrupted", sha1_to_hex(sha1));
}
if (memcmp(sha1, real_sha1, 20)) {
- unlink(filename);
+ unlink(tmpfile);
return error("File %s has bad hash\n", sha1_to_hex(sha1));
}
-
- return 0;
+
+ return move_temp_to_file(tmpfile, sha1_file_name(sha1));
}
int has_pack_index(const unsigned char *sha1)
@@ -1545,3 +1587,42 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, con
munmap(buf, size);
return ret;
}
+
+int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object)
+{
+ int fd;
+ char *target;
+
+ switch (st->st_mode & S_IFMT) {
+ case S_IFREG:
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return error("open(\"%s\"): %s", path,
+ strerror(errno));
+ if (index_fd(sha1, fd, st, write_object, NULL) < 0)
+ return error("%s: failed to insert into database",
+ path);
+ break;
+ case S_IFLNK:
+ target = xmalloc(st->st_size+1);
+ if (readlink(path, target, st->st_size+1) != st->st_size) {
+ char *errstr = strerror(errno);
+ free(target);
+ return error("readlink(\"%s\"): %s", path,
+ errstr);
+ }
+ if (!write_object) {
+ unsigned char hdr[50];
+ int hdrlen;
+ write_sha1_file_prepare(target, st->st_size, "blob",
+ sha1, hdr, &hdrlen);
+ } else if (write_sha1_file(target, st->st_size, "blob", sha1))
+ return error("%s: failed to insert into database",
+ path);
+ free(target);
+ break;
+ default:
+ return error("%s: unsupported file type", path);
+ }
+ return 0;
+}
diff --git a/t/diff-lib.sh b/t/diff-lib.sh
index a912f435aa..745a1b0311 100755
--- a/t/diff-lib.sh
+++ b/t/diff-lib.sh
@@ -29,7 +29,13 @@ compare_diff_raw_z () {
compare_diff_patch () {
# When heuristics are improved, the score numbers would change.
# Ignore them while comparing.
- sed -e '/^[dis]*imilarity index [0-9]*%$/d' <"$1" >.tmp-1
- sed -e '/^[dis]*imilarity index [0-9]*%$/d' <"$2" >.tmp-2
+ sed -e '
+ /^[dis]*imilarity index [0-9]*%$/d
+ /^index [0-9a-f]*\.\.[0-9a-f]/d
+ ' <"$1" >.tmp-1
+ sed -e '
+ /^[dis]*imilarity index [0-9]*%$/d
+ /^index [0-9a-f]*\.\.[0-9a-f]/d
+ ' <"$2" >.tmp-2
diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
}
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index bd940bd09b..5c5f854858 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -28,7 +28,7 @@ test_expect_success \
'.git/objects should be empty after git-init-db in an empty repo.' \
'cmp -s /dev/null should-be-empty'
-# also it should have 258 subdirectories; 256 fan-out, pack, and info.
+# also it should have 258 subdirectories; 256 fan-out anymore, pack, and info.
# 259 is counting "objects" itself
find .git/objects -type d -print >full-of-directories
test_expect_success \
diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh
index f3b6330a9b..beb6d8f487 100755
--- a/t/t4000-diff-format.sh
+++ b/t/t4000-diff-format.sh
@@ -7,6 +7,7 @@ test_description='Test built-in diff output engine.
'
. ./test-lib.sh
+. ../diff-lib.sh
echo >path0 'Line 1
Line 2
@@ -48,6 +49,6 @@ EOF
test_expect_success \
'validate git-diff-files -p output.' \
- 'cmp -s current expected'
+ 'compare_diff_patch current expected'
test_done
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index be47485682..2e3c20d6b9 100755
--- a/t/t4001-diff-rename.sh
+++ b/t/t4001-diff-rename.sh
@@ -7,6 +7,7 @@ test_description='Test rename detection in diff engine.
'
. ./test-lib.sh
+. ../diff-lib.sh
echo >path0 'Line 1
Line 2
@@ -61,6 +62,6 @@ EOF
test_expect_success \
'validate the output.' \
- 'diff -I "similarity.*" >/dev/null current expected'
+ 'compare_diff_patch current expected'
test_done
diff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh
index f59614ae25..a23aaa0a94 100755
--- a/t/t4004-diff-rename-symlink.sh
+++ b/t/t4004-diff-rename-symlink.sh
@@ -10,6 +10,7 @@ copy of symbolic links, but should not produce rename/copy followed
by an edit for them.
'
. ./test-lib.sh
+. ../diff-lib.sh
test_expect_success \
'prepare reference tree' \
@@ -61,6 +62,6 @@ EOF
test_expect_success \
'validate diff output' \
- 'diff -u current expected'
+ 'compare_diff_patch current expected'
test_done
diff --git a/update-index.c b/update-index.c
index b825a11d2f..01b4088ad6 100644
--- a/update-index.c
+++ b/update-index.c
@@ -37,8 +37,6 @@ static int add_file_to_cache(const char *path)
int size, namelen, option, status;
struct cache_entry *ce;
struct stat st;
- int fd;
- char *target;
status = lstat(path, &st);
if (status < 0 || S_ISDIR(st.st_mode)) {
@@ -77,34 +75,9 @@ static int add_file_to_cache(const char *path)
fill_stat_cache_info(ce, &st);
ce->ce_mode = create_ce_mode(st.st_mode);
ce->ce_flags = htons(namelen);
- switch (st.st_mode & S_IFMT) {
- case S_IFREG:
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return error("open(\"%s\"): %s", path, strerror(errno));
- if (index_fd(ce->sha1, fd, &st, !info_only, NULL) < 0)
- return error("%s: failed to insert into database", path);
- break;
- case S_IFLNK:
- target = xmalloc(st.st_size+1);
- if (readlink(path, target, st.st_size+1) != st.st_size) {
- char *errstr = strerror(errno);
- free(target);
- return error("readlink(\"%s\"): %s", path,
- errstr);
- }
- if (info_only) {
- unsigned char hdr[50];
- int hdrlen;
- write_sha1_file_prepare(target, st.st_size, "blob",
- ce->sha1, hdr, &hdrlen);
- } else if (write_sha1_file(target, st.st_size, "blob", ce->sha1))
- return error("%s: failed to insert into database", path);
- free(target);
- break;
- default:
- return error("%s: unsupported file type", path);
- }
+
+ if (index_path(ce->sha1, path, &st, !info_only))
+ return -1;
option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
if (add_cache_entry(ce, option))
@@ -299,6 +272,54 @@ static void update_one(const char *path, const char *prefix, int prefix_length)
die("Unable to process file %s", path);
}
+static void read_index_info(int line_termination)
+{
+ struct strbuf buf;
+ strbuf_init(&buf);
+ while (1) {
+ char *ptr;
+ unsigned char sha1[20];
+ unsigned int mode;
+
+ read_line(&buf, stdin, line_termination);
+ if (buf.eof)
+ break;
+
+ mode = strtoul(buf.buf, &ptr, 8);
+ if (ptr == buf.buf || *ptr != ' ' ||
+ get_sha1_hex(ptr + 1, sha1) ||
+ ptr[41] != '\t')
+ goto bad_line;
+
+ ptr += 42;
+ if (!verify_path(ptr)) {
+ fprintf(stderr, "Ignoring path %s\n", ptr);
+ continue;
+ }
+
+ if (!mode) {
+ /* mode == 0 means there is no such path -- remove */
+ if (remove_file_from_cache(ptr))
+ die("git-update-index: unable to remove %s",
+ ptr);
+ }
+ else {
+ /* mode ' ' sha1 '\t' name
+ * ptr[-1] points at tab,
+ * ptr[-41] is at the beginning of sha1
+ */
+ ptr[-42] = ptr[-1] = 0;
+ if (add_cacheinfo(buf.buf, ptr-41, ptr))
+ die("git-update-index: unable to update %s",
+ ptr);
+ }
+ continue;
+
+ bad_line:
+ die("malformed index info %s", buf.buf);
+ }
+}
+
int main(int argc, const char **argv)
{
int i, newfd, entries, has_errors = 0, line_termination = '\n';
@@ -373,6 +394,11 @@ int main(int argc, const char **argv)
read_from_stdin = 1;
break;
}
+ if (!strcmp(path, "--index-info")) {
+ allow_add = allow_replace = allow_remove = 1;
+ read_index_info(line_termination);
+ continue;
+ }
if (!strcmp(path, "--ignore-missing")) {
not_new = 1;
continue;
diff --git a/update-server-info.c b/update-server-info.c
index e824f62eaf..b70856373f 100644
--- a/update-server-info.c
+++ b/update-server-info.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "object.h"
static const char update_server_info_usage[] =
"git-update-server-info [--force]";
@@ -7,6 +8,8 @@ int main(int ac, char **av)
{
int i;
int force = 0;
+ track_object_refs = 0;
+
for (i = 1; i < ac; i++) {
if (av[i][0] == '-') {
if (!strcmp("--force", av[i]) ||