By default, the commits are shown in reverse chronological order.
---topo-order::
+--date-order::
+ Show no parents before all of its children are shown, but
+ otherwise show commits in the commit timestamp order.
- This option makes them appear in topological order (i.e.
- descendant commits are shown before their parents).
+--topo-order::
+ Show no parents before all of its children are shown, and
+ avoid showing commits on multiple lines of history
+ intermixed.
++
+For example, in a commit history like this:
++
+----------------------------------------------------------------
---date-order::
+ ---1----2----4----7
+ \ \
+ 3----5----6----8---
- This option is similar to '--topo-order' in the sense that no
- parent comes before all of its children, but otherwise things
- are still ordered in the commit timestamp order.
+----------------------------------------------------------------
++
+where the numbers denote the order of commit timestamps, `git
+rev-list` and friends with `--date-order` show the commits in the
+timestamp order: 8 7 6 5 4 3 2 1.
++
+With `--topo-order`, they would show 8 6 5 3 7 4 2 1 (or 8 7 4 2 6 5
+3 1); some older commits are shown before newer ones in order to
+avoid showing the commits from two parallel development track mixed
+together.
--reverse::
Only useful with '--objects'; print the object IDs that are not
in packs.
- --no-walk::
-
- Only show the given revs, but do not traverse their ancestors.
- This has no effect if a range is specified.
+ --no-walk[=(sorted|unsorted)]::
+
+ Only show the given commits, but do not traverse their ancestors.
+ This has no effect if a range is specified. If the argument
+ "unsorted" is given, the commits are show in the order they were
+ given on the command line. Otherwise (if "sorted" or no argument
+ was given), the commits are show in reverse chronological order
+ by commit time.
--do-walk::
--cc::
- This flag implies the '-c' options and further compresses the
+ This flag implies the '-c' option and further compresses the
patch output by omitting uninteresting hunks whose contents in
the parents have only two variants and the merge result picks
one of them without modification.
#include "parse-options.h"
#include "branch.h"
#include "streaming.h"
+#include "version.h"
/* Set a default date-time format for git log ("log.date" config variable) */
static const char *default_date_mode = NULL;
static const char *fmt_pretty;
static const char * const builtin_log_usage[] = {
- "git log [<options>] [<since>..<until>] [[--] <path>...]\n"
- " or: git show [options] <object>...",
+ N_("git log [<options>] [<since>..<until>] [[--] <path>...]\n")
+ N_(" or: git show [options] <object>..."),
NULL
};
int quiet = 0, source = 0;
const struct option builtin_log_options[] = {
- OPT_BOOLEAN(0, "quiet", &quiet, "suppress diff output"),
- OPT_BOOLEAN(0, "source", &source, "show source"),
- { OPTION_CALLBACK, 0, "decorate", NULL, NULL, "decorate options",
+ OPT_BOOLEAN(0, "quiet", &quiet, N_("suppress diff output")),
+ OPT_BOOLEAN(0, "source", &source, N_("show source")),
+ { OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
PARSE_OPT_OPTARG, decorate_callback},
OPT_END()
};
PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
PARSE_OPT_KEEP_DASHDASH);
- argc = setup_revisions(argc, argv, rev, opt);
if (quiet)
rev->diffopt.output_format |= DIFF_FORMAT_NO_OUTPUT;
+ argc = setup_revisions(argc, argv, rev, opt);
/* Any arguments at this point are not recognized */
if (argc > 1)
rev.simplify_history = 0;
memset(&opt, 0, sizeof(opt));
opt.def = "HEAD";
+ opt.revarg_opt = REVARG_COMMITTISH;
cmd_log_init(argc, argv, prefix, &rev, &opt);
if (!rev.diffopt.output_format)
rev.diffopt.output_format = DIFF_FORMAT_RAW;
init_revisions(&rev, prefix);
rev.diff = 1;
rev.always_show_header = 1;
- rev.no_walk = 1;
+ rev.no_walk = REVISION_WALK_NO_WALK_SORTED;
rev.diffopt.stat_width = -1; /* Scale to real terminal size */
memset(&opt, 0, sizeof(opt));
opt.tweak = show_rev_tweak_rev;
cmd_log_init(argc, argv, prefix, &rev, &opt);
+ if (!rev.no_walk)
+ return cmd_log_walk(&rev);
+
count = rev.pending.nr;
objects = rev.pending.objects;
for (i = 0; i < count && !ret; i++) {
rev.always_show_header = 1;
memset(&opt, 0, sizeof(opt));
opt.def = "HEAD";
+ opt.revarg_opt = REVARG_COMMITTISH;
cmd_log_init(argc, argv, prefix, &rev, &opt);
return cmd_log_walk(&rev);
}
return 0;
}
-static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const char *prefix)
+static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
{
struct rev_info check_rev;
struct commit *commit;
init_patch_ids(ids);
/* given a range a..b get all patch ids for b..a */
- init_revisions(&check_rev, prefix);
+ init_revisions(&check_rev, rev->prefix);
+ check_rev.max_parents = 1;
o1->flags ^= UNINTERESTING;
o2->flags ^= UNINTERESTING;
add_pending_object(&check_rev, o1, "o1");
die(_("revision walk setup failed"));
while ((commit = get_revision(&check_rev)) != NULL) {
- /* ignore merges */
- if (commit->parents && commit->parents->next)
- continue;
-
add_commit_patch_id(commit, ids);
}
}
static const char * const builtin_format_patch_usage[] = {
- "git format-patch [options] [<since> | <revision range>]",
+ N_("git format-patch [options] [<since> | <revision range>]"),
NULL
};
char *branch_name = NULL;
const struct option builtin_format_patch_options[] = {
{ OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
- "use [PATCH n/m] even with a single patch",
+ N_("use [PATCH n/m] even with a single patch"),
PARSE_OPT_NOARG, numbered_callback },
{ OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL,
- "use [PATCH] even with multiple patches",
+ N_("use [PATCH] even with multiple patches"),
PARSE_OPT_NOARG, no_numbered_callback },
- OPT_BOOLEAN('s', "signoff", &do_signoff, "add Signed-off-by:"),
+ OPT_BOOLEAN('s', "signoff", &do_signoff, N_("add Signed-off-by:")),
OPT_BOOLEAN(0, "stdout", &use_stdout,
- "print patches to standard out"),
+ N_("print patches to standard out")),
OPT_BOOLEAN(0, "cover-letter", &cover_letter,
- "generate a cover letter"),
+ N_("generate a cover letter")),
OPT_BOOLEAN(0, "numbered-files", &numbered_files,
- "use simple number sequence for output file names"),
- OPT_STRING(0, "suffix", &fmt_patch_suffix, "sfx",
- "use <sfx> instead of '.patch'"),
+ N_("use simple number sequence for output file names")),
+ OPT_STRING(0, "suffix", &fmt_patch_suffix, N_("sfx"),
+ N_("use <sfx> instead of '.patch'")),
OPT_INTEGER(0, "start-number", &start_number,
- "start numbering patches at <n> instead of 1"),
- { OPTION_CALLBACK, 0, "subject-prefix", &rev, "prefix",
- "Use [<prefix>] instead of [PATCH]",
+ N_("start numbering patches at <n> instead of 1")),
+ { OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"),
+ N_("Use [<prefix>] instead of [PATCH]"),
PARSE_OPT_NONEG, subject_prefix_callback },
{ OPTION_CALLBACK, 'o', "output-directory", &output_directory,
- "dir", "store resulting files in <dir>",
+ N_("dir"), N_("store resulting files in <dir>"),
PARSE_OPT_NONEG, output_directory_callback },
{ OPTION_CALLBACK, 'k', "keep-subject", &rev, NULL,
- "don't strip/add [PATCH]",
+ N_("don't strip/add [PATCH]"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback },
OPT_BOOLEAN(0, "no-binary", &no_binary_diff,
- "don't output binary diffs"),
+ N_("don't output binary diffs")),
OPT_BOOLEAN(0, "ignore-if-in-upstream", &ignore_if_in_upstream,
- "don't include a patch matching a commit upstream"),
+ N_("don't include a patch matching a commit upstream")),
{ OPTION_BOOLEAN, 'p', "no-stat", &use_patch_format, NULL,
- "show patch format instead of default (patch + stat)",
+ N_("show patch format instead of default (patch + stat)"),
PARSE_OPT_NONEG | PARSE_OPT_NOARG },
- OPT_GROUP("Messaging"),
- { OPTION_CALLBACK, 0, "add-header", NULL, "header",
- "add email header", 0, header_callback },
- { OPTION_CALLBACK, 0, "to", NULL, "email", "add To: header",
+ OPT_GROUP(N_("Messaging")),
+ { OPTION_CALLBACK, 0, "add-header", NULL, N_("header"),
+ N_("add email header"), 0, header_callback },
+ { OPTION_CALLBACK, 0, "to", NULL, N_("email"), N_("add To: header"),
0, to_callback },
- { OPTION_CALLBACK, 0, "cc", NULL, "email", "add Cc: header",
+ { OPTION_CALLBACK, 0, "cc", NULL, N_("email"), N_("add Cc: header"),
0, cc_callback },
- OPT_STRING(0, "in-reply-to", &in_reply_to, "message-id",
- "make first mail a reply to <message-id>"),
- { OPTION_CALLBACK, 0, "attach", &rev, "boundary",
- "attach the patch", PARSE_OPT_OPTARG,
+ OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"),
+ N_("make first mail a reply to <message-id>")),
+ { OPTION_CALLBACK, 0, "attach", &rev, N_("boundary"),
+ N_("attach the patch"), PARSE_OPT_OPTARG,
attach_callback },
- { OPTION_CALLBACK, 0, "inline", &rev, "boundary",
- "inline the patch",
+ { OPTION_CALLBACK, 0, "inline", &rev, N_("boundary"),
+ N_("inline the patch"),
PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
inline_callback },
- { OPTION_CALLBACK, 0, "thread", &thread, "style",
- "enable message threading, styles: shallow, deep",
+ { OPTION_CALLBACK, 0, "thread", &thread, N_("style"),
+ N_("enable message threading, styles: shallow, deep"),
PARSE_OPT_OPTARG, thread_callback },
- OPT_STRING(0, "signature", &signature, "signature",
- "add a signature"),
+ OPT_STRING(0, "signature", &signature, N_("signature"),
+ N_("add a signature")),
OPT_BOOLEAN(0, "quiet", &quiet,
- "don't print the patch filenames"),
+ N_("don't print the patch filenames")),
OPT_END()
};
rev.subject_prefix = fmt_patch_subject_prefix;
memset(&s_r_opt, 0, sizeof(s_r_opt));
s_r_opt.def = "HEAD";
+ s_r_opt.revarg_opt = REVARG_COMMITTISH;
if (default_attach) {
rev.mime_boundary = default_attach;
if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0)
return 0;
}
- get_patch_ids(&rev, &ids, prefix);
+ get_patch_ids(&rev, &ids);
}
if (!use_stdout)
}
static const char * const cherry_usage[] = {
- "git cherry [-v] [<upstream> [<head> [<limit>]]]",
+ N_("git cherry [-v] [<upstream> [<head> [<limit>]]]"),
NULL
};
struct option options[] = {
OPT__ABBREV(&abbrev),
- OPT__VERBOSE(&verbose, "be verbose"),
+ OPT__VERBOSE(&verbose, N_("be verbose")),
OPT_END()
};
}
init_revisions(&revs, prefix);
- revs.diff = 1;
- revs.combine_merges = 0;
- revs.ignore_merges = 1;
- DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
+ revs.max_parents = 1;
if (add_pending_commit(head, &revs, 0))
die(_("Unknown commit %s"), head);
return 0;
}
- get_patch_ids(&revs, &ids, prefix);
+ get_patch_ids(&revs, &ids);
if (limit && add_pending_commit(limit, &revs, UNINTERESTING))
die(_("Unknown commit %s"), limit);
if (prepare_revision_walk(&revs))
die(_("revision walk setup failed"));
while ((commit = get_revision(&revs)) != NULL) {
- /* ignore merges */
- if (commit->parents && commit->parents->next)
- continue;
-
commit_list_insert(commit, &list);
}
*/
static const char * const revert_usage[] = {
- "git revert [options] <commit-ish>",
- "git revert <subcommand>",
+ N_("git revert [options] <commit-ish>"),
+ N_("git revert <subcommand>"),
NULL
};
static const char * const cherry_pick_usage[] = {
- "git cherry-pick [options] <commit-ish>",
- "git cherry-pick <subcommand>",
+ N_("git cherry-pick [options] <commit-ish>"),
+ N_("git cherry-pick <subcommand>"),
NULL
};
int contin = 0;
int rollback = 0;
struct option options[] = {
- OPT_BOOLEAN(0, "quit", &remove_state, "end revert or cherry-pick sequence"),
- OPT_BOOLEAN(0, "continue", &contin, "resume revert or cherry-pick sequence"),
- OPT_BOOLEAN(0, "abort", &rollback, "cancel revert or cherry-pick sequence"),
- OPT_BOOLEAN('n', "no-commit", &opts->no_commit, "don't automatically commit"),
- OPT_BOOLEAN('e', "edit", &opts->edit, "edit the commit message"),
+ OPT_BOOLEAN(0, "quit", &remove_state, N_("end revert or cherry-pick sequence")),
+ OPT_BOOLEAN(0, "continue", &contin, N_("resume revert or cherry-pick sequence")),
+ OPT_BOOLEAN(0, "abort", &rollback, N_("cancel revert or cherry-pick sequence")),
+ OPT_BOOLEAN('n', "no-commit", &opts->no_commit, N_("don't automatically commit")),
+ OPT_BOOLEAN('e', "edit", &opts->edit, N_("edit the commit message")),
OPT_NOOP_NOARG('r', NULL),
- OPT_BOOLEAN('s', "signoff", &opts->signoff, "add Signed-off-by:"),
- OPT_INTEGER('m', "mainline", &opts->mainline, "parent number"),
+ OPT_BOOLEAN('s', "signoff", &opts->signoff, N_("add Signed-off-by:")),
+ OPT_INTEGER('m', "mainline", &opts->mainline, N_("parent number")),
OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
- OPT_STRING(0, "strategy", &opts->strategy, "strategy", "merge strategy"),
- OPT_CALLBACK('X', "strategy-option", &opts, "option",
- "option for merge strategy", option_parse_x),
+ OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")),
+ OPT_CALLBACK('X', "strategy-option", &opts, N_("option"),
+ N_("option for merge strategy"), option_parse_x),
+ OPT_END(),
OPT_END(),
OPT_END(),
OPT_END(),
if (opts->action == REPLAY_PICK) {
struct option cp_extra[] = {
- OPT_BOOLEAN('x', NULL, &opts->record_origin, "append commit name"),
- OPT_BOOLEAN(0, "ff", &opts->allow_ff, "allow fast-forward"),
- OPT_BOOLEAN(0, "allow-empty", &opts->allow_empty, "preserve initially empty commits"),
- OPT_BOOLEAN(0, "keep-redundant-commits", &opts->keep_redundant_commits, "keep redundant, empty commits"),
+ OPT_BOOLEAN('x', NULL, &opts->record_origin, N_("append commit name")),
+ OPT_BOOLEAN(0, "ff", &opts->allow_ff, N_("allow fast-forward")),
+ OPT_BOOLEAN(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")),
+ OPT_BOOLEAN(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")),
+ OPT_BOOLEAN(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
OPT_END(),
};
if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))
struct setup_revision_opt s_r_opt;
opts->revs = xmalloc(sizeof(*opts->revs));
init_revisions(opts->revs, NULL);
- opts->revs->no_walk = 1;
+ opts->revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED;
if (argc < 2)
usage_with_options(usage_str, options);
memset(&s_r_opt, 0, sizeof(s_r_opt));
static void file_add_remove(struct diff_options *options,
int addremove, unsigned mode,
const unsigned char *sha1,
+ int sha1_valid,
const char *fullpath, unsigned dirty_submodule)
{
int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
unsigned old_mode, unsigned new_mode,
const unsigned char *old_sha1,
const unsigned char *new_sha1,
+ int old_sha1_valid, int new_sha1_valid,
const char *fullpath,
unsigned old_dirty_submodule, unsigned new_dirty_submodule)
{
flags ^= UNINTERESTING;
arg++;
}
- if (get_sha1(arg, sha1))
+ if (get_sha1_committish(arg, sha1))
return 0;
while (1) {
it = get_reference(revs, arg, sha1, 0);
revs->limited = 1;
}
-int handle_revision_arg(const char *arg_, struct rev_info *revs,
- int flags,
- int cant_be_filename)
+int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
{
- unsigned mode;
+ struct object_context oc;
char *dotdot;
struct object *object;
unsigned char sha1[20];
int local_flags;
const char *arg = arg_;
+ int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME;
+ unsigned get_sha1_flags = 0;
dotdot = strstr(arg, "..");
if (dotdot) {
const char *this = arg;
int symmetric = *next == '.';
unsigned int flags_exclude = flags ^ UNINTERESTING;
+ static const char head_by_default[] = "HEAD";
unsigned int a_flags;
*dotdot = 0;
next += symmetric;
if (!*next)
- next = "HEAD";
+ next = head_by_default;
if (dotdot == arg)
- this = "HEAD";
- if (!get_sha1(this, from_sha1) &&
- !get_sha1(next, sha1)) {
+ this = head_by_default;
+ if (this == head_by_default && next == head_by_default &&
+ !symmetric) {
+ /*
+ * Just ".."? That is not a range but the
+ * pathspec for the parent directory.
+ */
+ if (!cant_be_filename) {
+ *dotdot = '.';
+ return -1;
+ }
+ }
+ if (!get_sha1_committish(this, from_sha1) &&
+ !get_sha1_committish(next, sha1)) {
struct commit *a, *b;
struct commit_list *exclude;
local_flags = UNINTERESTING;
arg++;
}
- if (get_sha1_with_mode(arg, sha1, &mode))
+
+ if (revarg_opt & REVARG_COMMITTISH)
+ get_sha1_flags = GET_SHA1_COMMITTISH;
+
+ if (get_sha1_with_context(arg, get_sha1_flags, sha1, &oc))
return revs->ignore_missing ? 0 : -1;
if (!cant_be_filename)
verify_non_filename(revs->prefix, arg);
object = get_reference(revs, arg, sha1, flags ^ local_flags);
add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
- add_pending_object_with_mode(revs, object, arg, mode);
+ add_pending_object_with_mode(revs, object, arg, oc.mode);
return 0;
}
}
die("options not supported in --stdin mode");
}
- if (handle_revision_arg(sb.buf, revs, 0, 1))
+ if (handle_revision_arg(sb.buf, revs, 0, REVARG_CANNOT_BE_FILENAME))
die("bad revision '%s'", sb.buf);
}
if (seen_dashdash)
!strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
!strcmp(arg, "--bisect") || !prefixcmp(arg, "--glob=") ||
!prefixcmp(arg, "--branches=") || !prefixcmp(arg, "--tags=") ||
- !prefixcmp(arg, "--remotes="))
+ !prefixcmp(arg, "--remotes=") || !prefixcmp(arg, "--no-walk="))
{
unkv[(*unkc)++] = arg;
return 1;
revs->topo_order = 1;
} else if (!strcmp(arg, "--simplify-merges")) {
revs->simplify_merges = 1;
+ revs->topo_order = 1;
revs->rewrite_parents = 1;
revs->simplify_history = 0;
revs->limited = 1;
} else if (!strcmp(arg, "--simplify-by-decoration")) {
revs->simplify_merges = 1;
+ revs->topo_order = 1;
revs->rewrite_parents = 1;
revs->simplify_history = 0;
revs->simplify_by_decoration = 1;
} else if (!strcmp(arg, "--not")) {
*flags ^= UNINTERESTING;
} else if (!strcmp(arg, "--no-walk")) {
- revs->no_walk = 1;
+ revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
+ } else if (!prefixcmp(arg, "--no-walk=")) {
+ /*
+ * Detached form ("--no-walk X" as opposed to "--no-walk=X")
+ * not allowed, since the argument is optional.
+ */
+ if (!strcmp(arg + 10, "sorted"))
+ revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
+ else if (!strcmp(arg + 10, "unsorted"))
+ revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED;
+ else
+ return error("invalid argument to --no-walk");
} else if (!strcmp(arg, "--do-walk")) {
revs->no_walk = 0;
} else {
*/
int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt)
{
- int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0;
+ int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0, revarg_opt;
struct cmdline_pathspec prune_data;
const char *submodule = NULL;
/* Second, deal with arguments and options */
flags = 0;
+ revarg_opt = opt ? opt->revarg_opt : 0;
+ if (seen_dashdash)
+ revarg_opt |= REVARG_CANNOT_BE_FILENAME;
read_from_stdin = 0;
for (left = i = 1; i < argc; i++) {
const char *arg = argv[i];
continue;
}
- if (handle_revision_arg(arg, revs, flags, seen_dashdash)) {
+
+ if (handle_revision_arg(arg, revs, flags, revarg_opt)) {
int j;
if (seen_dashdash || *arg == '^')
die("bad revision '%s'", arg);
* but the latter we have checked in the main loop.
*/
for (j = i; j < argc; j++)
- verify_filename(revs->prefix, argv[j]);
+ verify_filename(revs->prefix, argv[j], j == i);
append_prune_data(&prune_data, argv + i);
break;
if (revs->def && !revs->pending.nr && !got_rev_arg) {
unsigned char sha1[20];
struct object *object;
- unsigned mode;
- if (get_sha1_with_mode(revs->def, sha1, &mode))
+ struct object_context oc;
+ if (get_sha1_with_context(revs->def, 0, sha1, &oc))
die("bad default revision '%s'", revs->def);
object = get_reference(revs, revs->def, sha1, 0);
- add_pending_object_with_mode(revs, object, revs->def, mode);
+ add_pending_object_with_mode(revs, object, revs->def, oc.mode);
}
/* Did the user ask for any diff output? Run the diff! */
if (revs->combine_merges)
revs->ignore_merges = 0;
revs->diffopt.abbrev = revs->abbrev;
- if (diff_setup_done(&revs->diffopt) < 0)
- die("diff_setup_done failed");
+ diff_setup_done(&revs->diffopt);
compile_grep_patterns(&revs->grep_filter);
}
/*
- * Do we know what commit all of our parents should be rewritten to?
- * Otherwise we are not ready to rewrite this one yet.
+ * Do we know what commit all of our parents that matter
+ * should be rewritten to? Otherwise we are not ready to
+ * rewrite this one yet.
*/
for (cnt = 0, p = commit->parents; p; p = p->next) {
pst = locate_simplify_state(revs, p->item);
tail = &commit_list_insert(p->item, tail)->next;
cnt++;
}
+ if (revs->first_parent_only)
+ break;
}
if (cnt) {
tail = &commit_list_insert(commit, tail)->next;
for (p = commit->parents; p; p = p->next) {
pst = locate_simplify_state(revs, p->item);
p->item = pst->simplified;
+ if (revs->first_parent_only)
+ break;
}
- cnt = remove_duplicate_parents(commit);
+ if (!revs->first_parent_only)
+ cnt = remove_duplicate_parents(commit);
+ else
+ cnt = 1;
/*
* It is possible that we are a merge and one side branch
static void simplify_merges(struct rev_info *revs)
{
- struct commit_list *list;
+ struct commit_list *list, *next;
struct commit_list *yet_to_do, **tail;
+ struct commit *commit;
- if (!revs->topo_order)
- sort_in_topological_order(&revs->commits, revs->lifo);
if (!revs->prune)
return;
/* feed the list reversed */
yet_to_do = NULL;
- for (list = revs->commits; list; list = list->next)
- commit_list_insert(list->item, &yet_to_do);
+ for (list = revs->commits; list; list = next) {
+ commit = list->item;
+ next = list->next;
+ /*
+ * Do not free(list) here yet; the original list
+ * is used later in this function.
+ */
+ commit_list_insert(commit, &yet_to_do);
+ }
while (yet_to_do) {
list = yet_to_do;
yet_to_do = NULL;
tail = &yet_to_do;
while (list) {
- struct commit *commit = list->item;
- struct commit_list *next = list->next;
+ commit = list->item;
+ next = list->next;
free(list);
list = next;
tail = simplify_one(revs, commit, tail);
revs->commits = NULL;
tail = &revs->commits;
while (list) {
- struct commit *commit = list->item;
- struct commit_list *next = list->next;
struct merge_simplify_state *st;
+
+ commit = list->item;
+ next = list->next;
free(list);
list = next;
st = locate_simplify_state(revs, commit);
}
e++;
}
- commit_list_sort_by_date(&revs->commits);
if (!revs->leak_pending)
free(list);
+ if (revs->no_walk != REVISION_WALK_NO_WALK_UNSORTED)
+ commit_list_sort_by_date(&revs->commits);
if (revs->no_walk)
return 0;
if (revs->limited)
}
/*
- * Now pick up what they want to give us
+ * If our max_count counter has reached zero, then we are done. We
+ * don't simply return NULL because we still might need to show
+ * boundary commits. But we want to avoid calling get_revision_1, which
+ * might do a considerable amount of work finding the next commit only
+ * for us to throw it away.
+ *
+ * If it is non-zero, then either we don't have a max_count at all
+ * (-1), or it is still counting, in which case we decrement.
*/
- c = get_revision_1(revs);
- if (c) {
- while (0 < revs->skip_count) {
- revs->skip_count--;
- c = get_revision_1(revs);
- if (!c)
- break;
+ if (revs->max_count) {
+ c = get_revision_1(revs);
+ if (c) {
+ while (0 < revs->skip_count) {
+ revs->skip_count--;
+ c = get_revision_1(revs);
+ if (!c)
+ break;
+ }
}
- }
- /*
- * Check the max_count.
- */
- switch (revs->max_count) {
- case -1:
- break;
- case 0:
- c = NULL;
- break;
- default:
- revs->max_count--;
+ if (revs->max_count > 0)
+ revs->max_count--;
}
if (c)
} *rev;
};
+ #define REVISION_WALK_WALK 0
+ #define REVISION_WALK_NO_WALK_SORTED 1
+ #define REVISION_WALK_NO_WALK_UNSORTED 2
+
struct rev_info {
/* Starting list */
struct commit_list *commits;
/* Traversal flags */
unsigned int dense:1,
prune:1,
- no_walk:1,
+ no_walk:2,
show_all:1,
remove_empty_trees:1,
simplify_history:1,
void (*tweak)(struct rev_info *, struct setup_revision_opt *);
const char *submodule;
int assume_dashdash;
+ unsigned revarg_opt;
};
extern void init_revisions(struct rev_info *revs, const char *prefix);
extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
const struct option *options,
const char * const usagestr[]);
-extern int handle_revision_arg(const char *arg, struct rev_info *revs,int flags,int cant_be_filename);
+#define REVARG_CANNOT_BE_FILENAME 01
+#define REVARG_COMMITTISH 02
+extern int handle_revision_arg(const char *arg, struct rev_info *revs, int flags, unsigned revarg_opt);
extern void reset_revision_walk(void);
extern int prepare_revision_walk(struct rev_info *revs);
if (allow_empty)
argv_array_push(&array, "--allow-empty");
+ if (opts->allow_empty_message)
+ argv_array_push(&array, "--allow-empty-message");
+
rc = run_command_v_opt(array.argv, RUN_GIT_CMD);
argv_array_clear(&array);
return rc;
static void prepare_revs(struct replay_opts *opts)
{
- if (opts->action != REPLAY_REVERT)
+ /*
+ * picking (but not reverting) ranges (but not individual revisions)
+ * should be done in reverse
+ */
+ if (opts->action == REPLAY_PICK && !opts->revs->no_walk)
opts->revs->reverse ^= 1;
if (prepare_revision_walk(opts->revs))
test_cmp expect actual
'
+ test_expect_success 'git log --no-walk=sorted <commits> sorts by commit time' '
+ git log --no-walk=sorted --oneline 5d31159 804a787 394ef78 > actual &&
+ test_cmp expect actual
+ '
+
cat > expect << EOF
5d31159 fourth
804a787 sixth
394ef78 fifth
EOF
+ test_expect_success 'git log --no-walk=unsorted <commits> leaves list of commits as given' '
+ git log --no-walk=unsorted --oneline 5d31159 804a787 394ef78 > actual &&
+ test_cmp expect actual
+ '
+
test_expect_success 'git show <commits> leaves list of commits as given' '
git show --oneline -s 5d31159 804a787 394ef78 > actual &&
test_cmp expect actual
test_expect_success 'log --graph with diff and stats' '
git log --graph --pretty=short --stat -p >actual &&
sanitize_output >actual.sanitized <actual &&
- test_cmp expect actual.sanitized
+ test_i18ncmp expect actual.sanitized
+'
+
+test_expect_success 'dotdot is a parent directory' '
+ mkdir -p a/b &&
+ ( echo sixth && echo fifth ) >expect &&
+ ( cd a/b && git log --format=%s .. ) >actual &&
+ test_cmp expect actual
'
test_done