diff options
70 files changed, 1768 insertions, 937 deletions
diff --git a/.gitignore b/.gitignore index 8670081adf..c8c13f5503 100644 --- a/.gitignore +++ b/.gitignore @@ -153,6 +153,7 @@ test-delta test-dump-cache-tree test-genrandom test-match-trees +test-parse-options test-sha1 common-cmds.h *.tar.gz diff --git a/Documentation/RelNotes-1.5.3.5.txt b/Documentation/RelNotes-1.5.3.5.txt index 4e46d2c2a2..f99a2cd650 100644 --- a/Documentation/RelNotes-1.5.3.5.txt +++ b/Documentation/RelNotes-1.5.3.5.txt @@ -63,8 +63,8 @@ Fixes since v1.5.3.4 * Git segfaulted when reading an invalid .gitattributes file. Fixed. - * post-receive-email example hook fixed was fixed for - non-fast-forward updates. + * post-receive-email example hook was fixed for non-fast-forward + updates. * Documentation updates for supported (but previously undocumented) options of "git-archive" and "git-reflog". diff --git a/Documentation/config.txt b/Documentation/config.txt index edf50cd211..0df004ea26 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -661,6 +661,15 @@ pack.threads:: machines. The required amount of memory for the delta search window is however multiplied by the number of threads. +pack.indexVersion:: + Specify the default pack index version. Valid values are 1 for + legacy pack index used by Git versions prior to 1.5.2, and 2 for + the new pack index with capabilities for packs larger than 4 GB + as well as proper protection against the repacking of corrupted + packs. Version 2 is selected and this config option ignored + whenever the corresponding pack is larger than 2 GB. Otherwise + the default is 1. + pull.octopus:: The default merge strategy to use when pulling multiple branches at once. diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt index 5df97a1f9d..99817c5337 100644 --- a/Documentation/core-tutorial.txt +++ b/Documentation/core-tutorial.txt @@ -828,7 +828,7 @@ that branch, and do some work there. ------------------------------------------------ $ git checkout mybranch $ echo "Work, work, work" >>hello -$ git commit -m 'Some work.' -i hello +$ git commit -m "Some work." -i hello ------------------------------------------------ Here, we just added another line to `hello`, and we used a shorthand for @@ -853,7 +853,7 @@ hasn't happened in the `master` branch at all. Then do ------------ $ echo "Play, play, play" >>hello $ echo "Lots of fun" >>example -$ git commit -m 'Some fun.' -i hello example +$ git commit -m "Some fun." -i hello example ------------ since the master branch is obviously in a much better mood. @@ -1607,8 +1607,8 @@ in both of them. You could merge in 'diff-fix' first and then 'commit-fix' next, like this: ------------ -$ git merge -m 'Merge fix in diff-fix' diff-fix -$ git merge -m 'Merge fix in commit-fix' commit-fix +$ git merge -m "Merge fix in diff-fix" diff-fix +$ git merge -m "Merge fix in commit-fix" commit-fix ------------ Which would result in: diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt index 001503205b..9709c35c98 100644 --- a/Documentation/diff-format.txt +++ b/Documentation/diff-format.txt @@ -1,5 +1,5 @@ -The output format from "git-diff-index", "git-diff-tree" and -"git-diff-files" are very similar. +The output format from "git-diff-index", "git-diff-tree", +"git-diff-files" and "git diff --raw" are very similar. These commands all compare two sets of things; what is compared differs: @@ -62,7 +62,8 @@ respectively. diff format for merges ---------------------- -"git-diff-tree" and "git-diff-files" can take '-c' or '--cc' option +"git-diff-tree", "git-diff-files" and "git-diff --raw" +can take '-c' or '--cc' option to generate diff output also for merge commits. The output differs from the format described above in the following way: @@ -86,10 +87,10 @@ Generating patches with -p -------------------------- When "git-diff-index", "git-diff-tree", or "git-diff-files" are run -with a '-p' option, they do not produce the output described above; -instead they produce a patch file. You can customize the creation -of such patches via the GIT_EXTERNAL_DIFF and the GIT_DIFF_OPTS -environment variables. +with a '-p' option, or "git diff" without the '--raw' option, they +do not produce the output described above; instead they produce a +patch file. You can customize the creation of such patches via the +GIT_EXTERNAL_DIFF and the GIT_DIFF_OPTS environment variables. What the -p option produces is slightly different from the traditional diff format. @@ -137,8 +138,8 @@ file made it into the new one. combined diff format -------------------- -git-diff-tree and git-diff-files can take '-c' or '--cc' option -to produce 'combined diff', which looks like this: +"git-diff-tree", "git-diff-files" and "git-diff" can take '-c' or +'--cc' option to produce 'combined diff', which looks like this: ------------ diff --combined describe.c diff --git a/Documentation/everyday.txt b/Documentation/everyday.txt index 08c61b1f1a..ce7c170d69 100644 --- a/Documentation/everyday.txt +++ b/Documentation/everyday.txt @@ -109,7 +109,7 @@ $ tar zxf frotz.tar.gz $ cd frotz $ git-init $ git add . <1> -$ git commit -m 'import of frotz source tree.' +$ git commit -m "import of frotz source tree." $ git tag v2.43 <2> ------------ + @@ -300,7 +300,7 @@ $ git merge topic/one topic/two && git merge hold/linus <8> $ git checkout maint $ git cherry-pick master~4 <9> $ compile/test -$ git tag -s -m 'GIT 0.99.9x' v0.99.9x <10> +$ git tag -s -m "GIT 0.99.9x" v0.99.9x <10> $ git fetch ko && git show-branch master maint 'tags/ko-*' <11> $ git push ko <12> $ git push ko v0.99.9x <13> diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 2fe7355555..963e1ab1e2 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -50,10 +50,10 @@ OPTIONS and `dir/file2`) can be given to add all files in the directory, recursively. --n:: +-n, \--dry-run:: Don't actually add the file(s), just show if they exist. --v:: +-v, \--verbose:: Be verbose. -f:: diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index b7285bcdbc..5e81aa4ee1 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -85,7 +85,7 @@ OPTIONS -a:: List both remote-tracking branches and local branches. --v:: +-v, --verbose:: Show sha1 and commit subject line for each head. --abbrev=<length>:: diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt index 76a2edfd9b..937c4a7926 100644 --- a/Documentation/git-cherry-pick.txt +++ b/Documentation/git-cherry-pick.txt @@ -7,7 +7,7 @@ git-cherry-pick - Apply the change introduced by an existing commit SYNOPSIS -------- -'git-cherry-pick' [--edit] [-n] [-x] <commit> +'git-cherry-pick' [--edit] [-n] [-m parent-number] [-x] <commit> DESCRIPTION ----------- @@ -44,6 +44,13 @@ OPTIONS described above, and `-r` was to disable it. Now the default is not to do `-x` so this option is a no-op. +-m parent-number|--mainline parent-number:: + Usually you cannot revert a merge because you do not know which + side of the merge should be considered the mainline. This + option specifies the parent number (starting from 1) of + the mainline and allows cherry-pick to replay the change + relative to the specified parent. + -n|--no-commit:: Usually the command automatically creates a commit with a commit log message stating which commit was diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index cca14d6b5d..14e58f3866 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -12,7 +12,7 @@ SYNOPSIS 'git-clone' [--template=<template_directory>] [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [-o <name>] [-u <upload-pack>] [--reference <repository>] - [--depth <depth>] <repository> [<directory>] + [--depth <depth>] [--] <repository> [<directory>] DESCRIPTION ----------- diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt index ce0f502468..11c4216c4a 100644 --- a/Documentation/git-diff.txt +++ b/Documentation/git-diff.txt @@ -82,6 +82,9 @@ include::diff-options.txt[] the diff to the named paths (you can give directory names and get diff for all files under them). +Output format +------------- +include::diff-format.txt[] EXAMPLES -------- diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index 385ecc900f..895d750310 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -152,7 +152,7 @@ definition impossible to preserve signatures at any rate.) does this in the '.git-rewrite/' directory but you can override that choice by this parameter. --f\|--force:: +-f|--force:: `git filter-branch` refuses to start with an existing temporary directory or when there are already refs starting with 'refs/original/', unless forced. diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt index 2c9cf743c7..3b8ca76dff 100644 --- a/Documentation/git-mv.txt +++ b/Documentation/git-mv.txt @@ -34,7 +34,7 @@ OPTIONS condition. An error happens when a source is neither existing nor controlled by GIT, or when it would overwrite an existing file unless '-f' is given. --n:: +-n, \--dry-run:: Do nothing; only show what would happen diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index 15e3aca9a1..87afa6f8da 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -157,7 +157,7 @@ need to get to the other branch for a quick bugfix. ------------ $ git checkout feature ;# you were working in "feature" branch and $ work work work ;# got interrupted -$ git commit -a -m 'snapshot WIP' <1> +$ git commit -a -m "snapshot WIP" <1> $ git checkout master $ fix fix fix $ git commit ;# commit with real log diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt index 69db498447..3457c40787 100644 --- a/Documentation/git-revert.txt +++ b/Documentation/git-revert.txt @@ -7,7 +7,7 @@ git-revert - Revert an existing commit SYNOPSIS -------- -'git-revert' [--edit | --no-edit] [-n] <commit> +'git-revert' [--edit | --no-edit] [-n] [-m parent-number] <commit> DESCRIPTION ----------- @@ -27,6 +27,13 @@ OPTIONS message prior committing the revert. This is the default if you run the command from a terminal. +-m parent-number|--mainline parent-number:: + Usually you cannot revert a merge because you do not know which + side of the merge should be considered the mainline. This + option specifies the parent number (starting from 1) of + the mainline and allows revert to reverse the change + relative to the specified parent. + --no-edit:: With this option, `git-revert` will not start the commit message editor. diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt index be61a82164..48c1d97f93 100644 --- a/Documentation/git-rm.txt +++ b/Documentation/git-rm.txt @@ -30,7 +30,7 @@ OPTIONS -f:: Override the up-to-date check. --n:: +-n, \--dry-run:: Don't actually remove the file(s), just show if they exist in the index. @@ -51,7 +51,7 @@ OPTIONS \--ignore-unmatch:: Exit with a zero status even if no files matched. -\--quiet:: +-q, \--quiet:: git-rm normally outputs one line (in the form of an "rm" command) for each file removed. This option suppresses that output. diff --git a/Documentation/git-stripspace.txt b/Documentation/git-stripspace.txt index 5212358306..f80526ba7e 100644 --- a/Documentation/git-stripspace.txt +++ b/Documentation/git-stripspace.txt @@ -16,7 +16,7 @@ Remove multiple empty lines, and empty lines at beginning and end. OPTIONS ------- --s\|--strip-comments:: +-s|--strip-comments:: In addition to empty lines, also strip lines starting with '#'. <stream>:: diff --git a/Documentation/git-symbolic-ref.txt b/Documentation/git-symbolic-ref.txt index a88f722860..694cabab24 100644 --- a/Documentation/git-symbolic-ref.txt +++ b/Documentation/git-symbolic-ref.txt @@ -26,7 +26,7 @@ a regular file whose contents is `ref: refs/heads/master`. OPTIONS ------- --q:: +-q, --quiet:: Do not issue an error message if the <name> is not a symbolic ref but a detached HEAD; instead exit with non-zero status silently. @@ -289,7 +289,7 @@ LIB_H = \ run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \ tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \ utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \ - mailmap.h remote.h transport.h diffcore.h hash.h + mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h DIFF_OBJS = \ diff.o diff-lib.o diffcore-break.o diffcore-order.o \ @@ -312,7 +312,7 @@ LIB_OBJS = \ alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \ color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \ convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \ - transport.o bundle.o walker.o + transport.o bundle.o walker.o parse-options.o BUILTIN_OBJS = \ builtin-add.o \ @@ -974,7 +974,7 @@ endif ### Testing rules -TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X +TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X test-parse-options$X all:: $(TEST_PROGRAMS) diff --git a/builtin-add.c b/builtin-add.c index dbbb05215f..45b14e8a61 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -13,9 +13,12 @@ #include "commit.h" #include "revision.h" #include "run-command.h" +#include "parse-options.h" -static const char builtin_add_usage[] = -"git-add [-n] [-v] [-f] [--interactive | -i] [-u] [--refresh] [--] <filepattern>..."; +static const char * const builtin_add_usage[] = { + "git-add [options] [--] <filepattern>...", + NULL +}; static int take_worktree_changes; static const char *excludes_file; @@ -162,21 +165,30 @@ static struct lock_file lock_file; static const char ignore_error[] = "The following paths are ignored by one of your .gitignore files:\n"; +static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0; +static int add_interactive = 0; + +static struct option builtin_add_options[] = { + OPT__DRY_RUN(&show_only), + OPT__VERBOSE(&verbose), + OPT_GROUP(""), + OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"), + OPT_BOOLEAN('f', NULL, &ignored_too, "allow adding otherwise ignored files"), + OPT_BOOLEAN('u', NULL, &take_worktree_changes, "update tracked files"), + OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"), + OPT_END(), +}; + int cmd_add(int argc, const char **argv, const char *prefix) { - int i, newfd; - int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0; + int i, newfd, orig_argc = argc; const char **pathspec; struct dir_struct dir; - int add_interactive = 0; - for (i = 1; i < argc; i++) { - if (!strcmp("--interactive", argv[i]) || - !strcmp("-i", argv[i])) - add_interactive++; - } + argc = parse_options(argc, argv, builtin_add_options, + builtin_add_usage, 0); if (add_interactive) { - if (argc != 2) + if (add_interactive != 1 || orig_argc != 2) die("add --interactive does not take any parameters"); exit(interactive_add()); } @@ -185,51 +197,19 @@ int cmd_add(int argc, const char **argv, const char *prefix) newfd = hold_locked_index(&lock_file, 1); - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (arg[0] != '-') - break; - if (!strcmp(arg, "--")) { - i++; - break; - } - if (!strcmp(arg, "-n")) { - show_only = 1; - continue; - } - if (!strcmp(arg, "-f")) { - ignored_too = 1; - continue; - } - if (!strcmp(arg, "-v")) { - verbose = 1; - continue; - } - if (!strcmp(arg, "-u")) { - take_worktree_changes = 1; - continue; - } - if (!strcmp(arg, "--refresh")) { - refresh_only = 1; - continue; - } - usage(builtin_add_usage); - } - if (take_worktree_changes) { if (read_cache() < 0) die("index file corrupt"); - add_files_to_cache(verbose, prefix, argv + i); + add_files_to_cache(verbose, prefix, argv); goto finish; } - if (argc <= i) { + if (argc == 0) { fprintf(stderr, "Nothing specified, nothing added.\n"); fprintf(stderr, "Maybe you wanted to say 'git add .'?\n"); return 0; } - pathspec = get_pathspec(prefix, argv + i); + pathspec = get_pathspec(prefix, argv); if (refresh_only) { refresh(verbose, pathspec); diff --git a/builtin-blame.c b/builtin-blame.c index 8432b823e6..aedc294eac 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -2215,9 +2215,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix) argv[unk++] = arg; } - if (!incremental) - setup_pager(); - if (!blame_move_score) blame_move_score = BLAME_DEFAULT_MOVE_SCORE; if (!blame_copy_score) @@ -2411,6 +2408,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix) read_mailmap(&mailmap, ".mailmap", NULL); + if (!incremental) + setup_pager(); + assign_blame(&sb, &revs, opt); if (incremental) diff --git a/builtin-branch.c b/builtin-branch.c index 3e020ccf17..3bf40f1453 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -11,9 +11,15 @@ #include "commit.h" #include "builtin.h" #include "remote.h" - -static const char builtin_branch_usage[] = - "git-branch [-r] (-d | -D) <branchname> | [--track | --no-track] [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]]"; +#include "parse-options.h" + +static const char * const builtin_branch_usage[] = { + "git-branch [options] [-r | -a]", + "git-branch [options] [-l] [-f] <branchname> [<start-point>]", + "git-branch [options] [-r] (-d | -D) <branchname>", + "git-branch [options] (-m | -M) [<oldbranch>] <newbranch>", + NULL +}; #define REF_UNKNOWN_TYPE 0x00 #define REF_LOCAL_BRANCH 0x01 @@ -142,7 +148,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) if (!force && !in_merge_bases(rev, &head_rev, 1)) { - error("The branch '%s' is not a strict subset of " + error("The branch '%s' is not an ancestor of " "your current HEAD.\n" "If you are sure you want to delete it, " "run 'git branch -D %s'.", argv[i], argv[i]); @@ -505,93 +511,45 @@ int cmd_branch(int argc, const char **argv, const char *prefix) int rename = 0, force_rename = 0; int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0; int reflog = 0, track; - int kinds = REF_LOCAL_BRANCH; - int i; + int kinds = REF_LOCAL_BRANCH, kind_remote = 0, kind_any = 0; + + struct option options[] = { + OPT_GROUP("Generic options"), + OPT__VERBOSE(&verbose), + OPT_BOOLEAN( 0 , "track", &track, "set up tracking mode (see git-pull(1))"), + OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"), + OPT_BOOLEAN('r', NULL, &kind_remote, "act on remote-tracking branches"), + OPT__ABBREV(&abbrev), + + OPT_GROUP("Specific git-branch actions:"), + OPT_BOOLEAN('a', NULL, &kind_any, "list both remote-tracking and local branches"), + OPT_BOOLEAN('d', NULL, &delete, "delete fully merged branch"), + OPT_BOOLEAN('D', NULL, &force_delete, "delete branch (even if not merged)"), + OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"), + OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"), + OPT_BOOLEAN('m', NULL, &rename, "move/rename a branch and its reflog"), + OPT_BOOLEAN('M', NULL, &force_rename, "move/rename a branch, even if target exists"), + OPT_END(), + }; git_config(git_branch_config); track = branch_track; - - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (arg[0] != '-') - break; - if (!strcmp(arg, "--")) { - i++; - break; - } - if (!strcmp(arg, "--track")) { - track = 1; - continue; - } - if (!strcmp(arg, "--no-track")) { - track = 0; - continue; - } - if (!strcmp(arg, "-d")) { - delete = 1; - continue; - } - if (!strcmp(arg, "-D")) { - delete = 1; - force_delete = 1; - continue; - } - if (!strcmp(arg, "-f")) { - force_create = 1; - continue; - } - if (!strcmp(arg, "-m")) { - rename = 1; - continue; - } - if (!strcmp(arg, "-M")) { - rename = 1; - force_rename = 1; - continue; - } - if (!strcmp(arg, "-r")) { - kinds = REF_REMOTE_BRANCH; - continue; - } - if (!strcmp(arg, "-a")) { - kinds = REF_REMOTE_BRANCH | REF_LOCAL_BRANCH; - continue; - } - if (!strcmp(arg, "-l")) { - reflog = 1; - continue; - } - if (!prefixcmp(arg, "--no-abbrev")) { - abbrev = 0; - continue; - } - if (!prefixcmp(arg, "--abbrev=")) { - abbrev = strtoul(arg + 9, NULL, 10); - if (abbrev < MINIMUM_ABBREV) - abbrev = MINIMUM_ABBREV; - else if (abbrev > 40) - abbrev = 40; - continue; - } - if (!strcmp(arg, "-v")) { - verbose = 1; - continue; - } - if (!strcmp(arg, "--color")) { - branch_use_color = 1; - continue; - } - if (!strcmp(arg, "--no-color")) { - branch_use_color = 0; - continue; - } - usage(builtin_branch_usage); - } + argc = parse_options(argc, argv, options, builtin_branch_usage, 0); + + delete |= force_delete; + rename |= force_rename; + if (kind_remote) + kinds = REF_REMOTE_BRANCH; + if (kind_any) + kinds = REF_REMOTE_BRANCH | REF_LOCAL_BRANCH; + if (abbrev && abbrev < MINIMUM_ABBREV) + abbrev = MINIMUM_ABBREV; + else if (abbrev > 40) + abbrev = 40; if ((delete && rename) || (delete && force_create) || (rename && force_create)) - usage(builtin_branch_usage); + usage_with_options(builtin_branch_usage, options); head = resolve_ref("HEAD", head_sha1, 0, NULL); if (!head) @@ -599,26 +557,25 @@ int cmd_branch(int argc, const char **argv, const char *prefix) head = xstrdup(head); if (!strcmp(head, "HEAD")) { detached = 1; - } - else { + } else { if (prefixcmp(head, "refs/heads/")) die("HEAD not found below refs/heads!"); head += 11; } if (delete) - return delete_branches(argc - i, argv + i, force_delete, kinds); - else if (i == argc) + return delete_branches(argc, argv, force_delete, kinds); + else if (argc == 0) print_ref_list(kinds, detached, verbose, abbrev); - else if (rename && (i == argc - 1)) - rename_branch(head, argv[i], force_rename); - else if (rename && (i == argc - 2)) - rename_branch(argv[i], argv[i + 1], force_rename); - else if (i == argc - 1 || i == argc - 2) - create_branch(argv[i], (i == argc - 2) ? argv[i+1] : head, + else if (rename && (argc == 1)) + rename_branch(head, argv[0], force_rename); + else if (rename && (argc == 2)) + rename_branch(argv[0], argv[1], force_rename); + else if (argc <= 2) + create_branch(argv[0], (argc == 2) ? argv[1] : head, force_create, reflog, track); else - usage(builtin_branch_usage); + usage_with_options(builtin_branch_usage, options); return 0; } diff --git a/builtin-count-objects.c b/builtin-count-objects.c index 4274ec1950..f00306fb67 100644 --- a/builtin-count-objects.c +++ b/builtin-count-objects.c @@ -6,8 +6,7 @@ #include "cache.h" #include "builtin.h" - -static const char count_objects_usage[] = "git-count-objects [-v]"; +#include "parse-options.h" static void count_objects(DIR *d, char *path, int len, int verbose, unsigned long *loose, @@ -67,29 +66,28 @@ static void count_objects(DIR *d, char *path, int len, int verbose, } } -int cmd_count_objects(int ac, const char **av, const char *prefix) +static char const * const count_objects_usage[] = { + "git-count-objects [-v]", + NULL +}; + +int cmd_count_objects(int argc, const char **argv, const char *prefix) { - int i; - int verbose = 0; + int i, verbose = 0; const char *objdir = get_object_directory(); int len = strlen(objdir); char *path = xmalloc(len + 50); unsigned long loose = 0, packed = 0, packed_loose = 0, garbage = 0; unsigned long loose_size = 0; + struct option opts[] = { + OPT__VERBOSE(&verbose), + OPT_END(), + }; - for (i = 1; i < ac; i++) { - const char *arg = av[i]; - if (*arg != '-') - break; - else if (!strcmp(arg, "-v")) - verbose = 1; - else - usage(count_objects_usage); - } - + argc = parse_options(argc, argv, opts, count_objects_usage, 0); /* we do not take arguments other than flags for now */ - if (i < ac) - usage(count_objects_usage); + if (argc) + usage_with_options(count_objects_usage, opts); memcpy(path, objdir, len); if (len && objdir[len-1] != '/') path[len++] = '/'; diff --git a/builtin-describe.c b/builtin-describe.c index 669110cb06..6eeb9b5045 100644 --- a/builtin-describe.c +++ b/builtin-describe.c @@ -4,12 +4,15 @@ #include "refs.h" #include "builtin.h" #include "exec_cmd.h" +#include "parse-options.h" #define SEEN (1u<<0) #define MAX_TAGS (FLAG_BITS - 1) -static const char describe_usage[] = -"git-describe [--all] [--tags] [--abbrev=<n>] <committish>*"; +static const char * const describe_usage[] = { + "git-describe [options] <committish>*", + NULL +}; static int debug; /* Display lots of verbose info */ static int all; /* Default to annotated tags only */ @@ -242,57 +245,42 @@ static void describe(const char *arg, int last_one) int cmd_describe(int argc, const char **argv, const char *prefix) { - int i; int contains = 0; + struct option options[] = { + OPT_BOOLEAN(0, "contains", &contains, "find the tag that comes after the commit"), + OPT_BOOLEAN(0, "debug", &debug, "debug search strategy on stderr"), + OPT_BOOLEAN(0, "all", &all, "use any ref in .git/refs"), + OPT_BOOLEAN(0, "tags", &tags, "use any tag in .git/refs/tags"), + OPT__ABBREV(&abbrev), + OPT_INTEGER(0, "candidates", &max_candidates, + "consider <n> most recent tags (default: 10)"), + OPT_END(), + }; - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (*arg != '-') - break; - else if (!strcmp(arg, "--contains")) - contains = 1; - else if (!strcmp(arg, "--debug")) - debug = 1; - else if (!strcmp(arg, "--all")) - all = 1; - else if (!strcmp(arg, "--tags")) - tags = 1; - else if (!prefixcmp(arg, "--abbrev=")) { - abbrev = strtoul(arg + 9, NULL, 10); - if (abbrev != 0 && (abbrev < MINIMUM_ABBREV || 40 < abbrev)) - abbrev = DEFAULT_ABBREV; - } - else if (!prefixcmp(arg, "--candidates=")) { - max_candidates = strtoul(arg + 13, NULL, 10); - if (max_candidates < 1) - max_candidates = 1; - else if (max_candidates > MAX_TAGS) - max_candidates = MAX_TAGS; - } - else - usage(describe_usage); - } + argc = parse_options(argc, argv, options, describe_usage, 0); + if (max_candidates < 1) + max_candidates = 1; + else if (max_candidates > MAX_TAGS) + max_candidates = MAX_TAGS; save_commit_buffer = 0; if (contains) { - const char **args = xmalloc((4 + argc - i) * sizeof(char*)); + const char **args = xmalloc((4 + argc) * sizeof(char*)); args[0] = "name-rev"; args[1] = "--name-only"; args[2] = "--tags"; - memcpy(args + 3, argv + i, (argc - i) * sizeof(char*)); - args[3 + argc - i] = NULL; - return cmd_name_rev(3 + argc - i, args, prefix); + memcpy(args + 3, argv, argc * sizeof(char*)); + args[3 + argc] = NULL; + return cmd_name_rev(3 + argc, args, prefix); } - if (argc <= i) + if (argc == 0) { describe("HEAD", 1); - else - while (i < argc) { - describe(argv[i], (i == argc - 1)); - i++; + } else { + while (argc-- > 0) { + describe(*argv++, argc == 0); } - + } return 0; } diff --git a/builtin-fetch.c b/builtin-fetch.c index 003ed76d16..5f5b59bfdb 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -131,12 +131,6 @@ static struct ref *get_ref_map(struct transport *transport, return ref_map; } -static void show_new(enum object_type type, unsigned char *sha1_new) -{ - fprintf(stderr, " %s: %s\n", typename(type), - find_unique_abbrev(sha1_new, DEFAULT_ABBREV)); -} - static int s_update_ref(const char *action, struct ref *ref, int check_old) @@ -157,34 +151,38 @@ static int s_update_ref(const char *action, return 0; } +#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3) + static int update_local_ref(struct ref *ref, - const char *note, - int verbose) + const char *remote, + int verbose, + char *display) { - char oldh[41], newh[41]; struct commit *current = NULL, *updated; enum object_type type; struct branch *current_branch = branch_get(NULL); + const char *pretty_ref = ref->name + ( + !prefixcmp(ref->name, "refs/heads/") ? 11 : + !prefixcmp(ref->name, "refs/tags/") ? 10 : + !prefixcmp(ref->name, "refs/remotes/") ? 13 : + 0); + *display = 0; type = sha1_object_info(ref->new_sha1, NULL); if (type < 0) die("object %s not found", sha1_to_hex(ref->new_sha1)); if (!*ref->name) { /* Not storing */ - if (verbose) { - fprintf(stderr, "* fetched %s\n", note); - show_new(type, ref->new_sha1); - } + if (verbose) + sprintf(display, "* branch %s -> FETCH_HEAD", remote); return 0; } if (!hashcmp(ref->old_sha1, ref->new_sha1)) { - if (verbose) { - fprintf(stderr, "* %s: same as %s\n", - ref->name, note); - show_new(type, ref->new_sha1); - } + if (verbose) + sprintf(display, "= %-*s %s -> %s", SUMMARY_WIDTH, + "[up to date]", remote, pretty_ref); return 0; } @@ -196,63 +194,65 @@ static int update_local_ref(struct ref *ref, * If this is the head, and it's not okay to update * the head, and the old value of the head isn't empty... */ - fprintf(stderr, - " * %s: Cannot fetch into the current branch.\n", - ref->name); + sprintf(display, "! %-*s %s -> %s (can't fetch in current branch)", + SUMMARY_WIDTH, "[rejected]", remote, pretty_ref); return 1; } if (!is_null_sha1(ref->old_sha1) && !prefixcmp(ref->name, "refs/tags/")) { - fprintf(stderr, "* %s: updating with %s\n", - ref->name, note); - show_new(type, ref->new_sha1); + sprintf(display, "- %-*s %s -> %s", + SUMMARY_WIDTH, "[tag update]", remote, pretty_ref); return s_update_ref("updating tag", ref, 0); } current = lookup_commit_reference_gently(ref->old_sha1, 1); updated = lookup_commit_reference_gently(ref->new_sha1, 1); if (!current || !updated) { - char *msg; - if (!strncmp(ref->name, "refs/tags/", 10)) + const char *msg; + const char *what; + if (!strncmp(ref->name, "refs/tags/", 10)) { msg = "storing tag"; - else + what = "[new tag]"; + } + else { msg = "storing head"; - fprintf(stderr, "* %s: storing %s\n", - ref->name, note); - show_new(type, ref->new_sha1); + what = "[new branch]"; + } + + sprintf(display, "* %-*s %s -> %s", + SUMMARY_WIDTH, what, remote, pretty_ref); return s_update_ref(msg, ref, 0); } - strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); - strcpy(newh, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); - if (in_merge_bases(current, &updated, 1)) { - fprintf(stderr, "* %s: fast forward to %s\n", - ref->name, note); - fprintf(stderr, " old..new: %s..%s\n", oldh, newh); + char quickref[83]; + strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); + strcat(quickref, ".."); + strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); + sprintf(display, " %-*s %s -> %s (fast forward)", + SUMMARY_WIDTH, quickref, remote, pretty_ref); return s_update_ref("fast forward", ref, 1); - } - if (!force && !ref->force) { - fprintf(stderr, - "* %s: not updating to non-fast forward %s\n", - ref->name, note); - fprintf(stderr, - " old...new: %s...%s\n", oldh, newh); + } else if (force || ref->force) { + char quickref[84]; + strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); + strcat(quickref, "..."); + strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); + sprintf(display, "+ %-*s %s -> %s (forced update)", + SUMMARY_WIDTH, quickref, remote, pretty_ref); + return s_update_ref("forced-update", ref, 1); + } else { + sprintf(display, "! %-*s %s -> %s (non fast forward)", + SUMMARY_WIDTH, "[rejected]", remote, pretty_ref); return 1; } - fprintf(stderr, - "* %s: forcing update to non-fast forward %s\n", - ref->name, note); - fprintf(stderr, " old...new: %s...%s\n", oldh, newh); - return s_update_ref("forced-update", ref, 1); } static void store_updated_refs(const char *url, struct ref *ref_map) { FILE *fp; struct commit *commit; - int url_len, i, note_len; + int url_len, i, note_len, shown_url = 0; char note[1024]; const char *what, *kind; struct ref *rm; @@ -315,8 +315,17 @@ static void store_updated_refs(const char *url, struct ref *ref_map) rm->merge ? "" : "not-for-merge", note); - if (ref) - update_local_ref(ref, note, verbose); + if (ref) { + update_local_ref(ref, what, verbose, note); + if (*note) { + if (!shown_url) { + fprintf(stderr, "From %.*s\n", + url_len, url); + shown_url = 1; + } + fprintf(stderr, " %s\n", note); + } + } } fclose(fp); } @@ -376,9 +385,6 @@ static struct ref *find_non_local_tags(struct transport *transport, if (!path_list_has_path(&existing_refs, ref_name) && !path_list_has_path(&new_refs, ref_name) && lookup_object(ref->old_sha1)) { - fprintf(stderr, "Auto-following %s\n", - ref_name); - path_list_insert(ref_name, &new_refs); rm = alloc_ref(strlen(ref_name) + 1); @@ -517,7 +523,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) depth = argv[i]; continue; } - if (!strcmp(arg, "--quiet")) { + if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) { quiet = 1; continue; } diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index c74ef2800c..da8c7948e6 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -7,6 +7,7 @@ #include "tree.h" #include "blob.h" #include "quote.h" +#include "parse-options.h" /* Quoting styles */ #define QUOTE_NONE 0 @@ -158,17 +159,18 @@ static const char *find_next(const char *cp) * Make sure the format string is well formed, and parse out * the used atoms. */ -static void verify_format(const char *format) +static int verify_format(const char *format) { const char *cp, *sp; for (cp = format; *cp && (sp = find_next(cp)); ) { const char *ep = strchr(sp, ')'); if (!ep) - die("malformatted format string %s", sp); + return error("malformatted format string %s", sp); /* sp points at "%(" and ep points at the closing ")" */ parse_atom(sp + 2, ep); cp = ep + 1; } + return 0; } /* @@ -800,94 +802,76 @@ static struct ref_sort *default_sort(void) return sort; } -int cmd_for_each_ref(int ac, const char **av, const char *prefix) +int opt_parse_sort(const struct option *opt, const char *arg, int unset) +{ + struct ref_sort **sort_tail = opt->value; + struct ref_sort *s; + int len; + + if (!arg) /* should --no-sort void the list ? */ + return -1; + + *sort_tail = s = xcalloc(1, sizeof(*s)); + sort_tail = &s->next; + + if (*arg == '-') { + s->reverse = 1; + arg++; + } + len = strlen(arg); + s->atom = parse_atom(arg, arg+len); + return 0; +} + +static char const * const for_each_ref_usage[] = { + "git-for-each-ref [options] [<pattern>]", + NULL +}; + +int cmd_for_each_ref(int argc, const char **argv, const char *prefix) { int i, num_refs; - const char *format = NULL; + const char *format = "%(objectname) %(objecttype)\t%(refname)"; struct ref_sort *sort = NULL, **sort_tail = &sort; - int maxcount = 0; - int quote_style = -1; /* unspecified yet */ + int maxcount = 0, quote_style; + int quote_shell = 0, quote_perl = 0, quote_python = 0, quote_tcl = 0; struct refinfo **refs; struct grab_ref_cbdata cbdata; - for (i = 1; i < ac; i++) { - const char *arg = av[i]; - if (arg[0] != '-') - break; - if (!strcmp(arg, "--")) { - i++; - break; - } - if (!prefixcmp(arg, "--format=")) { - if (format) - die("more than one --format?"); - format = arg + 9; - continue; - } - if (!strcmp(arg, "-s") || !strcmp(arg, "--shell") ) { - if (0 <= quote_style) - die("more than one quoting style?"); - quote_style = QUOTE_SHELL; - continue; - } - if (!strcmp(arg, "-p") || !strcmp(arg, "--perl") ) { - if (0 <= quote_style) - die("more than one quoting style?"); - quote_style = QUOTE_PERL; - continue; - } - if (!strcmp(arg, "--python") ) { - if (0 <= quote_style) - die("more than one quoting style?"); - quote_style = QUOTE_PYTHON; - continue; - } - if (!strcmp(arg, "--tcl") ) { - if (0 <= quote_style) - die("more than one quoting style?"); - quote_style = QUOTE_TCL; - continue; - } - if (!prefixcmp(arg, "--count=")) { - if (maxcount) - die("more than one --count?"); - maxcount = atoi(arg + 8); - if (maxcount <= 0) - die("The number %s did not parse", arg); - continue; - } - if (!prefixcmp(arg, "--sort=")) { - struct ref_sort *s = xcalloc(1, sizeof(*s)); - int len; - - s->next = NULL; - *sort_tail = s; - sort_tail = &s->next; - - arg += 7; - if (*arg == '-') { - s->reverse = 1; - arg++; - } - len = strlen(arg); - sort->atom = parse_atom(arg, arg+len); - continue; - } - break; + struct option opts[] = { + OPT_BOOLEAN('s', "shell", "e_shell, "quote placeholders suitably for shells"), + OPT_BOOLEAN('p', "perl", "e_perl, "quote placeholders suitably for perl"), + OPT_BOOLEAN( 0 , "python", "e_python, "quote placeholders suitably for python"), + OPT_BOOLEAN( 0 , "tcl", "e_tcl, "quote placeholders suitably for tcl"), + + OPT_GROUP(""), + OPT_INTEGER( 0 , "count", &maxcount, "show only <n> matched refs"), + OPT_STRING( 0 , "format", &format, "format", "format to use for the output"), + OPT_CALLBACK(0 , "sort", &sort_tail, "key", + "field name to sort on", &opt_parse_sort), + OPT_END(), + }; + + parse_options(argc, argv, opts, for_each_ref_usage, 0); + if (maxcount < 0) { + error("invalid --count argument: `%d'", maxcount); + usage_with_options(for_each_ref_usage, opts); } - if (quote_style < 0) - quote_style = QUOTE_NONE; + if (quote_shell + quote_perl + quote_python + quote_tcl > 1) { + error("more than one quoting style ?"); + usage_with_options(for_each_ref_usage, opts); + } + if (verify_format(format)) + usage_with_options(for_each_ref_usage, opts); + quote_style = QUOTE_SHELL * quote_shell + QUOTE_PERL * quote_perl + + QUOTE_PYTHON * quote_python + QUOTE_TCL * quote_tcl; if (!sort) sort = default_sort(); sort_atom_limit = used_atom_cnt; - if (!format) - format = "%(objectname) %(objecttype)\t%(refname)"; - - verify_format(format); memset(&cbdata, 0, sizeof(cbdata)); - cbdata.grab_pattern = av + i; + cbdata.grab_pattern = argv; for_each_ref(grab_single_ref, &cbdata); refs = cbdata.grab_array; num_refs = cbdata.grab_cnt; diff --git a/builtin-fsck.c b/builtin-fsck.c index 8d12287f03..e4874f64a3 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -8,6 +8,7 @@ #include "pack.h" #include "cache-tree.h" #include "tree-walk.h" +#include "parse-options.h" #define REACHABLE 0x0001 #define SEEN 0x0002 @@ -666,9 +667,24 @@ static int fsck_cache_tree(struct cache_tree *it) return err; } -static const char fsck_usage[] = -"git-fsck [--tags] [--root] [[--unreachable] [--cache] [--full] " -"[--strict] [--verbose] <head-sha1>*]"; +static char const * const fsck_usage[] = { + "git-fsck [options] [<object>...]", + NULL +}; + +static struct option fsck_opts[] = { + OPT__VERBOSE(&verbose), + OPT_BOOLEAN(0, "unreachable", &show_unreachable, "show unreachable objects"), + OPT_BOOLEAN(0, "tags", &show_tags, "report tags"), + OPT_BOOLEAN(0, "root", &show_root, "report root nodes"), + OPT_BOOLEAN(0, "cache", &keep_cache_objects, "make index objects head nodes"), + OPT_BOOLEAN(0, "reflogs", &include_reflogs, "make reflogs head nodes (default)"), + OPT_BOOLEAN(0, "full", &check_full, "also consider alternate objects"), + OPT_BOOLEAN(0, "strict", &check_strict, "enable more strict checking"), + OPT_BOOLEAN(0, "lost-found", &write_lost_and_found, + "write dangling objects in .git/lost-found"), + OPT_END(), +}; int cmd_fsck(int argc, const char **argv, const char *prefix) { @@ -677,49 +693,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) track_object_refs = 1; errors_found = 0; - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (!strcmp(arg, "--unreachable")) { - show_unreachable = 1; - continue; - } - if (!strcmp(arg, "--tags")) { - show_tags = 1; - continue; - } - if (!strcmp(arg, "--root")) { - show_root = 1; - continue; - } - if (!strcmp(arg, "--cache")) { - keep_cache_objects = 1; - continue; - } - if (!strcmp(arg, "--no-reflogs")) { - include_reflogs = 0; - continue; - } - if (!strcmp(arg, "--full")) { - check_full = 1; - continue; - } - if (!strcmp(arg, "--strict")) { - check_strict = 1; - continue; - } - if (!strcmp(arg, "--verbose")) { - verbose = 1; - continue; - } - if (!strcmp(arg, "--lost-found")) { - check_full = 1; - include_reflogs = 0; - write_lost_and_found = 1; - continue; - } - if (*arg == '-') - usage(fsck_usage); + argc = parse_options(argc, argv, fsck_opts, fsck_usage, 0); + if (write_lost_and_found) { + check_full = 1; + include_reflogs = 0; } fsck_head_link(); @@ -741,22 +718,18 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) verify_pack(p, 0); for (p = packed_git; p; p = p->next) { - uint32_t i, num; + uint32_t j, num; if (open_pack_index(p)) continue; num = p->num_objects; - for (i = 0; i < num; i++) - fsck_sha1(nth_packed_object_sha1(p, i)); + for (j = 0; j < num; j++) + fsck_sha1(nth_packed_object_sha1(p, j)); } } heads = 0; for (i = 1; i < argc; i++) { const char *arg = argv[i]; - - if (*arg == '-') - continue; - if (!get_sha1(arg, head_sha1)) { struct object *obj = lookup_object(head_sha1); @@ -783,7 +756,6 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) } if (keep_cache_objects) { - int i; read_cache(); for (i = 0; i < active_nr; i++) { unsigned int mode; diff --git a/builtin-gc.c b/builtin-gc.c index 3a2ca4f901..c5bce893a6 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -12,11 +12,15 @@ #include "builtin.h" #include "cache.h" +#include "parse-options.h" #include "run-command.h" #define FAILED_RUN "failed to run %s" -static const char builtin_gc_usage[] = "git-gc [--prune] [--aggressive]"; +static const char * const builtin_gc_usage[] = { + "git-gc [options]", + NULL +}; static int pack_refs = 1; static int aggressive_window = -1; @@ -165,38 +169,34 @@ static int need_to_gc(void) int cmd_gc(int argc, const char **argv, const char *prefix) { - int i; int prune = 0; + int aggressive = 0; int auto_gc = 0; char buf[80]; + struct option builtin_gc_options[] = { + OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced loose objects"), + OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"), + OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"), + OPT_END() + }; + git_config(gc_config); if (pack_refs < 0) pack_refs = !is_bare_repository(); - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (!strcmp(arg, "--prune")) { - prune = 1; - continue; - } - if (!strcmp(arg, "--aggressive")) { - append_option(argv_repack, "-f", MAX_ADD); - if (aggressive_window > 0) { - sprintf(buf, "--window=%d", aggressive_window); - append_option(argv_repack, buf, MAX_ADD); - } - continue; - } - if (!strcmp(arg, "--auto")) { - auto_gc = 1; - continue; + argc = parse_options(argc, argv, builtin_gc_options, builtin_gc_usage, 0); + if (argc > 0) + usage_with_options(builtin_gc_usage, builtin_gc_options); + + if (aggressive) { + append_option(argv_repack, "-f", MAX_ADD); + if (aggressive_window > 0) { + sprintf(buf, "--window=%d", aggressive_window); + append_option(argv_repack, buf, MAX_ADD); } - break; } - if (i != argc) - usage(builtin_gc_usage); if (auto_gc) { /* diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index fb12248f82..2600847974 100644 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@ -915,6 +915,7 @@ static void handle_info(void) static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const char *msg, const char *patch) { + int peek; keep_subject = ks; metainfo_charset = encoding; fin = in; @@ -935,6 +936,11 @@ static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, p_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *)); s_hdr_data = xcalloc(MAX_HDR_PARSED, sizeof(char *)); + do { + peek = fgetc(in); + } while (isspace(peek)); + ungetc(peek, in); + /* process the email header */ while (read_one_header_line(line, sizeof(line), fin)) check_header(line, sizeof(line), p_hdr_data, 1); diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c index 43fc373a15..74b04706f3 100644 --- a/builtin-mailsplit.c +++ b/builtin-mailsplit.c @@ -164,6 +164,7 @@ static int split_mbox(const char *file, const char *dir, int allow_bare, { char name[PATH_MAX]; int ret = -1; + int peek; FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r"); int file_done = 0; @@ -173,6 +174,11 @@ static int split_mbox(const char *file, const char *dir, int allow_bare, goto out; } + do { + peek = fgetc(f); + } while (isspace(peek)); + ungetc(peek, f); + if (fgets(buf, sizeof(buf), f) == NULL) { /* empty stdin is OK */ if (f != stdin) { diff --git a/builtin-mv.c b/builtin-mv.c index b944651691..a3f9ad1744 100644 --- a/builtin-mv.c +++ b/builtin-mv.c @@ -8,9 +8,12 @@ #include "dir.h" #include "cache-tree.h" #include "path-list.h" +#include "parse-options.h" -static const char builtin_mv_usage[] = -"git-mv [-n] [-f] (<source> <destination> | [-k] <source>... <destination>)"; +static const char * const builtin_mv_usage[] = { + "git-mv [options] <source>... <destination>", + NULL +}; static const char **copy_pathspec(const char *prefix, const char **pathspec, int count, int base_name) @@ -61,8 +64,14 @@ static struct lock_file lock_file; int cmd_mv(int argc, const char **argv, const char *prefix) { - int i, newfd, count; + int i, newfd; int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; + struct option builtin_mv_options[] = { + OPT__DRY_RUN(&show_only), + OPT_BOOLEAN('f', NULL, &force, "force move/rename even if target exists"), + OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"), + OPT_END(), + }; const char **source, **destination, **dest_path; enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; struct stat st; @@ -78,52 +87,29 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (read_cache() < 0) die("index file corrupt"); - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; + argc = parse_options(argc, argv, builtin_mv_options, builtin_mv_usage, 0); + if (--argc < 1) + usage_with_options(builtin_mv_usage, builtin_mv_options); - if (arg[0] != '-') - break; - if (!strcmp(arg, "--")) { - i++; - break; - } - if (!strcmp(arg, "-n")) { - show_only = 1; - continue; - } - if (!strcmp(arg, "-f")) { - force = 1; - continue; - } - if (!strcmp(arg, "-k")) { - ignore_errors = 1; - continue; - } - usage(builtin_mv_usage); - } - count = argc - i - 1; - if (count < 1) - usage(builtin_mv_usage); - - source = copy_pathspec(prefix, argv + i, count, 0); - modes = xcalloc(count, sizeof(enum update_mode)); - dest_path = copy_pathspec(prefix, argv + argc - 1, 1, 0); + source = copy_pathspec(prefix, argv, argc, 0); + modes = xcalloc(argc, sizeof(enum update_mode)); + dest_path = copy_pathspec(prefix, argv + argc, 1, 0); if (dest_path[0][0] == '\0') /* special case: "." was normalized to "" */ - destination = copy_pathspec(dest_path[0], argv + i, count, 1); + destination = copy_pathspec(dest_path[0], argv, argc, 1); else if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode)) { dest_path[0] = add_slash(dest_path[0]); - destination = copy_pathspec(dest_path[0], argv + i, count, 1); + destination = copy_pathspec(dest_path[0], argv, argc, 1); } else { - if (count != 1) - usage(builtin_mv_usage); + if (argc != 1) + usage_with_options(builtin_mv_usage, builtin_mv_options); destination = dest_path; } /* Checking */ - for (i = 0; i < count; i++) { + for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; int length, src_is_dir; const char *bad = NULL; @@ -167,13 +153,13 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (last - first > 0) { source = xrealloc(source, - (count + last - first) + (argc + last - first) * sizeof(char *)); destination = xrealloc(destination, - (count + last - first) + (argc + last - first) * sizeof(char *)); modes = xrealloc(modes, - (count + last - first) + (argc + last - first) * sizeof(enum update_mode)); } @@ -183,13 +169,13 @@ int cmd_mv(int argc, const char **argv, const char *prefix) for (j = 0; j < last - first; j++) { const char *path = active_cache[first + j]->name; - source[count + j] = path; - destination[count + j] = + source[argc + j] = path; + destination[argc + j] = prefix_path(dst, dst_len, path + length); - modes[count + j] = INDEX; + modes[argc + j] = INDEX; } - count += last - first; + argc += last - first; } } else if (lstat(dst, &st) == 0) { bad = "destination exists"; @@ -216,12 +202,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (bad) { if (ignore_errors) { - if (--count > 0) { + if (--argc > 0) { memmove(source + i, source + i + 1, - (count - i) * sizeof(char *)); + (argc - i) * sizeof(char *)); memmove(destination + i, destination + i + 1, - (count - i) * sizeof(char *)); + (argc - i) * sizeof(char *)); } } else die ("%s, source=%s, destination=%s", @@ -229,7 +215,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) } } - for (i = 0; i < count; i++) { + for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; enum update_mode mode = modes[i]; if (show_only || verbose) @@ -253,7 +239,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) path_list_insert(dst, &added); } - if (show_only) { + if (show_only) { show_list("Changed : ", &changed); show_list("Adding : ", &added); show_list("Deleting : ", &deleted); diff --git a/builtin-name-rev.c b/builtin-name-rev.c index 03083e9477..a0c89a827b 100644 --- a/builtin-name-rev.c +++ b/builtin-name-rev.c @@ -3,12 +3,10 @@ #include "commit.h" #include "tag.h" #include "refs.h" +#include "parse-options.h" #define CUTOFF_DATE_SLOP 86400 /* one day */ -static const char name_rev_usage[] = - "git-name-rev [--tags | --refs=<pattern>] ( --all | --stdin | committish [committish...] )\n"; - typedef struct rev_name { const char *tip_name; int generation; @@ -153,51 +151,41 @@ static const char* get_rev_name(struct object *o) } } +static char const * const name_rev_usage[] = { + "git-name-rev [options] ( --all | --stdin | <commit>... )", + NULL +}; + int cmd_name_rev(int argc, const char **argv, const char *prefix) { struct object_array revs = { 0, 0, NULL }; - int as_is = 0, all = 0, transform_stdin = 0; + int all = 0, transform_stdin = 0; struct name_ref_data data = { 0, 0, NULL }; + struct option opts[] = { + OPT_BOOLEAN(0, "name-only", &data.name_only, "print only names (no SHA-1)"), + OPT_BOOLEAN(0, "tags", &data.tags_only, "only use tags to name the commits"), + OPT_STRING(0, "refs", &data.ref_filter, "pattern", + "only use refs matching <pattern>"), + OPT_GROUP(""), + OPT_BOOLEAN(0, "all", &all, "list all commits reachable from all refs"), + OPT_BOOLEAN(0, "stdin", &transform_stdin, "read from stdin"), + OPT_END(), + }; git_config(git_default_config); + argc = parse_options(argc, argv, opts, name_rev_usage, 0); + if (!!all + !!transform_stdin + !!argc > 1) { + error("Specify either a list, or --all, not both!"); + usage_with_options(name_rev_usage, opts); + } + if (all || transform_stdin) + cutoff = 0; - if (argc < 2) - usage(name_rev_usage); - - for (--argc, ++argv; argc; --argc, ++argv) { + for (; argc; argc--, argv++) { unsigned char sha1[20]; struct object *o; struct commit *commit; - if (!as_is && (*argv)[0] == '-') { - if (!strcmp(*argv, "--")) { - as_is = 1; - continue; - } else if (!strcmp(*argv, "--name-only")) { - data.name_only = 1; - continue; - } else if (!strcmp(*argv, "--tags")) { - data.tags_only = 1; - continue; - } else if (!prefixcmp(*argv, "--refs=")) { - data.ref_filter = *argv + 7; - continue; - } else if (!strcmp(*argv, "--all")) { - if (argc > 1) - die("Specify either a list, or --all, not both!"); - all = 1; - cutoff = 0; - continue; - } else if (!strcmp(*argv, "--stdin")) { - if (argc > 1) - die("Specify either a list, or --stdin, not both!"); - transform_stdin = 1; - cutoff = 0; - continue; - } - usage(name_rev_usage); - } - if (get_sha1(*argv, sha1)) { fprintf(stderr, "Could not get sha1 for %s. Skipping.\n", *argv); @@ -212,10 +200,8 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) } commit = (struct commit *)o; - if (cutoff > commit->date) cutoff = commit->date; - add_object_array((struct object *)commit, *argv, &revs); } diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 0be539ed7f..545ece5da7 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -57,7 +57,7 @@ struct object_entry { * nice "minimum seek" order. */ static struct object_entry *objects; -static struct object_entry **written_list; +static struct pack_idx_entry **written_list; static uint32_t nr_objects, nr_alloc, nr_result, nr_written; static int non_empty; @@ -65,8 +65,6 @@ static int no_reuse_delta, no_reuse_object, keep_unreachable; static int local; static int incremental; static int allow_ofs_delta; -static const char *pack_tmp_name, *idx_tmp_name; -static char tmpname[PATH_MAX]; static const char *base_name; static int progress = 1; static int window = 10; @@ -75,7 +73,7 @@ static int depth = 50; static int delta_search_threads = 1; static int pack_to_stdout; static int num_preferred_base; -static struct progress progress_state; +static struct progress *progress_state; static int pack_compression_level = Z_DEFAULT_COMPRESSION; static int pack_compression_seen; @@ -579,7 +577,7 @@ static off_t write_one(struct sha1file *f, e->idx.offset = 0; return 0; } - written_list[nr_written++] = e; + written_list[nr_written++] = &e->idx; /* make sure off_t is sufficiently large not to wrap */ if (offset > offset + size) @@ -587,12 +585,6 @@ static off_t write_one(struct sha1file *f, return offset + size; } -static int open_object_dir_tmp(const char *path) -{ - snprintf(tmpname, sizeof(tmpname), "%s/%s", get_object_directory(), path); - return xmkstemp(tmpname); -} - /* forward declaration for write_pack_file */ static int adjust_perm(const char *path, mode_t mode); @@ -606,16 +598,21 @@ static void write_pack_file(void) uint32_t nr_remaining = nr_result; if (do_progress) - start_progress(&progress_state, "Writing %u objects...", "", nr_result); - written_list = xmalloc(nr_objects * sizeof(struct object_entry *)); + progress_state = start_progress("Writing objects", nr_result); + written_list = xmalloc(nr_objects * sizeof(*written_list)); do { unsigned char sha1[20]; + char *pack_tmp_name = NULL; if (pack_to_stdout) { - f = sha1fd(1, "<stdout>"); + f = sha1fd_throughput(1, "<stdout>", progress_state); } else { - int fd = open_object_dir_tmp("tmp_pack_XXXXXX"); + char tmpname[PATH_MAX]; + int fd; + snprintf(tmpname, sizeof(tmpname), + "%s/tmp_pack_XXXXXX", get_object_directory()); + fd = xmkstemp(tmpname); pack_tmp_name = xstrdup(tmpname); f = sha1fd(fd, pack_tmp_name); } @@ -632,8 +629,7 @@ static void write_pack_file(void) if (!offset_one) break; offset = offset_one; - if (do_progress) - display_progress(&progress_state, written); + display_progress(progress_state, written); } /* @@ -643,19 +639,20 @@ static void write_pack_file(void) if (pack_to_stdout || nr_written == nr_remaining) { sha1close(f, sha1, 1); } else { - sha1close(f, sha1, 0); - fixup_pack_header_footer(f->fd, sha1, pack_tmp_name, nr_written); - close(f->fd); + int fd = sha1close(f, NULL, 0); + fixup_pack_header_footer(fd, sha1, pack_tmp_name, nr_written); + close(fd); } if (!pack_to_stdout) { mode_t mode = umask(0); + char *idx_tmp_name, tmpname[PATH_MAX]; umask(mode); mode = 0444 & ~mode; - idx_tmp_name = write_idx_file(NULL, - (struct pack_idx_entry **) written_list, nr_written, sha1); + idx_tmp_name = write_idx_file(NULL, written_list, + nr_written, sha1); snprintf(tmpname, sizeof(tmpname), "%s-%s.pack", base_name, sha1_to_hex(sha1)); if (adjust_perm(pack_tmp_name, mode)) @@ -672,19 +669,20 @@ static void write_pack_file(void) if (rename(idx_tmp_name, tmpname)) die("unable to rename temporary index file: %s", strerror(errno)); + free(idx_tmp_name); + free(pack_tmp_name); puts(sha1_to_hex(sha1)); } /* mark written objects as written to previous pack */ for (j = 0; j < nr_written; j++) { - written_list[j]->idx.offset = (off_t)-1; + written_list[j]->offset = (off_t)-1; } nr_remaining -= nr_written; } while (nr_remaining && i < nr_objects); free(written_list); - if (do_progress) - stop_progress(&progress_state); + stop_progress(&progress_state); if (written != nr_result) die("wrote %u objects while expecting %u", written, nr_result); /* @@ -852,8 +850,7 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type, else object_ix[-1 - ix] = nr_objects; - if (progress) - display_progress(&progress_state, nr_objects); + display_progress(progress_state, nr_objects); if (name && no_try_delta(name)) entry->no_try_delta = 1; @@ -1516,8 +1513,7 @@ static void find_deltas(struct object_entry **list, unsigned list_size, progress_lock(); (*processed)++; - if (progress) - display_progress(&progress_state, *processed); + display_progress(progress_state, *processed); progress_unlock(); /* @@ -1714,16 +1710,14 @@ static void prepare_pack(int window, int depth) delta_list[n++] = entry; } - if (nr_deltas) { + if (nr_deltas && n > 1) { unsigned nr_done = 0; if (progress) - start_progress(&progress_state, - "Deltifying %u objects...", "", - nr_deltas); + progress_state = start_progress("Compressing objects", + nr_deltas); qsort(delta_list, n, sizeof(*delta_list), type_size_sort); ll_find_deltas(delta_list, n, window+1, depth, &nr_done); - if (progress) - stop_progress(&progress_state); + stop_progress(&progress_state); if (nr_done != nr_deltas) die("inconsistency with delta count"); } @@ -1773,6 +1767,12 @@ static int git_pack_config(const char *k, const char *v) #endif return 0; } + if (!strcmp(k, "pack.indexversion")) { + pack_idx_default_version = git_config_int(k, v); + if (pack_idx_default_version > 2) + die("bad pack.indexversion=%d", pack_idx_default_version); + return 0; + } return git_default_config(k, v); } @@ -2135,23 +2135,17 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) prepare_packed_git(); if (progress) - start_progress(&progress_state, "Generating pack...", - "Counting objects: ", 0); + progress_state = start_progress("Counting objects", 0); if (!use_internal_rev_list) read_object_list_from_stdin(); else { rp_av[rp_ac] = NULL; get_object_list(rp_ac, rp_av); } - if (progress) { - stop_progress(&progress_state); - fprintf(stderr, "Done counting %u objects.\n", nr_objects); - } + stop_progress(&progress_state); if (non_empty && !nr_result) return 0; - if (progress && (nr_objects != nr_result)) - fprintf(stderr, "Result has %u objects.\n", nr_result); if (nr_result) prepare_pack(window, depth); write_pack_file(); diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index 09df4e11a8..a62f06bb89 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -3,9 +3,7 @@ #include "refs.h" #include "object.h" #include "tag.h" - -static const char builtin_pack_refs_usage[] = -"git-pack-refs [--all] [--prune | --no-prune]"; +#include "parse-options.h" struct ref_to_prune { struct ref_to_prune *next; @@ -117,31 +115,26 @@ static int pack_refs(unsigned int flags) return 0; } +static char const * const pack_refs_usage[] = { + "git-pack-refs [options]", + NULL +}; + int cmd_pack_refs(int argc, const char **argv, const char *prefix) { - int i; - unsigned int flags; - - flags = PACK_REFS_PRUNE; - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (!strcmp(arg, "--prune")) { - flags |= PACK_REFS_PRUNE; /* now the default */ - continue; - } - if (!strcmp(arg, "--no-prune")) { - flags &= ~PACK_REFS_PRUNE; - continue; - } - if (!strcmp(arg, "--all")) { - flags |= PACK_REFS_ALL; - continue; - } - /* perhaps other parameters later... */ - break; - } - if (i != argc) - usage(builtin_pack_refs_usage); - + int all = 0, prune = 1; + unsigned int flags = 0; + struct option opts[] = { + OPT_BOOLEAN(0, "all", &all, "pack everything"), + OPT_BOOLEAN(0, "prune", &prune, "prune loose refs (default)"), + OPT_END(), + }; + + if (parse_options(argc, argv, opts, pack_refs_usage, 0)) + usage_with_options(pack_refs_usage, opts); + if (prune) + flags |= PACK_REFS_PRUNE; + if (all) + flags |= PACK_REFS_ALL; return pack_refs(flags); } diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c index 977730064b..23faf3129f 100644 --- a/builtin-prune-packed.c +++ b/builtin-prune-packed.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "progress.h" static const char prune_packed_usage[] = "git-prune-packed [-n] [-q]"; @@ -7,6 +8,8 @@ static const char prune_packed_usage[] = #define DRY_RUN 01 #define VERBOSE 02 +static struct progress *progress; + static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts) { struct dirent *de; @@ -27,6 +30,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts) printf("rm -f %s\n", pathname); else if (unlink(pathname) < 0) error("unable to unlink %s", pathname); + display_progress(progress, i + 1); } pathname[len] = 0; rmdir(pathname); @@ -39,6 +43,10 @@ void prune_packed_objects(int opts) const char *dir = get_object_directory(); int len = strlen(dir); + if (opts == VERBOSE) + progress = start_progress_delay("Removing duplicate objects", + 256, 95, 2); + if (len > PATH_MAX - 42) die("impossible object directory"); memcpy(pathname, dir, len); @@ -49,16 +57,12 @@ void prune_packed_objects(int opts) sprintf(pathname + len, "%02x/", i); d = opendir(pathname); - if (opts == VERBOSE && (d || i == 255)) - fprintf(stderr, "Removing unused objects %d%%...\015", - ((i+1) * 100) / 256); if (!d) continue; prune_dir(i, d, pathname, len + 3, opts); closedir(d); } - if (opts == VERBOSE) - fprintf(stderr, "\nDone.\n"); + stop_progress(&progress); } int cmd_prune_packed(int argc, const char **argv, const char *prefix) diff --git a/builtin-reset.c b/builtin-reset.c index e1dc31e0eb..5467e36c73 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -113,10 +113,17 @@ static int update_index_refresh(void) return run_command_v_opt(argv_update_index, RUN_GIT_CMD); } +struct update_cb_data { + int index_fd; + struct lock_file *lock; + int exit_code; +}; + static void update_index_from_diff(struct diff_queue_struct *q, struct diff_options *opt, void *data) { int i; + struct update_cb_data *cb = data; /* do_diff_cache() mangled the index */ discard_cache(); @@ -133,29 +140,34 @@ static void update_index_from_diff(struct diff_queue_struct *q, } else remove_file_from_cache(one->path); } + + cb->exit_code = write_cache(cb->index_fd, active_cache, active_nr) || + close(cb->index_fd) || + commit_locked_index(cb->lock); } static int read_from_tree(const char *prefix, const char **argv, unsigned char *tree_sha1) { - struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); - int index_fd; struct diff_options opt; + struct update_cb_data cb; memset(&opt, 0, sizeof(opt)); diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt); opt.output_format = DIFF_FORMAT_CALLBACK; opt.format_callback = update_index_from_diff; + opt.format_callback_data = &cb; - index_fd = hold_locked_index(lock, 1); + cb.lock = xcalloc(1, sizeof(struct lock_file)); + cb.index_fd = hold_locked_index(cb.lock, 1); + cb.exit_code = 0; read_cache(); if (do_diff_cache(tree_sha1, &opt)) return 1; diffcore_std(&opt); diff_flush(&opt); - return write_cache(index_fd, active_cache, active_nr) || - close(index_fd) || - commit_locked_index(lock); + + return cb.exit_code; } static void prepend_reflog_action(const char *action, char *buf, size_t size) diff --git a/builtin-revert.c b/builtin-revert.c index e855b206cf..62ab1fa1f4 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -7,6 +7,7 @@ #include "run-command.h" #include "exec_cmd.h" #include "utf8.h" +#include "parse-options.h" /* * This implements the builtins revert and cherry-pick. @@ -19,51 +20,44 @@ * Copyright (c) 2005 Junio C Hamano */ -static const char *revert_usage = "git-revert [--edit | --no-edit] [-n] <commit-ish>"; +static const char * const revert_usage[] = { + "git-revert [options] <commit-ish>", + NULL +}; -static const char *cherry_pick_usage = "git-cherry-pick [--edit] [-n] [-r] [-x] <commit-ish>"; +static const char * const cherry_pick_usage[] = { + "git-cherry-pick [options] <commit-ish>", + NULL +}; -static int edit; -static int replay; +static int edit, no_replay, no_commit, needed_deref, mainline; static enum { REVERT, CHERRY_PICK } action; -static int no_commit; static struct commit *commit; -static int needed_deref; static const char *me; #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" -static void parse_options(int argc, const char **argv) +static void parse_args(int argc, const char **argv) { - const char *usage_str = action == REVERT ? - revert_usage : cherry_pick_usage; + const char * const * usage_str = + action == REVERT ? revert_usage : cherry_pick_usage; unsigned char sha1[20]; const char *arg; - int i; + int noop; + struct option options[] = { + OPT_BOOLEAN('n', "no-commit", &no_commit, "don't automatically commit"), + OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"), + OPT_BOOLEAN('x', NULL, &no_replay, "append commit name when cherry-picking"), + OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"), + OPT_INTEGER('m', "mainline", &mainline, "parent number"), + OPT_END(), + }; + + if (parse_options(argc, argv, options, usage_str, 0) != 1) + usage_with_options(usage_str, options); + arg = argv[0]; - if (argc < 2) - usage(usage_str); - - for (i = 1; i < argc; i++) { - arg = argv[i]; - if (arg[0] != '-') - break; - if (!strcmp(arg, "-n") || !strcmp(arg, "--no-commit")) - no_commit = 1; - else if (!strcmp(arg, "-e") || !strcmp(arg, "--edit")) - edit = 1; - else if (!strcmp(arg, "--no-edit")) - edit = 0; - else if (!strcmp(arg, "-x") || !strcmp(arg, "--i-really-want-" - "to-expose-my-private-commit-object-name")) - replay = 0; - else if (strcmp(arg, "-r")) - usage(usage_str); - } - if (i != argc - 1) - usage(usage_str); - arg = argv[argc - 1]; if (get_sha1(arg, sha1)) die ("Cannot find '%s'", arg); commit = (struct commit *)parse_object(sha1); @@ -234,7 +228,7 @@ static int merge_recursive(const char *base_sha1, static int revert_or_cherry_pick(int argc, const char **argv) { unsigned char head[20]; - struct commit *base, *next; + struct commit *base, *next, *parent; int i; char *oneline, *reencoded_message = NULL; const char *message, *encoding; @@ -243,10 +237,10 @@ static int revert_or_cherry_pick(int argc, const char **argv) git_config(git_default_config); me = action == REVERT ? "revert" : "cherry-pick"; setenv(GIT_REFLOG_ACTION, me, 0); - parse_options(argc, argv); + parse_args(argc, argv); /* this is copied from the shell script, but it's never triggered... */ - if (action == REVERT && replay) + if (action == REVERT && !no_replay) die("revert is incompatible with replay"); if (no_commit) { @@ -269,8 +263,29 @@ static int revert_or_cherry_pick(int argc, const char **argv) if (!commit->parents) die ("Cannot %s a root commit", me); - if (commit->parents->next) - die ("Cannot %s a multi-parent commit.", me); + if (commit->parents->next) { + /* Reverting or cherry-picking a merge commit */ + int cnt; + struct commit_list *p; + + if (!mainline) + die("Commit %s is a merge but no -m option was given.", + sha1_to_hex(commit->object.sha1)); + + for (cnt = 1, p = commit->parents; + cnt != mainline && p; + cnt++) + p = p->next; + if (cnt != mainline || !p) + die("Commit %s does not have parent %d", + sha1_to_hex(commit->object.sha1), mainline); + parent = p->item; + } else if (0 < mainline) + die("Mainline was specified but commit %s is not a merge.", + sha1_to_hex(commit->object.sha1)); + else + parent = commit->parents->item; + if (!(message = commit->buffer)) die ("Cannot get commit message for %s", sha1_to_hex(commit->object.sha1)); @@ -299,18 +314,18 @@ static int revert_or_cherry_pick(int argc, const char **argv) char *oneline_body = strchr(oneline, ' '); base = commit; - next = commit->parents->item; + next = parent; add_to_msg("Revert \""); add_to_msg(oneline_body + 1); add_to_msg("\"\n\nThis reverts commit "); add_to_msg(sha1_to_hex(commit->object.sha1)); add_to_msg(".\n"); } else { - base = commit->parents->item; + base = parent; next = commit; set_author_ident_env(message); add_message_to_msg(message); - if (!replay) { + if (no_replay) { add_to_msg("(cherry picked from commit "); add_to_msg(sha1_to_hex(commit->object.sha1)); add_to_msg(")\n"); @@ -388,13 +403,14 @@ int cmd_revert(int argc, const char **argv, const char *prefix) { if (isatty(0)) edit = 1; + no_replay = 1; action = REVERT; return revert_or_cherry_pick(argc, argv); } int cmd_cherry_pick(int argc, const char **argv, const char *prefix) { - replay = 1; + no_replay = 0; action = CHERRY_PICK; return revert_or_cherry_pick(argc, argv); } diff --git a/builtin-rm.c b/builtin-rm.c index 3b0677e44b..bca2bd9703 100644 --- a/builtin-rm.c +++ b/builtin-rm.c @@ -8,9 +8,12 @@ #include "dir.h" #include "cache-tree.h" #include "tree-walk.h" +#include "parse-options.h" -static const char builtin_rm_usage[] = -"git-rm [-f] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>..."; +static const char * const builtin_rm_usage[] = { + "git-rm [options] [--] <file>...", + NULL +}; static struct { int nr, alloc; @@ -121,11 +124,23 @@ static int check_local_mod(unsigned char *head, int index_only) static struct lock_file lock_file; +static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0; +static int ignore_unmatch = 0; + +static struct option builtin_rm_options[] = { + OPT__DRY_RUN(&show_only), + OPT__QUIET(&quiet), + OPT_BOOLEAN( 0 , "cached", &index_only, "only remove from the index"), + OPT_BOOLEAN('f', NULL, &force, "override the up-to-date check"), + OPT_BOOLEAN('r', NULL, &recursive, "allow recursive removal"), + OPT_BOOLEAN( 0 , "ignore-unmatch", &ignore_unmatch, + "exit with a zero status even if nothing matched"), + OPT_END(), +}; + int cmd_rm(int argc, const char **argv, const char *prefix) { int i, newfd; - int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0; - int ignore_unmatch = 0; const char **pathspec; char *seen; @@ -136,34 +151,11 @@ int cmd_rm(int argc, const char **argv, const char *prefix) if (read_cache() < 0) die("index file corrupt"); - for (i = 1 ; i < argc ; i++) { - const char *arg = argv[i]; - - if (*arg != '-') - break; - else if (!strcmp(arg, "--")) { - i++; - break; - } - else if (!strcmp(arg, "-n")) - show_only = 1; - else if (!strcmp(arg, "--cached")) - index_only = 1; - else if (!strcmp(arg, "-f")) - force = 1; - else if (!strcmp(arg, "-r")) - recursive = 1; - else if (!strcmp(arg, "--quiet")) - quiet = 1; - else if (!strcmp(arg, "--ignore-unmatch")) - ignore_unmatch = 1; - else - usage(builtin_rm_usage); - } - if (argc <= i) - usage(builtin_rm_usage); + argc = parse_options(argc, argv, builtin_rm_options, builtin_rm_usage, 0); + if (!argc) + usage_with_options(builtin_rm_usage, builtin_rm_options); - pathspec = get_pathspec(prefix, argv + i); + pathspec = get_pathspec(prefix, argv); seen = NULL; for (i = 0; pathspec[i] ; i++) /* nothing */; diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c index 9eb95e50da..d33982b967 100644 --- a/builtin-symbolic-ref.c +++ b/builtin-symbolic-ref.c @@ -1,9 +1,12 @@ #include "builtin.h" #include "cache.h" #include "refs.h" +#include "parse-options.h" -static const char git_symbolic_ref_usage[] = -"git-symbolic-ref [-q] [-m <reason>] name [ref]"; +static const char * const git_symbolic_ref_usage[] = { + "git-symbolic-ref [options] name [ref]", + NULL +}; static void check_symref(const char *HEAD, int quiet) { @@ -26,44 +29,25 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) { int quiet = 0; const char *msg = NULL; + struct option options[] = { + OPT__QUIET(&quiet), + OPT_STRING('m', NULL, &msg, "reason", "reason of the update"), + OPT_END(), + }; git_config(git_default_config); - - while (1 < argc) { - const char *arg = argv[1]; - if (arg[0] != '-') - break; - else if (!strcmp("-q", arg)) - quiet = 1; - else if (!strcmp("-m", arg)) { - argc--; - argv++; - if (argc <= 1) - break; - msg = argv[1]; - if (!*msg) - die("Refusing to perform update with empty message"); - } - else if (!strcmp("--", arg)) { - argc--; - argv++; - break; - } - else - die("unknown option %s", arg); - argc--; - argv++; - } - + argc = parse_options(argc, argv, options, git_symbolic_ref_usage, 0); + if (msg &&!*msg) + die("Refusing to perform update with empty message"); switch (argc) { - case 2: - check_symref(argv[1], quiet); + case 1: + check_symref(argv[0], quiet); break; - case 3: - create_symref(argv[1], argv[2], msg); + case 2: + create_symref(argv[0], argv[1], msg); break; default: - usage(git_symbolic_ref_usage); + usage_with_options(git_symbolic_ref_usage, options); } return 0; } diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c index a6ff62fd8c..1e51865c52 100644 --- a/builtin-unpack-objects.c +++ b/builtin-unpack-objects.c @@ -311,7 +311,7 @@ static void unpack_one(unsigned nr) static void unpack_all(void) { int i; - struct progress progress; + struct progress *progress = NULL; struct pack_header *hdr = fill(sizeof(struct pack_header)); unsigned nr_objects = ntohl(hdr->hdr_entries); @@ -322,15 +322,13 @@ static void unpack_all(void) use(sizeof(struct pack_header)); if (!quiet) - start_progress(&progress, "Unpacking %u objects...", "", nr_objects); + progress = start_progress("Unpacking objects", nr_objects); obj_list = xmalloc(nr_objects * sizeof(*obj_list)); for (i = 0; i < nr_objects; i++) { unpack_one(i); - if (!quiet) - display_progress(&progress, i + 1); + display_progress(progress, i + 1); } - if (!quiet) - stop_progress(&progress); + stop_progress(&progress); if (delta_list) die("unresolved deltas left after unpacking"); diff --git a/builtin-update-ref.c b/builtin-update-ref.c index fe1f74c9f3..e90737c350 100644 --- a/builtin-update-ref.c +++ b/builtin-update-ref.c @@ -1,59 +1,44 @@ #include "cache.h" #include "refs.h" #include "builtin.h" +#include "parse-options.h" -static const char git_update_ref_usage[] = -"git-update-ref [-m <reason>] (-d <refname> <value> | [--no-deref] <refname> <value> [<oldval>])"; +static const char * const git_update_ref_usage[] = { + "git-update-ref [options] -d <refname> <oldval>", + "git-update-ref [options] <refname> <newval> [<oldval>]", + NULL +}; int cmd_update_ref(int argc, const char **argv, const char *prefix) { - const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL; + const char *refname, *value, *oldval, *msg=NULL; unsigned char sha1[20], oldsha1[20]; - int i, delete, ref_flags; + int delete = 0, no_deref = 0; + struct option options[] = { + OPT_STRING( 'm', NULL, &msg, "reason", "reason of the update"), + OPT_BOOLEAN('d', NULL, &delete, "deletes the reference"), + OPT_BOOLEAN( 0 , "no-deref", &no_deref, + "update <refname> not the one it points to"), + OPT_END(), + }; - delete = 0; - ref_flags = 0; git_config(git_default_config); + argc = parse_options(argc, argv, options, git_update_ref_usage, 0); + if (msg && !*msg) + die("Refusing to perform update with empty message."); - for (i = 1; i < argc; i++) { - if (!strcmp("-m", argv[i])) { - if (i+1 >= argc) - usage(git_update_ref_usage); - msg = argv[++i]; - if (!*msg) - die("Refusing to perform update with empty message."); - continue; - } - if (!strcmp("-d", argv[i])) { - delete = 1; - continue; - } - if (!strcmp("--no-deref", argv[i])) { - ref_flags |= REF_NODEREF; - continue; - } - if (!refname) { - refname = argv[i]; - continue; - } - if (!value) { - value = argv[i]; - continue; - } - if (!oldval) { - oldval = argv[i]; - continue; - } - } - if (!refname || !value) - usage(git_update_ref_usage); + if (argc < 2 || argc > 3) + usage_with_options(git_update_ref_usage, options); + refname = argv[0]; + value = argv[1]; + oldval = argv[2]; if (get_sha1(value, sha1)) die("%s: not a valid SHA1", value); if (delete) { if (oldval) - usage(git_update_ref_usage); + usage_with_options(git_update_ref_usage, options); return delete_ref(refname, sha1); } @@ -62,5 +47,5 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) die("%s: not a valid old SHA1", oldval); return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL, - ref_flags, DIE_ON_ERR); + no_deref ? REF_NODEREF : 0, DIE_ON_ERR); } diff --git a/csum-file.c b/csum-file.c index 9ab997120d..3729e73e19 100644 --- a/csum-file.c +++ b/csum-file.c @@ -8,6 +8,7 @@ * able to verify hasn't been messed with afterwards. */ #include "cache.h" +#include "progress.h" #include "csum-file.h" static void sha1flush(struct sha1file *f, unsigned int count) @@ -17,6 +18,7 @@ static void sha1flush(struct sha1file *f, unsigned int count) for (;;) { int ret = xwrite(f->fd, buf, count); if (ret > 0) { + display_throughput(f->tp, ret); buf = (char *) buf + ret; count -= ret; if (count) @@ -31,22 +33,27 @@ static void sha1flush(struct sha1file *f, unsigned int count) int sha1close(struct sha1file *f, unsigned char *result, int final) { + int fd; unsigned offset = f->offset; if (offset) { SHA1_Update(&f->ctx, f->buffer, offset); sha1flush(f, offset); f->offset = 0; } - if (!final) - return 0; /* only want to flush (no checksum write, no close) */ - SHA1_Final(f->buffer, &f->ctx); - if (result) - hashcpy(result, f->buffer); - sha1flush(f, 20); - if (close(f->fd)) - die("%s: sha1 file error on close (%s)", f->name, strerror(errno)); + if (final) { + /* write checksum and close fd */ + SHA1_Final(f->buffer, &f->ctx); + if (result) + hashcpy(result, f->buffer); + sha1flush(f, 20); + if (close(f->fd)) + die("%s: sha1 file error on close (%s)", + f->name, strerror(errno)); + fd = 0; + } else + fd = f->fd; free(f); - return 0; + return fd; } int sha1write(struct sha1file *f, void *buf, unsigned int count) @@ -75,6 +82,11 @@ int sha1write(struct sha1file *f, void *buf, unsigned int count) struct sha1file *sha1fd(int fd, const char *name) { + return sha1fd_throughput(fd, name, NULL); +} + +struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp) +{ struct sha1file *f; unsigned len; @@ -89,6 +101,7 @@ struct sha1file *sha1fd(int fd, const char *name) f->fd = fd; f->error = 0; f->offset = 0; + f->tp = tp; f->do_crc = 0; SHA1_Init(&f->ctx); return f; diff --git a/csum-file.h b/csum-file.h index c3c792f1b5..4d1b231292 100644 --- a/csum-file.h +++ b/csum-file.h @@ -1,11 +1,14 @@ #ifndef CSUM_FILE_H #define CSUM_FILE_H +struct progress; + /* A SHA1-protected file */ struct sha1file { int fd, error; unsigned int offset, namelen; SHA_CTX ctx; + struct progress *tp; char name[PATH_MAX]; int do_crc; uint32_t crc32; @@ -13,6 +16,7 @@ struct sha1file { }; extern struct sha1file *sha1fd(int fd, const char *name); +extern struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp); extern int sha1close(struct sha1file *, unsigned char *, int); extern int sha1write(struct sha1file *, void *, unsigned int); extern void crc32_begin(struct sha1file *); diff --git a/git-clone.sh b/git-clone.sh index 0ea3c24f59..3f00693608 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -14,7 +14,7 @@ die() { } usage() { - die "Usage: $0 [--template=<template_directory>] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [--depth <n>] [-n] <repo> [<dir>]" + die "Usage: $0 [--template=<template_directory>] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [--depth <n>] [-n] [--] <repo> [<dir>]" } get_repo_base() { @@ -160,6 +160,9 @@ while *,--depth) shift depth="--depth=$1";; + *,--) + shift + break ;; *,-*) usage ;; *) break ;; esac diff --git a/git-compat-util.h b/git-compat-util.h index 474f1d1ffb..7b29d1b905 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -381,4 +381,17 @@ static inline int strtoul_ui(char const *s, int base, unsigned int *result) return 0; } +static inline int strtol_i(char const *s, int base, int *result) +{ + long ul; + char *p; + + errno = 0; + ul = strtol(s, &p, base); + if (errno || *p || p == s || (int) ul != ul) + return -1; + *result = ul; + return 0; +} + #endif diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 2954fb846e..e4bc2b54f6 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -818,6 +818,7 @@ while (<CVS>) { $state = 4; } elsif ($state == 4 and s/^Branch:\s+//) { s/\s+$//; + tr/_/\./ if ( $opt_u ); s/[\/]/$opt_s/g; $branch = $_; $state = 5; diff --git a/git-repack.sh b/git-repack.sh index e72adc4d91..7220635c96 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -83,9 +83,6 @@ for name in $names ; do fullbases="$fullbases pack-$name" chmod a-w "$PACKTMP-$name.pack" chmod a-w "$PACKTMP-$name.idx" - if test "$quiet" != '-q'; then - echo "Pack pack-$name created." - fi mkdir -p "$PACKDIR" || exit for sfx in pack idx diff --git a/git-svn.perl b/git-svn.perl index 22bb47b34d..4900f57f18 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -252,7 +252,7 @@ Usage: $0 <command> [options] [arguments]\n next if $cmd && $cmd ne $_; next if /^multi-/; # don't show deprecated commands print $fd ' ',pack('A17',$_),$cmd{$_}->[1],"\n"; - foreach (keys %{$cmd{$_}->[2]}) { + foreach (sort keys %{$cmd{$_}->[2]}) { # mixed-case options are for .git/config only next if /[A-Z]/ && /^[a-z]+$/i; # prints out arguments as they should be passed: diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 2e00756276..759dff1cce 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -611,6 +611,15 @@ sub href(%) { ); my %mapping = @mapping; + if ($params{-replay}) { + while (my ($name, $symbol) = each %mapping) { + if (!exists $params{$name}) { + # to allow for multivalued params we use arrayref form + $params{$name} = [ $cgi->param($symbol) ]; + } + } + } + $params{'project'} = $project unless exists $params{'project'}; my ($use_pathinfo) = gitweb_check_feature('pathinfo'); @@ -1423,20 +1432,121 @@ sub git_get_type { return $type; } +# repository configuration +our $config_file = ''; +our %config; + +# store multiple values for single key as anonymous array reference +# single values stored directly in the hash, not as [ <value> ] +sub hash_set_multi { + my ($hash, $key, $value) = @_; + + if (!exists $hash->{$key}) { + $hash->{$key} = $value; + } elsif (!ref $hash->{$key}) { + $hash->{$key} = [ $hash->{$key}, $value ]; + } else { + push @{$hash->{$key}}, $value; + } +} + +# return hash of git project configuration +# optionally limited to some section, e.g. 'gitweb' +sub git_parse_project_config { + my $section_regexp = shift; + my %config; + + local $/ = "\0"; + + open my $fh, "-|", git_cmd(), "config", '-z', '-l', + or return; + + while (my $keyval = <$fh>) { + chomp $keyval; + my ($key, $value) = split(/\n/, $keyval, 2); + + hash_set_multi(\%config, $key, $value) + if (!defined $section_regexp || $key =~ /^(?:$section_regexp)\./o); + } + close $fh; + + return %config; +} + +# convert config value to boolean, 'true' or 'false' +# no value, number > 0, 'true' and 'yes' values are true +# rest of values are treated as false (never as error) +sub config_to_bool { + my $val = shift; + + # strip leading and trailing whitespace + $val =~ s/^\s+//; + $val =~ s/\s+$//; + + return (!defined $val || # section.key + ($val =~ /^\d+$/ && $val) || # section.key = 1 + ($val =~ /^(?:true|yes)$/i)); # section.key = true +} + +# convert config value to simple decimal number +# an optional value suffix of 'k', 'm', or 'g' will cause the value +# to be multiplied by 1024, 1048576, or 1073741824 +sub config_to_int { + my $val = shift; + + # strip leading and trailing whitespace + $val =~ s/^\s+//; + $val =~ s/\s+$//; + + if (my ($num, $unit) = ($val =~ /^([0-9]*)([kmg])$/i)) { + $unit = lc($unit); + # unknown unit is treated as 1 + return $num * ($unit eq 'g' ? 1073741824 : + $unit eq 'm' ? 1048576 : + $unit eq 'k' ? 1024 : 1); + } + return $val; +} + +# convert config value to array reference, if needed +sub config_to_multi { + my $val = shift; + + return ref($val) ? $val : [ $val ]; +} + sub git_get_project_config { my ($key, $type) = @_; + # key sanity check return unless ($key); $key =~ s/^gitweb\.//; return if ($key =~ m/\W/); - my @x = (git_cmd(), 'config'); - if (defined $type) { push @x, $type; } - push @x, "--get"; - push @x, "gitweb.$key"; - my $val = qx(@x); - chomp $val; - return ($val); + # type sanity check + if (defined $type) { + $type =~ s/^--//; + $type = undef + unless ($type eq 'bool' || $type eq 'int'); + } + + # get config + if (!defined $config_file || + $config_file ne "$git_dir/config") { + %config = git_parse_project_config('gitweb'); + $config_file = "$git_dir/config"; + } + + # ensure given type + if (!defined $type) { + return $config{"gitweb.$key"}; + } elsif ($type eq 'bool') { + # backward compatibility: 'git config --bool' returns true/false + return config_to_bool($config{"gitweb.$key"}) ? 'true' : 'false'; + } elsif ($type eq 'int') { + return config_to_int($config{"gitweb.$key"}); + } + return $config{"gitweb.$key"}; } # get hash of given path at given ref @@ -1496,7 +1606,9 @@ sub git_get_path_by_hash { sub git_get_project_description { my $path = shift; - open my $fd, "$projectroot/$path/description" or return undef; + $git_dir = "$projectroot/$path"; + open my $fd, "$projectroot/$path/description" + or return git_get_project_config('description'); my $descr = <$fd>; close $fd; if (defined $descr) { @@ -1508,7 +1620,11 @@ sub git_get_project_description { sub git_get_project_url_list { my $path = shift; - open my $fd, "$projectroot/$path/cloneurl" or return; + $git_dir = "$projectroot/$path"; + open my $fd, "$projectroot/$path/cloneurl" + or return wantarray ? + @{ config_to_multi(git_get_project_config('url')) } : + config_to_multi(git_get_project_config('url')); my @git_project_url_list = map { chomp; $_ } <$fd>; close $fd; @@ -1990,12 +2106,12 @@ sub parse_difftree_raw_line { $res{'to_mode'} = $2; $res{'from_id'} = $3; $res{'to_id'} = $4; - $res{'status'} = $5; + $res{'status'} = $res{'status_str'} = $5; $res{'similarity'} = $6; if ($res{'status'} eq 'R' || $res{'status'} eq 'C') { # renamed or copied ($res{'from_file'}, $res{'to_file'}) = map { unquote($_) } split("\t", $7); } else { - $res{'file'} = unquote($7); + $res{'from_file'} = $res{'to_file'} = $res{'file'} = unquote($7); } } # '::100755 100755 100755 60e79ca1b01bc8b057abe17ddab484699a7f5fdb 94067cc5f73388f33722d52ae02f44692bc07490 94067cc5f73388f33722d52ae02f44692bc07490 MR git-gui/git-gui.sh' @@ -2006,6 +2122,7 @@ sub parse_difftree_raw_line { $res{'to_mode'} = pop @{$res{'from_mode'}}; $res{'from_id'} = [ split(' ', $3) ]; $res{'to_id'} = pop @{$res{'from_id'}}; + $res{'status_str'} = $4; $res{'status'} = [ split('', $4) ]; $res{'to_file'} = unquote($5); } @@ -2062,7 +2179,10 @@ sub parse_from_to_diffinfo { fill_from_file_info($diffinfo, @parents) unless exists $diffinfo->{'from_file'}; for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) { - $from->{'file'}[$i] = $diffinfo->{'from_file'}[$i] || $diffinfo->{'to_file'}; + $from->{'file'}[$i] = + defined $diffinfo->{'from_file'}[$i] ? + $diffinfo->{'from_file'}[$i] : + $diffinfo->{'to_file'}; if ($diffinfo->{'status'}[$i] ne "A") { # not new (added) file $from->{'href'}[$i] = href(action=>"blob", hash_base=>$parents[$i], @@ -2074,7 +2194,7 @@ sub parse_from_to_diffinfo { } } else { # ordinary (not combined) diff - $from->{'file'} = $diffinfo->{'from_file'} || $diffinfo->{'file'}; + $from->{'file'} = $diffinfo->{'from_file'}; if ($diffinfo->{'status'} ne "A") { # not new (added) file $from->{'href'} = href(action=>"blob", hash_base=>$hash_parent, hash=>$diffinfo->{'from_id'}, @@ -2084,7 +2204,7 @@ sub parse_from_to_diffinfo { } } - $to->{'file'} = $diffinfo->{'to_file'} || $diffinfo->{'file'}; + $to->{'file'} = $diffinfo->{'to_file'}; if (!is_deleted($diffinfo)) { # file exists in result $to->{'href'} = href(action=>"blob", hash_base=>$hash, hash=>$diffinfo->{'to_id'}, @@ -2505,7 +2625,7 @@ sub format_paging_nav { if ($page > 0) { $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>$action, hash=>$hash, page=>$page-1), + $cgi->a({-href => href(-replay=>1, page=>$page-1), -accesskey => "p", -title => "Alt-p"}, "prev"); } else { $paging_nav .= " ⋅ prev"; @@ -2513,7 +2633,7 @@ sub format_paging_nav { if ($nrevs >= (100 * ($page+1)-1)) { $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>$action, hash=>$hash, page=>$page+1), + $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); } else { $paging_nav .= " ⋅ next"; @@ -2818,7 +2938,7 @@ sub fill_from_file_info { sub is_deleted { my $diffinfo = shift; - return $diffinfo->{'to_id'} eq ('0' x 40); + return $diffinfo->{'status_str'} =~ /D/; } # does patch correspond to [previous] difftree raw line @@ -2829,7 +2949,7 @@ sub is_patch_split { my ($diffinfo, $patchinfo) = @_; return defined $diffinfo && defined $patchinfo - && ($diffinfo->{'to_file'} || $diffinfo->{'file'}) eq $patchinfo->{'to_file'}; + && $diffinfo->{'to_file'} eq $patchinfo->{'to_file'}; } @@ -3898,11 +4018,11 @@ sub git_blame2 { or die_error(undef, "Open git-blame failed"); git_header_html(); my $formats_nav = - $cgi->a({-href => href(action=>"blob", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"blob", -replay=>1)}, "blob") . " | " . - $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, file_name=>$file_name)}, - "history") . + $cgi->a({-href => href(action=>"history", -replay=>1)}, + "history") . " | " . $cgi->a({-href => href(action=>"blame", file_name=>$file_name)}, "HEAD"); @@ -4178,18 +4298,15 @@ sub git_blob { if (defined $file_name) { if ($have_blame) { $formats_nav .= - $cgi->a({-href => href(action=>"blame", hash_base=>$hash_base, - hash=>$hash, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"blame", -replay=>1)}, "blame") . " | "; } $formats_nav .= - $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, - hash=>$hash, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"history", -replay=>1)}, "history") . " | " . - $cgi->a({-href => href(action=>"blob_plain", - hash=>$hash, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"blob_plain", -replay=>1)}, "raw") . " | " . $cgi->a({-href => href(action=>"blob", @@ -4197,7 +4314,8 @@ sub git_blob { "HEAD"); } else { $formats_nav .= - $cgi->a({-href => href(action=>"blob_plain", hash=>$hash)}, "raw"); + $cgi->a({-href => href(action=>"blob_plain", -replay=>1)}, + "raw"); } git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav); git_print_header_div('commit', esc_html($co{'title'}), $hash_base); @@ -4260,8 +4378,7 @@ sub git_tree { my @views_nav = (); if (defined $file_name) { push @views_nav, - $cgi->a({-href => href(action=>"history", hash_base=>$hash_base, - hash=>$hash, file_name=>$file_name)}, + $cgi->a({-href => href(action=>"history", -replay=>1)}, "history"), $cgi->a({-href => href(action=>"tree", hash_base=>"HEAD", file_name=>$file_name)}, @@ -4435,7 +4552,7 @@ sub git_log { } if ($#commitlist >= 100) { print "<div class=\"page_nav\">\n"; - print $cgi->a({-href => href(action=>"log", hash=>$hash, page=>$page+1), + print $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); print "</div>\n"; } @@ -4667,8 +4784,8 @@ sub git_blobdiff { } %diffinfo = parse_difftree_raw_line($difftree[0]); - $file_parent ||= $diffinfo{'from_file'} || $file_name || $diffinfo{'file'}; - $file_name ||= $diffinfo{'to_file'} || $diffinfo{'file'}; + $file_parent ||= $diffinfo{'from_file'} || $file_name; + $file_name ||= $diffinfo{'to_file'}; $hash_parent ||= $diffinfo{'from_id'}; $hash ||= $diffinfo{'to_id'}; @@ -4729,10 +4846,7 @@ sub git_blobdiff { # header if ($format eq 'html') { my $formats_nav = - $cgi->a({-href => href(action=>"blobdiff_plain", - hash=>$hash, hash_parent=>$hash_parent, - hash_base=>$hash_base, hash_parent_base=>$hash_parent_base, - file_name=>$file_name, file_parent=>$file_parent)}, + $cgi->a({-href => href(action=>"blobdiff_plain", -replay=>1)}, "raw"); git_header_html(undef, $expires); if (defined $hash_base && (my %co = parse_commit($hash_base))) { @@ -4806,8 +4920,7 @@ sub git_commitdiff { my $formats_nav; if ($format eq 'html') { $formats_nav = - $cgi->a({-href => href(action=>"commitdiff_plain", - hash=>$hash, hash_parent=>$hash_parent)}, + $cgi->a({-href => href(action=>"commitdiff_plain", -replay=>1)}, "raw"); if (defined $hash_parent && @@ -5002,27 +5115,20 @@ sub git_history { file_name=>$file_name)}, "first"); $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, - file_name=>$file_name, page=>$page-1), + $cgi->a({-href => href(-replay=>1, page=>$page-1), -accesskey => "p", -title => "Alt-p"}, "prev"); } else { $paging_nav .= "first"; $paging_nav .= " ⋅ prev"; } - if ($#commitlist >= 100) { - $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, - file_name=>$file_name, page=>$page+1), - -accesskey => "n", -title => "Alt-n"}, "next"); - } else { - $paging_nav .= " ⋅ next"; - } my $next_link = ''; if ($#commitlist >= 100) { $next_link = - $cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base, - file_name=>$file_name, page=>$page+1), + $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); + $paging_nav .= " ⋅ $next_link"; + } else { + $paging_nav .= " ⋅ next"; } git_header_html(); @@ -5092,30 +5198,23 @@ sub git_search { searchtext=>$searchtext, searchtype=>$searchtype)}, "first"); $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>"search", hash=>$hash, - searchtext=>$searchtext, searchtype=>$searchtype, - page=>$page-1), + $cgi->a({-href => href(-replay=>1, page=>$page-1), -accesskey => "p", -title => "Alt-p"}, "prev"); } else { $paging_nav .= "first"; $paging_nav .= " ⋅ prev"; } + my $next_link = ''; if ($#commitlist >= 100) { - $paging_nav .= " ⋅ " . - $cgi->a({-href => href(action=>"search", hash=>$hash, - searchtext=>$searchtext, searchtype=>$searchtype, - page=>$page+1), + $next_link = + $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); + $paging_nav .= " ⋅ $next_link"; } else { $paging_nav .= " ⋅ next"; } - my $next_link = ''; + if ($#commitlist >= 100) { - $next_link = - $cgi->a({-href => href(action=>"search", hash=>$hash, - searchtext=>$searchtext, searchtype=>$searchtype, - page=>$page+1), - -accesskey => "n", -title => "Alt-n"}, "next"); } git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav); @@ -5314,7 +5413,7 @@ sub git_shortlog { my $next_link = ''; if ($#commitlist >= 100) { $next_link = - $cgi->a({-href => href(action=>"shortlog", hash=>$hash, page=>$page+1), + $cgi->a({-href => href(-replay=>1, page=>$page+1), -accesskey => "n", -title => "Alt-n"}, "next"); } diff --git a/http-push.c b/http-push.c index c02a3af634..9314621a11 100644 --- a/http-push.c +++ b/http-push.c @@ -2241,7 +2241,11 @@ static int delete_remote_branch(char *pattern, int force) /* Remote branch must be an ancestor of remote HEAD */ if (!verify_merge_base(head_sha1, remote_ref->old_sha1)) { - return error("The branch '%s' is not a strict subset of your current HEAD.\nIf you are sure you want to delete it, run:\n\t'git http-push -D %s %s'", remote_ref->name, remote->url, pattern); + return error("The branch '%s' is not an ancestor " + "of your current HEAD.\n" + "If you are sure you want to delete it," + " run:\n\t'git http-push -D %s %s'", + remote_ref->name, remote->url, pattern); } } @@ -2417,16 +2421,17 @@ int main(int argc, char **argv) if (!has_sha1_file(ref->old_sha1) || !ref_newer(ref->peer_ref->new_sha1, ref->old_sha1)) { - /* We do not have the remote ref, or + /* + * We do not have the remote ref, or * we know that the remote ref is not * an ancestor of what we are trying to * push. Either way this can be losing * commits at the remote end and likely * we were not up to date to begin with. */ - error("remote '%s' is not a strict " - "subset of local ref '%s'. " - "maybe you are not up-to-date and " + error("remote '%s' is not an ancestor of\n" + "local '%s'.\n" + "Maybe you are not up-to-date and " "need to pull first?", ref->name, ref->peer_ref->name); diff --git a/index-pack.c b/index-pack.c index db58e05041..715a5bb7a6 100644 --- a/index-pack.c +++ b/index-pack.c @@ -46,7 +46,7 @@ static int nr_resolved_deltas; static int from_stdin; static int verbose; -static struct progress progress; +static struct progress *progress; /* We always read in 4kB chunks. */ static unsigned char input_buffer[4096]; @@ -87,6 +87,8 @@ static void *fill(int min) die("early EOF"); die("read error on input: %s", strerror(errno)); } + if (from_stdin) + display_throughput(progress, ret); input_len += ret; } while (input_len < min); return input_buffer; @@ -106,7 +108,7 @@ static void use(int bytes) consumed_bytes += bytes; } -static const char *open_pack_file(const char *pack_name) +static char *open_pack_file(char *pack_name) { if (from_stdin) { input_fd = 0; @@ -406,7 +408,9 @@ static void parse_pack_objects(unsigned char *sha1) * - remember base (SHA1 or offset) for all deltas. */ if (verbose) - start_progress(&progress, "Indexing %u objects...", "", nr_objects); + progress = start_progress( + from_stdin ? "Receiving objects" : "Indexing objects", + nr_objects); for (i = 0; i < nr_objects; i++) { struct object_entry *obj = &objects[i]; data = unpack_raw_entry(obj, &delta->base); @@ -418,12 +422,10 @@ static void parse_pack_objects(unsigned char *sha1) } else sha1_object(data, obj->size, obj->type, obj->idx.sha1); free(data); - if (verbose) - display_progress(&progress, i+1); + display_progress(progress, i+1); } objects[i].idx.offset = consumed_bytes; - if (verbose) - stop_progress(&progress); + stop_progress(&progress); /* Check pack integrity */ flush(); @@ -455,7 +457,7 @@ static void parse_pack_objects(unsigned char *sha1) * for some more deltas. */ if (verbose) - start_progress(&progress, "Resolving %u deltas...", "", nr_deltas); + progress = start_progress("Resolving deltas", nr_deltas); for (i = 0; i < nr_objects; i++) { struct object_entry *obj = &objects[i]; union delta_base base; @@ -486,8 +488,7 @@ static void parse_pack_objects(unsigned char *sha1) obj->size, obj->type); } free(data); - if (verbose) - display_progress(&progress, nr_resolved_deltas); + display_progress(progress, nr_resolved_deltas); } } @@ -594,8 +595,7 @@ static void fix_unresolved_deltas(int nr_unresolved) die("local object %s is corrupt", sha1_to_hex(d->base.sha1)); append_obj_to_pack(d->base.sha1, data, size, type); free(data); - if (verbose) - display_progress(&progress, nr_resolved_deltas); + display_progress(progress, nr_resolved_deltas); } free(sorted_by_pos); } @@ -683,18 +683,31 @@ static void final(const char *final_pack_name, const char *curr_pack_name, } } +static int git_index_pack_config(const char *k, const char *v) +{ + if (!strcmp(k, "pack.indexversion")) { + pack_idx_default_version = git_config_int(k, v); + if (pack_idx_default_version > 2) + die("bad pack.indexversion=%d", pack_idx_default_version); + return 0; + } + return git_default_config(k, v); +} + int main(int argc, char **argv) { int i, fix_thin_pack = 0; - const char *curr_pack, *pack_name = NULL; - const char *curr_index, *index_name = NULL; + char *curr_pack, *pack_name = NULL; + char *curr_index, *index_name = NULL; const char *keep_name = NULL, *keep_msg = NULL; char *index_name_buf = NULL, *keep_name_buf = NULL; struct pack_idx_entry **idx_objects; unsigned char sha1[20]; + git_config(git_index_pack_config); + for (i = 1; i < argc; i++) { - const char *arg = argv[i]; + char *arg = argv[i]; if (*arg == '-') { if (!strcmp(arg, "--stdin")) { @@ -774,8 +787,7 @@ int main(int argc, char **argv) deltas = xmalloc(nr_objects * sizeof(struct delta_entry)); parse_pack_objects(sha1); if (nr_deltas == nr_resolved_deltas) { - if (verbose) - stop_progress(&progress); + stop_progress(&progress); /* Flush remaining pack final 20-byte SHA1. */ flush(); } else { @@ -788,11 +800,10 @@ int main(int argc, char **argv) (nr_objects + nr_unresolved + 1) * sizeof(*objects)); fix_unresolved_deltas(nr_unresolved); - if (verbose) { - stop_progress(&progress); + stop_progress(&progress); + if (verbose) fprintf(stderr, "%d objects were added to complete this thin pack.\n", nr_objects - nr_objects_initial); - } fixup_pack_header_footer(output_fd, sha1, curr_pack, nr_objects); } @@ -815,6 +826,10 @@ int main(int argc, char **argv) free(objects); free(index_name_buf); free(keep_name_buf); + if (pack_name == NULL) + free(curr_pack); + if (index_name == NULL) + free(curr_index); return 0; } diff --git a/pack-write.c b/pack-write.c index 979bdfff7c..665e2b29b8 100644 --- a/pack-write.c +++ b/pack-write.c @@ -17,7 +17,8 @@ static int sha1_compare(const void *_a, const void *_b) * the SHA1 hash of sorted object names. The objects array passed in * will be sorted by SHA1 on exit. */ -const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1) +char *write_idx_file(char *index_name, struct pack_idx_entry **objects, + int nr_objects, unsigned char *sha1) { struct sha1file *f; struct pack_idx_entry **sorted_by_sha, **list, **last; @@ -55,7 +55,7 @@ struct pack_idx_entry { off_t offset; }; -extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1); +extern char *write_idx_file(char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1); extern int verify_pack(struct packed_git *, int); extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t); diff --git a/parse-options.c b/parse-options.c new file mode 100644 index 0000000000..cc09c98ec3 --- /dev/null +++ b/parse-options.c @@ -0,0 +1,322 @@ +#include "git-compat-util.h" +#include "parse-options.h" + +#define OPT_SHORT 1 +#define OPT_UNSET 2 + +struct optparse_t { + const char **argv; + int argc; + const char *opt; +}; + +static inline const char *get_arg(struct optparse_t *p) +{ + if (p->opt) { + const char *res = p->opt; + p->opt = NULL; + return res; + } + p->argc--; + return *++p->argv; +} + +static inline const char *skip_prefix(const char *str, const char *prefix) +{ + size_t len = strlen(prefix); + return strncmp(str, prefix, len) ? NULL : str + len; +} + +static int opterror(const struct option *opt, const char *reason, int flags) +{ + if (flags & OPT_SHORT) + return error("switch `%c' %s", opt->short_name, reason); + if (flags & OPT_UNSET) + return error("option `no-%s' %s", opt->long_name, reason); + return error("option `%s' %s", opt->long_name, reason); +} + +static int get_value(struct optparse_t *p, + const struct option *opt, int flags) +{ + const char *s, *arg; + arg = p->opt ? p->opt : (p->argc > 1 ? p->argv[1] : NULL); + + if (p->opt && (flags & OPT_UNSET)) + return opterror(opt, "takes no value", flags); + + switch (opt->type) { + case OPTION_BOOLEAN: + if (!(flags & OPT_SHORT) && p->opt) + return opterror(opt, "takes no value", flags); + if (flags & OPT_UNSET) + *(int *)opt->value = 0; + else + (*(int *)opt->value)++; + return 0; + + case OPTION_STRING: + if (flags & OPT_UNSET) { + *(const char **)opt->value = (const char *)NULL; + return 0; + } + if (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-')) { + *(const char **)opt->value = (const char *)opt->defval; + return 0; + } + if (!arg) + return opterror(opt, "requires a value", flags); + *(const char **)opt->value = get_arg(p); + return 0; + + case OPTION_CALLBACK: + if (flags & OPT_UNSET) + return (*opt->callback)(opt, NULL, 1); + if (opt->flags & PARSE_OPT_NOARG) { + if (p->opt && !(flags & OPT_SHORT)) + return opterror(opt, "takes no value", flags); + return (*opt->callback)(opt, NULL, 0); + } + if (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-')) + return (*opt->callback)(opt, NULL, 0); + if (!arg) + return opterror(opt, "requires a value", flags); + return (*opt->callback)(opt, get_arg(p), 0); + + case OPTION_INTEGER: + if (flags & OPT_UNSET) { + *(int *)opt->value = 0; + return 0; + } + if (opt->flags & PARSE_OPT_OPTARG && (!arg || !isdigit(*arg))) { + *(int *)opt->value = opt->defval; + return 0; + } + if (!arg) + return opterror(opt, "requires a value", flags); + *(int *)opt->value = strtol(get_arg(p), (char **)&s, 10); + if (*s) + return opterror(opt, "expects a numerical value", flags); + return 0; + + default: + die("should not happen, someone must be hit on the forehead"); + } +} + +static int parse_short_opt(struct optparse_t *p, const struct option *options) +{ + for (; options->type != OPTION_END; options++) { + if (options->short_name == *p->opt) { + p->opt = p->opt[1] ? p->opt + 1 : NULL; + return get_value(p, options, OPT_SHORT); + } + } + return error("unknown switch `%c'", *p->opt); +} + +static int parse_long_opt(struct optparse_t *p, const char *arg, + const struct option *options) +{ + const char *arg_end = strchr(arg, '='); + const struct option *abbrev_option = NULL; + int abbrev_flags = 0; + + if (!arg_end) + arg_end = arg + strlen(arg); + + for (; options->type != OPTION_END; options++) { + const char *rest; + int flags = 0; + + if (!options->long_name) + continue; + + rest = skip_prefix(arg, options->long_name); + if (!rest) { + /* abbreviated? */ + if (!strncmp(options->long_name, arg, arg_end - arg)) { +is_abbreviated: + if (abbrev_option) + return error("Ambiguous option: %s " + "(could be --%s%s or --%s%s)", + arg, + (flags & OPT_UNSET) ? + "no-" : "", + options->long_name, + (abbrev_flags & OPT_UNSET) ? + "no-" : "", + abbrev_option->long_name); + if (!(flags & OPT_UNSET) && *arg_end) + p->opt = arg_end + 1; + abbrev_option = options; + abbrev_flags = flags; + continue; + } + /* negated and abbreviated very much? */ + if (!prefixcmp("no-", arg)) { + flags |= OPT_UNSET; + goto is_abbreviated; + } + /* negated? */ + if (strncmp(arg, "no-", 3)) + continue; + flags |= OPT_UNSET; + rest = skip_prefix(arg + 3, options->long_name); + /* abbreviated and negated? */ + if (!rest && !prefixcmp(options->long_name, arg + 3)) + goto is_abbreviated; + if (!rest) + continue; + } + if (*rest) { + if (*rest != '=') + continue; + p->opt = rest + 1; + } + return get_value(p, options, flags); + } + if (abbrev_option) + return get_value(p, abbrev_option, abbrev_flags); + return error("unknown option `%s'", arg); +} + +int parse_options(int argc, const char **argv, const struct option *options, + const char * const usagestr[], int flags) +{ + struct optparse_t args = { argv + 1, argc - 1, NULL }; + int j = 0; + + for (; args.argc; args.argc--, args.argv++) { + const char *arg = args.argv[0]; + + if (*arg != '-' || !arg[1]) { + argv[j++] = args.argv[0]; + continue; + } + + if (arg[1] != '-') { + args.opt = arg + 1; + do { + if (*args.opt == 'h') + usage_with_options(usagestr, options); + if (parse_short_opt(&args, options) < 0) + usage_with_options(usagestr, options); + } while (args.opt); + continue; + } + + if (!arg[2]) { /* "--" */ + if (!(flags & PARSE_OPT_KEEP_DASHDASH)) { + args.argc--; + args.argv++; + } + break; + } + + if (!strcmp(arg + 2, "help")) + usage_with_options(usagestr, options); + if (parse_long_opt(&args, arg + 2, options)) + usage_with_options(usagestr, options); + } + + memmove(argv + j, args.argv, args.argc * sizeof(*argv)); + argv[j + args.argc] = NULL; + return j + args.argc; +} + +#define USAGE_OPTS_WIDTH 24 +#define USAGE_GAP 2 + +void usage_with_options(const char * const *usagestr, + const struct option *opts) +{ + fprintf(stderr, "usage: %s\n", *usagestr++); + while (*usagestr && **usagestr) + fprintf(stderr, " or: %s\n", *usagestr++); + while (*usagestr) + fprintf(stderr, " %s\n", *usagestr++); + + if (opts->type != OPTION_GROUP) + fputc('\n', stderr); + + for (; opts->type != OPTION_END; opts++) { + size_t pos; + int pad; + + if (opts->type == OPTION_GROUP) { + fputc('\n', stderr); + if (*opts->help) + fprintf(stderr, "%s\n", opts->help); + continue; + } + + pos = fprintf(stderr, " "); + if (opts->short_name) + pos += fprintf(stderr, "-%c", opts->short_name); + if (opts->long_name && opts->short_name) + pos += fprintf(stderr, ", "); + if (opts->long_name) + pos += fprintf(stderr, "--%s", opts->long_name); + + switch (opts->type) { + case OPTION_INTEGER: + if (opts->flags & PARSE_OPT_OPTARG) + pos += fprintf(stderr, " [<n>]"); + else + pos += fprintf(stderr, " <n>"); + break; + case OPTION_CALLBACK: + if (opts->flags & PARSE_OPT_NOARG) + break; + /* FALLTHROUGH */ + case OPTION_STRING: + if (opts->argh) { + if (opts->flags & PARSE_OPT_OPTARG) + pos += fprintf(stderr, " [<%s>]", opts->argh); + else + pos += fprintf(stderr, " <%s>", opts->argh); + } else { + if (opts->flags & PARSE_OPT_OPTARG) + pos += fprintf(stderr, " [...]"); + else + pos += fprintf(stderr, " ..."); + } + break; + default: + break; + } + + if (pos <= USAGE_OPTS_WIDTH) + pad = USAGE_OPTS_WIDTH - pos; + else { + fputc('\n', stderr); + pad = USAGE_OPTS_WIDTH; + } + fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); + } + fputc('\n', stderr); + + exit(129); +} + +/*----- some often used options -----*/ +#include "cache.h" +int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset) +{ + int v; + + if (!arg) { + v = unset ? 0 : DEFAULT_ABBREV; + } else { + v = strtol(arg, (char **)&arg, 10); + if (*arg) + return opterror(opt, "expects a numerical value", 0); + if (v && v < MINIMUM_ABBREV) + v = MINIMUM_ABBREV; + else if (v > 40) + v = 40; + } + *(int *)(opt->value) = v; + return 0; +} diff --git a/parse-options.h b/parse-options.h new file mode 100644 index 0000000000..3a470e5eb8 --- /dev/null +++ b/parse-options.h @@ -0,0 +1,70 @@ +#ifndef PARSE_OPTIONS_H +#define PARSE_OPTIONS_H + +enum parse_opt_type { + OPTION_END, + OPTION_GROUP, + OPTION_BOOLEAN, + OPTION_STRING, + OPTION_INTEGER, + OPTION_CALLBACK, +}; + +enum parse_opt_flags { + PARSE_OPT_KEEP_DASHDASH = 1, +}; + +enum parse_opt_option_flags { + PARSE_OPT_OPTARG = 1, + PARSE_OPT_NOARG = 2, +}; + +struct option; +typedef int parse_opt_cb(const struct option *, const char *arg, int unset); + +struct option { + enum parse_opt_type type; + int short_name; + const char *long_name; + void *value; + const char *argh; + const char *help; + + int flags; + parse_opt_cb *callback; + /* holds default value for PARSE_OPT_OPTARG, + though callbacks can use it like they want */ + intptr_t defval; +}; + +#define OPT_END() { OPTION_END } +#define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) } +#define OPT_BOOLEAN(s, l, v, h) { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) } +#define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), NULL, (h) } +#define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v), (a), (h) } +#define OPT_CALLBACK(s, l, v, a, h, f) \ + { OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) } + +/* parse_options() will filter out the processed options and leave the + * non-option argments in argv[]. + * Returns the number of arguments left in argv[]. + */ +extern int parse_options(int argc, const char **argv, + const struct option *options, + const char * const usagestr[], int flags); + +extern NORETURN void usage_with_options(const char * const *usagestr, + const struct option *options); + +/*----- some often used options -----*/ +extern int parse_opt_abbrev_cb(const struct option *, const char *, int); + +#define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose") +#define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet") +#define OPT__DRY_RUN(var) OPT_BOOLEAN('n', "dry-run", (var), "dry run") +#define OPT__ABBREV(var) \ + { OPTION_CALLBACK, 0, "abbrev", (var), "n", \ + "use <n> digits to display SHA-1s", \ + PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 } + +#endif diff --git a/progress.c b/progress.c index 4344f4eed5..3f6a602a53 100644 --- a/progress.c +++ b/progress.c @@ -1,6 +1,40 @@ +/* + * Simple text-based progress display module for GIT + * + * Copyright (c) 2007 by Nicolas Pitre <nico@cam.org> + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + #include "git-compat-util.h" #include "progress.h" +#define TP_IDX_MAX 8 + +struct throughput { + struct timeval prev_tv; + off_t total; + unsigned long count; + unsigned long avg_bytes; + unsigned long last_bytes[TP_IDX_MAX]; + unsigned int avg_misecs; + unsigned int last_misecs[TP_IDX_MAX]; + unsigned int idx; + char display[32]; +}; + +struct progress { + const char *title; + int last_value; + unsigned total; + unsigned last_percent; + unsigned delay; + unsigned delayed_percent_treshold; + struct throughput *throughput; +}; + static volatile sig_atomic_t progress_update; static void progress_interval(int signum) @@ -35,10 +69,11 @@ static void clear_progress_signal(void) progress_update = 0; } -int display_progress(struct progress *progress, unsigned n) +static int display(struct progress *progress, unsigned n, int done) { + char *eol, *tp; + if (progress->delay) { - char buf[80]; if (!progress_update || --progress->delay) return 0; if (progress->total) { @@ -51,60 +86,151 @@ int display_progress(struct progress *progress, unsigned n) return 0; } } - if (snprintf(buf, sizeof(buf), - progress->delayed_title, progress->total)) - fprintf(stderr, "%s\n", buf); } + + progress->last_value = n; + tp = (progress->throughput) ? progress->throughput->display : ""; + eol = done ? ", done. \n" : " \r"; if (progress->total) { unsigned percent = n * 100 / progress->total; if (percent != progress->last_percent || progress_update) { progress->last_percent = percent; - fprintf(stderr, "%s%4u%% (%u/%u) done\r", - progress->prefix, percent, n, progress->total); + fprintf(stderr, "%s: %3u%% (%u/%u)%s%s", + progress->title, percent, n, + progress->total, tp, eol); progress_update = 0; - progress->need_lf = 1; return 1; } } else if (progress_update) { - fprintf(stderr, "%s%u\r", progress->prefix, n); + fprintf(stderr, "%s: %u%s%s", progress->title, n, tp, eol); progress_update = 0; - progress->need_lf = 1; return 1; } + return 0; } -void start_progress(struct progress *progress, const char *title, - const char *prefix, unsigned total) +void display_throughput(struct progress *progress, unsigned long n) { - char buf[80]; - progress->prefix = prefix; - progress->total = total; - progress->last_percent = -1; - progress->delay = 0; - progress->need_lf = 0; - if (snprintf(buf, sizeof(buf), title, total)) - fprintf(stderr, "%s\n", buf); - set_progress_signal(); + struct throughput *tp; + struct timeval tv; + unsigned int misecs; + + if (!progress) + return; + tp = progress->throughput; + + gettimeofday(&tv, NULL); + + if (!tp) { + progress->throughput = tp = calloc(1, sizeof(*tp)); + if (tp) + tp->prev_tv = tv; + return; + } + + tp->total += n; + tp->count += n; + + /* + * We have x = bytes and y = microsecs. We want z = KiB/s: + * + * z = (x / 1024) / (y / 1000000) + * z = x / y * 1000000 / 1024 + * z = x / (y * 1024 / 1000000) + * z = x / y' + * + * To simplify things we'll keep track of misecs, or 1024th of a sec + * obtained with: + * + * y' = y * 1024 / 1000000 + * y' = y / (1000000 / 1024) + * y' = y / 977 + */ + misecs = (tv.tv_sec - tp->prev_tv.tv_sec) * 1024; + misecs += (int)(tv.tv_usec - tp->prev_tv.tv_usec) / 977; + + if (misecs > 512) { + int l = sizeof(tp->display); + tp->prev_tv = tv; + tp->avg_bytes += tp->count; + tp->avg_misecs += misecs; + + if (tp->total > 1 << 30) { + l -= snprintf(tp->display, l, ", %u.%2.2u GiB", + (int)(tp->total >> 30), + (int)(tp->total & ((1 << 30) - 1)) / 10737419); + } else if (tp->total > 1 << 20) { + l -= snprintf(tp->display, l, ", %u.%2.2u MiB", + (int)(tp->total >> 20), + ((int)(tp->total & ((1 << 20) - 1)) + * 100) >> 20); + } else if (tp->total > 1 << 10) { + l -= snprintf(tp->display, l, ", %u.%2.2u KiB", + (int)(tp->total >> 10), + ((int)(tp->total & ((1 << 10) - 1)) + * 100) >> 10); + } else { + l -= snprintf(tp->display, l, ", %u bytes", + (int)tp->total); + } + snprintf(tp->display + sizeof(tp->display) - l, l, + " | %lu KiB/s", tp->avg_bytes / tp->avg_misecs); + + tp->avg_bytes -= tp->last_bytes[tp->idx]; + tp->avg_misecs -= tp->last_misecs[tp->idx]; + tp->last_bytes[tp->idx] = tp->count; + tp->last_misecs[tp->idx] = misecs; + tp->idx = (tp->idx + 1) % TP_IDX_MAX; + tp->count = 0; + + if (progress->last_value != -1 && progress_update) + display(progress, progress->last_value, 0); + } +} + +int display_progress(struct progress *progress, unsigned n) +{ + return progress ? display(progress, n, 0) : 0; } -void start_progress_delay(struct progress *progress, const char *title, - const char *prefix, unsigned total, - unsigned percent_treshold, unsigned delay) +struct progress *start_progress_delay(const char *title, unsigned total, + unsigned percent_treshold, unsigned delay) { - progress->prefix = prefix; + struct progress *progress = malloc(sizeof(*progress)); + if (!progress) { + /* unlikely, but here's a good fallback */ + fprintf(stderr, "%s...\n", title); + return NULL; + } + progress->title = title; progress->total = total; + progress->last_value = -1; progress->last_percent = -1; progress->delayed_percent_treshold = percent_treshold; - progress->delayed_title = title; progress->delay = delay; - progress->need_lf = 0; + progress->throughput = NULL; set_progress_signal(); + return progress; } -void stop_progress(struct progress *progress) +struct progress *start_progress(const char *title, unsigned total) { + return start_progress_delay(title, total, 0, 0); +} + +void stop_progress(struct progress **p_progress) +{ + struct progress *progress = *p_progress; + if (!progress) + return; + *p_progress = NULL; + if (progress->last_value != -1) { + /* Force the last update */ + progress_update = 1; + display(progress, progress->last_value, 1); + } clear_progress_signal(); - if (progress->need_lf) - fputc('\n', stderr); + free(progress->throughput); + free(progress); } diff --git a/progress.h b/progress.h index a7c17ca7c4..61cb68dfa5 100644 --- a/progress.h +++ b/progress.h @@ -1,22 +1,13 @@ #ifndef PROGRESS_H #define PROGRESS_H -struct progress { - const char *prefix; - unsigned total; - unsigned last_percent; - unsigned delay; - unsigned delayed_percent_treshold; - const char *delayed_title; - int need_lf; -}; +struct progress; +void display_throughput(struct progress *progress, unsigned long n); int display_progress(struct progress *progress, unsigned n); -void start_progress(struct progress *progress, const char *title, - const char *prefix, unsigned total); -void start_progress_delay(struct progress *progress, const char *title, - const char *prefix, unsigned total, - unsigned percent_treshold, unsigned delay); -void stop_progress(struct progress *progress); +struct progress *start_progress(const char *title, unsigned total); +struct progress *start_progress_delay(const char *title, unsigned total, + unsigned percent_treshold, unsigned delay); +void stop_progress(struct progress **progress); #endif diff --git a/send-pack.c b/send-pack.c index 5e127a1b7b..b74fd454f2 100644 --- a/send-pack.c +++ b/send-pack.c @@ -297,9 +297,9 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha * commits at the remote end and likely * we were not up to date to begin with. */ - error("remote '%s' is not a strict " - "subset of local ref '%s'. " - "maybe you are not up-to-date and " + error("remote '%s' is not an ancestor of\n" + " local '%s'.\n" + " Maybe you are not up-to-date and " "need to pull first?", ref->name, ref->peer_ref->name); diff --git a/sideband.c b/sideband.c index 277fa3c10d..ab8a1e990d 100644 --- a/sideband.c +++ b/sideband.c @@ -17,7 +17,7 @@ int recv_sideband(const char *me, int in_stream, int out, int err) strcpy(buf, "remote:"); while (1) { int band, len; - len = packet_read_line(in_stream, buf+7, LARGE_PACKET_MAX); + len = packet_read_line(in_stream, buf+7, LARGE_PACKET_MAX); if (len == 0) break; if (len < 1) { @@ -35,7 +35,22 @@ int recv_sideband(const char *me, int in_stream, int out, int err) return SIDEBAND_REMOTE_ERROR; case 2: buf[7] = ' '; - safe_write(err, buf, 8+len); + len += 8; + while (1) { + int brk = 8; + while (brk < len) { + brk++; + if (buf[brk-1] == '\n' || + buf[brk-1] == '\r') + break; + } + safe_write(err, buf, brk); + if (brk < len) { + memmove(buf + 8, buf + brk, len - brk); + len = len - brk + 8; + } else + break; + } continue; case 1: safe_write(out, buf+8, len); diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh new file mode 100755 index 0000000000..ae49424aa0 --- /dev/null +++ b/t/t0040-parse-options.sh @@ -0,0 +1,93 @@ +#!/bin/sh +# +# Copyright (c) 2007 Johannes Schindelin +# + +test_description='our own option parser' + +. ./test-lib.sh + +cat > expect.err << EOF +usage: test-parse-options <options> + + -b, --boolean get a boolean + -i, --integer <n> get a integer + -j <n> get a integer, too + +string options + -s, --string <string> + get a string + --string2 <str> get another string + +EOF + +test_expect_success 'test help' ' + ! test-parse-options -h > output 2> output.err && + test ! -s output && + git diff expect.err output.err +' + +cat > expect << EOF +boolean: 2 +integer: 1729 +string: 123 +EOF + +test_expect_success 'short options' ' + test-parse-options -s123 -b -i 1729 -b > output 2> output.err && + git diff expect output && + test ! -s output.err +' +cat > expect << EOF +boolean: 2 +integer: 1729 +string: 321 +EOF + +test_expect_success 'long options' ' + test-parse-options --boolean --integer 1729 --boolean --string2=321 \ + > output 2> output.err && + test ! -s output.err && + git diff expect output +' + +cat > expect << EOF +boolean: 1 +integer: 13 +string: 123 +arg 00: a1 +arg 01: b1 +arg 02: --boolean +EOF + +test_expect_success 'intermingled arguments' ' + test-parse-options a1 --string 123 b1 --boolean -j 13 -- --boolean \ + > output 2> output.err && + test ! -s output.err && + git diff expect output +' + +cat > expect << EOF +boolean: 0 +integer: 2 +string: (not set) +EOF + +test_expect_success 'unambiguously abbreviated option' ' + test-parse-options --int 2 --boolean --no-bo > output 2> output.err && + test ! -s output.err && + git diff expect output +' + +test_expect_success 'unambiguously abbreviated option with "="' ' + test-parse-options --int=2 > output 2> output.err && + test ! -s output.err && + git diff expect output +' + +test_expect_failure 'ambiguously abbreviated option' ' + test-parse-options --strin 123; + test $? != 129 +' + +test_done diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh index 552af1c4d2..2dbe04fb20 100755 --- a/t/t3501-revert-cherry-pick.sh +++ b/t/t3501-revert-cherry-pick.sh @@ -44,7 +44,7 @@ test_expect_success setup ' test_expect_success 'cherry-pick after renaming branch' ' git checkout rename2 && - EDITOR=: VISUAL=: git cherry-pick added && + git cherry-pick added && test -f opos && grep "Add extra line at the end" opos @@ -53,7 +53,7 @@ test_expect_success 'cherry-pick after renaming branch' ' test_expect_success 'revert after renaming branch' ' git checkout rename1 && - EDITOR=: VISUAL=: git revert added && + git revert added && test -f spoo && ! grep "Add extra line at the end" spoo diff --git a/t/t3502-cherry-pick-merge.sh b/t/t3502-cherry-pick-merge.sh new file mode 100755 index 0000000000..3274c6141b --- /dev/null +++ b/t/t3502-cherry-pick-merge.sh @@ -0,0 +1,123 @@ +#!/bin/sh + +test_description='cherry picking and reverting a merge + + b---c + / / + initial---a + +' + +. ./test-lib.sh + +test_expect_success setup ' + + >A && + >B && + git add A B && + git commit -m "Initial" && + git tag initial && + git branch side && + echo new line >A && + git commit -m "add line to A" A && + git tag a && + git checkout side && + echo new line >B && + git commit -m "add line to B" B && + git tag b && + git checkout master && + git merge side && + git tag c + +' + +test_expect_success 'cherry-pick a non-merge with -m should fail' ' + + git reset --hard && + git checkout a^0 && + ! git cherry-pick -m 1 b && + git diff --exit-code a + +' + +test_expect_success 'cherry pick a merge without -m should fail' ' + + git reset --hard && + git checkout a^0 && + ! git cherry-pick c && + git diff --exit-code a + +' + +test_expect_success 'cherry pick a merge (1)' ' + + git reset --hard && + git checkout a^0 && + git cherry-pick -m 1 c && + git diff --exit-code c + +' + +test_expect_success 'cherry pick a merge (2)' ' + + git reset --hard && + git checkout b^0 && + git cherry-pick -m 2 c && + git diff --exit-code c + +' + +test_expect_success 'cherry pick a merge relative to nonexistent parent should fail' ' + + git reset --hard && + git checkout b^0 && + ! git cherry-pick -m 3 c + +' + +test_expect_success 'revert a non-merge with -m should fail' ' + + git reset --hard && + git checkout c^0 && + ! git revert -m 1 b && + git diff --exit-code c + +' + +test_expect_success 'revert a merge without -m should fail' ' + + git reset --hard && + git checkout c^0 && + ! git revert c && + git diff --exit-code c + +' + +test_expect_success 'revert a merge (1)' ' + + git reset --hard && + git checkout c^0 && + git revert -m 1 c && + git diff --exit-code a + +' + +test_expect_success 'revert a merge (2)' ' + + git reset --hard && + git checkout c^0 && + git revert -m 2 c && + git diff --exit-code b + +' + +test_expect_success 'revert a merge relative to nonexistent parent should fail' ' + + git reset --hard && + git checkout c^0 && + ! git revert -m 3 c && + git diff --exit-code c + +' + +test_done diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh index 28e9e372f3..235f372832 100755 --- a/t/t3901-i18n-patch.sh +++ b/t/t3901-i18n-patch.sh @@ -154,7 +154,7 @@ test_expect_success 'cherry-pick(U/U)' ' git reset --hard master && git cherry-pick side^ && git cherry-pick side && - EDITOR=: VISUAL=: git revert HEAD && + git revert HEAD && check_encoding 3 ' @@ -169,7 +169,7 @@ test_expect_success 'cherry-pick(L/L)' ' git reset --hard master && git cherry-pick side^ && git cherry-pick side && - EDITOR=: VISUAL=: git revert HEAD && + git revert HEAD && check_encoding 3 8859 ' @@ -184,7 +184,7 @@ test_expect_success 'cherry-pick(U/L)' ' git reset --hard master && git cherry-pick side^ && git cherry-pick side && - EDITOR=: VISUAL=: git revert HEAD && + git revert HEAD && check_encoding 3 ' @@ -200,7 +200,7 @@ test_expect_success 'cherry-pick(L/U)' ' git reset --hard master && git cherry-pick side^ && git cherry-pick side && - EDITOR=: VISUAL=: git revert HEAD && + git revert HEAD && check_encoding 3 8859 ' diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox index b80c981c16..070c1661b9 100644 --- a/t/t5100/sample.mbox +++ b/t/t5100/sample.mbox @@ -1,3 +1,6 @@ + + + From nobody Mon Sep 17 00:00:00 2001 From: A U Thor <a.u.thor@example.com> Date: Fri, 9 Jun 2006 00:44:16 -0700 diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index d0809eb651..d0809eb651 100644..100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index f64b1cbf75..cea9afb764 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -402,4 +402,11 @@ test_expect_success 'test resetting the index at give paths' ' ' +test_expect_success 'resetting an unmodified path is a no-op' ' + git reset --hard && + git reset -- file1 && + git diff-files --exit-code && + git diff-index --cached --exit-code HEAD +' + test_done diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index f7bad5bb2f..35fff3ddba 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -31,7 +31,6 @@ our \$projects_list = ""; our \$export_ok = ""; our \$strict_export = ""; -CGI::Carp::set_programname("gitweb/gitweb.cgi"); EOF cat >.git/description <<EOF @@ -558,4 +557,27 @@ test_expect_success \ 'gitweb_run "p=.git;a=tree;opt=--no-merges"' test_debug 'cat gitweb.log' +# ---------------------------------------------------------------------- +# gitweb config and repo config + +cat >>gitweb_config.perl <<EOF + +\$feature{'blame'}{'override'} = 1; +\$feature{'snapshot'}{'override'} = 1; +EOF + +test_expect_success \ + 'config override: tree view, features disabled in repo config' \ + 'git config gitweb.blame no && + git config gitweb.snapshot none && + gitweb_run "p=.git;a=tree"' +test_debug 'cat gitweb.log' + +test_expect_success \ + 'config override: tree view, features enabled in repo config' \ + 'git config gitweb.blame yes && + git config gitweb.snapshot "zip,tgz, tbz2" && + gitweb_run "p=.git;a=tree"' +test_debug 'cat gitweb.log' + test_done diff --git a/test-parse-options.c b/test-parse-options.c new file mode 100644 index 0000000000..277cfe4d6d --- /dev/null +++ b/test-parse-options.c @@ -0,0 +1,35 @@ +#include "cache.h" +#include "parse-options.h" + +static int boolean = 0; +static int integer = 0; +static char *string = NULL; + +int main(int argc, const char **argv) +{ + const char *usage[] = { + "test-parse-options <options>", + NULL + }; + struct option options[] = { + OPT_BOOLEAN('b', "boolean", &boolean, "get a boolean"), + OPT_INTEGER('i', "integer", &integer, "get a integer"), + OPT_INTEGER('j', NULL, &integer, "get a integer, too"), + OPT_GROUP("string options"), + OPT_STRING('s', "string", &string, "string", "get a string"), + OPT_STRING(0, "string2", &string, "str", "get another string"), + OPT_END(), + }; + int i; + + argc = parse_options(argc, argv, options, usage, 0); + + printf("boolean: %d\n", boolean); + printf("integer: %d\n", integer); + printf("string: %s\n", string ? string : "(not set)"); + + for (i = 0; i < argc; i++) + printf("arg %02d: %s\n", i, argv[i]); + + return 0; +} diff --git a/transport.c b/transport.c index 5132d289da..d44fe7cee7 100644 --- a/transport.c +++ b/transport.c @@ -107,7 +107,7 @@ static void insert_packed_refs(const char *packed_refs, struct ref **list) return; for (;;) { - int cmp, len; + int cmp = cmp, len; if (!fgets(buffer, sizeof(buffer), f)) { fclose(f); diff --git a/unpack-trees.c b/unpack-trees.c index ccfeb6e245..c527d7d049 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -297,7 +297,7 @@ static void check_updates(struct cache_entry **src, int nr, { unsigned short mask = htons(CE_UPDATE); unsigned cnt = 0, total = 0; - struct progress progress; + struct progress *progress = NULL; char last_symlink[PATH_MAX]; if (o->update && o->verbose_update) { @@ -307,8 +307,8 @@ static void check_updates(struct cache_entry **src, int nr, total++; } - start_progress_delay(&progress, "Checking %u files out...", - "", total, 50, 2); + progress = start_progress_delay("Checking out files", + total, 50, 2); cnt = 0; } @@ -316,9 +316,8 @@ static void check_updates(struct cache_entry **src, int nr, while (nr--) { struct cache_entry *ce = *src++; - if (total) - if (!ce->ce_mode || ce->ce_flags & mask) - display_progress(&progress, ++cnt); + if (!ce->ce_mode || ce->ce_flags & mask) + display_progress(progress, ++cnt); if (!ce->ce_mode) { if (o->update) unlink_entry(ce->name, last_symlink); @@ -332,8 +331,7 @@ static void check_updates(struct cache_entry **src, int nr, } } } - if (total) - stop_progress(&progress);; + stop_progress(&progress); } int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o) |