diff options
-rw-r--r-- | Documentation/config.txt | 6 | ||||
-rw-r--r-- | Documentation/git-branch.txt | 11 | ||||
-rw-r--r-- | Documentation/git-clone.txt | 24 | ||||
-rw-r--r-- | Documentation/git-diff-tree.txt | 5 | ||||
-rw-r--r-- | Documentation/git-log.txt | 4 | ||||
-rw-r--r-- | Documentation/git-rev-list.txt | 6 | ||||
-rw-r--r-- | Documentation/git-show.txt | 5 | ||||
-rw-r--r-- | Documentation/pretty-formats.txt | 78 | ||||
-rw-r--r-- | Documentation/tutorial.txt | 2 | ||||
-rwxr-xr-x | GIT-VERSION-GEN | 2 | ||||
-rw-r--r-- | archive-zip.c | 24 | ||||
-rw-r--r-- | builtin-apply.c | 6 | ||||
-rw-r--r-- | builtin-archive.c | 2 | ||||
-rw-r--r-- | builtin-branch.c | 103 | ||||
-rw-r--r-- | builtin-log.c | 20 | ||||
-rw-r--r-- | builtin-pack-objects.c | 4 | ||||
-rw-r--r-- | builtin-prune.c | 9 | ||||
-rw-r--r-- | builtin-update-index.c | 6 | ||||
-rw-r--r-- | connect.c | 47 | ||||
-rwxr-xr-x | git-clone.sh | 14 | ||||
-rwxr-xr-x | git-cvsimport.perl | 54 | ||||
-rwxr-xr-x | git-svn.perl | 179 | ||||
-rw-r--r-- | read-cache.c | 6 | ||||
-rwxr-xr-x | t/t3700-add.sh | 14 | ||||
-rwxr-xr-x | t/t4013-diff-various.sh | 1 | ||||
-rw-r--r-- | upload-pack.c | 101 | ||||
-rw-r--r-- | xdiff/xemit.c | 2 |
27 files changed, 625 insertions, 110 deletions
diff --git a/Documentation/config.txt b/Documentation/config.txt index 9d3c71c3b8..9090762819 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -219,6 +219,12 @@ i18n.commitEncoding:: browser (and possibly at other places in the future or in other porcelains). See e.g. gitlink:git-mailinfo[1]. Defaults to 'utf-8'. +log.showroot:: + If true, the initial commit will be shown as a big creation event. + This is equivalent to a diff against an empty tree. + Tools like gitlink:git-log[1] or gitlink:git-whatchanged[1], which + normally hide the root commit will now show it. True by default. + merge.summary:: Whether to include summaries of merged commits in newly created merge commit messages. False by default. 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-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/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/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 ae5572ae20..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; @@ -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 61f047fd45..436d9e1880 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2119,7 +2119,11 @@ static void numstat_patch_list(struct patch *patch) for ( ; patch; patch = patch->next) { const char *name; name = patch->new_name ? patch->new_name : patch->old_name; - printf("%d\t%d\t", patch->lines_added, patch->lines_deleted); + if (patch->is_binary) + printf("-\t-\t"); + else + printf("%d\t%d\t", + patch->lines_added, patch->lines_deleted); if (line_termination && quote_c_style(name, NULL, NULL, 0)) quote_c_style(name, NULL, stdout, 0); else 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-log.c b/builtin-log.c index fedb0137bc..7acf5d3b0c 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -13,6 +13,8 @@ #include <time.h> #include <sys/time.h> +static int default_show_root = 1; + /* this is in builtin-diff.c */ void add_head(struct rev_info *revs); @@ -22,6 +24,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix, rev->abbrev = DEFAULT_ABBREV; rev->commit_format = CMIT_FMT_DEFAULT; rev->verbose_header = 1; + rev->show_root_diff = default_show_root; argc = setup_revisions(argc, argv, rev, "HEAD"); if (rev->diffopt.pickaxe || rev->diffopt.filter) rev->always_show_header = 0; @@ -44,11 +47,20 @@ static int cmd_log_walk(struct rev_info *rev) return 0; } +static int git_log_config(const char *var, const char *value) +{ + if (!strcmp(var, "log.showroot")) { + default_show_root = git_config_bool(var, value); + return 0; + } + return git_diff_ui_config(var, value); +} + int cmd_whatchanged(int argc, const char **argv, const char *prefix) { struct rev_info rev; - git_config(git_diff_ui_config); + git_config(git_log_config); init_revisions(&rev, prefix); rev.diff = 1; rev.diffopt.recursive = 1; @@ -63,7 +75,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) { struct rev_info rev; - git_config(git_diff_ui_config); + git_config(git_log_config); init_revisions(&rev, prefix); rev.diff = 1; rev.diffopt.recursive = 1; @@ -80,7 +92,7 @@ int cmd_log(int argc, const char **argv, const char *prefix) { struct rev_info rev; - git_config(git_diff_ui_config); + git_config(git_log_config); init_revisions(&rev, prefix); rev.always_show_header = 1; cmd_log_init(argc, argv, prefix, &rev); @@ -109,7 +121,7 @@ static int git_format_config(const char *var, const char *value) if (!strcmp(var, "diff.color")) { return 0; } - return git_diff_ui_config(var, value); + return git_log_config(var, value); } diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 69e5dd39ca..753bcd57b0 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1176,7 +1176,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, * on an earlier try, but only when reusing delta data. */ if (!no_reuse_delta && trg_entry->in_pack && - trg_entry->in_pack == src_entry->in_pack) + trg_entry->in_pack == src_entry->in_pack && + trg_entry->in_pack_type != OBJ_REF_DELTA && + trg_entry->in_pack_type != OBJ_OFS_DELTA) return 0; /* 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-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)) @@ -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/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 b54a9486d2..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; 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/read-cache.c b/read-cache.c index 0f5fb5bc33..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)) 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/t4013-diff-various.sh b/t/t4013-diff-various.sh index 71c454356f..ed37141b6e 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -73,6 +73,7 @@ test_expect_success setup ' for i in 1 2; do echo $i; done >>dir/sub && git update-index file0 dir/sub && + git repo-config log.showroot false && git commit --amend && git show-branch ' 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) |