diff options
42 files changed, 826 insertions, 147 deletions
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 8a3d31631c..646b6e7331 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -149,6 +149,9 @@ then you just add a line saying Signed-off-by: Random J Developer <random@developer.example.org> +This line can be automatically added by git if you run the git-commit +command with the -s option. + Some people also put extra tags at the end. They'll just be ignored for now, but you can do this to mark internal company procedures or just point out some special detail about the sign-off. diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt index d9137c7489..2cc32d1c5e 100644 --- a/Documentation/git-apply.txt +++ b/Documentation/git-apply.txt @@ -150,7 +150,7 @@ discouraged. * `strip` outputs warnings for a few such errors, strips out the trailing whitespaces and applies the patch. ---inacurate-eof:: +--inaccurate-eof:: Under certain circumstances, some versions of diff do not correctly detect a missing new-line at the end of the file. As a result, patches created by such diff programs do not record incomplete lines diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index d43ef1dec4..5376760813 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -8,14 +8,16 @@ git-branch - List, create, or delete branches. SYNOPSIS -------- [verse] -'git-branch' [-r] +'git-branch' [-r] [-a] 'git-branch' [-l] [-f] <branchname> [<start-point>] 'git-branch' (-d | -D) <branchname>... DESCRIPTION ----------- -With no arguments given (or just `-r`) a list of available branches +With no arguments given a list of existing branches will be shown, the current branch will be highlighted with an asterisk. +Option `-r` causes the remote-tracking branches to be listed, +and option `-a` shows both. In its second form, a new branch named <branchname> will be created. It will start out with a head equal to the one given as <start-point>. @@ -45,7 +47,10 @@ OPTIONS a branch that already exists with the same name. -r:: - List only the "remote" branches. + List the remote-tracking branches. + +-a:: + List both remote-tracking branches and local branches. <branchname>:: The name of the branch to create or delete. diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 86060472ad..4cb42237b5 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -11,7 +11,8 @@ SYNOPSIS [verse] 'git-clone' [--template=<template_directory>] [-l [-s]] [-q] [-n] [--bare] [-o <name>] [-u <upload-pack>] [--reference <repository>] - [--use-separate-remote] <repository> [<directory>] + [--use-separate-remote | --use-immingled-remote] <repository> + [<directory>] DESCRIPTION ----------- @@ -71,9 +72,13 @@ OPTIONS Make a 'bare' GIT repository. That is, instead of creating `<directory>` and placing the administrative files in `<directory>/.git`, make the `<directory>` - itself the `$GIT_DIR`. This implies `-n` option. When - this option is used, neither the `origin` branch nor the - default `remotes/origin` file is created. + itself the `$GIT_DIR`. This obviously implies the `-n` + because there is nowhere to check out the working tree. + Also the branch heads at the remote are copied directly + to corresponding local branch heads, without mapping + them to `refs/remotes/origin/`. When this option is + used, neither the `origin` branch nor the default + `remotes/origin` file is created. --origin <name>:: -o <name>:: @@ -97,8 +102,15 @@ OPTIONS --use-separate-remote:: Save remotes heads under `$GIT_DIR/remotes/origin/` instead - of `$GIT_DIR/refs/heads/`. Only the master branch is saved - in the latter. + of `$GIT_DIR/refs/heads/`. Only the local master branch is + saved in the latter. This is the default. + +--use-immingled-remote:: + Save remotes heads in the same namespace as the local + heads, `$GIT_DIR/refs/heads/'. In regular repositories, + this is a legacy setup git-clone created by default in + older Git versions, and will be removed before the next + major release. <repository>:: The (possibly remote) repository to clone from. It can diff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt index f7e8ff2968..5d6e9dc751 100644 --- a/Documentation/git-diff-tree.txt +++ b/Documentation/git-diff-tree.txt @@ -73,10 +73,7 @@ separated with a single space are given. This flag causes "git-diff-tree --stdin" to also show the commit message before the differences. ---pretty[=(raw|medium|short)]:: - This is used to control "pretty printing" format of the - commit message. Without "=<style>", it defaults to - medium. +include::pretty-formats.txt[] --no-commit-id:: git-diff-tree outputs a line with the commit ID when diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index c9ffff734c..79643ac928 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -24,8 +24,8 @@ This manual page describes only the most frequently used options. OPTIONS ------- ---pretty=<format>:: - Controls the way the commit log is formatted. + +include::pretty-formats.txt[] --max-count=<n>:: Limits the number of commits to show. diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index 00a95e249f..ec43c0b3a8 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -79,11 +79,7 @@ Using these options, gitlink:git-rev-list[1] will act similar to the more specialized family of commit log tools: gitlink:git-log[1], gitlink:git-show[1], and gitlink:git-whatchanged[1] ---pretty[='<format>']:: - - Pretty print the contents of the commit logs in a given format, - where '<format>' can be one of 'raw', 'medium', 'short', 'full', - and 'oneline'. When left out the format default to 'medium'. +include::pretty-formats.txt[] --relative-date:: diff --git a/Documentation/git-runstatus.txt b/Documentation/git-runstatus.txt new file mode 100644 index 0000000000..89d7b92731 --- /dev/null +++ b/Documentation/git-runstatus.txt @@ -0,0 +1,69 @@ +git-runstatus(1) +================ + +NAME +---- +git-runstatus - A helper for git-status and git-commit + + +SYNOPSIS +-------- +'git-runstatus' [--color|--nocolor] [--amend] [--verbose] [--untracked] + + +DESCRIPTION +----------- +Examines paths in the working tree that has changes unrecorded +to the index file, and changes between the index file and the +current HEAD commit. The former paths are what you _could_ +commit by running 'git-update-index' before running 'git +commit', and the latter paths are what you _would_ commit by +running 'git commit'. + +If there is no path that is different between the index file and +the current HEAD commit, the command exits with non-zero status. + +Note that this is _not_ the user level command you would want to +run from the command line. Use 'git-status' instead. + + +OPTIONS +------- +--color:: + Show colored status, highlighting modified file names. + +--nocolor:: + Turn off coloring. + +--amend:: + Show status based on HEAD^1, not HEAD, i.e. show what + 'git-commit --amend' would do. + +--verbose:: + Show unified diff of all file changes. + +--untracked:: + Show files in untracked directories, too. Without this + option only its name and a trailing slash are displayed + for each untracked directory. + + +OUTPUT +------ +The output from this command is designed to be used as a commit +template comments, and all the output lines are prefixed with '#'. + + +Author +------ +Originally written by Linus Torvalds <torvalds@osdl.org> as part +of git-commit, and later rewritten in C by Jeff King. + +Documentation +-------------- +Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>. + +GIT +--- +Part of the gitlink:git[7] suite + diff --git a/Documentation/git-show.txt b/Documentation/git-show.txt index 2b4df3f96f..4c880a8717 100644 --- a/Documentation/git-show.txt +++ b/Documentation/git-show.txt @@ -26,10 +26,7 @@ OPTIONS <commitid>:: ID of the commit to show. ---pretty=<format>:: - Controls the output format for the commit logs. - <format> can be one of 'raw', 'medium', 'short', 'full', - and 'oneline'. +include::pretty-formats.txt[] Author ------ diff --git a/Documentation/git.txt b/Documentation/git.txt index 52bc05ad50..619d65685e 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -302,6 +302,9 @@ gitlink:git-request-pull[1]:: gitlink:git-rev-parse[1]:: Pick out and massage parameters. +gitlink:git-runstatus[1]:: + A helper for git-status and git-commit. + gitlink:git-send-email[1]:: Send patch e-mails out of "format-patch --mbox" output. diff --git a/Documentation/glossary.txt b/Documentation/glossary.txt index 7e560b0eea..894883d7b6 100644 --- a/Documentation/glossary.txt +++ b/Documentation/glossary.txt @@ -282,6 +282,13 @@ SCM:: SHA1:: Synonym for object name. +symref:: + Symbolic reference: instead of containing the SHA1 id itself, it + is of the format 'ref: refs/some/thing' and when referenced, it + recursively dereferences to this reference. 'HEAD' is a prime + example of a symref. Symbolic references are manipulated with + the gitlink:git-symbolic-ref[1] command. + topic branch:: A regular git branch that is used by a developer to identify a conceptual line of development. Since branches diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt new file mode 100644 index 0000000000..996f628903 --- /dev/null +++ b/Documentation/pretty-formats.txt @@ -0,0 +1,78 @@ +--pretty[='<format>']:: + + Pretty-prints the details of a commit. `--pretty` + without an explicit `=<format>` defaults to 'medium'. + If the commit is a merge, and if the pretty-format + is not 'oneline', 'email' or 'raw', an additional line is + inserted before the 'Author:' line. This line begins with + "Merge: " and the sha1s of ancestral commits are printed, + separated by spaces. Note that the listed commits may not + necessarily be the list of the *direct* parent commits if you + have limited your view of history: for example, if you are + only interested in changes related to a certain directory or + file. Here are some additional details for each format: + + * 'oneline' + + <sha1> <title line> ++ +This is designed to be as compact as possible. + + * 'short' + + commit <sha1> + Author: <author> + + <title line> + + * 'medium' + + commit <sha1> + Author: <author> + Date: <date> + + <title line> + + <full commit message> + + * 'full' + + commit <sha1> + Author: <author> + Commit: <committer> + + <title line> + + <full commit message> + + * 'fuller' + + commit <sha1> + Author: <author> + AuthorDate: <date & time> + Commit: <committer> + CommitDate: <date & time> + + <title line> + + <full commit message> + + + * 'email' + + From <sha1> <date> + From: <author> + Date: <date & time> + Subject: [PATCH] <title line> + + full commit message> + + + * 'raw' ++ +The 'raw' format shows the entire commit exactly as +stored in the commit object. Notably, the SHA1s are +displayed in full, regardless of whether --abbrev or +--no-abbrev are used, and 'parents' information show the +true parent commits, without taking grafts nor history +simplification into account. diff --git a/Documentation/repository-layout.txt b/Documentation/repository-layout.txt index 275d18bb54..e20fb7e74c 100644 --- a/Documentation/repository-layout.txt +++ b/Documentation/repository-layout.txt @@ -52,9 +52,20 @@ objects/info/packs:: by default. objects/info/alternates:: - This file records absolute filesystem paths of alternate - object stores that this object store borrows objects - from, one pathname per line. + This file records paths to alternate object stores that + this object store borrows objects from, one pathname per + line. Note that not only native Git tools use it locally, + but the HTTP fetcher also tries to use it remotely; this + will usually work if you have relative paths (relative + to the object database, not to the repository!) in your + alternates file, but it will not work if you use absolute + paths unless the absolute path in filesystem and web URL + is the same. See also 'objects/info/http-alternates'. + +objects/info/http-alternates:: + This file records URLs to alternate object stores that + this object store borrows objects from, to be used when + the repository is fetched over HTTP. refs:: References are stored in subdirectories of this @@ -70,12 +81,16 @@ refs/tags/`name`:: object, or a tag object that points at a commit object). HEAD:: - A symlink of the form `refs/heads/'name'` to point at - the current branch, if exists. It does not mean much if - the repository is not associated with any working tree + A symref (see glossary) to the `refs/heads/` namespace + describing the currently active branch. It does not mean + much if the repository is not associated with any working tree (i.e. a 'bare' repository), but a valid git repository - *must* have such a symlink here. It is legal if the - named branch 'name' does not (yet) exist. + *must* have the HEAD file; some porcelains may use it to + guess the designated "default" branch of the repository + (usually 'master'). It is legal if the named branch + 'name' does not (yet) exist. In some legacy setups, it is + a symbolic link instead of a symref that points at the current + branch. branches:: A slightly deprecated way to store shorthands to be used diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt index 554ee0af91..1e4ddfbd11 100644 --- a/Documentation/tutorial.txt +++ b/Documentation/tutorial.txt @@ -141,7 +141,7 @@ $ git commit -a ------------------------------------------------ at this point the two branches have diverged, with different changes -made in each. To merge the changes made in the two branches, run +made in each. To merge the changes made in experimental into master, run ------------------------------------------------ $ git pull . experimental diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index eca1ff2175..4eac314f3a 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.4.4.GIT +DEF_VER=v1.4.4.1.GIT LF=' ' diff --git a/archive-zip.c b/archive-zip.c index 28e7352e98..36e922a1f2 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -35,6 +35,7 @@ struct zip_local_header { unsigned char size[4]; unsigned char filename_length[2]; unsigned char extra_length[2]; + unsigned char _end[1]; }; struct zip_dir_header { @@ -55,6 +56,7 @@ struct zip_dir_header { unsigned char attr1[2]; unsigned char attr2[4]; unsigned char offset[4]; + unsigned char _end[1]; }; struct zip_dir_trailer { @@ -66,8 +68,18 @@ struct zip_dir_trailer { unsigned char size[4]; unsigned char offset[4]; unsigned char comment_length[2]; + unsigned char _end[1]; }; +/* + * On ARM, padding is added at the end of the struct, so a simple + * sizeof(struct ...) reports two bytes more than the payload size + * we're interested in. + */ +#define ZIP_LOCAL_HEADER_SIZE offsetof(struct zip_local_header, _end) +#define ZIP_DIR_HEADER_SIZE offsetof(struct zip_dir_header, _end) +#define ZIP_DIR_TRAILER_SIZE offsetof(struct zip_dir_trailer, _end) + static void copy_le16(unsigned char *dest, unsigned int n) { dest[0] = 0xff & n; @@ -160,7 +172,7 @@ static int write_zip_entry(const unsigned char *sha1, void *buffer = NULL; void *deflated = NULL; - crc = crc32(0, Z_NULL, 0); + crc = crc32(0, NULL, 0); path = construct_path(base, baselen, filename, S_ISDIR(mode), &pathlen); if (verbose) @@ -211,7 +223,7 @@ static int write_zip_entry(const unsigned char *sha1, } /* make sure we have enough free space in the dictionary */ - direntsize = sizeof(struct zip_dir_header) + pathlen; + direntsize = ZIP_DIR_HEADER_SIZE + pathlen; while (zip_dir_size < zip_dir_offset + direntsize) { zip_dir_size += ZIP_DIRECTORY_MIN_SIZE; zip_dir = xrealloc(zip_dir, zip_dir_size); @@ -234,8 +246,8 @@ static int write_zip_entry(const unsigned char *sha1, copy_le16(dirent.attr1, 0); copy_le32(dirent.attr2, attr2); copy_le32(dirent.offset, zip_offset); - memcpy(zip_dir + zip_dir_offset, &dirent, sizeof(struct zip_dir_header)); - zip_dir_offset += sizeof(struct zip_dir_header); + memcpy(zip_dir + zip_dir_offset, &dirent, ZIP_DIR_HEADER_SIZE); + zip_dir_offset += ZIP_DIR_HEADER_SIZE; memcpy(zip_dir + zip_dir_offset, path, pathlen); zip_dir_offset += pathlen; zip_dir_entries++; @@ -251,8 +263,8 @@ static int write_zip_entry(const unsigned char *sha1, copy_le32(header.size, uncompressed_size); copy_le16(header.filename_length, pathlen); copy_le16(header.extra_length, 0); - write_or_die(1, &header, sizeof(struct zip_local_header)); - zip_offset += sizeof(struct zip_local_header); + write_or_die(1, &header, ZIP_LOCAL_HEADER_SIZE); + zip_offset += ZIP_LOCAL_HEADER_SIZE; write_or_die(1, path, pathlen); zip_offset += pathlen; if (compressed_size > 0) { @@ -282,7 +294,7 @@ static void write_zip_trailer(const unsigned char *sha1) copy_le16(trailer.comment_length, sha1 ? 40 : 0); write_or_die(1, zip_dir, zip_dir_offset); - write_or_die(1, &trailer, sizeof(struct zip_dir_trailer)); + write_or_die(1, &trailer, ZIP_DIR_TRAILER_SIZE); if (sha1) write_or_die(1, sha1_to_hex(sha1), 40); } diff --git a/builtin-apply.c b/builtin-apply.c index b80ad2ce2e..436d9e1880 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -140,12 +140,15 @@ struct fragment { struct patch { char *new_name, *old_name, *def_name; unsigned int old_mode, new_mode; - int is_rename, is_copy, is_new, is_delete, is_binary; + int is_new, is_delete; /* -1 = unknown, 0 = false, 1 = true */ int rejected; unsigned long deflate_origlen; int lines_added, lines_deleted; int score; - int inaccurate_eof:1; + unsigned int inaccurate_eof:1; + unsigned int is_binary:1; + unsigned int is_copy:1; + unsigned int is_rename:1; struct fragment *fragments; char *result; unsigned long resultsize; diff --git a/builtin-archive.c b/builtin-archive.c index 2df1a84b85..a8a1f079bf 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -249,7 +249,7 @@ int cmd_archive(int argc, const char **argv, const char *prefix) if (remote) return run_remote_archiver(remote, argc, argv); - setlinebuf(stderr); + setvbuf(stderr, NULL, _IOLBF, BUFSIZ); memset(&ar, 0, sizeof(ar)); tree_idx = parse_archive_args(argc, argv, &ar); diff --git a/builtin-branch.c b/builtin-branch.c index 368b68ec91..22e3285a4c 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -11,7 +11,7 @@ #include "builtin.h" static const char builtin_branch_usage[] = -"git-branch (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | [-r]"; +"git-branch (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | [-r] | [-a]"; static const char *head; @@ -79,46 +79,100 @@ static void delete_branches(int argc, const char **argv, int force) } } -static int ref_index, ref_alloc; -static char **ref_list; +#define REF_UNKNOWN_TYPE 0x00 +#define REF_LOCAL_BRANCH 0x01 +#define REF_REMOTE_BRANCH 0x02 +#define REF_TAG 0x04 -static int append_ref(const char *refname, const unsigned char *sha1, int flags, - void *cb_data) +struct ref_item { + char *name; + unsigned int kind; +}; + +struct ref_list { + int index, alloc; + struct ref_item *list; + int kinds; +}; + +static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { - if (ref_index >= ref_alloc) { - ref_alloc = alloc_nr(ref_alloc); - ref_list = xrealloc(ref_list, ref_alloc * sizeof(char *)); + struct ref_list *ref_list = (struct ref_list*)(cb_data); + struct ref_item *newitem; + int kind = REF_UNKNOWN_TYPE; + + /* Detect kind */ + if (!strncmp(refname, "refs/heads/", 11)) { + kind = REF_LOCAL_BRANCH; + refname += 11; + } else if (!strncmp(refname, "refs/remotes/", 13)) { + kind = REF_REMOTE_BRANCH; + refname += 13; + } else if (!strncmp(refname, "refs/tags/", 10)) { + kind = REF_TAG; + refname += 10; + } + + /* Don't add types the caller doesn't want */ + if ((kind & ref_list->kinds) == 0) + return 0; + + /* Resize buffer */ + if (ref_list->index >= ref_list->alloc) { + ref_list->alloc = alloc_nr(ref_list->alloc); + ref_list->list = xrealloc(ref_list->list, + ref_list->alloc * sizeof(struct ref_item)); } - ref_list[ref_index++] = xstrdup(refname); + /* Record the new item */ + newitem = &(ref_list->list[ref_list->index++]); + newitem->name = xstrdup(refname); + newitem->kind = kind; return 0; } +static void free_ref_list(struct ref_list *ref_list) +{ + int i; + + for (i = 0; i < ref_list->index; i++) + free(ref_list->list[i].name); + free(ref_list->list); +} + static int ref_cmp(const void *r1, const void *r2) { - return strcmp(*(char **)r1, *(char **)r2); + struct ref_item *c1 = (struct ref_item *)(r1); + struct ref_item *c2 = (struct ref_item *)(r2); + + if (c1->kind != c2->kind) + return c1->kind - c2->kind; + return strcmp(c1->name, c2->name); } -static void print_ref_list(int remote_only) +static void print_ref_list(int kinds) { int i; char c; + struct ref_list ref_list; - if (remote_only) - for_each_remote_ref(append_ref, NULL); - else - for_each_branch_ref(append_ref, NULL); + memset(&ref_list, 0, sizeof(ref_list)); + ref_list.kinds = kinds; + for_each_ref(append_ref, &ref_list); - qsort(ref_list, ref_index, sizeof(char *), ref_cmp); + qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp); - for (i = 0; i < ref_index; i++) { + for (i = 0; i < ref_list.index; i++) { c = ' '; - if (!strcmp(ref_list[i], head)) + if (ref_list.list[i].kind == REF_LOCAL_BRANCH && + !strcmp(ref_list.list[i].name, head)) c = '*'; - printf("%c %s\n", c, ref_list[i]); + printf("%c %s\n", c, ref_list.list[i].name); } + + free_ref_list(&ref_list); } static void create_branch(const char *name, const char *start, @@ -160,8 +214,9 @@ static void create_branch(const char *name, const char *start, int cmd_branch(int argc, const char **argv, const char *prefix) { - int delete = 0, force_delete = 0, force_create = 0, remote_only = 0; + int delete = 0, force_delete = 0, force_create = 0; int reflog = 0; + int kinds = REF_LOCAL_BRANCH; int i; git_config(git_default_config); @@ -189,7 +244,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "-r")) { - remote_only = 1; + kinds = REF_REMOTE_BRANCH; + continue; + } + if (!strcmp(arg, "-a")) { + kinds = REF_REMOTE_BRANCH | REF_LOCAL_BRANCH; continue; } if (!strcmp(arg, "-l")) { @@ -209,7 +268,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (delete) delete_branches(argc - i, argv + i, force_delete); else if (i == argc) - print_ref_list(remote_only); + print_ref_list(kinds); else if (i == argc - 1) create_branch(argv[i], head, force_create, reflog); else if (i == argc - 2) diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index 3d3097d299..87d3d63ec7 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -278,6 +278,8 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) /* get current branch */ current_branch = resolve_ref("HEAD", head_sha1, 1, NULL); + if (!current_branch) + die("No current branch"); if (!strncmp(current_branch, "refs/heads/", 11)) current_branch += 11; diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index 173bf38735..227aa3cd7f 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -478,9 +478,9 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct obj if (!strcmp(name, "subject")) v->s = copy_line(subpos); else if (!strcmp(name, "body")) - v->s = bodypos; + v->s = xstrdup(bodypos); else if (!strcmp(name, "contents")) - v->s = subpos; + v->s = xstrdup(subpos); } } diff --git a/builtin-prune.c b/builtin-prune.c index d853902c51..8591d28b8e 100644 --- a/builtin-prune.c +++ b/builtin-prune.c @@ -16,8 +16,15 @@ static struct rev_info revs; static int prune_object(char *path, const char *filename, const unsigned char *sha1) { + char buf[20]; + const char *type; + if (show_only) { - printf("would prune %s/%s\n", path, filename); + if (sha1_object_info(sha1, buf, NULL)) + type = "unknown"; + else + type = buf; + printf("%s %s\n", sha1_to_hex(sha1), type); return 0; } unlink(mkpath("%s/%s", path, filename)); diff --git a/builtin-runstatus.c b/builtin-runstatus.c index 303c556da0..0b63037dd0 100644 --- a/builtin-runstatus.c +++ b/builtin-runstatus.c @@ -4,7 +4,7 @@ extern int wt_status_use_color; static const char runstatus_usage[] = -"git-runstatus [--color|--nocolor] [--amend] [--verbose]"; +"git-runstatus [--color|--nocolor] [--amend] [--verbose] [--untracked]"; int cmd_runstatus(int argc, const char **argv, const char *prefix) { diff --git a/builtin-update-index.c b/builtin-update-index.c index 7f9c638466..182331d341 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -112,13 +112,13 @@ static int add_file_to_cache(const char *path) ce->ce_mode = create_ce_mode(st.st_mode); if (!trust_executable_bit) { /* If there is an existing entry, pick the mode bits - * from it, otherwise force to 644. + * from it, otherwise assume unexecutable. */ int pos = cache_name_pos(path, namelen); if (0 <= pos) ce->ce_mode = active_cache[pos]->ce_mode; - else - ce->ce_mode = create_ce_mode(S_IFREG | 0644); + else if (S_ISREG(st.st_mode)) + ce->ce_mode = create_ce_mode(S_IFREG | 0666); } if (index_path(ce->sha1, path, &st, !info_only)) diff --git a/compat/mmap.c b/compat/mmap.c index 55cb120764..a4d2e507f7 100644 --- a/compat/mmap.c +++ b/compat/mmap.c @@ -7,6 +7,7 @@ void *gitfakemmap(void *start, size_t length, int prot , int flags, int fd, off_t offset) { int n = 0; + off_t current_offset = lseek(fd, 0, SEEK_CUR); if (start != NULL || !(flags & MAP_PRIVATE)) die("Invalid usage of gitfakemmap."); @@ -39,6 +40,11 @@ void *gitfakemmap(void *start, size_t length, int prot , int flags, int fd, off_ n += count; } + if (current_offset != lseek(fd, current_offset, SEEK_SET)) { + errno = EINVAL; + return MAP_FAILED; + } + return start; } @@ -174,21 +174,58 @@ static int count_refspec_match(const char *pattern, struct ref *refs, struct ref **matched_ref) { - int match; int patlen = strlen(pattern); + struct ref *matched_weak = NULL; + struct ref *matched = NULL; + int weak_match = 0; + int match = 0; - for (match = 0; refs; refs = refs->next) { + for (weak_match = match = 0; refs; refs = refs->next) { char *name = refs->name; int namelen = strlen(name); + int weak_match; + if (namelen < patlen || memcmp(name + namelen - patlen, pattern, patlen)) continue; if (namelen != patlen && name[namelen - patlen - 1] != '/') continue; - match++; - *matched_ref = refs; + + /* A match is "weak" if it is with refs outside + * heads or tags, and did not specify the pattern + * in full (e.g. "refs/remotes/origin/master") or at + * least from the toplevel (e.g. "remotes/origin/master"); + * otherwise "git push $URL master" would result in + * ambiguity between remotes/origin/master and heads/master + * at the remote site. + */ + if (namelen != patlen && + patlen != namelen - 5 && + strncmp(name, "refs/heads/", 11) && + strncmp(name, "refs/tags/", 10)) { + /* We want to catch the case where only weak + * matches are found and there are multiple + * matches, and where more than one strong + * matches are found, as ambiguous. One + * strong match with zero or more weak matches + * are acceptable as a unique match. + */ + matched_weak = refs; + weak_match++; + } + else { + matched = refs; + match++; + } + } + if (!matched) { + *matched_ref = matched_weak; + return weak_match; + } + else { + *matched_ref = matched; + return match; } - return match; } static void link_dst_tail(struct ref *ref, struct ref ***tail) diff --git a/convert-objects.c b/convert-objects.c index 631678b08a..881258311a 100644 --- a/convert-objects.c +++ b/convert-objects.c @@ -1,4 +1,4 @@ -#define _XOPEN_SOURCE 500 /* glibc2 and AIX 5.3L need this */ +#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */ #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */ #define _GNU_SOURCE #include <time.h> diff --git a/git-checkout.sh b/git-checkout.sh index 119bca1ffb..737abd0c09 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -77,6 +77,11 @@ while [ "$#" != "0" ]; do esac done +case "$force$merge" in +11) + die "git checkout: -f and -m are incompatible" +esac + # The behaviour of the command with and without explicit path # parameters is quite different. # @@ -107,7 +112,11 @@ Did you intend to checkout '$@' which can not be resolved as commit?" git-ls-tree --full-name -r "$new" "$@" | git-update-index --index-info || exit $? fi - git-checkout-index -f -u -- "$@" + + # Make sure the request is about existing paths. + git-ls-files --error-unmatch -- "$@" >/dev/null || exit + git-ls-files -- "$@" | + git-checkout-index -f -u --stdin exit $? else # Make sure we did not fall back on $arg^{tree} codepath diff --git a/git-clone.sh b/git-clone.sh index 3f006d1a77..9ed4135544 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -14,7 +14,7 @@ die() { } usage() { - die "Usage: $0 [--template=<template_directory>] [--use-separate-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]" + die "Usage: $0 [--template=<template_directory>] [--use-immingled-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]" } get_repo_base() { @@ -115,7 +115,7 @@ bare= reference= origin= origin_override= -use_separate_remote= +use_separate_remote=t while case "$#,$1" in 0,*) break ;; @@ -134,7 +134,10 @@ while template="$1" ;; *,-q|*,--quiet) quiet=-q ;; *,--use-separate-remote) + # default use_separate_remote=t ;; + *,--use-immingled-remote) + use_separate_remote= ;; 1,--reference) usage ;; *,--reference) shift; reference="$1" ;; @@ -169,18 +172,15 @@ repo="$1" test -n "$repo" || die 'you must specify a repository to clone.' -# --bare implies --no-checkout +# --bare implies --no-checkout and --use-immingled-remote if test yes = "$bare" then if test yes = "$origin_override" then die '--bare and --origin $origin options are incompatible.' fi - if test t = "$use_separate_remote" - then - die '--bare and --use-separate-remote options are incompatible.' - fi no_checkout=yes + use_separate_remote= fi if test -z "$origin" diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 14e2c6131b..4310dea132 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -161,8 +161,22 @@ sub new { sub conn { my $self = shift; my $repo = $self->{'fullrep'}; - if($repo =~ s/^:pserver:(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?//) { - my($user,$pass,$serv,$port) = ($1,$2,$3,$4); + if($repo =~ s/^:pserver(?:([^:]*)):(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?//) { + my($param,$user,$pass,$serv,$port) = ($1,$2,$3,$4,$5); + + my($proxyhost,$proxyport); + if($param && ($param =~ m/proxy=([^;]+)/)) { + $proxyhost = $1; + # Default proxyport, if not specified, is 8080. + $proxyport = 8080; + if($ENV{"CVS_PROXY_PORT"}) { + $proxyport = $ENV{"CVS_PROXY_PORT"}; + } + if($param =~ m/proxyport=([^;]+)/){ + $proxyport = $1; + } + } + $user="anonymous" unless defined $user; my $rr2 = "-"; unless($port) { @@ -187,13 +201,43 @@ sub conn { } $pass="A" unless $pass; - my $s = IO::Socket::INET->new(PeerHost => $serv, PeerPort => $port); - die "Socket to $serv: $!\n" unless defined $s; + my ($s, $rep); + if($proxyhost) { + + # Use a HTTP Proxy. Only works for HTTP proxies that + # don't require user authentication + # + # See: http://www.ietf.org/rfc/rfc2817.txt + + $s = IO::Socket::INET->new(PeerHost => $proxyhost, PeerPort => $proxyport); + die "Socket to $proxyhost: $!\n" unless defined $s; + $s->write("CONNECT $serv:$port HTTP/1.1\r\nHost: $serv:$port\r\n\r\n") + or die "Write to $proxyhost: $!\n"; + $s->flush(); + + $rep = <$s>; + + # The answer should look like 'HTTP/1.x 2yy ....' + if(!($rep =~ m#^HTTP/1\.. 2[0-9][0-9]#)) { + die "Proxy connect: $rep\n"; + } + # Skip up to the empty line of the proxy server output + # including the response headers. + while ($rep = <$s>) { + last if (!defined $rep || + $rep eq "\n" || + $rep eq "\r\n"); + } + } else { + $s = IO::Socket::INET->new(PeerHost => $serv, PeerPort => $port); + die "Socket to $serv: $!\n" unless defined $s; + } + $s->write("BEGIN AUTH REQUEST\n$repo\n$user\n$pass\nEND AUTH REQUEST\n") or die "Write to $serv: $!\n"; $s->flush(); - my $rep = <$s>; + $rep = <$s>; if($rep ne "I LOVE YOU\n") { $rep="<unknown>" unless $rep; @@ -876,6 +920,16 @@ while(<CVS>) { } commit() if $branch and $state != 11; +# The heuristic of repacking every 1024 commits can leave a +# lot of unpacked data. If there is more than 1MB worth of +# not-packed objects, repack once more. +my $line = `git-count-objects`; +if ($line =~ /^(\d+) objects, (\d+) kilobytes$/) { + my ($n_objects, $kb) = ($1, $2); + 1024 < $kb + and system("git repack -a -d"); +} + foreach my $git_index (values %index) { if ($git_index ne '.git/index') { unlink($git_index); diff --git a/git-fetch.sh b/git-fetch.sh index 7442dd2ca5..eb32476bbd 100755 --- a/git-fetch.sh +++ b/git-fetch.sh @@ -432,10 +432,11 @@ case "$no_tags$tags" in # using local tracking branch. taglist=$(IFS=" " && git-ls-remote $upload_pack --tags "$remote" | - sed -ne 's|^\([0-9a-f]*\)[ ]\(refs/tags/.*\)^{}$|\1 \2|p' | + sed -n -e 's|^\('"$_x40"'\) \(refs/tags/.*\)^{}$|\1 \2|p' \ + -e 's|^\('"$_x40"'\) \(refs/tags/.*\)$|\1 \2|p' | while read sha1 name do - git-show-ref --verify --quiet -- $name && continue + git-show-ref --verify --quiet -- "$name" && continue git-check-ref-format "$name" || { echo >&2 "warning: tag ${name} ignored" continue diff --git a/git-pull.sh b/git-pull.sh index ed04e7d8d8..e23beb685d 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -44,10 +44,10 @@ do shift done -orig_head=$(git-rev-parse --verify HEAD) || die "Pulling into a black hole?" +orig_head=$(git-rev-parse --verify HEAD 2>/dev/null) git-fetch --update-head-ok --reflog-action=pull "$@" || exit 1 -curr_head=$(git-rev-parse --verify HEAD) +curr_head=$(git-rev-parse --verify HEAD 2>/dev/null) if test "$curr_head" != "$orig_head" then # The fetch involved updating the current branch. @@ -80,6 +80,11 @@ case "$merge_head" in exit 0 ;; ?*' '?*) + if test -z "$orig_head" + then + echo >&2 "Cannot merge multiple branches into empty head" + exit 1 + fi var=`git-repo-config --get pull.octopus` if test -n "$var" then @@ -95,6 +100,13 @@ case "$merge_head" in ;; esac +if test -z "$orig_head" +then + git-update-ref -m "initial pull" HEAD $merge_head "" && + git-read-tree --reset -u HEAD || exit 1 + exit +fi + case "$strategy_args" in '') strategy_args=$strategy_default_args diff --git a/git-svn.perl b/git-svn.perl index 80b7b87f0f..47cd3e27fe 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -39,7 +39,7 @@ memoize('revisions_eq'); memoize('cmt_metadata'); memoize('get_commit_time'); -my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib); +my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib, $AUTH_BATON, $AUTH_CALLBACKS); sub nag_lib { print STDERR <<EOF; @@ -66,7 +66,8 @@ my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit, $_template, $_shared, $_no_default_regex, $_no_graft_copy, $_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit, $_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m, - $_merge, $_strategy, $_dry_run, $_ignore_nodate, $_non_recursive); + $_merge, $_strategy, $_dry_run, $_ignore_nodate, $_non_recursive, + $_username, $_config_dir, $_no_auth_cache); my (@_branch_from, %tree_map, %users, %rusers, %equiv); my ($_svn_co_url_revs, $_svn_pg_peg_revs); my @repo_path_split_cache; @@ -79,6 +80,9 @@ my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext, 'repack:i' => \$_repack, 'no-metadata' => \$_no_metadata, 'quiet|q' => \$_q, + 'username=s' => \$_username, + 'config-dir=s' => \$_config_dir, + 'no-auth-cache' => \$_no_auth_cache, 'ignore-nodate' => \$_ignore_nodate, 'repack-flags|repack-args|repack-opts=s' => \$_repack_flags); @@ -232,7 +236,7 @@ sub rebuild { my @commit = grep(/^git-svn-id: /,`git-cat-file commit $c`); next if (!@commit); # skip merges my ($url, $rev, $uuid) = extract_metadata($commit[$#commit]); - if (!$rev || !$uuid) { + if (!defined $rev || !$uuid) { croak "Unable to extract revision or UUID from ", "$c, $commit[$#commit]\n"; } @@ -589,6 +593,13 @@ sub dcommit { chomp(my @refs = safe_qx(qw/git-rev-list --no-merges/, "$gs..HEAD")); my $last_rev; foreach my $d (reverse @refs) { + if (quiet_run('git-rev-parse','--verify',"$d~1") != 0) { + die "Commit $d\n", + "has no parent commit, and therefore ", + "nothing to diff against.\n", + "You should be working from a repository ", + "originally created by git-svn\n"; + } unless (defined $last_rev) { (undef, $last_rev, undef) = cmt_metadata("$d~1"); unless (defined $last_rev) { @@ -616,7 +627,7 @@ sub dcommit { } else { print "No changes between current HEAD and $gs\n", "Hard resetting to the latest $gs\n"; - @finish = qw/reset --hard/; + @finish = qw/reset --mixed/; } sys('git', @finish, $gs); } @@ -825,8 +836,14 @@ sub commit_diff { print STDERR "Needed URL or usable git-svn id command-line\n"; commit_diff_usage(); } - my $r = shift || $_revision; - die "-r|--revision is a required argument\n" unless (defined $r); + my $r = shift; + unless (defined $r) { + if (defined $_revision) { + $r = $_revision + } else { + die "-r|--revision is a required argument\n"; + } + } if (defined $_message && defined $_file) { print STDERR "Both --message/-m and --file/-F specified ", "for the commit message.\n", @@ -2486,7 +2503,7 @@ sub extract_metadata { my $id = shift or return (undef, undef, undef); my ($url, $rev, $uuid) = ($id =~ /^git-svn-id:\s(\S+?)\@(\d+) \s([a-f\d\-]+)$/x); - if (!$rev || !$uuid || !$url) { + if (!defined $rev || !$uuid || !$url) { # some of the original repositories I made had # identifiers like this: ($rev, $uuid) = ($id =~/^git-svn-id:\s(\d+)\@([a-f\d\-]+)/); @@ -2670,18 +2687,154 @@ sub libsvn_load { my $kill_stupid_warnings = $SVN::Node::none.$SVN::Node::file. $SVN::Node::dir.$SVN::Node::unknown. $SVN::Node::none.$SVN::Node::file. - $SVN::Node::dir.$SVN::Node::unknown; + $SVN::Node::dir.$SVN::Node::unknown. + $SVN::Auth::SSL::CNMISMATCH. + $SVN::Auth::SSL::NOTYETVALID. + $SVN::Auth::SSL::EXPIRED. + $SVN::Auth::SSL::UNKNOWNCA. + $SVN::Auth::SSL::OTHER; 1; }; } +sub _simple_prompt { + my ($cred, $realm, $default_username, $may_save, $pool) = @_; + $may_save = undef if $_no_auth_cache; + $default_username = $_username if defined $_username; + if (defined $default_username && length $default_username) { + if (defined $realm && length $realm) { + print "Authentication realm: $realm\n"; + } + $cred->username($default_username); + } else { + _username_prompt($cred, $realm, $may_save, $pool); + } + $cred->password(_read_password("Password for '" . + $cred->username . "': ", $realm)); + $cred->may_save($may_save); + $SVN::_Core::SVN_NO_ERROR; +} + +sub _ssl_server_trust_prompt { + my ($cred, $realm, $failures, $cert_info, $may_save, $pool) = @_; + $may_save = undef if $_no_auth_cache; + print "Error validating server certificate for '$realm':\n"; + if ($failures & $SVN::Auth::SSL::UNKNOWNCA) { + print " - The certificate is not issued by a trusted ", + "authority. Use the\n", + " fingerprint to validate the certificate manually!\n"; + } + if ($failures & $SVN::Auth::SSL::CNMISMATCH) { + print " - The certificate hostname does not match.\n"; + } + if ($failures & $SVN::Auth::SSL::NOTYETVALID) { + print " - The certificate is not yet valid.\n"; + } + if ($failures & $SVN::Auth::SSL::EXPIRED) { + print " - The certificate has expired.\n"; + } + if ($failures & $SVN::Auth::SSL::OTHER) { + print " - The certificate has an unknown error.\n"; + } + printf( "Certificate information:\n". + " - Hostname: %s\n". + " - Valid: from %s until %s\n". + " - Issuer: %s\n". + " - Fingerprint: %s\n", + map $cert_info->$_, qw(hostname valid_from valid_until + issuer_dname fingerprint) ); + my $choice; +prompt: + print $may_save ? + "(R)eject, accept (t)emporarily or accept (p)ermanently? " : + "(R)eject or accept (t)emporarily? "; + $choice = lc(substr(<STDIN> || 'R', 0, 1)); + if ($choice =~ /^t$/i) { + $cred->may_save(undef); + } elsif ($choice =~ /^r$/i) { + return -1; + } elsif ($may_save && $choice =~ /^p$/i) { + $cred->may_save($may_save); + } else { + goto prompt; + } + $cred->accepted_failures($failures); + $SVN::_Core::SVN_NO_ERROR; +} + +sub _ssl_client_cert_prompt { + my ($cred, $realm, $may_save, $pool) = @_; + $may_save = undef if $_no_auth_cache; + print "Client certificate filename: "; + chomp(my $filename = <STDIN>); + $cred->cert_file($filename); + $cred->may_save($may_save); + $SVN::_Core::SVN_NO_ERROR; +} + +sub _ssl_client_cert_pw_prompt { + my ($cred, $realm, $may_save, $pool) = @_; + $may_save = undef if $_no_auth_cache; + $cred->password(_read_password("Password: ", $realm)); + $cred->may_save($may_save); + $SVN::_Core::SVN_NO_ERROR; +} + +sub _username_prompt { + my ($cred, $realm, $may_save, $pool) = @_; + $may_save = undef if $_no_auth_cache; + if (defined $realm && length $realm) { + print "Authentication realm: $realm\n"; + } + my $username; + if (defined $_username) { + $username = $_username; + } else { + print "Username: "; + chomp($username = <STDIN>); + } + $cred->username($username); + $cred->may_save($may_save); + $SVN::_Core::SVN_NO_ERROR; +} + +sub _read_password { + my ($prompt, $realm) = @_; + print $prompt; + require Term::ReadKey; + Term::ReadKey::ReadMode('noecho'); + my $password = ''; + while (defined(my $key = Term::ReadKey::ReadKey(0))) { + last if $key =~ /[\012\015]/; # \n\r + $password .= $key; + } + Term::ReadKey::ReadMode('restore'); + print "\n"; + $password; +} + sub libsvn_connect { my ($url) = @_; - my $auth = SVN::Core::auth_open([SVN::Client::get_simple_provider(), - SVN::Client::get_ssl_server_trust_file_provider(), - SVN::Client::get_username_provider()]); - my $s = eval { SVN::Ra->new(url => $url, auth => $auth) }; - return $s; + if (!$AUTH_BATON || !$AUTH_CALLBACKS) { + SVN::_Core::svn_config_ensure($_config_dir, undef); + ($AUTH_BATON, $AUTH_CALLBACKS) = SVN::Core::auth_open_helper([ + SVN::Client::get_simple_provider(), + SVN::Client::get_ssl_server_trust_file_provider(), + SVN::Client::get_simple_prompt_provider( + \&_simple_prompt, 2), + SVN::Client::get_ssl_client_cert_prompt_provider( + \&_ssl_client_cert_prompt, 2), + SVN::Client::get_ssl_client_cert_pw_prompt_provider( + \&_ssl_client_cert_pw_prompt, 2), + SVN::Client::get_username_provider(), + SVN::Client::get_ssl_server_trust_prompt_provider( + \&_ssl_server_trust_prompt), + SVN::Client::get_username_prompt_provider( + \&_username_prompt, 2), + ]); + } + SVN::Ra->new(url => $url, auth => $AUTH_BATON, + auth_provider_callbacks => $AUTH_CALLBACKS); } sub libsvn_get_file { diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index e54a29e4f6..758759576c 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2454,7 +2454,7 @@ sub git_project_list_body { $pr->{'age_string'} . "</td>\n" . "<td class=\"link\">" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary") . " | " . - $cgi->a({-href => '/git-browser/by-commit.html?r='.$pr->{'path'}}, "graphiclog") . " | " . + $cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") . " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"tree")}, "tree") . ($pr->{'forks'} ? " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "forks") : '') . diff --git a/index-pack.c b/index-pack.c index 042aea8842..8331d99a62 100644 --- a/index-pack.c +++ b/index-pack.c @@ -90,7 +90,7 @@ static SHA_CTX input_ctx; static int input_fd, output_fd, mmap_fd; /* Discard current buffer used content. */ -static void flush() +static void flush(void) { if (input_offset) { if (output_fd >= 0) diff --git a/read-cache.c b/read-cache.c index 97c38670b4..eae4745d28 100644 --- a/read-cache.c +++ b/read-cache.c @@ -347,13 +347,13 @@ int add_file_to_index(const char *path, int verbose) ce->ce_mode = create_ce_mode(st.st_mode); if (!trust_executable_bit) { /* If there is an existing entry, pick the mode bits - * from it, otherwise force to 644. + * from it, otherwise assume unexecutable. */ int pos = cache_name_pos(path, namelen); if (pos >= 0) ce->ce_mode = active_cache[pos]->ce_mode; - else - ce->ce_mode = create_ce_mode(S_IFREG | 0644); + else if (S_ISREG(st.st_mode)) + ce->ce_mode = create_ce_mode(S_IFREG | 0666); } if (index_path(ce->sha1, path, &st, 1)) @@ -844,7 +844,7 @@ unmap: die("index file corrupt"); } -int discard_cache() +int discard_cache(void) { int ret; @@ -322,6 +322,20 @@ int read_ref(const char *ref, unsigned char *sha1) return -1; } +static int do_one_ref(const char *base, each_ref_fn fn, int trim, + void *cb_data, struct ref_list *entry) +{ + if (strncmp(base, entry->name, trim)) + return 0; + if (is_null_sha1(entry->sha1)) + return 0; + if (!has_sha1_file(entry->sha1)) { + error("%s does not point to a valid object!", entry->name); + return 0; + } + return fn(entry->name + trim, entry->sha1, entry->flag, cb_data); +} + static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, void *cb_data) { @@ -343,29 +357,15 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, entry = packed; packed = packed->next; } - if (strncmp(base, entry->name, trim)) - continue; - if (is_null_sha1(entry->sha1)) - continue; - if (!has_sha1_file(entry->sha1)) { - error("%s does not point to a valid object!", entry->name); - continue; - } - retval = fn(entry->name + trim, entry->sha1, - entry->flag, cb_data); + retval = do_one_ref(base, fn, trim, cb_data, entry); if (retval) return retval; } - packed = packed ? packed : loose; - while (packed) { - if (!strncmp(base, packed->name, trim)) { - retval = fn(packed->name + trim, packed->sha1, - packed->flag, cb_data); - if (retval) - return retval; - } - packed = packed->next; + for (packed = packed ? packed : loose; packed; packed = packed->next) { + retval = do_one_ref(base, fn, trim, cb_data, packed); + if (retval) + return retval; } return 0; } diff --git a/t/t3700-add.sh b/t/t3700-add.sh index c20e4c29fc..c09c53f20b 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -27,7 +27,7 @@ test_expect_success \ git-add xfoo1 && case "`git-ls-files --stage xfoo1`" in 100644" "*xfoo1) echo ok;; - *) echo fail; git-ls-files --stage xfoo1; exit 1;; + *) echo fail; git-ls-files --stage xfoo1; (exit 1);; esac' test_expect_success \ @@ -38,7 +38,17 @@ test_expect_success \ git-update-index --add xfoo2 && case "`git-ls-files --stage xfoo2`" in 100644" "*xfoo2) echo ok;; - *) echo fail; git-ls-files --stage xfoo2; exit 1;; + *) echo fail; git-ls-files --stage xfoo2; (exit 1);; + esac' + +test_expect_success \ + 'git-update-index --add: Test that executable bit is not used...' \ + 'git repo-config core.filemode 0 && + ln -s xfoo2 xfoo3 && + git-update-index --add xfoo3 && + case "`git-ls-files --stage xfoo3`" in + 120000" "*xfoo3) echo ok;; + *) echo fail; git-ls-files --stage xfoo3; (exit 1);; esac' test_done diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index df0ae4811b..a11ab0ad41 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -66,4 +66,20 @@ test_expect_success "fetch test for-merge" ' cut -f -2 .git/FETCH_HEAD >actual && diff expected actual' +test_expect_success 'fetch following tags' ' + + cd "$D" && + git tag -a -m 'annotated' anno HEAD && + git tag light HEAD && + + mkdir four && + cd four && + git init-db && + + git fetch .. :track && + git show-ref --verify refs/tags/anno && + git show-ref --verify refs/tags/light + +' + test_done diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh new file mode 100755 index 0000000000..f841574573 --- /dev/null +++ b/t/t5520-pull.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +test_description='pulling into void' + +. ./test-lib.sh + +D=`pwd` + +test_expect_success setup ' + + echo file >file && + git add file && + git commit -a -m original + +' + +test_expect_success 'pulling into void' ' + mkdir cloned && + cd cloned && + git init-db && + git pull .. +' + +cd "$D" + +test_expect_success 'checking the results' ' + test -f file && + test -f cloned/file && + diff file cloned/file +' + +test_done + diff --git a/upload-pack.c b/upload-pack.c index ddaa72f0a9..4572fff07c 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -12,9 +12,15 @@ static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>"; -#define THEY_HAVE (1U << 0) -#define OUR_REF (1U << 1) -#define WANTED (1U << 2) +/* bits #0..7 in revision.h, #8..10 in commit.c */ +#define THEY_HAVE (1u << 11) +#define OUR_REF (1u << 12) +#define WANTED (1u << 13) +#define COMMON_KNOWN (1u << 14) +#define REACHABLE (1u << 15) + +static unsigned long oldest_have; + static int multi_ack, nr_our_refs; static int use_thin_pack, use_ofs_delta; static struct object_array have_obj; @@ -303,11 +309,12 @@ static void create_pack_file(void) static int got_sha1(char *hex, unsigned char *sha1) { struct object *o; + int we_knew_they_have = 0; if (get_sha1_hex(hex, sha1)) die("git-upload-pack: expected SHA1 object, got '%s'", hex); if (!has_sha1_file(sha1)) - return 0; + return -1; o = lookup_object(sha1); if (!(o && o->parsed)) @@ -316,15 +323,84 @@ static int got_sha1(char *hex, unsigned char *sha1) die("oops (%s)", sha1_to_hex(sha1)); if (o->type == OBJ_COMMIT) { struct commit_list *parents; + struct commit *commit = (struct commit *)o; if (o->flags & THEY_HAVE) - return 0; - o->flags |= THEY_HAVE; - for (parents = ((struct commit*)o)->parents; + we_knew_they_have = 1; + else + o->flags |= THEY_HAVE; + if (!oldest_have || (commit->date < oldest_have)) + oldest_have = commit->date; + for (parents = commit->parents; parents; parents = parents->next) parents->item->object.flags |= THEY_HAVE; } - add_object_array(o, NULL, &have_obj); + if (!we_knew_they_have) { + add_object_array(o, NULL, &have_obj); + return 1; + } + return 0; +} + +static int reachable(struct commit *want) +{ + struct commit_list *work = NULL; + + insert_by_date(want, &work); + while (work) { + struct commit_list *list = work->next; + struct commit *commit = work->item; + free(work); + work = list; + + if (commit->object.flags & THEY_HAVE) { + want->object.flags |= COMMON_KNOWN; + break; + } + if (!commit->object.parsed) + parse_object(commit->object.sha1); + if (commit->object.flags & REACHABLE) + continue; + commit->object.flags |= REACHABLE; + if (commit->date < oldest_have) + continue; + for (list = commit->parents; list; list = list->next) { + struct commit *parent = list->item; + if (!(parent->object.flags & REACHABLE)) + insert_by_date(parent, &work); + } + } + want->object.flags |= REACHABLE; + clear_commit_marks(want, REACHABLE); + free_commit_list(work); + return (want->object.flags & COMMON_KNOWN); +} + +static int ok_to_give_up(void) +{ + int i; + + if (!have_obj.nr) + return 0; + + for (i = 0; i < want_obj.nr; i++) { + struct object *want = want_obj.objects[i].item; + + if (want->flags & COMMON_KNOWN) + continue; + want = deref_tag(want, "a want line", 0); + if (!want || want->type != OBJ_COMMIT) { + /* no way to tell if this is reachable by + * looking at the ancestry chain alone, so + * leave a note to ourselves not to worry about + * this object anymore. + */ + want_obj.objects[i].item->flags |= COMMON_KNOWN; + continue; + } + if (!reachable((struct commit *)want)) + return 0; + } return 1; } @@ -349,7 +425,13 @@ static int get_common_commits(void) } len = strip(line, len); if (!strncmp(line, "have ", 5)) { - if (got_sha1(line+5, sha1)) { + switch (got_sha1(line+5, sha1)) { + case -1: /* they have what we do not */ + if (multi_ack && ok_to_give_up()) + packet_write(1, "ACK %s continue\n", + sha1_to_hex(sha1)); + break; + default: memcpy(hex, sha1_to_hex(sha1), 41); if (multi_ack) { const char *msg = "ACK %s continue\n"; @@ -358,6 +440,7 @@ static int get_common_commits(void) } else if (have_obj.nr == 1) packet_write(1, "ACK %s\n", hex); + break; } continue; } diff --git a/xdiff/xemit.c b/xdiff/xemit.c index 07995ec33e..e291dc7608 100644 --- a/xdiff/xemit.c +++ b/xdiff/xemit.c @@ -118,7 +118,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, xdemitconf_t const *xecfg) { long s1, s2, e1, e2, lctx; xdchange_t *xch, *xche; - char funcbuf[40]; + char funcbuf[80]; long funclen = 0; if (xecfg->flags & XDL_EMIT_COMMON) |