mirror of
https://github.com/git/git.git
synced 2026-03-13 18:33:25 +01:00
Merge branch 'git_git' into next
Conflicts: connect.c git-clone.sh t/t7004-tag.sh
This commit is contained in:
@@ -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) >$@+
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
15
Documentation/fix-texi.perl
Executable 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;
|
||||
}
|
||||
}
|
||||
@@ -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::
|
||||
|
||||
@@ -224,7 +224,7 @@ left-to-right.
|
||||
G H I J
|
||||
\ / \ /
|
||||
D E F
|
||||
\ | / \
|
||||
\ | / \
|
||||
\ | / |
|
||||
\|/ |
|
||||
B C
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
-----------
|
||||
|
||||
@@ -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
14
INSTALL
@@ -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
|
||||
|
||||
9
Makefile
9
Makefile
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
460
builtin-tag.c
Normal 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;
|
||||
}
|
||||
@@ -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
110
builtin-verify-tag.c
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
3
cache.h
3
cache.h
@@ -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);
|
||||
|
||||
5
config.c
5
config.c
@@ -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;
|
||||
}
|
||||
|
||||
17
connect.c
17
connect.c
@@ -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");
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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() */
|
||||
|
||||
64
git-clone.sh
64
git-clone.sh
@@ -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
|
||||
;;
|
||||
*)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
2
git.c
@@ -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 },
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
20
read-cache.c
20
read-cache.c
@@ -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
1
refs.c
@@ -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
52
setup.c
@@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
125
t/t7004-tag.sh
125
t/t7004-tag.sh
@@ -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
69
tree.c
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user