test-dump-cache-tree
test-genrandom
test-match-trees
+test-parse-options
test-sha1
common-cmds.h
*.tar.gz
--- /dev/null
+Like other projects, we also have some guidelines to keep to the
+code. For git in general, three rough rules are:
+
+ - Most importantly, we never say "It's in POSIX; we'll happily
+ ignore your needs should your system not conform to it."
+ We live in the real world.
+
+ - However, we often say "Let's stay away from that construct,
+ it's not even in POSIX".
+
+ - In spite of the above two rules, we sometimes say "Although
+ this is not in POSIX, it (is so convenient | makes the code
+ much more readable | has other good characteristics) and
+ practically all the platforms we care about support it, so
+ let's use it".
+
+ Again, we live in the real world, and it is sometimes a
+ judgement call, the decision based more on real world
+ constraints people face than what the paper standard says.
+
+
+As for more concrete guidelines, just imitate the existing code
+(this is a good guideline, no matter which project you are
+contributing to). But if you must have a list of rules,
+here they are.
+
+For shell scripts specifically (not exhaustive):
+
+ - We prefer $( ... ) for command substitution; unlike ``, it
+ properly nests. It should have been the way Bourne spelled
+ it from day one, but unfortunately isn't.
+
+ - We use ${parameter-word} and its [-=?+] siblings, and their
+ colon'ed "unset or null" form.
+
+ - We use ${parameter#word} and its [#%] siblings, and their
+ doubled "longest matching" form.
+
+ - We use Arithmetic Expansion $(( ... )).
+
+ - No "Substring Expansion" ${parameter:offset:length}.
+
+ - No shell arrays.
+
+ - No strlen ${#parameter}.
+
+ - No regexp ${parameter/pattern/string}.
+
+ - We do not use Process Substitution <(list) or >(list).
+
+ - We prefer "test" over "[ ... ]".
+
+ - We do not write the noiseword "function" in front of shell
+ functions.
+
+For C programs:
+
+ - We use tabs to indent, and interpret tabs as taking up to
+ 8 spaces.
+
+ - We try to keep to at most 80 characters per line.
+
+ - When declaring pointers, the star sides with the variable
+ name, i.e. "char *string", not "char* string" or
+ "char * string". This makes it easier to understand code
+ like "char *string, c;".
+
+ - We avoid using braces unnecessarily. I.e.
+
+ if (bla) {
+ x = 1;
+ }
+
+ is frowned upon. A gray area is when the statement extends
+ over a few lines, and/or you have a lengthy comment atop of
+ it. Also, like in the Linux kernel, if there is a long list
+ of "else if" statements, it can make sense to add braces to
+ single line blocks.
+
+ - Try to make your code understandable. You may put comments
+ in, but comments invariably tend to stale out when the code
+ they were describing changes. Often splitting a function
+ into two makes the intention of the code much clearer.
+
+ - Double negation is often harder to understand than no negation
+ at all.
+
+ - Some clever tricks, like using the !! operator with arithmetic
+ constructs, can be extremely confusing to others. Avoid them,
+ unless there is a compelling reason to use them.
+
+ - Use the API. No, really. We have a strbuf (variable length
+ string), several arrays with the ALLOC_GROW() macro, a
+ path_list for sorted string lists, a hash map (mapping struct
+ objects) named "struct decorate", amongst other things.
+
+ - When you come up with an API, document it.
+
+ - The first #include in C files, except in platform specific
+ compat/ implementations, should be git-compat-util.h or another
+ header file that includes it, such as cache.h or builtin.h.
+
+ - If you are planning a new command, consider writing it in shell
+ or perl first, so that changes in semantics can be easily
+ changed and discussed. Many git commands started out like
+ that, and a few are still scripts.
+
+ - Avoid introducing a new dependency into git. This means you
+ usually should stay away from scripting languages not already
+ used in the git core command set (unless your command is clearly
+ separate from it, such as an importer to convert random-scm-X
+ repositories to git).
* 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".
* "git-send-pack $remote frotz" segfaulted when there is nothing
named 'frotz' on the local end.
- * "git-rebase -interactive" did not handle its "--strategy" option
+ * "git-rebase --interactive" did not handle its "--strategy" option
properly.
* Various Perforce importer updates.
+ * git-lost-found was deprecated in favor of git-fsck's --lost-found
+ option.
+
Fixes since v1.5.3
------------------
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.
------------------------------------------------
$ 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
------------
$ 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.
'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:
-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:
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:
--------------------------
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.
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
$ 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>
------------
+
$ 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>
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::
-a::
List both remote-tracking branches and local branches.
--v::
+-v, --verbose::
Show sha1 and commit subject line for each head.
--abbrev=<length>::
SYNOPSIS
--------
-'git-cherry-pick' [--edit] [-n] [-x] <commit>
+'git-cherry-pick' [--edit] [-n] [-m parent-number] [-x] <commit>
DESCRIPTION
-----------
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
'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
-----------
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
--------
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.
DESCRIPTION
-----------
+
+*NOTE*: this command is deprecated. Use gitlink:git-fsck[1] with
+the option '--lost-found' instead.
+
Finds dangling commits and tags from the object database, and
creates refs to them in the .git/lost-found/ directory. Commits and
tags that dereference to commits are stored in .git/lost-found/commit,
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
------------
$ 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
SYNOPSIS
--------
-'git-revert' [--edit | --no-edit] [-n] <commit>
+'git-revert' [--edit | --no-edit] [-n] [-m parent-number] <commit>
DESCRIPTION
-----------
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.
-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.
\--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.
is not set, this will be prompted for.
--suppress-from, --no-suppress-from::
- If this is set, do not add the From: address to the cc: list, if it
- shows up in a From: line.
+ If this is set, do not add the From: address to the cc: list.
Default is the value of 'sendemail.suppressfrom' configuration value;
if that is unspecified, default to --no-suppress-from.
OPTIONS
-------
--s\|--strip-comments::
+-s|--strip-comments::
In addition to empty lines, also strip lines starting with '#'.
<stream>::
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.
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 \
LIB_OBJS = \
blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
date.o diff-delta.o entry.o exec_cmd.o ident.o \
- interpolate.o hash.o \
+ pretty.o interpolate.o hash.o \
lockfile.o \
patch-ids.o \
object.o pack-check.o pack-write.o patch-delta.o path.o pkt-line.o \
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 \
### 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)
#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;
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());
}
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);
argv[unk++] = arg;
}
- if (!incremental)
- setup_pager();
-
if (!blame_move_score)
blame_move_score = BLAME_DEFAULT_MOVE_SCORE;
if (!blame_copy_score)
* do not default to HEAD, but use the working tree
* or "--contents".
*/
+ setup_work_tree();
sb.final = fake_working_tree_commit(path, contents_from);
add_pending_object(&revs, &(sb.final->object), ":");
}
read_mailmap(&mailmap, ".mailmap", NULL);
+ if (!incremental)
+ setup_pager();
+
assign_blame(&sb, &revs, opt);
if (incremental)
#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
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]);
commit = lookup_commit(item->sha1);
if (commit && !parse_commit(commit)) {
pretty_print_commit(CMIT_FMT_ONELINE, commit,
- &subject, 0, NULL, NULL, 0);
+ &subject, 0, NULL, NULL, 0, 0);
sub = subject.buf;
}
printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color),
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)
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;
}
#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,
}
}
-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++] = '/';
#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 */
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;
}
#define MAX_IN_VAIN 256
static struct commit_list *rev_list;
-static int non_common_revs, multi_ack, use_thin_pack, use_sideband;
+static int non_common_revs, multi_ack, use_sideband;
static void rev_list_push(struct commit *commit, int mark)
{
(multi_ack ? " multi_ack" : ""),
(use_sideband == 2 ? " side-band-64k" : ""),
(use_sideband == 1 ? " side-band" : ""),
- (use_thin_pack ? " thin-pack" : ""),
+ (args.use_thin_pack ? " thin-pack" : ""),
(args.no_progress ? " no-progress" : ""),
" ofs-delta");
else
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)
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;
}
* 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;
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);
}
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);
depth = argv[i];
continue;
}
- if (!strcmp(arg, "--quiet")) {
+ if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) {
quiet = 1;
continue;
}
#include "tree.h"
#include "blob.h"
#include "quote.h"
+#include "parse-options.h"
/* Quoting styles */
#define QUOTE_NONE 0
* 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;
}
/*
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;
#include "pack.h"
#include "cache-tree.h"
#include "tree-walk.h"
+#include "parse-options.h"
#define REACHABLE 0x0001
#define SEEN 0x0002
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)
{
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();
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);
}
if (keep_cache_objects) {
- int i;
read_cache();
for (i = 0; i < active_nr; i++) {
unsigned int mode;
#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;
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 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) {
/*
memcpy(name + 2, ce->name, len + 1);
}
argv[argc++] = name;
- if (argc < MAXARGS && !ce_stage(ce))
+ if (argc < MAXARGS)
continue;
status = flush_grep(opt, argc, nr, argv, &kept);
if (0 < status)
struct strbuf buf;
strbuf_init(&buf, 0);
pretty_print_commit(CMIT_FMT_ONELINE, commit,
- &buf, 0, NULL, NULL, 0);
+ &buf, 0, NULL, NULL, 0, 0);
printf("%c %s %s\n", sign,
sha1_to_hex(commit->object.sha1), buf.buf);
strbuf_release(&buf);
break;
}
- if (require_work_tree && !is_inside_work_tree()) {
- const char *work_tree = get_git_work_tree();
- if (!work_tree || chdir(work_tree))
- die("This operation must be run in a work tree");
- }
+ if (require_work_tree && !is_inside_work_tree())
+ setup_work_tree();
pathspec = get_pathspec(prefix, argv + i);
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;
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);
{
char name[PATH_MAX];
int ret = -1;
+ int peek;
FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
int file_done = 0;
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) {
#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)
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;
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;
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));
}
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";
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",
}
}
- 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)
path_list_insert(dst, &added);
}
- if (show_only) {
+ if (show_only) {
show_list("Changed : ", &changed);
show_list("Adding : ", &added);
show_list("Deleting : ", &deleted);
#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;
}
}
+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);
}
commit = (struct commit *)o;
-
if (cutoff > commit->date)
cutoff = commit->date;
-
add_object_array((struct object *)commit, *argv, &revs);
}
* 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;
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;
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;
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)
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);
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);
}
if (!offset_one)
break;
offset = offset_one;
- if (do_progress)
- display_progress(&progress_state, written);
+ display_progress(progress_state, written);
}
/*
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))
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);
/*
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;
progress_lock();
(*processed)++;
- if (progress)
- display_progress(&progress_state, *processed);
+ display_progress(progress_state, *processed);
progress_unlock();
/*
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");
}
#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);
}
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();
#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;
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);
}
#include "builtin.h"
#include "cache.h"
+#include "progress.h"
static const char prune_packed_usage[] =
"git-prune-packed [-n] [-q]";
#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;
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);
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);
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)
#include "builtin.h"
#include "remote.h"
#include "transport.h"
+#include "parse-options.h"
-static const char push_usage[] = "git-push [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]";
+static const char * const push_usage[] = {
+ "git-push [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]",
+ NULL,
+};
static int thin, verbose;
static const char *receivepack;
int cmd_push(int argc, const char **argv, const char *prefix)
{
- int i;
int flags = 0;
+ int all = 0;
+ int dry_run = 0;
+ int force = 0;
+ int tags = 0;
const char *repo = NULL; /* default repository */
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ struct option options[] = {
+ OPT__VERBOSE(&verbose),
+ OPT_STRING( 0 , "repo", &repo, "repository", "repository"),
+ OPT_BOOLEAN( 0 , "all", &all, "push all refs"),
+ OPT_BOOLEAN( 0 , "tags", &tags, "push tags"),
+ OPT_BOOLEAN( 0 , "dry-run", &dry_run, "dry run"),
+ OPT_BOOLEAN('f', "force", &force, "force updates"),
+ OPT_BOOLEAN( 0 , "thin", &thin, "use thin pack"),
+ OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", "receive pack program"),
+ OPT_STRING( 0 , "exec", &receivepack, "receive-pack", "receive pack program"),
+ OPT_END()
+ };
- if (arg[0] != '-') {
- repo = arg;
- i++;
- break;
- }
- if (!strcmp(arg, "-v")) {
- verbose=1;
- continue;
- }
- if (!prefixcmp(arg, "--repo=")) {
- repo = arg+7;
- continue;
- }
- if (!strcmp(arg, "--all")) {
- flags |= TRANSPORT_PUSH_ALL;
- continue;
- }
- if (!strcmp(arg, "--dry-run")) {
- flags |= TRANSPORT_PUSH_DRY_RUN;
- continue;
- }
- if (!strcmp(arg, "--tags")) {
- add_refspec("refs/tags/*");
- continue;
- }
- if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
- flags |= TRANSPORT_PUSH_FORCE;
- continue;
- }
- if (!strcmp(arg, "--thin")) {
- thin = 1;
- continue;
- }
- if (!strcmp(arg, "--no-thin")) {
- thin = 0;
- continue;
- }
- if (!prefixcmp(arg, "--receive-pack=")) {
- receivepack = arg + 15;
- continue;
- }
- if (!prefixcmp(arg, "--exec=")) {
- receivepack = arg + 7;
- continue;
- }
- usage(push_usage);
+ argc = parse_options(argc, argv, options, push_usage, 0);
+
+ if (force)
+ flags |= TRANSPORT_PUSH_FORCE;
+ if (dry_run)
+ flags |= TRANSPORT_PUSH_DRY_RUN;
+ if (tags)
+ add_refspec("refs/tags/*");
+ if (all)
+ flags |= TRANSPORT_PUSH_ALL;
+
+ if (argc > 0) {
+ repo = argv[0];
+ set_refspecs(argv + 1, argc - 1);
}
- set_refspecs(argv + i, argc - i);
if ((flags & TRANSPORT_PUSH_ALL) && refspec)
- usage(push_usage);
+ usage_with_options(push_usage, options);
return do_push(repo, flags);
}
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();
} 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)
struct strbuf buf;
strbuf_init(&buf, 0);
pretty_print_commit(revs.commit_format, commit,
- &buf, revs.abbrev, NULL, NULL, revs.date_mode);
+ &buf, revs.abbrev, NULL, NULL,
+ revs.date_mode, 0);
if (buf.len)
printf("%s%c", buf.buf, hdr_termination);
strbuf_release(&buf);
#include "run-command.h"
#include "exec_cmd.h"
#include "utf8.h"
+#include "parse-options.h"
/*
* This implements the builtins revert and cherry-pick.
* 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);
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;
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) {
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));
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");
{
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);
}
#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;
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;
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_rm_options, builtin_rm_usage, 0);
+ if (!argc)
+ usage_with_options(builtin_rm_usage, builtin_rm_options);
- 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);
+ if (!index_only)
+ setup_work_tree();
- pathspec = get_pathspec(prefix, argv + i);
+ pathspec = get_pathspec(prefix, argv);
seen = NULL;
for (i = 0; pathspec[i] ; i++)
/* nothing */;
strbuf_init(&pretty, 0);
if (commit->object.parsed) {
pretty_print_commit(CMIT_FMT_ONELINE, commit,
- &pretty, 0, NULL, NULL, 0);
+ &pretty, 0, NULL, NULL, 0, 0);
pretty_str = pretty.buf;
}
if (!prefixcmp(pretty_str, "[PATCH] "))
#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)
{
{
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;
}
}
printf("%-15s ", refname);
- sp = buf = read_sha1_file(sha1, &type, &size);
- if (!buf)
+ buf = read_sha1_file(sha1, &type, &size);
+ if (!buf || !size)
return 0;
- if (!size) {
+
+ /* skip header */
+ sp = strstr(buf, "\n\n");
+ if (!sp) {
free(buf);
return 0;
}
- /* skip header */
- while (sp + 1 < buf + size &&
- !(sp[0] == '\n' && sp[1] == '\n'))
- sp++;
/* only take up to "lines" lines, and strip the signature */
for (i = 0, sp += 2;
i < filter->lines && sp < buf + size &&
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);
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");
#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);
}
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);
}
#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
extern const char **get_pathspec(const char *prefix, const char **pathspec);
+extern void setup_work_tree(void);
extern const char *setup_git_directory_gently(int *);
extern const char *setup_git_directory(void);
extern const char *prefix_path(const char *prefix, int len, const char *path);
#include "commit.h"
#include "pkt-line.h"
#include "utf8.h"
-#include "interpolate.h"
#include "diff.h"
#include "revision.h"
const char *commit_type = "commit";
-static struct cmt_fmt_map {
- const char *n;
- size_t cmp_len;
- enum cmit_fmt v;
-} cmt_fmts[] = {
- { "raw", 1, CMIT_FMT_RAW },
- { "medium", 1, CMIT_FMT_MEDIUM },
- { "short", 1, CMIT_FMT_SHORT },
- { "email", 1, CMIT_FMT_EMAIL },
- { "full", 5, CMIT_FMT_FULL },
- { "fuller", 5, CMIT_FMT_FULLER },
- { "oneline", 1, CMIT_FMT_ONELINE },
- { "format:", 7, CMIT_FMT_USERFORMAT},
-};
-
-static char *user_format;
-
-enum cmit_fmt get_commit_format(const char *arg)
-{
- int i;
-
- if (!arg || !*arg)
- return CMIT_FMT_DEFAULT;
- if (*arg == '=')
- arg++;
- if (!prefixcmp(arg, "format:")) {
- if (user_format)
- free(user_format);
- user_format = xstrdup(arg + 7);
- return CMIT_FMT_USERFORMAT;
- }
- for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
- if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) &&
- !strncmp(arg, cmt_fmts[i].n, strlen(arg)))
- return cmt_fmts[i].v;
- }
-
- die("invalid --pretty format: %s", arg);
-}
-
static struct commit *check_commit(struct object *obj,
const unsigned char *sha1,
int quiet)
}
}
-/*
- * Generic support for pretty-printing the header
- */
-static int get_one_line(const char *msg)
-{
- int ret = 0;
-
- for (;;) {
- char c = *msg++;
- if (!c)
- break;
- ret++;
- if (c == '\n')
- break;
- }
- return ret;
-}
-
-/* High bit set, or ISO-2022-INT */
-static int non_ascii(int ch)
-{
- ch = (ch & 0xff);
- return ((ch & 0x80) || (ch == 0x1b));
-}
-
-static int is_rfc2047_special(char ch)
-{
- return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
-}
-
-static void add_rfc2047(struct strbuf *sb, const char *line, int len,
- const char *encoding)
-{
- int i, last;
-
- for (i = 0; i < len; i++) {
- int ch = line[i];
- if (non_ascii(ch))
- goto needquote;
- if ((i + 1 < len) && (ch == '=' && line[i+1] == '?'))
- goto needquote;
- }
- strbuf_add(sb, line, len);
- return;
-
-needquote:
- strbuf_grow(sb, len * 3 + strlen(encoding) + 100);
- strbuf_addf(sb, "=?%s?q?", encoding);
- for (i = last = 0; i < len; i++) {
- unsigned ch = line[i] & 0xFF;
- /*
- * We encode ' ' using '=20' even though rfc2047
- * allows using '_' for readability. Unfortunately,
- * many programs do not understand this and just
- * leave the underscore in place.
- */
- if (is_rfc2047_special(ch) || ch == ' ') {
- strbuf_add(sb, line + last, i - last);
- strbuf_addf(sb, "=%02X", ch);
- last = i + 1;
- }
- }
- strbuf_add(sb, line + last, len - last);
- strbuf_addstr(sb, "?=");
-}
-
-static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
- const char *line, enum date_mode dmode,
- const char *encoding)
-{
- char *date;
- int namelen;
- unsigned long time;
- int tz;
- const char *filler = " ";
-
- if (fmt == CMIT_FMT_ONELINE)
- return;
- date = strchr(line, '>');
- if (!date)
- return;
- namelen = ++date - line;
- time = strtoul(date, &date, 10);
- tz = strtol(date, NULL, 10);
-
- if (fmt == CMIT_FMT_EMAIL) {
- char *name_tail = strchr(line, '<');
- int display_name_length;
- if (!name_tail)
- return;
- while (line < name_tail && isspace(name_tail[-1]))
- name_tail--;
- display_name_length = name_tail - line;
- filler = "";
- strbuf_addstr(sb, "From: ");
- add_rfc2047(sb, line, display_name_length, encoding);
- strbuf_add(sb, name_tail, namelen - display_name_length);
- strbuf_addch(sb, '\n');
- } else {
- strbuf_addf(sb, "%s: %.*s%.*s\n", what,
- (fmt == CMIT_FMT_FULLER) ? 4 : 0,
- filler, namelen, line);
- }
- switch (fmt) {
- case CMIT_FMT_MEDIUM:
- strbuf_addf(sb, "Date: %s\n", show_date(time, tz, dmode));
- break;
- case CMIT_FMT_EMAIL:
- strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822));
- break;
- case CMIT_FMT_FULLER:
- strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode));
- break;
- default:
- /* notin' */
- break;
- }
-}
-
-static int is_empty_line(const char *line, int *len_p)
-{
- int len = *len_p;
- while (len && isspace(line[len-1]))
- len--;
- *len_p = len;
- return !len;
-}
-
-static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb,
- const struct commit *commit, int abbrev)
-{
- struct commit_list *parent = commit->parents;
-
- if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
- !parent || !parent->next)
- return;
-
- strbuf_addstr(sb, "Merge:");
-
- while (parent) {
- struct commit *p = parent->item;
- const char *hex = NULL;
- const char *dots;
- if (abbrev)
- hex = find_unique_abbrev(p->object.sha1, abbrev);
- if (!hex)
- hex = sha1_to_hex(p->object.sha1);
- dots = (abbrev && strlen(hex) != 40) ? "..." : "";
- parent = parent->next;
-
- strbuf_addf(sb, " %s%s", hex, dots);
- }
- strbuf_addch(sb, '\n');
-}
-
-static char *get_header(const struct commit *commit, const char *key)
-{
- int key_len = strlen(key);
- const char *line = commit->buffer;
-
- for (;;) {
- const char *eol = strchr(line, '\n'), *next;
-
- if (line == eol)
- return NULL;
- if (!eol) {
- eol = line + strlen(line);
- next = NULL;
- } else
- next = eol + 1;
- if (eol - line > key_len &&
- !strncmp(line, key, key_len) &&
- line[key_len] == ' ') {
- return xmemdupz(line + key_len + 1, eol - line - key_len - 1);
- }
- line = next;
- }
-}
-
-static char *replace_encoding_header(char *buf, const char *encoding)
-{
- struct strbuf tmp;
- size_t start, len;
- char *cp = buf;
-
- /* guess if there is an encoding header before a \n\n */
- while (strncmp(cp, "encoding ", strlen("encoding "))) {
- cp = strchr(cp, '\n');
- if (!cp || *++cp == '\n')
- return buf;
- }
- start = cp - buf;
- cp = strchr(cp, '\n');
- if (!cp)
- return buf; /* should not happen but be defensive */
- len = cp + 1 - (buf + start);
-
- strbuf_init(&tmp, 0);
- strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1);
- if (is_encoding_utf8(encoding)) {
- /* we have re-coded to UTF-8; drop the header */
- strbuf_remove(&tmp, start, len);
- } else {
- /* just replaces XXXX in 'encoding XXXX\n' */
- strbuf_splice(&tmp, start + strlen("encoding "),
- len - strlen("encoding \n"),
- encoding, strlen(encoding));
- }
- return strbuf_detach(&tmp, NULL);
-}
-
-static char *logmsg_reencode(const struct commit *commit,
- const char *output_encoding)
-{
- static const char *utf8 = "utf-8";
- const char *use_encoding;
- char *encoding;
- char *out;
-
- if (!*output_encoding)
- return NULL;
- encoding = get_header(commit, "encoding");
- use_encoding = encoding ? encoding : utf8;
- if (!strcmp(use_encoding, output_encoding))
- if (encoding) /* we'll strip encoding header later */
- out = xstrdup(commit->buffer);
- else
- return NULL; /* nothing to do */
- else
- out = reencode_string(commit->buffer,
- output_encoding, use_encoding);
- if (out)
- out = replace_encoding_header(out, output_encoding);
-
- free(encoding);
- return out;
-}
-
-static void fill_person(struct interp *table, const char *msg, int len)
-{
- int start, end, tz = 0;
- unsigned long date;
- char *ep;
-
- /* parse name */
- for (end = 0; end < len && msg[end] != '<'; end++)
- ; /* do nothing */
- start = end + 1;
- while (end > 0 && isspace(msg[end - 1]))
- end--;
- table[0].value = xmemdupz(msg, end);
-
- if (start >= len)
- return;
-
- /* parse email */
- for (end = start + 1; end < len && msg[end] != '>'; end++)
- ; /* do nothing */
-
- if (end >= len)
- return;
-
- table[1].value = xmemdupz(msg + start, end - start);
-
- /* parse date */
- for (start = end + 1; start < len && isspace(msg[start]); start++)
- ; /* do nothing */
- if (start >= len)
- return;
- date = strtoul(msg + start, &ep, 10);
- if (msg + start == ep)
- return;
-
- table[5].value = xmemdupz(msg + start, ep - (msg + start));
-
- /* parse tz */
- for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
- ; /* do nothing */
- if (start + 1 < len) {
- tz = strtoul(msg + start + 1, NULL, 10);
- if (msg[start] == '-')
- tz = -tz;
- }
-
- interp_set_entry(table, 2, show_date(date, tz, DATE_NORMAL));
- interp_set_entry(table, 3, show_date(date, tz, DATE_RFC2822));
- interp_set_entry(table, 4, show_date(date, tz, DATE_RELATIVE));
- interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601));
-}
-
-void format_commit_message(const struct commit *commit,
- const void *format, struct strbuf *sb)
-{
- struct interp table[] = {
- { "%H" }, /* commit hash */
- { "%h" }, /* abbreviated commit hash */
- { "%T" }, /* tree hash */
- { "%t" }, /* abbreviated tree hash */
- { "%P" }, /* parent hashes */
- { "%p" }, /* abbreviated parent hashes */
- { "%an" }, /* author name */
- { "%ae" }, /* author email */
- { "%ad" }, /* author date */
- { "%aD" }, /* author date, RFC2822 style */
- { "%ar" }, /* author date, relative */
- { "%at" }, /* author date, UNIX timestamp */
- { "%ai" }, /* author date, ISO 8601 */
- { "%cn" }, /* committer name */
- { "%ce" }, /* committer email */
- { "%cd" }, /* committer date */
- { "%cD" }, /* committer date, RFC2822 style */
- { "%cr" }, /* committer date, relative */
- { "%ct" }, /* committer date, UNIX timestamp */
- { "%ci" }, /* committer date, ISO 8601 */
- { "%e" }, /* encoding */
- { "%s" }, /* subject */
- { "%b" }, /* body */
- { "%Cred" }, /* red */
- { "%Cgreen" }, /* green */
- { "%Cblue" }, /* blue */
- { "%Creset" }, /* reset color */
- { "%n" }, /* newline */
- { "%m" }, /* left/right/bottom */
- };
- enum interp_index {
- IHASH = 0, IHASH_ABBREV,
- ITREE, ITREE_ABBREV,
- IPARENTS, IPARENTS_ABBREV,
- IAUTHOR_NAME, IAUTHOR_EMAIL,
- IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE,
- IAUTHOR_TIMESTAMP, IAUTHOR_ISO8601,
- ICOMMITTER_NAME, ICOMMITTER_EMAIL,
- ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822,
- ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP,
- ICOMMITTER_ISO8601,
- IENCODING,
- ISUBJECT,
- IBODY,
- IRED, IGREEN, IBLUE, IRESET_COLOR,
- INEWLINE,
- ILEFT_RIGHT,
- };
- struct commit_list *p;
- char parents[1024];
- unsigned long len;
- int i;
- enum { HEADER, SUBJECT, BODY } state;
- const char *msg = commit->buffer;
-
- if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table))
- die("invalid interp table!");
-
- /* these are independent of the commit */
- interp_set_entry(table, IRED, "\033[31m");
- interp_set_entry(table, IGREEN, "\033[32m");
- interp_set_entry(table, IBLUE, "\033[34m");
- interp_set_entry(table, IRESET_COLOR, "\033[m");
- interp_set_entry(table, INEWLINE, "\n");
-
- /* these depend on the commit */
- if (!commit->object.parsed)
- parse_object(commit->object.sha1);
- interp_set_entry(table, IHASH, sha1_to_hex(commit->object.sha1));
- interp_set_entry(table, IHASH_ABBREV,
- find_unique_abbrev(commit->object.sha1,
- DEFAULT_ABBREV));
- interp_set_entry(table, ITREE, sha1_to_hex(commit->tree->object.sha1));
- interp_set_entry(table, ITREE_ABBREV,
- find_unique_abbrev(commit->tree->object.sha1,
- DEFAULT_ABBREV));
- interp_set_entry(table, ILEFT_RIGHT,
- (commit->object.flags & BOUNDARY)
- ? "-"
- : (commit->object.flags & SYMMETRIC_LEFT)
- ? "<"
- : ">");
-
- parents[1] = 0;
- for (i = 0, p = commit->parents;
- p && i < sizeof(parents) - 1;
- p = p->next)
- i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
- sha1_to_hex(p->item->object.sha1));
- interp_set_entry(table, IPARENTS, parents + 1);
-
- parents[1] = 0;
- for (i = 0, p = commit->parents;
- p && i < sizeof(parents) - 1;
- p = p->next)
- i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
- find_unique_abbrev(p->item->object.sha1,
- DEFAULT_ABBREV));
- interp_set_entry(table, IPARENTS_ABBREV, parents + 1);
-
- for (i = 0, state = HEADER; msg[i] && state < BODY; i++) {
- int eol;
- for (eol = i; msg[eol] && msg[eol] != '\n'; eol++)
- ; /* do nothing */
-
- if (state == SUBJECT) {
- table[ISUBJECT].value = xmemdupz(msg + i, eol - i);
- i = eol;
- }
- if (i == eol) {
- state++;
- /* strip empty lines */
- while (msg[eol + 1] == '\n')
- eol++;
- } else if (!prefixcmp(msg + i, "author "))
- fill_person(table + IAUTHOR_NAME,
- msg + i + 7, eol - i - 7);
- else if (!prefixcmp(msg + i, "committer "))
- fill_person(table + ICOMMITTER_NAME,
- msg + i + 10, eol - i - 10);
- else if (!prefixcmp(msg + i, "encoding "))
- table[IENCODING].value =
- xmemdupz(msg + i + 9, eol - i - 9);
- i = eol;
- }
- if (msg[i])
- table[IBODY].value = xstrdup(msg + i);
-
- len = interpolate(sb->buf + sb->len, strbuf_avail(sb),
- format, table, ARRAY_SIZE(table));
- if (len > strbuf_avail(sb)) {
- strbuf_grow(sb, len);
- interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1,
- format, table, ARRAY_SIZE(table));
- }
- strbuf_setlen(sb, sb->len + len);
- interp_clear_table(table, ARRAY_SIZE(table));
-}
-
-static void pp_header(enum cmit_fmt fmt,
- int abbrev,
- enum date_mode dmode,
- const char *encoding,
- const struct commit *commit,
- const char **msg_p,
- struct strbuf *sb)
-{
- int parents_shown = 0;
-
- for (;;) {
- const char *line = *msg_p;
- int linelen = get_one_line(*msg_p);
-
- if (!linelen)
- return;
- *msg_p += linelen;
-
- if (linelen == 1)
- /* End of header */
- return;
-
- if (fmt == CMIT_FMT_RAW) {
- strbuf_add(sb, line, linelen);
- continue;
- }
-
- if (!memcmp(line, "parent ", 7)) {
- if (linelen != 48)
- die("bad parent line in commit");
- continue;
- }
-
- if (!parents_shown) {
- struct commit_list *parent;
- int num;
- for (parent = commit->parents, num = 0;
- parent;
- parent = parent->next, num++)
- ;
- /* with enough slop */
- strbuf_grow(sb, num * 50 + 20);
- add_merge_info(fmt, sb, commit, abbrev);
- parents_shown = 1;
- }
-
- /*
- * MEDIUM == DEFAULT shows only author with dates.
- * FULL shows both authors but not dates.
- * FULLER shows both authors and dates.
- */
- if (!memcmp(line, "author ", 7)) {
- strbuf_grow(sb, linelen + 80);
- add_user_info("Author", fmt, sb, line + 7, dmode, encoding);
- }
- if (!memcmp(line, "committer ", 10) &&
- (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) {
- strbuf_grow(sb, linelen + 80);
- add_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
- }
- }
-}
-
-static void pp_title_line(enum cmit_fmt fmt,
- const char **msg_p,
- struct strbuf *sb,
- const char *subject,
- const char *after_subject,
- const char *encoding,
- int plain_non_ascii)
-{
- struct strbuf title;
-
- strbuf_init(&title, 80);
-
- for (;;) {
- const char *line = *msg_p;
- int linelen = get_one_line(line);
-
- *msg_p += linelen;
- if (!linelen || is_empty_line(line, &linelen))
- break;
-
- strbuf_grow(&title, linelen + 2);
- if (title.len) {
- if (fmt == CMIT_FMT_EMAIL) {
- strbuf_addch(&title, '\n');
- }
- strbuf_addch(&title, ' ');
- }
- strbuf_add(&title, line, linelen);
- }
-
- strbuf_grow(sb, title.len + 1024);
- if (subject) {
- strbuf_addstr(sb, subject);
- add_rfc2047(sb, title.buf, title.len, encoding);
- } else {
- strbuf_addbuf(sb, &title);
- }
- strbuf_addch(sb, '\n');
-
- if (plain_non_ascii) {
- const char *header_fmt =
- "MIME-Version: 1.0\n"
- "Content-Type: text/plain; charset=%s\n"
- "Content-Transfer-Encoding: 8bit\n";
- strbuf_addf(sb, header_fmt, encoding);
- }
- if (after_subject) {
- strbuf_addstr(sb, after_subject);
- }
- if (fmt == CMIT_FMT_EMAIL) {
- strbuf_addch(sb, '\n');
- }
- strbuf_release(&title);
-}
-
-static void pp_remainder(enum cmit_fmt fmt,
- const char **msg_p,
- struct strbuf *sb,
- int indent)
-{
- int first = 1;
- for (;;) {
- const char *line = *msg_p;
- int linelen = get_one_line(line);
- *msg_p += linelen;
-
- if (!linelen)
- break;
-
- if (is_empty_line(line, &linelen)) {
- if (first)
- continue;
- if (fmt == CMIT_FMT_SHORT)
- break;
- }
- first = 0;
-
- strbuf_grow(sb, linelen + indent + 20);
- if (indent) {
- memset(sb->buf + sb->len, ' ', indent);
- strbuf_setlen(sb, sb->len + indent);
- }
- strbuf_add(sb, line, linelen);
- strbuf_addch(sb, '\n');
- }
-}
-
-void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
- struct strbuf *sb, int abbrev,
- const char *subject, const char *after_subject,
- enum date_mode dmode)
-{
- unsigned long beginning_of_body;
- int indent = 4;
- const char *msg = commit->buffer;
- int plain_non_ascii = 0;
- char *reencoded;
- const char *encoding;
-
- if (fmt == CMIT_FMT_USERFORMAT) {
- format_commit_message(commit, user_format, sb);
- return;
- }
-
- encoding = (git_log_output_encoding
- ? git_log_output_encoding
- : git_commit_encoding);
- if (!encoding)
- encoding = "utf-8";
- reencoded = logmsg_reencode(commit, encoding);
- if (reencoded) {
- msg = reencoded;
- }
-
- if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
- indent = 0;
-
- /* After-subject is used to pass in Content-Type: multipart
- * MIME header; in that case we do not have to do the
- * plaintext content type even if the commit message has
- * non 7-bit ASCII character. Otherwise, check if we need
- * to say this is not a 7-bit ASCII.
- */
- if (fmt == CMIT_FMT_EMAIL && !after_subject) {
- int i, ch, in_body;
-
- for (in_body = i = 0; (ch = msg[i]); i++) {
- if (!in_body) {
- /* author could be non 7-bit ASCII but
- * the log may be so; skip over the
- * header part first.
- */
- if (ch == '\n' && msg[i+1] == '\n')
- in_body = 1;
- }
- else if (non_ascii(ch)) {
- plain_non_ascii = 1;
- break;
- }
- }
- }
-
- pp_header(fmt, abbrev, dmode, encoding, commit, &msg, sb);
- if (fmt != CMIT_FMT_ONELINE && !subject) {
- strbuf_addch(sb, '\n');
- }
-
- /* Skip excess blank lines at the beginning of body, if any... */
- for (;;) {
- int linelen = get_one_line(msg);
- int ll = linelen;
- if (!linelen)
- break;
- if (!is_empty_line(msg, &ll))
- break;
- msg += linelen;
- }
-
- /* These formats treat the title line specially. */
- if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
- pp_title_line(fmt, &msg, sb, subject,
- after_subject, encoding, plain_non_ascii);
-
- beginning_of_body = sb->len;
- if (fmt != CMIT_FMT_ONELINE)
- pp_remainder(fmt, &msg, sb, indent);
- strbuf_rtrim(sb);
-
- /* Make sure there is an EOLN for the non-oneline case */
- if (fmt != CMIT_FMT_ONELINE)
- strbuf_addch(sb, '\n');
-
- /*
- * The caller may append additional body text in e-mail
- * format. Make sure we did not strip the blank line
- * between the header and the body.
- */
- if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
- strbuf_addch(sb, '\n');
- free(reencoded);
-}
-
struct commit *pop_commit(struct commit_list **stack)
{
struct commit_list *top = *stack;
CMIT_FMT_UNSPECIFIED,
};
+extern int non_ascii(int);
extern enum cmit_fmt get_commit_format(const char *arg);
extern void format_commit_message(const struct commit *commit,
const void *format, struct strbuf *sb);
extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,
struct strbuf *,
int abbrev, const char *subject,
- const char *after_subject, enum date_mode);
+ const char *after_subject, enum date_mode,
+ int non_ascii_present);
/** Removes the first commit from a list sorted by date, and adds all
* of its parents.
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
-#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
-#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
AC_ARG_WITH([lib],
[AS_HELP_STRING([--with-lib=ARG],
[ARG specifies alternative name for lib directory])],
- [if test "$withval" = "no" -o "$withval" = "yes"; then \
+ [if test "$withval" = "no" || test "$withval" = "yes"; then \
AC_MSG_WARN([You should provide name for --with-lib=ARG]); \
else \
GIT_CONF_APPEND_LINE(lib=$withval); \
[AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
[[char buf[64];
if (sprintf(buf, "%lld%hhd%jd%zd%td", (long long int)1, (char)2, (intmax_t)3, (size_t)4, (ptrdiff_t)5) != 5)
- exit(1);
+ return 1;
else if (strcmp(buf, "12345"))
- exit(2);]])],
+ return 2;]])],
[ac_cv_c_c99_format=yes],
[ac_cv_c_c99_format=no])
])
#
# Copyright (c) 2007 Andy Parkins
#
-# An example hook script to mail out commit update information. This hook sends emails
-# listing new revisions to the repository introduced by the change being reported. The
-# rule is that (for branch updates) each commit will appear on one email and one email
-# only.
+# An example hook script to mail out commit update information. This hook
+# sends emails listing new revisions to the repository introduced by the
+# change being reported. The rule is that (for branch updates) each commit
+# will appear on one email and one email only.
#
-# This hook is stored in the contrib/hooks directory. Your distribution will have put
-# this somewhere standard. You should make this script executable then link to it in
-# the repository you would like to use it in. For example, on debian the hook is stored
-# in /usr/share/doc/git-core/contrib/hooks/post-receive-email:
+# This hook is stored in the contrib/hooks directory. Your distribution
+# will have put this somewhere standard. You should make this script
+# executable then link to it in the repository you would like to use it in.
+# For example, on debian the hook is stored in
+# /usr/share/doc/git-core/contrib/hooks/post-receive-email:
#
# chmod a+x post-receive-email
# cd /path/to/your/repository.git
# ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive
#
-# This hook script assumes it is enabled on the central repository of a project, with
-# all users pushing only to it and not between each other. It will still work if you
-# don't operate in that style, but it would become possible for the email to be from
-# someone other than the person doing the push.
+# This hook script assumes it is enabled on the central repository of a
+# project, with all users pushing only to it and not between each other. It
+# will still work if you don't operate in that style, but it would become
+# possible for the email to be from someone other than the person doing the
+# push.
#
# Config
# ------
# emails for every ref update.
# hooks.announcelist
# This is the list that all pushes of annotated tags will go to. Leave it
-# blank to default to the mailinglist field. The announce emails lists the
-# short log summary of the changes since the last annotated tag.
-# hook.envelopesender
-# If set then the -f option is passed to sendmail to allow the envelope sender
-# address to be set
+# blank to default to the mailinglist field. The announce emails lists
+# the short log summary of the changes since the last annotated tag.
+# hooks.envelopesender
+# If set then the -f option is passed to sendmail to allow the envelope
+# sender address to be set
+# hooks.emailprefix
+# All emails have their subjects prefixed with this prefix, or "[SCM]"
+# if emailprefix is unset, to aid filtering
#
# Notes
# -----
-# All emails have their subjects prefixed with "[SCM]" to aid filtering.
# All emails include the headers "X-Git-Refname", "X-Git-Oldrev",
# "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and
# give information for debugging.
# this is and calls the appropriate body-generation routine after outputting
# the common header
#
-# Note this function doesn't actually generate any email output, that is taken
-# care of by the functions it calls:
+# Note this function doesn't actually generate any email output, that is
+# taken care of by the functions it calls:
# - generate_email_header
# - generate_create_XXXX_email
# - generate_update_XXXX_email
# Generate header
cat <<-EOF
To: $recipients
- Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe
+ Subject: ${emailprefix}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe
X-Git-Refname: $refname
X-Git-Reftype: $refname_type
X-Git-Oldrev: $oldrev
echo $LOGBEGIN
# This shows all log entries that are not already covered by
# another ref - i.e. commits that are now accessible from this
- # ref that were previously not accessible (see generate_update_branch_email
- # for the explanation of this command)
+ # ref that were previously not accessible
+ # (see generate_update_branch_email for the explanation of this
+ # command)
git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
git rev-list --pretty --stdin $newrev
echo $LOGEND
#
# git-rev-list N ^O ^X ^N
#
- # So, we need to build up the list more carefully. git-rev-parse will
- # generate a list of revs that may be fed into git-rev-list. We can get
- # it to make the "--not --all" part and then filter out the "^N" with:
+ # So, we need to build up the list more carefully. git-rev-parse
+ # will generate a list of revs that may be fed into git-rev-list.
+ # We can get it to make the "--not --all" part and then filter out
+ # the "^N" with:
#
# git-rev-parse --not --all | grep -v N
#
# git-rev-list N ^O ^X
#
# This leaves a problem when someone else updates the repository
- # while this script is running. Their new value of the ref we're working
- # on would be included in the "--not --all" output; and as our $newrev
- # would be an ancestor of that commit, it would exclude all of our
- # commits. What we really want is to exclude the current value of
- # $refname from the --not list, rather than N itself. So:
+ # while this script is running. Their new value of the ref we're
+ # working on would be included in the "--not --all" output; and as
+ # our $newrev would be an ancestor of that commit, it would exclude
+ # all of our commits. What we really want is to exclude the current
+ # value of $refname from the --not list, rather than N itself. So:
#
# git-rev-parse --not --all | grep -v $(git-rev-parse $refname)
#
- # Get's us to something pretty safe (apart from the small time between
- # refname being read, and git-rev-parse running - for that, I give up)
+ # Get's us to something pretty safe (apart from the small time
+ # between refname being read, and git-rev-parse running - for that,
+ # I give up)
#
#
# Next problem, consider this:
# \
# * --- X --- * --- N ($newrev)
#
- # That is to say, there is no guarantee that oldrev is a strict subset of
- # newrev (it would have required a --force, but that's allowed). So, we
- # can't simply say rev-list $oldrev..$newrev. Instead we find the common
- # base of the two revs and list from there.
+ # That is to say, there is no guarantee that oldrev is a strict
+ # subset of newrev (it would have required a --force, but that's
+ # allowed). So, we can't simply say rev-list $oldrev..$newrev.
+ # Instead we find the common base of the two revs and list from
+ # there.
#
- # As above, we need to take into account the presence of X; if another
- # branch is already in the repository and points at some of the revisions
- # that we are about to output - we don't want them. The solution is as
- # before: git-rev-parse output filtered.
+ # As above, we need to take into account the presence of X; if
+ # another branch is already in the repository and points at some of
+ # the revisions that we are about to output - we don't want them.
+ # The solution is as before: git-rev-parse output filtered.
#
- # Finally, tags:
- # 1 --- 2 --- O --- T --- 3 --- 4 --- N
+ # Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N
#
# Tags pushed into the repository generate nice shortlog emails that
# summarise the commits between them and the previous tag. However,
# for a branch update. Therefore we still want to output revisions
# that have been output on a tag email.
#
- # Luckily, git-rev-parse includes just the tool. Instead of using "--all"
- # we use "--branches"; this has the added benefit that "remotes/" will
- # be ignored as well.
-
- # List all of the revisions that were removed by this update, in a fast forward
- # update, this list will be empty, because rev-list O ^N is empty. For a non
- # fast forward, O ^N is the list of removed revisions
+ # Luckily, git-rev-parse includes just the tool. Instead of using
+ # "--all" we use "--branches"; this has the added benefit that
+ # "remotes/" will be ignored as well.
+
+ # List all of the revisions that were removed by this update, in a
+ # fast forward update, this list will be empty, because rev-list O
+ # ^N is empty. For a non fast forward, O ^N is the list of removed
+ # revisions
fast_forward=""
rev=""
for rev in $(git rev-list $newrev..$oldrev)
fi
# List all the revisions from baserev to newrev in a kind of
- # "table-of-contents"; note this list can include revisions that have
- # already had notification emails and is present to show the full detail
- # of the change from rolling back the old revision to the base revision and
- # then forward to the new revision
+ # "table-of-contents"; note this list can include revisions that
+ # have already had notification emails and is present to show the
+ # full detail of the change from rolling back the old revision to
+ # the base revision and then forward to the new revision
for rev in $(git rev-list $oldrev..$newrev)
do
revtype=$(git cat-file -t "$rev")
if [ "$fast_forward" ]; then
echo " from $oldrev ($oldrev_type)"
else
- # 1. Existing revisions were removed. In this case newrev is a
- # subset of oldrev - this is the reverse of a fast-forward,
- # a rewind
- # 2. New revisions were added on top of an old revision, this is
- # a rewind and addition.
+ # 1. Existing revisions were removed. In this case newrev
+ # is a subset of oldrev - this is the reverse of a
+ # fast-forward, a rewind
+ # 2. New revisions were added on top of an old revision,
+ # this is a rewind and addition.
- # (1) certainly happened, (2) possibly. When (2) hasn't happened,
- # we set a flag to indicate that no log printout is required.
+ # (1) certainly happened, (2) possibly. When (2) hasn't
+ # happened, we set a flag to indicate that no log printout
+ # is required.
echo ""
- # Find the common ancestor of the old and new revisions and compare
- # it with newrev
+ # Find the common ancestor of the old and new revisions and
+ # compare it with newrev
baserev=$(git merge-base $oldrev $newrev)
rewind_only=""
if [ "$baserev" = "$newrev" ]; then
git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
git rev-list --pretty --stdin $oldrev..$newrev
- # XXX: Need a way of detecting whether git rev-list actually outputted
- # anything, so that we can issue a "no new revisions added by this
- # update" message
+ # XXX: Need a way of detecting whether git rev-list actually
+ # outputted anything, so that we can issue a "no new
+ # revisions added by this update" message
echo $LOGEND
else
echo "No new revisions were added by this update."
fi
- # The diffstat is shown from the old revision to the new revision. This
- # is to show the truth of what happened in this change. There's no point
- # showing the stat from the base to the new revision because the base
- # is effectively a random revision at this point - the user will be
- # interested in what this revision changed - including the undoing of
- # previous revisions in the case of non-fast forward updates.
+ # The diffstat is shown from the old revision to the new revision.
+ # This is to show the truth of what happened in this change.
+ # There's no point showing the stat from the base to the new
+ # revision because the base is effectively a random revision at this
+ # point - the user will be interested in what this revision changed
+ # - including the undoing of previous revisions in the case of
+ # non-fast forward updates.
echo ""
echo "Summary of changes:"
git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev
#
generate_atag_email()
{
- # Use git-for-each-ref to pull out the individual fields from the tag
+ # Use git-for-each-ref to pull out the individual fields from the
+ # tag
eval $(git for-each-ref --shell --format='
tagobject=%(*objectname)
tagtype=%(*objecttype)
echo " tagging $tagobject ($tagtype)"
case "$tagtype" in
commit)
+
# If the tagged object is a commit, then we assume this is a
- # release, and so we calculate which tag this tag is replacing
+ # release, and so we calculate which tag this tag is
+ # replacing
prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null)
if [ -n "$prevtag" ]; then
echo ""
echo $LOGBEGIN
- # Show the content of the tag message; this might contain a change log
- # or release notes so is worth displaying.
+ # Show the content of the tag message; this might contain a change
+ # log or release notes so is worth displaying.
git cat-file tag $newrev | sed -e '1,/^$/d'
echo ""
case "$tagtype" in
commit)
- # Only commit tags make sense to have rev-list operations performed
- # on them
+ # Only commit tags make sense to have rev-list operations
+ # performed on them
if [ -n "$prevtag" ]; then
# Show changes since the previous release
git rev-list --pretty=short "$prevtag..$newrev" | git shortlog
else
- # No previous tag, show all the changes since time began
+ # No previous tag, show all the changes since time
+ # began
git rev-list --pretty=short $newrev | git shortlog
fi
;;
*)
- # XXX: Is there anything useful we can do for non-commit objects?
+ # XXX: Is there anything useful we can do for non-commit
+ # objects?
;;
esac
#
generate_general_email()
{
- # Unannotated tags are more about marking a point than releasing a version;
- # therefore we don't do the shortlog summary that we do for annotated tags
- # above - we simply show that the point has been marked, and print the log
- # message for the marked point for reference purposes
+ # Unannotated tags are more about marking a point than releasing a
+ # version; therefore we don't do the shortlog summary that we do for
+ # annotated tags above - we simply show that the point has been
+ # marked, and print the log message for the marked point for
+ # reference purposes
#
- # Note this section also catches any other reference type (although there
- # aren't any) and deals with them in the same way.
+ # Note this section also catches any other reference type (although
+ # there aren't any) and deals with them in the same way.
echo ""
if [ "$newrev_type" = "commit" ]; then
git show --no-color --root -s $newrev
echo $LOGEND
else
- # What can we do here? The tag marks an object that is not a commit,
- # so there is no log for us to display. It's probably not wise to
- # output git-cat-file as it could be a binary blob. We'll just say how
- # big it is
+ # What can we do here? The tag marks an object that is not
+ # a commit, so there is no log for us to display. It's
+ # probably not wise to output git-cat-file as it could be a
+ # binary blob. We'll just say how big it is
echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long."
fi
}
# ---------------------------- main()
# --- Constants
-EMAILPREFIX="[SCM] "
LOGBEGIN="- Log -----------------------------------------------------------------"
LOGEND="-----------------------------------------------------------------------"
fi
projectdesc=$(sed -ne '1p' "$GIT_DIR/description")
-# Check if the description is unchanged from it's default, and shorten it to a
-# more manageable length if it is
+# Check if the description is unchanged from it's default, and shorten it to
+# a more manageable length if it is
if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null
then
projectdesc="UNNAMED PROJECT"
recipients=$(git repo-config hooks.mailinglist)
announcerecipients=$(git repo-config hooks.announcelist)
envelopesender=$(git-repo-config hooks.envelopesender)
+emailprefix=$(git-repo-config hooks.emailprefix || echo '[SCM] ')
# --- Main loop
-# Allow dual mode: run from the command line just like the update hook, or if
-# no arguments are given then run as a hook script
+# Allow dual mode: run from the command line just like the update hook, or
+# if no arguments are given then run as a hook script
if [ -n "$1" -a -n "$2" -a -n "$3" ]; then
# Output to the terminal in command line mode - if someone wanted to
- # resend an email; they could redirect the output to sendmail themselves
+ # resend an email; they could redirect the output to sendmail
+ # themselves
PAGER= generate_email $2 $3 $1
else
while read oldrev newrev refname
* 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)
for (;;) {
int ret = xwrite(f->fd, buf, count);
if (ret > 0) {
+ display_throughput(f->tp, ret);
buf = (char *) buf + ret;
count -= ret;
if (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)
}
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;
f->fd = fd;
f->error = 0;
f->offset = 0;
+ f->tp = tp;
f->do_crc = 0;
SHA1_Init(&f->ctx);
return f;
#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;
};
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 *);
if (addr->sa_family == AF_INET) {
struct sockaddr_in *sin_addr = (void *) addr;
inet_ntop(addr->sa_family, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));
- port = sin_addr->sin_port;
+ port = ntohs(sin_addr->sin_port);
#ifndef NO_IPV6
} else if (addr && addr->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6_addr = (void *) addr;
inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1);
strcat(buf, "]");
- port = sin6_addr->sin6_port;
+ port = ntohs(sin6_addr->sin6_port);
#endif
}
loginfo("Connection from %s:%d", addrbuf, port);
}
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() {
*,--depth)
shift
depth="--depth=$1";;
+ *,--)
+ shift
+ break ;;
*,-*) usage ;;
*) break ;;
esac
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
$state = 4;
} elsif ($state == 4 and s/^Branch:\s+//) {
s/\s+$//;
+ tr/_/\./ if ( $opt_u );
s/[\/]/$opt_s/g;
$branch = $_;
$state = 5;
SUBDIRECTORY_OK='Yes'
. git-sh-setup
+echo "WARNING: '$0' is deprecated in favor of 'git fsck --lost-found'" >&2
+
if [ "$#" != "0" ]
then
usage
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
--smtp-ssl If set, connects to the SMTP server using SSL.
- --suppress-from Suppress sending emails to yourself if your address
- appears in a From: line. Defaults to off.
+ --suppress-from Suppress sending emails to yourself. Defaults to off.
--thread Specify that the "In-Reply-To:" header should be set on all
emails. Defaults to on.
if (/^(Signed-off-by|Cc): (.*)$/i && $signed_off_cc) {
my $c = $2;
chomp $c;
+ next if ($c eq $sender and $suppress_from);
push @cc, $c;
printf("(sob) Adding cc: %s from line '%s'\n",
$c, $_) unless $quiet;
my $c = $_;
$c =~ s/^\s*//g;
$c =~ s/\n$//g;
+ next if ($c eq $sender and $suppress_from);
push @cc, $c;
printf("(cc-cmd) Adding cc: %s from: '%s'\n",
$c, $cc_cmd) unless $quiet;
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:
"If these changes depend on each other, re-running ",
"without --no-rebase will be required."
}
- foreach my $d (@$linear_refs) {
+ while (1) {
+ my $d = shift @$linear_refs or last;
unless (defined $last_rev) {
(undef, $last_rev, undef) = cmt_metadata("$d~1");
unless (defined $last_rev) {
# we always want to rebase against the current HEAD,
# not any head that was passed to us
- my @diff = command('diff-tree', 'HEAD',
+ my @diff = command('diff-tree', $d,
$gs->refname, '--');
my @finish;
if (@diff) {
@finish = rebase_cmd();
- print STDERR "W: HEAD and ", $gs->refname,
+ print STDERR "W: $d and ", $gs->refname,
" differ, using @finish:\n",
- "@diff";
+ join("\n", @diff), "\n";
} else {
print "No changes between current HEAD and ",
$gs->refname,
@finish = qw/reset --mixed/;
}
command_noisy(@finish, $gs->refname);
+ if (@diff) {
+ @refs = ();
+ my ($url_, $rev_, $uuid_, $gs_) =
+ working_head_info($head, \@refs);
+ my ($linear_refs_, $parents_) =
+ linearize_history($gs_, \@refs);
+ if (scalar(@$linear_refs) !=
+ scalar(@$linear_refs_)) {
+ fatal "# of revisions changed ",
+ "\nbefore:\n",
+ join("\n", @$linear_refs),
+ "\n\nafter:\n",
+ join("\n", @$linear_refs_), "\n",
+ 'If you are attempting to commit ',
+ "merges, try running:\n\t",
+ 'git rebase --interactive',
+ '--preserve-merges ',
+ $gs->refname,
+ "\nBefore dcommitting";
+ }
+ if ($url_ ne $url) {
+ fatal "URL mismatch after rebase: ",
+ "$url_ != $url";
+ }
+ if ($uuid_ ne $uuid) {
+ fatal "uuid mismatch after rebase: ",
+ "$uuid_ != $uuid";
+ }
+ # remap parents
+ my (%p, @l, $i);
+ for ($i = 0; $i < scalar @$linear_refs; $i++) {
+ my $new = $linear_refs_->[$i] or next;
+ $p{$new} =
+ $parents->{$linear_refs->[$i]};
+ push @l, $new;
+ }
+ $parents = \%p;
+ $linear_refs = \@l;
+ }
$last_rev = $cmt_rev;
}
}
prefix = setup_git_directory();
if (p->option & USE_PAGER)
setup_pager();
- if (p->option & NEED_WORK_TREE) {
- const char *work_tree = get_git_work_tree();
- const char *git_dir = get_git_dir();
- if (!is_absolute_path(git_dir))
- set_git_dir(make_absolute_path(git_dir));
- if (!work_tree || chdir(work_tree))
- die("%s must be run in a work tree", p->cmd);
- }
+ if (p->option & NEED_WORK_TREE)
+ setup_work_tree();
+
trace_argv_printf(argv, argc, "trace: built-in: git");
status = p->fn(argc, argv, prefix);
{ "rev-list", cmd_rev_list, RUN_SETUP },
{ "rev-parse", cmd_rev_parse, RUN_SETUP },
{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
- { "rm", cmd_rm, RUN_SETUP | NEED_WORK_TREE },
+ { "rm", cmd_rm, RUN_SETUP },
{ "runstatus", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE },
{ "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
{ "show-branch", cmd_show_branch, RUN_SETUP },
);
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');
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
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) {
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;
$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'
$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);
}
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],
}
} 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'},
}
}
- $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'},
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";
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";
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
my ($diffinfo, $patchinfo) = @_;
return defined $diffinfo && defined $patchinfo
- && ($diffinfo->{'to_file'} || $diffinfo->{'file'}) eq $patchinfo->{'to_file'};
+ && $diffinfo->{'to_file'} eq $patchinfo->{'to_file'};
}
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");
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",
"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);
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)},
}
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";
}
}
%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'};
# 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))) {
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 &&
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();
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);
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");
}
/* 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);
}
}
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);
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];
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;
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;
* - 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);
} 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();
* 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;
obj->size, obj->type);
}
free(data);
- if (verbose)
- display_progress(&progress, nr_resolved_deltas);
+ display_progress(progress, nr_resolved_deltas);
}
}
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);
}
}
}
+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")) {
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 {
(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);
}
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;
}
return result;
}
+static int has_non_ascii(const char *s)
+{
+ int ch;
+ if (!s)
+ return 0;
+ while ((ch = *s++) != '\0') {
+ if (non_ascii(ch))
+ return 1;
+ }
+ return 0;
+}
+
void show_log(struct rev_info *opt, const char *sep)
{
struct strbuf msgbuf;
*/
strbuf_init(&msgbuf, 0);
pretty_print_commit(opt->commit_format, commit, &msgbuf,
- abbrev, subject, extra_headers, opt->date_mode);
+ abbrev, subject, extra_headers, opt->date_mode,
+ has_non_ascii(opt->add_signoff));
if (opt->add_signoff)
append_signoff(&msgbuf, opt->add_signoff);
* 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;
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);
--- /dev/null
+#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;
+}
--- /dev/null
+#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);
+
+/*
+ * `type`::
+ * holds the type of the option, you must have an OPTION_END last in your
+ * array.
+ *
+ * `short_name`::
+ * the character to use as a short option name, '\0' if none.
+ *
+ * `long_name`::
+ * the long option name, without the leading dashes, NULL if none.
+ *
+ * `value`::
+ * stores pointers to the values to be filled.
+ *
+ * `argh`::
+ * token to explain the kind of argument this option wants. Keep it
+ * homogenous across the repository.
+ *
+ * `help`::
+ * the short help associated to what the option does.
+ * Must never be NULL (except for OPTION_END).
+ * OPTION_GROUP uses this pointer to store the group header.
+ *
+ * `flags`::
+ * mask of parse_opt_option_flags.
+ * PARSE_OPT_OPTARG: says that the argument is optionnal (not for BOOLEANs)
+ * PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs
+ *
+ * `callback`::
+ * pointer to the callback to use for OPTION_CALLBACK.
+ *
+ * `defval`::
+ * default value to fill (*->value) with for PARSE_OPT_OPTARG.
+ * CALLBACKS can use it like they want.
+ */
+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;
+ 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
$self->wc_subdir() and chdir($self->wc_subdir());
}
_execv_git_cmd(@args);
- die "exec failed: $!";
+ die qq[exec "@args" failed: $!];
}
# Execute the given Git command ($_[0]) with arguments ($_[1..])
--- /dev/null
+#include "cache.h"
+#include "commit.h"
+#include "interpolate.h"
+#include "utf8.h"
+#include "diff.h"
+#include "revision.h"
+
+static struct cmt_fmt_map {
+ const char *n;
+ size_t cmp_len;
+ enum cmit_fmt v;
+} cmt_fmts[] = {
+ { "raw", 1, CMIT_FMT_RAW },
+ { "medium", 1, CMIT_FMT_MEDIUM },
+ { "short", 1, CMIT_FMT_SHORT },
+ { "email", 1, CMIT_FMT_EMAIL },
+ { "full", 5, CMIT_FMT_FULL },
+ { "fuller", 5, CMIT_FMT_FULLER },
+ { "oneline", 1, CMIT_FMT_ONELINE },
+ { "format:", 7, CMIT_FMT_USERFORMAT},
+};
+
+static char *user_format;
+
+enum cmit_fmt get_commit_format(const char *arg)
+{
+ int i;
+
+ if (!arg || !*arg)
+ return CMIT_FMT_DEFAULT;
+ if (*arg == '=')
+ arg++;
+ if (!prefixcmp(arg, "format:")) {
+ if (user_format)
+ free(user_format);
+ user_format = xstrdup(arg + 7);
+ return CMIT_FMT_USERFORMAT;
+ }
+ for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
+ if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) &&
+ !strncmp(arg, cmt_fmts[i].n, strlen(arg)))
+ return cmt_fmts[i].v;
+ }
+
+ die("invalid --pretty format: %s", arg);
+}
+
+/*
+ * Generic support for pretty-printing the header
+ */
+static int get_one_line(const char *msg)
+{
+ int ret = 0;
+
+ for (;;) {
+ char c = *msg++;
+ if (!c)
+ break;
+ ret++;
+ if (c == '\n')
+ break;
+ }
+ return ret;
+}
+
+/* High bit set, or ISO-2022-INT */
+int non_ascii(int ch)
+{
+ ch = (ch & 0xff);
+ return ((ch & 0x80) || (ch == 0x1b));
+}
+
+static int is_rfc2047_special(char ch)
+{
+ return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
+}
+
+static void add_rfc2047(struct strbuf *sb, const char *line, int len,
+ const char *encoding)
+{
+ int i, last;
+
+ for (i = 0; i < len; i++) {
+ int ch = line[i];
+ if (non_ascii(ch))
+ goto needquote;
+ if ((i + 1 < len) && (ch == '=' && line[i+1] == '?'))
+ goto needquote;
+ }
+ strbuf_add(sb, line, len);
+ return;
+
+needquote:
+ strbuf_grow(sb, len * 3 + strlen(encoding) + 100);
+ strbuf_addf(sb, "=?%s?q?", encoding);
+ for (i = last = 0; i < len; i++) {
+ unsigned ch = line[i] & 0xFF;
+ /*
+ * We encode ' ' using '=20' even though rfc2047
+ * allows using '_' for readability. Unfortunately,
+ * many programs do not understand this and just
+ * leave the underscore in place.
+ */
+ if (is_rfc2047_special(ch) || ch == ' ') {
+ strbuf_add(sb, line + last, i - last);
+ strbuf_addf(sb, "=%02X", ch);
+ last = i + 1;
+ }
+ }
+ strbuf_add(sb, line + last, len - last);
+ strbuf_addstr(sb, "?=");
+}
+
+static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
+ const char *line, enum date_mode dmode,
+ const char *encoding)
+{
+ char *date;
+ int namelen;
+ unsigned long time;
+ int tz;
+ const char *filler = " ";
+
+ if (fmt == CMIT_FMT_ONELINE)
+ return;
+ date = strchr(line, '>');
+ if (!date)
+ return;
+ namelen = ++date - line;
+ time = strtoul(date, &date, 10);
+ tz = strtol(date, NULL, 10);
+
+ if (fmt == CMIT_FMT_EMAIL) {
+ char *name_tail = strchr(line, '<');
+ int display_name_length;
+ if (!name_tail)
+ return;
+ while (line < name_tail && isspace(name_tail[-1]))
+ name_tail--;
+ display_name_length = name_tail - line;
+ filler = "";
+ strbuf_addstr(sb, "From: ");
+ add_rfc2047(sb, line, display_name_length, encoding);
+ strbuf_add(sb, name_tail, namelen - display_name_length);
+ strbuf_addch(sb, '\n');
+ } else {
+ strbuf_addf(sb, "%s: %.*s%.*s\n", what,
+ (fmt == CMIT_FMT_FULLER) ? 4 : 0,
+ filler, namelen, line);
+ }
+ switch (fmt) {
+ case CMIT_FMT_MEDIUM:
+ strbuf_addf(sb, "Date: %s\n", show_date(time, tz, dmode));
+ break;
+ case CMIT_FMT_EMAIL:
+ strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822));
+ break;
+ case CMIT_FMT_FULLER:
+ strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode));
+ break;
+ default:
+ /* notin' */
+ break;
+ }
+}
+
+static int is_empty_line(const char *line, int *len_p)
+{
+ int len = *len_p;
+ while (len && isspace(line[len-1]))
+ len--;
+ *len_p = len;
+ return !len;
+}
+
+static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb,
+ const struct commit *commit, int abbrev)
+{
+ struct commit_list *parent = commit->parents;
+
+ if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
+ !parent || !parent->next)
+ return;
+
+ strbuf_addstr(sb, "Merge:");
+
+ while (parent) {
+ struct commit *p = parent->item;
+ const char *hex = NULL;
+ const char *dots;
+ if (abbrev)
+ hex = find_unique_abbrev(p->object.sha1, abbrev);
+ if (!hex)
+ hex = sha1_to_hex(p->object.sha1);
+ dots = (abbrev && strlen(hex) != 40) ? "..." : "";
+ parent = parent->next;
+
+ strbuf_addf(sb, " %s%s", hex, dots);
+ }
+ strbuf_addch(sb, '\n');
+}
+
+static char *get_header(const struct commit *commit, const char *key)
+{
+ int key_len = strlen(key);
+ const char *line = commit->buffer;
+
+ for (;;) {
+ const char *eol = strchr(line, '\n'), *next;
+
+ if (line == eol)
+ return NULL;
+ if (!eol) {
+ eol = line + strlen(line);
+ next = NULL;
+ } else
+ next = eol + 1;
+ if (eol - line > key_len &&
+ !strncmp(line, key, key_len) &&
+ line[key_len] == ' ') {
+ return xmemdupz(line + key_len + 1, eol - line - key_len - 1);
+ }
+ line = next;
+ }
+}
+
+static char *replace_encoding_header(char *buf, const char *encoding)
+{
+ struct strbuf tmp;
+ size_t start, len;
+ char *cp = buf;
+
+ /* guess if there is an encoding header before a \n\n */
+ while (strncmp(cp, "encoding ", strlen("encoding "))) {
+ cp = strchr(cp, '\n');
+ if (!cp || *++cp == '\n')
+ return buf;
+ }
+ start = cp - buf;
+ cp = strchr(cp, '\n');
+ if (!cp)
+ return buf; /* should not happen but be defensive */
+ len = cp + 1 - (buf + start);
+
+ strbuf_init(&tmp, 0);
+ strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1);
+ if (is_encoding_utf8(encoding)) {
+ /* we have re-coded to UTF-8; drop the header */
+ strbuf_remove(&tmp, start, len);
+ } else {
+ /* just replaces XXXX in 'encoding XXXX\n' */
+ strbuf_splice(&tmp, start + strlen("encoding "),
+ len - strlen("encoding \n"),
+ encoding, strlen(encoding));
+ }
+ return strbuf_detach(&tmp, NULL);
+}
+
+static char *logmsg_reencode(const struct commit *commit,
+ const char *output_encoding)
+{
+ static const char *utf8 = "utf-8";
+ const char *use_encoding;
+ char *encoding;
+ char *out;
+
+ if (!*output_encoding)
+ return NULL;
+ encoding = get_header(commit, "encoding");
+ use_encoding = encoding ? encoding : utf8;
+ if (!strcmp(use_encoding, output_encoding))
+ if (encoding) /* we'll strip encoding header later */
+ out = xstrdup(commit->buffer);
+ else
+ return NULL; /* nothing to do */
+ else
+ out = reencode_string(commit->buffer,
+ output_encoding, use_encoding);
+ if (out)
+ out = replace_encoding_header(out, output_encoding);
+
+ free(encoding);
+ return out;
+}
+
+static void fill_person(struct interp *table, const char *msg, int len)
+{
+ int start, end, tz = 0;
+ unsigned long date;
+ char *ep;
+
+ /* parse name */
+ for (end = 0; end < len && msg[end] != '<'; end++)
+ ; /* do nothing */
+ start = end + 1;
+ while (end > 0 && isspace(msg[end - 1]))
+ end--;
+ table[0].value = xmemdupz(msg, end);
+
+ if (start >= len)
+ return;
+
+ /* parse email */
+ for (end = start + 1; end < len && msg[end] != '>'; end++)
+ ; /* do nothing */
+
+ if (end >= len)
+ return;
+
+ table[1].value = xmemdupz(msg + start, end - start);
+
+ /* parse date */
+ for (start = end + 1; start < len && isspace(msg[start]); start++)
+ ; /* do nothing */
+ if (start >= len)
+ return;
+ date = strtoul(msg + start, &ep, 10);
+ if (msg + start == ep)
+ return;
+
+ table[5].value = xmemdupz(msg + start, ep - (msg + start));
+
+ /* parse tz */
+ for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
+ ; /* do nothing */
+ if (start + 1 < len) {
+ tz = strtoul(msg + start + 1, NULL, 10);
+ if (msg[start] == '-')
+ tz = -tz;
+ }
+
+ interp_set_entry(table, 2, show_date(date, tz, DATE_NORMAL));
+ interp_set_entry(table, 3, show_date(date, tz, DATE_RFC2822));
+ interp_set_entry(table, 4, show_date(date, tz, DATE_RELATIVE));
+ interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601));
+}
+
+void format_commit_message(const struct commit *commit,
+ const void *format, struct strbuf *sb)
+{
+ struct interp table[] = {
+ { "%H" }, /* commit hash */
+ { "%h" }, /* abbreviated commit hash */
+ { "%T" }, /* tree hash */
+ { "%t" }, /* abbreviated tree hash */
+ { "%P" }, /* parent hashes */
+ { "%p" }, /* abbreviated parent hashes */
+ { "%an" }, /* author name */
+ { "%ae" }, /* author email */
+ { "%ad" }, /* author date */
+ { "%aD" }, /* author date, RFC2822 style */
+ { "%ar" }, /* author date, relative */
+ { "%at" }, /* author date, UNIX timestamp */
+ { "%ai" }, /* author date, ISO 8601 */
+ { "%cn" }, /* committer name */
+ { "%ce" }, /* committer email */
+ { "%cd" }, /* committer date */
+ { "%cD" }, /* committer date, RFC2822 style */
+ { "%cr" }, /* committer date, relative */
+ { "%ct" }, /* committer date, UNIX timestamp */
+ { "%ci" }, /* committer date, ISO 8601 */
+ { "%e" }, /* encoding */
+ { "%s" }, /* subject */
+ { "%b" }, /* body */
+ { "%Cred" }, /* red */
+ { "%Cgreen" }, /* green */
+ { "%Cblue" }, /* blue */
+ { "%Creset" }, /* reset color */
+ { "%n" }, /* newline */
+ { "%m" }, /* left/right/bottom */
+ };
+ enum interp_index {
+ IHASH = 0, IHASH_ABBREV,
+ ITREE, ITREE_ABBREV,
+ IPARENTS, IPARENTS_ABBREV,
+ IAUTHOR_NAME, IAUTHOR_EMAIL,
+ IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE,
+ IAUTHOR_TIMESTAMP, IAUTHOR_ISO8601,
+ ICOMMITTER_NAME, ICOMMITTER_EMAIL,
+ ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822,
+ ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP,
+ ICOMMITTER_ISO8601,
+ IENCODING,
+ ISUBJECT,
+ IBODY,
+ IRED, IGREEN, IBLUE, IRESET_COLOR,
+ INEWLINE,
+ ILEFT_RIGHT,
+ };
+ struct commit_list *p;
+ char parents[1024];
+ unsigned long len;
+ int i;
+ enum { HEADER, SUBJECT, BODY } state;
+ const char *msg = commit->buffer;
+
+ if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table))
+ die("invalid interp table!");
+
+ /* these are independent of the commit */
+ interp_set_entry(table, IRED, "\033[31m");
+ interp_set_entry(table, IGREEN, "\033[32m");
+ interp_set_entry(table, IBLUE, "\033[34m");
+ interp_set_entry(table, IRESET_COLOR, "\033[m");
+ interp_set_entry(table, INEWLINE, "\n");
+
+ /* these depend on the commit */
+ if (!commit->object.parsed)
+ parse_object(commit->object.sha1);
+ interp_set_entry(table, IHASH, sha1_to_hex(commit->object.sha1));
+ interp_set_entry(table, IHASH_ABBREV,
+ find_unique_abbrev(commit->object.sha1,
+ DEFAULT_ABBREV));
+ interp_set_entry(table, ITREE, sha1_to_hex(commit->tree->object.sha1));
+ interp_set_entry(table, ITREE_ABBREV,
+ find_unique_abbrev(commit->tree->object.sha1,
+ DEFAULT_ABBREV));
+ interp_set_entry(table, ILEFT_RIGHT,
+ (commit->object.flags & BOUNDARY)
+ ? "-"
+ : (commit->object.flags & SYMMETRIC_LEFT)
+ ? "<"
+ : ">");
+
+ parents[1] = 0;
+ for (i = 0, p = commit->parents;
+ p && i < sizeof(parents) - 1;
+ p = p->next)
+ i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
+ sha1_to_hex(p->item->object.sha1));
+ interp_set_entry(table, IPARENTS, parents + 1);
+
+ parents[1] = 0;
+ for (i = 0, p = commit->parents;
+ p && i < sizeof(parents) - 1;
+ p = p->next)
+ i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
+ find_unique_abbrev(p->item->object.sha1,
+ DEFAULT_ABBREV));
+ interp_set_entry(table, IPARENTS_ABBREV, parents + 1);
+
+ for (i = 0, state = HEADER; msg[i] && state < BODY; i++) {
+ int eol;
+ for (eol = i; msg[eol] && msg[eol] != '\n'; eol++)
+ ; /* do nothing */
+
+ if (state == SUBJECT) {
+ table[ISUBJECT].value = xmemdupz(msg + i, eol - i);
+ i = eol;
+ }
+ if (i == eol) {
+ state++;
+ /* strip empty lines */
+ while (msg[eol + 1] == '\n')
+ eol++;
+ } else if (!prefixcmp(msg + i, "author "))
+ fill_person(table + IAUTHOR_NAME,
+ msg + i + 7, eol - i - 7);
+ else if (!prefixcmp(msg + i, "committer "))
+ fill_person(table + ICOMMITTER_NAME,
+ msg + i + 10, eol - i - 10);
+ else if (!prefixcmp(msg + i, "encoding "))
+ table[IENCODING].value =
+ xmemdupz(msg + i + 9, eol - i - 9);
+ i = eol;
+ }
+ if (msg[i])
+ table[IBODY].value = xstrdup(msg + i);
+
+ len = interpolate(sb->buf + sb->len, strbuf_avail(sb),
+ format, table, ARRAY_SIZE(table));
+ if (len > strbuf_avail(sb)) {
+ strbuf_grow(sb, len);
+ interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1,
+ format, table, ARRAY_SIZE(table));
+ }
+ strbuf_setlen(sb, sb->len + len);
+ interp_clear_table(table, ARRAY_SIZE(table));
+}
+
+static void pp_header(enum cmit_fmt fmt,
+ int abbrev,
+ enum date_mode dmode,
+ const char *encoding,
+ const struct commit *commit,
+ const char **msg_p,
+ struct strbuf *sb)
+{
+ int parents_shown = 0;
+
+ for (;;) {
+ const char *line = *msg_p;
+ int linelen = get_one_line(*msg_p);
+
+ if (!linelen)
+ return;
+ *msg_p += linelen;
+
+ if (linelen == 1)
+ /* End of header */
+ return;
+
+ if (fmt == CMIT_FMT_RAW) {
+ strbuf_add(sb, line, linelen);
+ continue;
+ }
+
+ if (!memcmp(line, "parent ", 7)) {
+ if (linelen != 48)
+ die("bad parent line in commit");
+ continue;
+ }
+
+ if (!parents_shown) {
+ struct commit_list *parent;
+ int num;
+ for (parent = commit->parents, num = 0;
+ parent;
+ parent = parent->next, num++)
+ ;
+ /* with enough slop */
+ strbuf_grow(sb, num * 50 + 20);
+ add_merge_info(fmt, sb, commit, abbrev);
+ parents_shown = 1;
+ }
+
+ /*
+ * MEDIUM == DEFAULT shows only author with dates.
+ * FULL shows both authors but not dates.
+ * FULLER shows both authors and dates.
+ */
+ if (!memcmp(line, "author ", 7)) {
+ strbuf_grow(sb, linelen + 80);
+ add_user_info("Author", fmt, sb, line + 7, dmode, encoding);
+ }
+ if (!memcmp(line, "committer ", 10) &&
+ (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) {
+ strbuf_grow(sb, linelen + 80);
+ add_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
+ }
+ }
+}
+
+static void pp_title_line(enum cmit_fmt fmt,
+ const char **msg_p,
+ struct strbuf *sb,
+ const char *subject,
+ const char *after_subject,
+ const char *encoding,
+ int plain_non_ascii)
+{
+ struct strbuf title;
+
+ strbuf_init(&title, 80);
+
+ for (;;) {
+ const char *line = *msg_p;
+ int linelen = get_one_line(line);
+
+ *msg_p += linelen;
+ if (!linelen || is_empty_line(line, &linelen))
+ break;
+
+ strbuf_grow(&title, linelen + 2);
+ if (title.len) {
+ if (fmt == CMIT_FMT_EMAIL) {
+ strbuf_addch(&title, '\n');
+ }
+ strbuf_addch(&title, ' ');
+ }
+ strbuf_add(&title, line, linelen);
+ }
+
+ strbuf_grow(sb, title.len + 1024);
+ if (subject) {
+ strbuf_addstr(sb, subject);
+ add_rfc2047(sb, title.buf, title.len, encoding);
+ } else {
+ strbuf_addbuf(sb, &title);
+ }
+ strbuf_addch(sb, '\n');
+
+ if (plain_non_ascii) {
+ const char *header_fmt =
+ "MIME-Version: 1.0\n"
+ "Content-Type: text/plain; charset=%s\n"
+ "Content-Transfer-Encoding: 8bit\n";
+ strbuf_addf(sb, header_fmt, encoding);
+ }
+ if (after_subject) {
+ strbuf_addstr(sb, after_subject);
+ }
+ if (fmt == CMIT_FMT_EMAIL) {
+ strbuf_addch(sb, '\n');
+ }
+ strbuf_release(&title);
+}
+
+static void pp_remainder(enum cmit_fmt fmt,
+ const char **msg_p,
+ struct strbuf *sb,
+ int indent)
+{
+ int first = 1;
+ for (;;) {
+ const char *line = *msg_p;
+ int linelen = get_one_line(line);
+ *msg_p += linelen;
+
+ if (!linelen)
+ break;
+
+ if (is_empty_line(line, &linelen)) {
+ if (first)
+ continue;
+ if (fmt == CMIT_FMT_SHORT)
+ break;
+ }
+ first = 0;
+
+ strbuf_grow(sb, linelen + indent + 20);
+ if (indent) {
+ memset(sb->buf + sb->len, ' ', indent);
+ strbuf_setlen(sb, sb->len + indent);
+ }
+ strbuf_add(sb, line, linelen);
+ strbuf_addch(sb, '\n');
+ }
+}
+
+void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
+ struct strbuf *sb, int abbrev,
+ const char *subject, const char *after_subject,
+ enum date_mode dmode, int plain_non_ascii)
+{
+ unsigned long beginning_of_body;
+ int indent = 4;
+ const char *msg = commit->buffer;
+ char *reencoded;
+ const char *encoding;
+
+ if (fmt == CMIT_FMT_USERFORMAT) {
+ format_commit_message(commit, user_format, sb);
+ return;
+ }
+
+ encoding = (git_log_output_encoding
+ ? git_log_output_encoding
+ : git_commit_encoding);
+ if (!encoding)
+ encoding = "utf-8";
+ reencoded = logmsg_reencode(commit, encoding);
+ if (reencoded) {
+ msg = reencoded;
+ }
+
+ if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
+ indent = 0;
+
+ /* After-subject is used to pass in Content-Type: multipart
+ * MIME header; in that case we do not have to do the
+ * plaintext content type even if the commit message has
+ * non 7-bit ASCII character. Otherwise, check if we need
+ * to say this is not a 7-bit ASCII.
+ */
+ if (fmt == CMIT_FMT_EMAIL && !after_subject) {
+ int i, ch, in_body;
+
+ for (in_body = i = 0; (ch = msg[i]); i++) {
+ if (!in_body) {
+ /* author could be non 7-bit ASCII but
+ * the log may be so; skip over the
+ * header part first.
+ */
+ if (ch == '\n' && msg[i+1] == '\n')
+ in_body = 1;
+ }
+ else if (non_ascii(ch)) {
+ plain_non_ascii = 1;
+ break;
+ }
+ }
+ }
+
+ pp_header(fmt, abbrev, dmode, encoding, commit, &msg, sb);
+ if (fmt != CMIT_FMT_ONELINE && !subject) {
+ strbuf_addch(sb, '\n');
+ }
+
+ /* Skip excess blank lines at the beginning of body, if any... */
+ for (;;) {
+ int linelen = get_one_line(msg);
+ int ll = linelen;
+ if (!linelen)
+ break;
+ if (!is_empty_line(msg, &ll))
+ break;
+ msg += linelen;
+ }
+
+ /* These formats treat the title line specially. */
+ if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
+ pp_title_line(fmt, &msg, sb, subject,
+ after_subject, encoding, plain_non_ascii);
+
+ beginning_of_body = sb->len;
+ if (fmt != CMIT_FMT_ONELINE)
+ pp_remainder(fmt, &msg, sb, indent);
+ strbuf_rtrim(sb);
+
+ /* Make sure there is an EOLN for the non-oneline case */
+ if (fmt != CMIT_FMT_ONELINE)
+ strbuf_addch(sb, '\n');
+
+ /*
+ * The caller may append additional body text in e-mail
+ * format. Make sure we did not strip the blank line
+ * between the header and the body.
+ */
+ if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
+ strbuf_addch(sb, '\n');
+ free(reencoded);
+}
+/*
+ * 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)
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) {
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);
}
#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
strbuf_addch(dst, '\'');
while (*src) {
- size_t len = strcspn(src, "'\\");
+ size_t len = strcspn(src, "'!");
strbuf_add(dst, src, len);
src += len;
while (need_bs_quote(*src)) {
* 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);
return NULL;
}
+void setup_work_tree(void) {
+ const char *work_tree = get_git_work_tree();
+ const char *git_dir = get_git_dir();
+ if (!is_absolute_path(git_dir))
+ set_git_dir(make_absolute_path(git_dir));
+ if (!work_tree || chdir(work_tree))
+ die("This operation must be run in a work tree");
+}
+
/*
* We cannot decide in this function whether we are in the work tree or
* not, since the config can only be read _after_ this function was called.
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) {
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);
* that way:
*
* strbuf_grow(sb, SOME_SIZE);
- * // ... here the memory areay starting at sb->buf, and of length
- * // sb_avail(sb) is all yours, and you are sure that sb_avail(sb) is at
- * // least SOME_SIZE
+ * ... Here, the memory array starting at sb->buf, and of length
+ * ... strbuf_avail(sb) is all yours, and you are sure that
+ * ... strbuf_avail(sb) is at least SOME_SIZE.
* strbuf_setlen(sb, sb->len + SOME_OTHER_SIZE);
*
- * Of course, SOME_OTHER_SIZE must be smaller or equal to sb_avail(sb).
+ * Of course, SOME_OTHER_SIZE must be smaller or equal to strbuf_avail(sb).
*
* Doing so is safe, though if it has to be done in many places, adding the
* missing API to the strbuf module is the way to go.
--- /dev/null
+#!/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
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
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
--- /dev/null
+#!/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
git reset --hard master &&
git cherry-pick side^ &&
git cherry-pick side &&
- EDITOR=: VISUAL=: git revert HEAD &&
+ git revert HEAD &&
check_encoding 3
'
git reset --hard master &&
git cherry-pick side^ &&
git cherry-pick side &&
- EDITOR=: VISUAL=: git revert HEAD &&
+ git revert HEAD &&
check_encoding 3 8859
'
git reset --hard master &&
git cherry-pick side^ &&
git cherry-pick side &&
- EDITOR=: VISUAL=: git revert HEAD &&
+ git revert HEAD &&
check_encoding 3
'
git reset --hard master &&
git cherry-pick side^ &&
git cherry-pick side &&
- EDITOR=: VISUAL=: git revert HEAD &&
+ git revert HEAD &&
check_encoding 3 8859
'
--- /dev/null
+#!/bin/sh
+
+test_description='format-patch -s should force MIME encoding as needed'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ >F &&
+ git add F &&
+ git commit -m initial &&
+ echo new line >F &&
+
+ test_tick &&
+ git commit -m "This adds some lines to F" F
+
+'
+
+test_expect_success 'format normally' '
+
+ git format-patch --stdout -1 >output &&
+ ! grep Content-Type output
+
+'
+
+test_expect_success 'format with signoff without funny signer name' '
+
+ git format-patch -s --stdout -1 >output &&
+ ! grep Content-Type output
+
+'
+
+test_expect_success 'format with non ASCII signer name' '
+
+ GIT_COMMITTER_NAME="\e$B$O$^$N\e(B \e$B$U$K$*$&\e(B" \
+ git format-patch -s --stdout -1 >output &&
+ grep Content-Type output
+
+'
+
+test_done
+
+
+
+
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
git fetch blub
'
+# the strange name is: a\!'b
+test_expect_success 'quoting of a strangely named repo' '
+ ! git fetch "a\\!'\''b" > result 2>&1 &&
+ cat result &&
+ grep "fatal: '\''a\\\\!'\''b'\''" result
+'
+
test_done
'
+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
'
+test_expect_success 'git-clean src/' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ git-clean src/ &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f src/part3.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git-clean src/ src/' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ git-clean src/ src/ &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f src/part3.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git-clean with prefix' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ (cd src/ && git-clean) &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f src/part3.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+test_expect_success 'git-clean -d with prefix and path' '
+
+ mkdir -p build docs src/feature &&
+ touch a.out src/part3.c src/feature/file.c docs/manual.txt obj.o build/lib.so &&
+ (cd src/ && git-clean -d feature/) &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f src/part3.c &&
+ test ! -f src/feature/file.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git-clean symbolic link' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ ln -s docs/manual.txt src/part4.c
+ git-clean &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test ! -f src/part3.c &&
+ test ! -f src/part4.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
test_expect_success 'git-clean -n' '
mkdir -p build docs &&
'
+test_expect_success 'git-clean -d src/ examples/' '
+
+ mkdir -p build docs examples &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so examples/1.c &&
+ git-clean -d src/ examples/ &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f src/part3.c &&
+ test ! -f examples/1.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
test_expect_success 'git-clean -x' '
mkdir -p build docs &&
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+test_description='git-svn dcommit clobber series'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize repo' "
+ mkdir import &&
+ cd import &&
+ awk 'BEGIN { for (i = 1; i < 64; i++) { print i } }' > file
+ svn import -m 'initial' . $svnrepo &&
+ cd .. &&
+ git svn init $svnrepo &&
+ git svn fetch &&
+ test -e file
+ "
+
+test_expect_success '(supposedly) non-conflicting change from SVN' "
+ test x\"\`sed -n -e 58p < file\`\" = x58 &&
+ test x\"\`sed -n -e 61p < file\`\" = x61 &&
+ svn co $svnrepo tmp &&
+ cd tmp &&
+ perl -i -p -e 's/^58\$/5588/' file &&
+ perl -i -p -e 's/^61\$/6611/' file &&
+ test x\"\`sed -n -e 58p < file\`\" = x5588 &&
+ test x\"\`sed -n -e 61p < file\`\" = x6611 &&
+ svn commit -m '58 => 5588, 61 => 6611' &&
+ cd ..
+ "
+
+test_expect_success 'some unrelated changes to git' "
+ echo hi > life &&
+ git update-index --add life &&
+ git commit -m hi-life &&
+ echo bye >> life &&
+ git commit -m bye-life life
+ "
+
+test_expect_success 'change file but in unrelated area' "
+ test x\"\`sed -n -e 4p < file\`\" = x4 &&
+ test x\"\`sed -n -e 7p < file\`\" = x7 &&
+ perl -i -p -e 's/^4\$/4444/' file &&
+ perl -i -p -e 's/^7\$/7777/' file &&
+ test x\"\`sed -n -e 4p < file\`\" = x4444 &&
+ test x\"\`sed -n -e 7p < file\`\" = x7777 &&
+ git commit -m '4 => 4444, 7 => 7777' file &&
+ git svn dcommit &&
+ svn up tmp &&
+ cd tmp &&
+ test x\"\`sed -n -e 4p < file\`\" = x4444 &&
+ test x\"\`sed -n -e 7p < file\`\" = x7777 &&
+ test x\"\`sed -n -e 58p < file\`\" = x5588 &&
+ test x\"\`sed -n -e 61p < file\`\" = x6611
+ "
+
+test_done
git cat-file commit refs/heads/svn^ | grep '^friend$'
"
+test_expect_success 'verify merge commit message' "
+ git rev-list --pretty=raw -1 refs/heads/svn | \
+ grep \" Merge branch 'merge' into svn\"
+ "
+
test_done
our \$export_ok = "";
our \$strict_export = "";
-CGI::Carp::set_programname("gitweb/gitweb.cgi");
EOF
cat >.git/description <<EOF
'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
--- /dev/null
+#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;
+}
return;
for (;;) {
- int cmp, len;
+ int cmp = cmp, len;
if (!fgets(buffer, sizeof(buffer), f)) {
fclose(f);
{
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) {
total++;
}
- start_progress_delay(&progress, "Checking %u files out...",
- "", total, 50, 2);
+ progress = start_progress_delay("Checking out files",
+ total, 50, 2);
cnt = 0;
}
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);
}
}
}
- if (total)
- stop_progress(&progress);;
+ stop_progress(&progress);
}
int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)