diff options
70 files changed, 1623 insertions, 333 deletions
diff --git a/Documentation/RelNotes-1.5.6.txt b/Documentation/RelNotes-1.5.6.txt new file mode 100644 index 0000000000..f3256fb82c --- /dev/null +++ b/Documentation/RelNotes-1.5.6.txt @@ -0,0 +1,59 @@ +GIT v1.5.6 Release Notes +======================== + +Updates since v1.5.5 +-------------------- + +(subsystems) + + +(portability) + + +(performance) + +* "git rebase --onto $there $from $branch" used to switch to the tip of + $branch only to immediately reset back to $from, smudging work tree + files unnecessarily. This has been optimized. + +(usability, bells and whistles) + +* "git add -p" (and the "patch" subcommand of "git add -i") can choose to + apply (or not apply) mode changes independently from contents changes. + +* "git bisect help" gives longer and more helpful usage information. + +* "git diff/log --dirstat" output is consistent between binary and textual + changes. + +* "git gc --auto" honors a new pre-aut-gc hook to temporarily disable it. + +* "git log --pretty=tformat:<custom format>" gives a LF after each entry, + instead of giving a LF between each pair of entries which is how + "git log --pretty=format:<custom format>" works. + +* "git send-email" now can send out messages outside a git repository. + +* "git status" can optionally include output from "git submodule + summary". + +* "gitweb" can read from a system-wide configuration file. + +(internal) + +* "git unpack-objects" and "git receive-pack" is now more strict about + detecting breakage in the objects they receive over the wire. + + +Fixes since v1.5.5 +------------------ + +All of the fixes in v1.5.5 maintenance series are included in +this release, unless otherwise noted. + + +-- +exec >/var/tmp/1 +O=v1.5.5-56-g5f0734f +echo O=`git describe refs/heads/master` +git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint diff --git a/Documentation/config.txt b/Documentation/config.txt index 04c01c5fdc..824e416e9f 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -234,7 +234,13 @@ core.worktree:: used in combination with repositories found automatically in a .git directory (i.e. $GIT_DIR is not set). This can be overridden by the GIT_WORK_TREE environment - variable and the '--work-tree' command line option. + variable and the '--work-tree' command line option. It can be + a absolute path or relative path to the directory specified by + --git-dir or GIT_DIR. + Note: If --git-dir or GIT_DIR are specified but none of + --work-tree, GIT_WORK_TREE and core.worktree is specified, + the current working directory is regarded as the top directory + of your working tree. core.logAllRefUpdates:: Enable the reflog. Updates to a ref <ref> is logged to the file @@ -261,7 +267,12 @@ core.sharedRepository:: group-writable). When 'all' (or 'world' or 'everybody'), the repository will be readable by all users, additionally to being group-shareable. When 'umask' (or 'false'), git will use permissions - reported by umask(2). See linkgit:git-init[1]. False by default. + reported by umask(2). When '0xxx', where '0xxx' is an octal number, + files in the repository will have this mode value. '0xxx' will override + user's umask value, and thus, users with a safe umask (0077) can use + this option. Examples: '0660' is equivalent to 'group'. '0640' is a + repository that is group-readable but not group-writable. + See linkgit:git-init[1]. False by default. core.warnAmbiguousRefs:: If true, git will warn you if the ref name you passed it is ambiguous @@ -991,6 +1002,12 @@ imap:: The configuration variables in the 'imap' section are described in linkgit:git-imap-send[1]. +receive.fsckObjects:: + If it is set to true, git-receive-pack will check all received + objects. It will abort in the case of a malformed object or a + broken link. The result of an abort are only dangling objects. + Defaults to false. + receive.unpackLimit:: If the number of objects received in a push is below this limit then the objects will be unpacked into loose object diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index 96585ae8d9..698ffde7ce 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -15,6 +15,7 @@ DESCRIPTION The command takes various subcommands, and different options depending on the subcommand: + git bisect help git bisect start [<bad> [<good>...]] [--] [<paths>...] git bisect bad [<rev>] git bisect good [<rev>...] @@ -29,6 +30,12 @@ This command uses 'git-rev-list --bisect' option to help drive the binary search process to find which change introduced a bug, given an old "good" commit object name and a later "bad" commit object name. +Getting help +~~~~~~~~~~~~ + +Use "git bisect" to get a short usage description, and "git bisect +help" or "git bisect -h" to get a long usage description. + Basic bisect commands: start, bad, good ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index e11cddbfc9..a644173e15 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -47,7 +47,7 @@ OPTIONS by linkgit:git-check-ref-format[1]. Some of these checks may restrict the characters allowed in a branch name. ---track:: +-t, --track:: When creating a new branch, set up configuration so that git-pull will automatically retrieve data from the start point, which must be a branch. Use this if you always pull from the same upstream branch diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index fa161718dd..5de5d051b7 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -144,6 +144,8 @@ See also <<FILES>>. "auto". If `stdout-is-tty` is missing, then checks the standard output of the command itself, and exits with status 0 if color is to be used, or exits with status 1 otherwise. + When the color setting for `name` is undefined, the command uses + `color.ui` as fallback. --get-color name default:: diff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt index f16cb98612..4cc26fb744 100644 --- a/Documentation/git-fsck.txt +++ b/Documentation/git-fsck.txt @@ -22,7 +22,8 @@ OPTIONS An object to treat as the head of an unreachability trace. + If no objects are given, git-fsck defaults to using the -index file and all SHA1 references in .git/refs/* as heads. +index file, all SHA1 references in .git/refs/*, and all reflogs (unless +--no-reflogs is given) as heads. --unreachable:: Print out objects that exist but that aren't readable from any diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt index 62914da97b..b17ae8485c 100644 --- a/Documentation/git-init.txt +++ b/Documentation/git-init.txt @@ -31,7 +31,7 @@ structure, some suggested "exclude patterns", and copies of non-executing "hook" files. The suggested patterns and hook files are all modifiable and extensible. ---shared[={false|true|umask|group|all|world|everybody}]:: +--shared[={false|true|umask|group|all|world|everybody|0xxx}]:: Specify that the git repository is to be shared amongst several users. This allows users belonging to the same group to push into that @@ -52,6 +52,12 @@ is given: - 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository readable by all users. + - '0xxx': '0xxx' is an octal number and each file will have mode '0xxx' + Any option except 'umask' can be set using this option. '0xxx' will + override users umask(2) value, and thus, users with a safe umask (0077) + can use this option. '0640' will create a repository which is group-readable + but not writable. '0660' is equivalent to 'group'. + By default, the configuration flag receive.denyNonFastForwards is enabled in shared repositories, so that you cannot force a non fast-forwarding push into it. diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt index f151cff5d9..f92bb8cfa0 100644 --- a/Documentation/git-prune.txt +++ b/Documentation/git-prune.txt @@ -13,6 +13,9 @@ SYNOPSIS DESCRIPTION ----------- +NOTE: In most cases, users should run linkgit:git-gc[1], which calls +git-prune. See the section "NOTES", below. + This runs `git-fsck --unreachable` using all the refs available in `$GIT_DIR/refs`, optionally with additional set of objects specified on the command line, and prunes all @@ -50,6 +53,23 @@ borrows from your repository via its $ git prune $(cd ../another && $(git-rev-parse --all)) ------------ +Notes +----- + +In most cases, users will not need to call git-prune directly, but +should instead call linkgit:git-gc[1], which handles pruning along with +many other housekeeping tasks. + +For a description of which objects are considered for pruning, see +git-fsck's --unreachable option. + +See Also +-------- + +linkgit:git-fsck[1], +linkgit:git-gc[1], +linkgit:git-reflog[1] + Author ------ Written by Linus Torvalds <torvalds@osdl.org> diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 6513c2efe1..110e7ba71f 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -52,6 +52,11 @@ OPTIONS The parameter given must be usable as a single, valid object name. Otherwise barf and abort. +-q, --quiet:: + Only meaningful in `--verify` mode. Do not output an error + message if the first argument is not a valid object name; + instead exit with non-zero status silently. + --sq:: Usually the output is made one line per flag and parameter. This option makes output a single line, diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt index 3ea269aa7a..ea4376a17f 100644 --- a/Documentation/git-status.txt +++ b/Documentation/git-status.txt @@ -52,6 +52,11 @@ If the config variable `status.relativePaths` is set to false, then all paths shown are relative to the repository root, not to the current directory. +If `status.submodulesummary` is set to a non zero number or true (identical +to -1 or an unlimited number), the submodule summary will be enabled and a +summary of commits for modified submodules will be shown (see --summary-limit +option of linkgit:git-submodule[1]). + See Also -------- linkgit:gitignore[5] diff --git a/Documentation/git-unpack-objects.txt b/Documentation/git-unpack-objects.txt index b79be3fd4c..50947c50df 100644 --- a/Documentation/git-unpack-objects.txt +++ b/Documentation/git-unpack-objects.txt @@ -8,7 +8,7 @@ git-unpack-objects - Unpack objects from a packed archive SYNOPSIS -------- -'git-unpack-objects' [-n] [-q] [-r] <pack-file +'git-unpack-objects' [-n] [-q] [-r] [--strict] <pack-file DESCRIPTION @@ -40,6 +40,9 @@ OPTIONS and make the best effort to recover as many objects as possible. +--strict:: + Don't write objects with broken content or links. + Author ------ diff --git a/Documentation/git.txt b/Documentation/git.txt index a070e078e7..6f445b1e3b 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -143,7 +143,8 @@ help ...'. --git-dir=<path>:: Set the path to the repository. This can also be controlled by - setting the GIT_DIR environment variable. + setting the GIT_DIR environment variable. It can be an absolute + path or relative path to current working directory. --work-tree=<path>:: Set the path to the working tree. The value will not be @@ -151,7 +152,12 @@ help ...'. a .git directory (i.e. $GIT_DIR is not set). This can also be controlled by setting the GIT_WORK_TREE environment variable and the core.worktree configuration - variable. + variable. It can be an absolute path or relative path to + the directory specified by --git-dir or GIT_DIR. + Note: If --git-dir or GIT_DIR are specified but none of + --work-tree, GIT_WORK_TREE and core.worktree is specified, + the current working directory is regarded as the top directory + of your working tree. --bare:: Treat the repository as a bare repository. If GIT_DIR diff --git a/Documentation/gitk.txt b/Documentation/gitk.txt index ed3ba83c53..50d12da89f 100644 --- a/Documentation/gitk.txt +++ b/Documentation/gitk.txt @@ -41,6 +41,12 @@ frequently used options. Show all branches. +--merge:: + + After an attempt to merge stops with conflicts, show the commits on + the history between two branches (i.e. the HEAD and the MERGE_HEAD) + that modify the conflicted files. + <revs>:: Limit the revisions to show. This can be either a single revision diff --git a/Documentation/hooks.txt b/Documentation/hooks.txt index 76b8d77460..44fbe58f20 100644 --- a/Documentation/hooks.txt +++ b/Documentation/hooks.txt @@ -276,3 +276,10 @@ probably enable this hook. Both standard output and standard error output are forwarded to `git-send-pack` on the other end, so you can simply `echo` messages for the user. + +pre-auto-gc +----------- + +This hook is invoked by `git-gc --auto`. It takes no parameter, and +exiting with non-zero status from this script causes the `git-gc --auto` +to abort. diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index 0193c3ce58..e8bea3e18e 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -123,3 +123,4 @@ The placeholders are: - '%Creset': reset color - '%m': left, right or boundary mark - '%n': newline +- '%x00': print a byte from a hex code @@ -189,6 +189,7 @@ ETC_GITCONFIG = $(sysconfdir)/gitconfig # default configuration for gitweb GITWEB_CONFIG = gitweb_config.perl +GITWEB_CONFIG_SYSTEM = /etc/gitweb.conf GITWEB_HOME_LINK_STR = projects GITWEB_SITENAME = GITWEB_PROJECTROOT = /pub/git @@ -365,6 +366,7 @@ LIB_H += refs.h LIB_H += remote.h LIB_H += revision.h LIB_H += run-command.h +LIB_H += sha1-lookup.h LIB_H += sideband.h LIB_H += strbuf.h LIB_H += tag.h @@ -445,6 +447,7 @@ LIB_OBJS += run-command.o LIB_OBJS += server-info.o LIB_OBJS += setup.o LIB_OBJS += sha1_file.o +LIB_OBJS += sha1-lookup.o LIB_OBJS += sha1_name.o LIB_OBJS += shallow.o LIB_OBJS += sideband.o @@ -1034,6 +1037,7 @@ gitweb/gitweb.cgi: gitweb/gitweb.perl -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \ -e 's|++GIT_BINDIR++|$(bindir)|g' \ -e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \ + -e 's|++GITWEB_CONFIG_SYSTEM++|$(GITWEB_CONFIG_SYSTEM)|g' \ -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \ -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \ -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \ @@ -1 +1 @@ -Documentation/RelNotes-1.5.5.1.txt
\ No newline at end of file +Documentation/RelNotes-1.5.6.txt
\ No newline at end of file diff --git a/builtin-apply.c b/builtin-apply.c index 30d26e57b3..caa3f2aa0c 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2981,12 +2981,8 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof) static int git_apply_config(const char *var, const char *value) { - if (!strcmp(var, "apply.whitespace")) { - if (!value) - return config_error_nonbool(var); - apply_default_whitespace = xstrdup(value); - return 0; - } + if (!strcmp(var, "apply.whitespace")) + return git_config_string(&apply_default_whitespace, var, value); return git_default_config(var, value); } diff --git a/builtin-checkout.c b/builtin-checkout.c index 7deb504837..14b2fe7760 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -504,7 +504,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) OPT__QUIET(&opts.quiet), OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"), OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"), - OPT_SET_INT( 0 , "track", &opts.track, "track", + OPT_SET_INT('t', "track", &opts.track, "track", BRANCH_TRACK_EXPLICIT), OPT_BOOLEAN('f', NULL, &opts.force, "force"), OPT_BOOLEAN('m', NULL, &opts.merge, "merge"), diff --git a/builtin-commit.c b/builtin-commit.c index 52a05c20bb..256181a68b 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -745,10 +745,8 @@ static int parse_and_validate_options(int argc, const char *argv[], die("No paths with --include/--only does not make sense."); if (argc == 0 && only && amend) only_include_assumed = "Clever... amending the last one with dirty index."; - if (argc > 0 && !also && !only) { + if (argc > 0 && !also && !only) only_include_assumed = "Explicit paths specified without -i nor -o; assuming --only paths..."; - also = 0; - } if (!cleanup_arg || !strcmp(cleanup_arg, "default")) cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE; else if (!strcmp(cleanup_arg, "verbatim")) @@ -810,7 +808,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1) rev.verbose_header = 1; rev.show_root_diff = 1; - rev.commit_format = get_commit_format("format:%h: %s"); + get_commit_format("format:%h: %s", &rev); rev.always_show_header = 0; rev.diffopt.detect_rename = 1; rev.diffopt.rename_limit = 100; diff --git a/builtin-config.c b/builtin-config.c index c34bc8b6a6..8ee01bdecd 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -3,7 +3,7 @@ #include "color.h" static const char git_config_set_usage[] = -"git-config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default] | --get-colorbool name [stdout-is-tty]"; +"git-config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int | --bool-or-int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default] | --get-colorbool name [stdout-is-tty]"; static char *key; static regex_t *key_regexp; @@ -16,7 +16,7 @@ static int seen; static char delim = '='; static char key_delim = ' '; static char term = '\n'; -static enum { T_RAW, T_INT, T_BOOL } type = T_RAW; +static enum { T_RAW, T_INT, T_BOOL, T_BOOL_OR_INT } type = T_RAW; static int show_all_config(const char *key_, const char *value_) { @@ -53,6 +53,14 @@ static int show_config(const char* key_, const char* value_) sprintf(value, "%d", git_config_int(key_, value_?value_:"")); else if (type == T_BOOL) vptr = git_config_bool(key_, value_) ? "true" : "false"; + else if (type == T_BOOL_OR_INT) { + int is_bool, v; + v = git_config_bool_or_int(key_, value_, &is_bool); + if (is_bool) + vptr = v ? "true" : "false"; + else + sprintf(value, "%d", v); + } else vptr = value_?value_:""; seen++; @@ -157,6 +165,14 @@ char *normalize_value(const char *key, const char *value) else if (type == T_BOOL) sprintf(normalized, "%s", git_config_bool(key, value) ? "true" : "false"); + else if (type == T_BOOL_OR_INT) { + int is_bool, v; + v = git_config_bool_or_int(key, value, &is_bool); + if (!is_bool) + sprintf(normalized, "%d", v); + else + sprintf(normalized, "%s", v ? "true" : "false"); + } } return normalized; @@ -224,6 +240,10 @@ static int git_get_colorbool_config(const char *var, const char *value) get_diff_color_found = git_config_colorbool(var, value, stdout_is_tty); } + if (!strcmp(var, "color.ui")) { + git_use_color_default = git_config_colorbool(var, value, stdout_is_tty); + return 0; + } return 0; } @@ -251,7 +271,7 @@ static int get_colorbool(int argc, const char **argv) if (!strcmp(get_color_slot, "color.diff")) get_colorbool_found = get_diff_color_found; if (get_colorbool_found < 0) - get_colorbool_found = 0; + get_colorbool_found = git_use_color_default; } if (argc == 1) { @@ -273,6 +293,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) type = T_INT; else if (!strcmp(argv[1], "--bool")) type = T_BOOL; + else if (!strcmp(argv[1], "--bool-or-int")) + type = T_BOOL_OR_INT; else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) { if (argc != 2) usage(git_config_set_usage); diff --git a/builtin-gc.c b/builtin-gc.c index 8cef36f6a4..f99ebc7926 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -157,6 +157,34 @@ static int too_many_packs(void) return gc_auto_pack_limit <= cnt; } +static int run_hook(void) +{ + const char *argv[2]; + struct child_process hook; + int ret; + + argv[0] = git_path("hooks/pre-auto-gc"); + argv[1] = NULL; + + if (access(argv[0], X_OK) < 0) + return 0; + + memset(&hook, 0, sizeof(hook)); + hook.argv = argv; + hook.no_stdin = 1; + hook.stdout_to_stderr = 1; + + ret = start_command(&hook); + if (ret) { + warning("Could not spawn %s", argv[0]); + return ret; + } + ret = finish_command(&hook); + if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL) + warning("%s exited due to uncaught signal", argv[0]); + return ret; +} + static int need_to_gc(void) { /* @@ -176,6 +204,9 @@ static int need_to_gc(void) append_option(argv_repack, "-A", MAX_ADD); else if (!too_many_loose_objects()) return 0; + + if (run_hook()) + return 0; return 1; } diff --git a/builtin-init-db.c b/builtin-init-db.c index 2854868b4e..a76f5d3474 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -400,9 +400,16 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) char buf[10]; /* We do not spell "group" and such, so that * the configuration can be read by older version - * of git. + * of git. Note, we use octal numbers for new share modes, + * and compatibility values for PERM_GROUP and + * PERM_EVERYBODY. */ - sprintf(buf, "%d", shared_repository); + if (shared_repository == PERM_GROUP) + sprintf(buf, "%d", OLD_PERM_GROUP); + else if (shared_repository == PERM_EVERYBODY) + sprintf(buf, "%d", OLD_PERM_EVERYBODY); + else + sprintf(buf, "0%o", shared_repository); git_config_set("core.sharedrepository", buf); git_config_set("receive.denyNonFastforwards", "true"); } diff --git a/builtin-log.c b/builtin-log.c index 5c00725f03..1670d0b334 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -56,7 +56,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix, rev->abbrev = DEFAULT_ABBREV; rev->commit_format = CMIT_FMT_DEFAULT; if (fmt_pretty) - rev->commit_format = get_commit_format(fmt_pretty); + get_commit_format(fmt_pretty, rev); rev->verbose_header = 1; DIFF_OPT_SET(&rev->diffopt, RECURSIVE); rev->show_root_diff = default_show_root; @@ -400,6 +400,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix) * allow us to set a different default. */ rev.commit_format = CMIT_FMT_ONELINE; + rev.use_terminator = 1; rev.always_show_header = 1; /* diff --git a/builtin-remote.c b/builtin-remote.c index 4149f3b3ce..93bb84e1d4 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -19,6 +19,8 @@ static const char * const builtin_remote_usage[] = { static int verbose; +static int show_all(void); + static inline int postfixcmp(const char *string, const char *postfix) { int len1 = strlen(string), len2 = strlen(postfix); @@ -385,8 +387,11 @@ static int show_or_prune(int argc, const char **argv, int prune) argc = parse_options(argc, argv, options, builtin_remote_usage, 0); - if (argc < 1) + if (argc < 1) { + if (!prune) + return show_all(); usage_with_options(builtin_remote_usage, options); + } memset(&states, 0, sizeof(states)); for (; argc; argc--, argv++) { diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index 0351d54435..0e59707323 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -365,9 +365,17 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) return 0; } +static void die_no_single_rev(int quiet) +{ + if (quiet) + exit(1); + else + die("Needed a single revision"); +} + int cmd_rev_parse(int argc, const char **argv, const char *prefix) { - int i, as_is = 0, verify = 0; + int i, as_is = 0, verify = 0, quiet = 0; unsigned char sha1[20]; if (argc > 1 && !strcmp("--parseopt", argv[1])) @@ -432,6 +440,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) verify = 1; continue; } + if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) { + quiet = 1; + continue; + } if (!strcmp(arg, "--short") || !prefixcmp(arg, "--short=")) { filter &= ~(DO_FLAGS|DO_NOREV); @@ -549,7 +561,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (show_flag(arg) && verify) - die("Needed a single revision"); + die_no_single_rev(quiet); continue; } @@ -564,15 +576,15 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) show_rev(REVERSED, sha1, arg+1); continue; } + if (verify) + die_no_single_rev(quiet); as_is = 1; if (!show_file(arg)) continue; - if (verify) - die("Needed a single revision"); verify_filename(prefix, arg); } show_default(); if (verify && revs_count != 1) - die("Needed a single revision"); + die_no_single_rev(quiet); return 0; } diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c index 50e07faa12..fecf0be779 100644 --- a/builtin-unpack-objects.c +++ b/builtin-unpack-objects.c @@ -7,11 +7,13 @@ #include "commit.h" #include "tag.h" #include "tree.h" +#include "tree-walk.h" #include "progress.h" #include "decorate.h" +#include "fsck.h" -static int dry_run, quiet, recover, has_errors; -static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] < pack-file"; +static int dry_run, quiet, recover, has_errors, strict; +static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] [--strict] < pack-file"; /* We always read in 4kB chunks. */ static unsigned char buffer[4096]; @@ -19,6 +21,11 @@ static unsigned int offset, len; static off_t consumed_bytes; static SHA_CTX ctx; +/* + * When running under --strict mode, objects whose reachability are + * suspect are kept in core without getting written in the object + * store. + */ struct obj_buffer { char *buffer; unsigned long size; @@ -31,6 +38,16 @@ static struct obj_buffer *lookup_object_buffer(struct object *base) return lookup_decoration(&obj_decorate, base); } +static void add_object_buffer(struct object *object, char *buffer, unsigned long size) +{ + struct obj_buffer *obj; + obj = xcalloc(1, sizeof(struct obj_buffer)); + obj->buffer = buffer; + obj->size = size; + if (add_decoration(&obj_decorate, object, obj)) + die("object %s tried to add buffer twice!", sha1_to_hex(object->sha1)); +} + /* * Make sure at least "min" bytes are available in the buffer, and * return the pointer to the buffer. @@ -134,19 +151,110 @@ static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1, struct obj_info { off_t offset; unsigned char sha1[20]; + struct object *obj; }; +#define FLAG_OPEN (1u<<20) +#define FLAG_WRITTEN (1u<<21) + static struct obj_info *obj_list; +unsigned nr_objects; + +/* + * Called only from check_object() after it verified this object + * is Ok. + */ +static void write_cached_object(struct object *obj) +{ + unsigned char sha1[20]; + struct obj_buffer *obj_buf = lookup_object_buffer(obj); + if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), sha1) < 0) + die("failed to write object %s", sha1_to_hex(obj->sha1)); + obj->flags |= FLAG_WRITTEN; +} + +/* + * At the very end of the processing, write_rest() scans the objects + * that have reachability requirements and calls this function. + * Verify its reachability and validity recursively and write it out. + */ +static int check_object(struct object *obj, int type, void *data) +{ + if (!obj) + return 0; + + if (obj->flags & FLAG_WRITTEN) + return 1; + + if (type != OBJ_ANY && obj->type != type) + die("object type mismatch"); + + if (!(obj->flags & FLAG_OPEN)) { + unsigned long size; + int type = sha1_object_info(obj->sha1, &size); + if (type != obj->type || type <= 0) + die("object of unexpected type"); + obj->flags |= FLAG_WRITTEN; + return 1; + } + + if (fsck_object(obj, 1, fsck_error_function)) + die("Error in object"); + if (!fsck_walk(obj, check_object, 0)) + die("Error on reachable objects of %s", sha1_to_hex(obj->sha1)); + write_cached_object(obj); + return 1; +} + +static void write_rest(void) +{ + unsigned i; + for (i = 0; i < nr_objects; i++) + check_object(obj_list[i].obj, OBJ_ANY, 0); +} static void added_object(unsigned nr, enum object_type type, void *data, unsigned long size); +/* + * Write out nr-th object from the list, now we know the contents + * of it. Under --strict, this buffers structured objects in-core, + * to be checked at the end. + */ static void write_object(unsigned nr, enum object_type type, void *buf, unsigned long size) { - if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0) - die("failed to write object"); - added_object(nr, type, buf, size); + if (!strict) { + if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0) + die("failed to write object"); + added_object(nr, type, buf, size); + free(buf); + obj_list[nr].obj = NULL; + } else if (type == OBJ_BLOB) { + struct blob *blob; + if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0) + die("failed to write object"); + added_object(nr, type, buf, size); + free(buf); + + blob = lookup_blob(obj_list[nr].sha1); + if (blob) + blob->object.flags |= FLAG_WRITTEN; + else + die("invalid blob object"); + obj_list[nr].obj = NULL; + } else { + struct object *obj; + int eaten; + hash_sha1_file(buf, size, typename(type), obj_list[nr].sha1); + added_object(nr, type, buf, size); + obj = parse_object_buffer(obj_list[nr].sha1, type, size, buf, &eaten); + if (!obj) + die("invalid %s", typename(type)); + add_object_buffer(obj, buf, size); + obj->flags |= FLAG_OPEN; + obj_list[nr].obj = obj; + } } static void resolve_delta(unsigned nr, enum object_type type, @@ -163,9 +271,12 @@ static void resolve_delta(unsigned nr, enum object_type type, die("failed to apply delta"); free(delta); write_object(nr, type, result, result_size); - free(result); } +/* + * We now know the contents of an object (which is nr-th in the pack); + * resolve all the deltified objects that are based on it. + */ static void added_object(unsigned nr, enum object_type type, void *data, unsigned long size) { @@ -193,7 +304,24 @@ static void unpack_non_delta_entry(enum object_type type, unsigned long size, if (!dry_run && buf) write_object(nr, type, buf, size); - free(buf); + else + free(buf); +} + +static int resolve_against_held(unsigned nr, const unsigned char *base, + void *delta_data, unsigned long delta_size) +{ + struct object *obj; + struct obj_buffer *obj_buffer; + obj = lookup_object(base); + if (!obj) + return 0; + obj_buffer = lookup_object_buffer(obj); + if (!obj_buffer) + return 0; + resolve_delta(nr, obj->type, obj_buffer->buffer, + obj_buffer->size, delta_data, delta_size); + return 1; } static void unpack_delta_entry(enum object_type type, unsigned long delta_size, @@ -202,7 +330,6 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, void *delta_data, *base; unsigned long base_size; unsigned char base_sha1[20]; - struct object *obj; if (type == OBJ_REF_DELTA) { hashcpy(base_sha1, fill(20)); @@ -212,7 +339,13 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, free(delta_data); return; } - if (!has_sha1_file(base_sha1)) { + if (has_sha1_file(base_sha1)) + ; /* Ok we have this one */ + else if (resolve_against_held(nr, base_sha1, + delta_data, delta_size)) + return; /* we are done */ + else { + /* cannot resolve yet --- queue it */ hashcpy(obj_list[nr].sha1, null_sha1); add_delta_to_list(nr, base_sha1, 0, delta_data, delta_size); return; @@ -258,22 +391,18 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, } } if (!base_found) { - /* The delta base object is itself a delta that - has not been resolved yet. */ + /* + * The delta base object is itself a delta that + * has not been resolved yet. + */ hashcpy(obj_list[nr].sha1, null_sha1); add_delta_to_list(nr, null_sha1, base_offset, delta_data, delta_size); return; } } - obj = lookup_object(base_sha1); - if (obj) { - struct obj_buffer *obj_buf = lookup_object_buffer(obj); - if (obj_buf) { - resolve_delta(nr, obj->type, obj_buf->buffer, obj_buf->size, delta_data, delta_size); - return; - } - } + if (resolve_against_held(nr, base_sha1, delta_data, delta_size)) + return; base = read_sha1_file(base_sha1, &type, &base_size); if (!base) { @@ -336,7 +465,8 @@ static void unpack_all(void) int i; struct progress *progress = NULL; struct pack_header *hdr = fill(sizeof(struct pack_header)); - unsigned nr_objects = ntohl(hdr->hdr_entries); + + nr_objects = ntohl(hdr->hdr_entries); if (ntohl(hdr->hdr_signature) != PACK_SIGNATURE) die("bad pack file"); @@ -347,6 +477,7 @@ static void unpack_all(void) if (!quiet) progress = start_progress("Unpacking objects", nr_objects); obj_list = xmalloc(nr_objects * sizeof(*obj_list)); + memset(obj_list, 0, nr_objects * sizeof(*obj_list)); for (i = 0; i < nr_objects; i++) { unpack_one(i); display_progress(progress, i + 1); @@ -382,6 +513,10 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix) recover = 1; continue; } + if (!strcmp(arg, "--strict")) { + strict = 1; + continue; + } if (!prefixcmp(arg, "--pack_header=")) { struct pack_header *hdr; char *c; @@ -407,6 +542,8 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix) unpack_all(); SHA1_Update(&ctx, buffer, offset); SHA1_Final(sha1, &ctx); + if (strict) + write_rest(); if (hashcmp(fill(20), sha1)) die("final sha1 did not match"); use(20); @@ -474,10 +474,20 @@ static inline void hashclr(unsigned char *hash) int git_mkstemp(char *path, size_t n, const char *template); +/* + * NOTE NOTE NOTE!! + * + * PERM_UMASK, OLD_PERM_GROUP and OLD_PERM_EVERYBODY enumerations must + * not be changed. Old repositories have core.sharedrepository written in + * numeric format, and therefore these values are preserved for compatibility + * reasons. + */ enum sharedrepo { - PERM_UMASK = 0, - PERM_GROUP, - PERM_EVERYBODY + PERM_UMASK = 0, + OLD_PERM_GROUP = 1, + OLD_PERM_EVERYBODY = 2, + PERM_GROUP = 0660, + PERM_EVERYBODY = 0664, }; int git_config_perm(const char *var, const char *value); int adjust_shared_perm(const char *path); @@ -692,6 +702,7 @@ extern int git_parse_long(const char *, long *); extern int git_parse_ulong(const char *, unsigned long *); extern int git_config_int(const char *, const char *); extern unsigned long git_config_ulong(const char *, const char *); +extern int git_config_bool_or_int(const char *, const char *, int *); extern int git_config_bool(const char *, const char *); extern int git_config_string(const char **, const char *, const char *); extern int git_config_set(const char *, const char *); @@ -715,8 +726,8 @@ extern const char *git_log_output_encoding; extern void maybe_flush_or_die(FILE *, const char *); extern int copy_fd(int ifd, int ofd); extern int copy_file(const char *dst, const char *src, int mode); -extern int read_in_full(int fd, void *buf, size_t count); -extern int write_in_full(int fd, const void *buf, size_t count); +extern ssize_t read_in_full(int fd, void *buf, size_t count); +extern ssize_t write_in_full(int fd, const void *buf, size_t count); extern void write_or_die(int fd, const void *buf, size_t count); extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg); extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg); diff --git a/combine-diff.c b/combine-diff.c index 0e19cbaacc..f1e7a4d5d9 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -718,9 +718,9 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent, result = xmalloc(len + 1); while (sz < len) { ssize_t done = xread(fd, result+sz, len-sz); - if (done == 0) - break; - if (done < 0) + if (done == 0 && sz != len) + die("early EOF '%s'", elem->path); + else if (done < 0) die("read error '%s'", elem->path); sz += done; } @@ -63,7 +63,8 @@ enum cmit_fmt { }; extern int non_ascii(int); -extern enum cmit_fmt get_commit_format(const char *arg); +struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */ +extern void get_commit_format(const char *arg, struct rev_info *); extern void format_commit_message(const struct commit *commit, const void *format, struct strbuf *sb); extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*, @@ -303,8 +303,9 @@ unsigned long git_config_ulong(const char *name, const char *value) return ret; } -int git_config_bool(const char *name, const char *value) +int git_config_bool_or_int(const char *name, const char *value, int *is_bool) { + *is_bool = 1; if (!value) return 1; if (!*value) @@ -313,7 +314,14 @@ int git_config_bool(const char *name, const char *value) return 1; if (!strcasecmp(value, "false") || !strcasecmp(value, "no")) return 0; - return git_config_int(name, value) != 0; + *is_bool = 0; + return git_config_int(name, value); +} + +int git_config_bool(const char *name, const char *value) +{ + int discard; + return !!git_config_bool_or_int(name, value, &discard); } int git_config_string(const char **dest, const char *var, const char *value) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 6949cac45d..23db664f48 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -152,7 +152,7 @@ __git_heads () done return fi - for i in $(git-ls-remote "$1" 2>/dev/null); do + for i in $(git ls-remote "$1" 2>/dev/null); do case "$is_hash,$i" in y,*) is_hash=n ;; n,*^{}) is_hash=y ;; @@ -173,7 +173,7 @@ __git_tags () done return fi - for i in $(git-ls-remote "$1" 2>/dev/null); do + for i in $(git ls-remote "$1" 2>/dev/null); do case "$is_hash,$i" in y,*) is_hash=n ;; n,*^{}) is_hash=y ;; @@ -200,7 +200,7 @@ __git_refs () done return fi - for i in $(git-ls-remote "$dir" 2>/dev/null); do + for i in $(git ls-remote "$dir" 2>/dev/null); do case "$is_hash,$i" in y,*) is_hash=n ;; n,*^{}) is_hash=y ;; @@ -223,7 +223,7 @@ __git_refs2 () __git_refs_remotes () { local cmd i is_hash=y - for i in $(git-ls-remote "$1" 2>/dev/null); do + for i in $(git ls-remote "$1" 2>/dev/null); do case "$is_hash,$i" in n,refs/heads/*) is_hash=y @@ -1053,6 +1053,7 @@ _git_remote () local subcommands="add rm show prune update" local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then + __gitcomp "$subcommands" return fi @@ -1345,9 +1346,14 @@ _git () _gitk () { local cur="${COMP_WORDS[COMP_CWORD]}" + local g="$(git rev-parse --git-dir 2>/dev/null)" + local merge="" + if [ -f $g/MERGE_HEAD ]; then + merge="--merge" + fi case "$cur" in --*) - __gitcomp "--not --all" + __gitcomp "--not --all $merge" return ;; esac diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 4fa853fae7..2557a7667f 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -232,10 +232,8 @@ and returns the process output as a string, or nil if the git failed." (defun git-run-command-region (buffer start end env &rest args) "Run a git command with specified buffer region as input." - (unless (eq 0 (if env - (git-run-process-region - buffer start end "env" - (append (git-get-env-strings env) (list "git") args)) + (unless (eq 0 (let ((process-environment (append (git-get-env-strings env) + process-environment))) (git-run-process-region buffer start end "git" args))) (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))) @@ -250,9 +248,8 @@ and returns the process output as a string, or nil if the git failed." (erase-buffer) (cd dir) (setq status - (if env - (apply #'call-process "env" nil (list buffer t) nil - (append (git-get-env-strings env) (list hook-name) args)) + (let ((process-environment (append (git-get-env-strings env) + process-environment))) (apply #'call-process hook-name nil (list buffer t) nil args)))) (display-message-or-buffer buffer) (eq 0 status))))) diff --git a/contrib/hooks/pre-auto-gc-battery b/contrib/hooks/pre-auto-gc-battery new file mode 100644 index 0000000000..0096f57b7e --- /dev/null +++ b/contrib/hooks/pre-auto-gc-battery @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to verify if you are on battery, in case you +# are running Linux. Called by git-gc --auto with no arguments. The hook +# should exit with non-zero status after issuing an appropriate message +# if it wants to stop the auto repacking. +# +# This hook is stored in the contrib/hooks directory. Your distribution +# may have put this somewhere else. If you want to use this hook, you +# should make this script executable then link to it in the repository +# you would like to use it in. +# +# For example, if the hook is stored in +# /usr/share/git-core/contrib/hooks/pre-auto-gc-battery: +# +# chmod a+x pre-auto-gc-battery +# cd /path/to/your/repository.git +# ln -sf /usr/share/git-core/contrib/hooks/pre-auto-gc-battery \ +# hooks/pre-auto-gc + +if test -x /sbin/on_ac_power && /sbin/on_ac_power +then + exit 0 +elif test "$(cat /sys/class/power_supply/AC/online 2>/dev/null)" = 1 +then + exit 0 +elif grep -q 'on-line' /proc/acpi/ac_adapter/AC/state 2>/dev/null +then + exit 0 +elif grep -q '0x01$' /proc/apm 2>/dev/null +then + exit 0 +fi + +echo "Auto packing deferred; not on AC" +exit 1 diff --git a/diff-lib.c b/diff-lib.c index 069e4507ae..cfd629da48 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -264,6 +264,9 @@ int setup_diff_no_index(struct rev_info *revs, DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS); break; } + if (nongit && argc != i + 2) + die("git diff [--no-index] takes two paths"); + if (argc != i + 2 || (!is_outside_repo(argv[i + 1], nongit, prefix) && !is_outside_repo(argv[i], nongit, prefix))) return -1; @@ -991,18 +991,23 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options) } } -struct diffstat_dir { - struct diffstat_file **files; - int nr, percent, cumulative; +struct dirstat_file { + const char *name; + unsigned long changed; }; -static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen) +struct dirstat_dir { + struct dirstat_file *files; + int alloc, nr, percent, cumulative; +}; + +static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long changed, const char *base, int baselen) { unsigned long this_dir = 0; unsigned int sources = 0; while (dir->nr) { - struct diffstat_file *f = *dir->files; + struct dirstat_file *f = dir->files; int namelen = strlen(f->name); unsigned long this; char *slash; @@ -1017,10 +1022,7 @@ static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long c this = gather_dirstat(file, dir, changed, f->name, newbaselen); sources++; } else { - if (f->is_unmerged || f->is_binary) - this = 0; - else - this = f->added + f->deleted; + this = f->changed; dir->files++; dir->nr--; sources += 2; @@ -1048,19 +1050,58 @@ static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long c return this_dir; } -static void show_dirstat(struct diffstat_t *data, struct diff_options *options) +static void show_dirstat(struct diff_options *options) { int i; unsigned long changed; - struct diffstat_dir dir; + struct dirstat_dir dir; + struct diff_queue_struct *q = &diff_queued_diff; + + dir.files = NULL; + dir.alloc = 0; + dir.nr = 0; + dir.percent = options->dirstat_percent; + dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE; - /* Calculate total changes */ changed = 0; - for (i = 0; i < data->nr; i++) { - if (data->files[i]->is_binary || data->files[i]->is_unmerged) + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + const char *name; + unsigned long copied, added, damage; + + name = p->one->path ? p->one->path : p->two->path; + + if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) { + diff_populate_filespec(p->one, 0); + diff_populate_filespec(p->two, 0); + diffcore_count_changes(p->one, p->two, NULL, NULL, 0, + &copied, &added); + diff_free_filespec_data(p->one); + diff_free_filespec_data(p->two); + } else if (DIFF_FILE_VALID(p->one)) { + diff_populate_filespec(p->one, 1); + copied = added = 0; + diff_free_filespec_data(p->one); + } else if (DIFF_FILE_VALID(p->two)) { + diff_populate_filespec(p->two, 1); + copied = 0; + added = p->two->size; + diff_free_filespec_data(p->two); + } else continue; - changed += data->files[i]->added; - changed += data->files[i]->deleted; + + /* + * Original minus copied is the removed material, + * added is the new material. They are both damages + * made to the preimage. + */ + damage = (p->one->size - copied) + added; + + ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc); + dir.files[dir.nr].name = name; + dir.files[dir.nr].changed = damage; + changed += damage; + dir.nr++; } /* This can happen even with many files, if everything was renames */ @@ -1068,10 +1109,6 @@ static void show_dirstat(struct diffstat_t *data, struct diff_options *options) return; /* Show all directories with more than x% of the changes */ - dir.files = data->files; - dir.nr = data->nr; - dir.percent = options->dirstat_percent; - dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE; gather_dirstat(options->file, &dir, changed, "", 0); } @@ -3095,7 +3132,7 @@ void diff_flush(struct diff_options *options) separator++; } - if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIRSTAT)) { + if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) { struct diffstat_t diffstat; memset(&diffstat, 0, sizeof(struct diffstat_t)); @@ -3105,8 +3142,6 @@ void diff_flush(struct diff_options *options) if (check_pair_status(p)) diff_flush_stat(p, options, &diffstat); } - if (output_format & DIFF_FORMAT_DIRSTAT) - show_dirstat(&diffstat, options); if (output_format & DIFF_FORMAT_NUMSTAT) show_numstat(&diffstat, options); if (output_format & DIFF_FORMAT_DIFFSTAT) @@ -3116,6 +3151,8 @@ void diff_flush(struct diff_options *options) free_diffstat_info(&diffstat); separator++; } + if (output_format & DIFF_FORMAT_DIRSTAT) + show_dirstat(options); if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) { for (i = 0; i < q->nr; i++) diff --git a/diffcore-rename.c b/diffcore-rename.c index 31941bcbbf..1369a5ec45 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -112,8 +112,8 @@ static int basename_same(struct diff_filespec *src, struct diff_filespec *dst) struct diff_score { int src; /* index in rename_src */ int dst; /* index in rename_dst */ - int score; - int name_score; + unsigned short score; + short name_score; }; static int estimate_similarity(struct diff_filespec *src, @@ -223,6 +223,12 @@ static int score_compare(const void *a_, const void *b_) { const struct diff_score *a = a_, *b = b_; + /* sink the unused ones to the bottom */ + if (a->dst < 0) + return (0 <= b->dst); + else if (b->dst < 0) + return -1; + if (a->score == b->score) return b->name_score - a->name_score; @@ -387,6 +393,22 @@ static int find_exact_renames(void) return i; } +#define NUM_CANDIDATE_PER_DST 4 +static void record_if_better(struct diff_score m[], struct diff_score *o) +{ + int i, worst; + + /* find the worst one */ + worst = 0; + for (i = 1; i < NUM_CANDIDATE_PER_DST; i++) + if (score_compare(&m[i], &m[worst]) > 0) + worst = i; + + /* is it better than the worst one? */ + if (score_compare(&m[worst], o) > 0) + m[worst] = *o; +} + void diffcore_rename(struct diff_options *options) { int detect_rename = options->detect_rename; @@ -474,47 +496,61 @@ void diffcore_rename(struct diff_options *options) goto cleanup; } - mx = xmalloc(sizeof(*mx) * num_create * num_src); + mx = xcalloc(num_create * NUM_CANDIDATE_PER_DST, sizeof(*mx)); for (dst_cnt = i = 0; i < rename_dst_nr; i++) { - int base = dst_cnt * num_src; struct diff_filespec *two = rename_dst[i].two; + struct diff_score *m; + if (rename_dst[i].pair) continue; /* dealt with exact match already. */ + + m = &mx[dst_cnt * NUM_CANDIDATE_PER_DST]; + for (j = 0; j < NUM_CANDIDATE_PER_DST; j++) + m[j].dst = -1; + for (j = 0; j < rename_src_nr; j++) { struct diff_filespec *one = rename_src[j].one; - struct diff_score *m = &mx[base+j]; - m->src = j; - m->dst = i; - m->score = estimate_similarity(one, two, - minimum_score); - m->name_score = basename_same(one, two); + struct diff_score this_src; + this_src.score = estimate_similarity(one, two, + minimum_score); + this_src.name_score = basename_same(one, two); + this_src.dst = i; + this_src.src = j; + record_if_better(m, &this_src); diff_free_filespec_blob(one); } /* We do not need the text anymore */ diff_free_filespec_blob(two); dst_cnt++; } + /* cost matrix sorted by most to least similar pair */ - qsort(mx, num_create * num_src, sizeof(*mx), score_compare); - for (i = 0; i < num_create * num_src; i++) { - struct diff_rename_dst *dst = &rename_dst[mx[i].dst]; - struct diff_filespec *src; + qsort(mx, dst_cnt * NUM_CANDIDATE_PER_DST, sizeof(*mx), score_compare); + + for (i = 0; i < dst_cnt * NUM_CANDIDATE_PER_DST; i++) { + struct diff_rename_dst *dst; + + if ((mx[i].dst < 0) || + (mx[i].score < minimum_score)) + break; /* there is no more usable pair. */ + dst = &rename_dst[mx[i].dst]; if (dst->pair) continue; /* already done, either exact or fuzzy. */ - if (mx[i].score < minimum_score) - break; /* there is no more usable pair. */ - src = rename_src[mx[i].src].one; - if (src->rename_used) + if (rename_src[mx[i].src].one->rename_used) continue; record_rename_pair(mx[i].dst, mx[i].src, mx[i].score); rename_count++; } - for (i = 0; i < num_create * num_src; i++) { - struct diff_rename_dst *dst = &rename_dst[mx[i].dst]; + + for (i = 0; i < dst_cnt * NUM_CANDIDATE_PER_DST; i++) { + struct diff_rename_dst *dst; + + if ((mx[i].dst < 0) || + (mx[i].score < minimum_score)) + break; /* there is no more usable pair. */ + dst = &rename_dst[mx[i].dst]; if (dst->pair) continue; /* already done, either exact or fuzzy. */ - if (mx[i].score < minimum_score) - break; /* there is no more usable pair. */ record_rename_pair(mx[i].dst, mx[i].src, mx[i].score); rename_count++; } diff --git a/git-add--interactive.perl b/git-add--interactive.perl index a0a81f134a..903953e68e 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -550,6 +550,21 @@ sub parse_diff { return @hunk; } +sub parse_diff_header { + my $src = shift; + + my $head = { TEXT => [], DISPLAY => [] }; + my $mode = { TEXT => [], DISPLAY => [] }; + + for (my $i = 0; $i < @{$src->{TEXT}}; $i++) { + my $dest = $src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ? + $mode : $head; + push @{$dest->{TEXT}}, $src->{TEXT}->[$i]; + push @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i]; + } + return ($head, $mode); +} + sub hunk_splittable { my ($text) = @_; @@ -795,9 +810,40 @@ sub patch_update_file { my ($ix, $num); my $path = shift; my ($head, @hunk) = parse_diff($path); + ($head, my $mode) = parse_diff_header($head); for (@{$head->{DISPLAY}}) { print; } + + if (@{$mode->{TEXT}}) { + while (1) { + print @{$mode->{DISPLAY}}; + print colored $prompt_color, + "Stage mode change [y/n/a/d/?]? "; + my $line = <STDIN>; + if ($line =~ /^y/i) { + $mode->{USE} = 1; + last; + } + elsif ($line =~ /^n/i) { + $mode->{USE} = 0; + last; + } + elsif ($line =~ /^a/i) { + $_->{USE} = 1 foreach ($mode, @hunk); + last; + } + elsif ($line =~ /^d/i) { + $_->{USE} = 0 foreach ($mode, @hunk); + last; + } + else { + help_patch_cmd(''); + next; + } + } + } + $num = scalar @hunk; $ix = 0; @@ -920,6 +966,9 @@ sub patch_update_file { my $n_lofs = 0; my @result = (); + if ($mode->{USE}) { + push @result, @{$mode->{TEXT}}; + } for (@hunk) { my $text = $_->{TEXT}; my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) = diff --git a/git-bisect.sh b/git-bisect.sh index 8e57e9a75d..d8d9bfde4c 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -1,7 +1,9 @@ #!/bin/sh -USAGE='[start|bad|good|skip|next|reset|visualize|replay|log|run]' -LONG_USAGE='git bisect start [<bad> [<good>...]] [--] [<pathspec>...] +USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]' +LONG_USAGE='git bisect help + print this long help message. +git bisect start [<bad> [<good>...]] [--] [<pathspec>...] reset bisect state and start bisection. git bisect bad [<rev>] mark <rev> a known-bad revision. @@ -20,7 +22,9 @@ git bisect replay <logfile> git bisect log show bisect log. git bisect run <cmd>... - use <cmd>... to automatically bisect.' + use <cmd>... to automatically bisect. + +Please use "git help bisect" to get the full man page.' OPTIONS_SPEC= . git-sh-setup @@ -467,6 +471,8 @@ case "$#" in cmd="$1" shift case "$cmd" in + help) + git bisect -h ;; start) bisect_start "$@" ;; bad|good|skip) diff --git a/git-compat-util.h b/git-compat-util.h index a18235e6d0..167c3fe63a 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -268,6 +268,12 @@ static inline void *xmalloc(size_t size) return ret; } +/* + * xmemdupz() allocates (len + 1) bytes of memory, duplicates "len" bytes of + * "data" to the allocated memory, zero terminates the allocated memory, + * and returns a pointer to the allocated memory. If the allocation fails, + * the program dies. + */ static inline void *xmemdupz(const void *data, size_t len) { char *p = xmalloc(len + 1); @@ -329,6 +335,11 @@ static inline void *xmmap(void *start, size_t length, return ret; } +/* + * xread() is the same a read(), but it automatically restarts read() + * operations with a recoverable error (EAGAIN and EINTR). xread() + * DOES NOT GUARANTEE that "len" bytes is read even if the data is available. + */ static inline ssize_t xread(int fd, void *buf, size_t len) { ssize_t nr; @@ -340,6 +351,11 @@ static inline ssize_t xread(int fd, void *buf, size_t len) } } +/* + * xwrite() is the same a write(), but it automatically restarts write() + * operations with a recoverable error (EAGAIN and EINTR). xwrite() DOES NOT + * GUARANTEE that "len" bytes is written even if the operation is successful. + */ static inline ssize_t xwrite(int fd, const void *buf, size_t len) { ssize_t nr; diff --git a/git-rebase.sh b/git-rebase.sh index 60c458f201..9b13b833cb 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -309,22 +309,42 @@ then } fi -# If the branch to rebase is given, first switch to it. +# If the branch to rebase is given, that is the branch we will rebase +# $branch_name -- branch being rebased, or HEAD (already detached) +# $orig_head -- commit object name of tip of the branch before rebasing +# $head_name -- refs/heads/<that-branch> or "detached HEAD" +switch_to= case "$#" in 2) + # Is it "rebase other $branchname" or "rebase other $commit"? branch_name="$2" - git-checkout "$2" || usage + switch_to="$2" + + if git show-ref --verify --quiet -- "refs/heads/$2" && + branch=$(git rev-parse --verify "refs/heads/$2" 2>/dev/null) + then + head_name="refs/heads/$2" + elif branch=$(git rev-parse --verify "$2" 2>/dev/null) + then + head_name="detached HEAD" + else + usage + fi ;; *) + # Do not need to switch branches, we are already on it. if branch_name=`git symbolic-ref -q HEAD` then + head_name=$branch_name branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'` else + head_name="detached HEAD" branch_name=HEAD ;# detached fi + branch=$(git rev-parse --verify "${branch_name}^0") || exit ;; esac -branch=$(git rev-parse --verify "${branch_name}^0") || exit +orig_head=$branch # Now we are rebasing commits $upstream..$branch on top of $onto @@ -335,6 +355,8 @@ if test "$upstream" = "$onto" && test "$mb" = "$onto" && # linear history? ! git rev-list --parents "$onto".."$branch" | grep " .* " > /dev/null then + # Lazily switch to the target branch if needed... + test -z "$switch_to" || git checkout "$switch_to" echo >&2 "Current branch $branch_name is up to date." exit 0 fi @@ -346,22 +368,11 @@ then GIT_PAGER='' git diff --stat --summary "$mb" "$onto" fi -# move to a detached HEAD -orig_head=$(git rev-parse HEAD^0) -head_name=$(git symbolic-ref HEAD 2> /dev/null) -case "$head_name" in -'') - head_name="detached HEAD" - ;; -*) - git checkout "$orig_head" > /dev/null 2>&1 || - die "could not detach HEAD" - ;; -esac - -# Rewind the head to "$onto"; this saves our current head in ORIG_HEAD. +# Detach HEAD and reset the tree echo "First, rewinding head to replay your work on top of it..." -git-reset --hard "$onto" +git checkout "$onto^0" >/dev/null 2>&1 || + die "could not detach HEAD" +# git reset --hard "$onto^0" # If the $onto is a proper descendant of the tip of the branch, then # we just fast forwarded. @@ -374,7 +385,8 @@ fi if test -z "$do_merge" then - git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD | + git format-patch -k --stdout --full-index --ignore-if-in-upstream \ + "$upstream..$orig_head" | git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" && move_to_original_branch ret=$? @@ -397,7 +409,7 @@ echo "$orig_head" > "$dotest/orig-head" echo "$head_name" > "$dotest/head-name" msgnum=0 -for cmt in `git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD` +for cmt in `git rev-list --reverse --no-merges "$upstream..$orig_head"` do msgnum=$(($msgnum + 1)) echo "$cmt" > "$dotest/cmt.$msgnum" diff --git a/git-send-email.perl b/git-send-email.perl index be4a20d7cd..9e568bf9c0 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -168,7 +168,8 @@ my $envelope_sender; # Example reply to: #$initial_reply_to = ''; #<20050203173208.GA23964@foobar.com>'; -my $repo = Git->repository(); +my $repo = eval { Git->repository() }; +my @repo = $repo ? ($repo) : (); my $term = eval { $ENV{"GIT_SEND_EMAIL_NOTTY"} ? new Term::ReadLine 'git-send-email', \*STDIN, \*STDOUT @@ -271,25 +272,25 @@ sub read_config { foreach my $setting (keys %config_bool_settings) { my $target = $config_bool_settings{$setting}->[0]; - $$target = $repo->config_bool("$prefix.$setting") unless (defined $$target); + $$target = Git::config_bool(@repo, "$prefix.$setting") unless (defined $$target); } foreach my $setting (keys %config_settings) { my $target = $config_settings{$setting}; if (ref($target) eq "ARRAY") { unless (@$target) { - my @values = $repo->config("$prefix.$setting"); + my @values = Git::config(@repo, "$prefix.$setting"); @$target = @values if (@values && defined $values[0]); } } else { - $$target = $repo->config("$prefix.$setting") unless (defined $$target); + $$target = Git::config(@repo, "$prefix.$setting") unless (defined $$target); } } } # read configuration from [sendemail "$identity"], fall back on [sendemail] -$identity = $repo->config("sendemail.identity") unless (defined $identity); +$identity = Git::config(@repo, "sendemail.identity") unless (defined $identity); read_config("sendemail.$identity") if (defined $identity); read_config("sendemail"); @@ -327,8 +328,9 @@ if (0) { } } -my ($repoauthor) = $repo->ident_person('author'); -my ($repocommitter) = $repo->ident_person('committer'); +my ($repoauthor, $repocommitter); +($repoauthor) = Git::ident_person(@repo, 'author'); +($repocommitter) = Git::ident_person(@repo, 'committer'); # Verify the user input @@ -415,7 +417,7 @@ if (@files) { my $prompting = 0; if (!defined $sender) { - $sender = $repoauthor || $repocommitter; + $sender = $repoauthor || $repocommitter || ''; while (1) { $_ = $term->readline("Who should the emails appear to be from? [$sender] "); @@ -509,7 +511,7 @@ GIT: for the patch you are writing. EOT close(C); - my $editor = $ENV{GIT_EDITOR} || $repo->config("core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi"; + my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi"; system('sh', '-c', '$0 $@', $editor, $compose_filename); open(C2,">",$compose_filename . ".final") diff --git a/git-submodule.sh b/git-submodule.sh index a745e42bf7..ce0f00c8a4 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -343,6 +343,7 @@ set_name_rev () { # cmd_summary() { summary_limit=-1 + for_status= # parse $args after "submodule ... summary". while test $# -ne 0 @@ -351,6 +352,9 @@ cmd_summary() { --cached) cached="$1" ;; + --for-status) + for_status="$1" + ;; -n|--summary-limit) if summary_limit=$(($2 + 0)) 2>/dev/null && test "$summary_limit" = "$2" then @@ -398,7 +402,8 @@ cmd_summary() { done ) - test -n "$modules" && + test -z "$modules" && return + git diff-index $cached --raw $head -- $modules | grep -e '^:160000' -e '^:[0-7]* 160000' | cut -c2- | @@ -500,7 +505,14 @@ cmd_summary() { echo fi echo - done + done | + if test -n "$for_status"; then + echo "# Modified submodules:" + echo "#" + sed -e 's|^|# |' -e 's|^# $|#|' + else + cat + fi } # # List all submodules, prefixed with: diff --git a/git-svn.perl b/git-svn.perl index b1510495a7..711e7b7eb9 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -410,10 +410,12 @@ sub cmd_dcommit { $head ||= 'HEAD'; my @refs; my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs); - print "Committing to $url ...\n"; + if ($url) { + print "Committing to $url ...\n"; + } unless ($gs) { die "Unable to determine upstream SVN information from ", - "$head history\n"; + "$head history.\nPerhaps the repository is empty."; } my $last_rev; my ($linear_refs, $parents) = linearize_history($gs, \@refs); @@ -347,7 +347,7 @@ static void handle_internal_command(int argc, const char **argv) { "shortlog", cmd_shortlog, USE_PAGER }, { "show-branch", cmd_show_branch, RUN_SETUP }, { "show", cmd_show, RUN_SETUP | USE_PAGER }, - { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE }, + { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE | USE_PAGER }, { "stripspace", cmd_stripspace }, { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP }, { "tag", cmd_tag, RUN_SETUP }, diff --git a/gitweb/INSTALL b/gitweb/INSTALL index 9cd5b0a2b1..f7194dbef7 100644 --- a/gitweb/INSTALL +++ b/gitweb/INSTALL @@ -29,40 +29,40 @@ Build time configuration See also "How to configure gitweb for your local system" in README file for gitweb (in gitweb/README). -- There are many configuration variables which affects building of +- There are many configuration variables which affect building of gitweb.cgi; see "default configuration for gitweb" section in main (top dir) Makefile, and instructions for building gitweb/gitweb.cgi target. - One of most important is where to find git wrapper binary. Gitweb - tries to find git wrapper at $(bindir)/git, so you have to set $bindir + One of the most important is where to find the git wrapper binary. Gitweb + tries to find the git wrapper at $(bindir)/git, so you have to set $bindir when building gitweb.cgi, or $prefix from which $bindir is derived. If - you build and install gitweb together with the rest of git suite, + you build and install gitweb together with the rest of the git suite, there should be no problems. Otherwise, if git was for example installed from a binary package, you have to set $prefix (or $bindir) accordingly. - Another important issue is where are git repositories you want to make - available to gitweb. By default gitweb search for repositories under + available to gitweb. By default gitweb searches for repositories under /pub/git; if you want to have projects somewhere else, like /home/git, use GITWEB_PROJECTROOT build configuration variable. By default all git repositories under projectroot are visible and - available to gitweb. List of projects is generated by default by + available to gitweb. The list of projects is generated by default by scanning the projectroot directory for git repositories. This can be changed (configured) as described in "Gitweb repositories" section below. - Note that gitweb deals directly with object database, and does not - need working directory; the name of the project is the name of its + Note that gitweb deals directly with the object database, and does not + need a working directory; the name of the project is the name of its repository object database, usually projectname.git for bare repositories. If you want to provide gitweb access to non-bare (live) - repository, you can make projectname.git symbolic link under + repositories, you can make projectname.git a symbolic link under projectroot linking to projectname/.git (but it is just a suggestion). - You can control where gitweb tries to find its main CSS style file, - its favicon and logo with GITWEB_CSS, GITWEB_FAVICON and GITWEB_LOGO + its favicon and logo with the GITWEB_CSS, GITWEB_FAVICON and GITWEB_LOGO build configuration variables. By default gitweb tries to find them in the same directory as gitweb.cgi script. @@ -91,13 +91,17 @@ Gitweb config file See also "Runtime gitweb configuration" section in README file for gitweb (in gitweb/README). -- You can configure gitweb further using gitweb configuration file; - by default it is file named gitweb_config.perl in the same place as - gitweb.cgi script. You can control default place for config file - using GITWEB_CONFIG build configuration variable, and you can set it - using GITWEB_CONFIG environmental variable. - -- Gitweb config file is [fragment] of perl code. You can set variables +- You can configure gitweb further using the gitweb configuration file; + by default this is a file named gitweb_config.perl in the same place as + gitweb.cgi script. You can control the default place for the config file + using the GITWEB_CONFIG build configuration variable, and you can set it + using the GITWEB_CONFIG environment variable. If this file does not + exist, gitweb looks for a system-wide configuration file, normally + /etc/gitweb.conf. You can change the default using the + GITWEB_CONFIG_SYSTEM build configuration variable, and override it + through the GITWEB_CONFIG_SYSTEM environment variable. + +- The gitweb config file is a fragment of perl code. You can set variables using "our $variable = value"; text from "#" character until the end of a line is ignored. See perlsyn(1) for details. @@ -124,36 +128,37 @@ Gitweb repositories ------------------- - By default all git repositories under projectroot are visible and - available to gitweb. List of projects is generated by default by + available to gitweb. The list of projects is generated by default by scanning the projectroot directory for git repositories (for object databases to be more exact). - You can provide pre-generated list of [visible] repositories, + You can provide a pre-generated list of [visible] repositories, together with information about their owners (the project ownership - is taken from owner of repository directory otherwise), by setting - GITWEB_LIST build configuration variable (or $projects_list variable - in gitweb config file) to point to a plain file. - - Each line of projects list file should consist of url-encoded path - to project repository database (relative to projectroot) separated - by space from url-encoded project owner; spaces in both project path - and project owner have to be encoded as either '%20' or '+'. - - You can generate projects list index file using project_index action - (the 'TXT' link on projects list page) directly from gitweb. - -- By default even if project is not visible on projects list page, you - can view it nevertheless by hand-crafting gitweb URL. You can set - GITWEB_STRICT_EXPORT build configuration variable (or $strict_export - variable in gitweb config file) to only allow viewing of + defaults to the owner of the repository directory otherwise), by setting + the GITWEB_LIST build configuration variable (or the $projects_list + variable in the gitweb config file) to point to a plain file. + + Each line of the projects list file should consist of the url-encoded path + to the project repository database (relative to projectroot), followed + by the url-encoded project owner on the same line (separated by a space). + Spaces in both project path and project owner have to be encoded as either + '%20' or '+'. + + You can generate the projects list index file using the project_index + action (the 'TXT' link on projects list page) directly from gitweb. + +- By default, even if a project is not visible on projects list page, you + can view it nevertheless by hand-crafting a gitweb URL. You can set the + GITWEB_STRICT_EXPORT build configuration variable (or the $strict_export + variable in the gitweb config file) to only allow viewing of repositories also shown on the overview page. - Alternatively, you can configure gitweb to only list and allow - viewing of the explicitly exported repositories, via - GITWEB_EXPORT_OK build configuration variable (or $export_ok + viewing of the explicitly exported repositories, via the + GITWEB_EXPORT_OK build configuration variable (or the $export_ok variable in gitweb config file). If it evaluates to true, gitweb - show repository only if this file exists in its object database - (if directory has the magic file $export_ok). + shows repositories only if this file exists in its object database + (if directory has the magic file named $export_ok). Generating projects list using gitweb ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/gitweb/README b/gitweb/README index 2163071047..8f7ea367ba 100644 --- a/gitweb/README +++ b/gitweb/README @@ -2,7 +2,7 @@ GIT web Interface ================= The one working on: - http://www.kernel.org/git/ + http://git.kernel.org/ From the git version 1.4.0 gitweb is bundled with git. @@ -10,13 +10,13 @@ From the git version 1.4.0 gitweb is bundled with git. How to configure gitweb for your local system --------------------------------------------- -See also "Build time configuration" section in INSTALL +See also the "Build time configuration" section in the INSTALL file for gitweb (in gitweb/INSTALL). You can specify the following configuration variables when building GIT: * GIT_BINDIR - Points out where to find git executable. You should set up it to - the place where git binary was installed (usually /usr/bin) if you + Points where to find the git executable. You should set it up to + the place where the git binary was installed (usually /usr/bin) if you don't install git from sources together with gitweb. [Default: $(bindir)] * GITWEB_SITENAME Shown in the title of all generated pages, defaults to the server name @@ -24,13 +24,13 @@ You can specify the following configuration variables when building GIT: * GITWEB_PROJECTROOT The root directory for all projects shown by gitweb. Must be set correctly for gitweb to find repositories to display. See also - "Gitweb repositories" in INSTALL file for gitweb. [Default: /pub/git] + "Gitweb repositories" in the INSTALL file for gitweb. [Default: /pub/git] * GITWEB_PROJECT_MAXDEPTH - The filesystem traversing limit for getting projects list; the number + The filesystem traversing limit for getting the project list; the number is taken as depth relative to the projectroot. It is used when GITWEB_LIST is a directory (or is not set; then project root is used). Is is meant to speed up project listing on large work trees by limiting - find depth. [Default: 2007] + search depth. [Default: 2007] * GITWEB_LIST Points to a directory to scan for projects (defaults to project root if not set / if empty) or to a file with explicit listing of projects @@ -72,15 +72,15 @@ You can specify the following configuration variables when building GIT: Git base URLs used for URL to where fetch project from, i.e. full URL is "$git_base_url/$project". Shown on projects summary page. Repository URL for project can be also configured per repository; this - takes precendence over URL composed from base URL and project name. + takes precedence over URLs composed from base URL and a project name. Note that you can setup multiple base URLs (for example one for - git:// protocol access, one for http:// access) from gitweb config - file. [No default] + git:// protocol access, another for http:// access) from the gitweb + config file. [No default] * GITWEB_CSS Points to the location where you put gitweb.css on your web server - (or to be more generic URI of gitweb stylesheet). Relative to base - URI of gitweb. Note that you can setup multiple stylesheets from - gitweb config file. [Default: gitweb.css] + (or to be more generic, the URI of gitweb stylesheet). Relative to the + base URI of gitweb. Note that you can setup multiple stylesheets from + the gitweb config file. [Default: gitweb.css] * GITWEB_LOGO Points to the location where you put git-logo.png on your web server (or to be more generic URI of logo, 72x27 size, displayed in top right @@ -100,13 +100,20 @@ You can specify the following configuration variables when building GIT: is set when gitweb.cgi is executed, then the file specified in the environment variable will be loaded instead of the file specified when gitweb.cgi was created. [Default: gitweb_config.perl] + * GITWEB_CONFIG_SYSTEM + This Perl file will be loaded using 'do' as a fallback if GITWEB_CONFIG + does not exist. If the environment variable GITWEB_CONFIG_SYSTEM is set + when gitweb.cgi is executed, then the file specified in the environment + variable will be loaded instead of the file specified when gitweb.cgi was + created. [Default: /etc/gitweb.conf] Runtime gitweb configuration ---------------------------- You can adjust gitweb behaviour using the file specified in `GITWEB_CONFIG` -(defaults to 'gitweb_config.perl' in the same directory as the CGI). +(defaults to 'gitweb_config.perl' in the same directory as the CGI), and +as a fallback `GITWEB_CONFIG_SYSTEM` (defaults to /etc/gitweb.conf). The most notable thing that is not configurable at compile time are the optional features, stored in the '%features' variable. @@ -114,15 +121,15 @@ Ultimate description on how to reconfigure the default features setting in your `GITWEB_CONFIG` or per-project in `project.git/config` can be found as comments inside 'gitweb.cgi'. -See also "Gitweb config file" (with example of gitweb config file), and -"Gitweb repositories" sections in INSTALL file for gitweb. +See also the "Gitweb config file" (with an example of config file), and +the "Gitweb repositories" sections in INSTALL file for gitweb. -Gitweb config file is [fragment] of perl code. You can set variables +The gitweb config file is a fragment of perl code. You can set variables using "our $variable = value"; text from "#" character until the end of a line is ignored. See perlsyn(1) man page for details. -Below there is list of vaiables which you might want to set in gitweb config. +Below is the list of variables which you might want to set in gitweb config. See the top of 'gitweb.cgi' for the full list of variables and their descriptions. @@ -133,7 +140,7 @@ You can set, among others, the following variables in gitweb config files (with the exception of $projectroot and $projects_list this list does not include variables usually directly set during build): * $GIT - Cure git executable to use. By default set to "$GIT_BINDIR/git", which + Core git executable to use. By default set to "$GIT_BINDIR/git", which in turn is by default set to "$(bindir)/git". If you use git from binary package, set this to "/usr/bin/git". This can just be "git" if your webserver has a sensible PATH. If you have multiple git versions @@ -169,7 +176,7 @@ not include variables usually directly set during build): to make it easier to upgrade gitweb. You can add 'site' stylesheet for example by using push @stylesheets, "gitweb-site.css"; - in gitweb config file. + in the gitweb config file. * $logo_url, $logo_label URI and label (title) of GIT logo link (or your site logo, if you choose to use different logo image). By default they point to git homepage; @@ -191,12 +198,12 @@ not include variables usually directly set during build): Default mimetype for blob_plain (raw) view, if mimetype checking doesn't result in some other type; by default 'text/plain'. * $default_text_plain_charset - Default charset for text files. If not set, web serwer configuration + Default charset for text files. If not set, web server configuration would be used. * $mimetypes_file File to use for (filename extension based) guessing of MIME types before - trying /etc/mime.types. Path, if relative, is taken currently as taken - relative to current git repositoy. + trying /etc/mime.types. Path, if relative, is taken currently as + relative to the current git repository. * $fallback_encoding Gitweb assumes this charset if line contains non-UTF-8 characters. Fallback decoding is used without error checking, so it can be even @@ -225,14 +232,14 @@ You can use the following files in repository: single line description of a project (of a repository). Plain text file; HTML will be escaped. By default set to Unnamed repository; edit this file to name it for gitweb. - from the template during creating repository. You can use + from the template during repository creation. You can use the gitweb.description repo configuration variable, but the file takes - precendence. + precedence. * cloneurl (or multiple-valued gitweb.url) File with repository URL (used for clone and fetch), one per line. Displayed in the project summary page. You can use multiple-valued gitweb.url repository configuration variable for that, but the file - takes precendence. + takes precedence. * gitweb.owner You can use the gitweb.owner repository configuration variable to set repository's owner. It is displayed in the project list and summary diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 12843a4846..f83567ec39 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -369,7 +369,12 @@ sub filter_snapshot_fmts { } our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++"; -do $GITWEB_CONFIG if -e $GITWEB_CONFIG; +if (-e $GITWEB_CONFIG) { + do $GITWEB_CONFIG; +} else { + our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++"; + do $GITWEB_CONFIG_SYSTEM if -e $GITWEB_CONFIG_SYSTEM; +} # version of the core git binary our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown"; diff --git a/log-tree.c b/log-tree.c index 5b2963998c..8f5436b747 100644 --- a/log-tree.c +++ b/log-tree.c @@ -249,9 +249,9 @@ void show_log(struct rev_info *opt, const char *sep) * not have an empty line between entries. */ extra = ""; - if (*sep != '\n' && opt->commit_format == CMIT_FMT_ONELINE) + if (*sep != '\n' && opt->use_terminator) extra = "\n"; - if (opt->shown_one && opt->commit_format != CMIT_FMT_ONELINE) + if (opt->shown_one && !opt->use_terminator) putchar(opt->diffopt.line_termination); opt->shown_one = 1; @@ -317,8 +317,10 @@ void show_log(struct rev_info *opt, const char *sep) if (opt->show_log_size) printf("log size %i\n", (int)msgbuf.len); - if (msgbuf.len) - printf("%s%s%s", msgbuf.buf, extra, sep); + if (msgbuf.len) { + fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout); + printf("%s%s", extra, sep); + } strbuf_release(&msgbuf); } @@ -91,7 +91,8 @@ int validate_headref(const char *path) struct stat st; char *buf, buffer[256]; unsigned char sha1[20]; - int len, fd; + int fd; + ssize_t len; if (lstat(path, &st) < 0) return -1; @@ -266,24 +267,25 @@ int adjust_shared_perm(const char *path) if (lstat(path, &st) < 0) return -1; mode = st.st_mode; - if (mode & S_IRUSR) - mode |= (shared_repository == PERM_GROUP - ? S_IRGRP - : (shared_repository == PERM_EVERYBODY - ? (S_IRGRP|S_IROTH) - : 0)); - - if (mode & S_IWUSR) - mode |= S_IWGRP; - - if (mode & S_IXUSR) - mode |= (shared_repository == PERM_GROUP - ? S_IXGRP - : (shared_repository == PERM_EVERYBODY - ? (S_IXGRP|S_IXOTH) - : 0)); - if (S_ISDIR(mode)) + + if (shared_repository) { + int tweak = shared_repository; + if (!(mode & S_IWUSR)) + tweak &= ~0222; + mode = (mode & ~0777) | tweak; + } else { + /* Preserve old PERM_UMASK behaviour */ + if (mode & S_IWUSR) + mode |= S_IWGRP; + } + + if (S_ISDIR(mode)) { mode |= FORCE_DIR_SET_GID; + + /* Copy read bits to execute bits */ + mode |= (shared_repository & 0444) >> 2; + } + if ((mode & st.st_mode) != mode && chmod(path, mode) < 0) return -2; return 0; diff --git a/perl/Git.pm b/perl/Git.pm index a2812ea612..2e7f896bae 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -487,22 +487,20 @@ does. In scalar context requires the variable to be set only one time (exception is thrown otherwise), in array context returns allows the variable to be set multiple times and returns all the values. -Must be called on a repository instance. - This currently wraps command('config') so it is not so fast. =cut sub config { - my ($self, $var) = @_; - $self->repo_path() - or throw Error::Simple("not a repository"); + my ($self, $var) = _maybe_self(@_); try { + my @cmd = ('config'); + unshift @cmd, $self if $self; if (wantarray) { - return $self->command('config', '--get-all', $var); + return command(@cmd, '--get-all', $var); } else { - return $self->command_oneline('config', '--get', $var); + return command_oneline(@cmd, '--get', $var); } } catch Git::Error::Command with { my $E = shift; @@ -522,20 +520,17 @@ Retrieve the bool configuration C<VARIABLE>. The return value is usable as a boolean in perl (and C<undef> if it's not defined, of course). -Must be called on a repository instance. - This currently wraps command('config') so it is not so fast. =cut sub config_bool { - my ($self, $var) = @_; - $self->repo_path() - or throw Error::Simple("not a repository"); + my ($self, $var) = _maybe_self(@_); try { - my $val = $self->command_oneline('config', '--bool', '--get', - $var); + my @cmd = ('config', '--bool', '--get', $var); + unshift @cmd, $self if $self; + my $val = command_oneline(@cmd); return undef unless defined $val; return $val eq 'true'; } catch Git::Error::Command with { @@ -557,19 +552,17 @@ or 'g' in the config file will cause the value to be multiplied by 1024, 1048576 (1024^2), or 1073741824 (1024^3) prior to output. It would return C<undef> if configuration variable is not defined, -Must be called on a repository instance. - This currently wraps command('config') so it is not so fast. =cut sub config_int { - my ($self, $var) = @_; - $self->repo_path() - or throw Error::Simple("not a repository"); + my ($self, $var) = _maybe_self(@_); try { - return $self->command_oneline('config', '--int', '--get', $var); + my @cmd = ('config', '--int', '--get', $var); + unshift @cmd, $self if $self; + return command_oneline(@cmd); } catch Git::Error::Command with { my $E = shift; if ($E->value() == 1) { @@ -639,15 +632,15 @@ The synopsis is like: "$name <$email>" eq ident_person($name); $time_tz =~ /^\d+ [+-]\d{4}$/; -Both methods must be called on a repository instance. - =cut sub ident { - my ($self, $type) = @_; + my ($self, $type) = _maybe_self(@_); my $identstr; if (lc $type eq lc 'committer' or lc $type eq lc 'author') { - $identstr = $self->command_oneline('var', 'GIT_'.uc($type).'_IDENT'); + my @cmd = ('var', 'GIT_'.uc($type).'_IDENT'); + unshift @cmd, $self if $self; + $identstr = command_oneline(@cmd); } else { $identstr = $type; } @@ -659,8 +652,8 @@ sub ident { } sub ident_person { - my ($self, @ident) = @_; - $#ident == 0 and @ident = $self->ident($ident[0]); + my ($self, @ident) = _maybe_self(@_); + $#ident == 0 and @ident = $self ? $self->ident($ident[0]) : ident($ident[0]); return "$ident[0] <$ident[1]>"; } @@ -4,40 +4,49 @@ #include "diff.h" #include "revision.h" -static struct cmt_fmt_map { - const char *n; - size_t cmp_len; - enum cmit_fmt v; -} cmt_fmts[] = { - { "raw", 1, CMIT_FMT_RAW }, - { "medium", 1, CMIT_FMT_MEDIUM }, - { "short", 1, CMIT_FMT_SHORT }, - { "email", 1, CMIT_FMT_EMAIL }, - { "full", 5, CMIT_FMT_FULL }, - { "fuller", 5, CMIT_FMT_FULLER }, - { "oneline", 1, CMIT_FMT_ONELINE }, - { "format:", 7, CMIT_FMT_USERFORMAT}, -}; - static char *user_format; -enum cmit_fmt get_commit_format(const char *arg) +void get_commit_format(const char *arg, struct rev_info *rev) { int i; - - if (!arg || !*arg) - return CMIT_FMT_DEFAULT; + static struct cmt_fmt_map { + const char *n; + size_t cmp_len; + enum cmit_fmt v; + } cmt_fmts[] = { + { "raw", 1, CMIT_FMT_RAW }, + { "medium", 1, CMIT_FMT_MEDIUM }, + { "short", 1, CMIT_FMT_SHORT }, + { "email", 1, CMIT_FMT_EMAIL }, + { "full", 5, CMIT_FMT_FULL }, + { "fuller", 5, CMIT_FMT_FULLER }, + { "oneline", 1, CMIT_FMT_ONELINE }, + }; + + rev->use_terminator = 0; + if (!arg || !*arg) { + rev->commit_format = CMIT_FMT_DEFAULT; + return; + } if (*arg == '=') arg++; - if (!prefixcmp(arg, "format:")) { + if (!prefixcmp(arg, "format:") || !prefixcmp(arg, "tformat:")) { + const char *cp = strchr(arg, ':') + 1; free(user_format); - user_format = xstrdup(arg + 7); - return CMIT_FMT_USERFORMAT; + user_format = xstrdup(cp); + if (arg[0] == 't') + rev->use_terminator = 1; + rev->commit_format = CMIT_FMT_USERFORMAT; + return; } for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) { if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) && - !strncmp(arg, cmt_fmts[i].n, strlen(arg))) - return cmt_fmts[i].v; + !strncmp(arg, cmt_fmts[i].n, strlen(arg))) { + if (cmt_fmts[i].v == CMIT_FMT_ONELINE) + rev->use_terminator = 1; + rev->commit_format = cmt_fmts[i].v; + return; + } } die("invalid --pretty format: %s", arg); @@ -457,6 +466,7 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder, const struct commit *commit = c->commit; const char *msg = commit->buffer; struct commit_list *p; + int h1, h2; /* these are independent of the commit */ switch (placeholder[0]) { @@ -478,6 +488,16 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder, case 'n': /* newline */ strbuf_addch(sb, '\n'); return 1; + case 'x': + /* %x00 == NUL, %x0a == LF, etc. */ + if (0 <= (h1 = hexval_table[0xff & placeholder[1]]) && + h1 <= 16 && + 0 <= (h2 = hexval_table[0xff & placeholder[2]]) && + h2 <= 16) { + strbuf_addch(sb, (h1<<4)|h2); + return 3; + } else + return 0; } /* these depend on the commit */ diff --git a/receive-pack.c b/receive-pack.c index f83ae87e15..828d49001d 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -10,6 +10,7 @@ static const char receive_pack_usage[] = "git-receive-pack <git-dir>"; static int deny_non_fast_forwards = 0; +static int receive_fsck_objects; static int receive_unpack_limit = -1; static int transfer_unpack_limit = -1; static int unpack_limit = 100; @@ -35,6 +36,11 @@ static int receive_pack_config(const char *var, const char *value) return 0; } + if (strcmp(var, "receive.fsckobjects") == 0) { + receive_fsck_objects = git_config_bool(var, value); + return 0; + } + return git_default_config(var, value); } @@ -368,11 +374,13 @@ static const char *unpack(void) ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries)); if (ntohl(hdr.hdr_entries) < unpack_limit) { - int code; - const char *unpacker[3]; - unpacker[0] = "unpack-objects"; - unpacker[1] = hdr_arg; - unpacker[2] = NULL; + int code, i = 0; + const char *unpacker[4]; + unpacker[i++] = "unpack-objects"; + if (receive_fsck_objects) + unpacker[i++] = "--strict"; + unpacker[i++] = hdr_arg; + unpacker[i++] = NULL; code = run_command_v_opt(unpacker, RUN_GIT_CMD); switch (code) { case 0: @@ -393,8 +401,8 @@ static const char *unpack(void) return "unpacker exited with error code"; } } else { - const char *keeper[6]; - int s, status; + const char *keeper[7]; + int s, status, i = 0; char keep_arg[256]; struct child_process ip; @@ -402,12 +410,14 @@ static const char *unpack(void) if (gethostname(keep_arg + s, sizeof(keep_arg) - s)) strcpy(keep_arg + s, "localhost"); - keeper[0] = "index-pack"; - keeper[1] = "--stdin"; - keeper[2] = "--fix-thin"; - keeper[3] = hdr_arg; - keeper[4] = keep_arg; - keeper[5] = NULL; + keeper[i++] = "index-pack"; + keeper[i++] = "--stdin"; + if (receive_fsck_objects) + keeper[i++] = "--strict"; + keeper[i++] = "--fix-thin"; + keeper[i++] = hdr_arg; + keeper[i++] = keep_arg; + keeper[i++] = NULL; memset(&ip, 0, sizeof(ip)); ip.argv = keeper; ip.out = -1; @@ -368,7 +368,8 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *re const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag) { - int depth = MAXDEPTH, len; + int depth = MAXDEPTH; + ssize_t len; char buffer[256]; static char ref_buffer[256]; diff --git a/revision.c b/revision.c index ffbed3fbf2..4231ea2cce 100644 --- a/revision.c +++ b/revision.c @@ -1199,7 +1199,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch } if (!prefixcmp(arg, "--pretty")) { revs->verbose_header = 1; - revs->commit_format = get_commit_format(arg+8); + get_commit_format(arg+8, revs); continue; } if (!strcmp(arg, "--root")) { diff --git a/revision.h b/revision.h index c8b3b948ec..31217f8c67 100644 --- a/revision.h +++ b/revision.h @@ -64,7 +64,8 @@ struct rev_info { /* Format info */ unsigned int shown_one:1, - abbrev_commit:1; + abbrev_commit:1, + use_terminator:1; enum date_mode date_mode; const char **ignore_packed; /* pretend objects in these are unpacked */ @@ -428,21 +428,53 @@ const char *setup_git_directory_gently(int *nongit_ok) int git_config_perm(const char *var, const char *value) { - if (value) { - int i; - if (!strcmp(value, "umask")) - return PERM_UMASK; - if (!strcmp(value, "group")) - return PERM_GROUP; - if (!strcmp(value, "all") || - !strcmp(value, "world") || - !strcmp(value, "everybody")) - return PERM_EVERYBODY; - i = atoi(value); - if (i > 1) - return i; + int i; + char *endptr; + + if (value == NULL) + return PERM_GROUP; + + if (!strcmp(value, "umask")) + return PERM_UMASK; + if (!strcmp(value, "group")) + return PERM_GROUP; + if (!strcmp(value, "all") || + !strcmp(value, "world") || + !strcmp(value, "everybody")) + return PERM_EVERYBODY; + + /* Parse octal numbers */ + i = strtol(value, &endptr, 8); + + /* If not an octal number, maybe true/false? */ + if (*endptr != 0) + return git_config_bool(var, value) ? PERM_GROUP : PERM_UMASK; + + /* + * Treat values 0, 1 and 2 as compatibility cases, otherwise it is + * a chmod value. + */ + switch (i) { + case PERM_UMASK: /* 0 */ + return PERM_UMASK; + case OLD_PERM_GROUP: /* 1 */ + return PERM_GROUP; + case OLD_PERM_EVERYBODY: /* 2 */ + return PERM_EVERYBODY; } - return git_config_bool(var, value); + + /* A filemode value was given: 0xxx */ + + if ((i & 0600) != 0600) + die("Problem with core.sharedRepository filemode value " + "(0%.3o).\nThe owner of files must always have " + "read and write permissions.", i); + + /* + * Mask filemode value. Others can not get write permission. + * x flags for directories are handled separately. + */ + return i & 0666; } int check_repository_format_version(const char *var, const char *value) diff --git a/sha1-lookup.c b/sha1-lookup.c new file mode 100644 index 0000000000..da357479cf --- /dev/null +++ b/sha1-lookup.c @@ -0,0 +1,171 @@ +#include "cache.h" +#include "sha1-lookup.h" + +/* + * Conventional binary search loop looks like this: + * + * unsigned lo, hi; + * do { + * unsigned mi = (lo + hi) / 2; + * int cmp = "entry pointed at by mi" minus "target"; + * if (!cmp) + * return (mi is the wanted one) + * if (cmp > 0) + * hi = mi; "mi is larger than target" + * else + * lo = mi+1; "mi is smaller than target" + * } while (lo < hi); + * + * The invariants are: + * + * - When entering the loop, lo points at a slot that is never + * above the target (it could be at the target), hi points at a + * slot that is guaranteed to be above the target (it can never + * be at the target). + * + * - We find a point 'mi' between lo and hi (mi could be the same + * as lo, but never can be as same as hi), and check if it hits + * the target. There are three cases: + * + * - if it is a hit, we are happy. + * + * - if it is strictly higher than the target, we set it to hi, + * and repeat the search. + * + * - if it is strictly lower than the target, we update lo to + * one slot after it, because we allow lo to be at the target. + * + * If the loop exits, there is no matching entry. + * + * When choosing 'mi', we do not have to take the "middle" but + * anywhere in between lo and hi, as long as lo <= mi < hi is + * satisfied. When we somehow know that the distance between the + * target and lo is much shorter than the target and hi, we could + * pick mi that is much closer to lo than the midway. + * + * Now, we can take advantage of the fact that SHA-1 is a good hash + * function, and as long as there are enough entries in the table, we + * can expect uniform distribution. An entry that begins with for + * example "deadbeef..." is much likely to appear much later than in + * the midway of the table. It can reasonably be expected to be near + * 87% (222/256) from the top of the table. + * + * However, we do not want to pick "mi" too precisely. If the entry at + * the 87% in the above example turns out to be higher than the target + * we are looking for, we would end up narrowing the search space down + * only by 13%, instead of 50% we would get if we did a simple binary + * search. So we would want to hedge our bets by being less aggressive. + * + * The table at "table" holds at least "nr" entries of "elem_size" + * bytes each. Each entry has the SHA-1 key at "key_offset". The + * table is sorted by the SHA-1 key of the entries. The caller wants + * to find the entry with "key", and knows that the entry at "lo" is + * not higher than the entry it is looking for, and that the entry at + * "hi" is higher than the entry it is looking for. + */ +int sha1_entry_pos(const void *table, + size_t elem_size, + size_t key_offset, + unsigned lo, unsigned hi, unsigned nr, + const unsigned char *key) +{ + const unsigned char *base = table; + const unsigned char *hi_key, *lo_key; + unsigned ofs_0; + static int debug_lookup = -1; + + if (debug_lookup < 0) + debug_lookup = !!getenv("GIT_DEBUG_LOOKUP"); + + if (!nr || lo >= hi) + return -1; + + if (nr == hi) + hi_key = NULL; + else + hi_key = base + elem_size * hi + key_offset; + lo_key = base + elem_size * lo + key_offset; + + ofs_0 = 0; + do { + int cmp; + unsigned ofs, mi, range; + unsigned lov, hiv, kyv; + const unsigned char *mi_key; + + range = hi - lo; + if (hi_key) { + for (ofs = ofs_0; ofs < 20; ofs++) + if (lo_key[ofs] != hi_key[ofs]) + break; + ofs_0 = ofs; + /* + * byte 0 thru (ofs-1) are the same between + * lo and hi; ofs is the first byte that is + * different. + */ + hiv = hi_key[ofs_0]; + if (ofs_0 < 19) + hiv = (hiv << 8) | hi_key[ofs_0+1]; + } else { + hiv = 256; + if (ofs_0 < 19) + hiv <<= 8; + } + lov = lo_key[ofs_0]; + kyv = key[ofs_0]; + if (ofs_0 < 19) { + lov = (lov << 8) | lo_key[ofs_0+1]; + kyv = (kyv << 8) | key[ofs_0+1]; + } + assert(lov < hiv); + + if (kyv < lov) + return -1 - lo; + if (hiv < kyv) + return -1 - hi; + + /* + * Even if we know the target is much closer to 'hi' + * than 'lo', if we pick too precisely and overshoot + * (e.g. when we know 'mi' is closer to 'hi' than to + * 'lo', pick 'mi' that is higher than the target), we + * end up narrowing the search space by a smaller + * amount (i.e. the distance between 'mi' and 'hi') + * than what we would have (i.e. about half of 'lo' + * and 'hi'). Hedge our bets to pick 'mi' less + * aggressively, i.e. make 'mi' a bit closer to the + * middle than we would otherwise pick. + */ + kyv = (kyv * 6 + lov + hiv) / 8; + if (lov < hiv - 1) { + if (kyv == lov) + kyv++; + else if (kyv == hiv) + kyv--; + } + mi = (range - 1) * (kyv - lov) / (hiv - lov) + lo; + + if (debug_lookup) { + printf("lo %u hi %u rg %u mi %u ", lo, hi, range, mi); + printf("ofs %u lov %x, hiv %x, kyv %x\n", + ofs_0, lov, hiv, kyv); + } + if (!(lo <= mi && mi < hi)) + die("assertion failure lo %u mi %u hi %u %s", + lo, mi, hi, sha1_to_hex(key)); + + mi_key = base + elem_size * mi + key_offset; + cmp = memcmp(mi_key + ofs_0, key + ofs_0, 20 - ofs_0); + if (!cmp) + return mi; + if (cmp > 0) { + hi = mi; + hi_key = mi_key; + } else { + lo = mi + 1; + lo_key = mi_key + elem_size; + } + } while (lo < hi); + return -lo-1; +} diff --git a/sha1-lookup.h b/sha1-lookup.h new file mode 100644 index 0000000000..3249a81b3d --- /dev/null +++ b/sha1-lookup.h @@ -0,0 +1,9 @@ +#ifndef SHA1_LOOKUP_H +#define SHA1_LOOKUP_H + +extern int sha1_entry_pos(const void *table, + size_t elem_size, + size_t key_offset, + unsigned lo, unsigned hi, unsigned nr, + const unsigned char *key); +#endif diff --git a/sha1_file.c b/sha1_file.c index 445a871db3..c2ab7ea11d 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -15,6 +15,7 @@ #include "tree.h" #include "refs.h" #include "pack-revindex.h" +#include "sha1-lookup.h" #ifndef O_NOATIME #if defined(__linux__) && (defined(__i386__) || defined(__PPC__)) @@ -1675,7 +1676,12 @@ off_t find_pack_entry_one(const unsigned char *sha1, { const uint32_t *level1_ofs = p->index_data; const unsigned char *index = p->index_data; - unsigned hi, lo; + unsigned hi, lo, stride; + static int use_lookup = -1; + static int debug_lookup = -1; + + if (debug_lookup < 0) + debug_lookup = !!getenv("GIT_DEBUG_LOOKUP"); if (!index) { if (open_pack_index(p)) @@ -1690,11 +1696,34 @@ off_t find_pack_entry_one(const unsigned char *sha1, index += 4 * 256; hi = ntohl(level1_ofs[*sha1]); lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1])); + if (p->index_version > 1) { + stride = 20; + } else { + stride = 24; + index += 4; + } + + if (debug_lookup) + printf("%02x%02x%02x... lo %u hi %u nr %u\n", + sha1[0], sha1[1], sha1[2], lo, hi, p->num_objects); + + if (use_lookup < 0) + use_lookup = !!getenv("GIT_USE_LOOKUP"); + if (use_lookup) { + int pos = sha1_entry_pos(index, stride, 0, + lo, hi, p->num_objects, sha1); + if (pos < 0) + return 0; + return nth_packed_object_offset(p, pos); + } do { unsigned mi = (lo + hi) / 2; - unsigned x = (p->index_version > 1) ? (mi * 20) : (mi * 24 + 4); - int cmp = hashcmp(index + x, sha1); + int cmp = hashcmp(index + mi * stride, sha1); + + if (debug_lookup) + printf("lo %u hi %u rg %u mi %u\n", + lo, hi, hi - lo, mi); if (!cmp) return nth_packed_object_offset(p, mi); if (cmp > 0) diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index b36a9012ec..a675cbb51b 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -595,6 +595,64 @@ test_expect_success 'set --int' ' rm .git/config +cat >expect <<\EOF +[bool] + true1 = true + true2 = true + false1 = false + false2 = false +[int] + int1 = 0 + int2 = 1 + int3 = -1 +EOF + +test_expect_success 'get --bool-or-int' ' + ( + echo "[bool]" + echo true1 + echo true2 = true + echo false = false + echo "[int]" + echo int1 = 0 + echo int2 = 1 + echo int3 = -1 + ) >>.git/config && + test $(git config --bool-or-int bool.true1) = true && + test $(git config --bool-or-int bool.true2) = true && + test $(git config --bool-or-int bool.false) = false && + test $(git config --bool-or-int int.int1) = 0 && + test $(git config --bool-or-int int.int2) = 1 && + test $(git config --bool-or-int int.int3) = -1 + +' + +rm .git/config +cat >expect <<\EOF +[bool] + true1 = true + false1 = false + true2 = true + false2 = false +[int] + int1 = 0 + int2 = 1 + int3 = -1 +EOF + +test_expect_success 'set --bool-or-int' ' + git config --bool-or-int bool.true1 true && + git config --bool-or-int bool.false1 false && + git config --bool-or-int bool.true2 yes && + git config --bool-or-int bool.false2 no && + git config --bool-or-int int.int1 0 && + git config --bool-or-int int.int2 1 && + git config --bool-or-int int.int3 -1 && + test_cmp expect .git/config +' + +rm .git/config + git config quote.leading " test" git config quote.ending "test " git config quote.semicolon "test;test" diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh index 6bfe19a4e5..5e4252a320 100755 --- a/t/t1301-shared-repo.sh +++ b/t/t1301-shared-repo.sh @@ -7,6 +7,16 @@ test_description='Test shared repository initialization' . ./test-lib.sh +# User must have read permissions to the repo -> failure on --shared=0400 +test_expect_success 'shared = 0400 (faulty permission u-w)' ' + mkdir sub && ( + cd sub && git init --shared=0400 + ) + ret="$?" + rm -rf sub + test $ret != "0" +' + test_expect_success 'shared=all' ' mkdir sub && cd sub && @@ -33,4 +43,44 @@ test_expect_success 'update-server-info honors core.sharedRepository' ' esac ' +for u in 0660:rw-rw---- \ + 0640:rw-r----- \ + 0600:rw------- \ + 0666:rw-rw-rw- \ + 0664:rw-rw-r-- +do + x=$(expr "$u" : ".*:\([rw-]*\)") && + y=$(echo "$x" | sed -e "s/w/-/g") && + u=$(expr "$u" : "\([0-7]*\)") && + git config core.sharedrepository "$u" && + umask 0277 && + + test_expect_success "shared = $u ($y) ro" ' + + rm -f .git/info/refs && + git update-server-info && + actual="$(ls -l .git/info/refs)" && + actual=${actual%% *} && + test "x$actual" = "x-$y" || { + ls -lt .git/info + false + } + ' + + umask 077 && + test_expect_success "shared = $u ($x) rw" ' + + rm -f .git/info/refs && + git update-server-info && + actual="$(ls -l .git/info/refs)" && + actual=${actual%% *} && + test "x$actual" = "x-$x" || { + ls -lt .git/info + false + } + + ' + +done + test_done diff --git a/t/t2002-checkout-cache-u.sh b/t/t2002-checkout-cache-u.sh index 0f441bcef7..70361c806e 100755 --- a/t/t2002-checkout-cache-u.sh +++ b/t/t2002-checkout-cache-u.sh @@ -21,13 +21,13 @@ test_expect_success \ rm -f path0 && git read-tree $t && git checkout-index -f -a && -! git diff-files | diff - /dev/null' +test_must_fail git diff-files --exit-code' test_expect_success \ 'with -u, git checkout-index picks up stat information from new files.' ' rm -f path0 && git read-tree $t && git checkout-index -u -f -a && -git diff-files | diff - /dev/null' +git diff-files --exit-code' test_done diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 77c90f6fa0..f15be93e77 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -66,4 +66,23 @@ test_expect_success 'revert works (commit)' ' grep "unchanged *+3/-0 file" output ' +test_expect_success 'patch does not affect mode' ' + git reset --hard && + echo content >>file && + chmod +x file && + printf "n\\ny\\n" | git add -p && + git show :file | grep content && + git diff file | grep "new mode" +' + +test_expect_success 'stage mode but not hunk' ' + git reset --hard && + echo content >>file && + chmod +x file && + printf "y\\nn\\n" | git add -p && + git diff --cached file | grep "new mode" && + git diff file | grep "+content" +' + + test_done diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index c955fe44f5..983a39398f 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -274,4 +274,99 @@ test_expect_success \ packname_4=$(git pack-objects test-4 <obj-list) && test 3 = $(ls test-4-*.pack | wc -l)' +test_expect_success 'unpacking with --strict' ' + + git config --unset pack.packsizelimit && + for j in a b c d e f g + do + for i in 0 1 2 3 4 5 6 7 8 9 + do + o=$(echo $j$i | git hash-object -w --stdin) && + echo "100644 $o 0 $j$i" + done + done >LIST && + rm -f .git/index && + git update-index --index-info <LIST && + LIST=$(git write-tree) && + rm -f .git/index && + head -n 10 LIST | git update-index --index-info && + LI=$(git write-tree) && + rm -f .git/index && + tail -n 10 LIST | git update-index --index-info && + ST=$(git write-tree) && + PACK5=$( git rev-list --objects "$LIST" "$LI" "$ST" | \ + git pack-objects test-5 ) && + PACK6=$( ( + echo "$LIST" + echo "$LI" + echo "$ST" + ) | git pack-objects test-6 ) && + test_create_repo test-5 && + ( + cd test-5 && + git unpack-objects --strict <../test-5-$PACK5.pack && + git ls-tree -r $LIST && + git ls-tree -r $LI && + git ls-tree -r $ST + ) && + test_create_repo test-6 && + ( + # tree-only into empty repo -- many unreachables + cd test-6 && + test_must_fail git unpack-objects --strict <../test-6-$PACK6.pack + ) && + ( + # already populated -- no unreachables + cd test-5 && + git unpack-objects --strict <../test-6-$PACK6.pack + ) +' + +test_expect_success 'index-pack with --strict' ' + + for j in a b c d e f g + do + for i in 0 1 2 3 4 5 6 7 8 9 + do + o=$(echo $j$i | git hash-object -w --stdin) && + echo "100644 $o 0 $j$i" + done + done >LIST && + rm -f .git/index && + git update-index --index-info <LIST && + LIST=$(git write-tree) && + rm -f .git/index && + head -n 10 LIST | git update-index --index-info && + LI=$(git write-tree) && + rm -f .git/index && + tail -n 10 LIST | git update-index --index-info && + ST=$(git write-tree) && + PACK5=$( git rev-list --objects "$LIST" "$LI" "$ST" | \ + git pack-objects test-5 ) && + PACK6=$( ( + echo "$LIST" + echo "$LI" + echo "$ST" + ) | git pack-objects test-6 ) && + test_create_repo test-7 && + ( + cd test-7 && + git index-pack --strict --stdin <../test-5-$PACK5.pack && + git ls-tree -r $LIST && + git ls-tree -r $LI && + git ls-tree -r $ST + ) && + test_create_repo test-8 && + ( + # tree-only into empty repo -- many unreachables + cd test-8 && + test_must_fail git index-pack --strict --stdin <../test-6-$PACK6.pack + ) && + ( + # already populated -- no unreachables + cd test-7 && + git index-pack --strict --stdin <../test-6-$PACK6.pack + ) +' + test_done diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh index b9a22190e8..bf12dbdeef 100755 --- a/t/t7401-submodule-summary.sh +++ b/t/t7401-submodule-summary.sh @@ -192,4 +192,17 @@ test_expect_success 'given commit' " EOF " +test_expect_success '--for-status' " + git submodule summary --for-status HEAD^ >actual && + test_cmp actual - <<EOF +# Modified submodules: +# +# * sm1 $head6...0000000: +# +# * sm2 0000000...$head7 (2): +# > Add foo9 +# +EOF +" + test_done diff --git a/t/t7502-status.sh b/t/t7502-status.sh index cd08516e6d..e4bfcaece0 100755 --- a/t/t7502-status.sh +++ b/t/t7502-status.sh @@ -149,4 +149,138 @@ test_expect_success 'status of partial commit excluding new file in index' ' test_cmp expect output ' +test_expect_success 'setup status submodule summary' ' + test_create_repo sm && ( + cd sm && + >foo && + git add foo && + git commit -m "Add foo" + ) && + git add sm +' + +cat >expect <<EOF +# On branch master +# Changes to be committed: +# (use "git reset HEAD <file>..." to unstage) +# +# new file: dir2/added +# new file: sm +# +# Changed but not updated: +# (use "git add <file>..." to update what will be committed) +# +# modified: dir1/modified +# +# Untracked files: +# (use "git add <file>..." to include in what will be committed) +# +# dir1/untracked +# dir2/modified +# dir2/untracked +# expect +# output +# untracked +EOF +test_expect_success 'status submodule summary is disabled by default' ' + git status >output && + test_cmp expect output +' + +head=$(cd sm && git rev-parse --short=7 --verify HEAD) + +cat >expect <<EOF +# On branch master +# Changes to be committed: +# (use "git reset HEAD <file>..." to unstage) +# +# new file: dir2/added +# new file: sm +# +# Changed but not updated: +# (use "git add <file>..." to update what will be committed) +# +# modified: dir1/modified +# +# Modified submodules: +# +# * sm 0000000...$head (1): +# > Add foo +# +# Untracked files: +# (use "git add <file>..." to include in what will be committed) +# +# dir1/untracked +# dir2/modified +# dir2/untracked +# expect +# output +# untracked +EOF +test_expect_success 'status submodule summary' ' + git config status.submodulesummary 10 && + git status >output && + test_cmp expect output +' + + +cat >expect <<EOF +# On branch master +# Changed but not updated: +# (use "git add <file>..." to update what will be committed) +# +# modified: dir1/modified +# +# Untracked files: +# (use "git add <file>..." to include in what will be committed) +# +# dir1/untracked +# dir2/modified +# dir2/untracked +# expect +# output +# untracked +no changes added to commit (use "git add" and/or "git commit -a") +EOF +test_expect_success 'status submodule summary (clean submodule)' ' + git commit -m "commit submodule" && + git config status.submodulesummary 10 && + test_must_fail git status >output && + test_cmp expect output +' + +cat >expect <<EOF +# On branch master +# Changes to be committed: +# (use "git reset HEAD^1 <file>..." to unstage) +# +# new file: dir2/added +# new file: sm +# +# Changed but not updated: +# (use "git add <file>..." to update what will be committed) +# +# modified: dir1/modified +# +# Modified submodules: +# +# * sm 0000000...$head (1): +# > Add foo +# +# Untracked files: +# (use "git add <file>..." to include in what will be committed) +# +# dir1/untracked +# dir2/modified +# dir2/untracked +# expect +# output +# untracked +EOF +test_expect_success 'status submodule summary (--amend)' ' + git config status.submodulesummary 10 && + git status --amend >output && + test_cmp expect output +' + test_done @@ -51,11 +51,12 @@ static int show_config(const char *var, const char *value) int main(int argc, char **argv) { const char *val; + int nongit; if (argc != 2) { usage(var_usage); } - setup_git_directory(); + setup_git_directory_gently(&nongit); val = NULL; if (strcmp(argv[1], "-l") == 0) { diff --git a/write_or_die.c b/write_or_die.c index e125e11d3b..32f9914020 100644 --- a/write_or_die.c +++ b/write_or_die.c @@ -40,7 +40,7 @@ void maybe_flush_or_die(FILE *f, const char *desc) } } -int read_in_full(int fd, void *buf, size_t count) +ssize_t read_in_full(int fd, void *buf, size_t count) { char *p = buf; ssize_t total = 0; @@ -57,7 +57,7 @@ int read_in_full(int fd, void *buf, size_t count) return total; } -int write_in_full(int fd, const void *buf, size_t count) +ssize_t write_in_full(int fd, const void *buf, size_t count) { const char *p = buf; ssize_t total = 0; diff --git a/wt-status.c b/wt-status.c index b3fd57b79d..532b4ea2c1 100644 --- a/wt-status.c +++ b/wt-status.c @@ -8,9 +8,11 @@ #include "revision.h" #include "diffcore.h" #include "quote.h" +#include "run-command.h" int wt_status_relative_paths = 1; int wt_status_use_color = -1; +int wt_status_submodule_summary; static char wt_status_colors[][COLOR_MAXLEN] = { "", /* WT_STATUS_HEADER: normal */ "\033[32m", /* WT_STATUS_UPDATED: green */ @@ -220,6 +222,36 @@ static void wt_status_print_changed(struct wt_status *s) run_diff_files(&rev, 0); } +static void wt_status_print_submodule_summary(struct wt_status *s) +{ + struct child_process sm_summary; + char summary_limit[64]; + char index[PATH_MAX]; + const char *env[] = { index, NULL }; + const char *argv[] = { + "submodule", + "summary", + "--cached", + "--for-status", + "--summary-limit", + summary_limit, + s->amend ? "HEAD^" : "HEAD", + NULL + }; + + sprintf(summary_limit, "%d", wt_status_submodule_summary); + snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file); + + memset(&sm_summary, 0, sizeof(sm_summary)); + sm_summary.argv = argv; + sm_summary.env = env; + sm_summary.git_cmd = 1; + sm_summary.no_stdin = 1; + fflush(s->fp); + sm_summary.out = dup(fileno(s->fp)); /* run_command closes it */ + run_command(&sm_summary); +} + static void wt_status_print_untracked(struct wt_status *s) { struct dir_struct dir; @@ -308,6 +340,8 @@ void wt_status_print(struct wt_status *s) } wt_status_print_changed(s); + if (wt_status_submodule_summary) + wt_status_print_submodule_summary(s); wt_status_print_untracked(s); if (s->verbose && !s->is_initial) @@ -330,6 +364,13 @@ void wt_status_print(struct wt_status *s) int git_status_config(const char *k, const char *v) { + if (!strcmp(k, "status.submodulesummary")) { + int is_bool; + wt_status_submodule_summary = git_config_bool_or_int(k, v, &is_bool); + if (is_bool && wt_status_submodule_summary) + wt_status_submodule_summary = -1; + return 0; + } if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) { wt_status_use_color = git_config_colorbool(k, v, -1); return 0; |