aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <junkio@cox.net>2006-09-21 00:29:37 -0700
committerJunio C Hamano <junkio@cox.net>2006-09-21 00:29:37 -0700
commit340adb8bccf48b9d933f0608fd99c75a5c28003c (patch)
tree7f56898365b94fcac608f3d25788fd864570d2ca
parent9f613ddd21cbd05bfc139d9b1551b5780aa171f6 (diff)
parent968846015229fe0fec28e3c85db722e131c15f93 (diff)
downloadgit-340adb8bccf48b9d933f0608fd99c75a5c28003c.tar.gz
git-340adb8bccf48b9d933f0608fd99c75a5c28003c.tar.xz
Merge branch 'lt/refs' into jc/for-each-ref-with-lt-refs
* lt/refs: (58 commits) git-pack-refs --prune pack-refs: do not pack symbolic refs. Tell between packed, unpacked and symbolic refs. Add callback data to for_each_ref() family. symbolit-ref: fix resolve_ref conversion. Fix broken sha1 locking fsck-objects: adjust to resolve_ref() clean-up. gitignore: git-pack-refs is a generated file. wt-status: use simplified resolve_ref to find current branch Fix t1400-update-ref test minimally Enable the packed refs file format Make ref resolution saner Add support for negative refs Start handling references internally as a sorted in-memory list gitweb fix validating pg (page) parameter git-repack(1): document --window and --depth git-apply(1): document --unidiff-zero gitweb: fix warnings in PATH_INFO code and add export_ok/strict_export upload-archive: monitor child communication even more carefully. gitweb: export options ...
-rw-r--r--.gitignore4
-rw-r--r--Documentation/config.txt14
-rw-r--r--Documentation/git-apply.txt10
-rw-r--r--Documentation/git-archive.txt100
-rw-r--r--Documentation/git-pack-objects.txt21
-rw-r--r--Documentation/git-repack.txt12
-rw-r--r--Documentation/git-upload-archive.txt37
-rw-r--r--Makefile21
-rw-r--r--archive.h47
-rw-r--r--builtin-apply.c112
-rw-r--r--builtin-archive.c263
-rw-r--r--builtin-count-objects.c2
-rw-r--r--builtin-fmt-merge-msg.c7
-rw-r--r--builtin-for-each-ref.c43
-rw-r--r--builtin-init-db.c4
-rw-r--r--builtin-name-rev.c8
-rw-r--r--builtin-pack-objects.c278
-rw-r--r--builtin-pack-refs.c112
-rw-r--r--builtin-prune-packed.c2
-rw-r--r--builtin-prune.c4
-rw-r--r--builtin-push.c4
-rw-r--r--builtin-rev-list.c140
-rw-r--r--builtin-rev-parse.c10
-rw-r--r--builtin-runstatus.c36
-rw-r--r--builtin-show-branch.c72
-rw-r--r--builtin-symbolic-ref.c18
-rw-r--r--builtin-tar-tree.c155
-rw-r--r--builtin-upload-archive.c175
-rw-r--r--builtin-zip-tree.c45
-rw-r--r--builtin.h4
-rw-r--r--cache.h8
-rw-r--r--color.c176
-rw-r--r--color.h12
-rw-r--r--daemon.c7
-rw-r--r--describe.c4
-rw-r--r--diff.c139
-rw-r--r--diff.h8
-rw-r--r--dir.c7
-rw-r--r--dir.h1
-rw-r--r--fetch-clone.c32
-rw-r--r--fetch-pack.c20
-rw-r--r--fetch.c4
-rw-r--r--fsck-objects.c15
-rwxr-xr-xgenerate-cmdlist.sh1
-rwxr-xr-xgit-am.sh23
-rwxr-xr-xgit-checkout.sh4
-rwxr-xr-xgit-commit.sh107
-rw-r--r--git.c4
-rwxr-xr-xgitweb/gitweb.perl98
-rw-r--r--http-fetch.c25
-rw-r--r--http-push.c4
-rw-r--r--list-objects.c140
-rw-r--r--list-objects.h12
-rw-r--r--receive-pack.c6
-rw-r--r--refs.c361
-rw-r--r--refs.h15
-rw-r--r--revision.c28
-rw-r--r--revision.h6
-rw-r--r--send-pack.c4
-rw-r--r--server-info.c4
-rw-r--r--sha1_file.c26
-rw-r--r--sha1_name.c14
-rw-r--r--sideband.c74
-rw-r--r--sideband.h13
-rwxr-xr-xt/t1400-update-ref.sh89
-rwxr-xr-xt/t3403-rebase-skip.sh4
-rwxr-xr-xt/t4104-apply-boundary.sh115
-rw-r--r--upload-pack.c88
-rw-r--r--wt-status.c274
-rw-r--r--wt-status.h25
70 files changed, 2786 insertions, 971 deletions
diff --git a/.gitignore b/.gitignore
index 0b08f3762..688a502d9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ git-apply
git-applymbox
git-applypatch
git-archimport
+git-archive
git-bisect
git-branch
git-cat-file
@@ -73,6 +74,7 @@ git-name-rev
git-mv
git-pack-redundant
git-pack-objects
+git-pack-refs
git-parse-remote
git-patch-id
git-peek-remote
@@ -95,6 +97,7 @@ git-rev-list
git-rev-parse
git-revert
git-rm
+git-runstatus
git-send-email
git-send-pack
git-sh-setup
@@ -119,6 +122,7 @@ git-unpack-objects
git-update-index
git-update-ref
git-update-server-info
+git-upload-archive
git-upload-pack
git-upload-tar
git-var
diff --git a/Documentation/config.txt b/Documentation/config.txt
index ce722a2db..844cae4cf 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -225,6 +225,20 @@ showbranch.default::
The default set of branches for gitlink:git-show-branch[1].
See gitlink:git-show-branch[1].
+status.color::
+ A boolean to enable/disable color in the output of
+ gitlink:git-status[1]. May be set to `true` (or `always`),
+ `false` (or `never`) or `auto`, in which case colors are used
+ only when the output is to a terminal. Defaults to false.
+
+status.color.<slot>::
+ Use customized color for status colorization. `<slot>` is
+ one of `header` (the header text of the status message),
+ `updated` (files which are updated but not committed),
+ `changed` (files which are changed but not updated in the index),
+ or `untracked` (files which are not tracked by git). The values of
+ these variables may be specified as in diff.color.<slot>.
+
tar.umask::
By default, gitlink:git-tar-tree[1] sets file and directories modes
to 0666 or 0777. While this is both useful and acceptable for projects
diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
index 0a6f7b321..d9137c748 100644
--- a/Documentation/git-apply.txt
+++ b/Documentation/git-apply.txt
@@ -95,6 +95,16 @@ OPTIONS
context exist they all must match. By default no context is
ever ignored.
+--unidiff-zero::
+ By default, gitlink:git-apply[1] expects that the patch being
+ applied is a unified diff with at least one line of context.
+ This provides good safety measures, but breaks down when
+ applying a diff generated with --unified=0. To bypass these
+ checks use '--unidiff-zero'.
++
+Note, for the reasons stated above usage of context-free patches are
+discouraged.
+
--apply::
If you use any of the options marked "Turns off
'apply'" above, gitlink:git-apply[1] reads and outputs the
diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt
new file mode 100644
index 000000000..913528d37
--- /dev/null
+++ b/Documentation/git-archive.txt
@@ -0,0 +1,100 @@
+git-archive(1)
+==============
+
+NAME
+----
+git-archive - Creates a archive of the files in the named tree
+
+
+SYNOPSIS
+--------
+'git-archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>]
+ [--remote=<repo>] <tree-ish> [path...]
+
+DESCRIPTION
+-----------
+Creates an archive of the specified format containing the tree
+structure for the named tree. If <prefix> is specified it is
+prepended to the filenames in the archive.
+
+'git-archive' behaves differently when given a tree ID versus when
+given a commit ID or tag ID. In the first case the current time is
+used as modification time of each file in the archive. In the latter
+case the commit time as recorded in the referenced commit object is
+used instead. Additionally the commit ID is stored in a global
+extended pax header if the tar format is used; it can be extracted
+using 'git-get-tar-commit-id'. In ZIP files it is stored as a file
+comment.
+
+OPTIONS
+-------
+
+--format=<fmt>::
+ Format of the resulting archive: 'tar', 'zip'...
+
+--list::
+ Show all available formats.
+
+--prefix=<prefix>/::
+ Prepend <prefix>/ to each filename in the archive.
+
+<extra>::
+ This can be any options that the archiver backend understand.
+
+--remote=<repo>::
+ Instead of making a tar archive from local repository,
+ retrieve a tar archive from a remote repository.
+
+<tree-ish>::
+ The tree or commit to produce an archive for.
+
+path::
+ If one or more paths are specified, include only these in the
+ archive, otherwise include all files and subdirectories.
+
+CONFIGURATION
+-------------
+By default, file and directories modes are set to 0666 or 0777 in tar
+archives. It is possible to change this by setting the "umask" variable
+in the repository configuration as follows :
+
+[tar]
+ umask = 002 ;# group friendly
+
+The special umask value "user" indicates that the user's current umask
+will be used instead. The default value remains 0, which means world
+readable/writable files and directories.
+
+EXAMPLES
+--------
+git archive --format=tar --prefix=junk/ HEAD | (cd /var/tmp/ && tar xf -)::
+
+ Create a tar archive that contains the contents of the
+ latest commit on the current branch, and extracts it in
+ `/var/tmp/junk` directory.
+
+git archive --format=tar --prefix=git-1.4.0/ v1.4.0 | gzip >git-1.4.0.tar.gz::
+
+ Create a compressed tarball for v1.4.0 release.
+
+git archive --format=tar --prefix=git-1.4.0/ v1.4.0{caret}\{tree\} | gzip >git-1.4.0.tar.gz::
+
+ Create a compressed tarball for v1.4.0 release, but without a
+ global extended pax header.
+
+git archive --format=zip --prefix=git-docs/ HEAD:Documentation/ > git-1.4.0-docs.zip::
+
+ Put everything in the current head's Documentation/ directory
+ into 'git-1.4.0-docs.zip', with the prefix 'git-docs/'.
+
+Author
+------
+Written by Franck Bui-Huu and Rene Scharfe.
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index 4991f88c9..d4661ddc2 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -11,7 +11,7 @@ SYNOPSIS
[verse]
'git-pack-objects' [-q] [--no-reuse-delta] [--non-empty]
[--local] [--incremental] [--window=N] [--depth=N]
- {--stdout | base-name} < object-list
+ [--revs [--unpacked | --all]*] [--stdout | base-name] < object-list
DESCRIPTION
@@ -56,6 +56,24 @@ base-name::
Write the pack contents (what would have been written to
.pack file) out to the standard output.
+--revs::
+ Read the revision arguments from the standard input, instead of
+ individual object names. The revision arguments are processed
+ the same way as gitlink:git-rev-list[1] with `--objects` flag
+ uses its `commit` arguments to build the list of objects it
+ outputs. The objects on the resulting list are packed.
+
+--unpacked::
+ This implies `--revs`. When processing the list of
+ revision arguments read from the standard input, limit
+ the objects packed to those that are not already packed.
+
+--all::
+ This implies `--revs`. In addition to the list of
+ revision arguments read from the standard input, pretend
+ as if all refs under `$GIT_DIR/refs` are specifed to be
+ included.
+
--window and --depth::
These two options affects how the objects contained in
the pack are stored using delta compression. The
@@ -103,6 +121,7 @@ Documentation by Junio C Hamano
See Also
--------
+gitlink:git-rev-list[1]
gitlink:git-repack[1]
gitlink:git-prune-packed[1]
diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
index 951622774..49f7e0a4a 100644
--- a/Documentation/git-repack.txt
+++ b/Documentation/git-repack.txt
@@ -9,7 +9,7 @@ objects into pack files.
SYNOPSIS
--------
-'git-repack' [-a] [-d] [-f] [-l] [-n] [-q]
+'git-repack' [-a] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]
DESCRIPTION
-----------
@@ -56,6 +56,16 @@ OPTIONS
Do not update the server information with
`git update-server-info`.
+--window=[N], --depth=[N]::
+ These two options affects how the objects contained in the pack are
+ stored using delta compression. The objects are first internally
+ sorted by type, size and optionally names and compared against the
+ other objects within `--window` to see if using delta compression saves
+ space. `--depth` limits the maximum delta depth; making it too deep
+ affects the performance on the unpacker side, because delta data needs
+ to be applied that many times to get to the necessary object.
+
+
Author
------
Written by Linus Torvalds <torvalds@osdl.org>
diff --git a/Documentation/git-upload-archive.txt b/Documentation/git-upload-archive.txt
new file mode 100644
index 000000000..388bb53d2
--- /dev/null
+++ b/Documentation/git-upload-archive.txt
@@ -0,0 +1,37 @@
+git-upload-archive(1)
+====================
+
+NAME
+----
+git-upload-archive - Send archive
+
+
+SYNOPSIS
+--------
+'git-upload-archive' <directory>
+
+DESCRIPTION
+-----------
+Invoked by 'git-archive --remote' and sends a generated archive to the
+other end over the git protocol.
+
+This command is usually not invoked directly by the end user. The UI
+for the protocol is on the 'git-archive' side, and the program pair
+is meant to be used to get an archive from a remote repository.
+
+OPTIONS
+-------
+<directory>::
+ The repository to get a tar archive from.
+
+Author
+------
+Written by Franck Bui-Huu.
+
+Documentation
+--------------
+Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Makefile b/Makefile
index f0e2e51c2..54ac74e98 100644
--- a/Makefile
+++ b/Makefile
@@ -126,6 +126,8 @@ GITWEB_CONFIG = gitweb_config.perl
GITWEB_HOME_LINK_STR = projects
GITWEB_SITENAME =
GITWEB_PROJECTROOT = /pub/git
+GITWEB_EXPORT_OK =
+GITWEB_STRICT_EXPORT =
GITWEB_BASE_URL =
GITWEB_LIST =
GITWEB_HOMETEXT = indextext.html
@@ -232,8 +234,8 @@ LIB_FILE=libgit.a
XDIFF_LIB=xdiff/lib.a
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 \
+ archive.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 list-objects.h sideband.h \
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h
@@ -245,17 +247,19 @@ DIFF_OBJS = \
LIB_OBJS = \
blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
date.o diff-delta.o entry.o exec_cmd.o ident.o lockfile.o \
- object.o pack-check.o patch-delta.o path.o pkt-line.o \
+ object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \
quote.o read-cache.o refs.o run-command.o dir.o object-refs.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 \
- write_or_die.o trace.o \
- alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS)
+ write_or_die.o trace.o list-objects.o \
+ alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
+ color.o wt-status.o
BUILTIN_OBJS = \
builtin-add.o \
builtin-apply.o \
+ builtin-archive.o \
builtin-cat-file.o \
builtin-checkout-index.o \
builtin-check-ref-format.o \
@@ -286,6 +290,7 @@ BUILTIN_OBJS = \
builtin-rev-list.o \
builtin-rev-parse.o \
builtin-rm.o \
+ builtin-runstatus.o \
builtin-show-branch.o \
builtin-stripspace.o \
builtin-symbolic-ref.o \
@@ -293,10 +298,12 @@ BUILTIN_OBJS = \
builtin-unpack-objects.o \
builtin-update-index.o \
builtin-update-ref.o \
+ builtin-upload-archive.o \
builtin-upload-tar.o \
builtin-verify-pack.o \
builtin-write-tree.o \
- builtin-zip-tree.o
+ builtin-zip-tree.o \
+ builtin-pack-refs.o
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
LIBS = $(GITLIBS) -lz
@@ -632,6 +639,8 @@ gitweb/gitweb.cgi: gitweb/gitweb.perl
-e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
-e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
-e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
+ -e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \
+ -e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \
-e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \
-e 's|++GITWEB_LIST++|$(GITWEB_LIST)|g' \
-e 's|++GITWEB_HOMETEXT++|$(GITWEB_HOMETEXT)|g' \
diff --git a/archive.h b/archive.h
new file mode 100644
index 000000000..16dcdb875
--- /dev/null
+++ b/archive.h
@@ -0,0 +1,47 @@
+#ifndef ARCHIVE_H
+#define ARCHIVE_H
+
+#define MAX_EXTRA_ARGS 32
+#define MAX_ARGS (MAX_EXTRA_ARGS + 32)
+
+struct archiver_args {
+ const char *base;
+ struct tree *tree;
+ const unsigned char *commit_sha1;
+ time_t time;
+ const char **pathspec;
+ unsigned int verbose : 1;
+ void *extra;
+};
+
+typedef int (*write_archive_fn_t)(struct archiver_args *);
+
+typedef void *(*parse_extra_args_fn_t)(int argc, const char **argv);
+
+struct archiver {
+ const char *name;
+ struct archiver_args args;
+ write_archive_fn_t write_archive;
+ parse_extra_args_fn_t parse_extra;
+};
+
+extern struct archiver archivers[];
+
+extern int parse_archive_args(int argc,
+ const char **argv,
+ struct archiver *ar);
+
+extern void parse_treeish_arg(const char **treeish,
+ struct archiver_args *ar_args,
+ const char *prefix);
+
+extern void parse_pathspec_arg(const char **pathspec,
+ struct archiver_args *args);
+/*
+ * Archive-format specific backends.
+ */
+extern int write_tar_archive(struct archiver_args *);
+extern int write_zip_archive(struct archiver_args *);
+extern void *parse_extra_zip_args(int argc, const char **argv);
+
+#endif /* ARCHIVE_H */
diff --git a/builtin-apply.c b/builtin-apply.c
index 6e0864ce2..25e90d8d2 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -27,6 +27,7 @@ static const char *prefix;
static int prefix_length = -1;
static int newfd = -1;
+static int unidiff_zero;
static int p_value = 1;
static int check_index;
static int write_index;
@@ -854,11 +855,10 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
}
/*
- * Parse a unified diff. Note that this really needs
- * to parse each fragment separately, since the only
- * way to know the difference between a "---" that is
- * part of a patch, and a "---" that starts the next
- * patch is to look at the line counts..
+ * Parse a unified diff. Note that this really needs to parse each
+ * fragment separately, since the only way to know the difference
+ * between a "---" that is part of a patch, and a "---" that starts
+ * the next patch is to look at the line counts..
*/
static int parse_fragment(char *line, unsigned long size, struct patch *patch, struct fragment *fragment)
{
@@ -875,31 +875,14 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
leading = 0;
trailing = 0;
- if (patch->is_new < 0) {
- patch->is_new = !oldlines;
- if (!oldlines)
- patch->old_name = NULL;
- }
- if (patch->is_delete < 0) {
- patch->is_delete = !newlines;
- if (!newlines)
- patch->new_name = NULL;
- }
-
- if (patch->is_new && oldlines)
- return error("new file depends on old contents");
- if (patch->is_delete != !newlines) {
- if (newlines)
- return error("deleted file still has contents");
- fprintf(stderr, "** warning: file %s becomes empty but is not deleted\n", patch->new_name);
- }
-
/* Parse the thing.. */
line += len;
size -= len;
linenr++;
added = deleted = 0;
- for (offset = len; size > 0; offset += len, size -= len, line += len, linenr++) {
+ for (offset = len;
+ 0 < size;
+ offset += len, size -= len, line += len, linenr++) {
if (!oldlines && !newlines)
break;
len = linelen(line, size);
@@ -972,12 +955,18 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
patch->lines_added += added;
patch->lines_deleted += deleted;
+
+ if (0 < patch->is_new && oldlines)
+ return error("new file depends on old contents");
+ if (0 < patch->is_delete && newlines)
+ return error("deleted file still has contents");
return offset;
}
static int parse_single_patch(char *line, unsigned long size, struct patch *patch)
{
unsigned long offset = 0;
+ unsigned long oldlines = 0, newlines = 0, context = 0;
struct fragment **fragp = &patch->fragments;
while (size > 4 && !memcmp(line, "@@ -", 4)) {
@@ -988,9 +977,11 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc
len = parse_fragment(line, size, patch, fragment);
if (len <= 0)
die("corrupt patch at line %d", linenr);
-
fragment->patch = line;
fragment->size = len;
+ oldlines += fragment->oldlines;
+ newlines += fragment->newlines;
+ context += fragment->leading + fragment->trailing;
*fragp = fragment;
fragp = &fragment->next;
@@ -999,6 +990,46 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc
line += len;
size -= len;
}
+
+ /*
+ * If something was removed (i.e. we have old-lines) it cannot
+ * be creation, and if something was added it cannot be
+ * deletion. However, the reverse is not true; --unified=0
+ * patches that only add are not necessarily creation even
+ * though they do not have any old lines, and ones that only
+ * delete are not necessarily deletion.
+ *
+ * Unfortunately, a real creation/deletion patch do _not_ have
+ * any context line by definition, so we cannot safely tell it
+ * apart with --unified=0 insanity. At least if the patch has
+ * more than one hunk it is not creation or deletion.
+ */
+ if (patch->is_new < 0 &&
+ (oldlines || (patch->fragments && patch->fragments->next)))
+ patch->is_new = 0;
+ if (patch->is_delete < 0 &&
+ (newlines || (patch->fragments && patch->fragments->next)))
+ patch->is_delete = 0;
+ if (!unidiff_zero || context) {
+ /* If the user says the patch is not generated with
+ * --unified=0, or if we have seen context lines,
+ * then not having oldlines means the patch is creation,
+ * and not having newlines means the patch is deletion.
+ */
+ if (patch->is_new < 0 && !oldlines)
+ patch->is_new = 1;
+ if (patch->is_delete < 0 && !newlines)
+ patch->is_delete = 1;
+ }
+
+ if (0 < patch->is_new && oldlines)
+ die("new file %s depends on old contents", patch->new_name);
+ if (0 < patch->is_delete && newlines)
+ die("deleted file %s still has contents", patch->old_name);
+ if (!patch->is_delete && !newlines && context)
+ fprintf(stderr, "** warning: file %s becomes empty but "
+ "is not deleted\n", patch->new_name);
+
return offset;
}
@@ -1556,9 +1587,19 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
/*
* If we don't have any leading/trailing data in the patch,
* we want it to match at the beginning/end of the file.
+ *
+ * But that would break if the patch is generated with
+ * --unified=0; sane people wouldn't do that to cause us
+ * trouble, but we try to please not so sane ones as well.
*/
- match_beginning = !leading && (frag->oldpos == 1);
- match_end = !trailing;
+ if (unidiff_zero) {
+ match_beginning = (!leading && !frag->oldpos);
+ match_end = 0;
+ }
+ else {
+ match_beginning = !leading && (frag->oldpos == 1);
+ match_end = !trailing;
+ }
lines = 0;
pos = frag->newpos;
@@ -1804,7 +1845,7 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
patch->result = desc.buffer;
patch->resultsize = desc.size;
- if (patch->is_delete && patch->resultsize)
+ if (0 < patch->is_delete && patch->resultsize)
return error("removal patch leaves file contents");
return 0;
@@ -1876,7 +1917,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
old_name, st_mode, patch->old_mode);
}
- if (new_name && prev_patch && prev_patch->is_delete &&
+ if (new_name && prev_patch && 0 < prev_patch->is_delete &&
!strcmp(prev_patch->old_name, new_name))
/* A type-change diff is always split into a patch to
* delete old, immediately followed by a patch to
@@ -1889,7 +1930,8 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
else
ok_if_exists = 0;
- if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) {
+ if (new_name &&
+ ((0 < patch->is_new) | (0 < patch->is_rename) | patch->is_copy)) {
if (check_index &&
cache_name_pos(new_name, strlen(new_name)) >= 0 &&
!ok_if_exists)
@@ -1906,7 +1948,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
return error("%s: %s", new_name, strerror(errno));
}
if (!patch->new_mode) {
- if (patch->is_new)
+ if (0 < patch->is_new)
patch->new_mode = S_IFREG | 0644;
else
patch->new_mode = patch->old_mode;
@@ -1957,7 +1999,7 @@ static void show_index_list(struct patch *list)
const char *name;
name = patch->old_name ? patch->old_name : patch->new_name;
- if (patch->is_new)
+ if (0 < patch->is_new)
sha1_ptr = null_sha1;
else if (get_sha1(patch->old_sha1_prefix, sha1))
die("sha1 information is lacking or useless (%s).",
@@ -2543,6 +2585,10 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
apply_in_reverse = 1;
continue;
}
+ if (!strcmp(arg, "--unidiff-zero")) {
+ unidiff_zero = 1;
+ continue;
+ }
if (!strcmp(arg, "--reject")) {
apply = apply_with_reject = apply_verbosely = 1;
continue;
diff --git a/builtin-archive.c b/builtin-archive.c
new file mode 100644
index 000000000..6dabdee20
--- /dev/null
+++ b/builtin-archive.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2006 Franck Bui-Huu
+ * Copyright (c) 2006 Rene Scharfe
+ */
+#include <time.h>
+#include "cache.h"
+#include "builtin.h"
+#include "archive.h"
+#include "commit.h"
+#include "tree-walk.h"
+#include "exec_cmd.h"
+#include "pkt-line.h"
+#include "sideband.h"
+
+static const char archive_usage[] = \
+"git-archive --format=<fmt> [--prefix=<prefix>/] [--verbose] [<extra>] <tree-ish> [path...]";
+
+struct archiver archivers[] = {
+ {
+ .name = "tar",
+ .write_archive = write_tar_archive,
+ },
+ {
+ .name = "zip",
+ .write_archive = write_zip_archive,
+ .parse_extra = parse_extra_zip_args,
+ },
+};
+
+static int run_remote_archiver(const char *remote, int argc,
+ const char **argv)
+{
+ char *url, buf[LARGE_PACKET_MAX];
+ int fd[2], i, len, rv;
+ pid_t pid;
+ const char *exec = "git-upload-archive";
+ int exec_at = 0;
+
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (!strncmp("--exec=", arg, 7)) {
+ if (exec_at)
+ die("multiple --exec specified");
+ exec = arg + 7;
+ exec_at = i;
+ break;
+ }
+ }
+
+ url = xstrdup(remote);
+ pid = git_connect(fd, url, exec);
+ if (pid < 0)
+ return pid;
+
+ for (i = 1; i < argc; i++) {
+ if (i == exec_at)
+ continue;
+ packet_write(fd[1], "argument %s\n", argv[i]);
+ }
+ packet_flush(fd[1]);
+
+ len = packet_read_line(fd[0], buf, sizeof(buf));
+ if (!len)
+ die("git-archive: expected ACK/NAK, got EOF");
+ if (buf[len-1] == '\n')
+ buf[--len] = 0;
+ if (strcmp(buf, "ACK")) {
+ if (len > 5 && !strncmp(buf, "NACK ", 5))
+ die("git-archive: NACK %s", buf + 5);
+ die("git-archive: protocol error");
+ }
+
+ len = packet_read_line(fd[0], buf, sizeof(buf));
+ if (len)
+ die("git-archive: expected a flush");
+
+ /* Now, start reading from fd[0] and spit it out to stdout */
+ rv = recv_sideband("archive", fd[0], 1, 2, buf, sizeof(buf));
+ close(fd[0]);
+ rv |= finish_connect(pid);
+
+ return !!rv;
+}
+
+static int init_archiver(const char *name, struct archiver *ar)
+{
+ int rv = -1, i;
+
+ for (i = 0; i < ARRAY_SIZE(archivers); i++) {
+ if (!strcmp(name, archivers[i].name)) {
+ memcpy(ar, &archivers[i], sizeof(struct archiver));
+ rv = 0;
+ break;
+ }
+ }
+ return rv;
+}
+
+void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args)
+{
+ ar_args->pathspec = get_pathspec(ar_args->base, pathspec);
+}
+
+void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
+ const char *prefix)
+{
+ const char *name = argv[0];
+ const unsigned char *commit_sha1;
+ time_t archive_time;
+ struct tree *tree;
+ struct commit *commit;
+ unsigned char sha1[20];
+
+ if (get_sha1(name, sha1))
+ die("Not a valid object name");
+
+ commit = lookup_commit_reference_gently(sha1, 1);
+ if (commit) {
+ commit_sha1 = commit->object.sha1;
+ archive_time = commit->date;
+ } else {
+ commit_sha1 = NULL;
+ archive_time = time(NULL);
+ }
+
+ tree = parse_tree_indirect(sha1);
+ if (tree == NULL)
+ die("not a tree object");
+
+ if (prefix) {
+ unsigned char tree_sha1[20];
+ unsigned int mode;
+ int err;
+
+ err = get_tree_entry(tree->object.sha1, prefix,
+ tree_sha1, &mode);
+ if (err || !S_ISDIR(mode))
+ die("current working directory is untracked");
+
+ free(tree);
+ tree = parse_tree_indirect(tree_sha1);
+ }
+ ar_args->tree = tree;
+ ar_args->commit_sha1 = commit_sha1;
+ ar_args->time = archive_time;
+}
+
+int parse_archive_args(int argc, const char **argv, struct archiver *ar)
+{
+ const char *extra_argv[MAX_EXTRA_ARGS];
+ int extra_argc = 0;
+ const char *format = NULL; /* might want to default to "tar" */
+ const char *base = "";
+ int verbose = 0;
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+
+ if (!strcmp(arg, "--list") || !strcmp(arg, "-l")) {
+ for (i = 0; i < ARRAY_SIZE(archivers); i++)
+ printf("%s\n", archivers[i].name);
+ exit(0);
+ }
+ if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) {
+ verbose = 1;
+ continue;
+ }
+ if (!strncmp(arg, "--format=", 9)) {
+ format = arg + 9;
+ continue;
+ }
+ if (!strncmp(arg, "--prefix=", 9)) {
+ base = arg + 9;
+ continue;
+ }
+ if (!strcmp(arg, "--")) {
+ i++;
+ break;
+ }
+ if (arg[0] == '-') {
+ if (extra_argc > MAX_EXTRA_ARGS - 1)
+ die("Too many extra options");
+ extra_argv[extra_argc++] = arg;
+ continue;
+ }
+ break;
+ }
+
+ /* We need at least one parameter -- tree-ish */
+ if (argc - 1 < i)
+ usage(archive_usage);
+ if (!format)
+ die("You must specify an archive format");
+ if (init_archiver(format, ar) < 0)
+ die("Unknown archive format '%s'", format);
+
+ if (extra_argc) {
+ if (!ar->parse_extra)
+ die("'%s' format does not handle %s",
+ ar->name, extra_argv[0]);
+ ar->args.extra = ar->parse_extra(extra_argc, extra_argv);
+ }
+ ar->args.verbose = verbose;
+ ar->args.base = base;
+
+ return i;
+}
+
+static const char *extract_remote_arg(int *ac, const char **av)
+{
+ int ix, iy, cnt = *ac;
+ int no_more_options = 0;
+ const char *remote = NULL;
+
+ for (ix = iy = 1; ix < cnt; ix++) {
+ const char *arg = av[ix];
+ if (!strcmp(arg, "--"))
+ no_more_options = 1;
+ if (!no_more_options) {
+ if (!strncmp(arg, "--remote=", 9)) {
+ if (remote)
+ die("Multiple --remote specified");
+ remote = arg + 9;
+ continue;
+ }
+ if (arg[0] != '-')
+ no_more_options = 1;
+ }
+ if (ix != iy)
+ av[iy] = arg;
+ iy++;
+ }
+ if (remote) {
+ av[--cnt] = NULL;
+ *ac = cnt;
+ }
+ return remote;
+}
+
+int cmd_archive(int argc, const char **argv, const char *prefix)
+{
+ struct archiver ar;
+ int tree_idx;
+ const char *remote = NULL;
+
+ remote = extract_remote_arg(&argc, argv);
+ if (remote)
+ return run_remote_archiver(remote, argc, argv);
+
+ setlinebuf(stderr);
+
+ memset(&ar, 0, sizeof(ar));
+ tree_idx = parse_archive_args(argc, argv, &ar);
+ if (prefix == NULL)
+ prefix = setup_git_directory();
+
+ argv += tree_idx;
+ parse_treeish_arg(argv, &ar.args, prefix);
+ parse_pathspec_arg(argv + 1, &ar.args);
+
+ return ar.write_archive(&ar.args);
+}
diff --git a/builtin-count-objects.c b/builtin-count-objects.c
index 1d3729aa9..73c598242 100644
--- a/builtin-count-objects.c
+++ b/builtin-count-objects.c
@@ -62,7 +62,7 @@ static void count_objects(DIR *d, char *path, int len, int verbose,
hex[40] = 0;
if (get_sha1_hex(hex, sha1))
die("internal error");
- if (has_sha1_pack(sha1))
+ if (has_sha1_pack(sha1, NULL))
(*packed_loose)++;
}
}
diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c
index c407c033e..3d3097d29 100644
--- a/builtin-fmt-merge-msg.c
+++ b/builtin-fmt-merge-msg.c
@@ -249,7 +249,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
FILE *in = stdin;
const char *sep = "";
unsigned char head_sha1[20];
- const char *head, *current_branch;
+ const char *current_branch;
git_config(fmt_merge_msg_config);
@@ -277,10 +277,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
usage(fmt_merge_msg_usage);
/* get current branch */
- head = xstrdup(git_path("HEAD"));
- current_branch = resolve_ref(head, head_sha1, 1);
- current_branch += strlen(head) - 4;
- free((char *)head);
+ current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
if (!strncmp(current_branch, "refs/heads/", 11))
current_branch += 11;
diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c
index 698618b79..93d3d7eef 100644
--- a/builtin-for-each-ref.c
+++ b/builtin-for-each-ref.c
@@ -585,24 +585,27 @@ static void get_value(struct refinfo *ref, int atom, struct atom_value **v)
*v = &ref->value[atom];
}
-static struct refinfo **grab_array;
-static const char **grab_pattern;
-static int *grab_cnt;
+struct grab_ref_cbdata {
+ struct refinfo **grab_array;
+ const char **grab_pattern;
+ int grab_cnt;
+};
/*
* A call-back given to for_each_ref(). It is unfortunate that we
* need to use global variables to pass extra information to this
* function.
*/
-static int grab_single_ref(const char *refname, const unsigned char *sha1)
+static int grab_single_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
+ struct grab_ref_cbdata *cb = cb_data;
struct refinfo *ref;
int cnt;
- if (*grab_pattern) {
+ if (*cb->grab_pattern) {
const char **pattern;
int namelen = strlen(refname);
- for (pattern = grab_pattern; *pattern; pattern++) {
+ for (pattern = cb->grab_pattern; *pattern; pattern++) {
const char *p = *pattern;
int plen = strlen(p);
@@ -626,25 +629,14 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1)
ref->refname = xstrdup(refname);
hashcpy(ref->objectname, sha1);
- cnt = *grab_cnt;
- grab_array = xrealloc(grab_array, sizeof(*grab_array) * (cnt + 1));
- grab_array[cnt++] = ref;
- *grab_cnt = cnt;
+ cnt = cb->grab_cnt;
+ cb->grab_array = xrealloc(cb->grab_array,
+ sizeof(*cb->grab_array) * (cnt + 1));
+ cb->grab_array[cnt++] = ref;
+ cb->grab_cnt = cnt;
return 0;
}
-static struct refinfo **grab_refs(const char **pattern, int *cnt)
-{
- /* Sheesh, we really should make for-each-ref to take
- * callback data.
- */
- *cnt = 0;
- grab_pattern = pattern;
- grab_cnt = cnt;
- for_each_ref(grab_single_ref);
- return grab_array;
-}
-
static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b)
{
struct atom_value *va, *vb;
@@ -784,6 +776,7 @@ int cmd_for_each_ref(int ac, const char **av, char *prefix)
int maxcount = 0;
int quote_style = -1; /* unspecified yet */
struct refinfo **refs;
+ struct grab_ref_cbdata cbdata;
for (i = 1; i < ac; i++) {
const char *arg = av[i];
@@ -855,7 +848,11 @@ int cmd_for_each_ref(int ac, const char **av, char *prefix)
verify_format(format);
- refs = grab_refs(av + i, &num_refs);
+ memset(&cbdata, 0, sizeof(cbdata));
+ cbdata.grab_pattern = av + i;
+ for_each_ref(grab_single_ref, &cbdata);
+ refs = cbdata.grab_array;
+ num_refs = cbdata.grab_cnt;
for (i = 0; i < used_atom_cnt; i++) {
if (used_atom[i][0] == '*') {
diff --git a/builtin-init-db.c b/builtin-init-db.c
index 5085018e4..23b7714f8 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -218,8 +218,8 @@ static void create_default_files(const char *git_dir, const char *template_path)
* branch, if it does not exist yet.
*/
strcpy(path + len, "HEAD");
- if (read_ref(path, sha1) < 0) {
- if (create_symref(path, "refs/heads/master") < 0)
+ if (read_ref("HEAD", sha1) < 0) {
+ if (create_symref("HEAD", "refs/heads/master") < 0)
exit(1);
}
diff --git a/builtin-name-rev.c b/builtin-name-rev.c
index 52886b69b..618aa314d 100644
--- a/builtin-name-rev.c
+++ b/builtin-name-rev.c
@@ -75,11 +75,10 @@ copy_data:
}
}
-static int tags_only;
-
-static int name_ref(const char *path, const unsigned char *sha1)
+static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data)
{
struct object *o = parse_object(sha1);
+ int tags_only = *(int*)cb_data;
int deref = 0;
if (tags_only && strncmp(path, "refs/tags/", 10))
@@ -131,6 +130,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
{
struct object_array revs = { 0, 0, NULL };
int as_is = 0, all = 0, transform_stdin = 0;
+ int tags_only = 0;
git_config(git_default_config);
@@ -186,7 +186,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
add_object_array((struct object *)commit, *argv, &revs);
}
- for_each_ref(name_ref);
+ for_each_ref(name_ref, &tags_only);
if (transform_stdin) {
char buffer[2048];
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 149fa2839..8d7a1209d 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -9,10 +9,13 @@
#include "pack.h"
#include "csum-file.h"
#include "tree-walk.h"
+#include "diff.h"
+#include "revision.h"
+#include "list-objects.h"
#include <sys/time.h>
#include <signal.h>
-static const char pack_usage[] = "git-pack-objects [-q] [--no-reuse-delta] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list";
+static const char pack_usage[] = "git-pack-objects [-q] [--no-reuse-delta] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] [--revs [--unpacked | --all]*] [--stdout | base-name] <ref-list | <object-list]";
struct object_entry {
unsigned char sha1[20];
@@ -66,6 +69,7 @@ static int progress = 1;
static volatile sig_atomic_t progress_update;
static int window = 10;
static int pack_to_stdout;
+static int num_preferred_base;
/*
* The object names in objects array are hashed with this hashtable,
@@ -838,7 +842,7 @@ static int check_pbase_path(unsigned hash)
return 0;
}
-static void add_preferred_base_object(char *name, unsigned hash)
+static void add_preferred_base_object(const char *name, unsigned hash)
{
struct pbase_tree *it;
int cmplen = name_cmp_len(name);
@@ -867,6 +871,9 @@ static void add_preferred_base(unsigned char *sha1)
unsigned long size;
unsigned char tree_sha1[20];
+ if (window <= num_preferred_base++)
+ return;
+
data = read_object_with_reference(sha1, tree_type, &size, tree_sha1);
if (!data)
return;
@@ -1326,14 +1333,105 @@ static int git_pack_config(const char *k, const char *v)
return git_default_config(k, v);
}
+static void read_object_list_from_stdin(void)
+{
+ char line[40 + 1 + PATH_MAX + 2];
+ unsigned char sha1[20];
+ unsigned hash;
+
+ for (;;) {
+ if (!fgets(line, sizeof(line), stdin)) {
+ if (feof(stdin))
+ break;
+ if (!ferror(stdin))
+ die("fgets returned NULL, not EOF, not error!");
+ if (errno != EINTR)
+ die("fgets: %s", strerror(errno));
+ clearerr(stdin);
+ continue;
+ }
+ if (line[0] == '-') {
+ if (get_sha1_hex(line+1, sha1))
+ die("expected edge sha1, got garbage:\n %s",
+ line);
+ add_preferred_base(sha1);
+ continue;
+ }
+ if (get_sha1_hex(line, sha1))
+ die("expected sha1, got garbage:\n %s", line);
+
+ hash = name_hash(line+41);
+ add_preferred_base_object(line+41, hash);
+ add_object_entry(sha1, hash, 0);
+ }
+}
+
+static void show_commit(struct commit *commit)
+{
+ unsigned hash = name_hash("");
+ add_preferred_base_object("", hash);
+ add_object_entry(commit->object.sha1, hash, 0);
+}
+
+static void show_object(struct object_array_entry *p)
+{
+ unsigned hash = name_hash(p->name);
+ add_preferred_base_object(p->name, hash);
+ add_object_entry(p->item->sha1, hash, 0);
+}
+
+static void show_edge(struct commit *commit)
+{
+ add_preferred_base(commit->object.sha1);
+}
+
+static void get_object_list(int ac, const char **av)
+{
+ struct rev_info revs;
+ char line[1000];
+ int flags = 0;
+
+ init_revisions(&revs, NULL);
+ save_commit_buffer = 0;
+ track_object_refs = 0;
+ setup_revisions(ac, av, &revs, NULL);
+
+ while (fgets(line, sizeof(line), stdin) != NULL) {
+ int len = strlen(line);
+ if (line[len - 1] == '\n')
+ line[--len] = 0;
+ if (!len)
+ break;
+ if (*line == '-') {
+ if (!strcmp(line, "--not")) {
+ flags ^= UNINTERESTING;
+ continue;
+ }
+ die("not a rev '%s'", line);
+ }
+ if (handle_revision_arg(line, &revs, flags, 1))
+ die("bad revision '%s'", line);
+ }
+
+ prepare_revision_walk(&revs);
+ mark_edges_uninteresting(revs.commits, &revs, show_edge);
+ traverse_commit_list(&revs, show_commit, show_object);
+}
+
int cmd_pack_objects(int argc, const char **argv, const char *prefix)
{
SHA_CTX ctx;
- char line[40 + 1 + PATH_MAX + 2];
int depth = 10;
struct object_entry **list;
- int num_preferred_base = 0;
+ int use_internal_rev_list = 0;
+ int thin = 0;
int i;
+ const char *rp_av[64];
+ int rp_ac;
+
+ rp_av[0] = "pack-objects";
+ rp_av[1] = "--objects"; /* --thin will make it --objects-edge */
+ rp_ac = 2;
git_config(git_pack_config);
@@ -1341,63 +1439,99 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
- if (*arg == '-') {
- if (!strcmp("--non-empty", arg)) {
- non_empty = 1;
- continue;
- }
- if (!strcmp("--local", arg)) {
- local = 1;
- continue;
- }
- if (!strcmp("--progress", arg)) {
- progress = 1;
- continue;
- }
- if (!strcmp("--incremental", arg)) {
- incremental = 1;
- continue;
- }
- if (!strncmp("--window=", arg, 9)) {
- char *end;
- window = strtoul(arg+9, &end, 0);
- if (!arg[9] || *end)
- usage(pack_usage);
- continue;
- }
- if (!strncmp("--depth=", arg, 8)) {
- char *end;
- depth = strtoul(arg+8, &end, 0);
- if (!arg[8] || *end)
- usage(pack_usage);
- continue;
- }
- if (!strcmp("--progress", arg)) {
- progress = 1;
- continue;
- }
- if (!strcmp("-q", arg)) {
- progress = 0;
- continue;
- }
- if (!strcmp("--no-reuse-delta", arg)) {
- no_reuse_delta = 1;
- continue;
- }
- if (!strcmp("--stdout", arg)) {
- pack_to_stdout = 1;
- continue;
- }
- usage(pack_usage);
+ if (*arg != '-')
+ break;
+
+ if (!strcmp("--non-empty", arg)) {
+ non_empty = 1;
+ continue;
+ }
+ if (!strcmp("--local", arg)) {
+ local = 1;
+ continue;
+ }
+ if (!strcmp("--progress", arg)) {
+ progress = 1;
+ continue;
+ }
+ if (!strcmp("--incremental", arg)) {
+ incremental = 1;
+ continue;
+ }
+ if (!strncmp("--window=", arg, 9)) {
+ char *end;
+ window = strtoul(arg+9, &end, 0);
+ if (!arg[9] || *end)
+ usage(pack_usage);
+ continue;
+ }
+ if (!strncmp("--depth=", arg, 8)) {
+ char *end;
+ depth = strtoul(arg+8, &end, 0);
+ if (!arg[8] || *end)
+ usage(pack_usage);
+ continue;
+ }
+ if (!strcmp("--progress", arg)) {
+ progress = 1;
+ continue;
+ }
+ if (!strcmp("-q", arg)) {
+ progress = 0;
+ continue;
+ }
+ if (!strcmp("--no-reuse-delta", arg)) {
+ no_reuse_delta = 1;
+ continue;
+ }
+ if (!strcmp("--stdout", arg)) {
+ pack_to_stdout = 1;
+ continue;
+ }
+ if (!strcmp("--revs", arg)) {
+ use_internal_rev_list = 1;
+ continue;
+ }
+ if (!strcmp("--unpacked", arg) ||
+ !strncmp("--unpacked=", arg, 11) ||
+ !strcmp("--all", arg)) {
+ use_internal_rev_list = 1;
+ if (ARRAY_SIZE(rp_av) - 1 <= rp_ac)
+ die("too many internal rev-list options");
+ rp_av[rp_ac++] = arg;
+ continue;
}
- if (base_name)
- usage(pack_usage);
- base_name = arg;
+ if (!strcmp("--thin", arg)) {
+ use_internal_rev_list = 1;
+ thin = 1;
+ rp_av[1] = "--objects-edge";
+ continue;
+ }
+ usage(pack_usage);
}
+ /* Traditionally "pack-objects [options] base extra" failed;
+ * we would however want to take refs parameter that would
+ * have been given to upstream rev-list ourselves, which means
+ * we somehow want to say what the base name is. So the
+ * syntax would be:
+ *
+ * pack-objects [options] base <refs...>
+ *
+ * in other words, we would treat the first non-option as the
+ * base_name and send everything else to the internal revision
+ * walker.
+ */
+
+ if (!pack_to_stdout)
+ base_name = argv[i++];
+
if (pack_to_stdout != !base_name)
usage(pack_usage);
+ if (!pack_to_stdout && thin)
+ die("--thin cannot be used to build an indexable pack.");
+
prepare_packed_git();
if (progress) {
@@ -1405,35 +1539,13 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
setup_progress_signal();
}
- for (;;) {
- unsigned char sha1[20];
- unsigned hash;
-
- if (!fgets(line, sizeof(line), stdin)) {
- if (feof(stdin))
- break;
- if (!ferror(stdin))
- die("fgets returned NULL, not EOF, not error!");
- if (errno != EINTR)
- die("fgets: %s", strerror(errno));
- clearerr(stdin);
- continue;
- }
-
- if (line[0] == '-') {
- if (get_sha1_hex(line+1, sha1))
- die("expected edge sha1, got garbage:\n %s",
- line+1);
- if (num_preferred_base++ < window)
- add_preferred_base(sha1);
- continue;
- }
- if (get_sha1_hex(line, sha1))
- die("expected sha1, got garbage:\n %s", line);
- hash = name_hash(line+41);
- add_preferred_base_object(line+41, hash);
- add_object_entry(sha1, hash, 0);
+ if (!use_internal_rev_list)
+ read_object_list_from_stdin();
+ else {
+ rp_av[rp_ac] = NULL;
+ get_object_list(rp_ac, rp_av);
}
+
if (progress)
fprintf(stderr, "Done counting %d objects.\n", nr_objects);
sorted_by_sha = create_final_object_list();
diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c
new file mode 100644
index 000000000..246dd6372
--- /dev/null
+++ b/builtin-pack-refs.c
@@ -0,0 +1,112 @@
+#include "cache.h"
+#include "refs.h"
+
+static const char *result_path, *lock_path;
+static const char builtin_pack_refs_usage[] =
+"git-pack-refs [--prune]";
+
+struct ref_to_prune {
+ struct ref_to_prune *next;
+ unsigned char sha1[20];
+ char name[FLEX_ARRAY];
+};
+
+struct pack_refs_cb_data {
+ int prune;
+ struct ref_to_prune *ref_to_prune;
+ FILE *refs_file;
+};
+
+static void remove_lock_file(void)
+{
+ if (lock_path)
+ unlink(lock_path);
+}
+
+static int do_not_prune(int flags)
+{
+ /* If it is already packed or if it is a symref,
+ * do not prune it.
+ */
+ return (flags & (REF_ISSYMREF|REF_ISPACKED));
+}
+
+static int handle_one_ref(const char *path, const unsigned char *sha1,
+ int flags, void *cb_data)
+{
+ struct pack_refs_cb_data *cb = cb_data;
+
+ /* Do not pack the symbolic refs */
+ if (!(flags & REF_ISSYMREF))
+ fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path);
+ if (cb->prune && !do_not_prune(flags)) {
+ int namelen = strlen(path) + 1;
+ struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
+ hashcpy(n->sha1, sha1);
+ strcpy(n->name, path);
+ n->next = cb->ref_to_prune;
+ cb->ref_to_prune = n;
+ }
+ return 0;
+}
+
+/* make sure nobody touched the ref, and unlink */
+static void prune_ref(struct ref_to_prune *r)
+{
+ struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1, 1);
+
+ if (lock) {
+ unlink(git_path(r->name));
+ unlock_ref(lock);
+ }
+}
+
+static void prune_refs(struct ref_to_prune *r)
+{
+ while (r) {
+ prune_ref(r);
+ r = r->next;
+ }
+}
+
+int cmd_pack_refs(int argc, const char **argv, const char *prefix)
+{
+ int fd, i;
+ struct pack_refs_cb_data cbdata;
+
+ memset(&cbdata, 0, sizeof(cbdata));
+
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (!strcmp(arg, "--prune")) {
+ cbdata.prune = 1;
+ continue;
+ }
+ /* perhaps other parameters later... */
+ break;
+ }
+ if (i != argc)
+ usage(builtin_pack_refs_usage);
+
+ result_path = xstrdup(git_path("packed-refs"));
+ lock_path = xstrdup(mkpath("%s.lock", result_path));
+
+ fd = open(lock_path, O_CREAT | O_EXCL | O_WRONLY, 0666);
+ if (fd < 0)
+ die("unable to create new ref-pack file (%s)", strerror(errno));
+ atexit(remove_lock_file);
+
+ cbdata.refs_file = fdopen(fd, "w");
+ if (!cbdata.refs_file)
+ die("unable to create ref-pack file structure (%s)",
+ strerror(errno));
+ for_each_ref(handle_one_ref, &cbdata);
+ fsync(fd);
+ fclose(cbdata.refs_file);
+ if (rename(lock_path, result_path) < 0)
+ die("unable to overwrite old ref-pack file (%s)", strerror(errno));
+ lock_path = NULL;
+ if (cbdata.prune)
+ prune_refs(cbdata.ref_to_prune);
+ return 0;
+}
diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c
index d3dd94d9e..960db4985 100644
--- a/builtin-prune-packed.c
+++ b/builtin-prune-packed.c
@@ -19,7 +19,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len)
memcpy(hex+2, de->d_name, 38);
if (get_sha1_hex(hex, sha1))
continue;
- if (!has_sha1_pack(sha1))
+ if (!has_sha1_pack(sha1, NULL))
continue;
memcpy(pathname + len, de->d_name, 38);
if (dryrun)
diff --git a/builtin-prune.c b/builtin-prune.c
index 6228c7907..e79b515c7 100644
--- a/builtin-prune.c
+++ b/builtin-prune.c
@@ -174,7 +174,7 @@ static void walk_commit_list(struct rev_info *revs)
}
}
-static int add_one_ref(const char *path, const unsigned char *sha1)
+static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *object = parse_object(sha1);
if (!object)
@@ -240,7 +240,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
revs.tree_objects = 1;
/* Add all external refs */
- for_each_ref(add_one_ref);
+ for_each_ref(add_one_ref, NULL);
/* Add all refs from the index file */
add_cache_refs();
diff --git a/builtin-push.c b/builtin-push.c
index c43f2566d..581c44ba8 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -27,7 +27,7 @@ static void add_refspec(const char *ref)
refspec_nr = nr;
}
-static int expand_one_ref(const char *ref, const unsigned char *sha1)
+static int expand_one_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
{
/* Ignore the "refs/" at the beginning of the refname */
ref += 5;
@@ -51,7 +51,7 @@ static void expand_refspecs(void)
}
if (!tags)
return;
- for_each_ref(expand_one_ref);
+ for_each_ref(expand_one_ref, NULL);
}
static void set_refspecs(const char **refs, int nr)
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index 8fe8afbd0..1f3333da3 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -7,6 +7,7 @@
#include "tree-walk.h"
#include "diff.h"
#include "revision.h"
+#include "list-objects.h"
#include "builtin.h"
/* bits #0-15 in revision.h */
@@ -98,104 +99,24 @@ static void show_commit(struct commit *commit)
commit->buffer = NULL;
}
-static void process_blob(struct blob *blob,
- struct object_array *p,
- struct name_path *path,
- const char *name)
+static void show_object(struct object_array_entry *p)
{
- struct object *obj = &blob->object;
-
- if (!revs.blob_objects)
- return;
- if (obj->flags & (UNINTERESTING | SEEN))
- return;
- obj->flags |= SEEN;
- name = xstrdup(name);
- add_object(obj, p, path, name);
-}
-
-static void process_tree(struct tree *tree,
- struct object_array *p,
- struct name_path *path,
- const char *name)
-{
- struct object *obj = &tree->object;
- struct tree_desc desc;
- struct name_entry entry;
- struct name_path me;
-
- if (!revs.tree_objects)
- return;
- if (obj->flags & (UNINTERESTING | SEEN))
- return;
- if (parse_tree(tree) < 0)
- die("bad tree object %s", sha1_to_hex(obj->sha1));
- obj->flags |= SEEN;
- name = xstrdup(name);
- add_object(obj, p, path, name);
- me.up = path;
- me.elem = name;
- me.elem_len = strlen(name);
-
- desc.buf = tree->buffer;
- desc.size = tree->size;
-
- while (tree_entry(&desc, &entry)) {
- if (S_ISDIR(entry.mode))
- process_tree(lookup_tree(entry.sha1), p, &me, entry.path);
- else
- process_blob(lookup_blob(entry.sha1), p, &me, entry.path);
+ /* An object with name "foo\n0000000..." can be used to
+ * confuse downstream git-pack-objects very badly.
+ */
+ const char *ep = strchr(p->name, '\n');
+ if (ep) {
+ printf("%s %.*s\n", sha1_to_hex(p->item->sha1),
+ (int) (ep - p->name),
+ p->name);
}
- free(tree->buffer);
- tree->buffer = NULL;
+ else
+ printf("%s %s\n", sha1_to_hex(p->item->sha1), p->name);
}
-static void show_commit_list(struct rev_info *revs)
+static void show_edge(struct commit *commit)
{
- int i;
- struct commit *commit;
- struct object_array objects = { 0, 0, NULL };
-
- while ((commit = get_revision(revs)) != NULL) {
- process_tree(commit->tree, &objects, NULL, "");
- show_commit(commit);
- }
- for (i = 0; i < revs->pending.nr; i++) {
- struct object_array_entry *pending = revs->pending.objects + i;
- struct object *obj = pending->item;
- const char *name = pending->name;
- if (obj->flags & (UNINTERESTING | SEEN))
- continue;
- if (obj->type == OBJ_TAG) {
- obj->flags |= SEEN;
- add_object_array(obj, name, &objects);
- continue;
- }
- if (obj->type == OBJ_TREE) {
- process_tree((struct tree *)obj, &objects, NULL, name);
- continue;
- }
- if (obj->type == OBJ_BLOB) {
- process_blob((struct blob *)obj, &objects, NULL, name);
- continue;
- }
- die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
- }
- for (i = 0; i < objects.nr; i++) {
- struct object_array_entry *p = objects.objects + i;
-
- /* An object with name "foo\n0000000..." can be used to
- * confuse downstream git-pack-objects very badly.
- */
- const char *ep = strchr(p->name, '\n');
- if (ep) {
- printf("%s %.*s\n", sha1_to_hex(p->item->sha1),
- (int) (ep - p->name),
- p->name);
- }
- else
- printf("%s %s\n", sha1_to_hex(p->item->sha1), p->name);
- }
+ printf("-%s\n", sha1_to_hex(commit->object.sha1));
}
/*
@@ -276,35 +197,6 @@ static struct commit_list *find_bisection(struct commit_list *list)
return best;
}
-static void mark_edge_parents_uninteresting(struct commit *commit)
-{
- struct commit_list *parents;
-
- for (parents = commit->parents; parents; parents = parents->next) {
- struct commit *parent = parents->item;
- if (!(parent->object.flags & UNINTERESTING))
- continue;
- mark_tree_uninteresting(parent->tree);
- if (revs.edge_hint && !(parent->object.flags & SHOWN)) {
- parent->object.flags |= SHOWN;
- printf("-%s\n", sha1_to_hex(parent->object.sha1));
- }
- }
-}
-
-static void mark_edges_uninteresting(struct commit_list *list)
-{
- for ( ; list; list = list->next) {
- struct commit *commit = list->item;
-
- if (commit->object.flags & UNINTERESTING) {
- mark_tree_uninteresting(commit->tree);
- continue;
- }
- mark_edge_parents_uninteresting(commit);
- }
-}
-
static void read_revisions_from_stdin(struct rev_info *revs)
{
char line[1000];
@@ -384,12 +276,12 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
prepare_revision_walk(&revs);
if (revs.tree_objects)
- mark_edges_uninteresting(revs.commits);
+ mark_edges_uninteresting(revs.commits, &revs, show_edge);
if (bisect_list)
revs.commits = find_bisection(revs.commits);
- show_commit_list(&revs);
+ traverse_commit_list(&revs, show_commit, show_object);
return 0;
}
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index fd3ccc854..3b716fba1 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -137,7 +137,7 @@ static void show_default(void)
}
}
-static int show_reference(const char *refname, const unsigned char *sha1)
+static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
show_rev(NORMAL, sha1, refname);
return 0;
@@ -299,19 +299,19 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (!strcmp(arg, "--all")) {
- for_each_ref(show_reference);
+ for_each_ref(show_reference, NULL);
continue;
}
if (!strcmp(arg, "--branches")) {
- for_each_branch_ref(show_reference);
+ for_each_branch_ref(show_reference, NULL);
continue;
}
if (!strcmp(arg, "--tags")) {
- for_each_tag_ref(show_reference);
+ for_each_tag_ref(show_reference, NULL);
continue;
}
if (!strcmp(arg, "--remotes")) {
- for_each_remote_ref(show_reference);
+ for_each_remote_ref(show_reference, NULL);
continue;
}
if (!strcmp(arg, "--show-prefix")) {
diff --git a/builtin-runstatus.c b/builtin-runstatus.c
new file mode 100644
index 000000000..303c556da
--- /dev/null
+++ b/builtin-runstatus.c
@@ -0,0 +1,36 @@
+#include "wt-status.h"
+#include "cache.h"
+
+extern int wt_status_use_color;
+
+static const char runstatus_usage[] =
+"git-runstatus [--color|--nocolor] [--amend] [--verbose]";
+
+int cmd_runstatus(int argc, const char **argv, const char *prefix)
+{
+ struct wt_status s;
+ int i;
+
+ git_config(git_status_config);
+ wt_status_prepare(&s);
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "--color"))
+ wt_status_use_color = 1;
+ else if (!strcmp(argv[i], "--nocolor"))
+ wt_status_use_color = 0;
+ else if (!strcmp(argv[i], "--amend")) {
+ s.amend = 1;
+ s.reference = "HEAD^1";
+ }
+ else if (!strcmp(argv[i], "--verbose"))
+ s.verbose = 1;
+ else if (!strcmp(argv[i], "--untracked"))
+ s.untracked = 1;
+ else
+ usage(runstatus_usage);
+ }
+
+ wt_status_print(&s);
+ return s.commitable ? 0 : 1;
+}
diff --git a/builtin-show-branch.c b/builtin-show-branch.c
index 578c9fafd..5d6ce5683 100644
--- a/builtin-show-branch.c
+++ b/builtin-show-branch.c
@@ -346,7 +346,7 @@ static void sort_ref_range(int bottom, int top)
compare_ref_name);
}
-static int append_ref(const char *refname, const unsigned char *sha1)
+static int append_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
struct commit *commit = lookup_commit_reference_gently(sha1, 1);
int i;
@@ -369,7 +369,7 @@ static int append_ref(const char *refname, const unsigned char *sha1)
return 0;
}
-static int append_head_ref(const char *refname, const unsigned char *sha1)
+static int append_head_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
unsigned char tmp[20];
int ofs = 11;
@@ -380,14 +380,14 @@ static int append_head_ref(const char *refname, const unsigned char *sha1)
*/
if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))
ofs = 5;
- return append_ref(refname + ofs, sha1);
+ return append_ref(refname + ofs, sha1, flag, cb_data);
}
-static int append_tag_ref(const char *refname, const unsigned char *sha1)
+static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
if (strncmp(refname, "refs/tags/", 10))
return 0;
- return append_ref(refname + 5, sha1);
+ return append_ref(refname + 5, sha1, flag, cb_data);
}
static const char *match_ref_pattern = NULL;
@@ -401,7 +401,7 @@ static int count_slash(const char *s)
return cnt;
}
-static int append_matching_ref(const char *refname, const unsigned char *sha1)
+static int append_matching_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
/* we want to allow pattern hold/<asterisk> to show all
* branches under refs/heads/hold/, and v0.99.9? to show
@@ -417,41 +417,33 @@ static int append_matching_ref(const char *refname, const unsigned char *sha1)
if (fnmatch(match_ref_pattern, tail, 0))
return 0;
if (!strncmp("refs/heads/", refname, 11))
- return append_head_ref(refname, sha1);
+ return append_head_ref(refname, sha1, flag, cb_data);
if (!strncmp("refs/tags/", refname, 10))
- return append_tag_ref(refname, sha1);
- return append_ref(refname, sha1);
+ return append_tag_ref(refname, sha1, flag, cb_data);
+ return append_ref(refname, sha1, flag, cb_data);
}
static void snarf_refs(int head, int tag)
{
if (head) {
int orig_cnt = ref_name_cnt;
- for_each_ref(append_head_ref);
+ for_each_ref(append_head_ref, NULL);
sort_ref_range(orig_cnt, ref_name_cnt);
}
if (tag) {
int orig_cnt = ref_name_cnt;
- for_each_ref(append_tag_ref);
+ for_each_ref(append_tag_ref, NULL);
sort_ref_range(orig_cnt, ref_name_cnt);
}
}
-static int rev_is_head(char *head_path, int headlen, char *name,
+static int rev_is_head(char *head, int headlen, char *name,
unsigned char *head_sha1, unsigned char *sha1)
{
- int namelen;
- if ((!head_path[0]) ||
+ if ((!head[0]) ||
(head_sha1 && sha1 && hashcmp(head_sha1, sha1)))
return 0;
- namelen = strlen(name);
- if ((headlen < namelen) ||
- memcmp(head_path + headlen - namelen, name, namelen))
- return 0;
- if (headlen == namelen ||
- head_path[headlen - namelen - 1] == '/')
- return 1;
- return 0;
+ return !strcmp(head, name);
}
static int show_merge_base(struct commit_list *seen, int num_rev)
@@ -495,7 +487,7 @@ static void append_one_rev(const char *av)
{
unsigned char revkey[20];
if (!get_sha1(av, revkey)) {
- append_ref(av, revkey);
+ append_ref(av, revkey, 0, NULL);
return;
}
if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) {
@@ -503,7 +495,7 @@ static void append_one_rev(const char *av)
int saved_matches = ref_name_cnt;
match_ref_pattern = av;
match_ref_slash = count_slash(av);
- for_each_ref(append_matching_ref);
+ for_each_ref(append_matching_ref, NULL);
if (saved_matches == ref_name_cnt &&
ref_name_cnt < MAX_REVS)
error("no matching refs with %s", av);
@@ -559,9 +551,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
int all_heads = 0, all_tags = 0;
int all_mask, all_revs;
int lifo = 1;
- char head_path[128];
- const char *head_path_p;
- int head_path_len;
+ char head[128];
+ const char *head_p;
+ int head_len;
unsigned char head_sha1[20];
int merge_base = 0;
int independent = 0;
@@ -638,31 +630,31 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
ac--; av++;
}
- head_path_p = resolve_ref(git_path("HEAD"), head_sha1, 1);
- if (head_path_p) {
- head_path_len = strlen(head_path_p);
- memcpy(head_path, head_path_p, head_path_len + 1);
+ head_p = resolve_ref("HEAD", head_sha1, 1, NULL);
+ if (head_p) {
+ head_len = strlen(head_p);
+ memcpy(head, head_p, head_len + 1);
}
else {
- head_path_len = 0;
- head_path[0] = 0;
+ head_len = 0;
+ head[0] = 0;
}
- if (with_current_branch && head_path_p) {
+ if (with_current_branch && head_p) {
int has_head = 0;
for (i = 0; !has_head && i < ref_name_cnt; i++) {
/* We are only interested in adding the branch
* HEAD points at.
*/
- if (rev_is_head(head_path,
- head_path_len,
+ if (rev_is_head(head,
+ head_len,
ref_name[i],
head_sha1, NULL))
has_head++;
}
if (!has_head) {
- int pfxlen = strlen(git_path("refs/heads/"));
- append_one_rev(head_path + pfxlen);
+ int pfxlen = strlen("refs/heads/");
+ append_one_rev(head + pfxlen);
}
}
@@ -713,8 +705,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
if (1 < num_rev || extra < 0) {
for (i = 0; i < num_rev; i++) {
int j;
- int is_head = rev_is_head(head_path,
- head_path_len,
+ int is_head = rev_is_head(head,
+ head_len,
ref_name[i],
head_sha1,
rev[i]->object.sha1);
diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c
index 1d3a5e229..d8be0527f 100644
--- a/builtin-symbolic-ref.c
+++ b/builtin-symbolic-ref.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "refs.h"
static const char git_symbolic_ref_usage[] =
"git-symbolic-ref name [ref]";
@@ -7,15 +8,14 @@ static const char git_symbolic_ref_usage[] =
static void check_symref(const char *HEAD)
{
unsigned char sha1[20];
- const char *git_HEAD = xstrdup(git_path("%s", HEAD));
- const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 0);
- if (git_refs_heads_master) {
- /* we want to strip the .git/ part */
- int pfxlen = strlen(git_HEAD) - strlen(HEAD);
- puts(git_refs_heads_master + pfxlen);
- }
- else
+ int flag;
+ const char *refs_heads_master = resolve_ref(HEAD, sha1, 0, &flag);
+
+ if (!refs_heads_master)
die("No such ref: %s", HEAD);
+ else if (!(flag & REF_ISSYMREF))
+ die("ref %s is not a symbolic ref", HEAD);
+ puts(refs_heads_master);
}
int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
@@ -26,7 +26,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
check_symref(argv[1]);
break;
case 3:
- create_symref(xstrdup(git_path("%s", argv[1])), argv[2]);
+ create_symref(argv[1], argv[2]);
break;
default:
usage(git_symbolic_ref_usage);
diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c
index fa666f78c..437eb726a 100644
--- a/builtin-tar-tree.c
+++ b/builtin-tar-tree.c
@@ -3,12 +3,12 @@
*/
#include <time.h>
#include "cache.h"
-#include "tree-walk.h"
#include "commit.h"
#include "strbuf.h"
#include "tar.h"
#include "builtin.h"
#include "pkt-line.h"
+#include "archive.h"
#define RECORDSIZE (512)
#define BLOCKSIZE (RECORDSIZE * 20)
@@ -21,6 +21,7 @@ static unsigned long offset;
static time_t archive_time;
static int tar_umask;
+static int verbose;
/* writes out the whole block, but only if it is full */
static void write_if_needed(void)
@@ -168,6 +169,8 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path,
mode = 0100666;
sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
} else {
+ if (verbose)
+ fprintf(stderr, "%.*s\n", path->len, path->buf);
if (S_ISDIR(mode)) {
*header.typeflag = TYPEFLAG_DIR;
mode = (mode | 0777) & ~tar_umask;
@@ -244,37 +247,6 @@ static void write_global_extended_header(const unsigned char *sha1)
free(ext_header.buf);
}
-static void traverse_tree(struct tree_desc *tree, struct strbuf *path)
-{
- int pathlen = path->len;
- struct name_entry entry;
-
- while (tree_entry(tree, &entry)) {
- void *eltbuf;
- char elttype[20];
- unsigned long eltsize;
-
- eltbuf = read_sha1_file(entry.sha1, elttype, &eltsize);
- if (!eltbuf)
- die("cannot read %s", sha1_to_hex(entry.sha1));
-
- path->len = pathlen;
- strbuf_append_string(path, entry.path);
- if (S_ISDIR(entry.mode))
- strbuf_append_string(path, "/");
-
- write_entry(entry.sha1, path, entry.mode, eltbuf, eltsize);
-
- if (S_ISDIR(entry.mode)) {
- struct tree_desc subtree;
- subtree.buf = eltbuf;
- subtree.size = eltsize;
- traverse_tree(&subtree, path);
- }
- free(eltbuf);
- }
-}
-
static int git_tar_config(const char *var, const char *value)
{
if (!strcmp(var, "tar.umask")) {
@@ -291,50 +263,95 @@ static int git_tar_config(const char *var, const char *value)
static int generate_tar(int argc, const char **argv, const char *prefix)
{
- unsigned char sha1[20], tree_sha1[20];
- struct commit *commit;
- struct tree_desc tree;
- struct strbuf current_path;
- void *buffer;
-
- current_path.buf = xmalloc(PATH_MAX);
- current_path.alloc = PATH_MAX;
- current_path.len = current_path.eof = 0;
+ struct archiver_args args;
+ int result;
+ char *base = NULL;
git_config(git_tar_config);
- switch (argc) {
- case 3:
- strbuf_append_string(&current_path, argv[2]);
- strbuf_append_string(&current_path, "/");
- /* FALLTHROUGH */
- case 2:
- if (get_sha1(argv[1], sha1))
- die("Not a valid object name %s", argv[1]);
- break;
- default:
+ memset(&args, 0, sizeof(args));
+ if (argc != 2 && argc != 3)
usage(tar_tree_usage);
+ if (argc == 3) {
+ int baselen = strlen(argv[2]);
+ base = xmalloc(baselen + 2);
+ memcpy(base, argv[2], baselen);
+ base[baselen] = '/';
+ base[baselen + 1] = '\0';
}
+ args.base = base;
+ parse_treeish_arg(argv + 1, &args, NULL);
- commit = lookup_commit_reference_gently(sha1, 1);
- if (commit) {
- write_global_extended_header(commit->object.sha1);
- archive_time = commit->date;
- } else
- archive_time = time(NULL);
-
- tree.buf = buffer = read_object_with_reference(sha1, tree_type,
- &tree.size, tree_sha1);
- if (!tree.buf)
- die("not a reference to a tag, commit or tree object: %s",
- sha1_to_hex(sha1));
-
- if (current_path.len > 0)
- write_entry(tree_sha1, &current_path, 040777, NULL, 0);
- traverse_tree(&tree, &current_path);
- write_trailer();
+ result = write_tar_archive(&args);
+ free(base);
+
+ return result;
+}
+
+static int write_tar_entry(const unsigned char *sha1,
+ const char *base, int baselen,
+ const char *filename, unsigned mode, int stage)
+{
+ static struct strbuf path;
+ int filenamelen = strlen(filename);
+ void *buffer;
+ char type[20];
+ unsigned long size;
+
+ if (!path.alloc) {
+ path.buf = xmalloc(PATH_MAX);
+ path.alloc = PATH_MAX;
+ path.len = path.eof = 0;
+ }
+ if (path.alloc < baselen + filenamelen) {
+ free(path.buf);
+ path.buf = xmalloc(baselen + filenamelen);
+ path.alloc = baselen + filenamelen;
+ }
+ memcpy(path.buf, base, baselen);
+ memcpy(path.buf + baselen, filename, filenamelen);
+ path.len = baselen + filenamelen;
+ if (S_ISDIR(mode)) {
+ strbuf_append_string(&path, "/");
+ buffer = NULL;
+ size = 0;
+ } else {
+ buffer = read_sha1_file(sha1, type, &size);
+ if (!buffer)
+ die("cannot read %s", sha1_to_hex(sha1));
+ }
+
+ write_entry(sha1, &path, mode, buffer, size);
free(buffer);
- free(current_path.buf);
+
+ return READ_TREE_RECURSIVE;
+}
+
+int write_tar_archive(struct archiver_args *args)
+{
+ int plen = args->base ? strlen(args->base) : 0;
+
+ git_config(git_tar_config);
+
+ archive_time = args->time;
+ verbose = args->verbose;
+
+ if (args->commit_sha1)
+ write_global_extended_header(args->commit_sha1);
+
+ if (args->base && plen > 0 && args->base[plen - 1] == '/') {
+ char *base = xstrdup(args->base);
+ int baselen = strlen(base);
+
+ while (baselen > 0 && base[baselen - 1] == '/')
+ base[--baselen] = '\0';
+ write_tar_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
+ free(base);
+ }
+ read_tree_recursive(args->tree, args->base, plen, 0,
+ args->pathspec, write_tar_entry);
+ write_trailer();
+
return 0;
}
diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c
new file mode 100644
index 000000000..059686567
--- /dev/null
+++ b/builtin-upload-archive.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2006 Franck Bui-Huu
+ */
+#include <time.h>
+#include "cache.h"
+#include "builtin.h"
+#include "archive.h"
+#include "pkt-line.h"
+#include "sideband.h"
+#include <sys/wait.h>
+#include <sys/poll.h>
+
+static const char upload_archive_usage[] =
+ "git-upload-archive <repo>";
+
+static const char deadchild[] =
+"git-upload-archive: archiver died with error";
+
+static const char lostchild[] =
+"git-upload-archive: archiver process was lost";
+
+
+static int run_upload_archive(int argc, const char **argv, const char *prefix)
+{
+ struct archiver ar;
+ const char *sent_argv[MAX_ARGS];
+ const char *arg_cmd = "argument ";
+ char *p, buf[4096];
+ int treeish_idx;
+ int sent_argc;
+ int len;
+
+ if (argc != 2)
+ usage(upload_archive_usage);
+
+ if (strlen(argv[1]) > sizeof(buf))
+ die("insanely long repository name");
+
+ strcpy(buf, argv[1]); /* enter-repo smudges its argument */
+
+ if (!enter_repo(buf, 0))
+ die("not a git archive");
+
+ /* put received options in sent_argv[] */
+ sent_argc = 1;
+ sent_argv[0] = "git-upload-archive";
+ for (p = buf;;) {
+ /* This will die if not enough free space in buf */
+ len = packet_read_line(0, p, (buf + sizeof buf) - p);
+ if (len == 0)
+ break; /* got a flush */
+ if (sent_argc > MAX_ARGS - 2)
+ die("Too many options (>29)");
+
+ if (p[len-1] == '\n') {
+ p[--len] = 0;
+ }
+ if (len < strlen(arg_cmd) ||
+ strncmp(arg_cmd, p, strlen(arg_cmd)))
+ die("'argument' token or flush expected");
+
+ len -= strlen(arg_cmd);
+ memmove(p, p + strlen(arg_cmd), len);
+ sent_argv[sent_argc++] = p;
+ p += len;
+ *p++ = 0;
+ }
+ sent_argv[sent_argc] = NULL;
+
+ /* parse all options sent by the client */
+ treeish_idx = parse_archive_args(sent_argc, sent_argv, &ar);
+
+ parse_treeish_arg(sent_argv + treeish_idx, &ar.args, prefix);
+ parse_pathspec_arg(sent_argv + treeish_idx + 1, &ar.args);
+
+ return ar.write_archive(&ar.args);
+}
+
+static void error_clnt(const char *fmt, ...)
+{
+ char buf[1024];
+ va_list params;
+ int len;
+
+ va_start(params, fmt);
+ len = vsprintf(buf, fmt, params);
+ va_end(params);
+ send_sideband(1, 3, buf, len, LARGE_PACKET_MAX);
+ die("sent error to the client: %s", buf);
+}
+
+static void process_input(int child_fd, int band)
+{
+ char buf[16384];
+ ssize_t sz = read(child_fd, buf, sizeof(buf));
+ if (sz < 0) {
+ if (errno != EINTR)
+ error_clnt("read error: %s\n", strerror(errno));
+ return;
+ }
+ send_sideband(1, band, buf, sz, LARGE_PACKET_MAX);
+}
+
+int cmd_upload_archive(int argc, const char **argv, const char *prefix)
+{
+ pid_t writer;
+ int fd1[2], fd2[2];
+ /*
+ * Set up sideband subprocess.
+ *
+ * We (parent) monitor and read from child, sending its fd#1 and fd#2
+ * multiplexed out to our fd#1. If the child dies, we tell the other
+ * end over channel #3.
+ */
+ if (pipe(fd1) < 0 || pipe(fd2) < 0) {
+ int err = errno;
+ packet_write(1, "NACK pipe failed on the remote side\n");
+ die("upload-archive: %s", strerror(err));
+ }
+ writer = fork();
+ if (writer < 0) {
+ int err = errno;
+ packet_write(1, "NACK fork failed on the remote side\n");
+ die("upload-archive: %s", strerror(err));
+ }
+ if (!writer) {
+ /* child - connect fd#1 and fd#2 to the pipe */
+ dup2(fd1[1], 1);
+ dup2(fd2[1], 2);
+ close(fd1[1]); close(fd2[1]);
+ close(fd1[0]); close(fd2[0]); /* we do not read from pipe */
+
+ exit(run_upload_archive(argc, argv, prefix));
+ }
+
+ /* parent - read from child, multiplex and send out to fd#1 */
+ close(fd1[1]); close(fd2[1]); /* we do not write to pipe */
+ packet_write(1, "ACK\n");
+ packet_flush(1);
+
+ while (1) {
+ struct pollfd pfd[2];
+ int status;
+
+ pfd[0].fd = fd1[0];
+ pfd[0].events = POLLIN;
+ pfd[1].fd = fd2[0];
+ pfd[1].events = POLLIN;
+ if (poll(pfd, 2, -1) < 0) {
+ if (errno != EINTR) {
+ error("poll failed resuming: %s",
+ strerror(errno));
+ sleep(1);
+ }
+ continue;
+ }
+ if (pfd[0].revents & POLLIN)
+ /* Data stream ready */
+ process_input(pfd[0].fd, 1);
+ if (pfd[1].revents & POLLIN)
+ /* Status stream ready */
+ process_input(pfd[1].fd, 2);
+ /* Always finish to read data when available */
+ if ((pfd[0].revents | pfd[1].revents) & POLLIN)
+ continue;
+
+ if (waitpid(writer, &status, 0) < 0)
+ error_clnt("%s", lostchild);
+ else if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
+ error_clnt("%s", deadchild);
+ packet_flush(1);
+ break;
+ }
+ return 0;
+}
diff --git a/builtin-zip-tree.c b/builtin-zip-tree.c
index 1c1f6830c..52d4b7a17 100644
--- a/builtin-zip-tree.c
+++ b/builtin-zip-tree.c
@@ -8,10 +8,12 @@
#include "tree.h"
#include "quote.h"
#include "builtin.h"
+#include "archive.h"
static const char zip_tree_usage[] =
"git-zip-tree [-0|...|-9] <tree-ish> [ <base> ]";
+static int verbose;
static int zip_date;
static int zip_time;
@@ -163,6 +165,8 @@ static int write_zip_entry(const unsigned char *sha1,
crc = crc32(0, Z_NULL, 0);
path = construct_path(base, baselen, filename, S_ISDIR(mode), &pathlen);
+ if (verbose)
+ fprintf(stderr, "%s\n", path);
if (pathlen > 0xffff) {
error("path too long (%d chars, SHA1: %s): %s", pathlen,
sha1_to_hex(sha1), path);
@@ -351,3 +355,44 @@ int cmd_zip_tree(int argc, const char **argv, const char *prefix)
return 0;
}
+
+int write_zip_archive(struct archiver_args *args)
+{
+ int plen = strlen(args->base);
+
+ dos_time(&args->time, &zip_date, &zip_time);
+
+ zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);
+ zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
+ verbose = args->verbose;
+
+ if (args->base && plen > 0 && args->base[plen - 1] == '/') {
+ char *base = xstrdup(args->base);
+ int baselen = strlen(base);
+
+ while (baselen > 0 && base[baselen - 1] == '/')
+ base[--baselen] = '\0';
+ write_zip_entry(args->tree->object.sha1, "", 0, base, 040777, 0);
+ free(base);
+ }
+ read_tree_recursive(args->tree, args->base, plen, 0,
+ args->pathspec, write_zip_entry);
+ write_zip_trailer(args->commit_sha1);
+
+ free(zip_dir);
+
+ return 0;
+}
+
+void *parse_extra_zip_args(int argc, const char **argv)
+{
+ for (; argc > 0; argc--, argv++) {
+ const char *arg = argv[0];
+
+ if (arg[0] == '-' && isdigit(arg[1]) && arg[2] == '\0')
+ zlib_compression_level = arg[1] - '0';
+ else
+ die("Unknown argument for zip format: %s", arg);
+ }
+ return NULL;
+}
diff --git a/builtin.h b/builtin.h
index 37915bce4..fc704bae1 100644
--- a/builtin.h
+++ b/builtin.h
@@ -14,6 +14,7 @@ extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
extern int cmd_add(int argc, const char **argv, const char *prefix);
extern int cmd_apply(int argc, const char **argv, const char *prefix);
+extern int cmd_archive(int argc, const char **argv, const char *prefix);
extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
@@ -47,6 +48,7 @@ extern int cmd_repo_config(int argc, const char **argv, const char *prefix);
extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
extern int cmd_rm(int argc, const char **argv, const char *prefix);
+extern int cmd_runstatus(int argc, const char **argv, const char *prefix);
extern int cmd_show(int argc, const char **argv, const char *prefix);
extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
@@ -56,10 +58,12 @@ extern int cmd_zip_tree(int argc, const char **argv, const char *prefix);
extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
extern int cmd_update_index(int argc, const char **argv, const char *prefix);
extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
+extern int cmd_upload_archive(int argc, const char **argv, const char *prefix);
extern int cmd_upload_tar(int argc, const char **argv, const char *prefix);
extern int cmd_version(int argc, const char **argv, const char *prefix);
extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
extern int cmd_write_tree(int argc, const char **argv, const char *prefix);
extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);
+extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);
#endif
diff --git a/cache.h b/cache.h
index 8d099979d..6def15516 100644
--- a/cache.h
+++ b/cache.h
@@ -259,7 +259,7 @@ extern int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
extern int write_sha1_to_fd(int fd, const unsigned char *sha1);
extern int move_temp_to_file(const char *tmpfile, const char *filename);
-extern int has_sha1_pack(const unsigned char *sha1);
+extern int has_sha1_pack(const unsigned char *sha1, const char **ignore);
extern int has_sha1_file(const unsigned char *sha1);
extern void *map_sha1_file(const unsigned char *sha1, unsigned long *);
extern int legacy_loose_object(unsigned char *);
@@ -286,9 +286,9 @@ extern int get_sha1(const char *str, unsigned char *sha1);
extern int get_sha1_hex(const char *hex, unsigned char *sha1);
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
extern int read_ref(const char *filename, unsigned char *sha1);
-extern const char *resolve_ref(const char *path, unsigned char *sha1, int);
-extern int create_symref(const char *git_HEAD, const char *refs_heads_master);
-extern int validate_symref(const char *git_HEAD);
+extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
+extern int create_symref(const char *ref, const char *refs_heads_master);
+extern int validate_symref(const char *ref);
extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);
diff --git a/color.c b/color.c
new file mode 100644
index 000000000..d8c8399d5
--- /dev/null
+++ b/color.c
@@ -0,0 +1,176 @@
+#include "color.h"
+#include "cache.h"
+#include "git-compat-util.h"
+
+#include <stdarg.h>
+
+#define COLOR_RESET "\033[m"
+
+static int parse_color(const char *name, int len)
+{
+ static const char * const color_names[] = {
+ "normal", "black", "red", "green", "yellow",
+ "blue", "magenta", "cyan", "white"
+ };
+ char *end;
+ int i;
+ for (i = 0; i < ARRAY_SIZE(color_names); i++) {
+ const char *str = color_names[i];
+ if (!strncasecmp(name, str, len) && !str[len])
+ return i - 1;
+ }
+ i = strtol(name, &end, 10);
+ if (*name && !*end && i >= -1 && i <= 255)
+ return i;
+ return -2;
+}
+
+static int parse_attr(const char *name, int len)
+{
+ static const int attr_values[] = { 1, 2, 4, 5, 7 };
+ static const char * const attr_names[] = {
+ "bold", "dim", "ul", "blink", "reverse"
+ };
+ int i;
+ for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
+ const char *str = attr_names[i];
+ if (!strncasecmp(name, str, len) && !str[len])
+ return attr_values[i];
+ }
+ return -1;
+}
+
+void color_parse(const char *value, const char *var, char *dst)
+{
+ const char *ptr = value;
+ int attr = -1;
+ int fg = -2;
+ int bg = -2;
+
+ if (!strcasecmp(value, "reset")) {
+ strcpy(dst, "\033[m");
+ return;
+ }
+
+ /* [fg [bg]] [attr] */
+ while (*ptr) {
+ const char *word = ptr;
+ int val, len = 0;
+
+ while (word[len] && !isspace(word[len]))
+ len++;
+
+ ptr = word + len;
+ while (*ptr && isspace(*ptr))
+ ptr++;
+
+ val = parse_color(word, len);
+ if (val >= -1) {
+ if (fg == -2) {
+ fg = val;
+ continue;
+ }
+ if (bg == -2) {
+ bg = val;
+ continue;
+ }
+ goto bad;
+ }
+ val = parse_attr(word, len);
+ if (val < 0 || attr != -1)
+ goto bad;
+ attr = val;
+ }
+
+ if (attr >= 0 || fg >= 0 || bg >= 0) {
+ int sep = 0;
+
+ *dst++ = '\033';
+ *dst++ = '[';
+ if (attr >= 0) {
+ *dst++ = '0' + attr;
+ sep++;
+ }
+ if (fg >= 0) {
+ if (sep++)
+ *dst++ = ';';
+ if (fg < 8) {
+ *dst++ = '3';
+ *dst++ = '0' + fg;
+ } else {
+ dst += sprintf(dst, "38;5;%d", fg);
+ }
+ }
+ if (bg >= 0) {
+ if (sep++)
+ *dst++ = ';';
+ if (bg < 8) {
+ *dst++ = '4';
+ *dst++ = '0' + bg;
+ } else {
+ dst += sprintf(dst, "48;5;%d", bg);
+ }
+ }
+ *dst++ = 'm';
+ }
+ *dst = 0;
+ return;
+bad:
+ die("bad config value '%s' for variable '%s'", value, var);
+}
+
+int git_config_colorbool(const char *var, const char *value)
+{
+ if (!value)
+ return 1;
+ if (!strcasecmp(value, "auto")) {
+ if (isatty(1) || (pager_in_use && pager_use_color)) {
+ char *term = getenv("TERM");
+ if (term && strcmp(term, "dumb"))
+ return 1;
+ }
+ return 0;
+ }
+ if (!strcasecmp(value, "never"))
+ return 0;
+ if (!strcasecmp(value, "always"))
+ return 1;
+ return git_config_bool(var, value);
+}
+
+static int color_vprintf(const char *color, const char *fmt,
+ va_list args, const char *trail)
+{
+ int r = 0;
+
+ if (*color)
+ r += printf("%s", color);
+ r += vprintf(fmt, args);
+ if (*color)
+ r += printf("%s", COLOR_RESET);
+ if (trail)
+ r += printf("%s", trail);
+ return r;
+}
+
+
+
+int color_printf(const char *color, const char *fmt, ...)
+{
+ va_list args;
+ int r;
+ va_start(args, fmt);
+ r = color_vprintf(color, fmt, args, NULL);
+ va_end(args);
+ return r;
+}
+
+int color_printf_ln(const char *color, const char *fmt, ...)
+{
+ va_list args;
+ int r;
+ va_start(args, fmt);
+ r = color_vprintf(color, fmt, args, "\n");
+ va_end(args);
+ return r;
+}
diff --git a/color.h b/color.h
new file mode 100644
index 000000000..88bb8ff1b
--- /dev/null
+++ b/color.h
@@ -0,0 +1,12 @@
+#ifndef COLOR_H
+#define COLOR_H
+
+/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
+#define COLOR_MAXLEN 24
+
+int git_config_colorbool(const char *var, const char *value);
+void color_parse(const char *var, const char *value, char *dst);
+int color_printf(const char *color, const char *fmt, ...);
+int color_printf_ln(const char *color, const char *fmt, ...);
+
+#endif /* COLOR_H */
diff --git a/daemon.c b/daemon.c
index b14d8083b..a2954a045 100644
--- a/daemon.c
+++ b/daemon.c
@@ -325,7 +325,14 @@ static int upload_pack(void)
return -1;
}
+static int upload_archive(void)
+{
+ execl_git_cmd("upload-archive", ".", NULL);
+ return -1;
+}
+
static struct daemon_service daemon_service[] = {
+ { "upload-archive", "uploadarch", upload_archive, 0, 1 },
{ "upload-pack", "uploadpack", upload_pack, 1, 1 },
};
diff --git a/describe.c b/describe.c
index ab192f83a..f4029ee74 100644
--- a/describe.c
+++ b/describe.c
@@ -53,7 +53,7 @@ static void add_to_known_names(const char *path,
names = ++idx;
}
-static int get_name(const char *path, const unsigned char *sha1)
+static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct commit *commit = lookup_commit_reference_gently(sha1, 1);
struct object *object;
@@ -113,7 +113,7 @@ static void describe(const char *arg, int last_one)
if (!initialized) {
initialized = 1;
- for_each_ref(get_name);
+ for_each_ref(get_name, NULL);
qsort(name_array, names, sizeof(*name_array), compare_names);
}
diff --git a/diff.c b/diff.c
index 663886570..443e24861 100644
--- a/diff.c
+++ b/diff.c
@@ -10,6 +10,7 @@
#include "diffcore.h"
#include "delta.h"
#include "xdiff-interface.h"
+#include "color.h"
static int use_size_cache;
@@ -17,8 +18,7 @@ static int diff_detect_rename_default;
static int diff_rename_limit_default = -1;
static int diff_use_color_default;
-/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
-static char diff_colors[][24] = {
+static char diff_colors[][COLOR_MAXLEN] = {
"\033[m", /* reset */
"", /* normal */
"\033[1m", /* bold */
@@ -45,119 +45,6 @@ static int parse_diff_color_slot(const char *var, int ofs)
die("bad config variable '%s'", var);
}
-static int parse_color(const char *name, int len)
-{
- static const char * const color_names[] = {
- "normal", "black", "red", "green", "yellow",
- "blue", "magenta", "cyan", "white"
- };
- char *end;
- int i;
- for (i = 0; i < ARRAY_SIZE(color_names); i++) {
- const char *str = color_names[i];
- if (!strncasecmp(name, str, len) && !str[len])
- return i - 1;
- }
- i = strtol(name, &end, 10);
- if (*name && !*end && i >= -1 && i <= 255)
- return i;
- return -2;
-}
-
-static int parse_attr(const char *name, int len)
-{
- static const int attr_values[] = { 1, 2, 4, 5, 7 };
- static const char * const attr_names[] = {
- "bold", "dim", "ul", "blink", "reverse"
- };
- int i;
- for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
- const char *str = attr_names[i];
- if (!strncasecmp(name, str, len) && !str[len])
- return attr_values[i];
- }
- return -1;
-}
-
-static void parse_diff_color_value(const char *value, const char *var, char *dst)
-{
- const char *ptr = value;
- int attr = -1;
- int fg = -2;
- int bg = -2;
-
- if (!strcasecmp(value, "reset")) {
- strcpy(dst, "\033[m");
- return;
- }
-
- /* [fg [bg]] [attr] */
- while (*ptr) {
- const char *word = ptr;
- int val, len = 0;
-
- while (word[len] && !isspace(word[len]))
- len++;
-
- ptr = word + len;
- while (*ptr && isspace(*ptr))
- ptr++;
-
- val = parse_color(word, len);
- if (val >= -1) {
- if (fg == -2) {
- fg = val;
- continue;
- }
- if (bg == -2) {
- bg = val;
- continue;
- }
- goto bad;
- }
- val = parse_attr(word, len);
- if (val < 0 || attr != -1)
- goto bad;
- attr = val;
- }
-
- if (attr >= 0 || fg >= 0 || bg >= 0) {
- int sep = 0;
-
- *dst++ = '\033';
- *dst++ = '[';
- if (attr >= 0) {
- *dst++ = '0' + attr;
- sep++;
- }
- if (fg >= 0) {
- if (sep++)
- *dst++ = ';';
- if (fg < 8) {
- *dst++ = '3';
- *dst++ = '0' + fg;
- } else {
- dst += sprintf(dst, "38;5;%d", fg);
- }
- }
- if (bg >= 0) {
- if (sep++)
- *dst++ = ';';
- if (bg < 8) {
- *dst++ = '4';
- *dst++ = '0' + bg;
- } else {
- dst += sprintf(dst, "48;5;%d", bg);
- }
- }
- *dst++ = 'm';
- }
- *dst = 0;
- return;
-bad:
- die("bad config value '%s' for variable '%s'", value, var);
-}
-
/*
* These are to give UI layer defaults.
* The core-level commands such as git-diff-files should
@@ -171,22 +58,7 @@ int git_diff_ui_config(const char *var, const char *value)
return 0;
}
if (!strcmp(var, "diff.color")) {
- if (!value)
- diff_use_color_default = 1; /* bool */
- else if (!strcasecmp(value, "auto")) {
- diff_use_color_default = 0;
- if (isatty(1) || (pager_in_use && pager_use_color)) {
- char *term = getenv("TERM");
- if (term && strcmp(term, "dumb"))
- diff_use_color_default = 1;
- }
- }
- else if (!strcasecmp(value, "never"))
- diff_use_color_default = 0;
- else if (!strcasecmp(value, "always"))
- diff_use_color_default = 1;
- else
- diff_use_color_default = git_config_bool(var, value);
+ diff_use_color_default = git_config_colorbool(var, value);
return 0;
}
if (!strcmp(var, "diff.renames")) {
@@ -201,7 +73,7 @@ int git_diff_ui_config(const char *var, const char *value)
}
if (!strncmp(var, "diff.color.", 11)) {
int slot = parse_diff_color_slot(var, 11);
- parse_diff_color_value(value, var, diff_colors[slot]);
+ color_parse(value, var, diff_colors[slot]);
return 0;
}
return git_default_config(var, value);
@@ -2593,6 +2465,9 @@ void diff_flush(struct diff_options *options)
}
}
+ if (output_format & DIFF_FORMAT_CALLBACK)
+ options->format_callback(q, options, options->format_callback_data);
+
for (i = 0; i < q->nr; i++)
diff_free_filepair(q->queue[i]);
free_queue:
diff --git a/diff.h b/diff.h
index b007240a5..b60a02e62 100644
--- a/diff.h
+++ b/diff.h
@@ -8,6 +8,7 @@
struct rev_info;
struct diff_options;
+struct diff_queue_struct;
typedef void (*change_fn_t)(struct diff_options *options,
unsigned old_mode, unsigned new_mode,
@@ -20,6 +21,9 @@ typedef void (*add_remove_fn_t)(struct diff_options *options,
const unsigned char *sha1,
const char *base, const char *path);
+typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
+ struct diff_options *options, void *data);
+
#define DIFF_FORMAT_RAW 0x0001
#define DIFF_FORMAT_DIFFSTAT 0x0002
#define DIFF_FORMAT_SUMMARY 0x0004
@@ -35,6 +39,8 @@ typedef void (*add_remove_fn_t)(struct diff_options *options,
*/
#define DIFF_FORMAT_NO_OUTPUT 0x0080
+#define DIFF_FORMAT_CALLBACK 0x0100
+
struct diff_options {
const char *filter;
const char *orderfile;
@@ -68,6 +74,8 @@ struct diff_options {
int *pathlens;
change_fn_t change;
add_remove_fn_t add_remove;
+ diff_format_fn_t format_callback;
+ void *format_callback_data;
};
enum color_diff {
diff --git a/dir.c b/dir.c
index 5a40d8ff8..e2f472ba7 100644
--- a/dir.c
+++ b/dir.c
@@ -397,3 +397,10 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i
qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
return dir->nr;
}
+
+int
+file_exists(const char *f)
+{
+ struct stat sb;
+ return stat(f, &sb) == 0;
+}
diff --git a/dir.h b/dir.h
index 56a1b7fce..313f8ab64 100644
--- a/dir.h
+++ b/dir.h
@@ -47,5 +47,6 @@ 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);
+extern int file_exists(const char *);
#endif
diff --git a/fetch-clone.c b/fetch-clone.c
index c5cf4776f..b632ca043 100644
--- a/fetch-clone.c
+++ b/fetch-clone.c
@@ -1,6 +1,7 @@
#include "cache.h"
#include "exec_cmd.h"
#include "pkt-line.h"
+#include "sideband.h"
#include <sys/wait.h>
#include <sys/time.h>
@@ -114,36 +115,13 @@ static pid_t setup_sideband(int sideband, const char *me, int fd[2], int xd[2])
die("%s: unable to fork off sideband demultiplexer", me);
if (!side_pid) {
/* subprocess */
+ char buf[LARGE_PACKET_MAX];
+
close(fd[0]);
if (xd[0] != xd[1])
close(xd[1]);
- while (1) {
- char buf[1024];
- int len = packet_read_line(xd[0], buf, sizeof(buf));
- if (len == 0)
- break;
- if (len < 1)
- die("%s: protocol error: no band designator",
- me);
- len--;
- switch (buf[0] & 0xFF) {
- case 3:
- safe_write(2, "remote: ", 8);
- safe_write(2, buf+1, len);
- safe_write(2, "\n", 1);
- exit(1);
- case 2:
- safe_write(2, "remote: ", 8);
- safe_write(2, buf+1, len);
- continue;
- case 1:
- safe_write(fd[1], buf+1, len);
- continue;
- default:
- die("%s: protocol error: bad band #%d",
- me, (buf[0] & 0xFF));
- }
- }
+ if (recv_sideband(me, xd[0], fd[1], 2, buf, sizeof(buf)))
+ exit(1);
exit(0);
}
close(xd[0]);
diff --git a/fetch-pack.c b/fetch-pack.c
index 1b4d8272d..99ac08b2c 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -42,7 +42,7 @@ static void rev_list_push(struct commit *commit, int mark)
}
}
-static int rev_list_insert_ref(const char *path, const unsigned char *sha1)
+static int rev_list_insert_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *o = deref_tag(parse_object(sha1), path, 0);
@@ -143,7 +143,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
unsigned in_vain = 0;
int got_continue = 0;
- for_each_ref(rev_list_insert_ref);
+ for_each_ref(rev_list_insert_ref, NULL);
fetching = 0;
for ( ; refs ; refs = refs->next) {
@@ -166,10 +166,11 @@ static int find_common(int fd[2], unsigned char *result_sha1,
}
if (!fetching)
- packet_write(fd[1], "want %s%s%s%s\n",
+ packet_write(fd[1], "want %s%s%s%s%s\n",
sha1_to_hex(remote),
(multi_ack ? " multi_ack" : ""),
- (use_sideband ? " side-band" : ""),
+ (use_sideband == 2 ? " side-band-64k" : ""),
+ (use_sideband == 1 ? " side-band" : ""),
(use_thin_pack ? " thin-pack" : ""));
else
packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
@@ -252,7 +253,7 @@ done:
static struct commit_list *complete;
-static int mark_complete(const char *path, const unsigned char *sha1)
+static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *o = parse_object(sha1);
@@ -364,7 +365,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
}
}
- for_each_ref(mark_complete);
+ for_each_ref(mark_complete, NULL);
if (cutoff)
mark_recent_complete_commits(cutoff);
@@ -426,7 +427,12 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
fprintf(stderr, "Server supports multi_ack\n");
multi_ack = 1;
}
- if (server_supports("side-band")) {
+ if (server_supports("side-band-64k")) {
+ if (verbose)
+ fprintf(stderr, "Server supports side-band-64k\n");
+ use_sideband = 2;
+ }
+ else if (server_supports("side-band")) {
if (verbose)
fprintf(stderr, "Server supports side-band\n");
use_sideband = 1;
diff --git a/fetch.c b/fetch.c
index 34df8d37d..a2cbdfba8 100644
--- a/fetch.c
+++ b/fetch.c
@@ -201,7 +201,7 @@ static int interpret_target(char *target, unsigned char *sha1)
return -1;
}
-static int mark_complete(const char *path, const unsigned char *sha1)
+static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct commit *commit = lookup_commit_reference_gently(sha1, 1);
if (commit) {
@@ -274,7 +274,7 @@ int pull(int targets, char **target, const char **write_ref,
}
if (!get_recover)
- for_each_ref(mark_complete);
+ for_each_ref(mark_complete, NULL);
for (i = 0; i < targets; i++) {
if (interpret_target(target[i], &sha1[20 * i])) {
diff --git a/fsck-objects.c b/fsck-objects.c
index 4d994f3fc..46b628cb9 100644
--- a/fsck-objects.c
+++ b/fsck-objects.c
@@ -402,7 +402,7 @@ static void fsck_dir(int i, char *path)
static int default_refs;
-static int fsck_handle_ref(const char *refname, const unsigned char *sha1)
+static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *obj;
@@ -424,7 +424,7 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1)
static void get_default_heads(void)
{
- for_each_ref(fsck_handle_ref);
+ for_each_ref(fsck_handle_ref, NULL);
/*
* Not having any default heads isn't really fatal, but
@@ -458,15 +458,14 @@ static void fsck_object_dir(const char *path)
static int fsck_head_link(void)
{
unsigned char sha1[20];
- const char *git_HEAD = xstrdup(git_path("HEAD"));
- const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 1);
- int pfxlen = strlen(git_HEAD) - 4; /* strip .../.git/ part */
+ int flag;
+ const char *head_points_at = resolve_ref("HEAD", sha1, 1, &flag);
- if (!git_refs_heads_master)
+ if (!head_points_at || !(flag & REF_ISSYMREF))
return error("HEAD is not a symbolic ref");
- if (strncmp(git_refs_heads_master + pfxlen, "refs/heads/", 11))
+ if (strncmp(head_points_at, "refs/heads/", 11))
return error("HEAD points to something strange (%s)",
- git_refs_heads_master + pfxlen);
+ head_points_at);
if (is_null_sha1(sha1))
return error("HEAD: not a valid git pointer");
return 0;
diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh
index ec1eda20d..5450918be 100755
--- a/generate-cmdlist.sh
+++ b/generate-cmdlist.sh
@@ -12,6 +12,7 @@ struct cmdname_help common_cmds[] = {"
sort <<\EOF |
add
apply
+archive
bisect
branch
checkout
diff --git a/git-am.sh b/git-am.sh
index d0af786ae..afe322b20 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -166,10 +166,25 @@ fi
if test -d "$dotest"
then
- if test ",$#," != ",0," || ! tty -s
- then
- die "previous dotest directory $dotest still exists but mbox given."
- fi
+ case "$#,$skip$resolved" in
+ 0,*t*)
+ # Explicit resume command and we do not have file, so
+ # we are happy.
+ : ;;
+ 0,)
+ # No file input but without resume parameters; catch
+ # user error to feed us a patch from standard input
+ # when there is already .dotest. This is somewhat
+ # unreliable -- stdin could be /dev/null for example
+ # and the caller did not intend to feed us a patch but
+ # wanted to continue unattended.
+ tty -s
+ ;;
+ *)
+ false
+ ;;
+ esac ||
+ die "previous dotest directory $dotest still exists but mbox given."
resume=yes
else
# Make sure we are not given --skip nor --resolved
diff --git a/git-checkout.sh b/git-checkout.sh
index 580a9e8a2..c60e029b6 100755
--- a/git-checkout.sh
+++ b/git-checkout.sh
@@ -22,7 +22,7 @@ while [ "$#" != "0" ]; do
shift
[ -z "$newbranch" ] &&
die "git checkout: -b needs a branch name"
- [ -e "$GIT_DIR/refs/heads/$newbranch" ] &&
+ git-rev-parse --symbolic "heads/$newbranch" >&/dev/null &&
die "git checkout: branch $newbranch already exists"
git-check-ref-format "heads/$newbranch" ||
die "git checkout: we do not like '$newbranch' as a branch name."
@@ -51,7 +51,7 @@ while [ "$#" != "0" ]; do
fi
new="$rev"
new_name="$arg^0"
- if [ -f "$GIT_DIR/refs/heads/$arg" ]; then
+ if git-rev-parse "heads/$arg^0" >&/dev/null; then
branch="$arg"
fi
elif rev=$(git-rev-parse --verify "$arg^{tree}" 2>/dev/null)
diff --git a/git-commit.sh b/git-commit.sh
index 4cf3fab05..5a4c659b6 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -60,26 +60,6 @@ report () {
}
run_status () {
- (
- # We always show status for the whole tree.
- cd "$TOP"
-
- IS_INITIAL="$initial_commit"
- REFERENCE=HEAD
- case "$amend" in
- t)
- # If we are amending the initial commit, there
- # is no HEAD^1.
- if git-rev-parse --verify "HEAD^1" >/dev/null 2>&1
- then
- REFERENCE="HEAD^1"
- IS_INITIAL=
- else
- IS_INITIAL=t
- fi
- ;;
- esac
-
# If TMP_INDEX is defined, that means we are doing
# "--only" partial commit, and that index file is used
# to build the tree for the commit. Otherwise, if
@@ -96,85 +76,14 @@ run_status () {
export GIT_INDEX_FILE
fi
- case "$branch" in
- refs/heads/master) ;;
- *) echo "# On branch $branch" ;;
- esac
-
- if test -z "$IS_INITIAL"
- then
- git-diff-index -M --cached --name-status \
- --diff-filter=MDTCRA $REFERENCE |
- sed -e '
- s/\\/\\\\/g
- s/ /\\ /g
- ' |
- report "Updated but not checked in" "will commit"
- committable="$?"
- else
- echo '#
-# Initial commit
-#'
- git-ls-files |
- sed -e '
- s/\\/\\\\/g
- s/ /\\ /g
- s/^/A /
- ' |
- report "Updated but not checked in" "will commit"
-
- committable="$?"
- fi
-
- git-diff-files --name-status |
- sed -e '
- s/\\/\\\\/g
- s/ /\\ /g
- ' |
- 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
- hdr_shown=
- if test -f "$GIT_DIR/info/exclude"
- then
- git-ls-files --others $option \
- --exclude-from="$GIT_DIR/info/exclude" \
- --exclude-per-directory=.gitignore
- else
- git-ls-files --others $option \
- --exclude-per-directory=.gitignore
- fi |
- while read line; do
- if [ -z "$hdr_shown" ]; then
- echo '#'
- echo '# Untracked files:'
- echo '# (use "git add" to add to commit)'
- echo '#'
- hdr_shown=1
- fi
- echo "# $line"
- done
-
- if test -n "$verbose" -a -z "$IS_INITIAL"
- then
- git-diff-index --cached -M -p --diff-filter=MDTCRA $REFERENCE
- fi
- case "$committable" in
- 0)
- case "$amend" in
- t)
- echo "# No changes" ;;
- *)
- echo "nothing to commit" ;;
- esac
- exit 1 ;;
- esac
- exit 0
- )
+ case "$status_only" in
+ t) color= ;;
+ *) color=--nocolor ;;
+ esac
+ git-runstatus ${color} \
+ ${verbose:+--verbose} \
+ ${amend:+--amend} \
+ ${untracked_files:+--untracked}
}
trap '
diff --git a/git.c b/git.c
index 10db27bca..561be303d 100644
--- a/git.c
+++ b/git.c
@@ -220,6 +220,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
} commands[] = {
{ "add", cmd_add, RUN_SETUP },
{ "apply", cmd_apply },
+ { "archive", cmd_archive },
{ "cat-file", cmd_cat_file, RUN_SETUP },
{ "checkout-index", cmd_checkout_index, RUN_SETUP },
{ "check-ref-format", cmd_check_ref_format },
@@ -253,6 +254,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "rev-list", cmd_rev_list, RUN_SETUP },
{ "rev-parse", cmd_rev_parse, RUN_SETUP },
{ "rm", cmd_rm, RUN_SETUP },
+ { "runstatus", cmd_runstatus, RUN_SETUP },
{ "show-branch", cmd_show_branch, RUN_SETUP },
{ "show", cmd_show, RUN_SETUP | USE_PAGER },
{ "stripspace", cmd_stripspace },
@@ -262,11 +264,13 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "unpack-objects", cmd_unpack_objects, RUN_SETUP },
{ "update-index", cmd_update_index, RUN_SETUP },
{ "update-ref", cmd_update_ref, RUN_SETUP },
+ { "upload-archive", cmd_upload_archive },
{ "upload-tar", cmd_upload_tar },
{ "version", cmd_version },
{ "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
{ "write-tree", cmd_write_tree, RUN_SETUP },
{ "verify-pack", cmd_verify_pack },
+ { "pack-refs", cmd_pack_refs, RUN_SETUP },
};
int i;
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index a81c8d4cf..c77270c7c 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -54,6 +54,13 @@ our $favicon = "++GITWEB_FAVICON++";
# source of projects list
our $projects_list = "++GITWEB_LIST++";
+# show repository only if this file exists
+# (only effective if this variable evaluates to true)
+our $export_ok = "++GITWEB_EXPORT_OK++";
+
+# only allow viewing of repositories also shown on the overview page
+our $strict_export = "++GITWEB_STRICT_EXPORT++";
+
# list of git base URLs used for URL to where fetch project from,
# i.e. full URL is "$git_base_url/$project"
our @git_base_url_list = ("++GITWEB_BASE_URL++");
@@ -182,9 +189,6 @@ do $GITWEB_CONFIG if -e $GITWEB_CONFIG;
# version of the core git binary
our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown";
-# path to the current git repository
-our $git_dir;
-
$projects_list ||= $projectroot;
# ======================================================================
@@ -196,23 +200,16 @@ if (defined $action) {
}
}
-our $project = ($cgi->param('p') || $ENV{'PATH_INFO'});
-if (defined $project) {
- $project =~ s|^/||;
- $project =~ s|/$||;
- $project = undef unless $project;
-}
+our $project = $cgi->param('p');
if (defined $project) {
- if (!validate_input($project)) {
- die_error(undef, "Invalid project parameter");
- }
- if (!(-d "$projectroot/$project")) {
- die_error(undef, "No such directory");
- }
- if (!(-e "$projectroot/$project/HEAD")) {
+ if (!validate_input($project) ||
+ !(-d "$projectroot/$project") ||
+ !(-e "$projectroot/$project/HEAD") ||
+ ($export_ok && !(-e "$projectroot/$project/$export_ok")) ||
+ ($strict_export && !project_in_list($project))) {
+ undef $project;
die_error(undef, "No such project");
}
- $git_dir = "$projectroot/$project";
}
our $file_name = $cgi->param('f');
@@ -259,7 +256,7 @@ if (defined $hash_parent_base) {
our $page = $cgi->param('pg');
if (defined $page) {
- if ($page =~ m/[^0-9]$/) {
+ if ($page =~ m/[^0-9]/) {
die_error(undef, "Invalid page parameter");
}
}
@@ -272,6 +269,43 @@ if (defined $searchtext) {
$searchtext = quotemeta $searchtext;
}
+# now read PATH_INFO and use it as alternative to parameters
+sub evaluate_path_info {
+ return if defined $project;
+ my $path_info = $ENV{"PATH_INFO"};
+ return if !$path_info;
+ $path_info =~ s,(^/|/$),,gs;
+ $path_info = validate_input($path_info);
+ return if !$path_info;
+ $project = $path_info;
+ while ($project && !-e "$projectroot/$project/HEAD") {
+ $project =~ s,/*[^/]*$,,;
+ }
+ if (!$project ||
+ ($export_ok && !-e "$projectroot/$project/$export_ok") ||
+ ($strict_export && !project_in_list($project))) {
+ undef $project;
+ return;
+ }
+ # do not change any parameters if an action is given using the query string
+ return if $action;
+ if ($path_info =~ m,^$project/([^/]+)/(.+)$,) {
+ # we got "project.git/branch/filename"
+ $action ||= "blob_plain";
+ $hash_base ||= validate_input($1);
+ $file_name ||= validate_input($2);
+ } elsif ($path_info =~ m,^$project/([^/]+)$,) {
+ # we got "project.git/branch"
+ $action ||= "shortlog";
+ $hash ||= validate_input($1);
+ }
+}
+evaluate_path_info();
+
+# path to the current git repository
+our $git_dir;
+$git_dir = "$projectroot/$project" if $project;
+
# dispatch
my %actions = (
"blame" => \&git_blame2,
@@ -405,6 +439,12 @@ sub untabify {
return $line;
}
+sub project_in_list {
+ my $project = shift;
+ my @list = git_get_projects_list();
+ return @list && scalar(grep { $_->{'path'} eq $project } @list);
+}
+
## ----------------------------------------------------------------------
## HTML aware string manipulation
@@ -717,7 +757,8 @@ sub git_get_projects_list {
my $subdir = substr($File::Find::name, $pfxlen + 1);
# we check related file in $projectroot
- if (-e "$projectroot/$subdir/HEAD") {
+ if (-e "$projectroot/$subdir/HEAD" && (!$export_ok ||
+ -e "$projectroot/$subdir/$export_ok")) {
push @list, { path => $subdir };
$File::Find::prune = 1;
}
@@ -738,7 +779,8 @@ sub git_get_projects_list {
if (!defined $path) {
next;
}
- if (-e "$projectroot/$path/HEAD") {
+ if (-e "$projectroot/$path/HEAD" && (!$export_ok ||
+ -e "$projectroot/$path/$export_ok")) {
my $pr = {
path => $path,
owner => decode("utf8", $owner, Encode::FB_DEFAULT),
@@ -2527,11 +2569,7 @@ sub git_heads {
}
sub git_blob_plain {
- # blobs defined by non-textual hash id's can be cached
my $expires;
- if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
- $expires = "+1d";
- }
if (!defined $hash) {
if (defined $file_name) {
@@ -2541,7 +2579,11 @@ sub git_blob_plain {
} else {
die_error(undef, "No file name defined");
}
+ } elsif ($hash =~ m/^[0-9a-fA-F]{40}$/) {
+ # blobs defined by non-textual hash id's can be cached
+ $expires = "+1d";
}
+
my $type = shift;
open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
or die_error(undef, "Couldn't cat $file_name, $hash");
@@ -2569,11 +2611,7 @@ sub git_blob_plain {
}
sub git_blob {
- # blobs defined by non-textual hash id's can be cached
my $expires;
- if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
- $expires = "+1d";
- }
if (!defined $hash) {
if (defined $file_name) {
@@ -2583,7 +2621,11 @@ sub git_blob {
} else {
die_error(undef, "No file name defined");
}
+ } elsif ($hash =~ m/^[0-9a-fA-F]{40}$/) {
+ # blobs defined by non-textual hash id's can be cached
+ $expires = "+1d";
}
+
my ($have_blame) = gitweb_check_feature('blame');
open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
or die_error(undef, "Couldn't cat $file_name, $hash");
diff --git a/http-fetch.c b/http-fetch.c
index a113bb8c4..bc74f30f7 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -144,6 +144,19 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
return size;
}
+static int missing__target(int code, int result)
+{
+ return /* file:// URL -- do we ever use one??? */
+ (result == CURLE_FILE_COULDNT_READ_FILE) ||
+ /* http:// and https:// URL */
+ (code == 404 && result == CURLE_HTTP_RETURNED_ERROR) ||
+ /* ftp:// URL */
+ (code == 550 && result == CURLE_FTP_COULDNT_RETR_FILE)
+ ;
+}
+
+#define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
+
static void fetch_alternates(const char *base);
static void process_object_response(void *callback_data);
@@ -323,8 +336,7 @@ static void process_object_response(void *callback_data)
obj_req->state = COMPLETE;
/* Use alternates if necessary */
- if (obj_req->http_code == 404 ||
- obj_req->curl_result == CURLE_FILE_COULDNT_READ_FILE) {
+ if (missing_target(obj_req)) {
fetch_alternates(alt->base);
if (obj_req->repo->next != NULL) {
obj_req->repo =
@@ -537,8 +549,7 @@ static void process_alternates_response(void *callback_data)
return;
}
} else if (slot->curl_result != CURLE_OK) {
- if (slot->http_code != 404 &&
- slot->curl_result != CURLE_FILE_COULDNT_READ_FILE) {
+ if (!missing_target(slot)) {
got_alternates = -1;
return;
}
@@ -941,8 +952,7 @@ static int fetch_indices(struct alt_base *repo)
if (start_active_slot(slot)) {
run_active_slot(slot);
if (results.curl_result != CURLE_OK) {
- if (results.http_code == 404 ||
- results.curl_result == CURLE_FILE_COULDNT_READ_FILE) {
+ if (missing_target(&results)) {
repo->got_indices = 1;
free(buffer.buffer);
return 0;
@@ -1123,8 +1133,7 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1)
ret = error("Request for %s aborted", hex);
} else if (obj_req->curl_result != CURLE_OK &&
obj_req->http_code != 416) {
- if (obj_req->http_code == 404 ||
- obj_req->curl_result == CURLE_FILE_COULDNT_READ_FILE)
+ if (missing_target(obj_req))
ret = -1; /* Be silent, it is probably in a pack. */
else
ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",
diff --git a/http-push.c b/http-push.c
index 670ff007b..ecefdfd4f 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1864,7 +1864,7 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock)
static struct ref *local_refs, **local_tail;
static struct ref *remote_refs, **remote_tail;
-static int one_local_ref(const char *refname, const unsigned char *sha1)
+static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
struct ref *ref;
int len = strlen(refname) + 1;
@@ -1913,7 +1913,7 @@ static void one_remote_ref(char *refname)
static void get_local_heads(void)
{
local_tail = &local_refs;
- for_each_ref(one_local_ref);
+ for_each_ref(one_local_ref, NULL);
}
static void get_dav_remote_heads(void)
diff --git a/list-objects.c b/list-objects.c
new file mode 100644
index 000000000..f1fa21c39
--- /dev/null
+++ b/list-objects.c
@@ -0,0 +1,140 @@
+#include "cache.h"
+#include "tag.h"
+#include "commit.h"
+#include "tree.h"
+#include "blob.h"
+#include "diff.h"
+#include "tree-walk.h"
+#include "revision.h"
+#include "list-objects.h"
+
+static void process_blob(struct rev_info *revs,
+ struct blob *blob,
+ struct object_array *p,
+ struct name_path *path,
+ const char *name)
+{
+ struct object *obj = &blob->object;
+
+ if (!revs->blob_objects)
+ return;
+ if (obj->flags & (UNINTERESTING | SEEN))
+ return;
+ obj->flags |= SEEN;
+ name = xstrdup(name);
+ add_object(obj, p, path, name);
+}
+
+static void process_tree(struct rev_info *revs,
+ struct tree *tree,
+ struct object_array *p,
+ struct name_path *path,
+ const char *name)
+{
+ struct object *obj = &tree->object;
+ struct tree_desc desc;
+ struct name_entry entry;
+ struct name_path me;
+
+ if (!revs->tree_objects)
+ return;
+ if (obj->flags & (UNINTERESTING | SEEN))
+ return;
+ if (parse_tree(tree) < 0)
+ die("bad tree object %s", sha1_to_hex(obj->sha1));
+ obj->flags |= SEEN;
+ name = xstrdup(name);
+ add_object(obj, p, path, name);
+ me.up = path;
+ me.elem = name;
+ me.elem_len = strlen(name);
+
+ desc.buf = tree->buffer;
+ desc.size = tree->size;
+
+ while (tree_entry(&desc, &entry)) {
+ if (S_ISDIR(entry.mode))
+ process_tree(revs,
+ lookup_tree(entry.sha1),
+ p, &me, entry.path);
+ else
+ process_blob(revs,
+ lookup_blob(entry.sha1),
+ p, &me, entry.path);
+ }
+ free(tree->buffer);
+ tree->buffer = NULL;
+}
+
+static void mark_edge_parents_uninteresting(struct commit *commit,
+ struct rev_info *revs,
+ show_edge_fn show_edge)
+{
+ struct commit_list *parents;
+
+ for (parents = commit->parents; parents; parents = parents->next) {
+ struct commit *parent = parents->item;
+ if (!(parent->object.flags & UNINTERESTING))
+ continue;
+ mark_tree_uninteresting(parent->tree);
+ if (revs->edge_hint && !(parent->object.flags & SHOWN)) {
+ parent->object.flags |= SHOWN;
+ show_edge(parent);
+ }
+ }
+}
+
+void mark_edges_uninteresting(struct commit_list *list,
+ struct rev_info *revs,
+ show_edge_fn show_edge)
+{
+ for ( ; list; list = list->next) {
+ struct commit *commit = list->item;
+
+ if (commit->object.flags & UNINTERESTING) {
+ mark_tree_uninteresting(commit->tree);
+ continue;
+ }
+ mark_edge_parents_uninteresting(commit, revs, show_edge);
+ }
+}
+
+void traverse_commit_list(struct rev_info *revs,
+ void (*show_commit)(struct commit *),
+ void (*show_object)(struct object_array_entry *))
+{
+ int i;
+ struct commit *commit;
+ struct object_array objects = { 0, 0, NULL };
+
+ while ((commit = get_revision(revs)) != NULL) {
+ process_tree(revs, commit->tree, &objects, NULL, "");
+ show_commit(commit);
+ }
+ for (i = 0; i < revs->pending.nr; i++) {
+ struct object_array_entry *pending = revs->pending.objects + i;
+ struct object *obj = pending->item;
+ const char *name = pending->name;
+ if (obj->flags & (UNINTERESTING | SEEN))
+ continue;
+ if (obj->type == OBJ_TAG) {
+ obj->flags |= SEEN;
+ add_object_array(obj, name, &objects);
+ continue;
+ }
+ if (obj->type == OBJ_TREE) {
+ process_tree(revs, (struct tree *)obj, &objects,
+ NULL, name);
+ continue;
+ }
+ if (obj->type == OBJ_BLOB) {
+ process_blob(revs, (struct blob *)obj, &objects,
+ NULL, name);
+ continue;
+ }
+ die("unknown pending object %s (%s)",
+ sha1_to_hex(obj->sha1), name);
+ }
+ for (i = 0; i < objects.nr; i++)
+ show_object(&objects.objects[i]);
+}
diff --git a/list-objects.h b/list-objects.h
new file mode 100644
index 000000000..0f41391ec
--- /dev/null
+++ b/list-objects.h
@@ -0,0 +1,12 @@
+#ifndef LIST_OBJECTS_H
+#define LIST_OBJECTS_H
+
+typedef void (*show_commit_fn)(struct commit *);
+typedef void (*show_object_fn)(struct object_array_entry *);
+typedef void (*show_edge_fn)(struct commit *);
+
+void traverse_commit_list(struct rev_info *revs, show_commit_fn, show_object_fn);
+
+void mark_edges_uninteresting(struct commit_list *, struct rev_info *, show_edge_fn);
+
+#endif
diff --git a/receive-pack.c b/receive-pack.c
index 78f75da5c..abbcb6af0 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -12,7 +12,7 @@ static int report_status;
static char capabilities[] = "report-status";
static int capabilities_sent;
-static int show_ref(const char *path, const unsigned char *sha1)
+static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
if (capabilities_sent)
packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
@@ -25,9 +25,9 @@ static int show_ref(const char *path, const unsigned char *sha1)
static void write_head_info(void)
{
- for_each_ref(show_ref);
+ for_each_ref(show_ref, NULL);
if (!capabilities_sent)
- show_ref("capabilities^{}", null_sha1);
+ show_ref("capabilities^{}", null_sha1, 0, NULL);
}
diff --git a/refs.c b/refs.c
index 5e653141c..40f16af18 100644
--- a/refs.c
+++ b/refs.c
@@ -3,15 +3,165 @@
#include <errno.h>
+struct ref_list {
+ struct ref_list *next;
+ unsigned char flag; /* ISSYMREF? ISPACKED? */
+ unsigned char sha1[20];
+ char name[FLEX_ARRAY];
+};
+
+static const char *parse_ref_line(char *line, unsigned char *sha1)
+{
+ /*
+ * 42: the answer to everything.
+ *
+ * In this case, it happens to be the answer to
+ * 40 (length of sha1 hex representation)
+ * +1 (space in between hex and name)
+ * +1 (newline at the end of the line)
+ */
+ int len = strlen(line) - 42;
+
+ if (len <= 0)
+ return NULL;
+ if (get_sha1_hex(line, sha1) < 0)
+ return NULL;
+ if (!isspace(line[40]))
+ return NULL;
+ line += 41;
+ if (isspace(*line))
+ return NULL;
+ if (line[len] != '\n')
+ return NULL;
+ line[len] = 0;
+ return line;
+}
+
+static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
+ int flag, struct ref_list *list)
+{
+ int len;
+ struct ref_list **p = &list, *entry;
+
+ /* Find the place to insert the ref into.. */
+ while ((entry = *p) != NULL) {
+ int cmp = strcmp(entry->name, name);
+ if (cmp > 0)
+ break;
+
+ /* Same as existing entry? */
+ if (!cmp)
+ return list;
+ p = &entry->next;
+ }
+
+ /* Allocate it and add it in.. */
+ len = strlen(name) + 1;
+ entry = xmalloc(sizeof(struct ref_list) + len);
+ hashcpy(entry->sha1, sha1);
+ memcpy(entry->name, name, len);
+ entry->flag = flag;
+ entry->next = *p;
+ *p = entry;
+ return list;
+}
+
+static struct ref_list *get_packed_refs(void)
+{
+ static int did_refs = 0;
+ static struct ref_list *refs = NULL;
+
+ if (!did_refs) {
+ FILE *f = fopen(git_path("packed-refs"), "r");
+ if (f) {
+ struct ref_list *list = NULL;
+ char refline[PATH_MAX];
+ while (fgets(refline, sizeof(refline), f)) {
+ unsigned char sha1[20];
+ const char *name = parse_ref_line(refline, sha1);
+ if (!name)
+ continue;
+ list = add_ref(name, sha1, REF_ISPACKED, list);
+ }
+ fclose(f);
+ refs = list;
+ }
+ did_refs = 1;
+ }
+ return refs;
+}
+
+static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
+{
+ DIR *dir = opendir(git_path("%s", base));
+
+ if (dir) {
+ struct dirent *de;
+ int baselen = strlen(base);
+ char *ref = xmalloc(baselen + 257);
+
+ memcpy(ref, base, baselen);
+ if (baselen && base[baselen-1] != '/')
+ ref[baselen++] = '/';
+
+ while ((de = readdir(dir)) != NULL) {
+ unsigned char sha1[20];
+ struct stat st;
+ int flag;
+ int namelen;
+
+ if (de->d_name[0] == '.')
+ continue;
+ namelen = strlen(de->d_name);
+ if (namelen > 255)
+ continue;
+ if (has_extension(de->d_name, ".lock"))
+ continue;
+ memcpy(ref + baselen, de->d_name, namelen+1);
+ if (stat(git_path("%s", ref), &st) < 0)
+ continue;
+ if (S_ISDIR(st.st_mode)) {
+ list = get_ref_dir(ref, list);
+ continue;
+ }
+ if (!resolve_ref(ref, sha1, 1, &flag)) {
+ error("%s points nowhere!", ref);
+ continue;
+ }
+ list = add_ref(ref, sha1, flag, list);
+ }
+ free(ref);
+ closedir(dir);
+ }
+ return list;
+}
+
+static struct ref_list *get_loose_refs(void)
+{
+ static int did_refs = 0;
+ static struct ref_list *refs = NULL;
+
+ if (!did_refs) {
+ refs = get_ref_dir("refs", NULL);
+ did_refs = 1;
+ }
+ return refs;
+}
+
/* We allow "recursive" symbolic refs. Only within reason, though */
#define MAXDEPTH 5
-const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
+const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
{
int depth = MAXDEPTH, len;
char buffer[256];
+ static char ref_buffer[256];
+
+ if (flag)
+ *flag = 0;
for (;;) {
+ const char *path = git_path("%s", ref);
struct stat st;
char *buf;
int fd;
@@ -27,17 +177,31 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
* reading.
*/
if (lstat(path, &st) < 0) {
+ struct ref_list *list = get_packed_refs();
+ while (list) {
+ if (!strcmp(ref, list->name)) {
+ hashcpy(sha1, list->sha1);
+ if (flag)
+ *flag |= REF_ISPACKED;
+ return ref;
+ }
+ list = list->next;
+ }
if (reading || errno != ENOENT)
return NULL;
hashclr(sha1);
- return path;
+ return ref;
}
/* Follow "normalized" - ie "refs/.." symlinks by hand */
if (S_ISLNK(st.st_mode)) {
len = readlink(path, buffer, sizeof(buffer)-1);
if (len >= 5 && !memcmp("refs/", buffer, 5)) {
- path = git_path("%.*s", len, buffer);
+ buffer[len] = 0;
+ strcpy(ref_buffer, buffer);
+ ref = ref_buffer;
+ if (flag)
+ *flag |= REF_ISSYMREF;
continue;
}
}
@@ -62,19 +226,24 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
while (len && isspace(*buf))
buf++, len--;
while (len && isspace(buf[len-1]))
- buf[--len] = 0;
- path = git_path("%.*s", len, buf);
+ len--;
+ buf[len] = 0;
+ memcpy(ref_buffer, buf, len + 1);
+ ref = ref_buffer;
+ if (flag)
+ *flag |= REF_ISSYMREF;
}
if (len < 40 || get_sha1_hex(buffer, sha1))
return NULL;
- return path;
+ return ref;
}
-int create_symref(const char *git_HEAD, const char *refs_heads_master)
+int create_symref(const char *ref_target, const char *refs_heads_master)
{
const char *lockpath;
char ref[1000];
int fd, len, written;
+ const char *git_HEAD = git_path("%s", ref_target);
#ifndef NO_SYMLINK_HEAD
if (prefer_symlink_refs) {
@@ -112,104 +281,101 @@ int create_symref(const char *git_HEAD, const char *refs_heads_master)
return 0;
}
-int read_ref(const char *filename, unsigned char *sha1)
+int read_ref(const char *ref, unsigned char *sha1)
{
- if (resolve_ref(filename, sha1, 1))
+ if (resolve_ref(ref, sha1, 1, NULL))
return 0;
return -1;
}
-static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1), int trim)
+static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
+ void *cb_data)
{
- int retval = 0;
- DIR *dir = opendir(git_path("%s", base));
-
- if (dir) {
- struct dirent *de;
- int baselen = strlen(base);
- char *path = xmalloc(baselen + 257);
-
- if (!strncmp(base, "./", 2)) {
- base += 2;
- baselen -= 2;
+ int retval;
+ struct ref_list *packed = get_packed_refs();
+ struct ref_list *loose = get_loose_refs();
+
+ while (packed && loose) {
+ struct ref_list *entry;
+ int cmp = strcmp(packed->name, loose->name);
+ if (!cmp) {
+ packed = packed->next;
+ continue;
}
- memcpy(path, base, baselen);
- if (baselen && base[baselen-1] != '/')
- path[baselen++] = '/';
-
- while ((de = readdir(dir)) != NULL) {
- unsigned char sha1[20];
- struct stat st;
- int namelen;
+ if (cmp > 0) {
+ entry = loose;
+ loose = loose->next;
+ } else {
+ entry = packed;
+ packed = packed->next;
+ }
+ if (strncmp(base, entry->name, trim))
+ continue;
+ if (is_null_sha1(entry->sha1))
+ continue;
+ if (!has_sha1_file(entry->sha1)) {
+ error("%s does not point to a valid object!", entry->name);
+ continue;
+ }
+ retval = fn(entry->name + trim, entry->sha1,
+ entry->flag, cb_data);
+ if (retval)
+ return retval;
+ }
- if (de->d_name[0] == '.')
- continue;
- namelen = strlen(de->d_name);
- if (namelen > 255)
- continue;
- if (has_extension(de->d_name, ".lock"))
- continue;
- memcpy(path + baselen, de->d_name, namelen+1);
- if (stat(git_path("%s", path), &st) < 0)
- continue;
- if (S_ISDIR(st.st_mode)) {
- retval = do_for_each_ref(path, fn, trim);
- if (retval)
- break;
- continue;
- }
- if (read_ref(git_path("%s", path), sha1) < 0) {
- error("%s points nowhere!", path);
- continue;
- }
- if (!has_sha1_file(sha1)) {
- error("%s does not point to a valid "
- "commit object!", path);
- continue;
- }
- retval = fn(path + trim, sha1);
+ packed = packed ? packed : loose;
+ while (packed) {
+ if (!strncmp(base, packed->name, trim)) {
+ retval = fn(packed->name + trim, packed->sha1,
+ packed->flag, cb_data);
if (retval)
- break;
+ return retval;
}
- free(path);
- closedir(dir);
+ packed = packed->next;
}
- return retval;
+ return 0;
}
-int head_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int head_ref(each_ref_fn fn, void *cb_data)
{
unsigned char sha1[20];
- if (!read_ref(git_path("HEAD"), sha1))
- return fn("HEAD", sha1);
+ int flag;
+
+ if (resolve_ref("HEAD", sha1, 1, &flag))
+ return fn("HEAD", sha1, flag, cb_data);
return 0;
}
-int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int for_each_ref(each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref("refs", fn, 0);
+ return do_for_each_ref("refs/", fn, 0, cb_data);
}
-int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int for_each_tag_ref(each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref("refs/tags", fn, 10);
+ return do_for_each_ref("refs/tags/", fn, 10, cb_data);
}
-int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int for_each_branch_ref(each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref("refs/heads", fn, 11);
+ return do_for_each_ref("refs/heads/", fn, 11, cb_data);
}
-int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int for_each_remote_ref(each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref("refs/remotes", fn, 13);
+ return do_for_each_ref("refs/remotes/", fn, 13, cb_data);
}
+/* NEEDSWORK: This is only used by ssh-upload and it should go; the
+ * caller should do resolve_ref or read_ref like everybody else. Or
+ * maybe everybody else should use get_ref_sha1() instead of doing
+ * read_ref().
+ */
int get_ref_sha1(const char *ref, unsigned char *sha1)
{
if (check_ref_format(ref))
return -1;
- return read_ref(git_path("refs/%s", ref), sha1);
+ return read_ref(mkpath("refs/%s", ref), sha1);
}
/*
@@ -267,22 +433,13 @@ int check_ref_format(const char *ref)
static struct ref_lock *verify_lock(struct ref_lock *lock,
const unsigned char *old_sha1, int mustexist)
{
- char buf[40];
- int nr, fd = open(lock->ref_file, O_RDONLY);
- if (fd < 0 && (mustexist || errno != ENOENT)) {
- error("Can't verify ref %s", lock->ref_file);
- unlock_ref(lock);
- return NULL;
- }
- nr = read(fd, buf, 40);
- close(fd);
- if (nr != 40 || get_sha1_hex(buf, lock->old_sha1) < 0) {
- error("Can't verify ref %s", lock->ref_file);
+ if (!resolve_ref(lock->ref_name, lock->old_sha1, mustexist, NULL)) {
+ error("Can't verify ref %s", lock->ref_name);
unlock_ref(lock);
return NULL;
}
if (hashcmp(lock->old_sha1, old_sha1)) {
- error("Ref %s is at %s but expected %s", lock->ref_file,
+ error("Ref %s is at %s but expected %s", lock->ref_name,
sha1_to_hex(lock->old_sha1), sha1_to_hex(old_sha1));
unlock_ref(lock);
return NULL;
@@ -290,36 +447,37 @@ static struct ref_lock *verify_lock(struct ref_lock *lock,
return lock;
}
-static struct ref_lock *lock_ref_sha1_basic(const char *path,
+static struct ref_lock *lock_ref_sha1_basic(const char *ref,
int plen,
const unsigned char *old_sha1, int mustexist)
{
- const char *orig_path = path;
+ char *ref_file;
+ const char *orig_ref = ref;
struct ref_lock *lock;
struct stat st;
lock = xcalloc(1, sizeof(struct ref_lock));
lock->lock_fd = -1;
- plen = strlen(path) - plen;
- path = resolve_ref(path, lock->old_sha1, mustexist);
- if (!path) {
+ ref = resolve_ref(ref, lock->old_sha1, mustexist, NULL);
+ if (!ref) {
int last_errno = errno;
error("unable to resolve reference %s: %s",
- orig_path, strerror(errno));
+ orig_ref, strerror(errno));
unlock_ref(lock);
errno = last_errno;
return NULL;
}
lock->lk = xcalloc(1, sizeof(struct lock_file));
- lock->ref_file = xstrdup(path);
- lock->log_file = xstrdup(git_path("logs/%s", lock->ref_file + plen));
- lock->force_write = lstat(lock->ref_file, &st) && errno == ENOENT;
+ lock->ref_name = xstrdup(ref);
+ lock->log_file = xstrdup(git_path("logs/%s", ref));
+ ref_file = git_path(ref);
+ lock->force_write = lstat(ref_file, &st) && errno == ENOENT;
- if (safe_create_leading_directories(lock->ref_file))
- die("unable to create directory for %s", lock->ref_file);
- lock->lock_fd = hold_lock_file_for_update(lock->lk, lock->ref_file, 1);
+ if (safe_create_leading_directories(ref_file))
+ die("unable to create directory for %s", ref_file);
+ lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1);
return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
}
@@ -327,17 +485,18 @@ static struct ref_lock *lock_ref_sha1_basic(const char *path,
struct ref_lock *lock_ref_sha1(const char *ref,
const unsigned char *old_sha1, int mustexist)
{
+ char refpath[PATH_MAX];
if (check_ref_format(ref))
return NULL;
- return lock_ref_sha1_basic(git_path("refs/%s", ref),
- 5 + strlen(ref), old_sha1, mustexist);
+ strcpy(refpath, mkpath("refs/%s", ref));
+ return lock_ref_sha1_basic(refpath, strlen(refpath),
+ old_sha1, mustexist);
}
struct ref_lock *lock_any_ref_for_update(const char *ref,
const unsigned char *old_sha1, int mustexist)
{
- return lock_ref_sha1_basic(git_path("%s", ref),
- strlen(ref), old_sha1, mustexist);
+ return lock_ref_sha1_basic(ref, strlen(ref), old_sha1, mustexist);
}
void unlock_ref(struct ref_lock *lock)
@@ -348,7 +507,7 @@ void unlock_ref(struct ref_lock *lock)
if (lock->lk)
rollback_lock_file(lock->lk);
}
- free(lock->ref_file);
+ free(lock->ref_name);
free(lock->log_file);
free(lock);
}
@@ -425,7 +584,7 @@ int write_ref_sha1(struct ref_lock *lock,
return -1;
}
if (commit_lock_file(lock->lk)) {
- error("Couldn't set %s", lock->ref_file);
+ error("Couldn't set %s", lock->ref_name);
unlock_ref(lock);
return -1;
}
diff --git a/refs.h b/refs.h
index 553155c04..305d40869 100644
--- a/refs.h
+++ b/refs.h
@@ -2,7 +2,7 @@
#define REFS_H
struct ref_lock {
- char *ref_file;
+ char *ref_name;
char *log_file;
struct lock_file *lk;
unsigned char old_sha1[20];
@@ -14,11 +14,14 @@ struct ref_lock {
* Calls the specified function for each ref file until it returns nonzero,
* and returns the value
*/
-extern int head_ref(int (*fn)(const char *path, const unsigned char *sha1));
-extern int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1));
-extern int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1));
-extern int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1));
-extern int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1));
+#define REF_ISSYMREF 01
+#define REF_ISPACKED 02
+typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
+extern int head_ref(each_ref_fn, void *);
+extern int for_each_ref(each_ref_fn, void *);
+extern int for_each_tag_ref(each_ref_fn, void *);
+extern int for_each_branch_ref(each_ref_fn, void *);
+extern int for_each_remote_ref(each_ref_fn, void *);
/** Reads the refs file specified into sha1 **/
extern int get_ref_sha1(const char *ref, unsigned char *sha1);
diff --git a/revision.c b/revision.c
index db0168275..cb13b9077 100644
--- a/revision.c
+++ b/revision.c
@@ -416,7 +416,8 @@ static void limit_list(struct rev_info *revs)
if (revs->max_age != -1 && (commit->date < revs->max_age))
obj->flags |= UNINTERESTING;
- if (revs->unpacked && has_sha1_pack(obj->sha1))
+ if (revs->unpacked &&
+ has_sha1_pack(obj->sha1, revs->ignore_packed))
obj->flags |= UNINTERESTING;
add_parents_to_list(revs, commit, &list);
if (obj->flags & UNINTERESTING) {
@@ -465,7 +466,7 @@ static void limit_list(struct rev_info *revs)
static int all_flags;
static struct rev_info *all_revs;
-static int handle_one_ref(const char *path, const unsigned char *sha1)
+static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *object = get_reference(all_revs, path, sha1, all_flags);
add_pending_object(all_revs, object, "");
@@ -476,7 +477,7 @@ static void handle_all(struct rev_info *revs, unsigned flags)
{
all_revs = revs;
all_flags = flags;
- for_each_ref(handle_one_ref);
+ for_each_ref(handle_one_ref, NULL);
}
static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
@@ -671,6 +672,16 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
return 0;
}
+static void add_ignore_packed(struct rev_info *revs, const char *name)
+{
+ int num = ++revs->num_ignore_packed;
+
+ revs->ignore_packed = xrealloc(revs->ignore_packed,
+ sizeof(const char **) * (num + 1));
+ revs->ignore_packed[num-1] = name;
+ revs->ignore_packed[num] = NULL;
+}
+
/*
* Parse revision information, filling in the "rev_info" structure,
* and removing the used arguments from the argument list.
@@ -811,6 +822,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
}
if (!strcmp(arg, "--unpacked")) {
revs->unpacked = 1;
+ free(revs->ignore_packed);
+ revs->ignore_packed = NULL;
+ revs->num_ignore_packed = 0;
+ continue;
+ }
+ if (!strncmp(arg, "--unpacked=", 11)) {
+ revs->unpacked = 1;
+ add_ignore_packed(revs, arg+11);
continue;
}
if (!strcmp(arg, "-r")) {
@@ -1057,7 +1076,8 @@ struct commit *get_revision(struct rev_info *revs)
*/
if (!revs->limited) {
if ((revs->unpacked &&
- has_sha1_pack(commit->object.sha1)) ||
+ has_sha1_pack(commit->object.sha1,
+ revs->ignore_packed)) ||
(revs->max_age != -1 &&
(commit->date < revs->max_age)))
continue;
diff --git a/revision.h b/revision.h
index c1f71afe6..a5c35d05c 100644
--- a/revision.h
+++ b/revision.h
@@ -38,7 +38,7 @@ struct rev_info {
blob_objects:1,
edge_hint:1,
limited:1,
- unpacked:1,
+ unpacked:1, /* see also ignore_packed below */
boundary:1,
parents:1;
@@ -57,6 +57,10 @@ struct rev_info {
unsigned int shown_one:1,
abbrev_commit:1,
relative_date:1;
+
+ const char **ignore_packed; /* pretend objects in these are unpacked */
+ int num_ignore_packed;
+
unsigned int abbrev;
enum cmit_fmt commit_format;
struct log_info *loginfo;
diff --git a/send-pack.c b/send-pack.c
index 5bb123a37..fbd792ccf 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -215,7 +215,7 @@ static int ref_newer(const unsigned char *new_sha1,
static struct ref *local_refs, **local_tail;
static struct ref *remote_refs, **remote_tail;
-static int one_local_ref(const char *refname, const unsigned char *sha1)
+static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
struct ref *ref;
int len = strlen(refname) + 1;
@@ -230,7 +230,7 @@ static int one_local_ref(const char *refname, const unsigned char *sha1)
static void get_local_heads(void)
{
local_tail = &local_refs;
- for_each_ref(one_local_ref);
+ for_each_ref(one_local_ref, NULL);
}
static int receive_status(int in)
diff --git a/server-info.c b/server-info.c
index 2fb8f5710..6cd38be32 100644
--- a/server-info.c
+++ b/server-info.c
@@ -7,7 +7,7 @@
/* refs */
static FILE *info_ref_fp;
-static int add_info_ref(const char *path, const unsigned char *sha1)
+static int add_info_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *o = parse_object(sha1);
@@ -34,7 +34,7 @@ static int update_info_refs(int force)
info_ref_fp = fopen(path1, "w");
if (!info_ref_fp)
return error("unable to update %s", path0);
- for_each_ref(add_info_ref);
+ for_each_ref(add_info_ref, NULL);
fclose(info_ref_fp);
rename(path1, path0);
free(path0);
diff --git a/sha1_file.c b/sha1_file.c
index b64b92de4..b89edb951 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -1217,12 +1217,20 @@ int find_pack_entry_one(const unsigned char *sha1,
return 0;
}
-static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
+static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed)
{
struct packed_git *p;
prepare_packed_git();
for (p = packed_git; p; p = p->next) {
+ if (ignore_packed) {
+ const char **ig;
+ for (ig = ignore_packed; *ig; ig++)
+ if (!strcmp(p->pack_name, *ig))
+ break;
+ if (*ig)
+ continue;
+ }
if (find_pack_entry_one(sha1, e, p))
return 1;
}
@@ -1255,10 +1263,10 @@ int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep
if (!map) {
struct pack_entry e;
- if (find_pack_entry(sha1, &e))
+ if (find_pack_entry(sha1, &e, NULL))
return packed_object_info(&e, type, sizep);
reprepare_packed_git();
- if (find_pack_entry(sha1, &e))
+ if (find_pack_entry(sha1, &e, NULL))
return packed_object_info(&e, type, sizep);
return error("unable to find %s", sha1_to_hex(sha1));
}
@@ -1281,7 +1289,7 @@ static void *read_packed_sha1(const unsigned char *sha1, char *type, unsigned lo
{
struct pack_entry e;
- if (!find_pack_entry(sha1, &e)) {
+ if (!find_pack_entry(sha1, &e, NULL)) {
error("cannot read sha1_file for %s", sha1_to_hex(sha1));
return NULL;
}
@@ -1294,7 +1302,7 @@ void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size
void *map, *buf;
struct pack_entry e;
- if (find_pack_entry(sha1, &e))
+ if (find_pack_entry(sha1, &e, NULL))
return read_packed_sha1(sha1, type, size);
map = map_sha1_file(sha1, &mapsize);
if (map) {
@@ -1303,7 +1311,7 @@ void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size
return buf;
}
reprepare_packed_git();
- if (find_pack_entry(sha1, &e))
+ if (find_pack_entry(sha1, &e, NULL))
return read_packed_sha1(sha1, type, size);
return NULL;
}
@@ -1735,10 +1743,10 @@ int has_pack_file(const unsigned char *sha1)
return 1;
}
-int has_sha1_pack(const unsigned char *sha1)
+int has_sha1_pack(const unsigned char *sha1, const char **ignore_packed)
{
struct pack_entry e;
- return find_pack_entry(sha1, &e);
+ return find_pack_entry(sha1, &e, ignore_packed);
}
int has_sha1_file(const unsigned char *sha1)
@@ -1746,7 +1754,7 @@ int has_sha1_file(const unsigned char *sha1)
struct stat st;
struct pack_entry e;
- if (find_pack_entry(sha1, &e))
+ if (find_pack_entry(sha1, &e, NULL))
return 1;
return find_sha1_file(sha1, &st) ? 1 : 0;
}
diff --git a/sha1_name.c b/sha1_name.c
index 1fbc44380..84d24c6ab 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -247,8 +247,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
NULL
};
static const char *warning = "warning: refname '%.*s' is ambiguous.\n";
- const char **p, *pathname;
- char *real_path = NULL;
+ const char **p, *ref;
+ char *real_ref = NULL;
int refs_found = 0, am;
unsigned long at_time = (unsigned long)-1;
unsigned char *this_result;
@@ -276,10 +276,10 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
for (p = fmt; *p; p++) {
this_result = refs_found ? sha1_from_ref : sha1;
- pathname = resolve_ref(git_path(*p, len, str), this_result, 1);
- if (pathname) {
+ ref = resolve_ref(mkpath(*p, len, str), this_result, 1, NULL);
+ if (ref) {
if (!refs_found++)
- real_path = xstrdup(pathname);
+ real_ref = xstrdup(ref);
if (!warn_ambiguous_refs)
break;
}
@@ -293,12 +293,12 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
if (at_time != (unsigned long)-1) {
read_ref_at(
- real_path + strlen(git_path(".")) - 1,
+ real_ref,
at_time,
sha1);
}
- free(real_path);
+ free(real_ref);
return 0;
}
diff --git a/sideband.c b/sideband.c
new file mode 100644
index 000000000..1b14ff889
--- /dev/null
+++ b/sideband.c
@@ -0,0 +1,74 @@
+#include "pkt-line.h"
+#include "sideband.h"
+
+/*
+ * Receive multiplexed output stream over git native protocol.
+ * in_stream is the input stream from the remote, which carries data
+ * in pkt_line format with band designator. Demultiplex it into out
+ * and err and return error appropriately. Band #1 carries the
+ * primary payload. Things coming over band #2 is not necessarily
+ * error; they are usually informative message on the standard error
+ * stream, aka "verbose"). A message over band #3 is a signal that
+ * the remote died unexpectedly. A flush() concludes the stream.
+ */
+int recv_sideband(const char *me, int in_stream, int out, int err, char *buf, int bufsz)
+{
+ while (1) {
+ int len = packet_read_line(in_stream, buf, bufsz);
+ if (len == 0)
+ break;
+ if (len < 1) {
+ len = sprintf(buf, "%s: protocol error: no band designator\n", me);
+ safe_write(err, buf, len);
+ return SIDEBAND_PROTOCOL_ERROR;
+ }
+ len--;
+ switch (buf[0] & 0xFF) {
+ case 3:
+ safe_write(err, "remote: ", 8);
+ safe_write(err, buf+1, len);
+ safe_write(err, "\n", 1);
+ return SIDEBAND_REMOTE_ERROR;
+ case 2:
+ safe_write(err, "remote: ", 8);
+ safe_write(err, buf+1, len);
+ continue;
+ case 1:
+ safe_write(out, buf+1, len);
+ continue;
+ default:
+ len = sprintf(buf + 1,
+ "%s: protocol error: bad band #%d\n",
+ me, buf[0] & 0xFF);
+ safe_write(err, buf+1, len);
+ return SIDEBAND_PROTOCOL_ERROR;
+ }
+ }
+ return 0;
+}
+
+/*
+ * fd is connected to the remote side; send the sideband data
+ * over multiplexed packet stream.
+ */
+ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max)
+{
+ ssize_t ssz = sz;
+ const char *p = data;
+
+ while (sz) {
+ unsigned n;
+ char hdr[5];
+
+ n = sz;
+ if (packet_max - 5 < n)
+ n = packet_max - 5;
+ sprintf(hdr, "%04x", n + 5);
+ hdr[4] = band;
+ safe_write(fd, hdr, 5);
+ safe_write(fd, p, n);
+ p += n;
+ sz -= n;
+ }
+ return ssz;
+}
diff --git a/sideband.h b/sideband.h
new file mode 100644
index 000000000..4872106fa
--- /dev/null
+++ b/sideband.h
@@ -0,0 +1,13 @@
+#ifndef SIDEBAND_H
+#define SIDEBAND_H
+
+#define SIDEBAND_PROTOCOL_ERROR -2
+#define SIDEBAND_REMOTE_ERROR -1
+
+#define DEFAULT_PACKET_MAX 1000
+#define LARGE_PACKET_MAX 65520
+
+int recv_sideband(const char *me, int in_stream, int out, int err, char *, int);
+ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);
+
+#endif
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index ddc80bbea..6a917f2ff 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -19,51 +19,48 @@ n=$n_dir/fixes
test_expect_success \
"create $m" \
- 'git-update-ref $m $A &&
- test $A = $(cat .git/$m)'
+ "git-update-ref $m $A &&
+ test $A"' = $(cat .git/'"$m"')'
test_expect_success \
"create $m" \
- 'git-update-ref $m $B $A &&
- test $B = $(cat .git/$m)'
+ "git-update-ref $m $B $A &&
+ test $B"' = $(cat .git/'"$m"')'
rm -f .git/$m
test_expect_success \
"fail to create $n" \
- 'touch .git/$n_dir
- git-update-ref $n $A >out 2>err
- test $? = 1 &&
- test "" = "$(cat out)" &&
- grep "error: unable to resolve reference" err &&
- grep $n err'
+ "touch .git/$n_dir
+ git-update-ref $n $A >out 2>err"'
+ test $? != 0'
rm -f .git/$n_dir out err
test_expect_success \
"create $m (by HEAD)" \
- 'git-update-ref HEAD $A &&
- test $A = $(cat .git/$m)'
+ "git-update-ref HEAD $A &&
+ test $A"' = $(cat .git/'"$m"')'
test_expect_success \
"create $m (by HEAD)" \
- 'git-update-ref HEAD $B $A &&
- test $B = $(cat .git/$m)'
+ "git-update-ref HEAD $B $A &&
+ test $B"' = $(cat .git/'"$m"')'
rm -f .git/$m
test_expect_failure \
'(not) create HEAD with old sha1' \
- 'git-update-ref HEAD $A $B'
+ "git-update-ref HEAD $A $B"
test_expect_failure \
"(not) prior created .git/$m" \
- 'test -f .git/$m'
+ "test -f .git/$m"
rm -f .git/$m
test_expect_success \
"create HEAD" \
- 'git-update-ref HEAD $A'
+ "git-update-ref HEAD $A"
test_expect_failure \
'(not) change HEAD with wrong SHA1' \
- 'git-update-ref HEAD $B $Z'
+ "git-update-ref HEAD $B $Z"
test_expect_failure \
"(not) changed .git/$m" \
- 'test $B = $(cat .git/$m)'
+ "test $B"' = $(cat .git/'"$m"')'
rm -f .git/$m
mkdir -p .git/logs/refs/heads
@@ -71,18 +68,18 @@ touch .git/logs/refs/heads/master
test_expect_success \
"create $m (logged by touch)" \
'GIT_COMMITTER_DATE="2005-05-26 23:30" \
- git-update-ref HEAD $A -m "Initial Creation" &&
- test $A = $(cat .git/$m)'
+ git-update-ref HEAD '"$A"' -m "Initial Creation" &&
+ test '"$A"' = $(cat .git/'"$m"')'
test_expect_success \
"update $m (logged by touch)" \
'GIT_COMMITTER_DATE="2005-05-26 23:31" \
- git-update-ref HEAD $B $A -m "Switch" &&
- test $B = $(cat .git/$m)'
+ git-update-ref HEAD'" $B $A "'-m "Switch" &&
+ test '"$B"' = $(cat .git/'"$m"')'
test_expect_success \
"set $m (logged by touch)" \
'GIT_COMMITTER_DATE="2005-05-26 23:41" \
- git-update-ref HEAD $A &&
- test $A = $(cat .git/$m)'
+ git-update-ref HEAD'" $A &&
+ test $A"' = $(cat .git/'"$m"')'
cat >expect <<EOF
$Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 Initial Creation
@@ -91,7 +88,7 @@ $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000
EOF
test_expect_success \
"verifying $m's log" \
- 'diff expect .git/logs/$m'
+ "diff expect .git/logs/$m"
rm -rf .git/$m .git/logs expect
test_expect_success \
@@ -102,18 +99,18 @@ test_expect_success \
test_expect_success \
"create $m (logged by config)" \
'GIT_COMMITTER_DATE="2005-05-26 23:32" \
- git-update-ref HEAD $A -m "Initial Creation" &&
- test $A = $(cat .git/$m)'
+ git-update-ref HEAD'" $A "'-m "Initial Creation" &&
+ test '"$A"' = $(cat .git/'"$m"')'
test_expect_success \
"update $m (logged by config)" \
'GIT_COMMITTER_DATE="2005-05-26 23:33" \
- git-update-ref HEAD $B $A -m "Switch" &&
- test $B = $(cat .git/$m)'
+ git-update-ref HEAD'" $B $A "'-m "Switch" &&
+ test '"$B"' = $(cat .git/'"$m"')'
test_expect_success \
"set $m (logged by config)" \
'GIT_COMMITTER_DATE="2005-05-26 23:43" \
- git-update-ref HEAD $A &&
- test $A = $(cat .git/$m)'
+ git-update-ref HEAD '"$A &&
+ test $A"' = $(cat .git/'"$m"')'
cat >expect <<EOF
$Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 +0000 Initial Creation
@@ -140,50 +137,50 @@ test_expect_success \
'Query "master@{May 25 2005}" (before history)' \
'rm -f o e
git-rev-parse --verify "master@{May 25 2005}" >o 2>e &&
- test $C = $(cat o) &&
- test "warning: Log .git/logs/$m only goes back to $ed." = "$(cat e)"'
+ test '"$C"' = $(cat o) &&
+ test "warning: Log .git/logs/'"$m only goes back to $ed"'." = "$(cat e)"'
test_expect_success \
"Query master@{2005-05-25} (before history)" \
'rm -f o e
git-rev-parse --verify master@{2005-05-25} >o 2>e &&
- test $C = $(cat o) &&
- echo test "warning: Log .git/logs/$m only goes back to $ed." = "$(cat e)"'
+ test '"$C"' = $(cat o) &&
+ echo test "warning: Log .git/logs/'"$m only goes back to $ed"'." = "$(cat e)"'
test_expect_success \
'Query "master@{May 26 2005 23:31:59}" (1 second before history)' \
'rm -f o e
git-rev-parse --verify "master@{May 26 2005 23:31:59}" >o 2>e &&
- test $C = $(cat o) &&
- test "warning: Log .git/logs/$m only goes back to $ed." = "$(cat e)"'
+ test '"$C"' = $(cat o) &&
+ test "warning: Log .git/logs/'"$m only goes back to $ed"'." = "$(cat e)"'
test_expect_success \
'Query "master@{May 26 2005 23:32:00}" (exactly history start)' \
'rm -f o e
git-rev-parse --verify "master@{May 26 2005 23:32:00}" >o 2>e &&
- test $A = $(cat o) &&
+ test '"$A"' = $(cat o) &&
test "" = "$(cat e)"'
test_expect_success \
'Query "master@{2005-05-26 23:33:01}" (middle of history with gap)' \
'rm -f o e
git-rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e &&
- test $B = $(cat o) &&
- test "warning: Log .git/logs/$m has gap after $gd." = "$(cat e)"'
+ test '"$B"' = $(cat o) &&
+ test "warning: Log .git/logs/'"$m has gap after $gd"'." = "$(cat e)"'
test_expect_success \
'Query "master@{2005-05-26 23:38:00}" (middle of history)' \
'rm -f o e
git-rev-parse --verify "master@{2005-05-26 23:38:00}" >o 2>e &&
- test $Z = $(cat o) &&
+ test '"$Z"' = $(cat o) &&
test "" = "$(cat e)"'
test_expect_success \
'Query "master@{2005-05-26 23:43:00}" (exact end of history)' \
'rm -f o e
git-rev-parse --verify "master@{2005-05-26 23:43:00}" >o 2>e &&
- test $E = $(cat o) &&
+ test '"$E"' = $(cat o) &&
test "" = "$(cat e)"'
test_expect_success \
'Query "master@{2005-05-28}" (past end of history)' \
'rm -f o e
git-rev-parse --verify "master@{2005-05-28}" >o 2>e &&
- test $D = $(cat o) &&
- test "warning: Log .git/logs/$m unexpectedly ended on $ld." = "$(cat e)"'
+ test '"$D"' = $(cat o) &&
+ test "warning: Log .git/logs/'"$m unexpectedly ended on $ld"'." = "$(cat e)"'
rm -f .git/$m .git/logs/$m expect
@@ -221,7 +218,7 @@ $h_FIXED $h_MERGED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151100 +0000 c
EOF
test_expect_success \
'git-commit logged updates' \
- 'diff expect .git/logs/$m'
+ "diff expect .git/logs/$m"
unset h_TEST h_OTHER h_FIXED h_MERGED
test_expect_success \
diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh
index 8ab63c527..bb2531536 100755
--- a/t/t3403-rebase-skip.sh
+++ b/t/t3403-rebase-skip.sh
@@ -37,7 +37,9 @@ test_expect_success setup '
git branch skip-merge skip-reference
'
-test_expect_failure 'rebase with git am -3 (default)' 'git rebase master'
+test_expect_failure 'rebase with git am -3 (default)' '
+ git rebase master
+'
test_expect_success 'rebase --skip with am -3' '
git reset --hard HEAD &&
diff --git a/t/t4104-apply-boundary.sh b/t/t4104-apply-boundary.sh
new file mode 100755
index 000000000..2ff800c23
--- /dev/null
+++ b/t/t4104-apply-boundary.sh
@@ -0,0 +1,115 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-apply boundary tests
+
+'
+. ./test-lib.sh
+
+L="c d e f g h i j k l m n o p q r s t u v w x"
+
+test_expect_success setup '
+ for i in b '"$L"' y
+ do
+ echo $i
+ done >victim &&
+ cat victim >original &&
+ git update-index --add victim &&
+
+ : add to the head
+ for i in a b '"$L"' y
+ do
+ echo $i
+ done >victim &&
+ cat victim >add-a-expect &&
+ git diff victim >add-a-patch.with &&
+ git diff --unified=0 >add-a-patch.without &&
+
+ : modify at the head
+ for i in a '"$L"' y
+ do
+ echo $i
+ done >victim &&
+ cat victim >mod-a-expect &&
+ git diff victim >mod-a-patch.with &&
+ git diff --unified=0 >mod-a-patch.without &&
+
+ : remove from the head
+ for i in '"$L"' y
+ do
+ echo $i
+ done >victim &&
+ cat victim >del-a-expect &&
+ git diff victim >del-a-patch.with
+ git diff --unified=0 >del-a-patch.without &&
+
+ : add to the tail
+ for i in b '"$L"' y z
+ do
+ echo $i
+ done >victim &&
+ cat victim >add-z-expect &&
+ git diff victim >add-z-patch.with &&
+ git diff --unified=0 >add-z-patch.without &&
+
+ : modify at the tail
+ for i in a '"$L"' y
+ do
+ echo $i
+ done >victim &&
+ cat victim >mod-z-expect &&
+ git diff victim >mod-z-patch.with &&
+ git diff --unified=0 >mod-z-patch.without &&
+
+ : remove from the tail
+ for i in b '"$L"'
+ do
+ echo $i
+ done >victim &&
+ cat victim >del-z-expect &&
+ git diff victim >del-z-patch.with
+ git diff --unified=0 >del-z-patch.without &&
+
+ : done
+'
+
+for with in with without
+do
+ case "$with" in
+ with) u= ;;
+ without) u='--unidiff-zero ' ;;
+ esac
+ for kind in add-a add-z mod-a mod-z del-a del-z
+ do
+ test_expect_success "apply $kind-patch $with context" '
+ cat original >victim &&
+ git update-index victim &&
+ git apply --index '"$u$kind-patch.$with"' || {
+ cat '"$kind-patch.$with"'
+ (exit 1)
+ } &&
+ diff -u '"$kind"'-expect victim
+ '
+ done
+done
+
+for kind in add-a add-z mod-a mod-z del-a del-z
+do
+ rm -f $kind-ng.without
+ sed -e "s/^diff --git /diff /" \
+ -e '/^index /d' \
+ <$kind-patch.without >$kind-ng.without
+ test_expect_success "apply non-git $kind-patch without context" '
+ cat original >victim &&
+ git update-index victim &&
+ git apply --unidiff-zero --index '"$kind-ng.without"' || {
+ cat '"$kind-ng.without"'
+ (exit 1)
+ } &&
+ diff -u '"$kind"'-expect victim
+ '
+done
+
+test_done
diff --git a/upload-pack.c b/upload-pack.c
index 51ce936b0..9412a9b26 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -4,6 +4,7 @@
#include "cache.h"
#include "refs.h"
#include "pkt-line.h"
+#include "sideband.h"
#include "tag.h"
#include "object.h"
#include "commit.h"
@@ -19,6 +20,9 @@ static int use_thin_pack;
static struct object_array have_obj;
static struct object_array want_obj;
static unsigned int timeout;
+/* 0 for no sideband,
+ * otherwise maximum packet size (up to 65520 bytes).
+ */
static int use_sideband;
static void reset_timeout(void)
@@ -33,45 +37,18 @@ static int strip(char *line, int len)
return len;
}
-#define PACKET_MAX 1000
static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
{
- ssize_t ssz;
- const char *p;
-
- if (!data) {
- if (!use_sideband)
- return 0;
- packet_flush(1);
- }
-
- if (!use_sideband) {
- if (fd == 3)
- /* emergency quit */
- fd = 2;
- if (fd == 2) {
- xwrite(fd, data, sz);
- return sz;
- }
- return safe_write(fd, data, sz);
- }
- p = data;
- ssz = sz;
- while (sz) {
- unsigned n;
- char hdr[5];
-
- n = sz;
- if (PACKET_MAX - 5 < n)
- n = PACKET_MAX - 5;
- sprintf(hdr, "%04x", n + 5);
- hdr[4] = fd;
- safe_write(1, hdr, 5);
- safe_write(1, p, n);
- p += n;
- sz -= n;
+ if (use_sideband)
+ return send_sideband(1, fd, data, sz, use_sideband);
+ if (fd == 3)
+ /* emergency quit */
+ fd = 2;
+ if (fd == 2) {
+ xwrite(fd, data, sz);
+ return sz;
}
- return ssz;
+ return safe_write(fd, data, sz);
}
static void create_pack_file(void)
@@ -308,7 +285,8 @@ static void create_pack_file(void)
goto fail;
fprintf(stderr, "flushed.\n");
}
- send_client_data(1, NULL, 0);
+ if (use_sideband)
+ packet_flush(1);
return;
}
fail:
@@ -351,7 +329,8 @@ static int got_sha1(char *hex, unsigned char *sha1)
static int get_common_commits(void)
{
static char line[1000];
- unsigned char sha1[20], last_sha1[20];
+ unsigned char sha1[20];
+ char hex[41], last_hex[41];
int len;
track_object_refs = 0;
@@ -368,21 +347,22 @@ static int get_common_commits(void)
}
len = strip(line, len);
if (!strncmp(line, "have ", 5)) {
- if (got_sha1(line+5, sha1) &&
- (multi_ack || have_obj.nr == 1)) {
- packet_write(1, "ACK %s%s\n",
- sha1_to_hex(sha1),
- multi_ack ? " continue" : "");
- if (multi_ack)
- hashcpy(last_sha1, sha1);
+ if (got_sha1(line+5, sha1)) {
+ memcpy(hex, sha1_to_hex(sha1), 41);
+ if (multi_ack) {
+ const char *msg = "ACK %s continue\n";
+ packet_write(1, msg, hex);
+ memcpy(last_hex, hex, 41);
+ }
+ else if (have_obj.nr == 1)
+ packet_write(1, "ACK %s\n", hex);
}
continue;
}
if (!strcmp(line, "done")) {
if (have_obj.nr > 0) {
if (multi_ack)
- packet_write(1, "ACK %s\n",
- sha1_to_hex(last_sha1));
+ packet_write(1, "ACK %s\n", last_hex);
return 0;
}
packet_write(1, "NAK\n");
@@ -413,8 +393,10 @@ static void receive_needs(void)
multi_ack = 1;
if (strstr(line+45, "thin-pack"))
use_thin_pack = 1;
- if (strstr(line+45, "side-band"))
- use_sideband = 1;
+ if (strstr(line+45, "side-band-64k"))
+ use_sideband = LARGE_PACKET_MAX;
+ else if (strstr(line+45, "side-band"))
+ use_sideband = DEFAULT_PACKET_MAX;
/* We have sent all our refs already, and the other end
* should have chosen out of them; otherwise they are
@@ -434,9 +416,9 @@ static void receive_needs(void)
}
}
-static int send_ref(const char *refname, const unsigned char *sha1)
+static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
- static const char *capabilities = "multi_ack thin-pack side-band";
+ static const char *capabilities = "multi_ack thin-pack side-band side-band-64k";
struct object *o = parse_object(sha1);
if (!o)
@@ -462,8 +444,8 @@ static int send_ref(const char *refname, const unsigned char *sha1)
static void upload_pack(void)
{
reset_timeout();
- head_ref(send_ref);
- for_each_ref(send_ref);
+ head_ref(send_ref, NULL);
+ for_each_ref(send_ref, NULL);
packet_flush(1);
receive_needs();
if (want_obj.nr) {
diff --git a/wt-status.c b/wt-status.c
new file mode 100644
index 000000000..d8e284c31
--- /dev/null
+++ b/wt-status.c
@@ -0,0 +1,274 @@
+#include "wt-status.h"
+#include "color.h"
+#include "cache.h"
+#include "object.h"
+#include "dir.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "diffcore.h"
+
+int wt_status_use_color = 0;
+static char wt_status_colors[][COLOR_MAXLEN] = {
+ "", /* WT_STATUS_HEADER: normal */
+ "\033[32m", /* WT_STATUS_UPDATED: green */
+ "\033[31m", /* WT_STATUS_CHANGED: red */
+ "\033[31m", /* WT_STATUS_UNTRACKED: red */
+};
+
+static int parse_status_slot(const char *var, int offset)
+{
+ if (!strcasecmp(var+offset, "header"))
+ return WT_STATUS_HEADER;
+ if (!strcasecmp(var+offset, "updated"))
+ return WT_STATUS_UPDATED;
+ if (!strcasecmp(var+offset, "changed"))
+ return WT_STATUS_CHANGED;
+ if (!strcasecmp(var+offset, "untracked"))
+ return WT_STATUS_UNTRACKED;
+ die("bad config variable '%s'", var);
+}
+
+static const char* color(int slot)
+{
+ return wt_status_use_color ? wt_status_colors[slot] : "";
+}
+
+void wt_status_prepare(struct wt_status *s)
+{
+ unsigned char sha1[20];
+ const char *head;
+
+ s->is_initial = get_sha1("HEAD", sha1) ? 1 : 0;
+
+ head = resolve_ref("HEAD", sha1, 0, NULL);
+ s->branch = head ? xstrdup(head) : NULL;
+
+ s->reference = "HEAD";
+ s->amend = 0;
+ s->verbose = 0;
+ s->commitable = 0;
+ s->untracked = 0;
+}
+
+static void wt_status_print_header(const char *main, const char *sub)
+{
+ const char *c = color(WT_STATUS_HEADER);
+ color_printf_ln(c, "# %s:", main);
+ color_printf_ln(c, "# (%s)", sub);
+ color_printf_ln(c, "#");
+}
+
+static void wt_status_print_trailer(void)
+{
+ color_printf_ln(color(WT_STATUS_HEADER), "#");
+}
+
+static void wt_status_print_filepair(int t, struct diff_filepair *p)
+{
+ const char *c = color(t);
+ color_printf(color(WT_STATUS_HEADER), "#\t");
+ switch (p->status) {
+ case DIFF_STATUS_ADDED:
+ color_printf(c, "new file: %s", p->one->path); break;
+ case DIFF_STATUS_COPIED:
+ color_printf(c, "copied: %s -> %s",
+ p->one->path, p->two->path);
+ break;
+ case DIFF_STATUS_DELETED:
+ color_printf(c, "deleted: %s", p->one->path); break;
+ case DIFF_STATUS_MODIFIED:
+ color_printf(c, "modified: %s", p->one->path); break;
+ case DIFF_STATUS_RENAMED:
+ color_printf(c, "renamed: %s -> %s",
+ p->one->path, p->two->path);
+ break;
+ case DIFF_STATUS_TYPE_CHANGED:
+ color_printf(c, "typechange: %s", p->one->path); break;
+ case DIFF_STATUS_UNKNOWN:
+ color_printf(c, "unknown: %s", p->one->path); break;
+ case DIFF_STATUS_UNMERGED:
+ color_printf(c, "unmerged: %s", p->one->path); break;
+ default:
+ die("bug: unhandled diff status %c", p->status);
+ }
+ printf("\n");
+}
+
+static void wt_status_print_updated_cb(struct diff_queue_struct *q,
+ struct diff_options *options,
+ void *data)
+{
+ struct wt_status *s = data;
+ int shown_header = 0;
+ int i;
+ if (q->nr) {
+ }
+ for (i = 0; i < q->nr; i++) {
+ if (q->queue[i]->status == 'U')
+ continue;
+ if (!shown_header) {
+ wt_status_print_header("Updated but not checked in",
+ "will commit");
+ s->commitable = 1;
+ shown_header = 1;
+ }
+ wt_status_print_filepair(WT_STATUS_UPDATED, q->queue[i]);
+ }
+ if (shown_header)
+ wt_status_print_trailer();
+}
+
+static void wt_status_print_changed_cb(struct diff_queue_struct *q,
+ struct diff_options *options,
+ void *data)
+{
+ int i;
+ if (q->nr)
+ wt_status_print_header("Changed but not updated",
+ "use git-update-index to mark for commit");
+ for (i = 0; i < q->nr; i++)
+ wt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]);
+ if (q->nr)
+ wt_status_print_trailer();
+}
+
+void wt_status_print_initial(struct wt_status *s)
+{
+ int i;
+ read_cache();
+ if (active_nr) {
+ s->commitable = 1;
+ wt_status_print_header("Updated but not checked in",
+ "will commit");
+ }
+ for (i = 0; i < active_nr; i++) {
+ color_printf(color(WT_STATUS_HEADER), "#\t");
+ color_printf_ln(color(WT_STATUS_UPDATED), "new file: %s",
+ active_cache[i]->name);
+ }
+ if (active_nr)
+ wt_status_print_trailer();
+}
+
+static void wt_status_print_updated(struct wt_status *s)
+{
+ struct rev_info rev;
+ const char *argv[] = { NULL, NULL, NULL };
+ argv[1] = s->reference;
+ init_revisions(&rev, NULL);
+ setup_revisions(2, argv, &rev, NULL);
+ rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
+ rev.diffopt.format_callback = wt_status_print_updated_cb;
+ rev.diffopt.format_callback_data = s;
+ rev.diffopt.detect_rename = 1;
+ run_diff_index(&rev, 1);
+}
+
+static void wt_status_print_changed(struct wt_status *s)
+{
+ struct rev_info rev;
+ const char *argv[] = { NULL, NULL };
+ init_revisions(&rev, "");
+ setup_revisions(1, argv, &rev, NULL);
+ rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
+ rev.diffopt.format_callback = wt_status_print_changed_cb;
+ rev.diffopt.format_callback_data = s;
+ run_diff_files(&rev, 0);
+}
+
+static void wt_status_print_untracked(const struct wt_status *s)
+{
+ struct dir_struct dir;
+ const char *x;
+ int i;
+ int shown_header = 0;
+
+ memset(&dir, 0, sizeof(dir));
+
+ dir.exclude_per_dir = ".gitignore";
+ if (!s->untracked) {
+ dir.show_other_directories = 1;
+ dir.hide_empty_directories = 1;
+ }
+ x = git_path("info/exclude");
+ if (file_exists(x))
+ add_excludes_from_file(&dir, x);
+
+ read_directory(&dir, ".", "", 0);
+ for(i = 0; i < dir.nr; i++) {
+ /* check for matching entry, which is unmerged; lifted from
+ * builtin-ls-files:show_other_files */
+ struct dir_entry *ent = dir.entries[i];
+ int pos = cache_name_pos(ent->name, ent->len);
+ struct cache_entry *ce;
+ if (0 <= pos)
+ die("bug in wt_status_print_untracked");
+ pos = -pos - 1;
+ if (pos < active_nr) {
+ ce = active_cache[pos];
+ if (ce_namelen(ce) == ent->len &&
+ !memcmp(ce->name, ent->name, ent->len))
+ continue;
+ }
+ if (!shown_header) {
+ wt_status_print_header("Untracked files",
+ "use \"git add\" to add to commit");
+ shown_header = 1;
+ }
+ color_printf(color(WT_STATUS_HEADER), "#\t");
+ color_printf_ln(color(WT_STATUS_UNTRACKED), "%.*s",
+ ent->len, ent->name);
+ }
+}
+
+static void wt_status_print_verbose(struct wt_status *s)
+{
+ struct rev_info rev;
+ const char *argv[] = { NULL, NULL, NULL };
+ argv[1] = s->reference;
+ init_revisions(&rev, NULL);
+ setup_revisions(2, argv, &rev, NULL);
+ rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
+ rev.diffopt.detect_rename = 1;
+ run_diff_index(&rev, 1);
+}
+
+void wt_status_print(struct wt_status *s)
+{
+ if (s->branch && strcmp(s->branch, "refs/heads/master"))
+ color_printf_ln(color(WT_STATUS_HEADER),
+ "# On branch %s", s->branch);
+
+ if (s->is_initial) {
+ color_printf_ln(color(WT_STATUS_HEADER), "#");
+ color_printf_ln(color(WT_STATUS_HEADER), "# Initial commit");
+ color_printf_ln(color(WT_STATUS_HEADER), "#");
+ wt_status_print_initial(s);
+ }
+ else {
+ wt_status_print_updated(s);
+ discard_cache();
+ }
+
+ wt_status_print_changed(s);
+ wt_status_print_untracked(s);
+
+ if (s->verbose && !s->is_initial)
+ wt_status_print_verbose(s);
+ if (!s->commitable)
+ printf("%s\n", s->amend ? "# No changes" : "nothing to commit");
+}
+
+int git_status_config(const char *k, const char *v)
+{
+ if (!strcmp(k, "status.color")) {
+ wt_status_use_color = git_config_colorbool(k, v);
+ return 0;
+ }
+ if (!strncmp(k, "status.color.", 13)) {
+ int slot = parse_status_slot(k, 13);
+ color_parse(v, k, wt_status_colors[slot]);
+ }
+ return git_default_config(k, v);
+}
diff --git a/wt-status.h b/wt-status.h
new file mode 100644
index 000000000..0a5a5b7ba
--- /dev/null
+++ b/wt-status.h
@@ -0,0 +1,25 @@
+#ifndef STATUS_H
+#define STATUS_H
+
+enum color_wt_status {
+ WT_STATUS_HEADER,
+ WT_STATUS_UPDATED,
+ WT_STATUS_CHANGED,
+ WT_STATUS_UNTRACKED,
+};
+
+struct wt_status {
+ int is_initial;
+ char *branch;
+ const char *reference;
+ int commitable;
+ int verbose;
+ int amend;
+ int untracked;
+};
+
+int git_status_config(const char *var, const char *value);
+void wt_status_prepare(struct wt_status *s);
+void wt_status_print(struct wt_status *s);
+
+#endif /* STATUS_H */