Merge branch 'git_git' into next

Conflicts:

	connect.c
	git-clone.sh
	t/t7004-tag.sh
This commit is contained in:
Marius Storm-Olsen
2007-08-14 09:30:22 +02:00
45 changed files with 1352 additions and 265 deletions

View File

@@ -44,6 +44,11 @@ INSTALL?=install
RM ?= rm -f
DOC_REF = origin/man
infodir?=$(prefix)/share/info
MAKEINFO=makeinfo
INSTALL_INFO=install-info
DOCBOOK2X_TEXI=docbook2x-texi
-include ../config.mak.autogen
-include ../config.mak
@@ -67,6 +72,8 @@ man1: $(DOC_MAN1)
man5: $(DOC_MAN5)
man7: $(DOC_MAN7)
info: git.info
install: man
$(INSTALL) -d -m755 $(DESTDIR)$(man1dir)
$(INSTALL) -d -m755 $(DESTDIR)$(man5dir)
@@ -75,6 +82,14 @@ install: man
$(INSTALL) -m644 $(DOC_MAN5) $(DESTDIR)$(man5dir)
$(INSTALL) -m644 $(DOC_MAN7) $(DESTDIR)$(man7dir)
install-info: info
$(INSTALL) -d -m755 $(DESTDIR)$(infodir)
$(INSTALL) -m644 git.info $(DESTDIR)$(infodir)
if test -r $(DESTDIR)$(infodir)/dir; then \
$(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) git.info ;\
else \
echo "No directory found in $(DESTDIR)$(infodir)" >&2 ; \
fi
../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
$(MAKE) -C ../ GIT-VERSION-FILE
@@ -104,13 +119,14 @@ cmds_txt = cmds-ancillaryinterrogators.txt \
$(cmds_txt): cmd-list.made
cmd-list.made: cmd-list.perl $(MAN1_TXT)
$(RM) $@
perl ./cmd-list.perl
date >$@
git.7 git.html: git.txt core-intro.txt
clean:
$(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7 howto-index.txt howto/*.html doc.dep
$(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7 *.texi *.texi+ howto-index.txt howto/*.html doc.dep
$(RM) $(cmds_txt) *.made
%.html : %.txt
@@ -138,6 +154,13 @@ XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
user-manual.html: user-manual.xml
xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
git.info: user-manual.xml
$(RM) $@ $*.texi $*.texi+
$(DOCBOOK2X_TEXI) user-manual.xml --to-stdout >$*.texi+
perl fix-texi.perl <$*.texi+ >$*.texi
$(MAKEINFO) --no-split $*.texi
$(RM) $*.texi $*.texi+
howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
$(RM) $@+ $@
sh ./howto-index.sh $(wildcard howto/*.txt) >$@+

View File

@@ -64,11 +64,11 @@ of lines before or after the line given by <start>.
assigns blame to the lines that were moved down (i.e. A)
to the child commit. With this option, both groups of lines
are blamed on the parent.
<num> is optional but it is the lower bound on the number of
alphanumeric characters that git must detect as moving
within a file for it to associate those lines with the parent
commit.
+
<num> is optional but it is the lower bound on the number of
alphanumeric characters that git must detect as moving
within a file for it to associate those lines with the parent
commit.
-C|<num>|::
In addition to `-M`, detect lines copied from other
@@ -77,11 +77,11 @@ of lines before or after the line given by <start>.
around across files. When this option is given twice,
the command looks for copies from all other files in the
parent for the commit that creates the file in addition.
<num> is optional but it is the lower bound on the number of
alphanumeric characters that git must detect as moving
between files for it to associate those lines with the parent
commit.
+
<num> is optional but it is the lower bound on the number of
alphanumeric characters that git must detect as moving
between files for it to associate those lines with the parent
commit.
-h, --help::
Show help message.

View File

@@ -68,6 +68,8 @@ for my $cat (qw(ancillaryinterrogators
}
}
# The following list is sorted with "sort -d" to make it easier
# to find entry in the resulting git.html manual page.
__DATA__
git-add mainporcelain
git-am mainporcelain
@@ -80,9 +82,9 @@ git-blame ancillaryinterrogators
git-branch mainporcelain
git-bundle mainporcelain
git-cat-file plumbinginterrogators
git-checkout-index plumbingmanipulators
git-checkout mainporcelain
git-check-attr purehelpers
git-checkout mainporcelain
git-checkout-index plumbingmanipulators
git-check-ref-format purehelpers
git-cherry ancillaryinterrogators
git-cherry-pick mainporcelain
@@ -91,6 +93,7 @@ git-clean mainporcelain
git-clone mainporcelain
git-commit mainporcelain
git-commit-tree plumbingmanipulators
git-config ancillarymanipulators
git-convert-objects ancillarymanipulators
git-count-objects ancillaryinterrogators
git-cvsexportcommit foreignscminterface
@@ -98,9 +101,9 @@ git-cvsimport foreignscminterface
git-cvsserver foreignscminterface
git-daemon synchingrepositories
git-describe mainporcelain
git-diff mainporcelain
git-diff-files plumbinginterrogators
git-diff-index plumbinginterrogators
git-diff mainporcelain
git-diff-tree plumbinginterrogators
git-fast-import ancillarymanipulators
git-fetch mainporcelain
@@ -130,13 +133,13 @@ git-ls-remote plumbinginterrogators
git-ls-tree plumbinginterrogators
git-mailinfo purehelpers
git-mailsplit purehelpers
git-merge mainporcelain
git-merge-base plumbinginterrogators
git-merge-file plumbingmanipulators
git-merge-index plumbingmanipulators
git-merge mainporcelain
git-merge-one-file purehelpers
git-merge-tree ancillaryinterrogators
git-mergetool ancillarymanipulators
git-merge-tree ancillaryinterrogators
git-mktag plumbingmanipulators
git-mktree plumbingmanipulators
git-mv mainporcelain
@@ -157,9 +160,8 @@ git-rebase mainporcelain
git-receive-pack synchelpers
git-reflog ancillarymanipulators
git-relink ancillarymanipulators
git-repack ancillarymanipulators
git-config ancillarymanipulators
git-remote ancillarymanipulators
git-repack ancillarymanipulators
git-request-pull foreignscminterface
git-rerere ancillaryinterrogators
git-reset mainporcelain

15
Documentation/fix-texi.perl Executable file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/perl -w
while (<>) {
if (/^\@setfilename/) {
$_ = "\@setfilename git.info\n";
} elsif (/^\@direntry/) {
print '@dircategory Development
@direntry
* Git: (git). A fast distributed revision control system
@end direntry
'; }
unless (/^\@direntry/../^\@end direntry/) {
print;
}
}

View File

@@ -9,7 +9,8 @@ git-clone - Clone a repository into a new directory
SYNOPSIS
--------
[verse]
'git-clone' [--template=<template_directory>] [-l [-s]] [-q] [-n] [--bare]
'git-clone' [--template=<template_directory>]
[-l] [-s] [--no-hardlinks] [-q] [-n] [--bare]
[-o <name>] [-u <upload-pack>] [--reference <repository>]
[--depth <depth>] <repository> [<directory>]
@@ -40,8 +41,19 @@ OPTIONS
this flag bypasses normal "git aware" transport
mechanism and clones the repository by making a copy of
HEAD and everything under objects and refs directories.
The files under .git/objects/ directory are hardlinked
to save space when possible.
The files under `.git/objects/` directory are hardlinked
to save space when possible. This is now the default when
the source repository is specified with `/path/to/repo`
syntax, so it essentially is a no-op option. To force
copying instead of hardlinking (which may be desirable
if you are trying to make a back-up of your repository),
but still avoid the usual "git aware" transport
mechanism, `--no-hardlinks` can be used.
--no-hardlinks::
Optimize the cloning process from a repository on a
local filesystem by copying files under `.git/objects`
directory.
--shared::
-s::

View File

@@ -224,7 +224,7 @@ left-to-right.
G H I J
\ / \ /
D E F
\ | / \
\ | / \
\ | / |
\|/ |
B C

View File

@@ -8,7 +8,7 @@ git-stripspace - Filter out empty lines
SYNOPSIS
--------
'git-stripspace' < <stream>
'git-stripspace' [-s | --strip-comments] < <stream>
DESCRIPTION
-----------
@@ -16,6 +16,9 @@ Remove multiple empty lines, and empty lines at beginning and end.
OPTIONS
-------
-s\|--strip-comments::
In addition to empty lines, also strip lines starting with '#'.
<stream>::
Byte stream to act on.

View File

@@ -12,7 +12,7 @@ SYNOPSIS
'git-tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <name> [<head>]
'git-tag' -d <name>...
'git-tag' [-n [<num>]] -l [<pattern>]
'git-tag' -v <name>
'git-tag' -v <name>...
DESCRIPTION
-----------
@@ -23,7 +23,7 @@ Unless `-f` is given, the tag must not yet exist in
If one of `-a`, `-s`, or `-u <key-id>` is passed, the command
creates a 'tag' object, and requires the tag message. Unless
`-m <msg>` is given, an editor is started for the user to type
`-m <msg>` or `-F <file>` is given, an editor is started for the user to type
in the tag message.
Otherwise just the SHA1 object name of the commit object is
@@ -59,15 +59,17 @@ OPTIONS
Delete existing tags with the given names.
-v::
Verify the gpg signature of given the tag
Verify the gpg signature of the given tag names.
-n <num>::
<num> specifies how many lines from the annotation, if any,
are printed when using -l.
The default is not to print any annotation lines.
If no number is given to `-n`, only the first line is printed.
-l <pattern>::
List tags with names that match the given pattern (or all if no pattern is given).
Typing "git tag" without arguments, also lists all tags.
-m <msg>::
Use the given tag message (instead of prompting)

View File

@@ -3,11 +3,11 @@ git-verify-tag(1)
NAME
----
git-verify-tag - Check the GPG signature of tag
git-verify-tag - Check the GPG signature of tags
SYNOPSIS
--------
'git-verify-tag' <tag>
'git-verify-tag' <tag>...
DESCRIPTION
-----------

View File

@@ -15,11 +15,11 @@ to name the remote repository:
- ssh://{startsb}user@{endsb}host.xz/~/path/to/repo.git
===============================================================
SSH is the default transport protocol. You can optionally specify
which user to log-in as, and an alternate, scp-like syntax is also
supported. Both syntaxes support username expansion,
as does the native git protocol. The following three are
identical to the last three above, respectively:
SSH is the default transport protocol over the network. You can
optionally specify which user to log-in as, and an alternate,
scp-like syntax is also supported. Both syntaxes support
username expansion, as does the native git protocol. The following
three are identical to the last three above, respectively:
===============================================================
- {startsb}user@{endsb}host.xz:/path/to/repo.git/
@@ -27,8 +27,12 @@ identical to the last three above, respectively:
- {startsb}user@{endsb}host.xz:path/to/repo.git
===============================================================
To sync with a local directory, use:
To sync with a local directory, you can use:
===============================================================
- /path/to/repo.git/
- file:///path/to/repo.git/
===============================================================
They are mostly equivalent, except when cloning. See
gitlink:git-clone[1] for details.

14
INSTALL
View File

@@ -5,8 +5,8 @@ Normally you can just do "make" followed by "make install", and that
will install the git programs in your own ~/bin/ directory. If you want
to do a global install, you can do
$ make prefix=/usr all doc ;# as yourself
# make prefix=/usr install install-doc ;# as root
$ make prefix=/usr all doc info ;# as yourself
# make prefix=/usr install install-doc install-info ;# as root
(or prefix=/usr/local, of course). Just like any program suite
that uses $prefix, the built results have some paths encoded,
@@ -91,9 +91,13 @@ Issues of note:
- To build and install documentation suite, you need to have
the asciidoc/xmlto toolchain. Because not many people are
inclined to install the tools, the default build target
("make all") does _not_ build them. The documentation is
written for AsciiDoc 7, but "make ASCIIDOC8=YesPlease doc"
will let you format with AsciiDoc 8.
("make all") does _not_ build them.
Building and installing the info file additionally requires
makeinfo and docbook2X. Version 0.8.3 is known to work.
The documentation is written for AsciiDoc 7, but "make
ASCIIDOC8=YesPlease doc" will let you format with AsciiDoc 8.
Alternatively, pre-formatted documentation are available in
"html" and "man" branches of the git repository itself. For

View File

@@ -208,7 +208,6 @@ SCRIPT_SH = \
git-pull.sh git-rebase.sh git-rebase--interactive.sh \
git-repack.sh git-request-pull.sh git-reset.sh \
git-sh-setup.sh \
git-tag.sh git-verify-tag.sh \
git-am.sh \
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
git-merge-resolve.sh git-merge-ours.sh \
@@ -364,12 +363,14 @@ BUILTIN_OBJS = \
builtin-show-branch.o \
builtin-stripspace.o \
builtin-symbolic-ref.o \
builtin-tag.o \
builtin-tar-tree.o \
builtin-unpack-objects.o \
builtin-update-index.o \
builtin-update-ref.o \
builtin-upload-archive.o \
builtin-verify-pack.o \
builtin-verify-tag.o \
builtin-write-tree.o \
builtin-show-ref.o \
builtin-pack-refs.o
@@ -943,6 +944,9 @@ perl/Makefile: perl/Git.pm perl/Makefile.PL GIT-CFLAGS
doc:
$(MAKE) -C Documentation all
info:
$(MAKE) -C Documentation info
TAGS:
$(RM) TAGS
$(FIND) . -name '*.[hcS]' -print | xargs etags -a
@@ -1036,6 +1040,9 @@ endif
install-doc:
$(MAKE) -C Documentation install
install-info:
$(MAKE) -C Documentation install-info
quick-install-doc:
$(MAKE) -C Documentation quick-install

View File

@@ -44,38 +44,21 @@ struct bundle_header {
struct ref_list references;
};
/* this function returns the length of the string */
static int read_string(int fd, char *buffer, int size)
{
int i;
for (i = 0; i < size - 1; i++) {
ssize_t count = xread(fd, buffer + i, 1);
if (count < 0)
return error("Read error: %s", strerror(errno));
if (count == 0) {
i--;
break;
}
if (buffer[i] == '\n')
break;
}
buffer[i + 1] = '\0';
return i + 1;
}
/* returns an fd */
static int read_header(const char *path, struct bundle_header *header) {
char buffer[1024];
int fd = open(path, O_RDONLY);
int fd;
long fpos;
FILE *ffd = fopen(path, "rb");
if (fd < 0)
if (!ffd)
return error("could not open '%s'", path);
if (read_string(fd, buffer, sizeof(buffer)) < 0 ||
if (!fgets(buffer, sizeof(buffer), ffd) ||
strcmp(buffer, bundle_signature)) {
close(fd);
fclose(ffd);
return error("'%s' does not look like a v2 bundle file", path);
}
while (read_string(fd, buffer, sizeof(buffer)) > 0
while (fgets(buffer, sizeof(buffer), ffd)
&& buffer[0] != '\n') {
int is_prereq = buffer[0] == '-';
int offset = is_prereq ? 1 : 0;
@@ -97,6 +80,12 @@ static int read_header(const char *path, struct bundle_header *header) {
add_to_ref_list(sha1, isspace(delim) ?
buffer + 41 + offset : "", list);
}
fpos = ftell(ffd);
fclose(ffd);
fd = open(path, O_RDONLY);
if (fd < 0)
return error("could not open '%s'", path);
lseek(fd, fpos, SEEK_SET);
return fd;
}
@@ -207,7 +196,12 @@ static int create_bundle(struct bundle_header *header, const char *path,
char buffer[1024];
struct rev_info revs;
struct child_process rls;
FILE *rls_fout;
/*
* NEEDSWORK: this should use something like lock-file
* to create temporary that is cleaned up upon error.
*/
bundle_fd = (!strcmp(path, "-") ? 1 :
open(path, O_CREAT | O_EXCL | O_WRONLY, 0666));
if (bundle_fd < 0)
@@ -232,10 +226,11 @@ static int create_bundle(struct bundle_header *header, const char *path,
rls.git_cmd = 1;
if (start_command(&rls))
return -1;
while ((i = read_string(rls.out, buffer, sizeof(buffer))) > 0) {
rls_fout = fdopen(rls.out, "r");
while (fgets(buffer, sizeof(buffer), rls_fout)) {
unsigned char sha1[20];
if (buffer[0] == '-') {
write_or_die(bundle_fd, buffer, i);
write_or_die(bundle_fd, buffer, strlen(buffer));
if (!get_sha1_hex(buffer + 1, sha1)) {
struct object *object = parse_object(sha1);
object->flags |= UNINTERESTING;
@@ -246,6 +241,7 @@ static int create_bundle(struct bundle_header *header, const char *path,
object->flags |= SHOWN;
}
}
fclose(rls_fout);
if (finish_command(&rls))
return error("rev-list died");
@@ -267,12 +263,49 @@ static int create_bundle(struct bundle_header *header, const char *path,
* Make sure the refs we wrote out is correct; --max-count and
* other limiting options could have prevented all the tips
* from getting output.
*
* Non commit objects such as tags and blobs do not have
* this issue as they are not affected by those extra
* constraints.
*/
if (!(e->item->flags & SHOWN)) {
if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) {
warning("ref '%s' is excluded by the rev-list options",
e->name);
free(ref);
continue;
}
/*
* If you run "git bundle create bndl v1.0..v2.0", the
* name of the positive ref is "v2.0" but that is the
* commit that is referenced by the tag, and not the tag
* itself.
*/
if (hashcmp(sha1, e->item->sha1)) {
/*
* Is this the positive end of a range expressed
* in terms of a tag (e.g. v2.0 from the range
* "v1.0..v2.0")?
*/
struct commit *one = lookup_commit_reference(sha1);
struct object *obj;
if (e->item == &(one->object)) {
/*
* Need to include e->name as an
* independent ref to the pack-objects
* input, so that the tag is included
* in the output; otherwise we would
* end up triggering "empty bundle"
* error.
*/
obj = parse_object(sha1);
obj->flags |= SHOWN;
add_pending_object(&revs, obj, e->name);
}
free(ref);
continue;
}
ref_count++;
write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40);
write_or_die(bundle_fd, " ", 1);

View File

@@ -13,14 +13,19 @@
#include "dir.h"
#include "builtin.h"
static struct object_list *trees;
static int nr_trees;
static struct tree *trees[4];
static int list_tree(unsigned char *sha1)
{
struct tree *tree = parse_tree_indirect(sha1);
struct tree *tree;
if (nr_trees >= 4)
return -1;
tree = parse_tree_indirect(sha1);
if (!tree)
return -1;
object_list_append(&tree->object, &trees);
trees[nr_trees++] = tree;
return 0;
}
@@ -76,11 +81,10 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
static void prime_cache_tree(void)
{
struct tree *tree = (struct tree *)trees->item;
if (!tree)
if (!nr_trees)
return;
active_cache_tree = cache_tree();
prime_cache_tree_rec(active_cache_tree, tree);
prime_cache_tree_rec(active_cache_tree, trees[0]);
}
@@ -92,6 +96,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
{
int i, newfd, stage = 0;
unsigned char sha1[20];
struct tree_desc t[3];
struct unpack_trees_options opts;
memset(&opts, 0, sizeof(opts));
@@ -258,7 +263,12 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
opts.head_idx = 1;
}
unpack_trees(trees, &opts);
for (i = 0; i < nr_trees; i++) {
struct tree *tree = trees[i];
parse_tree(tree);
init_tree_desc(t+i, tree->buffer, tree->size);
}
unpack_trees(nr_trees, t, &opts);
/*
* When reading only one tree (either the most basic form,
@@ -266,7 +276,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
* valid cache-tree because the index must match exactly
* what came from the tree.
*/
if (trees && trees->item && !opts.prefix && (!opts.merge || (stage == 2))) {
if (nr_trees && !opts.prefix && (!opts.merge || (stage == 2))) {
cache_tree_free(&active_cache_tree);
prime_cache_tree();
}

View File

@@ -76,6 +76,11 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
{
char *buffer;
unsigned long size;
int strip_comments = 0;
if (argc > 1 && (!strcmp(argv[1], "-s") ||
!strcmp(argv[1], "--strip-comments")))
strip_comments = 1;
size = 1024;
buffer = xmalloc(size);
@@ -84,7 +89,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
die("could not read the input");
}
size = stripspace(buffer, size, 0);
size = stripspace(buffer, size, strip_comments);
write_or_die(1, buffer, size);
if (size)
putc('\n', stdout);

460
builtin-tag.c Normal file
View File

@@ -0,0 +1,460 @@
/*
* Builtin "git tag"
*
* Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>,
* Carlos Rica <jasampler@gmail.com>
* Based on git-tag.sh and mktag.c by Linus Torvalds.
*/
#include "cache.h"
#include "builtin.h"
#include "refs.h"
#include "tag.h"
#include "run-command.h"
static const char builtin_tag_usage[] =
"git-tag [-n [<num>]] -l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg> | -F <file>] <tagname> [<head>]";
static char signingkey[1000];
static void launch_editor(const char *path, char **buffer, unsigned long *len)
{
const char *editor, *terminal;
struct child_process child;
const char *args[3];
int fd;
editor = getenv("GIT_EDITOR");
if (!editor && editor_program)
editor = editor_program;
if (!editor)
editor = getenv("VISUAL");
if (!editor)
editor = getenv("EDITOR");
terminal = getenv("TERM");
if (!editor && (!terminal || !strcmp(terminal, "dumb"))) {
fprintf(stderr,
"Terminal is dumb but no VISUAL nor EDITOR defined.\n"
"Please supply the message using either -m or -F option.\n");
exit(1);
}
if (!editor)
editor = "vi";
memset(&child, 0, sizeof(child));
child.argv = args;
args[0] = editor;
args[1] = path;
args[2] = NULL;
if (run_command(&child))
die("There was a problem with the editor %s.", editor);
fd = open(path, O_RDONLY);
if (fd < 0)
die("could not open '%s': %s", path, strerror(errno));
if (read_fd(fd, buffer, len)) {
free(*buffer);
die("could not read message file '%s': %s",
path, strerror(errno));
}
close(fd);
}
struct tag_filter {
const char *pattern;
int lines;
};
#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
static int show_reference(const char *refname, const unsigned char *sha1,
int flag, void *cb_data)
{
struct tag_filter *filter = cb_data;
if (!fnmatch(filter->pattern, refname, 0)) {
int i;
unsigned long size;
enum object_type type;
char *buf, *sp, *eol;
size_t len;
if (!filter->lines) {
printf("%s\n", refname);
return 0;
}
printf("%-15s ", refname);
sp = buf = read_sha1_file(sha1, &type, &size);
if (!buf)
return 0;
if (!size) {
free(buf);
return 0;
}
/* skip header */
while (sp + 1 < buf + size &&
!(sp[0] == '\n' && sp[1] == '\n'))
sp++;
/* only take up to "lines" lines, and strip the signature */
for (i = 0, sp += 2;
i < filter->lines && sp < buf + size &&
prefixcmp(sp, PGP_SIGNATURE "\n");
i++) {
if (i)
printf("\n ");
eol = memchr(sp, '\n', size - (sp - buf));
len = eol ? eol - sp : size - (sp - buf);
fwrite(sp, len, 1, stdout);
if (!eol)
break;
sp = eol + 1;
}
putchar('\n');
free(buf);
}
return 0;
}
static int list_tags(const char *pattern, int lines)
{
struct tag_filter filter;
char *newpattern;
if (pattern == NULL)
pattern = "";
/* prepend/append * to the shell pattern: */
newpattern = xmalloc(strlen(pattern) + 3);
sprintf(newpattern, "*%s*", pattern);
filter.pattern = newpattern;
filter.lines = lines;
for_each_tag_ref(show_reference, (void *) &filter);
free(newpattern);
return 0;
}
typedef int (*each_tag_name_fn)(const char *name, const char *ref,
const unsigned char *sha1);
static int for_each_tag_name(const char **argv, each_tag_name_fn fn)
{
const char **p;
char ref[PATH_MAX];
int had_error = 0;
unsigned char sha1[20];
for (p = argv; *p; p++) {
if (snprintf(ref, sizeof(ref), "refs/tags/%s", *p)
>= sizeof(ref)) {
error("tag name too long: %.*s...", 50, *p);
had_error = 1;
continue;
}
if (!resolve_ref(ref, sha1, 1, NULL)) {
error("tag '%s' not found.", *p);
had_error = 1;
continue;
}
if (fn(*p, ref, sha1))
had_error = 1;
}
return had_error;
}
static int delete_tag(const char *name, const char *ref,
const unsigned char *sha1)
{
if (delete_ref(ref, sha1))
return 1;
printf("Deleted tag '%s'\n", name);
return 0;
}
static int verify_tag(const char *name, const char *ref,
const unsigned char *sha1)
{
const char *argv_verify_tag[] = {"git-verify-tag",
"-v", "SHA1_HEX", NULL};
argv_verify_tag[2] = sha1_to_hex(sha1);
if (run_command_v_opt(argv_verify_tag, 0))
return error("could not verify the tag '%s'", name);
return 0;
}
static ssize_t do_sign(char *buffer, size_t size, size_t max)
{
struct child_process gpg;
const char *args[4];
char *bracket;
int len;
if (!*signingkey) {
if (strlcpy(signingkey, git_committer_info(1),
sizeof(signingkey)) > sizeof(signingkey) - 1)
return error("committer info too long.");
bracket = strchr(signingkey, '>');
if (bracket)
bracket[1] = '\0';
}
memset(&gpg, 0, sizeof(gpg));
gpg.argv = args;
gpg.in = -1;
gpg.out = -1;
args[0] = "gpg";
args[1] = "-bsau";
args[2] = signingkey;
args[3] = NULL;
if (start_command(&gpg))
return error("could not run gpg.");
write_or_die(gpg.in, buffer, size);
close(gpg.in);
gpg.close_in = 0;
len = read_in_full(gpg.out, buffer + size, max - size);
finish_command(&gpg);
if (len == max - size)
return error("could not read the entire signature from gpg.");
return size + len;
}
static const char tag_template[] =
"\n"
"#\n"
"# Write a tag message\n"
"#\n";
static int git_tag_config(const char *var, const char *value)
{
if (!strcmp(var, "user.signingkey")) {
if (!value)
die("user.signingkey without value");
if (strlcpy(signingkey, value, sizeof(signingkey))
>= sizeof(signingkey))
die("user.signingkey value too long");
return 0;
}
return git_default_config(var, value);
}
#define MAX_SIGNATURE_LENGTH 1024
/* message must be NULL or allocated, it will be reallocated and freed */
static void create_tag(const unsigned char *object, const char *tag,
char *message, int sign, unsigned char *result)
{
enum object_type type;
char header_buf[1024], *buffer = NULL;
int header_len, max_size;
unsigned long size = 0;
type = sha1_object_info(object, NULL);
if (type <= OBJ_NONE)
die("bad object type.");
header_len = snprintf(header_buf, sizeof(header_buf),
"object %s\n"
"type %s\n"
"tag %s\n"
"tagger %s\n\n",
sha1_to_hex(object),
typename(type),
tag,
git_committer_info(1));
if (header_len > sizeof(header_buf) - 1)
die("tag header too big.");
if (!message) {
char *path;
int fd;
/* write the template message before editing: */
path = xstrdup(git_path("TAG_EDITMSG"));
fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (fd < 0)
die("could not create file '%s': %s",
path, strerror(errno));
write_or_die(fd, tag_template, strlen(tag_template));
close(fd);
launch_editor(path, &buffer, &size);
unlink(path);
free(path);
}
else {
buffer = message;
size = strlen(message);
}
size = stripspace(buffer, size, 1);
if (!message && !size)
die("no tag message?");
/* insert the header and add the '\n' if needed: */
max_size = header_len + size + (sign ? MAX_SIGNATURE_LENGTH : 0) + 1;
buffer = xrealloc(buffer, max_size);
if (size)
buffer[size++] = '\n';
memmove(buffer + header_len, buffer, size);
memcpy(buffer, header_buf, header_len);
size += header_len;
if (sign) {
size = do_sign(buffer, size, max_size);
if (size < 0)
die("unable to sign the tag");
}
if (write_sha1_file(buffer, size, tag_type, result) < 0)
die("unable to write tag file");
free(buffer);
}
int cmd_tag(int argc, const char **argv, const char *prefix)
{
unsigned char object[20], prev[20];
int annotate = 0, sign = 0, force = 0, lines = 0;
char *message = NULL;
char ref[PATH_MAX];
const char *object_ref, *tag;
int i;
struct ref_lock *lock;
git_config(git_tag_config);
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (arg[0] != '-')
break;
if (!strcmp(arg, "-a")) {
annotate = 1;
continue;
}
if (!strcmp(arg, "-s")) {
annotate = 1;
sign = 1;
continue;
}
if (!strcmp(arg, "-f")) {
force = 1;
continue;
}
if (!strcmp(arg, "-n")) {
if (i + 1 == argc || *argv[i + 1] == '-')
/* no argument */
lines = 1;
else
lines = isdigit(*argv[++i]) ?
atoi(argv[i]) : 1;
continue;
}
if (!strcmp(arg, "-m")) {
annotate = 1;
i++;
if (i == argc)
die("option -m needs an argument.");
if (message)
die("only one -F or -m option is allowed.");
message = xstrdup(argv[i]);
continue;
}
if (!strcmp(arg, "-F")) {
unsigned long len;
int fd;
annotate = 1;
i++;
if (i == argc)
die("option -F needs an argument.");
if (message)
die("only one -F or -m option is allowed.");
if (!strcmp(argv[i], "-"))
fd = 0;
else {
fd = open(argv[i], O_RDONLY);
if (fd < 0)
die("could not open '%s': %s",
argv[i], strerror(errno));
}
len = 1024;
message = xmalloc(len);
if (read_fd(fd, &message, &len)) {
free(message);
die("cannot read %s", argv[i]);
}
continue;
}
if (!strcmp(arg, "-u")) {
annotate = 1;
sign = 1;
i++;
if (i == argc)
die("option -u needs an argument.");
if (strlcpy(signingkey, argv[i], sizeof(signingkey))
>= sizeof(signingkey))
die("argument to option -u too long");
continue;
}
if (!strcmp(arg, "-l"))
return list_tags(argv[i + 1], lines);
if (!strcmp(arg, "-d"))
return for_each_tag_name(argv + i + 1, delete_tag);
if (!strcmp(arg, "-v"))
return for_each_tag_name(argv + i + 1, verify_tag);
usage(builtin_tag_usage);
}
if (i == argc) {
if (annotate)
usage(builtin_tag_usage);
return list_tags(NULL, lines);
}
tag = argv[i++];
object_ref = i < argc ? argv[i] : "HEAD";
if (i + 1 < argc)
die("too many params");
if (get_sha1(object_ref, object))
die("Failed to resolve '%s' as a valid ref.", object_ref);
if (snprintf(ref, sizeof(ref), "refs/tags/%s", tag) > sizeof(ref) - 1)
die("tag name too long: %.*s...", 50, tag);
if (check_ref_format(ref))
die("'%s' is not a valid tag name.", tag);
if (!resolve_ref(ref, prev, 1, NULL))
hashclr(prev);
else if (!force)
die("tag '%s' already exists", tag);
if (annotate)
create_tag(object, tag, message, sign, object);
lock = lock_any_ref_for_update(ref, prev, 0);
if (!lock)
die("%s: cannot lock the ref", ref);
if (write_ref_sha1(lock, object, NULL) < 0)
die("%s: cannot update the ref", ref);
return 0;
}

View File

@@ -86,9 +86,15 @@ static int process_lstat_error(const char *path, int err)
static int add_one_path(struct cache_entry *old, const char *path, int len, struct stat *st)
{
int option, size = cache_entry_size(len);
struct cache_entry *ce = xcalloc(1, size);
int option, size;
struct cache_entry *ce;
/* Was the old index entry already up-to-date? */
if (old && !ce_stage(old) && !ce_match_stat(old, st, 0))
return 0;
size = cache_entry_size(len);
ce = xcalloc(1, size);
memcpy(ce->name, path, len);
ce->ce_flags = htons(len);
fill_stat_cache_info(ce, st);

110
builtin-verify-tag.c Normal file
View File

@@ -0,0 +1,110 @@
/*
* Builtin "git verify-tag"
*
* Copyright (c) 2007 Carlos Rica <jasampler@gmail.com>
*
* Based on git-verify-tag.sh
*/
#include "cache.h"
#include "builtin.h"
#include "tag.h"
#include "run-command.h"
#include <signal.h>
static const char builtin_verify_tag_usage[] =
"git-verify-tag [-v|--verbose] <tag>...";
#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
static int run_gpg_verify(const char *buf, unsigned long size, int verbose)
{
struct child_process gpg;
const char *args_gpg[] = {"gpg", "--verify", "FILE", "-", NULL};
char path[PATH_MAX], *eol;
size_t len;
int fd, ret;
fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX");
if (fd < 0)
return error("could not create temporary file '%s': %s",
path, strerror(errno));
if (write_in_full(fd, buf, size) < 0)
return error("failed writing temporary file '%s': %s",
path, strerror(errno));
close(fd);
/* find the length without signature */
len = 0;
while (len < size && prefixcmp(buf + len, PGP_SIGNATURE "\n")) {
eol = memchr(buf + len, '\n', size - len);
len += eol ? eol - (buf + len) + 1 : size - len;
}
if (verbose)
write_in_full(1, buf, len);
memset(&gpg, 0, sizeof(gpg));
gpg.argv = args_gpg;
gpg.in = -1;
gpg.out = 1;
args_gpg[2] = path;
if (start_command(&gpg))
return error("could not run gpg.");
write_in_full(gpg.in, buf, len);
close(gpg.in);
gpg.close_in = 0;
ret = finish_command(&gpg);
unlink(path);
return ret;
}
static int verify_tag(const char *name, int verbose)
{
enum object_type type;
unsigned char sha1[20];
char *buf;
unsigned long size;
int ret;
if (get_sha1(name, sha1))
return error("tag '%s' not found.", name);
type = sha1_object_info(sha1, NULL);
if (type != OBJ_TAG)
return error("%s: cannot verify a non-tag object of type %s.",
name, typename(type));
buf = read_sha1_file(sha1, &type, &size);
if (!buf)
return error("%s: unable to read file.", name);
ret = run_gpg_verify(buf, size, verbose);
free(buf);
return ret;
}
int cmd_verify_tag(int argc, const char **argv, const char *prefix)
{
int i = 1, verbose = 0, had_error = 0;
git_config(git_default_config);
if (argc == 1)
usage(builtin_verify_tag_usage);
if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) {
verbose = 1;
i++;
}
/* sometimes the program was terminated because this signal
* was received in the process of writing the gpg input: */
signal(SIGPIPE, SIG_IGN);
while (i < argc)
if (verify_tag(argv[i++], verbose))
had_error = 1;
return had_error;
}

View File

@@ -70,12 +70,14 @@ 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);
extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
extern int cmd_tag(int argc, const char **argv, const char *prefix);
extern int cmd_tar_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_verify_tag(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);

View File

@@ -258,6 +258,7 @@ extern int index_name_pos(struct index_state *, const char *name, int namelen);
#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */
#define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */
#define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */
#define ADD_CACHE_JUST_APPEND 8 /* Append only; tree.c::read_tree() */
extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
extern int remove_index_entry_at(struct index_state *, int pos);
@@ -583,6 +584,8 @@ extern char *pager_program;
extern int pager_in_use;
extern int pager_use_color;
extern char *editor_program;
/* base85 */
int decode_85(char *dst, const char *line, int linelen);
void encode_85(char *buf, const unsigned char *data, int bytes);

View File

@@ -429,6 +429,11 @@ int git_default_config(const char *var, const char *value)
return 0;
}
if (!strcmp(var, "core.editor")) {
editor_program = xstrdup(value);
return 0;
}
/* Add other config variables here and to Documentation/config.txt. */
return 0;
}

View File

@@ -146,6 +146,8 @@ static enum protocol get_protocol(const char *name)
return PROTO_SSH;
if (!strcmp(name, "ssh+git"))
return PROTO_SSH;
if (!strcmp(name, "file"))
return PROTO_LOCAL;
die("I don't handle protocol '%s'", name);
}
@@ -515,14 +517,17 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
end = host;
path = strchr(end, c);
if (c == ':') {
/* host must have at least 2 chars to catch DOS C:/path */
if (path && path - end > 1) {
if (path) {
if (c == ':'
#ifdef __MINGW32__
&& path - end > 1 /* host must have at least 2 chars to catch DOS C:/path */
#endif
) {
protocol = PROTO_SSH;
*path++ = '\0';
} else
path = host;
}
}
} else
path = end;
if (!path || !*path)
die("No path specified. See 'man git-pull' for valid url syntax");

View File

@@ -390,6 +390,30 @@ class P4Submit(Command):
return result
def prepareSubmitTemplate(self):
# remove lines in the Files section that show changes to files outside the depot path we're committing into
template = ""
inFilesSection = False
for line in read_pipe_lines("p4 change -o"):
if inFilesSection:
if line.startswith("\t"):
# path starts and ends with a tab
path = line[1:]
lastTab = path.rfind("\t")
if lastTab != -1:
path = path[:lastTab]
if not path.startswith(self.depotPath):
continue
else:
inFilesSection = False
else:
if line.startswith("Files:"):
inFilesSection = True
template += line
return template
def applyCommit(self, id):
if self.directSubmit:
print "Applying local change in working directory/index"
@@ -467,7 +491,7 @@ class P4Submit(Command):
logMessage = logMessage.replace("\n", "\r\n")
logMessage = logMessage.strip()
template = read_pipe("p4 change -o")
template = self.prepareSubmitTemplate()
if self.interactive:
submitTemplate = self.prepareLogMessage(template, logMessage)
@@ -558,24 +582,24 @@ class P4Submit(Command):
return False
[upstream, settings] = findUpstreamBranchPoint()
depotPath = settings['depot-paths'][0]
self.depotPath = settings['depot-paths'][0]
if len(self.origin) == 0:
self.origin = upstream
if self.verbose:
print "Origin branch is " + self.origin
if len(depotPath) == 0:
if len(self.depotPath) == 0:
print "Internal error: cannot locate perforce depot path from existing branches"
sys.exit(128)
self.clientPath = p4Where(depotPath)
self.clientPath = p4Where(self.depotPath)
if len(self.clientPath) == 0:
print "Error: Cannot locate perforce checkout of %s in client view" % depotPath
print "Error: Cannot locate perforce checkout of %s in client view" % self.depotPath
sys.exit(128)
print "Perforce checkout for depot path %s located at %s" % (depotPath, self.clientPath)
print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
self.oldWorkingDirectory = os.getcwd()
if self.directSubmit:

View File

@@ -102,6 +102,8 @@ my ($this_user) = getpwuid $<; # REAL_USER_ID
my $repository_name;
my %user_committer;
my @allow_rules;
my @path_rules;
my %diff_cache;
sub deny ($) {
print STDERR "-Deny- $_[0]\n" if $debug;
@@ -118,22 +120,36 @@ sub info ($) {
print STDERR "-Info- $_[0]\n" if $debug;
}
sub parse_config ($$) {
my ($data, $fn) = @_;
info "Loading $fn";
open(I,'-|','git',"--git-dir=$acl_git",'cat-file','blob',$fn);
sub git_value (@) {
open(T,'-|','git',@_); local $_ = <T>; chop; close T; $_;
}
sub match_string ($$) {
my ($acl_n, $ref) = @_;
($acl_n eq $ref)
|| ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n)
|| ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:);
}
sub parse_config ($$$$) {
my $data = shift;
local $ENV{GIT_DIR} = shift;
my $br = shift;
my $fn = shift;
info "Loading $br:$fn";
open(I,'-|','git','cat-file','blob',"$br:$fn");
my $section = '';
while (<I>) {
chomp;
if (/^\s*$/ || /^\s*#/) {
} elsif (/^\[([a-z]+)\]$/i) {
$section = $1;
$section = lc $1;
} elsif (/^\[([a-z]+)\s+"(.*)"\]$/i) {
$section = "$1.$2";
$section = join('.',lc $1,$2);
} elsif (/^\s*([a-z][a-z0-9]+)\s*=\s*(.*?)\s*$/i) {
push @{$data->{"$section.$1"}}, $2;
push @{$data->{join('.',$section,lc $1)}}, $2;
} else {
deny "bad config file line $. in $fn";
deny "bad config file line $. in $br:$fn";
}
}
close I;
@@ -202,9 +218,40 @@ sub check_committers (@) {
}
}
sub git_value (@) {
open(T,'-|','git',@_); local $_ = <T>; chop; close T;
$_;
sub load_diff ($) {
my $base = shift;
my $d = $diff_cache{$base};
unless ($d) {
local $/ = "\0";
my %this_diff;
if ($base =~ /^0{40}$/) {
open(T,'-|','git','ls-tree',
'-r','--name-only','-z',
$new) or return undef;
while (<T>) {
chop;
$this_diff{$_} = 'A';
}
close T or return undef;
} else {
open(T,'-|','git','diff-tree',
'-r','--name-status','-z',
$base,$new) or return undef;
while (<T>) {
my $op = $_;
chop $op;
my $path = <T>;
chop $path;
$this_diff{$path} = $op;
}
close T or return undef;
}
$d = \%this_diff;
$diff_cache{$base} = $d;
}
return $d;
}
deny "No GIT_DIR inherited from caller" unless $git_dir;
@@ -231,14 +278,52 @@ $op = 'U' if ($op eq 'R'
&& $ref =~ m,^heads/,
&& $old eq git_value('merge-base',$old,$new));
# Load the user's ACL file.
# Load the user's ACL file. Expand groups (user.memberof) one level.
{
my %data = ('user.committer' => []);
parse_config(\%data, "$acl_branch:users/$this_user.acl");
parse_config(\%data,$acl_git,$acl_branch,"external/$repository_name.acl");
%data = (
'user.committer' => $data{'user.committer'},
'user.memberof' => [],
);
parse_config(\%data,$acl_git,$acl_branch,"users/$this_user.acl");
%user_committer = map {$_ => $_} @{$data{'user.committer'}};
my $rules = $data{"repository.$repository_name.allow"} || [];
my $rule_key = "repository.$repository_name.allow";
my $rules = $data{$rule_key} || [];
foreach my $group (@{$data{'user.memberof'}}) {
my %g;
parse_config(\%g,$acl_git,$acl_branch,"groups/$group.acl");
my $group_rules = $g{$rule_key};
push @$rules, @$group_rules if $group_rules;
}
RULE:
foreach (@$rules) {
if (/^([CDRU ]+)\s+for\s+([^\s]+)$/) {
while (/\${user\.([a-z][a-zA-Z0-9]+)}/) {
my $k = lc $1;
my $v = $data{"user.$k"};
next RULE unless defined $v;
next RULE if @$v != 1;
next RULE unless defined $v->[0];
s/\${user\.$k}/$v->[0]/g;
}
if (/^([AMD ]+)\s+of\s+([^\s]+)\s+for\s+([^\s]+)\s+diff\s+([^\s]+)$/) {
my ($ops, $pth, $ref, $bst) = ($1, $2, $3, $4);
$ops =~ s/ //g;
$pth =~ s/\\\\/\\/g;
$ref =~ s/\\\\/\\/g;
push @path_rules, [$ops, $pth, $ref, $bst];
} elsif (/^([AMD ]+)\s+of\s+([^\s]+)\s+for\s+([^\s]+)$/) {
my ($ops, $pth, $ref) = ($1, $2, $3);
$ops =~ s/ //g;
$pth =~ s/\\\\/\\/g;
$ref =~ s/\\\\/\\/g;
push @path_rules, [$ops, $pth, $ref, $old];
} elsif (/^([CDRU ]+)\s+for\s+([^\s]+)$/) {
my $ops = $1;
my $ref = $2;
$ops =~ s/ //g;
@@ -272,13 +357,65 @@ foreach my $acl_entry (@allow_rules) {
next unless $acl_ops =~ /^[CDRU]+$/; # Uhh.... shouldn't happen.
next unless $acl_n;
next unless $op =~ /^[$acl_ops]$/;
next unless match_string $acl_n, $ref;
grant "Allowed by: $acl_ops for $acl_n"
if (
($acl_n eq $ref)
|| ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n)
|| ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:)
);
# Don't test path rules on branch deletes.
#
grant "Allowed by: $acl_ops for $acl_n" if $op eq 'D';
# Aggregate matching path rules; allow if there aren't
# any matching this ref.
#
my %pr;
foreach my $p_entry (@path_rules) {
my ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry;
next unless $p_ref;
push @{$pr{$p_bst}}, $p_entry if match_string $p_ref, $ref;
}
grant "Allowed by: $acl_ops for $acl_n" unless %pr;
# Allow only if all changes against a single base are
# allowed by file path rules.
#
my @bad;
foreach my $p_bst (keys %pr) {
my $diff_ref = load_diff $p_bst;
deny "Cannot difference trees." unless ref $diff_ref;
my %fd = %$diff_ref;
foreach my $p_entry (@{$pr{$p_bst}}) {
my ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry;
next unless $p_ops =~ /^[AMD]+$/;
next unless $p_n;
foreach my $f_n (keys %fd) {
my $f_op = $fd{$f_n};
next unless $f_op;
next unless $f_op =~ /^[$p_ops]$/;
delete $fd{$f_n} if match_string $p_n, $f_n;
}
last unless %fd;
}
if (%fd) {
push @bad, [$p_bst, \%fd];
} else {
# All changes relative to $p_bst were allowed.
#
grant "Allowed by: $acl_ops for $acl_n diff $p_bst";
}
}
foreach my $bad_ref (@bad) {
my ($p_bst, $fd) = @$bad_ref;
print STDERR "\n";
print STDERR "Not allowed to make the following changes:\n";
print STDERR "(base: $p_bst)\n";
foreach my $f_n (sort keys %$fd) {
print STDERR " $fd->{$f_n} $f_n\n";
}
}
deny "You are not permitted to $op $ref";
}
close A;
deny "You are not permitted to $op $ref";

View File

@@ -33,6 +33,7 @@ size_t delta_base_cache_limit = 16 * 1024 * 1024;
char *pager_program;
int pager_in_use;
int pager_use_color = 1;
char *editor_program;
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
/* This is set by setup_git_dir_gently() and/or git_default_config() */

View File

@@ -87,7 +87,7 @@ Perhaps git-update-server-info needs to be run there?"
quiet=
local=no
use_local=no
use_local_hardlink=yes
local_shared=no
unset template
no_checkout=
@@ -108,9 +108,13 @@ while
no_checkout=yes ;;
*,--na|*,--nak|*,--nake|*,--naked|\
*,-b|*,--b|*,--ba|*,--bar|*,--bare) bare=yes ;;
*,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local) use_local=yes ;;
*,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local)
use_local_hardlink=yes ;;
*,--no-h|*,--no-ha|*,--no-har|*,--no-hard|*,--no-hardl|\
*,--no-hardli|*,--no-hardlin|*,--no-hardlink|*,--no-hardlinks)
use_local_hardlink=no ;;
*,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared)
local_shared=yes; use_local=yes ;;
local_shared=yes; ;;
1,--template) usage ;;
*,--template)
shift; template="--template=$1" ;;
@@ -249,34 +253,36 @@ fi
rm -f "$GIT_DIR/CLONE_HEAD"
# We do local magic only when the user tells us to.
case "$local,$use_local" in
yes,yes)
case "$local" in
yes)
( cd "$repo/objects" ) ||
die "-l flag seen but repository '$repo' is not local."
die "cannot chdir to local '$repo/objects'."
case "$local_shared" in
no)
# See if we can hardlink and drop "l" if not.
sample_file=$(cd "$repo" && \
/usr/bin/find objects -type f -print | sed -e 1q)
# objects directory should not be empty since we are cloning!
test -f "$repo/$sample_file" || exit
l=
if ln "$repo/$sample_file" "$GIT_DIR/objects/sample" 2>/dev/null
then
l=l
fi &&
rm -f "$GIT_DIR/objects/sample" &&
cd "$repo" &&
/usr/bin/find objects -depth -print | cpio -pumd$l "$GIT_DIR/" || exit 1
;;
yes)
mkdir -p "$GIT_DIR/objects/info"
echo "$repo/objects" >> "$GIT_DIR/objects/info/alternates"
;;
esac
if test "$local_shared" = yes
then
mkdir -p "$GIT_DIR/objects/info"
echo "$repo/objects" >>"$GIT_DIR/objects/info/alternates"
else
l= &&
if test "$use_local_hardlink" = yes
then
# See if we can hardlink and drop "l" if not.
sample_file=$(cd "$repo" && \
find objects -type f -print | sed -e 1q)
# objects directory should not be empty because
# we are cloning!
test -f "$repo/$sample_file" || exit
if ln "$repo/$sample_file" "$GIT_DIR/objects/sample" 2>/dev/null
then
rm -f "$GIT_DIR/objects/sample"
l=l
else
echo >&2 "Warning: -l asked but cannot hardlink to $repo"
fi
fi &&
cd "$repo" &&
find objects -depth -print | cpio -pumd$l "$GIT_DIR/" || exit 1
fi
git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1
;;
*)

View File

@@ -1196,6 +1196,7 @@ sub req_ci
$log->info("Lockless commit start, basing commit on '$tmpdir', index file is '$file_index'");
$ENV{GIT_DIR} = $state->{CVSROOT} . "/";
$ENV{GIT_WORK_TREE} = ".";
$ENV{GIT_INDEX_FILE} = $file_index;
# Remember where the head was at the beginning.
@@ -1721,6 +1722,7 @@ sub req_annotate
$log->info("Temp checkoutdir creation successful, basing annotate session work on '$tmpdir', index file is '$file_index'");
$ENV{GIT_DIR} = $state->{CVSROOT} . "/";
$ENV{GIT_WORK_TREE} = ".";
$ENV{GIT_INDEX_FILE} = $file_index;
chdir $tmpdir;

View File

@@ -137,7 +137,7 @@ my $compose_filename = ".msg.$$";
# Variables we fill in automatically, or via prompting:
my (@to,@cc,@initial_cc,@bcclist,@xh,
$initial_reply_to,$initial_subject,@files,$from,$compose,$time);
$initial_reply_to,$initial_subject,@files,$author,$sender,$compose,$time);
my $smtp_server;
my $envelope_sender;
@@ -179,7 +179,7 @@ if (!@bcclist or !$bcclist[0]) {
# Begin by accumulating all the variables (defined above), that we will end up
# needing, first, from the command line:
my $rc = GetOptions("from=s" => \$from,
my $rc = GetOptions("sender|from=s" => \$sender,
"in-reply-to=s" => \$initial_reply_to,
"subject=s" => \$initial_subject,
"to=s" => \@to,
@@ -216,8 +216,8 @@ foreach my $entry (@bcclist) {
# Now, let's fill any that aren't set in with defaults:
my ($author) = $repo->ident_person('author');
my ($committer) = $repo->ident_person('committer');
my ($repoauthor) = $repo->ident_person('author');
my ($repocommitter) = $repo->ident_person('committer');
my %aliases;
my @alias_files = $repo->config('sendemail.aliasesfile');
@@ -254,17 +254,17 @@ if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) {
}
}
($from) = expand_aliases($from) if defined $from;
($sender) = expand_aliases($sender) if defined $sender;
my $prompting = 0;
if (!defined $from) {
$from = $author || $committer;
if (!defined $sender) {
$sender = $repoauthor || $repocommitter;
do {
$_ = $term->readline("Who should the emails appear to be from? [$from] ");
$_ = $term->readline("Who should the emails appear to be from? [$sender] ");
} while (!defined $_);
$from = $_ if ($_);
print "Emails will be sent from: ", $from, "\n";
$sender = $_ if ($_);
print "Emails will be sent from: ", $sender, "\n";
$prompting++;
}
@@ -330,7 +330,7 @@ if ($compose) {
# effort to have it be unique
open(C,">",$compose_filename)
or die "Failed to open for writing $compose_filename: $!";
print C "From $from # This line is ignored.\n";
print C "From $sender # This line is ignored.\n";
printf C "Subject: %s\n\n", $initial_subject;
printf C <<EOT;
GIT: Please enter your email below.
@@ -408,8 +408,8 @@ sub extract_valid_address {
# check for a local address:
return $address if ($address =~ /^($local_part_regexp)$/);
$address =~ s/^\s*<(.*)>\s*$/$1/;
if ($have_email_valid) {
$address =~ s/^\s*<(.*)>\s*$/$1/;
return scalar Email::Valid->address($address);
} else {
# less robust/correct than the monster regexp in Email::Valid,
@@ -433,11 +433,11 @@ sub make_message_id
my $date = time;
my $pseudo_rand = int (rand(4200));
my $du_part;
for ($from, $committer, $author) {
$du_part = extract_valid_address($_);
last if ($du_part ne '');
for ($sender, $repocommitter, $repoauthor) {
$du_part = extract_valid_address(sanitize_address($_));
last if (defined $du_part and $du_part ne '');
}
if ($du_part eq '') {
if (not defined $du_part or $du_part eq '') {
use Sys::Hostname qw();
$du_part = 'user@' . Sys::Hostname::hostname();
}
@@ -508,10 +508,10 @@ sub send_message
if ($cc ne '') {
$ccline = "\nCc: $cc";
}
$from = sanitize_address($from);
my $sanitized_sender = sanitize_address($sender);
make_message_id();
my $header = "From: $from
my $header = "From: $sanitized_sender
To: $to${ccline}
Subject: $subject
Date: $date
@@ -528,7 +528,7 @@ X-Mailer: git-send-email $gitversion
}
my @sendmail_parameters = ('-i', @recipients);
my $raw_from = $from;
my $raw_from = $sanitized_sender;
$raw_from = $envelope_sender if (defined $envelope_sender);
$raw_from = extract_valid_address($raw_from);
unshift (@sendmail_parameters,
@@ -565,7 +565,7 @@ X-Mailer: git-send-email $gitversion
} else {
print "Sendmail: $smtp_server ".join(' ',@sendmail_parameters)."\n";
}
print "From: $from\nSubject: $subject\nCc: $cc\nTo: $to\n\n";
print "From: $sanitized_sender\nSubject: $subject\nCc: $cc\nTo: $to\n\n";
if ($smtp) {
print "Result: ", $smtp->code, ' ',
($smtp->message =~ /\n([^\n]+\n)$/s), "\n";
@@ -582,7 +582,7 @@ $subject = $initial_subject;
foreach my $t (@files) {
open(F,"<",$t) or die "can't open file $t";
my $author_not_sender = undef;
my $author = undef;
@cc = @initial_cc;
@xh = ();
my $input_format = undef;
@@ -604,12 +604,11 @@ foreach my $t (@files) {
$subject = $1;
} elsif (/^(Cc|From):\s+(.*)$/) {
if (unquote_rfc2047($2) eq $from) {
$from = $2;
if (unquote_rfc2047($2) eq $sender) {
next if ($suppress_from);
}
elsif ($1 eq 'From') {
$author_not_sender = $2;
$author = unquote_rfc2047($2);
}
printf("(mbox) Adding cc: %s from line '%s'\n",
$2, $_) unless $quiet;
@@ -653,9 +652,8 @@ foreach my $t (@files) {
}
}
close F;
if (defined $author_not_sender) {
$author_not_sender = unquote_rfc2047($author_not_sender);
$message = "From: $author_not_sender\n\n$message";
if (defined $author) {
$message = "From: $author\n\n$message";
}

2
git.c
View File

@@ -373,11 +373,13 @@ static void handle_internal_command(int argc, const char **argv)
{ "show", cmd_show, RUN_SETUP | USE_PAGER },
{ "stripspace", cmd_stripspace },
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
{ "tag", cmd_tag, RUN_SETUP },
{ "tar-tree", cmd_tar_tree },
{ "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 },
{ "verify-tag", cmd_verify_tag, RUN_SETUP },
{ "version", cmd_version },
{ "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
{ "write-tree", cmd_write_tree, RUN_SETUP },

View File

@@ -216,13 +216,19 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
*/
static int index_only = 0;
static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree)
{
parse_tree(tree);
init_tree_desc(desc, tree->buffer, tree->size);
}
static int git_merge_trees(int index_only,
struct tree *common,
struct tree *head,
struct tree *merge)
{
int rc;
struct object_list *trees = NULL;
struct tree_desc t[3];
struct unpack_trees_options opts;
memset(&opts, 0, sizeof(opts));
@@ -234,11 +240,11 @@ static int git_merge_trees(int index_only,
opts.head_idx = 2;
opts.fn = threeway_merge;
object_list_append(&common->object, &trees);
object_list_append(&head->object, &trees);
object_list_append(&merge->object, &trees);
init_tree_desc_from_tree(t+0, common);
init_tree_desc_from_tree(t+1, head);
init_tree_desc_from_tree(t+2, merge);
rc = unpack_trees(trees, &opts);
rc = unpack_trees(3, t, &opts);
cache_tree_free(&active_cache_tree);
return rc;
}

View File

@@ -670,7 +670,7 @@ static int check_file_directory_conflict(struct index_state *istate,
return retval + has_dir_name(istate, ce, pos, ok_to_replace);
}
int add_index_entry(struct index_state *istate, struct cache_entry *ce, int option)
static int add_index_entry_with_check(struct index_state *istate, struct cache_entry *ce, int option)
{
int pos;
int ok_to_add = option & ADD_CACHE_OK_TO_ADD;
@@ -712,6 +712,22 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti
pos = index_name_pos(istate, ce->name, ntohs(ce->ce_flags));
pos = -pos-1;
}
return pos + 1;
}
int add_index_entry(struct index_state *istate, struct cache_entry *ce, int option)
{
int pos;
if (option & ADD_CACHE_JUST_APPEND)
pos = istate->cache_nr;
else {
int ret;
ret = add_index_entry_with_check(istate, ce, option);
if (ret <= 0)
return ret;
pos = ret - 1;
}
/* Make sure the array is big enough .. */
if (istate->cache_nr == istate->cache_alloc) {
@@ -722,7 +738,7 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti
/* Add it in.. */
istate->cache_nr++;
if (istate->cache_nr > pos)
if (istate->cache_nr > pos + 1)
memmove(istate->cache + pos + 1,
istate->cache + pos,
(istate->cache_nr - pos - 1) * sizeof(ce));

1
refs.c
View File

@@ -869,6 +869,7 @@ static int repack_without_ref(const char *refname)
die("too long a refname '%s'", list->name);
write_or_die(fd, line, len);
}
close(fd);
return commit_lock_file(&packlock);
}

52
setup.c
View File

@@ -226,53 +226,21 @@ int is_inside_work_tree(void)
}
/*
* If no worktree was given, and we are outside of a default work tree,
* now is the time to set it.
*
* In other words, if the user calls git with something like
*
* git --git-dir=/some/where/else/.git bla
*
* default to /some/where/else as working directory; if the specified
* git-dir does not end in "/.git", the cwd is used as working directory.
* set_work_tree() is only ever called if you set GIT_DIR explicitely.
* The old behaviour (which we retain here) is to set the work tree root
* to the cwd, unless overridden by the config, the command line, or
* GIT_WORK_TREE.
*/
const char *set_work_tree(const char *dir)
static const char *set_work_tree(const char *dir)
{
char dir_buffer[PATH_MAX], *rel = NULL;
static char buffer[PATH_MAX + 1];
int len, suffix_len = strlen(DEFAULT_GIT_DIR_ENVIRONMENT) + 1;
char buffer[PATH_MAX + 1];
/* strip the variable 'dir' of the postfix "/.git" if it has it */
len = strlen(dir);
if (len > suffix_len &&
!strcmp(dir + len - suffix_len, "/" DEFAULT_GIT_DIR_ENVIRONMENT)) {
if ((len - suffix_len) >= sizeof(dir_buffer))
die("directory name too long");
memcpy(dir_buffer, dir, len - suffix_len);
dir_buffer[len - suffix_len] = '\0';
/* are we inside the default work tree? */
rel = get_relative_cwd(buffer, sizeof(buffer), dir_buffer);
}
/* if rel is set, the cwd is _not_ the current working tree */
if (rel && *rel) {
if (!is_absolute_path(dir))
set_git_dir(make_absolute_path(dir));
dir = dir_buffer;
if (chdir(dir))
die("cannot chdir to %s: %s", dir, strerror(errno));
else
strcat(rel, "/");
inside_git_dir = 0;
} else {
rel = NULL;
dir = getcwd(buffer, sizeof(buffer));
}
git_work_tree_cfg = xstrdup(dir);
if (!getcwd(buffer, sizeof(buffer)))
die ("Could not get the current working directory");
git_work_tree_cfg = xstrdup(buffer);
inside_work_tree = 1;
return rel;
return NULL;
}
/*

View File

@@ -392,4 +392,9 @@ test_expect_success \
git diff expect actual
'
test_expect_success 'strip comments, too' '
test ! -z "$(echo "# comment" | git stripspace)" &&
test -z "$(echo "# comment" | git stripspace -s)"
'
test_done

View File

@@ -28,6 +28,8 @@ test_rev_parse() {
[ $# -eq 0 ] && return
}
# label is-bare is-inside-git is-inside-work prefix
test_rev_parse toplevel false false true ''
cd .git || exit 1
@@ -53,13 +55,13 @@ export GIT_DIR=../.git
export GIT_CONFIG="$(pwd)"/../.git/config
git config core.bare false
test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true work/
test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true ''
git config core.bare true
test_rev_parse 'GIT_DIR=../.git, core.bare = true' true false false ''
git config --unset core.bare
test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true work/
test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true ''
mv ../.git ../repo.git || exit 1
export GIT_DIR=../repo.git

View File

@@ -129,7 +129,7 @@ pull_to_client 2nd "B" $((64*3))
pull_to_client 3rd "A" $((1*3)) # old fails
test_expect_success "clone shallow" "git-clone --depth 2 . shallow"
test_expect_success "clone shallow" "git-clone --depth 2 file://`pwd`/. shallow"
(cd shallow; git count-objects -v) > count.shallow

View File

@@ -145,4 +145,12 @@ test_expect_success 'bundle does not prerequisite objects' '
test 4 = $(git verify-pack -v bundle.pack | wc -l)
'
test_expect_success 'bundle should be able to create a full history' '
cd "$D" &&
git tag -a -m '1.0' v1.0 master &&
git bundle create bundle4 v1.0
'
test_done

View File

@@ -51,7 +51,7 @@ diff expected current'
cd "$base_dir"
test_expect_success 'cloning with reference (no -l -s)' \
'git clone --reference B A D'
'git clone --reference B file://`pwd`/A D'
cd "$base_dir"

View File

@@ -43,4 +43,21 @@ test_expect_success 'local clone from x.git that does not exist' '
fi
'
test_expect_success 'With -no-hardlinks, local will make a copy' '
cd "$D" &&
git clone --bare --no-hardlinks x w &&
cd w &&
linked=$(find objects -type f ! -links 1 | wc -l) &&
test "$linked" = 0
'
test_expect_success 'Even without -l, local will make a hardlink' '
cd "$D" &&
rm -fr w &&
git clone -l --bare x w &&
cd w &&
copied=$(find objects -type f -links 1 | wc -l) &&
test "$copied" = 0
'
test_done

View File

@@ -5,7 +5,7 @@
test_description='git-tag
Basic tests for operations with tags.'
Tests for operations with tags.'
. ./test-lib.sh
@@ -16,11 +16,15 @@ tag_exists () {
}
# todo: git tag -l now returns always zero, when fixed, change this test
test_expect_success 'listing all tags in an empty tree should succeed' \
'git tag -l'
test_expect_success 'listing all tags in an empty tree should succeed' '
git tag -l &&
git tag
'
test_expect_success 'listing all tags in an empty tree should output nothing' \
'test `git-tag -l | wc -l` -eq 0'
test_expect_success 'listing all tags in an empty tree should output nothing' '
test `git-tag -l | wc -l` -eq 0 &&
test `git-tag | wc -l` -eq 0
'
test_expect_failure 'looking for a tag in an empty tree should fail' \
'tag_exists mytag'
@@ -49,11 +53,15 @@ test_expect_success 'creating a tag using default HEAD should succeed' '
git tag mytag
'
test_expect_success 'listing all tags if one exists should succeed' \
'git-tag -l'
test_expect_success 'listing all tags if one exists should succeed' '
git-tag -l &&
git-tag
'
test_expect_success 'listing all tags if one exists should output that tag' \
'test `git-tag -l` = mytag'
test_expect_success 'listing all tags if one exists should output that tag' '
test `git-tag -l` = mytag &&
test `git-tag` = mytag
'
# pattern matching:
@@ -164,7 +172,9 @@ test_expect_success 'listing all tags should print them ordered' '
git tag a1 &&
git tag v1.0 &&
git tag t210 &&
git tag -l | cat > actual &&
git tag -l > actual &&
git diff expect actual &&
git tag > actual &&
git diff expect actual
'
@@ -264,6 +274,10 @@ test_expect_failure \
'trying to verify a non-annotated and non-signed tag should fail' \
'git-tag -v non-annotated-tag'
test_expect_failure \
'trying to verify many non-annotated or unknown tags, should fail' \
'git-tag -v unknown-tag1 non-annotated-tag unknown-tag2'
# creating annotated tags:
get_tag_msg () {
@@ -306,6 +320,45 @@ test_expect_success \
git diff expect actual
'
cat >inputmsg <<EOF
A message from the
standard input
EOF
get_tag_header stdin-annotated-tag $commit commit $time >expect
cat inputmsg >>expect
test_expect_success 'creating an annotated tag with -F - should succeed' '
git-tag -F - stdin-annotated-tag <inputmsg &&
get_tag_msg stdin-annotated-tag >actual &&
git diff expect actual
'
test_expect_success \
'trying to create a tag with a non-existing -F file should fail' '
! test -f nonexistingfile &&
! tag_exists notag &&
! git-tag -F nonexistingfile notag &&
! tag_exists notag
'
test_expect_success \
'trying to create tags giving many -m or -F options should fail' '
echo "message file 1" >msgfile1 &&
echo "message file 2" >msgfile2 &&
! tag_exists msgtag &&
! git-tag -m "message 1" -m "message 2" msgtag &&
! tag_exists msgtag &&
! git-tag -F msgfile1 -F msgfile2 msgtag &&
! tag_exists msgtag &&
! git-tag -m "message 1" -F msgfile1 msgtag &&
! tag_exists msgtag &&
! git-tag -F msgfile1 -m "message 1" msgtag &&
! tag_exists msgtag &&
! git-tag -F msgfile1 -m "message 1" -F msgfile2 msgtag &&
! tag_exists msgtag &&
! git-tag -m "message 1" -F msgfile1 -m "message 2" msgtag &&
! tag_exists msgtag
'
# blank and empty messages:
get_tag_header empty-annotated-tag $commit commit $time >expect
@@ -551,6 +604,12 @@ test_expect_success \
! git-tag -v file-annotated-tag
'
test_expect_success \
'trying to verify two annotated non-signed tags should fail' '
tag_exists annotated-tag file-annotated-tag &&
! git-tag -v annotated-tag file-annotated-tag
'
# creating and verifying signed tags:
gpg --version >/dev/null
@@ -589,9 +648,55 @@ test_expect_success 'creating a signed tag with -m message should succeed' '
git diff expect actual
'
cat >sigmsgfile <<EOF
Another signed tag
message in a file.
EOF
get_tag_header file-signed-tag $commit commit $time >expect
cat sigmsgfile >>expect
echo '-----BEGIN PGP SIGNATURE-----' >>expect
test_expect_success \
'creating a signed tag with -F messagefile should succeed' '
git-tag -s -F sigmsgfile file-signed-tag &&
get_tag_msg file-signed-tag >actual &&
git diff expect actual
'
cat >siginputmsg <<EOF
A signed tag message from
the standard input
EOF
get_tag_header stdin-signed-tag $commit commit $time >expect
cat siginputmsg >>expect
echo '-----BEGIN PGP SIGNATURE-----' >>expect
test_expect_success 'creating a signed tag with -F - should succeed' '
git-tag -s -F - stdin-signed-tag <siginputmsg &&
get_tag_msg stdin-signed-tag >actual &&
git diff expect actual
'
test_expect_success \
'trying to create a signed tag with non-existing -F file should fail' '
! test -f nonexistingfile &&
! tag_exists nosigtag &&
! git-tag -s -F nonexistingfile nosigtag &&
! tag_exists nosigtag
'
test_expect_success 'verifying a signed tag should succeed' \
'git-tag -v signed-tag'
test_expect_success 'verifying two signed tags in one command should succeed' \
'git-tag -v signed-tag file-signed-tag'
test_expect_success \
'verifying many signed and non-signed tags should fail' '
! git-tag -v signed-tag annotated-tag &&
! git-tag -v file-annotated-tag file-signed-tag &&
! git-tag -v annotated-tag file-signed-tag file-annotated-tag &&
! git-tag -v signed-tag annotated-tag file-signed-tag
'
test_expect_success 'verifying a forged tag should fail' '
forged=$(git cat-file tag signed-tag |
sed -e "s/signed-tag/forged-tag/" |

69
tree.c
View File

@@ -1,4 +1,5 @@
#include "cache.h"
#include "cache-tree.h"
#include "tree.h"
#include "blob.h"
#include "commit.h"
@@ -7,7 +8,7 @@
const char *tree_type = "tree";
static int read_one_entry(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
static int read_one_entry_opt(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage, int opt)
{
int len;
unsigned int size;
@@ -25,7 +26,23 @@ static int read_one_entry(const unsigned char *sha1, const char *base, int basel
memcpy(ce->name, base, baselen);
memcpy(ce->name + baselen, pathname, len+1);
hashcpy(ce->sha1, sha1);
return add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
return add_cache_entry(ce, opt);
}
static int read_one_entry(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
{
return read_one_entry_opt(sha1, base, baselen, pathname, mode, stage,
ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
}
/*
* This is used when the caller knows there is no existing entries at
* the stage that will conflict with the entry being added.
*/
static int read_one_entry_quick(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
{
return read_one_entry_opt(sha1, base, baselen, pathname, mode, stage,
ADD_CACHE_JUST_APPEND);
}
static int match_tree_entry(const char *base, int baselen, const char *path, unsigned int mode, const char **paths)
@@ -119,9 +136,55 @@ int read_tree_recursive(struct tree *tree,
return 0;
}
static int cmp_cache_name_compare(const void *a_, const void *b_)
{
const struct cache_entry *ce1, *ce2;
ce1 = *((const struct cache_entry **)a_);
ce2 = *((const struct cache_entry **)b_);
return cache_name_compare(ce1->name, ntohs(ce1->ce_flags),
ce2->name, ntohs(ce2->ce_flags));
}
int read_tree(struct tree *tree, int stage, const char **match)
{
return read_tree_recursive(tree, "", 0, stage, match, read_one_entry);
read_tree_fn_t fn = NULL;
int i, err;
/*
* Currently the only existing callers of this function all
* call it with stage=1 and after making sure there is nothing
* at that stage; we could always use read_one_entry_quick().
*
* But when we decide to straighten out git-read-tree not to
* use unpack_trees() in some cases, this will probably start
* to matter.
*/
/*
* See if we have cache entry at the stage. If so,
* do it the original slow way, otherwise, append and then
* sort at the end.
*/
for (i = 0; !fn && i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
if (ce_stage(ce) == stage)
fn = read_one_entry;
}
if (!fn)
fn = read_one_entry_quick;
err = read_tree_recursive(tree, "", 0, stage, match, fn);
if (fn == read_one_entry || err)
return err;
/*
* Sort the cache entry -- we need to nuke the cache tree, though.
*/
cache_tree_free(&active_cache_tree);
qsort(active_cache, active_nr, sizeof(active_cache[0]),
cmp_cache_name_compare);
return 0;
}
struct tree *lookup_tree(const unsigned char *sha1)

View File

@@ -16,19 +16,13 @@ struct tree_entry_list {
const unsigned char *sha1;
};
static struct tree_entry_list *create_tree_entry_list(struct tree *tree)
static struct tree_entry_list *create_tree_entry_list(struct tree_desc *desc)
{
struct tree_desc desc;
struct name_entry one;
struct tree_entry_list *ret = NULL;
struct tree_entry_list **list_p = &ret;
if (!tree->object.parsed)
parse_tree(tree);
init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &one)) {
while (tree_entry(desc, &one)) {
struct tree_entry_list *entry;
entry = xmalloc(sizeof(struct tree_entry_list));
@@ -64,10 +58,17 @@ static int entcmp(const char *name1, int dir1, const char *name2, int dir2)
return ret;
}
static inline void remove_entry(int remove)
{
if (remove >= 0)
remove_cache_entry_at(remove);
}
static int unpack_trees_rec(struct tree_entry_list **posns, int len,
const char *base, struct unpack_trees_options *o,
struct tree_entry_list *df_conflict_list)
{
int remove;
int baselen = strlen(base);
int src_size = len + 1;
int i_stk = i_stk;
@@ -151,10 +152,11 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
subposns = xcalloc(len, sizeof(struct tree_list_entry *));
remove = -1;
if (cache_name && !strcmp(cache_name, first)) {
any_files = 1;
src[0] = active_cache[o->pos];
remove_cache_entry_at(o->pos);
remove = o->pos;
}
for (i = 0; i < len; i++) {
@@ -173,9 +175,11 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
if (S_ISDIR(posns[i]->mode)) {
struct tree *tree = lookup_tree(posns[i]->sha1);
struct tree_desc t;
any_dirs = 1;
parse_tree(tree);
subposns[i] = create_tree_entry_list(tree);
init_tree_desc(&t, tree->buffer, tree->size);
subposns[i] = create_tree_entry_list(&t);
posns[i] = posns[i]->next;
src[i + o->merge] = o->df_conflict_entry;
continue;
@@ -218,13 +222,14 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
printf("\n");
}
#endif
ret = o->fn(src, o);
ret = o->fn(src, o, remove);
#if DBRT_DEBUG > 1
printf("Added %d entries\n", ret);
#endif
o->pos += ret;
} else {
remove_entry(remove);
for (i = 0; i < src_size; i++) {
if (src[i]) {
add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
@@ -331,12 +336,10 @@ static void check_updates(struct cache_entry **src, int nr,
stop_progress(&progress);;
}
int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)
int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
{
unsigned len = object_list_length(trees);
struct tree_entry_list **posns;
int i;
struct object_list *posn = trees;
struct tree_entry_list df_conflict_list;
static struct cache_entry *dfc;
@@ -356,10 +359,9 @@ int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)
if (len) {
posns = xmalloc(len * sizeof(struct tree_entry_list *));
for (i = 0; i < len; i++) {
posns[i] = create_tree_entry_list((struct tree *) posn->item);
posn = posn->next;
}
for (i = 0; i < len; i++)
posns[i] = create_tree_entry_list(t+i);
if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "",
o, &df_conflict_list))
return -1;
@@ -648,7 +650,8 @@ static void show_stage_entry(FILE *o,
#endif
int threeway_merge(struct cache_entry **stages,
struct unpack_trees_options *o)
struct unpack_trees_options *o,
int remove)
{
struct cache_entry *index;
struct cache_entry *head;
@@ -726,8 +729,10 @@ int threeway_merge(struct cache_entry **stages,
}
/* #1 */
if (!head && !remote && any_anc_missing)
if (!head && !remote && any_anc_missing) {
remove_entry(remove);
return 0;
}
/* Under the new "aggressive" rule, we resolve mostly trivial
* cases that we historically had git-merge-one-file resolve.
@@ -759,6 +764,7 @@ int threeway_merge(struct cache_entry **stages,
if ((head_deleted && remote_deleted) ||
(head_deleted && remote && remote_match) ||
(remote_deleted && head && head_match)) {
remove_entry(remove);
if (index)
return deleted_entry(index, index, o);
else if (ce && !head_deleted)
@@ -781,6 +787,7 @@ int threeway_merge(struct cache_entry **stages,
verify_uptodate(index, o);
}
remove_entry(remove);
o->nontrivial_merge = 1;
/* #2, #3, #4, #6, #7, #9, #10, #11. */
@@ -816,7 +823,8 @@ int threeway_merge(struct cache_entry **stages,
*
*/
int twoway_merge(struct cache_entry **src,
struct unpack_trees_options *o)
struct unpack_trees_options *o,
int remove)
{
struct cache_entry *current = src[0];
struct cache_entry *oldtree = src[1];
@@ -844,6 +852,7 @@ int twoway_merge(struct cache_entry **src,
}
else if (oldtree && !newtree && same(current, oldtree)) {
/* 10 or 11 */
remove_entry(remove);
return deleted_entry(oldtree, current, o);
}
else if (oldtree && newtree &&
@@ -853,6 +862,7 @@ int twoway_merge(struct cache_entry **src,
}
else {
/* all other failures */
remove_entry(remove);
if (oldtree)
reject_merge(oldtree);
if (current)
@@ -864,8 +874,8 @@ int twoway_merge(struct cache_entry **src,
}
else if (newtree)
return merged_entry(newtree, current, o);
else
return deleted_entry(oldtree, current, o);
remove_entry(remove);
return deleted_entry(oldtree, current, o);
}
/*
@@ -875,7 +885,8 @@ int twoway_merge(struct cache_entry **src,
* stage0 does not have anything there.
*/
int bind_merge(struct cache_entry **src,
struct unpack_trees_options *o)
struct unpack_trees_options *o,
int remove)
{
struct cache_entry *old = src[0];
struct cache_entry *a = src[1];
@@ -898,7 +909,8 @@ int bind_merge(struct cache_entry **src,
* - take the stat information from stage0, take the data from stage1
*/
int oneway_merge(struct cache_entry **src,
struct unpack_trees_options *o)
struct unpack_trees_options *o,
int remove)
{
struct cache_entry *old = src[0];
struct cache_entry *a = src[1];
@@ -907,8 +919,10 @@ int oneway_merge(struct cache_entry **src,
return error("Cannot do a oneway merge of %d trees",
o->merge_size);
if (!a)
if (!a) {
remove_entry(remove);
return deleted_entry(old, old, o);
}
if (old && same(old, a)) {
if (o->reset) {
struct stat st;

View File

@@ -4,7 +4,8 @@
struct unpack_trees_options;
typedef int (*merge_fn_t)(struct cache_entry **src,
struct unpack_trees_options *options);
struct unpack_trees_options *options,
int remove);
struct unpack_trees_options {
int reset;
@@ -26,12 +27,12 @@ struct unpack_trees_options {
struct cache_entry *df_conflict_entry;
};
extern int unpack_trees(struct object_list *trees,
extern int unpack_trees(unsigned n, struct tree_desc *t,
struct unpack_trees_options *options);
int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o);
int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o);
int bind_merge(struct cache_entry **src, struct unpack_trees_options *o);
int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o);
int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o, int);
int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o, int);
int bind_merge(struct cache_entry **src, struct unpack_trees_options *o, int);
int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o, int);
#endif