diff options
-rw-r--r-- | .github/CONTRIBUTING.md | 19 | ||||
-rw-r--r-- | .github/PULL_REQUEST_TEMPLATE.md | 7 | ||||
-rw-r--r-- | Documentation/git-rm.txt | 9 | ||||
-rw-r--r-- | Documentation/pretty-formats.txt | 4 | ||||
-rw-r--r-- | builtin/cat-file.c | 4 | ||||
-rw-r--r-- | builtin/diff-tree.c | 8 | ||||
-rw-r--r-- | builtin/diff.c | 60 | ||||
-rw-r--r-- | builtin/grep.c | 4 | ||||
-rw-r--r-- | builtin/log.c | 10 | ||||
-rw-r--r-- | builtin/name-rev.c | 59 | ||||
-rw-r--r-- | cache.h | 10 | ||||
-rwxr-xr-x | git-stash.sh | 3 | ||||
-rw-r--r-- | revision.c | 330 | ||||
-rw-r--r-- | revision.h | 5 | ||||
-rw-r--r-- | sha1_name.c | 11 | ||||
-rwxr-xr-x | t/t3200-branch.sh | 17 | ||||
-rwxr-xr-x | t/t3903-stash.sh | 16 | ||||
-rwxr-xr-x | t/t4063-diff-blobs.sh | 96 | ||||
-rwxr-xr-x | t/t4202-log.sh | 11 | ||||
-rwxr-xr-x | t/t5100-mailinfo.sh | 42 | ||||
-rwxr-xr-x | t/t5313-pack-bounds-checks.sh | 8 | ||||
-rw-r--r-- | tree-walk.c | 1 |
22 files changed, 521 insertions, 213 deletions
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000000..64e605a02b --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,19 @@ +## Contributing to Git + +Thanks for taking the time to contribute to Git! Please be advised that the +Git community does not use github.com for their contributions. Instead, we use +a mailing list (git@vger.kernel.org) for code submissions, code +reviews, and bug reports. + +Nevertheless, you can use [submitGit](http://submitgit.herokuapp.com/) to +conveniently send your Pull Requests commits to our mailing list. + +Please read ["A note from the maintainer"](https://git.kernel.org/pub/scm/git/git.git/plain/MaintNotes?h=todo) +to learn how the Git project is managed, and how you can work with it. +In addition, we highly recommend you to read [our submission guidelines](../Documentation/SubmittingPatches). + +If you prefer video, then [this talk](https://www.youtube.com/watch?v=Q7i_qQW__q4&feature=youtu.be&t=6m4s) +might be useful to you as the presenter walks you through the contribution +process by example. + +Your friendly Git community! diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..adba13e5ba --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,7 @@ +Thanks for taking the time to contribute to Git! Please be advised that the +Git community does not use github.com for their contributions. Instead, we use +a mailing list (git@vger.kernel.org) for code submissions, code reviews, and +bug reports. Nevertheless, you can use submitGit to conveniently send your Pull +Requests commits to our mailing list. + +Please read the "guidelines for contributing" linked above! diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt index f1efc116eb..8c87e8cdd7 100644 --- a/Documentation/git-rm.txt +++ b/Documentation/git-rm.txt @@ -140,10 +140,11 @@ Only submodules using a gitfile (which means they were cloned with a Git version 1.7.8 or newer) will be removed from the work tree, as their repository lives inside the .git directory of the superproject. If a submodule (or one of those nested inside it) -still uses a .git directory, `git rm` will fail - no matter if forced -or not - to protect the submodule's history. If it exists the -submodule.<name> section in the linkgit:gitmodules[5] file will also -be removed and that file will be staged (unless --cached or -n are used). +still uses a .git directory, `git rm` will move the submodules +git directory into the superprojects git directory to protect +the submodule's history. If it exists the submodule.<name> section +in the linkgit:gitmodules[5] file will also be removed and that file +will be staged (unless --cached or -n are used). A submodule is considered up-to-date when the HEAD is the same as recorded in the index, no tracked files are modified and no untracked diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index 38040e95b5..a48d267e26 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -213,8 +213,8 @@ If you add a `+` (plus sign) after '%' of a placeholder, a line-feed is inserted immediately before the expansion if and only if the placeholder expands to a non-empty string. -If you add a `-` (minus sign) after '%' of a placeholder, line-feeds that -immediately precede the expansion are deleted if and only if the +If you add a `-` (minus sign) after '%' of a placeholder, all consecutive +line-feeds immediately preceding the expansion are deleted if and only if the placeholder expands to an empty string. If you add a ` ` (space) after '%' of a placeholder, a space diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 9af863e791..73c81f0cb1 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -61,7 +61,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, if (unknown_type) flags |= LOOKUP_UNKNOWN_OBJECT; - if (get_sha1_with_context(obj_name, 0, oid.hash, &obj_context)) + if (get_sha1_with_context(obj_name, GET_SHA1_RECORD_PATH, + oid.hash, &obj_context)) die("Not a valid object name %s", obj_name); if (!path) @@ -166,6 +167,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name, write_or_die(1, buf, size); free(buf); + free(obj_context.path); return 0; } diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index 326f88b657..a570fea55b 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -128,9 +128,11 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) } /* - * NOTE! We expect "a ^b" to be equal to "a..b", so we - * reverse the order of the objects if the second one - * is marked UNINTERESTING. + * NOTE! We expect "a..b" to expand to "^a b" but it is + * perfectly valid for revision range parser to yield "b ^a", + * which means the same thing. If we get the latter, i.e. the + * second one is marked UNINTERESTING, we recover the original + * order the user gave, i.e. "a..b", by swapping the trees. */ nr_sha1 = opt->pending.nr; switch (nr_sha1) { diff --git a/builtin/diff.c b/builtin/diff.c index d184aafab9..5e7c6428c9 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -20,23 +20,22 @@ #define DIFF_NO_INDEX_EXPLICIT 1 #define DIFF_NO_INDEX_IMPLICIT 2 -struct blobinfo { - struct object_id oid; - const char *name; - unsigned mode; -}; - static const char builtin_diff_usage[] = "git diff [<options>] [<commit> [<commit>]] [--] [<path>...]"; +static const char *blob_path(struct object_array_entry *entry) +{ + return entry->path ? entry->path : entry->name; +} + static void stuff_change(struct diff_options *opt, unsigned old_mode, unsigned new_mode, const struct object_id *old_oid, const struct object_id *new_oid, int old_oid_valid, int new_oid_valid, - const char *old_name, - const char *new_name) + const char *old_path, + const char *new_path) { struct diff_filespec *one, *two; @@ -47,16 +46,16 @@ static void stuff_change(struct diff_options *opt, if (DIFF_OPT_TST(opt, REVERSE_DIFF)) { SWAP(old_mode, new_mode); SWAP(old_oid, new_oid); - SWAP(old_name, new_name); + SWAP(old_path, new_path); } if (opt->prefix && - (strncmp(old_name, opt->prefix, opt->prefix_length) || - strncmp(new_name, opt->prefix, opt->prefix_length))) + (strncmp(old_path, opt->prefix, opt->prefix_length) || + strncmp(new_path, opt->prefix, opt->prefix_length))) return; - one = alloc_filespec(old_name); - two = alloc_filespec(new_name); + one = alloc_filespec(old_path); + two = alloc_filespec(new_path); fill_filespec(one, old_oid->hash, old_oid_valid, old_mode); fill_filespec(two, new_oid->hash, new_oid_valid, new_mode); @@ -65,7 +64,7 @@ static void stuff_change(struct diff_options *opt, static int builtin_diff_b_f(struct rev_info *revs, int argc, const char **argv, - struct blobinfo *blob) + struct object_array_entry **blob) { /* Blob vs file in the working tree*/ struct stat st; @@ -84,14 +83,15 @@ static int builtin_diff_b_f(struct rev_info *revs, diff_set_mnemonic_prefix(&revs->diffopt, "o/", "w/"); - if (blob[0].mode == S_IFINVALID) - blob[0].mode = canon_mode(st.st_mode); + if (blob[0]->mode == S_IFINVALID) + blob[0]->mode = canon_mode(st.st_mode); stuff_change(&revs->diffopt, - blob[0].mode, canon_mode(st.st_mode), - &blob[0].oid, &null_oid, + blob[0]->mode, canon_mode(st.st_mode), + &blob[0]->item->oid, &null_oid, 1, 0, - path, path); + blob[0]->path ? blob[0]->path : path, + path); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); return 0; @@ -99,24 +99,24 @@ static int builtin_diff_b_f(struct rev_info *revs, static int builtin_diff_blobs(struct rev_info *revs, int argc, const char **argv, - struct blobinfo *blob) + struct object_array_entry **blob) { unsigned mode = canon_mode(S_IFREG | 0644); if (argc > 1) usage(builtin_diff_usage); - if (blob[0].mode == S_IFINVALID) - blob[0].mode = mode; + if (blob[0]->mode == S_IFINVALID) + blob[0]->mode = mode; - if (blob[1].mode == S_IFINVALID) - blob[1].mode = mode; + if (blob[1]->mode == S_IFINVALID) + blob[1]->mode = mode; stuff_change(&revs->diffopt, - blob[0].mode, blob[1].mode, - &blob[0].oid, &blob[1].oid, + blob[0]->mode, blob[1]->mode, + &blob[0]->item->oid, &blob[1]->item->oid, 1, 1, - blob[0].name, blob[1].name); + blob_path(blob[0]), blob_path(blob[1])); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); return 0; @@ -259,7 +259,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) struct rev_info rev; struct object_array ent = OBJECT_ARRAY_INIT; int blobs = 0, paths = 0; - struct blobinfo blob[2]; + struct object_array_entry *blob[2]; int nongit = 0, no_index = 0; int result = 0; @@ -408,9 +408,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) } else if (obj->type == OBJ_BLOB) { if (2 <= blobs) die(_("more than two blobs given: '%s'"), name); - hashcpy(blob[blobs].oid.hash, obj->oid.hash); - blob[blobs].name = name; - blob[blobs].mode = entry->mode; + blob[blobs] = entry; blobs++; } else { diff --git a/builtin/grep.c b/builtin/grep.c index 3ffb5b4e81..254c1c7849 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -1190,7 +1190,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) break; } - if (get_sha1_with_context(arg, 0, oid.hash, &oc)) { + if (get_sha1_with_context(arg, GET_SHA1_RECORD_PATH, + oid.hash, &oc)) { if (seen_dashdash) die(_("unable to resolve revision: %s"), arg); break; @@ -1200,6 +1201,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (!seen_dashdash) verify_non_filename(prefix, arg); add_object_array_with_path(object, arg, &list, oc.mode, oc.path); + free(oc.path); } /* diff --git a/builtin/log.c b/builtin/log.c index ec3258368c..57ce470f50 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -483,16 +483,20 @@ static int show_blob_object(const struct object_id *oid, struct rev_info *rev, c !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV)) return stream_blob_to_fd(1, oid, NULL, 0); - if (get_sha1_with_context(obj_name, 0, oidc.hash, &obj_context)) + if (get_sha1_with_context(obj_name, GET_SHA1_RECORD_PATH, + oidc.hash, &obj_context)) die(_("Not a valid object name %s"), obj_name); - if (!obj_context.path[0] || - !textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size)) + if (!obj_context.path || + !textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size)) { + free(obj_context.path); return stream_blob_to_fd(1, oid, NULL, 0); + } if (!buf) die(_("git show %s: bad file"), obj_name); write_or_die(1, buf, size); + free(obj_context.path); return 0; } diff --git a/builtin/name-rev.c b/builtin/name-rev.c index e7a3fe7ee7..1767af750d 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -13,6 +13,7 @@ typedef struct rev_name { unsigned long taggerdate; int generation; int distance; + int from_tag; } rev_name; static long cutoff = LONG_MAX; @@ -20,9 +21,47 @@ static long cutoff = LONG_MAX; /* How many generations are maximally preferred over _one_ merge traversal? */ #define MERGE_TRAVERSAL_WEIGHT 65535 +static int is_better_name(struct rev_name *name, + const char *tip_name, + unsigned long taggerdate, + int generation, + int distance, + int from_tag) +{ + /* + * When comparing names based on tags, prefer names + * based on the older tag, even if it is farther away. + */ + if (from_tag && name->from_tag) + return (name->taggerdate > taggerdate || + (name->taggerdate == taggerdate && + name->distance > distance)); + + /* + * We know that at least one of them is a non-tag at this point. + * favor a tag over a non-tag. + */ + if (name->from_tag != from_tag) + return from_tag; + + /* + * We are now looking at two non-tags. Tiebreak to favor + * shorter hops. + */ + if (name->distance != distance) + return name->distance > distance; + + /* ... or tiebreak to favor older date */ + if (name->taggerdate != taggerdate) + return name->taggerdate > taggerdate; + + /* keep the current one if we cannot decide */ + return 0; +} + static void name_rev(struct commit *commit, const char *tip_name, unsigned long taggerdate, - int generation, int distance, + int generation, int distance, int from_tag, int deref) { struct rev_name *name = (struct rev_name *)commit->util; @@ -46,14 +85,14 @@ static void name_rev(struct commit *commit, name = xmalloc(sizeof(rev_name)); commit->util = name; goto copy_data; - } else if (name->taggerdate > taggerdate || - (name->taggerdate == taggerdate && - name->distance > distance)) { + } else if (is_better_name(name, tip_name, taggerdate, + generation, distance, from_tag)) { copy_data: name->tip_name = tip_name; name->taggerdate = taggerdate; name->generation = generation; name->distance = distance; + name->from_tag = from_tag; } else { free(to_free); return; @@ -75,10 +114,12 @@ copy_data: parent_number); name_rev(parents->item, new_name, taggerdate, 0, - distance + MERGE_TRAVERSAL_WEIGHT, 0); + distance + MERGE_TRAVERSAL_WEIGHT, + from_tag, 0); } else { name_rev(parents->item, tip_name, taggerdate, - generation + 1, distance + 1, 0); + generation + 1, distance + 1, + from_tag, 0); } } } @@ -209,9 +250,13 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo } if (o && o->type == OBJ_COMMIT) { struct commit *commit = (struct commit *)o; + int from_tag = starts_with(path, "refs/tags/"); + if (taggerdate == ULONG_MAX) + taggerdate = ((struct commit *)o)->date; path = name_ref_abbrev(path, can_abbreviate_output); - name_rev(commit, xstrdup(path), taggerdate, 0, 0, deref); + name_rev(commit, xstrdup(path), taggerdate, 0, 0, + from_tag, deref); } return 0; } @@ -1333,13 +1333,18 @@ static inline int hex2chr(const char *s) struct object_context { unsigned char tree[20]; - char path[PATH_MAX]; unsigned mode; /* * symlink_path is only used by get_tree_entry_follow_symlinks, * and only for symlinks that point outside the repository. */ struct strbuf symlink_path; + /* + * If GET_SHA1_RECORD_PATH is set, this will record path (if any) + * found when resolving the name. The caller is responsible for + * releasing the memory. + */ + char *path; }; #define GET_SHA1_QUIETLY 01 @@ -1349,6 +1354,7 @@ struct object_context { #define GET_SHA1_TREEISH 020 #define GET_SHA1_BLOB 040 #define GET_SHA1_FOLLOW_SYMLINKS 0100 +#define GET_SHA1_RECORD_PATH 0200 #define GET_SHA1_ONLY_TO_DIE 04000 #define GET_SHA1_DISAMBIGUATORS \ @@ -1363,7 +1369,7 @@ extern int get_sha1_tree(const char *str, unsigned char *sha1); extern int get_sha1_treeish(const char *str, unsigned char *sha1); extern int get_sha1_blob(const char *str, unsigned char *sha1); extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix); -extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc); +extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *oc); extern int get_oid(const char *str, struct object_id *oid); diff --git a/git-stash.sh b/git-stash.sh index 2fb651b2b8..e7b85932d6 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -19,6 +19,7 @@ OPTIONS_SPEC= START_DIR=$(pwd) . git-sh-setup require_work_tree +prefix=$(git rev-parse --show-prefix) || exit 1 cd_to_toplevel TMP="$GIT_DIR/.git-stash.$$" @@ -273,6 +274,8 @@ push_stash () { shift done + eval "set $(git rev-parse --sq --prefix "$prefix" -- "$@")" + if test -n "$patch_mode" && test -n "$untracked" then die "$(gettext "Can't use --patch and --include-untracked or --all at the same time")" diff --git a/revision.c b/revision.c index 67ebc6fc4b..5dfb322ccd 100644 --- a/revision.c +++ b/revision.c @@ -1429,134 +1429,168 @@ static void prepare_show_merge(struct rev_info *revs) revs->limited = 1; } +static int dotdot_missing(const char *arg, char *dotdot, + struct rev_info *revs, int symmetric) +{ + if (revs->ignore_missing) + return 0; + /* de-munge so we report the full argument */ + *dotdot = '.'; + die(symmetric + ? "Invalid symmetric difference expression %s" + : "Invalid revision range %s", arg); +} + +static int handle_dotdot_1(const char *arg, char *dotdot, + struct rev_info *revs, int flags, + int cant_be_filename, + struct object_context *a_oc, + struct object_context *b_oc) +{ + const char *a_name, *b_name; + struct object_id a_oid, b_oid; + struct object *a_obj, *b_obj; + unsigned int a_flags, b_flags; + int symmetric = 0; + unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM); + unsigned int oc_flags = GET_SHA1_COMMITTISH | GET_SHA1_RECORD_PATH; + + a_name = arg; + if (!*a_name) + a_name = "HEAD"; + + b_name = dotdot + 2; + if (*b_name == '.') { + symmetric = 1; + b_name++; + } + if (!*b_name) + b_name = "HEAD"; + + if (get_sha1_with_context(a_name, oc_flags, a_oid.hash, a_oc) || + get_sha1_with_context(b_name, oc_flags, b_oid.hash, b_oc)) + return -1; + + if (!cant_be_filename) { + *dotdot = '.'; + verify_non_filename(revs->prefix, arg); + *dotdot = '\0'; + } + + a_obj = parse_object(a_oid.hash); + b_obj = parse_object(b_oid.hash); + if (!a_obj || !b_obj) + return dotdot_missing(arg, dotdot, revs, symmetric); + + if (!symmetric) { + /* just A..B */ + b_flags = flags; + a_flags = flags_exclude; + } else { + /* A...B -- find merge bases between the two */ + struct commit *a, *b; + struct commit_list *exclude; + + a = lookup_commit_reference(a_obj->oid.hash); + b = lookup_commit_reference(b_obj->oid.hash); + if (!a || !b) + return dotdot_missing(arg, dotdot, revs, symmetric); + + exclude = get_merge_bases(a, b); + add_rev_cmdline_list(revs, exclude, REV_CMD_MERGE_BASE, + flags_exclude); + add_pending_commit_list(revs, exclude, flags_exclude); + free_commit_list(exclude); + + b_flags = flags; + a_flags = flags | SYMMETRIC_LEFT; + } + + a_obj->flags |= a_flags; + b_obj->flags |= b_flags; + add_rev_cmdline(revs, a_obj, a_name, REV_CMD_LEFT, a_flags); + add_rev_cmdline(revs, b_obj, b_name, REV_CMD_RIGHT, b_flags); + add_pending_object_with_path(revs, a_obj, a_name, a_oc->mode, a_oc->path); + add_pending_object_with_path(revs, b_obj, b_name, b_oc->mode, b_oc->path); + return 0; +} + +static int handle_dotdot(const char *arg, + struct rev_info *revs, int flags, + int cant_be_filename) +{ + struct object_context a_oc, b_oc; + char *dotdot = strstr(arg, ".."); + int ret; + + if (!dotdot) + return -1; + + memset(&a_oc, 0, sizeof(a_oc)); + memset(&b_oc, 0, sizeof(b_oc)); + + *dotdot = '\0'; + ret = handle_dotdot_1(arg, dotdot, revs, flags, cant_be_filename, + &a_oc, &b_oc); + *dotdot = '.'; + + free(a_oc.path); + free(b_oc.path); + + return ret; +} + int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt) { struct object_context oc; - char *dotdot; + char *mark; 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; + unsigned get_sha1_flags = GET_SHA1_RECORD_PATH; flags = flags & UNINTERESTING ? flags | BOTTOM : flags & ~BOTTOM; - dotdot = strstr(arg, ".."); - if (dotdot) { - unsigned char from_sha1[20]; - const char *next = dotdot + 2; - const char *this = arg; - int symmetric = *next == '.'; - unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM); - static const char head_by_default[] = "HEAD"; - unsigned int a_flags; - - *dotdot = 0; - next += symmetric; - - if (!*next) - next = head_by_default; - if (dotdot == arg) - 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 object *a_obj, *b_obj; - - if (!cant_be_filename) { - *dotdot = '.'; - verify_non_filename(revs->prefix, arg); - } - - a_obj = parse_object(from_sha1); - b_obj = parse_object(sha1); - if (!a_obj || !b_obj) { - missing: - if (revs->ignore_missing) - return 0; - die(symmetric - ? "Invalid symmetric difference expression %s" - : "Invalid revision range %s", arg); - } - - if (!symmetric) { - /* just A..B */ - a_flags = flags_exclude; - } else { - /* A...B -- find merge bases between the two */ - struct commit *a, *b; - struct commit_list *exclude; - - a = (a_obj->type == OBJ_COMMIT - ? (struct commit *)a_obj - : lookup_commit_reference(a_obj->oid.hash)); - b = (b_obj->type == OBJ_COMMIT - ? (struct commit *)b_obj - : lookup_commit_reference(b_obj->oid.hash)); - if (!a || !b) - goto missing; - exclude = get_merge_bases(a, b); - add_rev_cmdline_list(revs, exclude, - REV_CMD_MERGE_BASE, - flags_exclude); - add_pending_commit_list(revs, exclude, - flags_exclude); - free_commit_list(exclude); - - a_flags = flags | SYMMETRIC_LEFT; - } - - a_obj->flags |= a_flags; - b_obj->flags |= flags; - add_rev_cmdline(revs, a_obj, this, - REV_CMD_LEFT, a_flags); - add_rev_cmdline(revs, b_obj, next, - REV_CMD_RIGHT, flags); - add_pending_object(revs, a_obj, this); - add_pending_object(revs, b_obj, next); - return 0; - } - *dotdot = '.'; + if (!cant_be_filename && !strcmp(arg, "..")) { + /* + * Just ".."? That is not a range but the + * pathspec for the parent directory. + */ + return -1; } - dotdot = strstr(arg, "^@"); - if (dotdot && !dotdot[2]) { - *dotdot = 0; + if (!handle_dotdot(arg, revs, flags, revarg_opt)) + return 0; + + mark = strstr(arg, "^@"); + if (mark && !mark[2]) { + *mark = 0; if (add_parents_only(revs, arg, flags, 0)) return 0; - *dotdot = '^'; + *mark = '^'; } - dotdot = strstr(arg, "^!"); - if (dotdot && !dotdot[2]) { - *dotdot = 0; + mark = strstr(arg, "^!"); + if (mark && !mark[2]) { + *mark = 0; if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), 0)) - *dotdot = '^'; + *mark = '^'; } - dotdot = strstr(arg, "^-"); - if (dotdot) { + mark = strstr(arg, "^-"); + if (mark) { int exclude_parent = 1; - if (dotdot[2]) { + if (mark[2]) { char *end; - exclude_parent = strtoul(dotdot + 2, &end, 10); + exclude_parent = strtoul(mark + 2, &end, 10); if (*end != '\0' || !exclude_parent) return -1; } - *dotdot = 0; + *mark = 0; if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), exclude_parent)) - *dotdot = '^'; + *mark = '^'; } local_flags = 0; @@ -1566,7 +1600,7 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi } if (revarg_opt & REVARG_COMMITTISH) - get_sha1_flags = GET_SHA1_COMMITTISH; + get_sha1_flags |= GET_SHA1_COMMITTISH; if (get_sha1_with_context(arg, get_sha1_flags, sha1, &oc)) return revs->ignore_missing ? 0 : -1; @@ -1574,7 +1608,8 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi 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, oc.mode); + add_pending_object_with_path(revs, object, arg, oc.mode, oc.path); + free(oc.path); return 0; } @@ -1690,8 +1725,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->max_count = atoi(argv[1]); revs->no_walk = 0; return 2; - } else if (starts_with(arg, "-n")) { - revs->max_count = atoi(arg + 2); + } else if (skip_prefix(arg, "-n", &optarg)) { + revs->max_count = atoi(optarg); revs->no_walk = 0; } else if ((argcount = parse_long_opt("max-age", argv, &optarg))) { revs->max_age = atoi(optarg); @@ -1750,16 +1785,13 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } else if (!strcmp(arg, "--author-date-order")) { revs->sort_order = REV_SORT_BY_AUTHOR_DATE; revs->topo_order = 1; - } else if (starts_with(arg, "--early-output")) { - int count = 100; - switch (arg[14]) { - case '=': - count = atoi(arg+15); - /* Fallthrough */ - case 0: - revs->topo_order = 1; - revs->early_output = count; - } + } else if (!strcmp(arg, "--early-output")) { + revs->early_output = 100; + revs->topo_order = 1; + } else if (skip_prefix(arg, "--early-output=", &optarg)) { + if (strtoul_ui(optarg, 10, &revs->early_output) < 0) + die("'%s': not a non-negative integer", optarg); + revs->topo_order = 1; } else if (!strcmp(arg, "--parents")) { revs->rewrite_parents = 1; revs->print_parents = 1; @@ -1775,13 +1807,13 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->min_parents = 2; } else if (!strcmp(arg, "--no-merges")) { revs->max_parents = 1; - } else if (starts_with(arg, "--min-parents=")) { - revs->min_parents = atoi(arg+14); - } else if (starts_with(arg, "--no-min-parents")) { + } else if (skip_prefix(arg, "--min-parents=", &optarg)) { + revs->min_parents = atoi(optarg); + } else if (!strcmp(arg, "--no-min-parents")) { revs->min_parents = 0; - } else if (starts_with(arg, "--max-parents=")) { - revs->max_parents = atoi(arg+14); - } else if (starts_with(arg, "--no-max-parents")) { + } else if (skip_prefix(arg, "--max-parents=", &optarg)) { + revs->max_parents = atoi(optarg); + } else if (!strcmp(arg, "--no-max-parents")) { revs->max_parents = -1; } else if (!strcmp(arg, "--boundary")) { revs->boundary = 1; @@ -1862,14 +1894,15 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->verbose_header = 1; revs->pretty_given = 1; get_commit_format(NULL, revs); - } else if (starts_with(arg, "--pretty=") || starts_with(arg, "--format=")) { + } else if (skip_prefix(arg, "--pretty=", &optarg) || + skip_prefix(arg, "--format=", &optarg)) { /* * Detached form ("--pretty X" as opposed to "--pretty=X") * not allowed, since the argument is optional. */ revs->verbose_header = 1; revs->pretty_given = 1; - get_commit_format(arg+9, revs); + get_commit_format(optarg, revs); } else if (!strcmp(arg, "--expand-tabs")) { revs->expand_tabs_in_log = 8; } else if (!strcmp(arg, "--no-expand-tabs")) { @@ -1887,26 +1920,23 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->show_signature = 1; } else if (!strcmp(arg, "--no-show-signature")) { revs->show_signature = 0; - } else if (!strcmp(arg, "--show-linear-break") || - starts_with(arg, "--show-linear-break=")) { - if (starts_with(arg, "--show-linear-break=")) - revs->break_bar = xstrdup(arg + 20); - else - revs->break_bar = " .........."; + } else if (!strcmp(arg, "--show-linear-break")) { + revs->break_bar = " .........."; + revs->track_linear = 1; + revs->track_first_time = 1; + } else if (skip_prefix(arg, "--show-linear-break=", &optarg)) { + revs->break_bar = xstrdup(optarg); revs->track_linear = 1; revs->track_first_time = 1; - } else if (starts_with(arg, "--show-notes=") || - starts_with(arg, "--notes=")) { + } else if (skip_prefix(arg, "--show-notes=", &optarg) || + skip_prefix(arg, "--notes=", &optarg)) { struct strbuf buf = STRBUF_INIT; revs->show_notes = 1; revs->show_notes_given = 1; - if (starts_with(arg, "--show-notes")) { - if (revs->notes_opt.use_default_notes < 0) - revs->notes_opt.use_default_notes = 1; - strbuf_addstr(&buf, arg+13); - } - else - strbuf_addstr(&buf, arg+8); + if (starts_with(arg, "--show-notes=") && + revs->notes_opt.use_default_notes < 0) + revs->notes_opt.use_default_notes = 1; + strbuf_addstr(&buf, optarg); expand_notes_ref(&buf); string_list_append(&revs->notes_opt.extra_notes_refs, strbuf_detach(&buf, NULL)); @@ -1943,8 +1973,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg |