summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Documentation/Makefile6
-rw-r--r--Documentation/git-cat-file.txt9
-rw-r--r--Documentation/git-clone.txt9
-rw-r--r--Documentation/tutorial-2.txt2
-rwxr-xr-xGIT-VERSION-GEN4
-rw-r--r--Makefile89
-rw-r--r--builtin-add.c187
-rw-r--r--builtin-apply.c (renamed from apply.c)3
-rw-r--r--builtin-cat-file.c (renamed from cat-file.c)5
-rw-r--r--builtin-commit-tree.c (renamed from commit-tree.c)5
-rw-r--r--builtin-diff-files.c (renamed from diff-files.c)3
-rw-r--r--builtin-diff-index.c (renamed from diff-index.c)3
-rw-r--r--builtin-diff-stages.c (renamed from diff-stages.c)3
-rw-r--r--builtin-diff-tree.c (renamed from diff-tree.c)3
-rw-r--r--builtin-diff.c2
-rw-r--r--builtin-log.c191
-rw-r--r--builtin-ls-files.c (renamed from ls-files.c)384
-rw-r--r--builtin-ls-tree.c (renamed from ls-tree.c)5
-rw-r--r--builtin-read-tree.c (renamed from read-tree.c)3
-rw-r--r--builtin-rev-list.c5
-rw-r--r--builtin-rm.c150
-rw-r--r--builtin-show-branch.c (renamed from show-branch.c)9
-rw-r--r--builtin-tar-tree.c (renamed from tar-tree.c)62
-rw-r--r--builtin-upload-tar.c74
-rw-r--r--builtin.h16
-rw-r--r--cache.h3
-rw-r--r--commit.c134
-rw-r--r--commit.h3
-rw-r--r--connect.c2
-rw-r--r--contrib/git-svn/Makefile1
-rwxr-xr-xcontrib/git-svn/git-svn.perl108
-rw-r--r--contrib/git-svn/t/lib-git-svn.sh39
-rw-r--r--contrib/git-svn/t/t0000-contrib-git-svn.sh43
-rw-r--r--contrib/git-svn/t/t0001-contrib-git-svn-props.sh125
-rwxr-xr-xcontrib/gitview/gitview26
-rw-r--r--contrib/gitview/gitview.txt6
-rw-r--r--date.c29
-rw-r--r--diff.c21
-rw-r--r--diff.h3
-rw-r--r--dir.c401
-rw-r--r--dir.h51
-rw-r--r--fetch-pack.c66
-rw-r--r--fetch.c20
-rw-r--r--fetch.h3
-rwxr-xr-xgenerate-cmdlist.sh1
-rwxr-xr-xgit-add.sh56
-rwxr-xr-xgit-clone.sh14
-rwxr-xr-xgit-commit.sh20
-rwxr-xr-xgit-cvsexportcommit.perl44
-rwxr-xr-xgit-cvsimport.perl228
-rwxr-xr-xgit-format-patch.sh344
-rwxr-xr-xgit-ls-remote.sh10
-rwxr-xr-xgit-rebase.sh2
-rwxr-xr-xgit-rm.sh70
-rw-r--r--git.c19
-rw-r--r--log-tree.c74
-rw-r--r--mailinfo.c145
-rw-r--r--mktag.c45
-rw-r--r--read-cache.c66
-rw-r--r--repo-config.c3
-rw-r--r--revision.c8
-rw-r--r--revision.h2
-rw-r--r--sha1_file.c185
-rwxr-xr-xt/t1002-read-tree-m-u-2way.sh20
-rw-r--r--update-index.c64
66 files changed, 2342 insertions, 1396 deletions
diff --git a/.gitignore b/.gitignore
index b5959d6311..ec4c0dc3df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -77,6 +77,7 @@ git-prune
git-prune-packed
git-pull
git-push
+git-quiltimport
git-read-tree
git-rebase
git-receive-pack
@@ -115,6 +116,7 @@ git-update-index
git-update-ref
git-update-server-info
git-upload-pack
+git-upload-tar
git-var
git-verify-pack
git-verify-tag
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 2a08f592d9..2b0efe7921 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -52,9 +52,9 @@ man1: $(DOC_MAN1)
man7: $(DOC_MAN7)
install: man
- $(INSTALL) -d -m755 $(DESTDIR)/$(man1) $(DESTDIR)/$(man7)
- $(INSTALL) $(DOC_MAN1) $(DESTDIR)/$(man1)
- $(INSTALL) $(DOC_MAN7) $(DESTDIR)/$(man7)
+ $(INSTALL) -d -m755 $(DESTDIR)$(man1) $(DESTDIR)$(man7)
+ $(INSTALL) $(DOC_MAN1) $(DESTDIR)$(man1)
+ $(INSTALL) $(DOC_MAN7) $(DESTDIR)$(man7)
#
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index 504eb1b16a..5e9cbf875d 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -8,12 +8,12 @@ git-cat-file - Provide content or type information for repository objects
SYNOPSIS
--------
-'git-cat-file' [-t | -s | -e | <type>] <object>
+'git-cat-file' [-t | -s | -e | -p | <type>] <object>
DESCRIPTION
-----------
Provides content or type of objects in the repository. The type
-is required unless '-t' is used to find the object type,
+is required unless '-t' or '-p' is used to find the object type,
or '-s' is used to find the object size.
OPTIONS
@@ -33,6 +33,9 @@ OPTIONS
Suppress all output; instead exit with zero status if <object>
exists and is a valid object.
+-p::
+ Pretty-print the contents of <object> based on its type.
+
<type>::
Typically this matches the real type of <object> but asking
for a type that can trivially be dereferenced from the given
@@ -49,6 +52,8 @@ If '-s' is specified, the size of the <object> in bytes.
If '-e' is specified, no output.
+If '-p' is specified, the contents of <object> are pretty-printed.
+
Otherwise the raw (though uncompressed) contents of the <object> will
be returned.
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index b333f51045..94d9393372 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -9,8 +9,8 @@ git-clone - Clones a repository
SYNOPSIS
--------
[verse]
-'git-clone' [-l [-s]] [-q] [-n] [--bare] [-o <name>] [-u <upload-pack>]
- [--reference <repository>]
+'git-clone' [--template=<template_directory>] [-l [-s]] [-q] [-n] [--bare]
+ [-o <name>] [-u <upload-pack>] [--reference <repository>]
<repository> [<directory>]
DESCRIPTION
@@ -89,6 +89,11 @@ OPTIONS
the command to specify non-default path for the command
run on the other end.
+--template=<template_directory>::
+ Specify the directory from which templates will be used;
+ if unset the templates are taken from the installation
+ defined default, typically `/usr/share/git-core/templates`.
+
<repository>::
The (possibly remote) repository to clone from. It can
be any URL git-fetch supports.
diff --git a/Documentation/tutorial-2.txt b/Documentation/tutorial-2.txt
index 7de91aa735..08d3453e5c 100644
--- a/Documentation/tutorial-2.txt
+++ b/Documentation/tutorial-2.txt
@@ -375,7 +375,7 @@ What next?
At this point you should know everything necessary to read the man
pages for any of the git commands; one good place to start would be
-with the commands mentioned in link:everday.html[Everyday git]. You
+with the commands mentioned in link:everyday.html[Everyday git]. You
should be able to find any unknown jargon in the
link:glossary.html[Glosssay].
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 7fcefcd7c4..a461518cde 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -5,7 +5,7 @@ DEF_VER=v1.3.GIT
# First try git-describe, then see if there is a version file
# (included in release tarballs), then default
-if VN=$(git-describe --abbrev=4 HEAD 2>/dev/null); then
+if VN=$(git describe --abbrev=4 HEAD 2>/dev/null); then
VN=$(echo "$VN" | sed -e 's/-/./g');
elif test -f version
then
@@ -16,7 +16,7 @@ fi
VN=$(expr "$VN" : v*'\(.*\)')
-dirty=$(sh -c 'git-diff-index --name-only HEAD' 2>/dev/null) || dirty=
+dirty=$(sh -c 'git diff-index --name-only HEAD' 2>/dev/null) || dirty=
case "$dirty" in
'')
;;
diff --git a/Makefile b/Makefile
index efe6b12719..5ad16a0f45 100644
--- a/Makefile
+++ b/Makefile
@@ -113,14 +113,14 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
### --- END CONFIGURATION SECTION ---
SCRIPT_SH = \
- git-add.sh git-bisect.sh git-branch.sh git-checkout.sh \
+ git-bisect.sh git-branch.sh git-checkout.sh \
git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \
git-fetch.sh \
- git-format-patch.sh git-ls-remote.sh \
+ git-ls-remote.sh \
git-merge-one-file.sh git-parse-remote.sh \
git-prune.sh git-pull.sh git-rebase.sh \
git-repack.sh git-request-pull.sh git-reset.sh \
- git-resolve.sh git-revert.sh git-rm.sh git-sh-setup.sh \
+ git-resolve.sh git-revert.sh git-sh-setup.sh \
git-tag.sh git-verify-tag.sh \
git-applymbox.sh git-applypatch.sh git-am.sh \
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
@@ -149,19 +149,16 @@ SIMPLE_PROGRAMS = \
# ... and all the rest that could be moved out of bindir to gitexecdir
PROGRAMS = \
- 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-checkout-index$X git-clone-pack$X \
+ git-convert-objects$X git-fetch-pack$X git-fsck-objects$X \
git-hash-object$X git-index-pack$X git-local-fetch$X \
- git-ls-files$X git-ls-tree$X git-mailinfo$X git-merge-base$X \
+ git-mailinfo$X git-merge-base$X \
git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \
- git-peek-remote$X git-prune-packed$X git-read-tree$X \
+ git-peek-remote$X git-prune-packed$X \
git-receive-pack$X git-rev-parse$X \
- git-send-pack$X git-show-branch$X git-shell$X \
+ git-send-pack$X git-shell$X \
git-show-index$X git-ssh-fetch$X \
- git-ssh-upload$X git-tar-tree$X git-unpack-file$X \
+ git-ssh-upload$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 \
@@ -170,8 +167,13 @@ PROGRAMS = \
BUILT_INS = git-log$X git-whatchanged$X git-show$X \
git-count-objects$X git-diff$X git-push$X \
- git-grep$X git-rev-list$X git-check-ref-format$X \
- git-init-db$X
+ git-grep$X git-add$X git-rm$X git-rev-list$X \
+ git-check-ref-format$X \
+ git-init-db$X git-tar-tree$X git-upload-tar$X git-format-patch$X \
+ git-ls-files$X git-ls-tree$X \
+ git-read-tree$X git-commit-tree$X \
+ git-apply$X git-show-branch$X git-diff-files$X \
+ git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X
# what 'all' will build and 'install' will install, in gitexecdir
ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
@@ -200,7 +202,7 @@ LIB_H = \
blob.h cache.h commit.h csum-file.h delta.h \
diff.h object.h pack.h pkt-line.h quote.h refs.h \
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
- tree-walk.h log-tree.h
+ tree-walk.h log-tree.h dir.h
DIFF_OBJS = \
diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@ -211,7 +213,7 @@ LIB_OBJS = \
blob.o commit.o connect.o csum-file.o base85.o \
date.o diff-delta.o entry.o exec_cmd.o ident.o index.o \
object.o pack-check.o patch-delta.o path.o pkt-line.o \
- quote.o read-cache.o refs.o run-command.o \
+ quote.o read-cache.o refs.o run-command.o dir.o \
server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
@@ -219,8 +221,14 @@ LIB_OBJS = \
BUILTIN_OBJS = \
builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \
- builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \
- builtin-init-db.o
+ builtin-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.o \
+ builtin-rm.o builtin-init-db.o \
+ builtin-tar-tree.o builtin-upload-tar.o \
+ builtin-ls-files.o builtin-ls-tree.o \
+ builtin-read-tree.o builtin-commit-tree.o \
+ builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
+ builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \
+ builtin-cat-file.o
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
LIBS = $(GITLIBS) -lz
@@ -488,37 +496,43 @@ $(BUILT_INS): git$X
rm -f $@ && ln git$X $@
common-cmds.h: Documentation/git-*.txt
- ./generate-cmdlist.sh > $@
+ ./generate-cmdlist.sh > $@+
+ mv $@+ $@
$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
- rm -f $@
+ rm -f $@ $@+
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
- $@.sh >$@
- chmod +x $@
+ $@.sh >$@+
+ chmod +x $@+
+ mv $@+ $@
$(patsubst %.perl,%,$(SCRIPT_PERL)) : % : %.perl
- rm -f $@
+ rm -f $@ $@+
sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
- $@.perl >$@
- chmod +x $@
+ $@.perl >$@+
+ chmod +x $@+
+ mv $@+ $@
$(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py
- rm -f $@
+ rm -f $@ $@+
sed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
-e 's|@@GIT_PYTHON_PATH@@|$(GIT_PYTHON_DIR_SQ)|g' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
- $@.py >$@
- chmod +x $@
+ $@.py >$@+
+ chmod +x $@+
+ mv $@+ $@
git-cherry-pick: git-revert
- cp $< $@
+ cp $< $@+
+ mv $@+ $@
git-status: git-commit
- cp $< $@
+ cp $< $@+
+ mv $@+ $@
# These can record GIT_VERSION
git$X git.spec \
@@ -627,7 +641,14 @@ install: all
$(MAKE) -C templates install
$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'
$(INSTALL) $(PYMODULES) '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'
- $(foreach p,$(BUILT_INS), rm -f '$(DESTDIR_SQ)$(bindir_SQ)/$p' && ln '$(DESTDIR_SQ)$(bindir_SQ)/git$X' '$(DESTDIR_SQ)$(bindir_SQ)/$p' ;)
+ if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \
+ then \
+ ln -f '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \
+ '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' || \
+ cp '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \
+ '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X'; \
+ fi
+ $(foreach p,$(BUILT_INS), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;)
install-doc:
$(MAKE) -C Documentation install
@@ -638,7 +659,8 @@ install-doc:
### Maintainer's dist rules
git.spec: git.spec.in
- sed -e 's/@@VERSION@@/$(GIT_VERSION)/g' < $< > $@
+ sed -e 's/@@VERSION@@/$(GIT_VERSION)/g' < $< > $@+
+ mv $@+ $@
GIT_TARNAME=git-$(GIT_VERSION)
dist: git.spec git-tar-tree
@@ -665,7 +687,7 @@ dist-doc:
:
rm -fr .doc-tmp-dir
mkdir .doc-tmp-dir .doc-tmp-dir/man1 .doc-tmp-dir/man7
- $(MAKE) -C Documentation DESTDIR=. \
+ $(MAKE) -C Documentation DESTDIR=./ \
man1=../.doc-tmp-dir/man1 \
man7=../.doc-tmp-dir/man7 \
install
@@ -709,4 +731,3 @@ check-docs::
*) echo "no link: $$v";; \
esac ; \
done | sort
-
diff --git a/builtin-add.c b/builtin-add.c
new file mode 100644
index 0000000000..6166f66bce
--- /dev/null
+++ b/builtin-add.c
@@ -0,0 +1,187 @@
+/*
+ * "git add" builtin command
+ *
+ * Copyright (C) 2006 Linus Torvalds
+ */
+#include <fnmatch.h>
+
+#include "cache.h"
+#include "builtin.h"
+#include "dir.h"
+
+static const char builtin_add_usage[] =
+"git-add [-n] [-v] <filepattern>...";
+
+static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
+{
+ char *seen;
+ int i, specs;
+ struct dir_entry **src, **dst;
+
+ for (specs = 0; pathspec[specs]; specs++)
+ /* nothing */;
+ seen = xmalloc(specs);
+ memset(seen, 0, specs);
+
+ src = dst = dir->entries;
+ i = dir->nr;
+ while (--i >= 0) {
+ struct dir_entry *entry = *src++;
+ if (!match_pathspec(pathspec, entry->name, entry->len, prefix, seen)) {
+ free(entry);
+ continue;
+ }
+ *dst++ = entry;
+ }
+ dir->nr = dst - dir->entries;
+
+ for (i = 0; i < specs; i++) {
+ struct stat st;
+ const char *match;
+ if (seen[i])
+ continue;
+
+ /* Existing file? We must have ignored it */
+ match = pathspec[i];
+ if (!match[0] || !lstat(match, &st))
+ continue;
+ die("pathspec '%s' did not match any files", match);
+ }
+}
+
+static void fill_directory(struct dir_struct *dir, const char **pathspec)
+{
+ const char *path, *base;
+ int baselen;
+
+ /* Set up the default git porcelain excludes */
+ memset(dir, 0, sizeof(*dir));
+ dir->exclude_per_dir = ".gitignore";
+ path = git_path("info/exclude");
+ if (!access(path, R_OK))
+ add_excludes_from_file(dir, path);
+
+ /*
+ * Calculate common prefix for the pathspec, and
+ * use that to optimize the directory walk
+ */
+ baselen = common_prefix(pathspec);
+ path = ".";
+ base = "";
+ if (baselen) {
+ char *common = xmalloc(baselen + 1);
+ common = xmalloc(baselen + 1);
+ memcpy(common, *pathspec, baselen);
+ common[baselen] = 0;
+ path = base = common;
+ }
+
+ /* Read the directory and prune it */
+ read_directory(dir, path, base, baselen);
+ if (pathspec)
+ prune_directory(dir, pathspec, baselen);
+}
+
+static int add_file_to_index(const char *path, int verbose)
+{
+ int size, namelen;
+ struct stat st;
+ struct cache_entry *ce;
+
+ if (lstat(path, &st))
+ die("%s: unable to stat (%s)", path, strerror(errno));
+
+ if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
+ die("%s: can only add regular files or symbolic links", path);
+
+ namelen = strlen(path);
+ size = cache_entry_size(namelen);
+ ce = xcalloc(1, size);
+ memcpy(ce->name, path, namelen);
+ ce->ce_flags = htons(namelen);
+ fill_stat_cache_info(ce, &st);
+
+ ce->ce_mode = create_ce_mode(st.st_mode);
+ if (!trust_executable_bit) {
+ /* If there is an existing entry, pick the mode bits
+ * from it.
+ */
+ int pos = cache_name_pos(path, namelen);
+ if (pos >= 0)
+ ce->ce_mode = active_cache[pos]->ce_mode;
+ }
+
+ if (index_path(ce->sha1, path, &st, 1))
+ die("unable to index file %s", path);
+ if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD))
+ die("unable to add %s to index",path);
+ if (verbose)
+ printf("add '%s'\n", path);
+ return 0;
+}
+
+static struct cache_file cache_file;
+
+int cmd_add(int argc, const char **argv, char **envp)
+{
+ int i, newfd;
+ int verbose = 0, show_only = 0;
+ const char *prefix = setup_git_directory();
+ const char **pathspec;
+ struct dir_struct dir;
+
+ git_config(git_default_config);
+
+ newfd = hold_index_file_for_update(&cache_file, get_index_file());
+ if (newfd < 0)
+ die("unable to create new cachefile");
+
+ if (read_cache() < 0)
+ die("index file corrupt");
+
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+
+ if (arg[0] != '-')
+ break;
+ if (!strcmp(arg, "--")) {
+ i++;
+ break;
+ }
+ if (!strcmp(arg, "-n")) {
+ show_only = 1;
+ continue;
+ }
+ if (!strcmp(arg, "-v")) {
+ verbose = 1;
+ continue;
+ }
+ die(builtin_add_usage);
+ }
+ git_config(git_default_config);
+ pathspec = get_pathspec(prefix, argv + i);
+
+ fill_directory(&dir, pathspec);
+
+ if (show_only) {
+ const char *sep = "", *eof = "";
+ for (i = 0; i < dir.nr; i++) {
+ printf("%s%s", sep, dir.entries[i]->name);
+ sep = " ";
+ eof = "\n";
+ }
+ fputs(eof, stdout);
+ return 0;
+ }
+
+ for (i = 0; i < dir.nr; i++)
+ add_file_to_index(dir.entries[i]->name, verbose);
+
+ if (active_cache_changed) {
+ if (write_cache(newfd, active_cache, active_nr) ||
+ commit_index_file(&cache_file))
+ die("Unable to write new index file");
+ }
+
+ return 0;
+}
diff --git a/apply.c b/builtin-apply.c
index 0ed9d132e8..4056b9d67b 100644
--- a/apply.c
+++ b/builtin-apply.c
@@ -11,6 +11,7 @@
#include "quote.h"
#include "blob.h"
#include "delta.h"
+#include "builtin.h"
// --check turns on checking that the working tree matches the
// files that are being modified, but doesn't apply the patch
@@ -2151,7 +2152,7 @@ static int git_apply_config(const char *var, const char *value)
}
-int main(int argc, char **argv)
+int cmd_apply(int argc, const char **argv, char **envp)
{
int i;
int read_stdin = 1;
diff --git a/cat-file.c b/builtin-cat-file.c
index 7413feed78..4d36817e5f 100644
--- a/cat-file.c
+++ b/builtin-cat-file.c
@@ -7,6 +7,7 @@
#include "exec_cmd.h"
#include "tag.h"
#include "tree.h"
+#include "builtin.h"
static void flush_buffer(const char *buf, unsigned long size)
{
@@ -93,7 +94,7 @@ static int pprint_tag(const unsigned char *sha1, const char *buf, unsigned long
return 0;
}
-int main(int argc, char **argv)
+int cmd_cat_file(int argc, const char **argv, char **envp)
{
unsigned char sha1[20];
char type[20];
@@ -140,7 +141,7 @@ int main(int argc, char **argv)
/* custom pretty-print here */
if (!strcmp(type, tree_type))
- return execl_git_cmd("ls-tree", argv[2], NULL);
+ return cmd_ls_tree(2, argv + 1, NULL);
buf = read_sha1_file(sha1, type, &size);
if (!buf)
diff --git a/commit-tree.c b/builtin-commit-tree.c
index 0320036e80..ec082bf754 100644
--- a/commit-tree.c
+++ b/builtin-commit-tree.c
@@ -6,6 +6,7 @@
#include "cache.h"
#include "commit.h"
#include "tree.h"
+#include "builtin.h"
#define BLOCKING (1ul << 14)
@@ -76,7 +77,7 @@ static int new_parent(int idx)
return 1;
}
-int main(int argc, char **argv)
+int cmd_commit_tree(int argc, const char **argv, char **envp)
{
int i;
int parents = 0;
@@ -98,7 +99,7 @@ int main(int argc, char **argv)
check_valid(tree_sha1, tree_type);
for (i = 2; i < argc; i += 2) {
- char *a, *b;
+ const char *a, *b;
a = argv[i]; b = argv[i+1];
if (!b || strcmp(a, "-p"))
usage(commit_tree_usage);
diff --git a/diff-files.c b/builtin-diff-files.c
index b9d193d506..cebda828ee 100644
--- a/diff-files.c
+++ b/builtin-diff-files.c
@@ -7,12 +7,13 @@
#include "diff.h"
#include "commit.h"
#include "revision.h"
+#include "builtin.h"
static const char diff_files_usage[] =
"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
COMMON_DIFF_OPTIONS_HELP;
-int main(int argc, const char **argv)
+int cmd_diff_files(int argc, const char **argv, char **envp)
{
struct rev_info rev;
int silent = 0;
diff --git a/diff-index.c b/builtin-diff-index.c
index 8c9f60173b..1958580d82 100644
--- a/diff-index.c
+++ b/builtin-diff-index.c
@@ -2,13 +2,14 @@
#include "diff.h"
#include "commit.h"
#include "revision.h"
+#include "builtin.h"
static const char diff_cache_usage[] =
"git-diff-index [-m] [--cached] "
"[<common diff options>] <tree-ish> [<path>...]"
COMMON_DIFF_OPTIONS_HELP;
-int main(int argc, const char **argv)
+int cmd_diff_index(int argc, const char **argv, char **envp)
{
struct rev_info rev;
int cached = 0;
diff --git a/diff-stages.c b/builtin-diff-stages.c
index dcd20e79e4..7c157ca889 100644
--- a/diff-stages.c
+++ b/builtin-diff-stages.c
@@ -4,6 +4,7 @@
#include "cache.h"
#include "diff.h"
+#include "builtin.h"
static struct diff_options diff_options;
@@ -54,7 +55,7 @@ static void diff_stages(int stage1, int stage2, const char **pathspec)
}
}
-int main(int ac, const char **av)
+int cmd_diff_stages(int ac, const char **av, char **envp)
{
int stage1, stage2;
const char *prefix = setup_git_directory();
diff --git a/diff-tree.c b/builtin-diff-tree.c
index 69bb74b310..cc53b81ac4 100644
--- a/diff-tree.c
+++ b/builtin-diff-tree.c
@@ -2,6 +2,7 @@
#include "diff.h"
#include "commit.h"
#include "log-tree.h"
+#include "builtin.h"
static struct rev_info log_tree_opt;
@@ -58,7 +59,7 @@ static const char diff_tree_usage[] =
" --root include the initial commit as diff against /dev/null\n"
COMMON_DIFF_OPTIONS_HELP;
-int main(int argc, const char **argv)
+int cmd_diff_tree(int argc, const char **argv, char **envp)
{
int nr_sha1;
char line[1000];
diff --git a/builtin-diff.c b/builtin-diff.c
index de81b05e32..27451d5613 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -233,7 +233,7 @@ static int builtin_diff_combined(struct rev_info *revs,
return 0;
}
-static void add_head(struct rev_info *revs)
+void add_head(struct rev_info *revs)
{
unsigned char sha1[20];
struct object *obj;
diff --git a/builtin-log.c b/builtin-log.c
index c4ceee0f98..db1912a05b 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -9,6 +9,10 @@
#include "diff.h"
#include "revision.h"
#include "log-tree.h"
+#include "builtin.h"
+
+/* this is in builtin-diff.c */
+void add_head(struct rev_info *revs);
static int cmd_log_wc(int argc, const char **argv, char **envp,
struct rev_info *rev)
@@ -74,3 +78,190 @@ int cmd_log(int argc, const char **argv, char **envp)
rev.diffopt.recursive = 1;
return cmd_log_wc(argc, argv, envp, &rev);
}
+
+static int istitlechar(char c)
+{
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') || c == '.' || c == '_';
+}
+
+static FILE *realstdout = NULL;
+static char *output_directory = NULL;
+
+static void reopen_stdout(struct commit *commit, int nr, int keep_subject)
+{
+ char filename[1024];
+ char *sol;
+ int len = 0;
+
+ if (output_directory) {
+ strncpy(filename, output_directory, 1010);
+ len = strlen(filename);
+ if (filename[len - 1] != '/')
+ filename[len++] = '/';
+ }
+
+ sprintf(filename + len, "%04d", nr);
+ len = strlen(filename);
+
+ sol = strstr(commit->buffer, "\n\n");
+ if (sol) {
+ int j, space = 1;
+
+ sol += 2;
+ /* strip [PATCH] or [PATCH blabla] */
+ if (!keep_subject && !strncmp(sol, "[PATCH", 6)) {
+ char *eos = strchr(sol + 6, ']');
+ if (eos) {
+ while (isspace(*eos))
+ eos++;
+ sol = eos;
+ }
+ }
+
+ for (j = 0; len < 1024 - 6 && sol[j] && sol[j] != '\n'; j++) {
+ if (istitlechar(sol[j])) {
+ if (space) {
+ filename[len++] = '-';
+ space = 0;
+ }
+ filename[len++] = sol[j];
+ if (sol[j] == '.')
+ while (sol[j + 1] == '.')
+ j++;
+ } else
+ space = 1;
+ }
+ while (filename[len - 1] == '.' || filename[len - 1] == '-')
+ len--;
+ }
+ strcpy(filename + len, ".txt");
+ fprintf(realstdout, "%s\n", filename);
+ freopen(filename, "w", stdout);
+}
+
+int cmd_format_patch(int argc, const char **argv, char **envp)
+{
+ struct commit *commit;
+ struct commit **list = NULL;
+ struct rev_info rev;
+ int nr = 0, total, i, j;
+ int use_stdout = 0;
+ int numbered = 0;
+ int start_number = -1;
+ int keep_subject = 0;
+
+ init_revisions(&rev);
+ rev.commit_format = CMIT_FMT_EMAIL;
+ rev.verbose_header = 1;
+ rev.diff = 1;
+ rev.diffopt.with_raw = 0;
+ rev.diffopt.with_stat = 1;
+ rev.combine_merges = 0;
+ rev.ignore_merges = 1;
+ rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+
+ /*
+ * Parse the arguments before setup_revisions(), or something
+ * like "git fmt-patch -o a123 HEAD^.." may fail; a123 is
+ * possibly a valid SHA1.
+ */
+ for (i = 1, j = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "--stdout"))
+ use_stdout = 1;
+ else if (!strcmp(argv[i], "-n") ||
+ !strcmp(argv[i], "--numbered"))
+ numbered = 1;
+ else if (!strncmp(argv[i], "--start-number=", 15))
+ start_number = strtol(argv[i] + 15, NULL, 10);
+ else if (!strcmp(argv[i], "--start-number")) {
+ i++;
+ if (i == argc)
+ die("Need a number for --start-number");
+ start_number = strtol(argv[i], NULL, 10);
+ } else if (!strcmp(argv[i], "-k") ||
+ !strcmp(argv[i], "--keep-subject")) {
+ keep_subject = 1;
+ rev.total = -1;
+ } else if (!strcmp(argv[i], "-o")) {
+ if (argc < 3)
+ die ("Which directory?");
+ if (mkdir(argv[i + 1], 0777) < 0 && errno != EEXIST)
+ die("Could not create directory %s",
+ argv[i + 1]);
+ output_directory = strdup(argv[i + 1]);
+ i++;
+ }
+ else if (!strcmp(argv[i], "--attach"))
+ rev.mime_boundary = git_version_string;
+ else if (!strncmp(argv[i], "--attach=", 9))
+ rev.mime_boundary = argv[i] + 9;
+ else
+ argv[j++] = argv[i];
+ }
+ argc = j;
+
+ if (start_number < 0)
+ start_number = 1;
+ if (numbered && keep_subject)
+ die ("-n and -k are mutually exclusive.");
+
+ argc = setup_revisions(argc, argv, &rev, "HEAD");
+ if (argc > 1)
+ die ("unrecognized argument: %s", argv[1]);
+
+ if (rev.pending_objects && rev.pending_objects->next == NULL) {
+ rev.pending_objects->item->flags |= UNINTERESTING;
+ add_head(&rev);
+ }
+
+ if (!use_stdout)
+ realstdout = fdopen(dup(1), "w");
+
+ prepare_revision_walk(&rev);
+ while ((commit = get_revision(&rev)) != NULL) {
+ /* ignore merges */
+ if (commit->parents && commit->parents->next)
+ continue;
+ nr++;
+ list = realloc(list, nr * sizeof(list[0]));
+ list[nr - 1] = commit;
+ }
+ total = nr;
+ if (numbered)
+ rev.total = total + start_number - 1;
+ while (0 <= --nr) {
+ int shown;
+ commit = list[nr];
+ rev.nr = total - nr + (start_number - 1);
+ if (!use_stdout)
+ reopen_stdout(commit, rev.nr, keep_subject);
+ shown = log_tree_commit(&rev, commit);
+ free(commit->buffer);
+ commit->buffer = NULL;
+
+ /* We put one extra blank line between formatted
+ * patches and this flag is used by log-tree code
+ * to see if it needs to emit a LF before showing
+ * the log; when using one file per patch, we do
+ * not want the extra blank line.
+ */
+ if (!use_stdout)
+ rev.shown_one = 0;
+ if (shown) {
+ if (rev.mime_boundary)
+ printf("\n--%s%s--\n\n\n",
+ mime_boundary_leader,
+ rev.mime_boundary);
+ else
+ printf("-- \n%s\n\n", git_version_string);
+ }
+ if (!use_stdout)
+ fclose(stdout);
+ }
+ if (output_directory)
+ free(output_directory);
+ free(list);
+ return 0;
+}
+
diff --git a/ls-files.c b/builtin-ls-files.c
index 4a4af1ca3b..8dae9f70e2 100644
--- a/ls-files.c
+++ b/builtin-ls-files.c
@@ -5,23 +5,21 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#include <dirent.h>
#include <fnmatch.h>
#include "cache.h"
#include "quote.h"
+#include "dir.h"
+#include "builtin.h"
static int abbrev = 0;
static int show_deleted = 0;
static int show_cached = 0;
static int show_others = 0;
-static int show_ignored = 0;
static int show_stage = 0;
static int show_unmerged = 0;
static int show_modified = 0;
static int show_killed = 0;
-static int show_other_directories = 0;
-static int hide_empty_directories = 0;
static int show_valid_bit = 0;
static int line_terminator = '\n';
@@ -38,309 +36,6 @@ static const char *tag_other = "";
static const char *tag_killed = "";
static const char *tag_modified = "";
-static const char *exclude_per_dir = NULL;
-
-/* We maintain three exclude pattern lists:
- * EXC_CMDL lists patterns explicitly given on the command line.
- * EXC_DIRS lists patterns obtained from per-directory ignore files.
- * EXC_FILE lists patterns from fallback ignore files.
- */
-#define EXC_CMDL 0
-#define EXC_DIRS 1
-#define EXC_FILE 2
-static struct exclude_list {
- int nr;
- int alloc;
- struct exclude {
- const char *pattern;
- const char *base;
- int baselen;
- } **excludes;
-} exclude_list[3];
-
-static void add_exclude(const char *string, const char *base,
- int baselen, struct exclude_list *which)
-{
- struct exclude *x = xmalloc(sizeof (*x));
-
- x->pattern = string;
- x->base = base;
- x->baselen = baselen;
- if (which->nr == which->alloc) {
- which->alloc = alloc_nr(which->alloc);
- which->excludes = realloc(which->excludes,
- which->alloc * sizeof(x));
- }
- which->excludes[which->nr++] = x;
-}
-
-static int add_excludes_from_file_1(const char *fname,
- const char *base,
- int baselen,
- struct exclude_list *which)
-{
- int fd, i;
- long size;
- char *buf, *entry;
-
- fd = open(fname, O_RDONLY);
- if (fd < 0)
- goto err;
- size = lseek(fd, 0, SEEK_END);
- if (size < 0)
- goto err;
- lseek(fd, 0, SEEK_SET);
- if (size == 0) {
- close(fd);
- return 0;
- }
- buf = xmalloc(size+1);
- if (read(fd, buf, size) != size)
- goto err;
- close(fd);
-
- buf[size++] = '\n';
- entry = buf;
- for (i = 0; i < size; i++) {
- if (buf[i] == '\n') {
- if (entry != buf + i && entry[0] != '#') {
- buf[i - (i && buf[i-1] == '\r')] = 0;
- add_exclude(entry, base, baselen, which);
- }
- entry = buf + i + 1;
- }
- }
- return 0;
-
- err:
- if (0 <= fd)
- close(fd);
- return -1;
-}
-
-static void add_excludes_from_file(const char *fname)
-{
- if (add_excludes_from_file_1(fname, "", 0,
- &exclude_list[EXC_FILE]) < 0)
- die("cannot use %s as an exclude file", fname);
-}
-
-static int push_exclude_per_directory(const char *base, int baselen)
-{
- char exclude_file[PATH_MAX];
- struct exclude_list *el = &exclude_list[EXC_DIRS];
- int current_nr = el->nr;
-
- if (exclude_per_dir) {
- memcpy(exclude_file, base, baselen);
- strcpy(exclude_file + baselen, exclude_per_dir);
- add_excludes_from_file_1(exclude_file, base, baselen, el);
- }
- return current_nr;
-}
-
-static void pop_exclude_per_directory(int stk)
-{
- struct exclude_list *el = &exclude_list[EXC_DIRS];
-
- while (stk < el->nr)
- free(el->excludes[--el->nr]);
-}
-
-/* Scan the list and let the last match determines the fate.
- * Return 1 for exclude, 0 for include and -1 for undecided.
- */
-static int excluded_1(const char *pathname,
- int pathlen,
- struct exclude_list *el)
-{
- int i;
-
- if (el->nr) {
- for (i = el->nr - 1; 0 <= i; i--) {
- struct exclude *x = el->excludes[i];
- const char *exclude = x->pattern;
- int to_exclude = 1;
-
- if (*exclude == '!') {
- to_exclude = 0;
- exclude++;
- }
-
- if (!strchr(exclude, '/')) {
- /* match basename */
- const char *basename = strrchr(pathname, '/');
- basename = (basename) ? basename+1 : pathname;
- if (fnmatch(exclude, basename, 0) == 0)
- return to_exclude;
- }
- else {
- /* match with FNM_PATHNAME:
- * exclude has base (baselen long) implicitly
- * in front of it.
- */
- int baselen = x->baselen;
- if (*exclude == '/')
- exclude++;
-
- if (pathlen < baselen ||
- (baselen && pathname[baselen-1] != '/') ||
- strncmp(pathname, x->base, baselen))
- continue;
-
- if (fnmatch(exclude, pathname+baselen,
- FNM_PATHNAME) == 0)
- return to_exclude;
- }
- }
- }
- return -1; /* undecided */
-}
-
-static int excluded(const char *pathname)
-{
- int pathlen = strlen(pathname);
- int st;
-
- for (st = EXC_CMDL; st <= EXC_FILE; st++) {
- switch (excluded_1(pathname, pathlen, &exclude_list[st])) {
- case 0:
- return 0;
- case 1:
- return 1;
- }
- }
- return 0;
-}
-
-struct nond_on_fs {
- int len;
- char name[FLEX_ARRAY]; /* more */
-};
-
-static struct nond_on_fs **dir;
-static int nr_dir;
-static int dir_alloc;
-
-static void add_name(const char *pathname, int len)
-{
- struct nond_on_fs *ent;
-
- if (cache_name_pos(pathname, len) >= 0)
- return;
-
- if (nr_dir == dir_alloc) {
- dir_alloc = alloc_nr(dir_alloc);
- dir = xrealloc(dir, dir_alloc*sizeof(ent));
- }
- ent = xmalloc(sizeof(*ent) + len + 1);
- ent->len = len;
- memcpy(ent->name, pathname, len);
- ent->name[len] = 0;
- dir[nr_dir++] = ent;
-}
-
-static int dir_exists(const char *dirname, int len)
-{
- int pos = cache_name_pos(dirname, len);
- if (pos >= 0)
- return 1;
- pos = -pos-1;
- if (pos >= active_nr) /* can't */
- return 0;
- return !strncmp(active_cache[pos]->name, dirname, len);
-}
-
-/*
- * Read a directory tree. We currently ignore anything but
- * directories, regular files and symlinks. That's because git
- * doesn't handle them at all yet. Maybe that will change some
- * day.
- *
- * Also, we ignore the name ".git" (even if it is not a directory).
- * That likely will not change.
- */
-static int read_directory(const char *path, const char *base, int baselen)
-{
- DIR *fdir = opendir(path);
- int contents = 0;
-
- if (fdir) {
- int exclude_stk;
- struct dirent *de;
- char fullname[MAXPATHLEN + 1];
- memcpy(fullname, base, baselen);
-
- exclude_stk = push_exclude_per_directory(base, baselen);
-
- while ((de = readdir(fdir)) != NULL) {
- int len;
-
- if ((de->d_name[0] == '.') &&
- (de->d_name[1] == 0 ||
- !strcmp(de->d_name + 1, ".") ||
- !strcmp(de->d_name + 1, "git")))
- continue;
- len = strlen(de->d_name);
- memcpy(fullname + baselen, de->d_name, len+1);
- if (excluded(fullname) != show_ignored) {
- if (!show_ignored || DTYPE(de) != DT_DIR) {
- continue;
- }
- }
-
- switch (DTYPE(de)) {
- struct stat st;
- int subdir, rewind_base;
- default:
- continue;
- case DT_UNKNOWN:
- if (lstat(fullname, &st))
- continue;
- if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
- break;
- if (!S_ISDIR(st.st_mode))
- continue;
- /* fallthrough */
- case DT_DIR:
- memcpy(fullname + baselen + len, "/", 2);
- len++;
- rewind_base = nr_dir;
- subdir = read_directory(fullname, fullname,
- baselen + len);
- if (show_other_directories &&
- (subdir || !hide_empty_directories) &&
- !dir_exists(fullname, baselen + len)) {
- // Rewind the read subdirectory
- while (nr_dir > rewind_base)
- free(dir[--nr_dir]);
- break;
- }
- contents += subdir;
- continue;
- case DT_REG:
- case DT_LNK:
- break;
- }
- add_name(fullname, baselen + len);
- contents++;
- }
- closedir(fdir);
-
- pop_exclude_per_directory(exclude_stk);
- }
-
- return contents;
-}
-
-static int cmp_name(const void *p1, const void *p2)
-{
- const struct nond_on_fs *e1 = *(const struct nond_on_fs **)p1;
- const struct nond_on_fs *e2 = *(const struct nond_on_fs **)p2;
-
- return cache_name_compare(e1->name, e1->len,
- e2->name, e2->len);
-}
/*
* Match a pathspec against a filename. The first "len" characters
@@ -377,7 +72,7 @@ static int match(const char **spec, char *ps_matched,
return 0;
}
-static void show_dir_entry(const char *tag, struct nond_on_fs *ent)
+static void show_dir_entry(const char *tag, struct dir_entry *ent)
{
int len = prefix_len;
int offset = prefix_offset;
@@ -393,14 +88,14 @@ static void show_dir_entry(const char *tag, struct nond_on_fs *ent)
putchar(line_terminator);
}
-static void show_other_files(void)
+static void show_other_files(struct dir_struct *dir)
{
int i;
- for (i = 0; i < nr_dir; i++) {
+ for (i = 0; i < dir->nr; i++) {
/* We should not have a matching entry, but we
* may have an unmerged entry for this path.
*/
- struct nond_on_fs *ent = dir[i];
+ struct dir_entry *ent = dir->entries[i];
int pos = cache_name_pos(ent->name, ent->len);
struct cache_entry *ce;
if (0 <= pos)
@@ -416,11 +111,11 @@ static void show_other_files(void)
}
}
-static void show_killed_files(void)
+static void show_killed_files(struct dir_struct *dir)
{
int i;
- for (i = 0; i < nr_dir; i++) {
- struct nond_on_fs *ent = dir[i];
+ for (i = 0; i < dir->nr; i++) {
+ struct dir_entry *ent = dir->entries[i];
char *cp, *sp;
int pos, len, killed = 0;
@@ -461,7 +156,7 @@ static void show_killed_files(void)
}
}
if (killed)
- show_dir_entry(tag_killed, dir[i]);
+ show_dir_entry(tag_killed, dir->entries[i]);
}
}
@@ -512,7 +207,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
}
}
-static void show_files(void)
+static void show_files(struct dir_struct *dir)
{
int i;
@@ -521,39 +216,18 @@ static void show_files(void)
const char *path = ".", *base = "";
int baselen = prefix_len;
- if (baselen) {
+ if (baselen)
path = base = prefix;
- if (exclude_per_dir) {
- char *p, *pp = xmalloc(baselen+1);
- memcpy(pp, prefix, baselen+1);
- p = pp;
- while (1) {
- char save = *p;
- *p = 0;
- push_exclude_per_directory(pp, p-pp);
- *p++ = save;
- if (!save)
- break;
- p = strchr(p, '/');
- if (p)
- p++;
- else
- p = pp + baselen;
- }
- free(pp);
- }
- }
- read_directory(path, base, baselen);
- qsort(dir, nr_dir, sizeof(struct nond_on_fs *), cmp_name);
+ read_directory(dir, path, base, baselen);
if (show_others)
- show_other_files();
+ show_other_files(dir);
if (show_killed)
- show_killed_files();
+ show_killed_files(dir);
}
if (show_cached | show_stage) {
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
- if (excluded(ce->name) != show_ignored)
+ if (excluded(dir, ce->name) != dir->show_ignored)
continue;
if (show_unmerged && !ce_stage(ce))
continue;
@@ -565,7 +239,7 @@ static void show_files(void)
struct cache_entry *ce = active_cache[i];
struct stat st;
int err;
- if (excluded(ce->name) != show_ignored)
+ if (excluded(dir, ce->name) != dir->show_ignored)
continue;
err = lstat(ce->name, &st);
if (show_deleted && err)
@@ -648,11 +322,13 @@ static const char ls_files_usage[] =
"[ --exclude-per-directory=<filename> ] [--full-name] [--abbrev] "
"[--] [<file>]*";
-int main(int argc, const char **argv)
+int cmd_ls_files(int argc, const char **argv, char** envp)
{
int i;
int exc_given = 0;
+ struct dir_struct dir;
+ memset(&dir, 0, sizeof(dir));
prefix = setup_git_directory();
if (prefix)
prefix_offset = strlen(prefix);
@@ -697,7 +373,7 @@ int main(int argc, const char **argv)
continue;
}
if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) {
- show_ignored = 1;
+ dir.show_ignored = 1;
continue;
}
if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) {
@@ -709,11 +385,11 @@ int main(int argc, const char **argv)
continue;
}
if (!strcmp(arg, "--directory")) {
- show_other_directories = 1;
+ dir.show_other_directories = 1;
continue;
}
if (!strcmp(arg, "--no-empty-directory")) {
- hide_empty_directories = 1;
+ dir.hide_empty_directories = 1;
continue;
}
if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) {
@@ -726,27 +402,27 @@ int main(int argc, const char **argv)
}
if (!strcmp(arg, "-x") && i+1 < argc) {
exc_given = 1;
- add_exclude(argv[++i], "", 0, &exclude_list[EXC_CMDL]);
+ add_exclude(argv[++i], "", 0, &dir.exclude_list[EXC_CMDL]);
continue;
}
if (!strncmp(arg, "--exclude=", 10)) {
exc_given = 1;
- add_exclude(arg+10, "", 0, &exclude_list[EXC_CMDL]);
+ add_exclude(arg+10, "", 0, &dir.exclude_list[EXC_CMDL]);
continue;
}
if (!strcmp(arg, "-X") && i+1 < argc) {
exc_given = 1;
- add_excludes_from_file(argv[++i]);
+ add_excludes_from_file(&dir, argv[++i]);
continue;
}
if (!strncmp(arg, "--exclude-from=", 15)) {
exc_given = 1;
- add_excludes_from_file(arg+15);
+ add_excludes_from_file(&dir, arg+15);
continue;
}
if (!strncmp(arg, "--exclude-per-directory=", 24)) {
exc_given = 1;
- exclude_per_dir = arg + 24;
+ dir.exclude_per_dir = arg + 24;
continue;
}
if (!strcmp(arg, "--full-name")) {
@@ -788,7 +464,7 @@ int main(int argc, const char **argv)
ps_matched = xcalloc(1, num);
}
- if (show_ignored && !exc_given) {
+ if (dir.show_ignored && !exc_given) {
fprintf(stderr, "%s: --ignored needs some exclude pattern\n",
argv[0]);
exit(1);
@@ -802,7 +478,7 @@ int main(int argc, const char **argv)
read_cache();
if (prefix)
prune_cache();
- show_files();
+ show_files(&dir);
if (ps_matched) {
/* We need to make sure all pathspec matched otherwise
diff --git a/ls-tree.c b/builtin-ls-tree.c
index f2b3bc1231..48385d59f6 100644
--- a/ls-tree.c
+++ b/builtin-ls-tree.c
@@ -7,6 +7,7 @@
#include "blob.h"
#include "tree.h"
#include "quote.h"
+#include "builtin.h"
static int line_termination = '\n';
#define LS_RECURSIVE 1
@@ -15,7 +16,7 @@ static int line_termination = '\n';
#define LS_NAME_ONLY 8
static int abbrev = 0;
static int ls_options = 0;
-const char **pathspec;
+static const char **pathspec;
static int chomp_prefix = 0;
static const char *prefix;
@@ -84,7 +85,7 @@ static int show_tree(unsigned char *sha1, const char *base, int baselen,
return retval;
}
-int main(int argc, const char **argv)
+int cmd_ls_tree(int argc, const char **argv, char **envp)
{
unsigned char sha1[20];
struct tree *tree;
diff --git a/read-tree.c b/builtin-read-tree.c
index 82e2a9a4d3..ec40d013c4 100644
--- a/read-tree.c
+++ b/builtin-read-tree.c
@@ -11,6 +11,7 @@
#include "tree.h"
#include <sys/time.h>
#include <signal.h>
+#include "builtin.h"
static int reset = 0;
static int merge = 0;
@@ -763,7 +764,7 @@ static const char read_tree_usage[] = "git-read-tree (<sha> | -m [--aggressive]
static struct cache_file cache_file;
-int main(int argc, char **argv)
+int cmd_read_tree(int argc, const char **argv, char **envp)
{
int i, newfd, stage = 0;
unsigned char sha1[20];
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index 446802d377..5277d3cf12 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -85,7 +85,7 @@ static void show_commit(struct commit *commit)
static char pretty_header[16384];
pretty_print_commit(revs.commit_format, commit, ~0,
pretty_header, sizeof(pretty_header),
- revs.abbrev);
+ revs.abbrev, NULL, NULL);
printf("%s%c", pretty_header, hdr_termination);
}
fflush(stdout);
@@ -103,6 +103,7 @@ static struct object_list **process_blob(struct blob *blob,
if (obj->flags & (UNINTERESTING | SEEN))
return p;
obj->flags |= SEEN;
+ name = strdup(name);
return add_object(obj, p, path, name);
}
@@ -122,6 +123,7 @@ static struct object_list **process_tree(struct tree *tree,
if (parse_tree(tree) < 0)
die("bad tree object %s", sha1_to_hex(obj->sha1));
obj->flags |= SEEN;
+ name = strdup(name);
p = add_object(obj, p, path, name);
me.up = path;
me.elem = name;
@@ -134,6 +136,7 @@ static struct object_list **process_tree(struct tree *tree,
p = process_tree(entry->item.tree, p, &me, entry->name);
else
p = process_blob(entry->item.blob, p, &me, entry->name);
+ free(entry->name);
free(entry);
entry = next;
}
diff --git a/builtin-rm.c b/builtin-rm.c
new file mode 100644
index 0000000000..9014c61556
--- /dev/null
+++ b/builtin-rm.c
@@ -0,0 +1,150 @@
+/*
+ * "git rm" builtin command
+ *
+ * Copyright (C) Linus Torvalds 2006
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "dir.h"
+
+static const char builtin_rm_usage[] =
+"git-rm [-n] [-v] [-f] <filepattern>...";
+
+static struct {
+ int nr, alloc;
+ const char **name;
+} list;
+
+static void add_list(const char *name)
+{
+ if (list.nr >= list.alloc) {
+ list.alloc = alloc_nr(list.alloc);
+ list.name = xrealloc(list.name, list.alloc * sizeof(const char *));
+ }
+ list.name[list.nr++] = name;
+}
+
+static int remove_file(const char *name)
+{
+ int ret;
+ char *slash;
+
+ ret = unlink(name);
+ if (!ret && (slash = strrchr(name, '/'))) {
+ char *n = strdup(name);
+ do {
+ n[slash - name] = 0;
+ name = n;
+ } while (!rmdir(name) && (slash = strrchr(name, '/')));
+ }
+ return ret;
+}
+
+static struct cache_file cache_file;
+
+int cmd_rm(int argc, const char **argv, char **envp)
+{
+ int i, newfd;
+ int verbose = 0, show_only = 0, force = 0;
+ const char *prefix = setup_git_directory();
+ const char **pathspec;
+ char *seen;
+
+ git_config(git_default_config);
+
+ newfd = hold_index_file_for_update(&cache_file, get_index_file());
+ if (newfd < 0)
+ die("unable to create new index file");
+
+ if (read_cache() < 0)
+ die("index file corrupt");
+
+ for (i = 1 ; i < argc ; i++) {
+ const char *arg = argv[i];
+
+ if (*arg != '-')
+ break;
+ if (!strcmp(arg, "--")) {
+ i++;
+ break;
+ }
+ if (!strcmp(arg, "-n")) {
+ show_only = 1;
+ continue;
+ }
+ if (!strcmp(arg, "-v")) {
+ verbose = 1;
+ continue;
+ }
+ if (!strcmp(arg, "-f")) {
+ force = 1;
+ continue;
+ }
+ die(builtin_rm_usage);
+ }
+ pathspec = get_pathspec(prefix, argv + i);
+
+ seen = NULL;
+ if (pathspec) {
+ for (i = 0; pathspec[i] ; i++)
+ /* nothing */;
+ seen = xmalloc(i);
+ memset(seen, 0, i);
+ }
+
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
+ continue;
+ add_list(ce->name);
+ }
+
+ if (pathspec) {
+ const char *match;
+ for (i = 0; (match = pathspec[i]) != NULL ; i++) {
+ if (*match && !seen[i])
+ die("pathspec '%s' did not match any files", match);
+ }
+ }
+
+ /*
+ * First remove the names from the index: we won't commit
+ * the index unless all of them succeed
+ */
+ for (i = 0; i < list.nr; i++) {
+ const char *path = list.name[i];
+ printf("rm '%s'\n", path);
+
+ if (remove_file_from_cache(path))
+ die("git rm: unable to remove %s", path);
+ }
+
+ /*
+ * Then, if we used "-f", remove the filenames from the
+ * workspace. If we fail to remove the first one, we
+ * abort the "git rm" (but once we've successfully removed
+ * any file at all, we'll go ahead and commit to it all:
+ * by then we've already committed ourself and can't fail
+ * in the middle)
+ */
+ if (force) {
+ int removed = 0;
+ for (i = 0; i < list.nr; i++) {
+ const char *path = list.name[i];
+ if (!remove_file(path)) {
+ removed = 1;
+ continue;
+ }
+ if (!removed)
+ die("git rm: %s: %s", path, strerror(errno));
+ }
+ }
+
+ if (active_cache_changed) {
+ if (write_cache(newfd, active_cache, active_nr) ||
+ commit_index_file(&cache_file))
+ die("Unable to write new index file");
+ }
+
+ return 0;
+}
diff --git a/show-branch.c b/builtin-show-branch.c
index 268c57b180..2895140915 100644
--- a/show-branch.c
+++ b/builtin-show-branch.c
@@ -3,13 +3,14 @@
#include "cache.h"
#include "commit.h"
#include "refs.h"
+#include "builtin.h"
static const char show_branch_usage[] =
"git-show-branch [--dense] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...]";
static int default_num = 0;
static int default_alloc = 0;
-static char **default_arg = NULL;
+static const char **default_arg = NULL;
#define UNINTERESTING 01
@@ -259,7 +260,7 @@ static void show_one_commit(struct commit *commit, int no_name)
struct commit_name *name = commit->object.util;
if (commit->object.parsed)
pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
- pretty, sizeof(pretty), 0);
+ pretty, sizeof(pretty), 0, NULL, NULL);
else
strcpy(pretty, "(unavailable)");
if (!strncmp(pretty, "[PATCH] ", 8))
@@ -548,7 +549,7 @@ static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
return 0;
}
-int main(int ac, char **av)
+int cmd_show_branch(int ac, const char **av, char **envp)
{
struct commit *rev[MAX_REVS], *commit;
struct commit_list *list = NULL, *seen = NULL;
@@ -581,7 +582,7 @@ int main(int ac, char **av)
}
while (1 < ac && av[1][0] == '-') {
- char *arg = av[1];
+ const char *arg = av[1];
if (!strcmp(arg, "--")) {
ac--; av++;
break;
diff --git a/tar-tree.c b/builtin-tar-tree.c
index 33087366c3..2d5e06fb69 100644
--- a/tar-tree.c
+++ b/builtin-tar-tree.c
@@ -7,11 +7,14 @@
#include "commit.h"
#include "strbuf.h"
#include "tar.h"
+#include "builtin.h"
+#include "pkt-line.h"
#define RECORDSIZE (512)
#define BLOCKSIZE (RECORDSIZE * 20)
-static const char tar_tree_usage[] = "git-tar-tree <key> [basedir]";
+static const char tar_tree_usage[] =
+"git-tar-tree [--remote=<repo>] <ent> [basedir]";
static char block[BLOCKSIZE];
static unsigned long offset;
@@ -301,7 +304,7 @@ static void traverse_tree(struct tree_desc *tree, struct strbuf *path)
}
}
-int main(int argc, char **argv)
+static int generate_tar(int argc, const char **argv, char** envp)
{
unsigned char sha1[20], tree_sha1[20];
struct commit *commit;
@@ -348,3 +351,58 @@ int main(int argc, char **argv)
free(current_path.buf);
return 0;
}
+
+static const char *exec = "git-upload-tar";
+
+static int remote_tar(int argc, const char **argv)
+{
+ int fd[2], ret, len;
+ pid_t pid;
+ char buf[1024];
+ char *url;
+
+ if (argc < 3 || 4 < argc)
+ usage(tar_tree_usage);
+
+ /* --remote=<repo> */
+ url = strdup(argv[1]+9);
+ pid = git_connect(fd, url, exec);
+ if (pid < 0)
+ return 1;
+
+ packet_write(fd[1], "want %s\n", argv[2]);
+ if (argv[3])
+ packet_write(fd[1], "base %s\n", argv[3]);
+ packet_flush(fd[1]);
+
+ len = packet_read_line(fd[0], buf, sizeof(buf));
+ if (!len)
+ die("git-tar-tree: expected ACK/NAK, got EOF");
+ if (buf[len-1] == '\n')
+ buf[--len] = 0;
+ if (strcmp(buf, "ACK")) {
+ if (5 < len && !strncmp(buf, "NACK ", 5))
+ die("git-tar-tree: NACK %s", buf + 5);
+ die("git-tar-tree: protocol error");
+ }
+ /* expect a flush */
+ len = packet_read_line(fd[0], buf, sizeof(buf));
+ if (len)
+ die("git-tar-tree: expected a flush");
+
+ /* Now, start reading from fd[0] and spit it out to stdout */
+ ret = copy_fd(fd[0], 1);
+ close(fd[0]);
+
+ ret |= finish_connect(pid);
+ return !!ret;
+}
+
+int cmd_tar_tree(int argc, const char **argv, char **envp)
+{
+ if (argc < 2)
+ usage(tar_tree_usage);
+ if (!strncmp("--remote=", argv[1], 9))
+ return remote_tar(argc, argv);
+ return generate_tar(argc, argv, envp);
+}
diff --git a/builtin-upload-tar.c b/builtin-upload-tar.c
new file mode 100644
index 0000000000..d4fa7b56c3
--- /dev/null
+++ b/builtin-upload-tar.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2006 Junio C Hamano
+ */
+#include "cache.h"
+#include "pkt-line.h"
+#include "exec_cmd.h"
+#include "builtin.h"
+
+static const char upload_tar_usage[] = "git-upload-tar <repo>";
+
+static int nak(const char *reason)
+{
+ packet_write(1, "NACK %s\n", reason);
+ packet_flush(1);
+ return 1;
+}
+
+int cmd_upload_tar(int argc, const char **argv, char **envp)
+{
+ int len;
+ const char *dir = argv[1];
+ char buf[8192];
+ unsigned char sha1[20];
+ char *base = NULL;
+ char hex[41];
+ int ac;
+ const char *av[4];
+
+ if (argc != 2)
+ usage(upload_tar_usage);
+ if (strlen(dir) < sizeof(buf)-1)
+ strcpy(buf, dir); /* enter-repo smudges its argument */
+ else
+ packet_write(1, "NACK insanely long repository name %s\n", dir);
+ if (!enter_repo(buf, 0)) {
+ packet_write(1, "NACK not a git archive %s\n", dir);
+ packet_flush(1);
+ return 1;
+ }
+
+ len = packet_read_line(0, buf, sizeof(buf));
+ if (len < 5 || strncmp("want ", buf, 5))
+ return nak("expected want");
+ if (buf[len-1] == '\n')
+ buf[--len] = 0;
+ if (get_sha1(buf + 5, sha1))
+ return nak("expected sha1");
+ strcpy(hex, sha1_to_hex(sha1));
+
+ len = packet_read_line(0, buf, sizeof(buf));
+ if (len) {
+ if (len < 5 || strncmp("base ", buf, 5))
+ return nak("expected (optional) base");
+ if (buf[len-1] == '\n')
+ buf[--len] = 0;
+ base = strdup(buf + 5);
+ len = packet_read_line(0, buf, sizeof(buf));
+ }
+ if (len)
+ return nak("expected flush");
+
+ packet_write(1, "ACK\n");
+ packet_flush(1);
+
+ ac = 0;
+ av[ac++] = "tar-tree";
+ av[ac++] = hex;
+ if (base)
+ av[ac++] = base;
+ av[ac++] = NULL;
+ execv_git_cmd(av);
+ /* should it return that is an error */
+ return 1;
+}
diff --git a/builtin.h b/builtin.h
index 60541262c4..738ec3d945 100644
--- a/builtin.h
+++ b/builtin.h
@@ -20,12 +20,28 @@ extern int cmd_whatchanged(int argc, const char **argv, char **envp);
extern int cmd_show(int argc, const char **argv, char **envp);
extern int cmd_log(int argc, const char **argv, char **envp);
extern int cmd_diff(int argc, const char **argv, char **envp);
+extern int cmd_format_patch(int argc, const char **argv, char **envp);
extern int cmd_count_objects(int argc, const char **argv, char **envp);
extern int cmd_push(int argc, const char **argv, char **envp);
extern int cmd_grep(int argc, const char **argv, char **envp);
+extern int cmd_rm(int argc, const char **argv, char **envp);
+extern int cmd_add(int argc, const char **argv, char **envp);
extern int cmd_rev_list(int argc, const char **argv, char **envp);
extern int cmd_check_ref_format(int argc, const char **argv, char **envp);
extern int cmd_init_db(int argc, const char **argv, char **envp);
+extern int cmd_tar_tree(int argc, const char **argv, char **envp);
+extern int cmd_upload_tar(int argc, const char **argv, char **envp);
+extern int cmd_ls_files(int argc, const char **argv, char **envp);
+extern int cmd_ls_tree(int argc, const char **argv, char **envp);
+extern int cmd_read_tree(int argc, const char **argv, char **envp);
+extern int cmd_commit_tree(int argc, const char **argv, char **envp);
+extern int cmd_apply(int argc, const char **argv, char **envp);
+extern int cmd_show_branch(int argc, const char **argv, char **envp);
+extern int cmd_diff_files(int argc, const char **argv, char **envp);
+extern int cmd_diff_index(int argc, const char **argv, char **envp);
+extern int cmd_diff_stages(int argc, const char **argv, char **envp);
+extern int cmd_diff_tree(int argc, const char **argv, char **envp);
+extern int cmd_cat_file(int argc, const char **argv, char **envp);
#endif
diff --git a/cache.h b/cache.h
index afa8e4f0ac..3a46fb97bc 100644
--- a/cache.h
+++ b/cache.h
@@ -142,6 +142,7 @@ extern void verify_non_filename(const char *prefix, const char *name);
/* Initialize and use the cache information */
extern int read_cache(void);
extern int write_cache(int newfd, struct cache_entry **cache, int entries);
+extern int verify_path(const char *path);
extern int cache_name_pos(const char *name, int namelen);
#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */
#define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */
@@ -154,6 +155,7 @@ extern int ce_match_stat(struct cache_entry *ce, struct stat *st, int);
extern int ce_modified(struct cache_entry *ce, struct stat *st, int);
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 read_pipe(int fd, char** return_buf, unsigned long* return_size);
extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
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);
@@ -257,6 +259,7 @@ extern void *read_object_with_reference(const unsigned char *sha1,
unsigned char *sha1_ret);
const char *show_date(unsigned long time, int timezone);
+const char *show_rfc2822_date(unsigned long time, int timezone);
int parse_date(const char *date, char *buf, int bufsize);
void datestamp(char *buf, int bufsize);
unsigned long approxidate(const char *);
diff --git a/commit.c b/commit.c
index 4a26070c13..94f470b75c 100644
--- a/commit.c
+++ b/commit.c
@@ -30,6 +30,7 @@ struct cmt_fmt_map {
{ "raw", 1, CMIT_FMT_RAW },
{ "medium", 1, CMIT_FMT_MEDIUM },
{ "short", 1, CMIT_FMT_SHORT },
+ { "email", 1, CMIT_FMT_EMAIL },
{ "full", 5, CMIT_FMT_FULL },
{ "fuller", 5, CMIT_FMT_FULLER },
{ "oneline", 1, CMIT_FMT_ONELINE },
@@ -421,6 +422,46 @@ static int get_one_line(const char *msg, unsigned long len)
return ret;
}
+static int is_rfc2047_special(char ch)
+{
+ return ((ch & 0x80) || (ch == '=') || (ch == '?') || (ch == '_'));
+}
+
+static int add_rfc2047(char *buf, const char *line, int len)
+{
+ char *bp = buf;
+ int i, needquote;
+ static const char q_utf8[] = "=?utf-8?q?";
+
+ for (i = needquote = 0; !needquote && i < len; i++) {
+ unsigned ch = line[i];
+ if (ch & 0x80)
+ needquote++;
+ if ((i + 1 < len) &&
+ (ch == '=' && line[i+1] == '?'))
+ needquote++;
+ }
+ if (!needquote)
+ return sprintf(buf, "%.*s", len, line);
+
+ memcpy(bp, q_utf8, sizeof(q_utf8)-1);
+ bp += sizeof(q_utf8)-1;
+ for (i = 0; i < len; i++) {
+ unsigned ch = line[i];
+ if (is_rfc2047_special(ch)) {
+ sprintf(bp, "=%02X", ch);
+ bp += 3;
+ }
+ else if (ch == ' ')
+ *bp++ = '_';
+ else
+ *bp++ = ch;
+ }
+ memcpy(bp, "?=", 2);
+ bp += 2;
+ return bp - buf;
+}
+
static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const char *line)
{
char *date;
@@ -438,13 +479,35 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
time = strtoul(date, &date, 10);
tz = strtol(date, NULL, 10);
- ret = sprintf(buf, "%s: %.*s%.*s\n", what,
- (fmt == CMIT_FMT_FULLER) ? 4 : 0,
- filler, namelen, line);
+ if (fmt == CMIT_FMT_EMAIL) {
+ char *name_tail = strchr(line, '<');
+ int display_name_length;
+ if (!name_tail)
+ return 0;
+ while (line < name_tail && isspace(name_tail[-1]))
+ name_tail--;
+ display_name_length = name_tail - line;
+ filler = "";
+ strcpy(buf, "From: ");
+ ret = strlen(buf);
+ ret += add_rfc2047(buf + ret, line, display_name_length);
+ memcpy(buf + ret, name_tail, namelen - display_name_length);
+ ret += namelen - display_name_length;
+ buf[ret++] = '\n';
+ }
+ else {
+ ret = sprintf(buf, "%s: %.*s%.*s\n", what,
+ (fmt == CMIT_FMT_FULLER) ? 4 : 0,
+ filler, namelen, line);
+ }
switch (fmt) {
case CMIT_FMT_MEDIUM:
ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz));
break;
+ case CMIT_FMT_EMAIL:
+ ret += sprintf(buf + ret, "Date: %s\n",
+ show_rfc2822_date(time, tz));
+ break;
case CMIT_FMT_FULLER:
ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz));
break;
@@ -455,10 +518,12 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
return ret;
}
-static int is_empty_line(const char *line, int len)
+static int is_empty_line(const char *line, int *len_p)
{
+ int len = *len_p;
while (len && isspace(line[len-1]))
len--;
+ *len_p = len;
return !len;
}
@@ -467,7 +532,8 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com
struct commit_list *parent = commit->parents;
int offset;
- if ((fmt == CMIT_FMT_ONELINE) || !parent || !parent->next)
+ if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
+ !parent || !parent->next)
return 0;
offset = sprintf(buf, "Merge:");
@@ -486,13 +552,30 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com
return offset;
}
-unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev)
+unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject)
{
int hdr = 1, body = 0;
unsigned long offset = 0;
- int indent = (fmt == CMIT_FMT_ONELINE) ? 0 : 4;
+ int indent = 4;
int parents_shown = 0;
const char *msg = commit->buffer;
+ int plain_non_ascii = 0;
+
+ if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
+ indent = 0;
+
+ /* After-subject is used to pass in Content-Type: multipart
+ * MIME header; in that case we do not have to do the
+ * plaintext content type even if the commit message has
+ * non 7-bit ASCII character. Otherwise, check if we need
+ * to say this is not a 7-bit ASCII.
+ */
+ if (fmt == CMIT_FMT_EMAIL && !after_subject) {
+ int i;
+ for (i = 0; !plain_non_ascii && msg[i] && i < len; i++)
+ if (msg[i] & 0x80)
+ plain_non_ascii = 1;
+ }
for (;;) {
const char *line = msg;
@@ -516,7 +599,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit
if (hdr) {
if (linelen == 1) {
hdr = 0;
- if (fmt != CMIT_FMT_ONELINE)
+ if ((fmt != CMIT_FMT_ONELINE) && !subject)
buf[offset++] = '\n';
continue;
}
@@ -554,20 +637,47 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit
continue;
}
- if (is_empty_line(line, linelen)) {
+ if (is_empty_line(line, &linelen)) {
if (!body)
continue;
+ if (subject)
+ continue;
if (fmt == CMIT_FMT_SHORT)
break;
} else {
body = 1;
}
- memset(buf + offset, ' ', indent);
- memcpy(buf + offset + indent, line, linelen);
- offset += linelen + indent;
+ if (subject) {
+ int slen = strlen(subject);
+ memcpy(buf + offset, subject, slen);
+ offset += slen;
+ offset += add_rfc2047(buf + offset, line, linelen);
+ }
+ else {
+ memset(buf + offset, ' ', indent);
+ memcpy(buf + offset + indent, line, linelen);
+ offset += linelen + indent;
+ }
+ buf[offset++] = '\n';
if (fmt == CMIT_FMT_ONELINE)
break;
+ if (subject && plain_non_ascii) {
+ static const char header[] =
+ "Content-Type: text/plain; charset=UTF-8\n"
+ "Content-Transfer-Encoding: 8bit\n";
+ memcpy(buf + offset, header, sizeof(header)-1);
+ offset += sizeof(header)-1;
+ }
+ if (after_subject) {
+ int slen = strlen(after_subject);
+ if (slen > space - offset - 1)
+ slen = space - offset - 1;
+ memcpy(buf + offset, after_subject, slen);
+ offset += slen;
+ after_subject = NULL;
+ }
+ subject = NULL;
}
while (offset && isspace(buf[offset-1]))
offset--;
diff --git a/commit.h b/commit.h
index de142afe73..c9de1677e9 100644
--- a/commit.h
+++ b/commit.h
@@ -45,12 +45,13 @@ enum cmit_fmt {
CMIT_FMT_FULL,
CMIT_FMT_FULLER,
CMIT_FMT_ONELINE,
+ CMIT_FMT_EMAIL,
CMIT_FMT_UNSPECIFIED,
};
extern enum cmit_fmt get_commit_format(const char *arg);
-extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev);
+extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject);
/** Removes the first commit from a list sorted by date, and adds all
* of its parents.
diff --git a/connect.c b/connect.c
index 6a8f8a6a24..54f7bf7915 100644
--- a/connect.c
+++ b/connect.c
@@ -100,7 +100,7 @@ int path_match(const char *path, int nr, char **match)
if (pathlen > len && path[pathlen - len - 1] != '/')
continue;
*s = 0;
- return 1;
+ return (i + 1);
}
return 0;
}
diff --git a/contrib/git-svn/Makefile b/contrib/git-svn/Makefile
index acedf7305e..48f60b3a0d 100644
--- a/contrib/git-svn/Makefile
+++ b/contrib/git-svn/Makefile
@@ -30,6 +30,7 @@ git-svn.html : git-svn.txt
-f ../../Documentation/asciidoc.conf $<
test: git-svn
cd t && $(SHELL) ./t0000-contrib-git-svn.sh
+ cd t && $(SHELL) ./t0001-contrib-git-svn-props.sh
clean:
rm -f git-svn *.xml *.html *.1
diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl
index de13a96b8a..b3e0684c44 100755
--- a/contrib/git-svn/git-svn.perl
+++ b/contrib/git-svn/git-svn.perl
@@ -8,7 +8,7 @@ use vars qw/ $AUTHOR $VERSION
$GIT_SVN_INDEX $GIT_SVN
$GIT_DIR $REV_DIR/;
$AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
-$VERSION = '1.0.0';
+$VERSION = '1.1.0-pre';
use Cwd qw/abs_path/;
$GIT_DIR = abs_path($ENV{GIT_DIR} || '.git');
@@ -39,6 +39,10 @@ my $_svn_co_url_revs;
my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
'branch|b=s' => \@_branch_from,
'authors-file|A=s' => \$_authors );
+
+# yes, 'native' sets "\n". Patches to fix this for non-*nix systems welcome:
+my %EOL = ( CR => "\015", LF => "\012", CRLF => "\015\012", native => "\012" );
+
my %cmd = (
fetch => [ \&fetch, "Download new revisions from SVN",
{ 'revision|r=s' => \$_revision, %fc_opts } ],
@@ -207,7 +211,7 @@ sub rebuild {
push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
sys(@svn_up,"-r$newest_rev");
$ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX;
- git_addremove();
+ index_changes();
exec('git-write-tree');
}
waitpid $pid, 0;
@@ -249,7 +253,7 @@ sub fetch {
chdir $SVN_WC or croak $!;
read_uuid();
$last_commit = git_commit($base, @parents);
- assert_svn_wc_clean($base->{revision}, $last_commit);
+ assert_tree($last_commit);
} else {
chdir $SVN_WC or croak $!;
read_uuid();
@@ -259,16 +263,20 @@ sub fetch {
push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
my $last = $base;
while (my $log_msg = next_log_entry($svn_log)) {
- assert_svn_wc_clean($last->{revision}, $last_commit);
+ assert_tree($last_commit);
if ($last->{revision} >= $log_msg->{revision}) {
croak "Out of order: last >= current: ",
"$last->{revision} >= $log_msg->{revision}\n";
}
+ # Revert is needed for cases like:
+ # https://svn.musicpd.org/Jamming/trunk (r166:167), but
+ # I can't seem to reproduce something like that on a test...
+ sys(qw/svn revert -R ./);
+ assert_svn_wc_clean($last->{revision});
sys(@svn_up,"-r$log_msg->{revision}");
$last_commit = git_commit($log_msg, $last_commit, @parents);
$last = $log_msg;
}
- assert_svn_wc_clean($last->{revision}, $last_commit);
unless (-e "$GIT_DIR/refs/heads/master") {
sys(qw(git-update-ref refs/heads/master),$last_commit);
}
@@ -314,7 +322,6 @@ sub commit {
$svn_current_rev = svn_commit_tree($svn_current_rev, $c);
}
print "Done committing ",scalar @revs," revisions to SVN\n";
-
}
sub show_ignore {
@@ -367,13 +374,11 @@ sub setup_git_svn {
}
sub assert_svn_wc_clean {
- my ($svn_rev, $treeish) = @_;
+ my ($svn_rev) = @_;
croak "$svn_rev is not an integer!\n" unless ($svn_rev =~ /^\d+$/);
- croak "$treeish is not a sha1!\n" unless ($treeish =~ /^$sha1$/o);
my $lcr = svn_info('.')->{'Last Changed Rev'};
if ($svn_rev != $lcr) {
print STDERR "Checking for copy-tree ... ";
- # use
my @diff = grep(/^Index: /,(safe_qx(qw(svn diff),
"-r$lcr:$svn_rev")));
if (@diff) {
@@ -389,7 +394,6 @@ sub assert_svn_wc_clean {
print STDERR $_ foreach @status;
croak;
}
- assert_tree($treeish);
}
sub assert_tree {
@@ -416,7 +420,7 @@ sub assert_tree {
unlink $tmpindex or croak $!;
}
$ENV{GIT_INDEX_FILE} = $tmpindex;
- git_addremove();
+ index_changes(1);
chomp(my $tree = `git-write-tree`);
if ($old_index) {
$ENV{GIT_INDEX_FILE} = $old_index;
@@ -426,6 +430,7 @@ sub assert_tree {
if ($tree ne $expected) {
croak "Tree mismatch, Got: $tree, Expected: $expected\n";
}
+ unlink $tmpindex;
}
sub parse_diff_tree {
@@ -562,7 +567,8 @@ sub precommit_check {
sub svn_checkout_tree {
my ($svn_rev, $treeish) = @_;
my $from = file_to_s("$REV_DIR/$svn_rev");
- assert_svn_wc_clean($svn_rev,$from);
+ assert_svn_wc_clean($svn_rev);
+ assert_tree($from);
print "diff-tree $from $treeish\n";
my $pid = open my $diff_fh, '-|';
defined $pid or croak $!;
@@ -852,13 +858,75 @@ sub svn_info {
sub sys { system(@_) == 0 or croak $? }
-sub git_addremove {
- system( "git-diff-files --name-only -z ".
- " | git-update-index --remove -z --stdin && ".
- "git-ls-files -z --others ".
- "'--exclude-from=$GIT_DIR/$GIT_SVN/info/exclude'".
- " | git-update-index --add -z --stdin"
- ) == 0 or croak $?
+sub eol_cp {
+ my ($from, $to) = @_;
+ my $es = safe_qx(qw/svn propget svn:eol-style/, $to);
+ open my $rfd, '<', $from or croak $!;
+ binmode $rfd or croak $!;
+ open my $wfd, '>', $to or croak $!;
+ binmode $wfd or croak $!;
+
+ my $eol = $EOL{$es} or undef;
+ if ($eol) {
+ print "$eol: $from => $to\n";
+ }
+ my $buf;
+ while (1) {
+ my ($r, $w, $t);
+ defined($r = sysread($rfd, $buf, 4096)) or croak $!;
+ return unless $r;
+ $buf =~ s/(?:\015|\012|\015\012)/$eol/gs if $eol;
+ for ($w = 0; $w < $r; $w += $t) {
+ $t = syswrite($wfd, $buf, $r - $w, $w) or croak $!;
+ }
+ }
+}
+
+sub do_update_index {
+ my ($z_cmd, $cmd, $no_text_base) = @_;
+
+ my $z = open my $p, '-|';
+ defined $z or croak $!;
+ unless ($z) { exec @$z_cmd or croak $! }
+
+ my $pid = open my $ui, '|-';
+ defined $pid or croak $!;
+ unless ($pid) {
+ exec('git-update-index',"--$cmd",'-z','--stdin') or croak $!;
+ }
+ local $/ = "\0";
+ while (my $x = <$p>) {
+ chomp $x;
+ if (!$no_text_base && lstat $x && ! -l _ &&
+ safe_qx(qw/svn propget svn:keywords/,$x)) {
+ my $mode = -x _ ? 0755 : 0644;
+ my ($v,$d,$f) = File::Spec->splitpath($x);
+ my $tb = File::Spec->catfile($d, '.svn', 'tmp',
+ 'text-base',"$f.svn-base");
+ $tb =~ s#^/##;
+ unless (-f $tb) {
+ $tb = File::Spec->catfile($d, '.svn',
+ 'text-base',"$f.svn-base");
+ $tb =~ s#^/##;
+ }
+ unlink $x or croak $!;
+ eol_cp($tb, $x);
+ chmod(($mode &~ umask), $x) or croak $!;
+ }
+ print $ui $x,"\0";
+ }
+ close $ui or croak $!;
+}
+
+sub index_changes {
+ my $no_text_base = shift;
+ do_update_index([qw/git-diff-files --name-only -z/],
+ 'remove',
+ $no_text_base);
+ do_update_index([qw/git-ls-files -z --others/,
+ "--exclude-from=$GIT_DIR/$GIT_SVN/info/exclude"],
+ 'add',
+ $no_text_base);
}
sub s_to_file {
@@ -936,7 +1004,7 @@ sub git_commit {
defined $pid or croak $!;
if ($pid == 0) {
$ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX;
- git_addremove();
+ index_changes();
chomp(my $tree = `git-write-tree`);
croak if $?;
if (exists $tree_map{$tree}) {
diff --git a/contrib/git-svn/t/lib-git-svn.sh b/contrib/git-svn/t/lib-git-svn.sh
new file mode 100644
index 0000000000..a98e9d164d
--- /dev/null
+++ b/contrib/git-svn/t/lib-git-svn.sh
@@ -0,0 +1,39 @@
+PATH=$PWD/../:$PATH
+if test -d ../../../t
+then
+ cd ../../../t
+else
+ echo "Must be run in contrib/git-svn/t" >&2
+ exit 1
+fi
+
+. ./test-lib.sh
+
+GIT_DIR=$PWD/.git
+GIT_SVN_DIR=$GIT_DIR/git-svn
+SVN_TREE=$GIT_SVN_DIR/tree
+
+svnadmin >/dev/null 2>&1
+if test $? != 1
+then
+ test_expect_success 'skipping contrib/git-svn test' :
+ test_done
+ exit
+fi
+
+svn >/dev/null 2>&1
+if test $? != 1
+then
+ test_expect_success 'skipping contrib/git-svn test' :
+ test_done
+ exit
+fi
+
+svnrepo=$PWD/svnrepo
+
+set -e
+
+svnadmin create $svnrepo
+svnrepo="file://$svnrepo/test-git-svn"
+
+
diff --git a/contrib/git-svn/t/t0000-contrib-git-svn.sh b/contrib/git-svn/t/t0000-contrib-git-svn.sh
index 80ad3573db..8b3a0d9029 100644
--- a/contrib/git-svn/t/t0000-contrib-git-svn.sh
+++ b/contrib/git-svn/t/t0000-contrib-git-svn.sh
@@ -3,48 +3,10 @@
# Copyright (c) 2006 Eric Wong
#
-
-PATH=$PWD/../:$PATH
test_description='git-svn tests'
-if test -d ../../../t
-then
- cd ../../../t
-else
- echo "Must be run in contrib/git-svn/t" >&2
- exit 1
-fi
-
-. ./test-lib.sh
-
-GIT_DIR=$PWD/.git
-GIT_SVN_DIR=$GIT_DIR/git-svn
-SVN_TREE=$GIT_SVN_DIR/tree
-
-svnadmin >/dev/null 2>&1
-if test $? != 1
-then
- test_expect_success 'skipping contrib/git-svn test' :
- test_done
- exit
-fi
-
-svn >/dev/null 2>&1
-if test $? != 1
-then
- test_expect_success 'skipping contrib/git-svn test' :
- test_done
- exit
-fi
-
-svnrepo=$PWD/svnrepo
-
-set -e
-
-svnadmin create $svnrepo
-svnrepo="file://$svnrepo/test-git-svn"
+. ./lib-git-svn.sh
mkdir import
-
cd import
echo foo > foo
@@ -55,10 +17,9 @@ mkdir -p bar
echo 'zzz' > bar/zzz
echo '#!/bin/sh' > exec.sh
chmod +x exec.sh
-svn import -m 'import for git-svn' . $svnrepo >/dev/null
+svn import -m 'import for git-svn' . "$svnrepo" >/dev/null
cd ..
-
rm -rf import
test_expect_success \
diff --git a/contrib/git-svn/t/t0001-contrib-git-svn-props.sh b/contrib/git-svn/t/t0001-contrib-git-svn-props.sh
new file mode 100644
index 0000000000..6fa7889e9a
--- /dev/null
+++ b/contrib/git-svn/t/t0001-contrib-git-svn-props.sh
@@ -0,0 +1,125 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Eric Wong
+#
+
+test_description='git-svn property tests'
+. ./lib-git-svn.sh
+
+mkdir import
+
+a_crlf=
+a_lf=
+a_cr=
+a_ne_crlf=
+a_ne_lf=
+a_ne_cr=
+a_empty=
+a_empty_lf=
+a_empty_cr=
+a_empty_crlf=
+
+cd import
+ cat >> kw.c <<''
+/* Make it look like somebody copied a file from CVS into SVN: */
+/* $Id: kw.c,v 1.1.1.1 1994/03/06 00:00:00 eric Exp $ */
+
+ printf "Hello\r\nWorld\r\n" > crlf
+ a_crlf=`git-hash-object -w crlf`
+ printf "Hello\rWorld\r" > cr
+ a_cr=`git-hash-object -w cr`
+ printf "Hello\nWorld\n" > lf
+ a_lf=`git-hash-object -w lf`
+
+ printf "Hello\r\nWorld" > ne_crlf
+ a_ne_crlf=`git-hash-object -w ne_crlf`
+ printf "Hello\nWorld" > ne_lf
+ a_ne_lf=`git-hash-object -w ne_lf`
+ printf "Hello\rWorld" > ne_cr
+ a_ne_cr=`git-hash-object -w ne_cr`
+
+ touch empty
+ a_empty=`git-hash-object -w empty`
+ printf "\n" > empty_lf
+ a_empty_lf=`git-hash-object -w empty_lf`
+ printf "\r" > empty_cr
+ a_empty_cr=`git-hash-object -w empty_cr`
+ printf "\r\n" > empty_crlf
+ a_empty_crlf=`git-hash-object -w empty_crlf`
+
+ svn import -m 'import for git-svn' . "$svnrepo" >/dev/null
+cd ..
+
+rm -rf import
+svn co "$svnrepo" test_wc
+
+cd test_wc
+ echo 'Greetings' >> kw.c
+ svn commit -m 'Not yet an $Id$'
+ svn up
+
+ echo 'Hello world' >> kw.c
+ svn commit -m 'Modified file, but still not yet an $Id$'
+ svn up
+
+ svn propset svn:keywords Id kw.c
+ svn commit -m 'Propset $Id$'
+ svn up
+cd ..
+
+git-svn init "$svnrepo"
+git-svn fetch
+
+git checkout -b mybranch remotes/git-svn
+echo 'Hi again' >> kw.c
+name='test svn:keywords ignoring'
+
+git commit -a -m "$name"
+git-svn commit remotes/git-svn..mybranch
+git pull . remotes/git-svn
+
+expect='/* $Id$ */'
+got="`sed -ne 2p kw.c`"
+test_expect_success 'raw $Id$ found in kw.c' "test '$expect' = '$got'"
+
+cd test_wc
+ svn propset svn:eol-style CR empty
+ svn propset svn:eol-style CR crlf
+ svn propset svn:eol-style CR ne_crlf
+ svn commit -m 'propset CR on crlf files'
+ svn up
+cd ..
+
+git-svn fetch
+git pull . remotes/git-svn
+
+svn co "$svnrepo" new_wc
+for i in crlf ne_crlf lf ne_lf cr ne_cr empty_cr empty_lf empty empty_crlf
+do
+ test_expect_success "Comparing $i" "cmp $i new_wc/$i"
+done
+
+
+cd test_wc
+ printf '$Id$\rHello\rWorld\r' > cr
+ printf '$Id$\rHello\rWorld' > ne_cr
+ a_cr=`printf '$Id$\r\nHello\r\nWorld\r\n' | git-hash-object --stdin`
+ a_ne_cr=`printf '$Id$\r\nHello\r\nWorld' | git-hash-object --stdin`
+ svn propset svn:eol-style CRLF cr
+ svn propset svn:eol-style CRLF ne_cr
+ svn propset svn:keywords Id cr
+ svn propset svn:keywords Id ne_cr
+ svn commit -m 'propset CRLF on cr files'
+ svn up
+cd ..
+
+git-svn fetch
+git pull . remotes/git-svn
+
+b_cr="`git-hash-object cr`"
+b_ne_cr="`git-hash-object ne_cr`"
+
+test_expect_success 'CRLF + $Id$' "test '$a_cr' = '$b_cr'"
+test_expect_success 'CRLF + $Id$ (no newline)' "test '$a_ne_cr' = '$b_ne_cr'"
+
+test_done
diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview
index 781badbc5b..b836047cf3 100755
--- a/contrib/gitview/gitview
+++ b/contrib/gitview/gitview
@@ -425,7 +425,7 @@ class DiffWindow:
class GitView:
""" This is the main class
"""
- version = "0.7"
+ version = "0.8"
def __init__(self, with_diff=0):
self.with_diff = with_diff
@@ -449,8 +449,17 @@ class GitView:
self.accel_group = gtk.AccelGroup()
self.window.add_accel_group(self.accel_group)
+ self.accel_group.connect_group(0xffc2, 0, gtk.ACCEL_LOCKED, self.refresh);
- self.construct()
+ self.window.add(self.construct())
+
+ def refresh(self, widget, event=None, *arguments, **keywords):
+ self.get_encoding()
+ self.get_bt_sha1()
+ Commit.children_sha1 = {}
+ self.set_branch(sys.argv[without_diff:])
+ self.window.show()
+ return True
def get_bt_sha1(self):
""" Update the bt_sha1 dictionary with the
@@ -500,9 +509,9 @@ class GitView:
menu_bar.show()
vbox.pack_start(menu_bar, expand=False, fill=True)
vbox.pack_start(paned, expand=True, fill=True)
- self.window.add(vbox)
paned.show()
vbox.show()
+ return vbox
def construct_top(self):
@@ -974,10 +983,15 @@ class GitView:
try:
self.treeview.set_cursor(self.index[revid])
except KeyError:
- print "Revision %s not present in the list" % revid
+ dialog = gtk.MessageDialog(parent=None, flags=0,
+ type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE,
+ message_format=None)
+ dialog.set_markup("Revision <b>%s</b> not present in the list" % revid)
# revid == 0 is the parent of the first commit
if (revid != 0 ):
- print "Try running gitview without any options"
+ dialog.format_secondary_text("Try running gitview without any options")
+ dialog.run()
+ dialog.destroy()
self.treeview.grab_focus()
@@ -987,8 +1001,8 @@ class GitView:
window.set_diff(commit_sha1, parent_sha1, encoding)
self.treeview.grab_focus()
+without_diff = 0
if __name__ == "__main__":
- without_diff = 0
if (len(sys.argv) > 1 ):
if (sys.argv[1] == "--without-diff"):
diff --git a/contrib/gitview/gitview.txt b/contrib/gitview/gitview.txt
index fcf759c307..e3bc4f46c2 100644
--- a/contrib/gitview/gitview.txt
+++ b/contrib/gitview/gitview.txt
@@ -25,6 +25,9 @@ OPTIONS
<args>
All the valid option for git-rev-list(1)
+ Key Bindings:
+ F5:
+ To reread references.
EXAMPLES
------
@@ -33,6 +36,5 @@ EXAMPLES
or drivers/scsi subdirectories
gitview --since=2.weeks.ago
- Show the changes during the last two weeks
+ Show the changes during the last two weeks
-
diff --git a/date.c b/date.c
index 034d7228bf..365dc3b14e 100644
--- a/date.c
+++ b/date.c
@@ -42,18 +42,24 @@ static const char *weekday_names[] = {
* thing, which means that tz -0100 is passed in as the integer -100,
* even though it means "sixty minutes off"
*/
-const char *show_date(unsigned long time, int tz)
+static struct tm *time_to_tm(unsigned long time, int tz)
{
- struct tm *tm;
time_t t;
- static char timebuf[200];
int minutes;
minutes = tz < 0 ? -tz : tz;
minutes = (minutes / 100)*60 + (minutes % 100);
minutes = tz < 0 ? -minutes : minutes;
t = time + minutes * 60;
- tm = gmtime(&t);
+ return gmtime(&t);
+}
+
+const char *show_date(unsigned long time, int tz)
+{
+ struct tm *tm;
+ static char timebuf[200];
+
+ tm = time_to_tm(time, tz);
if (!tm)
return NULL;
sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d %+05d",
@@ -65,6 +71,21 @@ const char *show_date(unsigned long time, int tz)
return timebuf;
}
+const char *show_rfc2822_date(unsigned long time, int tz)
+{
+ struct tm *tm;
+ static char timebuf[200];
+
+ tm = time_to_tm(time, tz);
+ if (!tm)
+ return NULL;
+ sprintf(timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
+ weekday_names[tm->tm_wday], tm->tm_mday,
+ month_names[tm->tm_mon], tm->tm_year + 1900,
+ tm->tm_hour, tm->tm_min, tm->tm_sec, tz);
+ return timebuf;
+}
+
/*
* Check these. And note how it doesn't do the summer-time conversion.
*
diff --git a/diff.c b/diff.c
index d3bb10e009..9e9cfc8b75 100644
--- a/diff.c
+++ b/diff.c
@@ -237,7 +237,7 @@ static char *pprint_rename(const char *a, const char *b)
if (a_midlen < 0) a_midlen = 0;
if (b_midlen < 0) b_midlen = 0;
- name = xmalloc(len_a + len_b - pfx_length - sfx_length + 7);
+ name = xmalloc(pfx_length + a_midlen + b_midlen + sfx_length + 7);
sprintf(name, "%.*s{%.*s => %.*s}%s",
pfx_length, a,
a_midlen, a + pfx_length,
@@ -299,6 +299,7 @@ static void diffstat_consume(void *priv, char *line, unsigned long len)
static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
static const char minuses[]= "----------------------------------------------------------------------";
+const char mime_boundary_leader[] = "------------";
static void show_stats(struct diffstat_t* data)
{
@@ -1303,6 +1304,14 @@ int diff_setup_done(struct diff_options *options)
(options->output_format == DIFF_FORMAT_CHECKDIFF))
options->recursive = 1;
+ /*
+ * These combinations do not make sense.
+ */
+ if (options->output_format == DIFF_FORMAT_RAW)
+ options->with_raw = 0;
+ if (options->output_format == DIFF_FORMAT_DIFFSTAT)
+ options->with_stat = 0;
+
if (options->detect_rename && options->rename_limit < 0)
options->rename_limit = diff_rename_limit_default;
if (options->setup & DIFF_SETUP_USE_CACHE) {
@@ -1980,7 +1989,13 @@ void diff_flush(struct diff_options *options)
show_stats(diffstat);
free(diffstat);
diffstat = NULL;
- putchar(options->line_termination);
+ if (options->summary)
+ for (i = 0; i < q->nr; i++)
+ diff_summary(q->queue[i]);
+ if (options->stat_sep)
+ fputs(options->stat_sep, stdout);
+ else
+ putchar(options->line_termination);
}
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
@@ -1993,7 +2008,7 @@ void diff_flush(struct diff_options *options)
}
for (i = 0; i < q->nr; i++) {
- if (options->summary)
+ if (diffstat && options->summary)
diff_summary(q->queue[i]);
diff_free_filepair(q->queue[i]);
}
diff --git a/diff.h b/diff.h
index c672277df2..4fc597c594 100644
--- a/diff.h
+++ b/diff.h
@@ -44,6 +44,7 @@ struct diff_options {
int rename_limit;
int setup;
int abbrev;
+ const char *stat_sep;
int nr_paths;
const char **paths;
@@ -52,6 +53,8 @@ struct diff_options {
add_remove_fn_t add_remove;
};
+extern const char mime_boundary_leader[];
+
extern void diff_tree_setup_paths(const char **paths, struct diff_options *);
extern void diff_tree_release_paths(struct diff_options *);
extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
diff --git a/dir.c b/dir.c
new file mode 100644
index 0000000000..d778ecd890
--- /dev/null
+++ b/dir.c
@@ -0,0 +1,401 @@
+/*
+ * This handles recursive filename detection with exclude
+ * files, index knowledge etc..
+ *
+ * Copyright (C) Linus Torvalds, 2005-2006
+ * Junio Hamano, 2005-2006
+ */
+#include <dirent.h>
+#include <fnmatch.h>
+
+#include "cache.h"
+#include "dir.h"
+
+int common_prefix(const char **pathspec)
+{
+ const char *path, *slash, *next;
+ int prefix;
+
+ if (!pathspec)
+ return 0;
+
+ path = *pathspec;
+ slash = strrchr(path, '/');
+ if (!slash)
+ return 0;
+
+ prefix = slash - path + 1;
+ while ((next = *++pathspec) != NULL) {
+ int len = strlen(next);
+ if (len >= prefix && !memcmp(path, next, len))
+ continue;
+ for (;;) {
+ if (!len)
+ return 0;
+ if (next[--len] != '/')
+ continue;
+ if (memcmp(path, next, len+1))
+ continue;
+ prefix = len + 1;
+ break;
+ }
+ }
+ return prefix;
+}
+
+static int match_one(const char *match, const char *name, int namelen)
+{
+ int matchlen;
+
+ /* If the match was just the prefix, we matched */
+ matchlen = strlen(match);
+ if (!matchlen)
+ return 1;
+
+ /*
+ * If we don't match the matchstring exactly,
+ * we need to match by fnmatch
+ */
+ if (strncmp(match, name, matchlen))
+ return !fnmatch(match, name, 0);
+
+ /*
+ * If we did match the string exactly, we still
+ * need to make sure that it happened on a path
+ * component boundary (ie either the last character
+ * of the match was '/', or the next character of
+ * the name was '/' or the terminating NUL.
+ */
+ return match[matchlen-1] == '/' ||
+ name[matchlen] == '/' ||
+ !name[matchlen];
+}
+
+int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen)
+{
+ int retval;
+ const char *match;
+
+ name += prefix;
+ namelen -= prefix;
+
+ for (retval = 0; (match = *pathspec++) != NULL; seen++) {
+ if (retval & *seen)
+ continue;
+ match += prefix;
+ if (match_one(match, name, namelen)) {
+ retval = 1;
+ *seen = 1;
+ }
+ }
+ return retval;
+}
+
+void add_exclude(const char *string, const char *base,
+ int baselen, struct exclude_list *which)
+{
+ struct exclude *x = xmalloc(sizeof (*x));
+
+ x->pattern = string;
+ x->base = base;
+ x->baselen = baselen;
+ if (which->nr == which->alloc) {
+ which->alloc = alloc_nr(which->alloc);
+ which->excludes = realloc(which->excludes,
+ which->alloc * sizeof(x));
+ }
+ which->excludes[which->nr++] = x;
+}
+
+static int add_excludes_from_file_1(const char *fname,
+ const char *base,
+ int baselen,
+ struct exclude_list *which)
+{
+ int fd, i;
+ long size;
+ char *buf, *entry;
+
+ fd = open(fname, O_RDONLY);
+ if (fd < 0)
+ goto err;
+ size = lseek(fd, 0, SEEK_END);
+ if (size < 0)
+ goto err;
+ lseek(fd, 0, SEEK_SET);
+ if (size == 0) {
+ close(fd);
+ return 0;
+ }
+ buf = xmalloc(size+1);
+ if (read(fd, buf, size) != size)
+ goto err;
+ close(fd);
+
+ buf[size++] = '\n';
+ entry = buf;
+ for (i = 0; i < size; i++) {
+ if (buf[i] == '\n') {
+ if (entry != buf + i && entry[0] != '#') {
+ buf[i - (i && buf[i-1] == '\r')] = 0;
+ add_exclude(entry, base, baselen, which);
+ }
+ entry = buf + i + 1;
+ }
+ }
+ return 0;
+
+ err:
+ if (0 <= fd)
+ close(fd);
+ return -1;
+}
+
+void add_excludes_from_file(struct dir_struct *dir, const char *fname)
+{
+ if (add_excludes_from_file_1(fname, "", 0,
+ &dir->exclude_list[EXC_FILE]) < 0)
+ die("cannot use %s as an exclude file", fname);
+}
+
+static int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen)
+{
+ char exclude_file[PATH_MAX];
+ struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
+ int current_nr = el->nr;
+
+ if (dir->exclude_per_dir) {
+ memcpy(exclude_file, base, baselen);
+ strcpy(exclude_file + baselen, dir->exclude_per_dir);
+ add_excludes_from_file_1(exclude_file, base, baselen, el);
+ }
+ return current_nr;
+}
+
+static void pop_exclude_per_directory(struct dir_struct *dir, int stk)
+{
+ struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
+
+ while (stk < el->nr)
+ free(el->excludes[--el->nr]);
+}
+
+/* Scan the list and let the last match determines the fate.
+ * Return 1 for exclude, 0 for include and -1 for undecided.
+ */
+static int excluded_1(const char *pathname,
+ int pathlen,
+ struct exclude_list *el)
+{
+ int i;
+
+ if (el->nr) {
+ for (i = el->nr - 1; 0 <= i; i--) {
+ struct exclude *x = el->excludes[i];
+ const char *exclude = x->pattern;
+ int to_exclude = 1;
+
+ if (*exclude == '!') {
+ to_exclude = 0;
+ exclude++;
+ }
+
+ if (!strchr(exclude, '/')) {
+ /* match basename */
+ const char *basename = strrchr(pathname, '/');
+ basename = (basename) ? basename+1 : pathname;
+ if (fnmatch(exclude, basename, 0) == 0)
+ return to_exclude;
+ }
+ else {
+ /* match with FNM_PATHNAME:
+ * exclude has base (baselen long) implicitly
+ * in front of it.
+ */
+ int baselen = x->baselen;
+ if (*exclude == '/')
+ exclude++;
+
+ if (pathlen < baselen ||
+ (baselen && pathname[baselen-1] != '/') ||
+ strncmp(pathname, x->base, baselen))
+ continue;
+
+ if (fnmatch(exclude, pathname+baselen,
+ FNM_PATHNAME) == 0)
+ return to_exclude;
+ }
+ }
+ }
+ return -1; /* undecided */
+}
+
+int excluded(struct dir_struct *dir, const char *pathname)
+{
+ int pathlen = strlen(pathname);
+ int st;
+
+ for (st = EXC_CMDL; st <= EXC_FILE; st++) {
+ switch (excluded_1(pathname, pathlen, &dir->exclude_list[st])) {
+ case 0:
+ return 0;
+ case 1:
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void add_name(struct dir_struct *dir, const char *pathname, int len)
+{
+ struct dir_entry *ent;
+
+ if (cache_name_pos(pathname, len) >= 0)
+ return;
+
+ if (dir->nr == dir->alloc) {
+ int alloc = alloc_nr(dir->alloc);
+ dir->alloc = alloc;
+ dir->entries = xrealloc(dir->entries, alloc*sizeof(ent));
+ }
+ ent = xmalloc(sizeof(*ent) + len + 1);
+ ent->len = len;
+ memcpy(ent->name, pathname, len);
+ ent->name[len] = 0;
+ dir->entries[dir->nr++] = ent;
+}
+
+static int dir_exists(const char *dirname, int len)
+{
+ int pos = cache_name_pos(dirname, len);
+ if (pos >= 0)
+ return 1;
+ pos = -pos-1;
+ if (pos >= active_nr) /* can't */
+ return 0;
+ return !strncmp(active_cache[pos]->name, dirname, len);
+}
+
+/*
+ * Read a directory tree. We currently ignore anything but
+ * directories, regular files and symlinks. That's because git
+ * doesn't handle them at all yet. Maybe that will change some
+ * day.
+ *
+ * Also, we ignore the name ".git" (even if it is not a directory).
+ * That likely will not change.
+ */
+static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen)
+{
+ DIR *fdir = opendir(path);
+ int contents = 0;
+
+ if (fdir) {
+ int exclude_stk;
+ struct dirent *de;
+ char fullname[MAXPATHLEN + 1];
+ memcpy(fullname, base, baselen);
+
+ exclude_stk = push_exclude_per_directory(dir, base, baselen);
+
+ while ((de = readdir(fdir)) != NULL) {
+ int len;
+
+ if ((de->d_name[0] == '.') &&
+ (de->d_name[1] == 0 ||
+ !strcmp(de->d_name + 1, ".") ||
+ !strcmp(de->d_name + 1, "git")))
+ continue;
+ len = strlen(de->d_name);
+ memcpy(fullname + baselen, de->d_name, len+1);
+ if (excluded(dir, fullname) != dir->show_ignored) {
+ if (!dir->show_ignored || DTYPE(de) != DT_DIR) {
+ continue;
+ }
+ }
+
+ switch (DTYPE(de)) {
+ struct stat st;
+ int subdir, rewind_base;
+ default:
+ continue;
+ case DT_UNKNOWN:
+ if (lstat(fullname, &st))
+ continue;
+ if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
+ break;
+ if (!S_ISDIR(st.st_mode))
+ continue;
+ /* fallthrough */
+ case DT_DIR:
+ memcpy(fullname + baselen + len, "/", 2);
+ len++;
+ rewind_base = dir->nr;
+ subdir = read_directory_recursive(dir, fullname, fullname,
+ baselen + len);
+ if (dir->show_other_directories &&
+ (subdir || !dir->hide_empty_directories) &&
+ !dir_exists(fullname, baselen + len)) {
+ // Rewind the read subdirectory
+ while (dir->nr > rewind_base)
+ free(dir->entries[--dir->nr]);
+ break;
+ }
+ contents += subdir;
+ continue;
+ case DT_REG:
+ case DT_LNK:
+ break;
+ }
+ add_name(dir, fullname, baselen + len);
+ contents++;
+ }
+ closedir(fdir);
+
+ pop_exclude_per_directory(dir, exclude_stk);
+ }
+
+ return contents;
+}
+
+static int cmp_name(const void *p1, const void *p2)
+{
+ const struct dir_entry *e1 = *(const struct dir_entry **)p1;
+ const struct dir_entry *e2 = *(const struct dir_entry **)p2;
+
+ return cache_name_compare(e1->name, e1->len,
+ e2->name, e2->len);
+}
+
+int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen)
+{
+ /*
+ * Make sure to do the per-directory exclude for all the
+ * directories leading up to our base.
+ */
+ if (baselen) {
+ if (dir->exclude_per_dir) {
+ char *p, *pp = xmalloc(baselen+1);
+ memcpy(pp, base, baselen+1);
+ p = pp;
+ while (1) {
+ char save = *p;
+ *p = 0;
+ push_exclude_per_directory(dir, pp, p-pp);
+ *p++ = save;
+ if (!save)
+ break;
+ p = strchr(p, '/');
+ if (p)
+ p++;
+ else
+ p = pp + baselen;
+ }
+ free(pp);
+ }
+ }
+
+ read_directory_recursive(dir, path, base, baselen);
+ qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
+ return dir->nr;
+}
diff --git a/dir.h b/dir.h
new file mode 100644
index 0000000000..56a1b7fce2
--- /dev/null
+++ b/dir.h
@@ -0,0 +1,51 @@
+#ifndef DIR_H
+#define DIR_H
+
+/*
+ * We maintain three exclude pattern lists:
+ * EXC_CMDL lists patterns explicitly given on the command line.
+ * EXC_DIRS lists patterns obtained from per-directory ignore files.
+ * EXC_FILE lists patterns from fallback ignore files.
+ */
+#define EXC_CMDL 0
+#define EXC_DIRS 1
+#define EXC_FILE 2
+
+
+struct dir_entry {
+ int len;
+ char name[FLEX_ARRAY]; /* more */
+};
+
+struct exclude_list {
+ int nr;
+ int alloc;
+ struct exclude {
+ const char *pattern;
+ const char *base;
+ int baselen;
+ } **excludes;
+};
+
+struct dir_struct {
+ int nr, alloc;
+ unsigned int show_ignored:1,
+ show_other_directories:1,
+ hide_empty_directories:1;
+ struct dir_entry **entries;
+
+ /* Exclude info */
+ const char *exclude_per_dir;
+ struct exclude_list exclude_list[3];
+};
+
+extern int common_prefix(const char **pathspec);
+extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
+
+extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen);
+extern int excluded(struct dir_struct *, const char *);
+extern void add_excludes_from_file(struct dir_struct *, const char *fname);
+extern void add_exclude(const char *string, const char *base,
+ int baselen, struct exclude_list *which);
+
+#endif
diff --git a/fetch-pack.c b/fetch-pack.c
index a3bcad016f..8daa93d024 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -262,22 +262,58 @@ static void mark_recent_complete_commits(unsigned long cutoff)
static void filter_refs(struct ref **refs, int nr_match, char **match)
{
- struct ref *prev, *current, *next;
-
- for (prev = NULL, current = *refs; current; current = next) {
- next = current->next;
- if ((!memcmp(current->name, "refs/", 5) &&
- check_ref_format(current->name + 5)) ||
- (!fetch_all &&
- !path_match(current->name, nr_match, match))) {
- if (prev == NULL)
- *refs = next;
- else
- prev->next = next;
- free(current);
- } else
- prev = current;
+ struct ref **return_refs;
+ struct ref *newlist = NULL;
+ struct ref **newtail = &newlist;
+ struct ref *ref, *next;
+ struct ref *fastarray[32];
+
+ if (nr_match && !fetch_all) {
+ if (ARRAY_SIZE(fastarray) < nr_match)
+ return_refs = xcalloc(nr_match, sizeof(struct ref *));
+ else {
+ return_refs = fastarray;
+ memset(return_refs, 0, sizeof(struct ref *) * nr_match);
+ }
+ }
+ else
+ return_refs = NULL;
+
+ for (ref = *refs; ref; ref = next) {
+ next = ref->next;
+ if (!memcmp(ref->name, "refs/", 5) &&
+ check_ref_format(ref->name + 5))
+ ; /* trash */
+ else if (fetch_all) {
+ *newtail = ref;
+ ref->next = NULL;
+ newtail = &ref->next;
+ continue;
+ }
+ else {
+ int order = path_match(ref->name, nr_match, match);
+ if (order) {
+ return_refs[order-1] = ref;
+ continue; /* we will link it later */
+ }
+ }
+ free(ref);
+ }
+
+ if (!fetch_all) {
+ int i;
+ for (i = 0; i < nr_match; i++) {
+ ref = return_refs[i];
+ if (ref) {
+ *newtail = ref;
+ ref->next = NULL;
+ newtail = &ref->next;
+ }
+ }
+ if (return_refs != fastarray)
+ free(return_refs);
}
+ *refs = newlist;
}
static int everything_local(struct ref **refs, int nr_match, char **match)
diff --git a/fetch.c b/fetch.c
index 73bde07aea..f7f8902580 100644
--- a/fetch.c
+++ b/fetch.c
@@ -9,8 +9,6 @@
const char *write_ref = NULL;
-const unsigned char *current_ref = NULL;
-
int get_tree = 0;
int get_history = 0;
int get_all = 0;
@@ -205,19 +203,12 @@ static int mark_complete(const char *path, const unsigned char *sha1)
int pull(char *target)
{
unsigned char sha1[20];
- int fd = -1;
save_commit_buffer = 0;
track_object_refs = 0;
- if (write_ref && current_ref) {
- fd = lock_ref_sha1(write_ref, current_ref);
- if (fd < 0)
- return -1;
- }
- if (!get_recover) {
+ if (!get_recover)
for_each_ref(mark_complete);
- }
if (interpret_target(target, sha1))
return error("Could not interpret %s as something to pull",
@@ -227,12 +218,7 @@ int pull(char *target)
if (loop())
return -1;
- if (write_ref) {
- if (current_ref) {
- write_ref_sha1(write_ref, fd, sha1);
- } else {
- write_ref_sha1_unlocked(write_ref, sha1);
- }
- }
+ if (write_ref)
+ write_ref_sha1_unlocked(write_ref, sha1);
return 0;
}
diff --git a/fetch.h b/fetch.h
index 9837a3d035..001a6b8e2d 100644
--- a/fetch.h
+++ b/fetch.h
@@ -25,9 +25,6 @@ extern int fetch_ref(char *ref, unsigned char *sha1);
/* If set, the ref filename to write the target value to. */
extern const char *write_ref;
-/* If set, the hash that the current value of write_ref must be. */
-extern const unsigned char *current_ref;
-
/* Set to fetch the target tree. */
extern int get_tree;
diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh
index 6c59dbd68f..ec1eda20de 100755
--- a/generate-cmdlist.sh
+++ b/generate-cmdlist.sh
@@ -37,7 +37,6 @@ show-branch
status
tag
verify-tag
-whatchanged
EOF
while read cmd
do
diff --git a/git-add.sh b/git-add.sh
deleted file mode 100755
index d6a4bc7d09..0000000000
--- a/git-add.sh
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/sh
-
-USAGE='[-n] [-v] <file>...'
-SUBDIRECTORY_OK='Yes'
-. git-sh-setup
-
-show_only=
-verbose=
-while : ; do
- case "$1" in
- -n)
- show_only=true
- ;;
- -v)
- verbose=--verbose
- ;;
- --)
- shift
- break
- ;;
- -*)
- usage
- ;;
- *)
- break
- ;;
- esac
- shift
-done
-
-# Check misspelled pathspec
-case "$#" in
-0) ;;
-*)
- git-ls-files --error-unmatch --others --cached -- "$@" >/dev/null || {
- echo >&2 "Maybe you misspelled it?"
- exit 1
- }
- ;;
-esac
-
-if test -f "$GIT_DIR/info/exclude"
-then
- git-ls-files -z \
- --exclude-from="$GIT_DIR/info/exclude" \
- --others --exclude-per-directory=.gitignore -- "$@"
-else
- git-ls-files -z \
- --others --exclude-per-directory=.gitignore -- "$@"
-fi |
-case "$show_only" in
-true)
- xargs -0 echo ;;
-*)
- git-update-index --add $verbose -z --stdin ;;
-esac
diff --git a/git-clone.sh b/git-clone.sh
index 227245c865..de59904d56 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -9,7 +9,7 @@
unset CDPATH
usage() {
- echo >&2 "Usage: $0 [--use-separate-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
+ echo >&2 "Usage: $0 [--template=<template_directory>] [--use-separate-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
exit 1
}
@@ -102,6 +102,7 @@ quiet=
local=no
use_local=no
local_shared=no
+unset template
no_checkout=
upload_pack=
bare=
@@ -120,6 +121,11 @@ while
*,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local) use_local=yes ;;
*,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared)
local_shared=yes; use_local=yes ;;
+ 1,--template) usage ;;
+ *,--template)
+ shift; template="--template=$1" ;;
+ *,--template=*)
+ template="$1" ;;
*,-q|*,--quiet) quiet=-q ;;
*,--use-separate-remote)
use_separate_remote=t ;;
@@ -199,11 +205,11 @@ dir="$2"
[ -e "$dir" ] && echo "$dir already exists." && usage
mkdir -p "$dir" &&
D=$(cd "$dir" && pwd) &&
-trap 'err=$?; cd ..; rm -r "$D"; exit $err' exit
+trap 'err=$?; cd ..; rm -r "$D"; exit $err' 0
case "$bare" in
yes) GIT_DIR="$D" ;;
*) GIT_DIR="$D/.git" ;;
-esac && export GIT_DIR && git-init-db || usage
+esac && export GIT_DIR && git-init-db ${template+"$template"} || usage
case "$bare" in
yes)
GIT_DIR="$D" ;;
@@ -407,5 +413,5 @@ Pull: refs/heads/$head_points_at:$origin_track" &&
fi
rm -f "$GIT_DIR/CLONE_HEAD" "$GIT_DIR/REMOTE_HEAD"
-trap - exit
+trap - 0
diff --git a/git-commit.sh b/git-commit.sh
index 6ef1a9dedc..1983d45828 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Linus Torvalds
# Copyright (c) 2006 Junio C Hamano
-USAGE='[-a] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit>) [--amend] [-e] [--author <author>] [[-i | -o] <path>...]'
+USAGE='[-a] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit>] [-u] [--amend] [-e] [--author <author>] [[-i | -o] <path>...]'
SUBDIRECTORY_OK=Yes
. git-sh-setup
@@ -134,13 +134,17 @@ run_status () {
report "Changed but not updated" \
"use git-update-index to mark for commit"
+ option=""
+ if test -z "$untracked_files"; then
+ option="--directory --no-empty-directory"
+ fi
if test -f "$GIT_DIR/info/exclude"
then
- git-ls-files -z --others --directory \
+ git-ls-files -z --others $option \
--exclude-from="$GIT_DIR/info/exclude" \
--exclude-per-directory=.gitignore
else
- git-ls-files -z --others --directory \
+ git-ls-files -z --others $option \
--exclude-per-directory=.gitignore
fi |
perl -e '$/ = "\0";
@@ -203,6 +207,7 @@ verbose=
signoff=
force_author=
only_include_assumed=
+untracked_files=
while case "$#" in 0) break;; esac
do
case "$1" in
@@ -340,6 +345,12 @@ do
verbose=t
shift
;;
+ -u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|--untracked|\
+ --untracked-|--untracked-f|--untracked-fi|--untracked-fil|--untracked-file|\
+ --untracked-files)
+ untracked_files=t
+ shift
+ ;;
--)
shift
break
@@ -615,6 +626,9 @@ fi
if test -z "$no_edit"
then
{
+ echo ""
+ echo "# Please enter the commit message for your changes."
+ echo "# (Comment lines starting with '#' will not be included)"
test -z "$only_include_assumed" || echo "$only_include_assumed"
run_status
} >>"$GIT_DIR"/COMMIT_EDITMSG
diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl
index f994443c6f..57088c3f0b 100755
--- a/git-cvsexportcommit.perl
+++ b/git-cvsexportcommit.perl
@@ -1,10 +1,16 @@
#!/usr/bin/perl -w
+# Known limitations:
+# - cannot add or remove binary files
+# - does not propagate permissions
+# - tells "ready for commit" even when things could not be completed
+# (eg addition of a binary file)
+
use strict;
use Getopt::Std;
use File::Temp qw(tempdir);
use Data::Dumper;
-use File::Basename qw(basename);
+use File::Basename qw(basename dirname);
unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
die "GIT_DIR is not defined or is unreadable";
@@ -84,7 +90,7 @@ close MSG;
`git-cat-file commit $commit | sed -e '1,/^\$/d' >> .msg`;
$? && die "Error extracting the commit message";
-my (@afiles, @dfiles, @mfiles);
+my (@afiles, @dfiles, @mfiles, @dirs);
my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit);
#print @files;
$? && die "Error in git-diff-tree";
@@ -92,7 +98,14 @@ foreach my $f (@files) {
chomp $f;
my @fields = split(m!\s+!, $f);
if ($fields[4] eq 'A') {
- push @afiles, $fields[5];
+ my $path = $fields[5];
+ push @afiles, $path;
+ # add any needed parent directories
+ $path = dirname $path;
+ while (!-d $path and ! grep { $_ eq $path } @dirs) {
+ unshift @dirs, $path;
+ $path = dirname $path;
+ }
}
if ($fields[4] eq 'M') {
push @mfiles, $fields[5];
@@ -107,13 +120,21 @@ undef @files; # don't need it anymore
# check that the files are clean and up to date according to cvs
my $dirty;
+foreach my $d (@dirs) {
+ if (-e $d) {
+ $dirty = 1;
+ warn "$d exists and is not a directory!\n";
+ }
+}
foreach my $f (@afiles) {
# This should return only one value
my @status = grep(m/^File/, safe_pipe_capture('cvs', '-q', 'status' ,$f));
if (@status > 1) { warn 'Strange! cvs status returned more than one line?'};
- unless ($status[0] =~ m/Status: Unknown$/) {
+ if (-d dirname $f and $status[0] !~ m/Status: Unknown$/
+ and $status[0] !~ m/^File: no file /) {
$dirty = 1;
warn "File $f is already known in your CVS checkout -- perhaps it has been added by another user. Or this may indicate that it exists on a different branch. If this is the case, use -f to force the merge.\n";
+ warn "Status was: $status\n";
}
}
foreach my $f (@mfiles, @dfiles) {
@@ -139,6 +160,19 @@ if ($dirty) {
###
+print "Creating new directories\n";
+foreach my $d (@dirs) {
+ unless (mkdir $d) {
+ warn "Could not mkdir $d: $!";
+ $dirty = 1;
+ }
+ `cvs add $d`;
+ if ($?) {
+ $dirty = 1;
+ warn "Failed to cvs add directory $d -- you may need to do it manually";
+ }
+}
+
print "'Patching' binary files\n";
my @bfiles = grep(m/^Binary/, safe_pipe_capture('git-diff-tree', '-p', $parent, $commit));
@@ -151,7 +185,7 @@ foreach my $f (@bfiles) {
my $blob = `git-ls-tree $tree "$f" | cut -f 1 | cut -d ' ' -f 3`;
chomp $blob;
`git-cat-file blob $blob > $tmpdir/blob`;
- if (system('cmp', '-q', $f, "$tmpdir/blob")) {
+ if (system('cmp', '-s', $f, "$tmpdir/blob")) {
warn "Binary file $f in CVS does not match parent.\n";
$dirty = 1;
next;
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index 8c707f2c66..76f6246a31 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -23,13 +23,13 @@ use File::Basename qw(basename dirname);
use Time::Local;
use IO::Socket;
use IO::Pipe;
-use POSIX qw(strftime dup2);
+use POSIX qw(strftime dup2 ENOENT);
use IPC::Open2;
$SIG{'PIPE'}="IGNORE";
$ENV{'TZ'}="UTC";
-our($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S);
+our($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L);
my (%conv_author_name, %conv_author_email);
sub usage() {
@@ -85,7 +85,7 @@ sub write_author_info($) {
close ($f);
}
-getopts("hivmkuo:d:p:C:z:s:M:P:A:S:") or usage();
+getopts("hivmkuo:d:p:C:z:s:M:P:A:S:L:") or usage();
usage if $opt_h;
@ARGV <= 1 or usage();
@@ -315,15 +315,7 @@ sub _line {
chomp $cnt;
die "Duh: Filesize $cnt" if $cnt !~ /^\d+$/;
$line="";
- $res=0;
- while($cnt) {
- my $buf;
- my $num = $self->{'socketi'}->read($buf,$cnt);
- die "Server: Filesize $cnt: $num: $!\n" if not defined $num or $num<=0;
- print $fh $buf;
- $res += $num;
- $cnt -= $num;
- }
+ $res = $self->_fetchfile($fh, $cnt);
} elsif($line =~ s/^ //) {
print $fh $line;
$res += length($line);
@@ -335,14 +327,7 @@ sub _line {
chomp $cnt;
die "Duh: Mbinary $cnt" if $cnt !~ /^\d+$/ or $cnt<1;
$line="";
- while($cnt) {
- my $buf;
- my $num = $self->{'socketi'}->read($buf,$cnt);
- die "S: Mbinary $cnt: $num: $!\n" if not defined $num or $num<=0;
- print $fh $buf;
- $res += $num;
- $cnt -= $num;
- }
+ $res += $self->_fetchfile($fh, $cnt);
} else {
chomp $line;
if($line eq "ok") {
@@ -384,6 +369,23 @@ sub file {
return ($name, $res);
}
+sub _fetchfile {
+ my ($self, $fh, $cnt) = @_;
+ my $res = 0;
+ my $bufsize = 1024 * 1024;
+ while($cnt) {
+ if ($bufsize > $cnt) {
+ $bufsize = $cnt;
+ }
+ my $buf;
+ my $num = $self->{'socketi'}->read($buf,$bufsize);
+ die "Server: Filesize $cnt: $num: $!\n" if not defined $num or $num<=0;
+ print $fh $buf;
+ $res += $num;
+ $cnt -= $num;
+ }
+ return $res;
+}
package main;
@@ -429,22 +431,25 @@ sub getwd() {
return $pwd;
}
+sub is_sha1 {
+ my $s = shift;
+ return $s =~ /^[a-f0-9]{40}$/;
+}
-sub get_headref($$) {
+sub get_headref ($$) {
my $name = shift;
my $git_dir = shift;
- my $sha;
- if (open(C,"$git_dir/refs/heads/$name")) {
- chomp($sha = <C>);
- close(C);
- length($sha) == 40
- or die "Cannot get head id for $name ($sha): $!\n";
+ my $f = "$git_dir/refs/heads/$name";
+ if(open(my $fh, $f)) {
+ chomp(my $r = <$fh>);
+ is_sha1($r) or die "Cannot get head id for $name ($r): $!";
+ return $r;
}
- return $sha;
+ die "unable to open $f: $!" unless $! == POSIX::ENOENT;
+ return undef;
}
-
-d $git_tree
or mkdir($git_tree,0777)
or die "Could not create $git_tree: $!";
@@ -561,98 +566,66 @@ unless($pid) {
my $state = 0;
-my($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg);
-my(@old,@new,@skipped);
-my $commit = sub {
- my $pid;
- while(@old) {
- my @o2;
- if(@old > 55) {
- @o2 = splice(@old,0,50);
- } else {
- @o2 = @old;
- @old = ();
- }
- system("git-update-index","--force-remove","--",@o2);
- die "Cannot remove files: $?\n" if $?;
- }
- while(@new) {
- my @n2;
- if(@new > 12) {
- @n2 = splice(@new,0,10);
- } else {
- @n2 = @new;
- @new = ();
- }
- system("git-update-index","--add",
- (map { ('--cacheinfo', @$_) } @n2));
- die "Cannot add files: $?\n" if $?;
- }
+sub update_index (\@\@) {
+ my $old = shift;
+ my $new = shift;
+ open(my $fh, '|-', qw(git-update-index -z --index-info))
+ or die "unable to open git-update-index: $!";
+ print $fh
+ (map { "0 0000000000000000000000000000000000000000\t$_\0" }
+ @$old),
+ (map { '100' . sprintf('%o', $_->[0]) . " $_->[1]\t$_->[2]\0" }
+ @$new)
+ or die "unable to write to git-update-index: $!";
+ close $fh
+ or die "unable to write to git-update-index: $!";
+ $? and die "git-update-index reported error: $?";
+}
- $pid = open(C,"-|");
- die "Cannot fork: $!" unless defined $pid;
- unless($pid) {
- exec("git-write-tree");
- die "Cannot exec git-write-tree: $!\n";
- }
- chomp(my $tree = <C>);
- length($tree) == 40
- or die "Cannot get tree id ($tree): $!\n";
- close(C)
+sub write_tree () {
+ open(my $fh, '-|', qw(git-write-tree))
+ or die "unable to open git-write-tree: $!";
+ chomp(my $tree = <$fh>);
+ is_sha1($tree)
+ or die "Cannot get tree id ($tree): $!";
+ close($fh)
or die "Error running git-write-tree: $?\n";
print "Tree ID $tree\n" if $opt_v;
+ return $tree;
+}
- my $parent = "";
- if(open(C,"$git_dir/refs/heads/$last_branch")) {
- chomp($parent = <C>);
- close(C);
- length($parent) == 40
- or die "Cannot get parent id ($parent): $!\n";
- print "Parent ID $parent\n" if $opt_v;
- }
-
- my $pr = IO::Pipe->new() or die "Cannot open pipe: $!\n";
- my $pw = IO::Pipe->new() or die "Cannot open pipe: $!\n";
- $pid = fork();
- die "Fork: $!\n" unless defined $pid;
- unless($pid) {
- $pr->writer();
- $pw->reader();
- open(OUT,">&STDOUT");
- dup2($pw->fileno(),0);
- dup2($pr->fileno(),1);
- $pr->close();
- $pw->close();
-
- my @par = ();
- @par = ("-p",$parent) if $parent;
-
- # loose detection of merges
- # based on the commit msg
- foreach my $rx (@mergerx) {
- if ($logmsg =~ $rx) {
- my $mparent = $1;
- if ($mparent eq 'HEAD') { $mparent = $opt_o };
- if ( -e "$git_dir/refs/heads/$mparent") {
- $mparent = get_headref($mparent, $git_dir);
- push @par, '-p', $mparent;
- print OUT "Merge parent branch: $mparent\n" if $opt_v;
- }
- }
+my($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg);
+my(@old,@new,@skipped);
+sub commit {
+ update_index(@old, @new);
+ @old = @new = ();
+ my $tree = write_tree();
+ my $parent = get_headref($last_branch, $git_dir);
+ print "Parent ID " . ($parent ? $parent : "(empty)") . "\n" if $opt_v;
+
+ my @commit_args;
+ push @commit_args, ("-p", $parent) if $parent;
+
+ # loose detection of merges
+ # based on the commit msg
+ foreach my $rx (@mergerx) {
+ next unless $logmsg =~ $rx && $1;
+ my $mparent = $1 eq 'HEAD' ? $opt_o : $1;
+ if(my $sha1 = get_headref($mparent, $git_dir)) {
+ push @commit_args, '-p', $mparent;
+ print "Merge parent branch: $mparent\n" if $opt_v;
}
-
- exec("env",
- "GIT_AUTHOR_NAME=$author_name",
- "GIT_AUTHOR_EMAIL=$author_email",
- "GIT_AUTHOR_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)),
- "GIT_COMMITTER_NAME=$author_name",
- "GIT_COMMITTER_EMAIL=$author_email",
- "GIT_COMMITTER_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)),
- "git-commit-tree", $tree,@par);
- die "Cannot exec git-commit-tree: $!\n";
}
- $pw->writer();
- $pr->reader();
+
+ my $commit_date = strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date));
+ $ENV{GIT_AUTHOR_NAME} = $author_name;
+ $ENV{GIT_AUTHOR_EMAIL} = $author_email;
+ $ENV{GIT_AUTHOR_DATE} = $commit_date;
+ $ENV{GIT_COMMITTER_NAME} = $author_name;
+ $ENV{GIT_COMMITTER_EMAIL} = $author_email;
+ $ENV{GIT_COMMITTER_DATE} = $commit_date;
+ my $pid = open2(my $commit_read, my $commit_write,
+ 'git-commit-tree', $tree, @commit_args);
# compatibility with git2cvs
substr($logmsg,32767) = "" if length($logmsg) > 32767;
@@ -661,18 +634,17 @@ my $commit = sub {
if (@skipped) {
$logmsg .= "\n\n\nSKIPPED:\n\t";
$logmsg .= join("\n\t", @skipped) . "\n";
+ @skipped = ();
}
- print $pw "$logmsg\n"
+ print($commit_write "$logmsg\n") && close($commit_write)
or die "Error writing to git-commit-tree: $!\n";
- $pw->close();
- print "Committed patch $patchset ($branch ".strftime("%Y-%m-%d %H:%M:%S",gmtime($date)).")\n" if $opt_v;
- chomp(my $cid = <$pr>);
- length($cid) == 40
- or die "Cannot get commit id ($cid): $!\n";
+ print "Committed patch $patchset ($branch $commit_date)\n" if $opt_v;
+ chomp(my $cid = <$commit_read>);
+ is_sha1($cid) or die "Cannot get commit id ($cid): $!\n";
print "Commit ID $cid\n" if $opt_v;
- $pr->close();
+ close($commit_read);
waitpid($pid,0);
die "Error running git-commit-tree: $?\n" if $?;
@@ -716,6 +688,7 @@ my $commit = sub {
}
};
+my $commitcount = 1;
while(<CVS>) {
chomp;
if($state == 0 and /^-+$/) {
@@ -849,7 +822,14 @@ while(<CVS>) {
} elsif($state == 9 and /^\s*$/) {
$state = 10;
} elsif(($state == 9 or $state == 10) and /^-+$/) {
- &$commit();
+ $commitcount++;
+ if ($opt_L && $commitcount > $opt_L) {
+ last;
+ }
+ commit();
+ if (($commitcount & 1023) == 0) {
+ system("git repack -a -d");
+ }
$state = 1;
} elsif($state == 11 and /^-+$/) {
$state = 1;
@@ -859,7 +839,7 @@ while(<CVS>) {
print "* UNKNOWN LINE * $_\n";
}
}
-&$commit() if $branch and $state != 11;
+commit() if $branch and $state != 11;
unlink($git_index);
diff --git a/git-format-patch.sh b/git-format-patch.sh
deleted file mode 100755
index 8a16eadfbd..0000000000
--- a/git-format-patch.sh
+++ /dev/null
@@ -1,344 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Junio C Hamano
-#
-
-USAGE='[-n | -k] [-o <dir> | --stdout] [--signoff] [--check] [--diff-options] [--attach] <his> [<mine>]'
-LONG_USAGE='Prepare each commit with its patch since <mine> head forked from
-<his> head, one file per patch formatted to resemble UNIX mailbox
-format, for e-mail submission or use with git-am.
-
-Each output file is numbered sequentially from 1, and uses the
-first line of the commit message (massaged for pathname safety)
-as the filename.
-
-When -o is specified, output files are created in <dir>; otherwise
-they are created in the current working directory. This option
-is ignored if --stdout is specified.
-
-When -n is specified, instead of "[PATCH] Subject", the first
-line is formatted as "[PATCH N/M] Subject", unless you have only
-one patch.
-
-When --attach is specified, patches are attached, not inlined.'
-
-. git-sh-setup
-
-# Force diff to run in C locale.
-LANG=C LC_ALL=C
-export LANG LC_ALL
-
-diff_opts=
-LF='
-'
-
-outdir=./
-while case "$#" in 0) break;; esac
-do
- case "$1" in
- -c|--c|--ch|--che|--chec|--check)
- check=t ;;
- -a|--a|--au|--aut|--auth|--autho|--author|\
- -d|--d|--da|--dat|--date|\
- -m|--m|--mb|--mbo|--mbox) # now noop
- ;;
- --at|--att|--atta|--attac|--attach)
- attach=t ;;
- -k|--k|--ke|--kee|--keep|--keep-|--keep-s|--keep-su|--keep-sub|\
- --keep-subj|--keep-subje|--keep-subjec|--keep-subject)
- keep_subject=t ;;
- -n|--n|--nu|--num|--numb|--numbe|--number|--numbere|--numbered)
- numbered=t ;;
- -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
- signoff=t ;;
- --st|--std|--stdo|--stdou|--stdout)
- stdout=t ;;
- -o=*|--o=*|--ou=*|--out=*|--outp=*|--outpu=*|--output=*|--output-=*|\
- --output-d=*|--output-di=*|--output-dir=*|--output-dire=*|\
- --output-direc=*|--output-direct=*|--output-directo=*|\
- --output-director=*|--output-directory=*)
- outdir=`expr "$1" : '-[^=]*=\(.*\)'` ;;
- -o|--o|--ou|--out|--outp|--outpu|--output|--output-|--output-d|\
- --output-di|--output-dir|--output-dire|--output-direc|--output-direct|\
- --output-directo|--output-director|--output-directory)
- case "$#" in 1) usage ;; esac; shift
- outdir="$1" ;;
- -h|--h|--he|--hel|--help)
- usage
- ;;
- -*' '* | -*"$LF"* | -*' '*)
- # Ignore diff option that has whitespace for now.
- ;;
- -*) diff_opts="$diff_opts$1 " ;;
- *) break ;;
- esac
- shift
-done
-
-case "$keep_subject$numbered" in
-tt)
- die '--keep-subject and --numbered are incompatible.' ;;
-esac
-
-tmp=.tmp-series$$
-trap 'rm -f $tmp-*' 0 1 2 3 15
-
-series=$tmp-series
-commsg=$tmp-commsg
-filelist=$tmp-files
-
-# Backward compatible argument parsing hack.
-#
-# Historically, we supported:
-# 1. "rev1" is equivalent to "rev1..HEAD"
-# 2. "rev1..rev2"
-# 3. "rev1" "rev2 is equivalent to "rev1..rev2"
-#
-# We want to take a sequence of "rev1..rev2" in general.
-# Also, "rev1.." should mean "rev1..HEAD"; git-diff users are
-# familiar with that syntax.
-
-case "$#,$1$2" in
-1,?*..?*)
- # single "rev1..rev2"
- ;;
-1,?*..)
- # single "rev1.." should mean "rev1..HEAD"
- set x "$1"HEAD
- shift
- ;;
-1,*)
- # single rev1
- set x "$1..HEAD"
- shift
- ;;
-2,?*..?*)
- # not traditional "rev1" "rev2"
- ;;
-2,*)
- set x "$1..$2"
- shift
- ;;
-esac
-
-# Now we have what we want in $@
-for revpair
-do
- case "$revpair" in
- ?*..?*)
- rev1=`expr "z$revpair" : 'z\(.*\)\.\.'`
- rev2=`expr "z$revpair" : 'z.*\.\.\(.*\)'`
- ;;
- *)
- rev1="$revpair^"
- rev2="$revpair"
- ;;
- esac
- git-rev-parse --verify "$rev1^0" >/dev/null 2>&1 ||
- die "Not a valid rev $rev1 ($revpair)"
- git-rev-parse --verify "$rev2^0" >/dev/null 2>&1 ||
- die "Not a valid rev $rev2 ($revpair)"
- git-cherry -v "$rev1" "$rev2" |
- while read sign rev comment
- do
- case "$sign" in
- '-')
- echo >&2 "Merged already: $comment"
- ;;
- *)
- echo $rev
- ;;
- esac
- done
-done >$series
-
-me=`git-var GIT_AUTHOR_IDENT | sed -e 's/>.*/>/'`
-headers=`git-repo-config --get format.headers`
-case "$attach" in
-"") ;;
-*)
- mimemagic="050802040500080604070107"
-esac
-
-case "$outdir" in
-*/) ;;
-*) outdir="$outdir/" ;;
-esac
-test -d "$outdir" || mkdir -p "$outdir" || exit
-
-titleScript='
- /./d
- /^$/n
- s/^\[PATCH[^]]*\] *//
- s/[^-a-z.A-Z_0-9]/-/g
- s/\.\.\.*/\./g
- s/\.*$//
- s/--*/-/g
- s/^-//
- s/-$//
- s/$/./
- p
- q
-'
-
-process_one () {
- perl -w -e '
-my ($keep_subject, $num, $signoff, $headers, $mimemagic, $commsg) = @ARGV;
-my ($signoff_pattern, $done_header, $done_subject, $done_separator, $signoff_seen,
- $last_was_signoff);
-
-if ($signoff) {
- $signoff = "Signed-off-by: " . `git-var GIT_COMMITTER_IDENT`;
- $signoff =~ s/>.*/>/;
- $signoff_pattern = quotemeta($signoff);
-}
-
-my @weekday_names = qw(Sun Mon Tue Wed Thu Fri Sat);
-my @month_names = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
-
-sub show_date {
- my ($time, $tz) = @_;
- my $minutes = abs($tz);
- $minutes = int($minutes / 100) * 60 + ($minutes % 100);
- if ($tz < 0) {
- $minutes = -$minutes;
- }
- my $t = $time + $minutes * 60;
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($t);
- return sprintf("%s, %d %s %d %02d:%02d:%02d %+05d",
- $weekday_names[$wday], $mday,
- $month_names[$mon], $year+1900,
- $hour, $min, $sec, $tz);
-}
-
-print "From nobody Mon Sep 17 00:00:00 2001\n";
-open FH, "git stripspace <$commsg |" or die "open $commsg pipe";
-while (<FH>) {
- unless ($done_header) {
- if (/^$/) {
- $done_header = 1;
- }
- elsif (/^author (.*>) (.*)$/) {
- my ($author_ident, $author_date) = ($1, $2);
- my ($utc, $off) = ($author_date =~ /^(\d+) ([-+]?\d+)$/);
- $author_date = show_date($utc, $off);
-
- print "From: $author_ident\n";
- print "Date: $author_date\n";
- }
- next;
- }
- unless ($done_subject) {
- unless ($keep_subject) {
- s/^\[PATCH[^]]*\]\s*//;
- s/^/[PATCH$num] /;
- }
- if ($headers) {
- print "$headers\n";
- }
- print "Subject: $_";
- if ($mimemagic) {
- print "MIME-Version: 1.0\n";
- print "Content-Type: multipart/mixed;\n";
- print " boundary=\"------------$mimemagic\"\n";
- print "\n";
- print "This is a multi-part message in MIME format.\n";
- print "--------------$mimemagic\n";
- print "Content-Type: text/plain; charset=UTF-8; format=fixed\n";
- print "Content-Transfer-Encoding: 8bit\n";
- }
- $done_subject = 1;
- next;
- }
- unless ($done_separator) {
- print "\n";
- $done_separator = 1;
- next if (/^$/);
- }
-
- $last_was_signoff = 0;
- if (/Signed-off-by:/i) {
- if ($signoff ne "" && /Signed-off-by:\s*$signoff_pattern$/i) {
- $signoff_seen = 1;
- }
- }
- print $_;
-}
-if (!$signoff_seen && $signoff ne "") {
- if (!$last_was_signoff) {
- print "\n";
- }
- print "$signoff\n";
-}
-print "\n---\n\n";
-close FH or die "close $commsg pipe";
-' "$keep_subject" "$num" "$signoff" "$headers" "$mimemagic" $commsg
-
- git-diff-tree -p --stat --summary $diff_opts "$commit"
- echo
- case "$mimemagic" in
- '');;
- *)
- echo "--------------$mimemagic"
- echo "Content-Type: text/x-patch;"
- echo " name=\"$commit.diff\""
- echo "Content-Transfer-Encoding: 8bit"
- echo "Content-Disposition: inline;"
- echo " filename=\"$commit.diff\""
- echo
- esac
- git-diff-tree -p $diff_opts "$commit"
- case "$mimemagic" in
- '')
- echo "-- "
- echo "@@GIT_VERSION@@"
- ;;
- *)
- echo
- echo "--------------$mimemagic--"
- echo
- ;;
- esac
- echo
-}
-
-total=`wc -l <$series | tr -dc "[0-9]"`
-case "$total,$numbered" in
-1,*)
- numfmt='' ;;
-*,t)
- numfmt=`echo "$total" | wc -c`
- numfmt=$(($numfmt-1))
- numfmt=" %0${numfmt}d/$total"
-esac
-
-i=1
-while read commit
-do
- git-cat-file commit "$commit" | git-stripspace >$commsg
- title=`sed -ne "$titleScript" <$commsg`
- case "$numbered" in
- '') num= ;;
- *)
- num=`printf "$numfmt" $i` ;;
- 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 b6882a90c1..2fdcaf7886 100755
--- a/git-ls-remote.sh
+++ b/git-ls-remote.sh
@@ -58,11 +58,19 @@ http://* | https://* )
;;
rsync://* )
- mkdir $tmpdir
+ mkdir $tmpdir &&
+ rsync -rlq "$peek_repo/HEAD" $tmpdir &&
rsync -rq "$peek_repo/refs" $tmpdir || {
echo "failed slurping"
exit
}
+ head=$(cat "$tmpdir/HEAD") &&
+ case "$head" in
+ ref:' '*)
+ head=$(expr "z$head" : 'zref: \(.*\)') &&
+ head=$(cat "$tmpdir/$head") || exit
+ esac &&
+ echo "$head HEAD"
(cd $tmpdir && find refs -type f) |
while read path
do
diff --git a/git-rebase.sh b/git-rebase.sh
index 6ff6088d18..e6b57b8ab9 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -152,6 +152,6 @@ then
exit 0
fi
-git-format-patch -k --stdout --full-index "$upstream" ORIG_HEAD |
+git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
diff --git a/git-rm.sh b/git-rm.sh
deleted file mode 100755
index fda4541c76..0000000000
--- a/git-rm.sh
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/bin/sh
-
-USAGE='[-f] [-n] [-v] [--] <file>...'
-SUBDIRECTORY_OK='Yes'
-. git-sh-setup
-
-remove_files=
-show_only=
-verbose=
-while : ; do
- case "$1" in
- -f)
- remove_files=true
- ;;
- -n)
- show_only=true
- ;;
- -v)
- verbose=--verbose
- ;;
- --)
- shift; break
- ;;
- -*)
- usage
- ;;
- *)
- break
- ;;
- esac
- shift
-done
-
-# This is typo-proofing. If some paths match and some do not, we want
-# to do nothing.
-case "$#" in
-0) ;;
-*)
- git-ls-files --error-unmatch -- "$@" >/dev/null || {
- echo >&2 "Maybe you misspelled it?"
- exit 1
- }
- ;;
-esac
-
-if test -f "$GIT_DIR/info/exclude"
-then
- git-ls-files -z \
- --exclude-from="$GIT_DIR/info/exclude" \
- --exclude-per-directory=.gitignore -- "$@"
-else
- git-ls-files -z \
- --exclude-per-directory=.gitignore -- "$@"
-fi |
-case "$show_only,$remove_files" in
-true,*)
- xargs -0 echo
- ;;
-*,true)
- xargs -0 sh -c "
- while [ \$# -gt 0 ]; do
- file=\$1; shift
- rm -- \"\$file\" && git-update-index --remove $verbose \"\$file\"
- done
- " inline
- ;;
-*)
- git-update-index --force-remove $verbose -z --stdin
- ;;
-esac
diff --git a/git.c b/git.c
index 3216d311b2..10ea934bcf 100644
--- a/git.c
+++ b/git.c
@@ -47,12 +47,29 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "whatchanged", cmd_whatchanged },
{ "show", cmd_show },
{ "push", cmd_push },
+ { "format-patch", cmd_format_patch },
{ "count-objects", cmd_count_objects },
{ "diff", cmd_diff },
{ "grep", cmd_grep },
+ { "rm", cmd_rm },
+ { "add", cmd_add },
{ "rev-list", cmd_rev_list },
{ "init-db", cmd_init_db },
- { "check-ref-format", cmd_check_ref_format }
+ { "tar-tree", cmd_tar_tree },
+ { "upload-tar", cmd_upload_tar },
+ { "check-ref-format", cmd_check_ref_format },
+ { "ls-files", cmd_ls_files },
+ { "ls-tree", cmd_ls_tree },
+ { "tar-tree", cmd_tar_tree },
+ { "read-tree", cmd_read_tree },
+ { "commit-tree", cmd_commit_tree },
+ { "apply", cmd_apply },
+ { "show-branch", cmd_show_branch },
+ { "diff-files", cmd_diff_files },
+ { "diff-index", cmd_diff_index },
+ { "diff-stages", cmd_diff_stages },
+ { "diff-tree", cmd_diff_tree },
+ { "cat-file", cmd_cat_file }
};
int i;
diff --git a/log-tree.c b/log-tree.c
index b90ba6762a..58b016378c 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -20,6 +20,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
const char *extra;
int len;
+ char *subject = NULL, *after_subject = NULL;
opt->loginfo = NULL;
if (!opt->verbose_header) {
@@ -49,19 +50,67 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
/*
* Print header line of header..
*/
- printf("%s%s",
- opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
- diff_unique_abbrev(commit->object.sha1, abbrev_commit));
- if (opt->parents)
- show_parents(commit, abbrev_commit);
- if (parent)
- printf(" (from %s)", diff_unique_abbrev(parent->object.sha1, abbrev_commit));
- putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
+
+ if (opt->commit_format == CMIT_FMT_EMAIL) {
+ char *sha1 = sha1_to_hex(commit->object.sha1);
+ if (opt->total > 0) {
+ static char buffer[64];
+ snprintf(buffer, sizeof(buffer),
+ "Subject: [PATCH %d/%d] ",
+ opt->nr, opt->total);
+ subject = buffer;
+ } else if (opt->total == 0)
+ subject = "Subject: [PATCH] ";
+ else
+ subject = "Subject: ";
+
+ printf("From %s Mon Sep 17 00:00:00 2001\n", sha1);
+ if (opt->mime_boundary) {
+ static char subject_buffer[1024];
+ static char buffer[1024];
+ snprintf(subject_buffer, sizeof(subject_buffer) - 1,
+ "MIME-Version: 1.0\n"
+ "Content-Type: multipart/mixed;\n"
+ " boundary=\"%s%s\"\n"
+ "\n"
+ "This is a multi-part message in MIME "
+ "format.\n"
+ "--%s%s\n"
+ "Content-Type: text/plain; "
+ "charset=UTF-8; format=fixed\n"
+ "Content-Transfer-Encoding: 8bit\n\n",
+ mime_boundary_leader, opt->mime_boundary,
+ mime_boundary_leader, opt->mime_boundary);
+ after_subject = subject_buffer;
+
+ snprintf(buffer, sizeof(buffer) - 1,
+ "--%s%s\n"
+ "Content-Type: text/x-patch;\n"
+ " name=\"%s.diff\"\n"
+ "Content-Transfer-Encoding: 8bit\n"
+ "Content-Disposition: inline;\n"
+ " filename=\"%s.diff\"\n\n",
+ mime_boundary_leader, opt->mime_boundary,
+ sha1, sha1);
+ opt->diffopt.stat_sep = buffer;
+ }
+ } else {
+ printf("%s%s",
+ opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
+ diff_unique_abbrev(commit->object.sha1, abbrev_commit));
+ if (opt->parents)
+ show_parents(commit, abbrev_commit);
+ if (parent)
+ printf(" (from %s)",
+ diff_unique_abbrev(parent->object.sha1,
+ abbrev_commit));
+ putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
+ }
/*
* And then the pretty-printed message itself
*/
- len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev);
+ len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev, subject, after_subject);
printf("%s%s%s", this_header, extra, sep);
}
@@ -166,15 +215,18 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
int log_tree_commit(struct rev_info *opt, struct commit *commit)
{
struct log_info log;
+ int shown;
log.commit = commit;
log.parent = NULL;
opt->loginfo = &log;
- if (!log_tree_diff(opt, commit, &log) && opt->loginfo && opt->always_show_header) {
+ shown = log_tree_diff(opt, commit, &log);
+ if (!shown && opt->loginfo && opt->always_show_header) {
log.parent = NULL;
show_log(opt, opt->loginfo, "");
+ shown = 1;
}
opt->loginfo = NULL;
- return 0;
+ return shown;
}
diff --git a/mailinfo.c b/mailinfo.c
index a133e6d08a..5b6c2157ed 100644
--- a/mailinfo.c
+++ b/mailinfo.c
@@ -72,11 +72,14 @@ static int bogus_from(char *line)
return 1;
}
-static int handle_from(char *line)
+static int handle_from(char *in_line)
{
- char *at = strchr(line, '@');
+ char line[1000];
+ char *at;
char *dst;
+ strcpy(line, in_line);
+ at = strchr(line, '@');
if (!at)
return bogus_from(line);
@@ -238,44 +241,45 @@ static int eatspace(char *line)
#define SEEN_DATE 02
#define SEEN_SUBJECT 04
#define SEEN_BOGUS_UNIX_FROM 010
+#define SEEN_PREFIX 020
/* First lines of body can have From:, Date:, and Subject: */
-static int handle_inbody_header(int *seen, char *line)
+static void handle_inbody_header(int *seen, char *line)
{
if (!memcmp(">From", line, 5) && isspace(line[5])) {
if (!(*seen & SEEN_BOGUS_UNIX_FROM)) {
*seen |= SEEN_BOGUS_UNIX_FROM;
- return 1;
+ return;
}
}
if (!memcmp("From:", line, 5) && isspace(line[5])) {
if (!(*seen & SEEN_FROM) && handle_from(line+6)) {
*seen |= SEEN_FROM;
- return 1;
+ return;
}
}
if (!memcmp("Date:", line, 5) && isspace(line[5])) {
if (!(*seen & SEEN_DATE)) {
handle_date(line+6);
*seen |= SEEN_DATE;
- return 1;
+ return;
}
}
if (!memcmp("Subject:", line, 8) && isspace(line[8])) {
if (!(*seen & SEEN_SUBJECT)) {
handle_subject(line+9);
*seen |= SEEN_SUBJECT;
- return 1;
+ return;
}
}
if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
if (!(*seen & SEEN_SUBJECT)) {
handle_subject(line);
*seen |= SEEN_SUBJECT;
- return 1;
+ return;
}
}
- return 0;
+ *seen |= SEEN_PREFIX;
}
static char *cleanup_subject(char *subject)
@@ -331,6 +335,7 @@ static void cleanup_space(char *buf)
}
}
+static void decode_header_bq(char *it);
typedef int (*header_fn_t)(char *);
struct header_def {
const char *name;
@@ -338,7 +343,7 @@ struct header_def {
int namelen;
};
-static void check_header(char *line, int len, struct header_def *header)
+static void check_header(char *line, struct header_def *header)
{
int i;
@@ -350,13 +355,17 @@ static void check_header(char *line, int len, struct header_def *header)
int len = header[i].namelen;
if (!strncasecmp(line, header[i].name, len) &&
line[len] == ':' && isspace(line[len + 1])) {
+ /* Unwrap inline B and Q encoding, and optionally
+ * normalize the meta information to utf8.
+ */
+ decode_header_bq(line + len + 2);
header[i].func(line + len + 2);
break;
}
}
}
-static void check_subheader_line(char *line, int len)
+static void check_subheader_line(char *line)
{
static struct header_def header[] = {
{ "Content-Type", handle_subcontent_type },
@@ -364,9 +373,9 @@ static void check_subheader_line(char *line, int len)
handle_content_transfer_encoding },
{ NULL },
};
- check_header(line, len, header);
+ check_header(line, header);
}
-static void check_header_line(char *line, int len)
+static void check_header_line(char *line)
{
static struct header_def header[] = {
{ "From", handle_from },
@@ -377,7 +386,30 @@ static void check_header_line(char *line, int len)
handle_content_transfer_encoding },
{ NULL },
};
- check_header(line, len, header);
+ check_header(line, header);
+}
+
+static int is_rfc2822_header(char *line)
+{
+ /*
+ * The section that defines the loosest possible
+ * field name is "3.6.8 Optional fields".
+ *
+ * optional-field = field-name ":" unstructured CRLF
+ * field-name = 1*ftext
+ * ftext = %d33-57 / %59-126
+ */
+ int ch;
+ char *cp = line;
+ while ((ch = *cp++)) {
+ if (ch == ':')
+ return cp != line;
+ if ((33 <= ch && ch <= 57) ||
+ (59 <= ch && ch <= 126))
+ continue;
+ break;
+ }
+ return 0;
}
static int read_one_header_line(char *line, int sz, FILE *in)
@@ -386,18 +418,25 @@ static int read_one_header_line(char *line, int sz, FILE *in)
while (ofs < sz) {
int peek, len;
if (fgets(line + ofs, sz - ofs, in) == NULL)
- return ofs;
+ break;
len = eatspace(line + ofs);
if (len == 0)
- return ofs;
- peek = fgetc(in); ungetc(peek, in);
- if (peek == ' ' || peek == '\t') {
- /* Yuck, 2822 header "folding" */
- ofs += len;
- continue;
+ break;
+ if (!is_rfc2822_header(line)) {
+ /* Re-add the newline */
+ line[ofs + len] = '\n';
+ line[ofs + len + 1] = '\0';
+ break;
}
- return ofs + len;
+ ofs += len;
+ /* Yuck, 2822 header "folding" */
+ peek = fgetc(in); ungetc(peek, in);
+ if (peek != ' ' && peek != '\t')
+ break;
}
+ /* Count mbox From headers as headers */
+ if (!ofs && !memcmp(line, "From ", 5))
+ ofs = 1;
return ofs;
}
@@ -592,25 +631,13 @@ static void decode_transfer_encoding(char *line)
static void handle_info(void)
{
char *sub;
- static int done_info = 0;
-
- if (done_info)
- return;
- done_info = 1;
sub = cleanup_subject(subject);
cleanup_space(name);
cleanup_space(date);
cleanup_space(email);
cleanup_space(sub);
- /* Unwrap inline B and Q encoding, and optionally
- * normalize the meta information to utf8.
- */
- decode_header_bq(name);
- decode_header_bq(date);
- decode_header_bq(email);
- decode_header_bq(sub);
printf("Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n",
name, email, sub, date);
}
@@ -618,7 +645,7 @@ static void handle_info(void)
/* We are inside message body and have read line[] already.
* Spit out the commit log.
*/
-static int handle_commit_msg(void)
+static int handle_commit_msg(int *seen)
{
if (!cmitmsg)
return 0;
@@ -642,6 +669,11 @@ static int handle_commit_msg(void)
decode_transfer_encoding(line);
if (metainfo_charset)
convert_to_utf8(line, charset);
+
+ handle_inbody_header(seen, line);
+ if (!(*seen & SEEN_PREFIX))
+ continue;
+
fputs(line, cmitmsg);
} while (fgets(line, sizeof(line), stdin) != NULL);
fclose(cmitmsg);
@@ -673,26 +705,16 @@ static void handle_patch(void)
* that the first part to contain commit message and a patch, and
* handle other parts as pure patches.
*/
-static int handle_multipart_one_part(void)
+static int handle_multipart_one_part(int *seen)
{
- int seen = 0;
int n = 0;
- int len;
while (fgets(line, sizeof(line), stdin) != NULL) {
again:
- len = eatspace(line);
n++;
- if (!len)
- continue;
if (is_multipart_boundary(line))
break;
- if (0 <= seen && handle_inbody_header(&seen, line))
- continue;
- seen = -1; /* no more inbody headers */
- line[len] = '\n';
- handle_info();
- if (handle_commit_msg())
+ if (handle_commit_msg(seen))
goto again;
handle_patch();
break;
@@ -704,6 +726,7 @@ static int handle_multipart_one_part(void)
static void handle_multipart_body(void)
{
+ int seen = 0;
int part_num = 0;
/* Skip up to the first boundary */
@@ -716,16 +739,16 @@ static void handle_multipart_body(void)
return;
/* We are on boundary line. Start slurping the subhead. */
while (1) {
- int len = read_one_header_line(line, sizeof(line), stdin);
- if (!len) {
- if (handle_multipart_one_part() < 0)
+ int hdr = read_one_header_line(line, sizeof(line), stdin);
+ if (!hdr) {
+ if (handle_multipart_one_part(&seen) < 0)
return;
/* Reset per part headers */
transfer_encoding = TE_DONTCARE;
charset[0] = 0;
}
else
- check_subheader_line(line, len);
+ check_subheader_line(line);
}
fclose(patchfile);
if (!patch_lines) {
@@ -739,18 +762,9 @@ static void handle_body(void)
{
int seen = 0;
- while (fgets(line, sizeof(line), stdin) != NULL) {
- int len = eatspace(line);
- if (!len)
- continue;
- if (0 <= seen && handle_inbody_header(&seen, line))
- continue;
- seen = -1; /* no more inbody headers */
- line[len] = '\n';
- handle_info();
- handle_commit_msg();
+ if (line[0] || fgets(line, sizeof(line), stdin) != NULL) {
+ handle_commit_msg(&seen);
handle_patch();
- break;
}
fclose(patchfile);
if (!patch_lines) {
@@ -794,15 +808,16 @@ int main(int argc, char **argv)
exit(1);
}
while (1) {
- int len = read_one_header_line(line, sizeof(line), stdin);
- if (!len) {
+ int hdr = read_one_header_line(line, sizeof(line), stdin);
+ if (!hdr) {
if (multipart_boundary[0])
handle_multipart_body();
else
handle_body();
+ handle_info();
break;
}
- check_header_line(line, len);
+ check_header_line(line);
}
return 0;
}
diff --git a/mktag.c b/mktag.c
index 23288781cf..f0fe5285b2 100644
--- a/mktag.c
+++ b/mktag.c
@@ -45,42 +45,46 @@ static int verify_tag(char *buffer, unsigned long size)
unsigned char sha1[20];
const char *object, *type_line, *tag_line, *tagger_line;
- if (size < 64 || size > MAXSIZE-1)
- return -1;
+ if (size < 64)
+ return error("wanna fool me ? you obviously got the size wrong !\n");
+
buffer[size] = 0;
/* Verify object line */
object = buffer;
if (memcmp(object, "object ", 7))
- return -1;
+ return error("char%d: does not start with \"object \"\n", 0);
+
if (get_sha1_hex(object + 7, sha1))
- return -1;
+ return error("char%d: could not get SHA1 hash\n", 7);
/* Verify type line */
type_line = object + 48;
if (memcmp(type_line - 1, "\ntype ", 6))
- return -1;
+ return error("char%d: could not find \"\\ntype \"\n", 47);
/* Verify tag-line */
tag_line = strchr(type_line, '\n');
if (!tag_line)
- return -1;
+ return error("char%td: could not find next \"\\n\"\n", type_line - buffer);
tag_line++;
if (memcmp(tag_line, "tag ", 4) || tag_line[4] == '\n')
- return -1;
+ return error("char%td: no \"tag \" found\n", tag_line - buffer);
/* Get the actual type */
typelen = tag_line - type_line - strlen("type \n");
if (typelen >= sizeof(type))
- return -1;
+ return error("char%td: type too long\n", type_line+5 - buffer);
+
memcpy(type, type_line+5, typelen);
type[typelen] = 0;
/* Verify that the object matches */
if (get_sha1_hex(object + 7, sha1))
- return -1;
+ return error("char%d: could not get SHA1 hash but this is really odd since i got it before !\n", 7);
+
if (verify_object(sha1, type))
- return -1;
+ return error("char%d: could not verify object %s\n", 7, sha1);
/* Verify the tag-name: we don't allow control characters or spaces in it */
tag_line += 4;
@@ -90,14 +94,14 @@ static int verify_tag(char *buffer, unsigned long size)
break;
if (c > ' ')
continue;
- return -1;
+ return error("char%td: could not verify tag name\n", tag_line - buffer);
}
/* Verify the tagger line */
tagger_line = tag_line;
if (memcmp(tagger_line, "tagger", 6) || (tagger_line[6] == '\n'))
- return -1;
+ return error("char%td: could not find \"tagger\"\n", tagger_line - buffer);
/* The actual stuff afterwards we don't care about.. */
return 0;
@@ -105,8 +109,8 @@ static int verify_tag(char *buffer, unsigned long size)
int main(int argc, char **argv)
{
- unsigned long size;
- char buffer[MAXSIZE];
+ unsigned long size = 4096;
+ char *buffer = malloc(size);
unsigned char result_sha1[20];
if (argc != 1)
@@ -114,13 +118,9 @@ int main(int argc, char **argv)
setup_git_directory();
- // Read the signature
- size = 0;
- for (;;) {
- int ret = xread(0, buffer + size, MAXSIZE - size);
- if (ret <= 0)
- break;
- size += ret;
+ if (read_pipe(0, &buffer, &size)) {
+ free(buffer);
+ die("could not read from stdin");
}
// Verify it for some basic sanity: it needs to start with "object <sha1>\ntype\ntagger "
@@ -129,6 +129,9 @@ int main(int argc, char **argv)
if (write_sha1_file(buffer, size, tag_type, result_sha1) < 0)
die("unable to write tag file");
+
+ free(buffer);
+
printf("%s\n", sha1_to_hex(result_sha1));
return 0;
}
diff --git a/read-cache.c b/read-cache.c
index b95edcc14c..32ba917923 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -332,6 +332,70 @@ int ce_path_match(const struct cache_entry *ce, const char **pathspec)
}
/*
+ * We fundamentally don't like some paths: we don't want
+ * dot or dot-dot anywhere, and for obvious reasons don't
+ * want to recurse into ".git" either.
+ *
+ * Also, we don't want double slashes or slashes at the
+ * end that can make pathnames ambiguous.
+ */
+static int verify_dotfile(const char *rest)
+{
+ /*
+ * The first character was '.', but that
+ * has already been discarded, we now test
+ * the rest.
+ */
+ switch (*rest) {
+ /* "." is not allowed */
+ case '\0': case '/':
+ return 0;
+
+ /*
+ * ".git" followed by NUL or slash is bad. This
+ * shares the path end test with the ".." case.
+ */
+ case 'g':
+ if (rest[1] != 'i')
+ break;
+ if (rest[2] != 't')
+ break;
+ rest += 2;
+ /* fallthrough */
+ case '.':
+ if (rest[1] == '\0' || rest[1] == '/')
+ return 0;
+ }
+ return 1;
+}
+
+int verify_path(const char *path)
+{
+ char c;
+
+ goto inside;
+ for (;;) {
+ if (!c)
+ return 1;
+ if (c == '/') {
+inside:
+ c = *path++;
+ switch (c) {
+ default:
+ continue;
+ case '/': case '\0':
+ break;
+ case '.':
+ if (verify_dotfile(path))
+ continue;
+ }
+ return 0;
+ }
+ c = *path++;
+ }
+}
+
+/*
* Do we have another file that has the beginning components being a
* proper superset of the name we're trying to add?
*/
@@ -472,6 +536,8 @@ int add_cache_entry(struct cache_entry *ce, int option)
if (!ok_to_add)
return -1;
+ if (!verify_path(ce->name))
+ return -1;
if (!skip_df_check &&
check_file_directory_conflict(ce, pos, ok_to_replace)) {
diff --git a/repo-config.c b/repo-config.c
index 127afd784c..08fc4cc57d 100644
--- a/repo-config.c
+++ b/repo-config.c
@@ -108,7 +108,8 @@ static int get_value(const char* key_, const char* regex_)
int main(int argc, const char **argv)
{
- setup_git_directory();
+ int nongit = 0;
+ setup_git_directory_gently(&nongit);
while (1 < argc) {
if (!strcmp(argv[1], "--int"))
diff --git a/revision.c b/revision.c
index 2294b16ea2..42c077a4cb 100644
--- a/revision.c
+++ b/revision.c
@@ -733,6 +733,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
revs->abbrev = DEFAULT_ABBREV;
continue;
}
+ if (!strncmp(arg, "--abbrev=", 9)) {
+ revs->abbrev = strtoul(arg + 9, NULL, 10);
+ if (revs->abbrev < MINIMUM_ABBREV)
+ revs->abbrev = MINIMUM_ABBREV;
+ else if (revs->abbrev > 40)
+ revs->abbrev = 40;
+ continue;
+ }
if (!strcmp(arg, "--abbrev-commit")) {
revs->abbrev_commit = 1;
continue;
diff --git a/revision.h b/revision.h
index 48d7b4ca94..bdbdd235d8 100644
--- a/revision.h
+++ b/revision.h
@@ -58,6 +58,8 @@ struct rev_info {
unsigned int abbrev;
enum cmit_fmt commit_format;
struct log_info *loginfo;
+ int nr, total;
+ const char *mime_boundary;
/* special limits */
int max_count;
diff --git a/sha1_file.c b/sha1_file.c
index 223001033c..f77c18934a 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -1399,6 +1399,25 @@ int move_temp_to_file(const char *tmpfile, char *filename)
return 0;
}
+static int write_buffer(int fd, const void *buf, size_t len)
+{
+ while (len) {
+ ssize_t size;
+
+ size = write(fd, buf, len);
+ if (!size)
+ return error("file write: disk full");
+ if (size < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return error("file write error (%s)", strerror(errno));
+ }
+ len -= size;
+ buf += size;
+ }
+ return 0;
+}
+
int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
{
int size;
@@ -1465,8 +1484,8 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
deflateEnd(&stream);
size = stream.total_out;
- if (write(fd, compressed, size) != size)
- die("unable to write file");
+ if (write_buffer(fd, compressed, size) < 0)
+ die("unable to write sha1 file");
fchmod(fd, 0444);
close(fd);
free(compressed);
@@ -1474,73 +1493,70 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
return move_temp_to_file(tmpfile, filename);
}
-int write_sha1_to_fd(int fd, const unsigned char *sha1)
+/*
+ * We need to unpack and recompress the object for writing
+ * it out to a different file.
+ */
+static void *repack_object(const unsigned char *sha1, unsigned long *objsize)
{
- ssize_t size;
- unsigned long objsize;
- int posn = 0;
- void *map = map_sha1_file_internal(sha1, &objsize);
- void *buf = map;
- void *temp_obj = NULL;
+ size_t size;
z_stream stream;
+ unsigned char *unpacked;
+ unsigned long len;
+ char type[20];
+ char hdr[50];
+ int hdrlen;
+ void *buf;
- if (!buf) {
- unsigned char *unpacked;
- unsigned long len;
- char type[20];
- char hdr[50];
- int hdrlen;
- // need to unpack and recompress it by itself
- unpacked = read_packed_sha1(sha1, type, &len);
+ // need to unpack and recompress it by itself
+ unpacked = read_packed_sha1(sha1, type, &len);
- hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;
+ hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;
- /* Set it up */
- memset(&stream, 0, sizeof(stream));
- deflateInit(&stream, Z_BEST_COMPRESSION);
- size = deflateBound(&stream, len + hdrlen);
- temp_obj = buf = xmalloc(size);
-
- /* Compress it */
- stream.next_out = buf;
- stream.avail_out = size;
-
- /* First header.. */
- stream.next_in = (void *)hdr;
- stream.avail_in = hdrlen;
- while (deflate(&stream, 0) == Z_OK)
- /* nothing */;
+ /* Set it up */
+ memset(&stream, 0, sizeof(stream));
+ deflateInit(&stream, Z_BEST_COMPRESSION);
+ size = deflateBound(&stream, len + hdrlen);
+ buf = xmalloc(size);
- /* Then the data itself.. */
- stream.next_in = unpacked;
- stream.avail_in = len;
- while (deflate(&stream, Z_FINISH) == Z_OK)
- /* nothing */;
- deflateEnd(&stream);
- free(unpacked);
-
- objsize = stream.total_out;
- }
+ /* Compress it */
+ stream.next_out = buf;
+ stream.avail_out = size;
- do {
- size = write(fd, buf + posn, objsize - posn);
- if (size <= 0) {
- if (!size) {
- fprintf(stderr, "write closed\n");
- } else {
- perror("write ");
- }
- return -1;
- }
- posn += size;
- } while (posn < objsize);
+ /* First header.. */
+ stream.next_in = (void *)hdr;
+ stream.avail_in = hdrlen;
+ while (deflate(&stream, 0) == Z_OK)
+ /* nothing */;
- if (map)
- munmap(map, objsize);
- if (temp_obj)
- free(temp_obj);
+ /* Then the data itself.. */
+ stream.next_in = unpacked;
+ stream.avail_in = len;
+ while (deflate(&stream, Z_FINISH) == Z_OK)
+ /* nothing */;
+ deflateEnd(&stream);
+ free(unpacked);
- return 0;
+ *objsize = stream.total_out;
+ return buf;
+}
+
+int write_sha1_to_fd(int fd, const unsigned char *sha1)
+{
+ int retval;
+ unsigned long objsize;
+ void *buf = map_sha1_file_internal(sha1, &objsize);
+
+ if (buf) {
+ retval = write_buffer(fd, buf, objsize);
+ munmap(buf, objsize);
+ return retval;
+ }
+
+ buf = repack_object(sha1, &objsize);
+ retval = write_buffer(fd, buf, objsize);
+ free(buf);
+ return retval;
}
int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
@@ -1579,7 +1595,8 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
SHA1_Update(&c, discard, sizeof(discard) -
stream.avail_out);
} while (stream.avail_in && ret == Z_OK);
- write(local, buffer, *bufposn - stream.avail_in);
+ if (write_buffer(local, buffer, *bufposn - stream.avail_in) < 0)
+ die("unable to write sha1 file");
memmove(buffer, buffer + *bufposn - stream.avail_in,
stream.avail_in);
*bufposn = stream.avail_in;
@@ -1645,16 +1662,24 @@ int has_sha1_file(const unsigned char *sha1)
return find_sha1_file(sha1, &st) ? 1 : 0;
}
-int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
+/*
+ * reads from fd as long as possible into a supplied buffer of size bytes.
+ * If neccessary the buffer's size is increased using realloc()
+ *
+ * returns 0 if anything went fine and -1 otherwise
+ *
+ * NOTE: both buf and size may change, but even when -1 is returned
+ * you still have to free() it yourself.
+ */
+int read_pipe(int fd, char** return_buf, unsigned long* return_size)
{
- unsigned long size = 4096;
- char *buf = malloc(size);
- int iret, ret;
+ char* buf = *return_buf;
+ unsigned long size = *return_size;
+ int iret;
unsigned long off = 0;
- unsigned char hdr[50];
- int hdrlen;
+
do {
- iret = read(fd, buf + off, size - off);
+ iret = xread(fd, buf + off, size - off);
if (iret > 0) {
off += iret;
if (off == size) {
@@ -1663,16 +1688,34 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
}
}
} while (iret > 0);
- if (iret < 0) {
+
+ *return_buf = buf;
+ *return_size = off;
+
+ if (iret < 0)
+ return -1;
+ return 0;
+}
+
+int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
+{
+ unsigned long size = 4096;
+ char *buf = malloc(size);
+ int ret;
+ unsigned char hdr[50];
+ int hdrlen;
+
+ if (read_pipe(fd, &buf, &size)) {
free(buf);
return -1;
}
+
if (!type)
type = blob_type;
if (write_object)
- ret = write_sha1_file(buf, off, type, sha1);
+ ret = write_sha1_file(buf, size, type, sha1);
else {
- write_sha1_file_prepare(buf, off, type, sha1, hdr, &hdrlen);
+ write_sha1_file_prepare(buf, size, type, sha1, hdr, &hdrlen);
ret = 0;
}
free(buf);
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
index 8335a63e2e..da3c81357b 100755
--- a/t/t1002-read-tree-m-u-2way.sh
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -75,7 +75,7 @@ test_expect_success \
git-update-index --add yomin &&
git-read-tree -m -u $treeH $treeM &&
git-ls-files --stage >4.out || return 1
- diff --unified=0 M.out 4.out >4diff.out
+ diff -U0 M.out 4.out >4diff.out
compare_change 4diff.out expected &&
check_cache_at yomin clean &&
sum bozbar frotz nitfol >actual4.sum &&
@@ -94,7 +94,7 @@ test_expect_success \
echo yomin yomin >yomin &&
git-read-tree -m -u $treeH $treeM &&
git-ls-files --stage >5.out || return 1
- diff --unified=0 M.out 5.out >5diff.out
+ diff -U0 M.out 5.out >5diff.out
compare_change 5diff.out expected &&
check_cache_at yomin dirty &&
sum bozbar frotz nitfol >actual5.sum &&
@@ -112,7 +112,7 @@ test_expect_success \
git-update-index --add frotz &&
git-read-tree -m -u $treeH $treeM &&
git-ls-files --stage >6.out &&
- diff --unified=0 M.out 6.out &&
+ diff -U0 M.out 6.out &&
check_cache_at frotz clean &&
sum bozbar frotz nitfol >actual3.sum &&
cmp M.sum actual3.sum &&
@@ -129,7 +129,7 @@ test_expect_success \
echo frotz frotz >frotz &&
git-read-tree -m -u $treeH $treeM &&
git-ls-files --stage >7.out &&
- diff --unified=0 M.out 7.out &&
+ diff -U0 M.out 7.out &&
check_cache_at frotz dirty &&
sum bozbar frotz nitfol >actual7.sum &&
if cmp M.sum actual7.sum; then false; else :; fi &&
@@ -206,7 +206,7 @@ test_expect_success \
git-update-index --add nitfol &&
git-read-tree -m -u $treeH $treeM &&
git-ls-files --stage >14.out || return 1
- diff --unified=0 M.out 14.out >14diff.out
+ diff -U0 M.out 14.out >14diff.out
compare_change 14diff.out expected &&
sum bozbar frotz >actual14.sum &&
grep -v nitfol M.sum > expected14.sum &&
@@ -227,7 +227,7 @@ test_expect_success \
echo nitfol nitfol nitfol >nitfol &&
git-read-tree -m -u $treeH $treeM &&
git-ls-files --stage >15.out || return 1
- diff --unified=0 M.out 15.out >15diff.out
+ diff -U0 M.out 15.out >15diff.out
compare_change 15diff.out expected &&
check_cache_at nitfol dirty &&
sum bozbar frotz >actual15.sum &&
@@ -264,7 +264,7 @@ test_expect_success \
git-update-index --add bozbar &&
git-read-tree -m -u $treeH $treeM &&
git-ls-files --stage >18.out &&
- diff --unified=0 M.out 18.out &&
+ diff -U0 M.out 18.out &&
check_cache_at bozbar clean &&
sum bozbar frotz nitfol >actual18.sum &&
cmp M.sum actual18.sum'
@@ -278,7 +278,7 @@ test_expect_success \
echo gnusto gnusto >bozbar &&
git-read-tree -m -u $treeH $treeM &&
git-ls-files --stage >19.out &&
- diff --unified=0 M.out 19.out &&
+ diff -U0 M.out 19.out &&
check_cache_at bozbar dirty &&
sum frotz nitfol >actual19.sum &&
grep -v bozbar M.sum > expected19.sum &&
@@ -297,7 +297,7 @@ test_expect_success \
git-update-index --add bozbar &&
git-read-tree -m -u $treeH $treeM &&
git-ls-files --stage >20.out &&
- diff --unified=0 M.out 20.out &&
+ diff -U0 M.out 20.out &&
check_cache_at bozbar clean &&
sum bozbar frotz nitfol >actual20.sum &&
cmp M.sum actual20.sum'
@@ -338,7 +338,7 @@ test_expect_success \
git-update-index --add DF &&
git-read-tree -m -u $treeDF $treeDFDF &&
git-ls-files --stage >DFDFcheck.out &&
- diff --unified=0 DFDF.out DFDFcheck.out &&
+ diff -U0 DFDF.out DFDFcheck.out &&
check_cache_at DF/DF clean'
test_done
diff --git a/update-index.c b/update-index.c
index 7d6de821e2..5854d11fd9 100644
--- a/update-index.c
+++ b/update-index.c
@@ -120,70 +120,6 @@ static int add_file_to_cache(const char *path)
return 0;
}
-/*
- * We fundamentally don't like some paths: we don't want
- * dot or dot-dot anywhere, and for obvious reasons don't
- * want to recurse into ".git" either.
- *
- * Also, we don't want double slashes or slashes at the
- * end that can make pathnames ambiguous.
- */
-static int verify_dotfile(const char *rest)
-{
- /*
- * The first character was '.', but that
- * has already been discarded, we now test
- * the rest.
- */
- switch (*rest) {
- /* "." is not allowed */
- case '\0': case '/':
- return 0;
-
- /*
- * ".git" followed by NUL or slash is bad. This
- * shares the path end test with the ".." case.
- */
- case 'g':
- if (rest[1] != 'i')
- break;
- if (rest[2] != 't')
- break;
- rest += 2;
- /* fallthrough */
- case '.':
- if (rest[1] == '\0' || rest[1] == '/')
- return 0;
- }
- return 1;
-}
-
-static int verify_path(const char *path)
-{
- char c;
-
- goto inside;
- for (;;) {
- if (!c)
- return 1;
- if (c == '/') {
-inside:
- c = *path++;
- switch (c) {
- default:
- continue;
- case '/': case '\0':
- break;
- case '.':
- if (verify_dotfile(path))
- continue;
- }
- return 0;
- }
- c = *path++;
- }
-}
-
static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
const char *path, int stage)
{