diff options
292 files changed, 5134 insertions, 2347 deletions
diff --git a/.gitignore b/.gitignore index b2a1ae4a1d..388cc4beee 100644 --- a/.gitignore +++ b/.gitignore @@ -182,7 +182,7 @@ /gitweb/gitweb.cgi /gitweb/static/gitweb.js /gitweb/static/gitweb.min.* -/common-cmds.h +/command-list.h *.tar.gz *.dsc *.deb diff --git a/.travis.yml b/.travis.yml index 5f5ee4f3bd..4d4e26c9df 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,10 +16,13 @@ compiler: addons: apt: + sources: + - ubuntu-toolchain-r-test packages: - language-pack-is - git-svn - apache2 + - gcc-8 matrix: include: diff --git a/Documentation/RelNotes/2.13.7.txt b/Documentation/RelNotes/2.13.7.txt new file mode 100644 index 0000000000..09fc01406c --- /dev/null +++ b/Documentation/RelNotes/2.13.7.txt @@ -0,0 +1,20 @@ +Git v2.13.7 Release Notes +========================= + +Fixes since v2.13.6 +------------------- + + * Submodule "names" come from the untrusted .gitmodules file, but we + blindly append them to $GIT_DIR/modules to create our on-disk repo + paths. This means you can do bad things by putting "../" into the + name. We now enforce some rules for submodule names which will cause + Git to ignore these malicious names (CVE-2018-11235). + + Credit for finding this vulnerability and the proof of concept from + which the test script was adapted goes to Etienne Stalmans. + + * It was possible to trick the code that sanity-checks paths on NTFS + into reading random piece of memory (CVE-2018-11233). + +Credit for fixing for these bugs goes to Jeff King, Johannes +Schindelin and others. diff --git a/Documentation/RelNotes/2.14.4.txt b/Documentation/RelNotes/2.14.4.txt new file mode 100644 index 0000000000..97755a89d9 --- /dev/null +++ b/Documentation/RelNotes/2.14.4.txt @@ -0,0 +1,5 @@ +Git v2.14.4 Release Notes +========================= + +This release is to forward-port the fixes made in the v2.13.7 version +of Git. See its release notes for details. diff --git a/Documentation/RelNotes/2.15.2.txt b/Documentation/RelNotes/2.15.2.txt index 9f7e28f8a2..b480e56b68 100644 --- a/Documentation/RelNotes/2.15.2.txt +++ b/Documentation/RelNotes/2.15.2.txt @@ -43,5 +43,8 @@ Fixes since v2.15.1 * Clarify and enhance documentation for "merge-base --fork-point", as it was clear what it computed but not why/what for. + * This release also contains the fixes made in the v2.13.7 version of + Git. See its release notes for details. + Also contains various documentation updates and code clean-ups. diff --git a/Documentation/RelNotes/2.16.4.txt b/Documentation/RelNotes/2.16.4.txt new file mode 100644 index 0000000000..6be538ba30 --- /dev/null +++ b/Documentation/RelNotes/2.16.4.txt @@ -0,0 +1,5 @@ +Git v2.16.4 Release Notes +========================= + +This release is to forward-port the fixes made in the v2.13.7 version +of Git. See its release notes for details. diff --git a/Documentation/RelNotes/2.17.1.txt b/Documentation/RelNotes/2.17.1.txt new file mode 100644 index 0000000000..e01384fe8e --- /dev/null +++ b/Documentation/RelNotes/2.17.1.txt @@ -0,0 +1,16 @@ +Git v2.17.1 Release Notes +========================= + +Fixes since v2.17 +----------------- + + * This release contains the same fixes made in the v2.13.7 version of + Git, covering CVE-2018-11233 and 11235, and forward-ported to + v2.14.4, v2.15.2 and v2.16.4 releases. See release notes to + v2.13.7 for details. + + * In addition to the above fixes, this release has support on the + server side to reject pushes to repositories that attempt to create + such problematic .gitmodules file etc. as tracked contents, to help + hosting sites protect their customers by preventing malicious + contents from spreading. diff --git a/Documentation/RelNotes/2.18.0.txt b/Documentation/RelNotes/2.18.0.txt index 40c3b9470b..fd5aecf8e9 100644 --- a/Documentation/RelNotes/2.18.0.txt +++ b/Documentation/RelNotes/2.18.0.txt @@ -12,7 +12,9 @@ UI, Workflows & Features want to move to z/d by taking the hint that the entire directory 'x' moved to 'z'. A bug causing dirty files involved in a rename to be overwritten during merge has also been fixed as part of this - work. + work. Incidentally, this also avoids updating a file in the + working tree after a (non-trivial) merge whose result matches what + our side originally had. * "git filter-branch" learned to use a different exit code to allow the callers to tell the case where there was no new commits to @@ -104,6 +106,44 @@ UI, Workflows & Features custom "git-$command" that the end user has on the $PATH when using newer version of bash. + * "git send-email" can sometimes offer confirmation dialog "Send this + email?" with choices 'Yes', 'No', 'Quit', and 'All'. A new action + 'Edit' has been added to this dialog's choice. + + * With merge.renames configuration set to false, the recursive merge + strategy can be told not to spend cycles trying to find renamed + paths and merge them accordingly. + + * "git status" learned to honor a new status.renames configuration to + skip rename detection, which could be useful for those who want to + do so without disabling the default rename detection done by the + "git diff" command. + + * Command line completion (in contrib/) learned to complete pathnames + for various commands better. + + * "git blame" learns to unhighlight uninteresting metadata from the + originating commit on lines that are the same as the previous one, + and also paint lines in different colors depending on the age of + the commit. + + * Transfer protocol v2 learned to support the partial clone. + + * When a short hexadecimal string is used to name an object but there + are multiple objects that share the string as the prefix of their + names, the code lists these ambiguous candidates in a help message. + These object names are now sorted according to their types for + easier eyeballing. + + * "git fetch $there $refspec" that talks over protocol v2 can take + advantage of server-side ref filtering; the code has been extended + so that this mechanism triggers also when fetching with configured + refspec. + + * Our HTTP client code used to advertise that we accept gzip encoding + from the other side; instead, just let cURL library to advertise + and negotiate the best one. + Performance, Internal Implementation, Development Support etc. @@ -218,20 +258,38 @@ Performance, Internal Implementation, Development Support etc. repository object (which in turn tells the API which object store the objects are to be located). - * Rename detection logic in "diff" family that is used in "merge" has - learned to guess when all of x/a, x/b and x/c have moved to z/a, - z/b and z/c, it is likely that x/d added in the meantime would also - want to move to z/d by taking the hint that the entire directory - 'x' moved to 'z'. A bug causing dirty files involved in a rename - to be overwritten during merge has also been fixed as part of this - work. Incidentally, this also avoids updating a file in the - working tree after a (non-trivial) merge whose result matches what - our side originally had. - * "git pack-objects" needs to allocate tons of "struct object_entry" while doing its work, and shrinking its size helps the performance quite a bit. + * The implementation of "git rebase -i --root" has been updated to use + the sequencer machinery more. + + * Developer support update, by using BUG() macro instead of die() to + mark codepaths that should not happen more clearly. + + * Developer support. Use newer GCC on one of the builds done at + TravisCI.org to get more warnings and errors diagnosed. + + * Conversion from uchar[20] to struct object_id continues. + + * By code restructuring of submodule merge in merge-recursive, + informational messages from the codepath are now given using the + same mechanism as other output, and honor the merge.verbosity + configuration. The code also learned to give a few new messages + when a submodule three-way merge resolves cleanly when one side + records a descendant of the commit chosen by the other side. + + * Avoid unchecked snprintf() to make future code auditing easier. + (merge ac4896f007 jk/snprintf-truncation later to maint). + + * Many tests hardcode the raw object names, which would change once + we migrate away from SHA-1. While some of them must test against + exact object names, most of them do not have to use hardcoded + constants in the test. The latter kind of tests have been updated + to test the moral equivalent of the original without hardcoding the + actual object names. + Also contains various documentation updates and code clean-ups. @@ -382,6 +440,54 @@ Fixes since v2.17 HT by default. The problem is fixed by forcing 8-space tabs. (merge 379805051d bc/asciidoctor-tab-width later to maint). + * Code clean-up to adjust to a more recent lockfile API convention that + allows lockfile instances kept on the stack. + (merge 0fa5a2ed8d ma/lockfile-cleanup later to maint). + + * the_repository->index is not a allocated piece of memory but + repo_clear() indiscriminately attempted to free(3) it, which has + been corrected. + (merge 74373b5f10 nd/repo-clear-keep-the-index later to maint). + + * Code clean-up to avoid non-standard-conformant pointer arithmetic. + (merge c112084af9 rs/no-null-ptr-arith-in-fast-export later to maint). + + * Code clean-up to turn history traversal more robust in a + semi-corrupt repository. + (merge 8702b30fd7 jk/unavailable-can-be-missing later to maint). + + * "git update-ref A B" is supposed to ensure that ref A does not yet + exist when B is a NULL OID, but this check was not done correctly + for pseudo-refs outside refs/ hierarchy, e.g. MERGE_HEAD. + + * "git submodule update" and "git submodule add" supported the + "--reference" option to borrow objects from a neighbouring local + repository like "git clone" does, but lacked the more recent + invention "--dissociate". Also "git submodule add" has been taught + to take the "--progress" option. + (merge a0ef29341a cf/submodule-progress-dissociate later to maint). + + * Update credential-netrc helper (in contrib/) to allow customizing + the GPG used to decrypt the encrypted .netrc file. + (merge 786ef50a23 lm/credential-netrc later to maint). + + * "git submodule update" attempts two different kinds of "git fetch" + against the upstream repository to grab a commit bound at the + submodule's path, but it incorrectly gave up if the first kind + (i.e. a normal fetch) failed, making the second "last resort" one + (i.e. fetching an exact commit object by object name) ineffective. + This has been corrected. + (merge e30d833671 sb/submodule-update-try-harder later to maint). + + * Error behaviour of "git grep" when it cannot read the index was + inconsistent with other commands that uses the index, which has + been corrected to error out early. + (merge b2aa84c789 sb/grep-die-on-unreadable-index later to maint). + + * We used to call regfree() after regcomp() failed in some codepaths, + which have been corrected. + (merge 17154b1576 ma/regex-no-regfree-after-comp-fail later to maint). + * Other minor doc, test and build updates and code cleanups. (merge 248f66ed8e nd/trace-with-env later to maint). (merge 14ced5562c ys/bisect-object-id-missing-conversion-fix later to maint). @@ -411,3 +517,5 @@ Fixes since v2.17 (merge 5356a3c354 ah/misc-doc-updates later to maint). (merge 92c4a7a129 nd/completion-aliasfiletype-typofix later to maint). (merge 58bd77b66a nd/pack-unreachable-objects-doc later to maint). + (merge 4ed79d5203 sg/t6500-no-redirect-of-stdin later to maint). + (merge 17b8a2d6cd jk/config-blob-sans-repo later to maint). diff --git a/Documentation/config.txt b/Documentation/config.txt index 84e2891aed..ab641bf5a9 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1251,6 +1251,33 @@ color.status.<slot>:: status short-format), or `unmerged` (files which have unmerged changes). +color.blame.repeatedLines:: + Use the customized color for the part of git-blame output that + is repeated meta information per line (such as commit id, + author name, date and timezone). Defaults to cyan. + +color.blame.highlightRecent:: + This can be used to color the metadata of a blame line depending + on age of the line. ++ +This setting should be set to a comma-separated list of color and date settings, +starting and ending with a color, the dates should be set from oldest to newest. +The metadata will be colored given the colors if the the line was introduced +before the given timestamp, overwriting older timestamped colors. ++ +Instead of an absolute timestamp relative timestamps work as well, e.g. +2.weeks.ago is valid to address anything older than 2 weeks. ++ +It defaults to 'blue,12 month ago,white,1 month ago,red', which colors +everything older than one year blue, recent changes between one month and +one year old are kept white, and lines introduced within the last month are +colored red. + +blame.coloring:: + This determines the coloring scheme to be applied to blame + output. It can be 'repeatedLines', 'highlightRecent', + or 'none' which is the default. + color.transport:: A boolean to enable/disable color when pushes are rejected. May be set to `always`, `false` (or `never`) or `auto` (or `true`), in which @@ -1385,6 +1412,14 @@ credential.<url>.*:: credentialCache.ignoreSIGHUP:: Tell git-credential-cache--daemon to ignore SIGHUP, instead of quitting. +completion.commands:: + This is only used by git-completion.bash to add or remove + commands from the list of completed commands. Normally only + porcelain commands and a few select others are completed. You + can add more commands, separated by space, in this + variable. Prefixing the command with '-' will remove it from + the existing list. + include::diff-config.txt[] difftool.<tool>.path:: @@ -3179,6 +3214,18 @@ status.displayCommentPrefix:: behavior of linkgit:git-status[1] in Git 1.8.4 and previous. Defaults to false. +status.renameLimit:: + The number of files to consider when performing rename detection + in linkgit:git-status[1] and linkgit:git-commit[1]. Defaults to + the value of diff.renameLimit. + +status.renames:: + Whether and how Git detects renames in linkgit:git-status[1] and + linkgit:git-commit[1] . If set to "false", rename detection is + disabled. If set to "true", basic rename detection is enabled. + If set to "copies" or "copy", Git will detect copies, as well. + Defaults to the value of diff.renames. + status.showStash:: If set to true, linkgit:git-status[1] will display the number of entries currently stashed away. diff --git a/Documentation/diff-config.txt b/Documentation/diff-config.txt index 5ca942ab5e..77caa66c2f 100644 --- a/Documentation/diff-config.txt +++ b/Documentation/diff-config.txt @@ -112,7 +112,8 @@ diff.orderFile:: diff.renameLimit:: The number of files to consider when performing the copy/rename - detection; equivalent to the 'git diff' option `-l`. + detection; equivalent to the 'git diff' option `-l`. This setting + has no effect if rename detection is turned off. diff.renames:: Whether and how Git detects renames. If set to "false", diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt index 40d328a4b3..a40fc38d8b 100644 --- a/Documentation/git-help.txt +++ b/Documentation/git-help.txt @@ -8,7 +8,7 @@ git-help - Display help information about Git SYNOPSIS -------- [verse] -'git help' [-a|--all] [-g|--guide] +'git help' [-a|--all [--verbose]] [-g|--guide] [-i|--info|-m|--man|-w|--web] [COMMAND|GUIDE] DESCRIPTION @@ -42,6 +42,8 @@ OPTIONS --all:: Prints all the available commands on the standard output. This option overrides any given command or guide name. + When used with `--verbose` print description for all recognized + commands. -g:: --guides:: diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt index c16e27e63d..c4467ffb98 100644 --- a/Documentation/git-status.txt +++ b/Documentation/git-status.txt @@ -135,6 +135,16 @@ ignored, then the directory is not shown, but all contents are shown. Display or do not display detailed ahead/behind counts for the branch relative to its upstream branch. Defaults to true. +--renames:: +--no-renames:: + Turn on/off rename detection regardless of user configuration. + See also linkgit:git-diff[1] `--no-renames`. + +--find-renames[=<n>]:: + Turn on rename detection, optionally setting the similarity + threshold. + See also linkgit:git-diff[1] `--find-renames`. + <pathspec>...:: See the 'pathspec' entry in linkgit:gitglossary[7]. diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 630999f41a..4a5cc38a6f 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -239,6 +239,13 @@ OPTIONS --quiet:: Only print error messages. +--progress:: + This option is only valid for add and update commands. + Progress status is reported on the standard error stream + by default when it is attached to a terminal, unless -q + is specified. This flag forces progress status even if the + standard error stream is not directed to a terminal. + --all:: This option is only valid for the deinit command. Unregister all submodules in the working tree. @@ -362,7 +369,15 @@ the submodule itself. this option will be passed to the linkgit:git-clone[1] command. + *NOTE*: Do *not* use this option unless you have read the note -for linkgit:git-clone[1]'s `--reference` and `--shared` options carefully. +for linkgit:git-clone[1]'s `--reference`, `--shared`, and `--dissociate` +options carefully. + +--dissociate:: + This option is only valid for add and update commands. These + commands sometimes need to clone a remote repository. In this case, + this option will be passed to the linkgit:git-clone[1] command. ++ +*NOTE*: see the NOTE for the `--reference` option. --recursive:: This option is only valid for foreach, update, status and sync commands. diff --git a/Documentation/git.txt b/Documentation/git.txt index c662f41c1d..dba7f0c18e 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -164,6 +164,16 @@ foo.bar= ...`) sets `foo.bar` to the empty string which `git config Do not perform optional operations that require locks. This is equivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`. +--list-cmds=group[,group...]:: + List commands by group. This is an internal/experimental + option and may change or be removed in the future. Supported + groups are: builtins, parseopt (builtin commands that use + parse-options), main (all commands in libexec directory), + others (all other commands in `$PATH` that have git- prefix), + list-<category> (see categories in command-list.txt), + nohelpers (exclude helper commands), alias and config + (retrieve command list from config variable completion.commands) + GIT COMMANDS ------------ diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index b72936a885..92010b062e 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -3,7 +3,7 @@ gitattributes(5) NAME ---- -gitattributes - defining attributes per path +gitattributes - Defining attributes per path SYNOPSIS -------- diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt index db5d47eb19..4d63def206 100644 --- a/Documentation/gitmodules.txt +++ b/Documentation/gitmodules.txt @@ -3,7 +3,7 @@ gitmodules(5) NAME ---- -gitmodules - defining submodule properties +gitmodules - Defining submodule properties SYNOPSIS -------- diff --git a/Documentation/gitrevisions.txt b/Documentation/gitrevisions.txt index 27dec5b91d..1f6cceaefb 100644 --- a/Documentation/gitrevisions.txt +++ b/Documentation/gitrevisions.txt @@ -3,7 +3,7 @@ gitrevisions(7) NAME ---- -gitrevisions - specifying revisions and ranges for Git +gitrevisions - Specifying revisions and ranges for Git SYNOPSIS -------- diff --git a/Documentation/merge-config.txt b/Documentation/merge-config.txt index 12b6bbf591..662c2713ca 100644 --- a/Documentation/merge-config.txt +++ b/Documentation/merge-config.txt @@ -35,7 +35,13 @@ include::fmt-merge-msg-config.txt[] merge.renameLimit:: The number of files to consider when performing rename detection during a merge; if not specified, defaults to the value of - diff.renameLimit. + diff.renameLimit. This setting has no effect if rename detection + is turned off. + +merge.renames:: + Whether and how Git detects renames. If set to "false", + rename detection is disabled. If set to "true", basic rename + detection is enabled. Defaults to the value of diff.renames. merge.renormalize:: Tell Git that canonical representation of files in the diff --git a/Documentation/merge-strategies.txt b/Documentation/merge-strategies.txt index 4a58aad4b8..aa66cbe41e 100644 --- a/Documentation/merge-strategies.txt +++ b/Documentation/merge-strategies.txt @@ -23,8 +23,9 @@ recursive:: causing mismerges by tests done on actual merge commits taken from Linux 2.6 kernel development history. Additionally this can detect and handle merges involving - renames. This is the default merge strategy when - pulling or merging one branch. + renames, but currently cannot make use of detected + copies. This is the default merge strategy when pulling + or merging one branch. + The 'recursive' strategy can take the following options: @@ -84,12 +85,14 @@ no-renormalize;; `merge.renormalize` configuration variable. no-renames;; - Turn off rename detection. + Turn off rename detection. This overrides the `merge.renames` + configuration variable. See also linkgit:git-diff[1] `--no-renames`. find-renames[=<n>];; Turn on rename detection, optionally setting the similarity - threshold. This is the default. + threshold. This is the default. This overrides the + 'merge.renames' configuration variable. See also linkgit:git-diff[1] `--find-renames`. rename-threshold=<n>;; diff --git a/Documentation/technical/api-oid-array.txt b/Documentation/technical/api-oid-array.txt index b0c11f868d..9febfb1d52 100644 --- a/Documentation/technical/api-oid-array.txt +++ b/Documentation/technical/api-oid-array.txt @@ -35,13 +35,18 @@ Functions Free all memory associated with the array and return it to the initial, empty state. +`oid_array_for_each`:: + Iterate over each element of the list, executing the callback + function for each one. Does not sort the list, so any custom + hash order is retained. If the callback returns a non-zero + value, the iteration ends immediately and the callback's + return is propagated; otherwise, 0 is returned. + `oid_array_for_each_unique`:: - Efficiently iterate over each unique element of the list, - executing the callback function for each one. If the array is - not sorted, this function has the side effect of sorting it. If - the callback returns a non-zero value, the iteration ends - immediately and the callback's return is propagated; otherwise, - 0 is returned. + Iterate over each unique element of the list in sorted order, + but otherwise behave like `oid_array_for_each`. If the array + is not sorted, this function has the side effect of sorting + it. Examples -------- diff --git a/Documentation/technical/protocol-v2.txt b/Documentation/technical/protocol-v2.txt index d7b6f38e0a..49bda76d23 100644 --- a/Documentation/technical/protocol-v2.txt +++ b/Documentation/technical/protocol-v2.txt @@ -290,6 +290,15 @@ included in the clients request as well as the potential addition of the Cannot be used with "deepen", but can be used with "deepen-since". +If the 'filter' feature is advertised, the following argument can be +included in the client's request: + + filter <filter-spec> + Request that various objects from the packfile be omitted + using one of several filtering techniques. These are intended + for use with partial clone and partial fetch operations. See + `rev-list` for possible "filter-spec" values. + The response of `fetch` is broken into a number of sections separated by delimiter packets (0001), with each section beginning with its section header. diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 12ff59c2c7..bdd758e409 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.17.GIT +DEF_VER=v2.18.0-rc0 LF=' ' @@ -795,7 +795,7 @@ LIB_FILE = libgit.a XDIFF_LIB = xdiff/lib.a VCSSVN_LIB = vcs-svn/lib.a -GENERATED_H += common-cmds.h +GENERATED_H += command-list.h LIB_H = $(shell $(FIND) . \ -name .git -prune -o \ @@ -928,6 +928,7 @@ LIB_OBJS += refs/files-backend.o LIB_OBJS += refs/iterator.o LIB_OBJS += refs/packed-backend.o LIB_OBJS += refs/ref-cache.o +LIB_OBJS += refspec.o LIB_OBJS += ref-filter.o LIB_OBJS += remote.o LIB_OBJS += replace-object.o @@ -2005,9 +2006,9 @@ git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \ $(filter %.o,$^) $(LIBS) -help.sp help.s help.o: common-cmds.h +help.sp help.s help.o: command-list.h -builtin/help.sp builtin/help.s builtin/help.o: common-cmds.h GIT-PREFIX +builtin/help.sp builtin/help.s builtin/help.o: command-list.h GIT-PREFIX builtin/help.sp builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \ '-DGIT_HTML_PATH="$(htmldir_relative_SQ)"' \ '-DGIT_MAN_PATH="$(mandir_relative_SQ)"' \ @@ -2026,9 +2027,9 @@ $(BUILT_INS): git$X ln -s $< $@ 2>/dev/null || \ cp $< $@ -common-cmds.h: generate-cmdlist.sh command-list.txt +command-list.h: generate-cmdlist.sh command-list.txt -common-cmds.h: $(wildcard Documentation/git-*.txt) +command-list.h: $(wildcard Documentation/git*.txt) $(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh command-list.txt >$@+ && mv $@+ $@ SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\ @@ -2272,7 +2273,7 @@ else # Dependencies on header files, for platforms that do not support # the gcc -MMD option. # -# Dependencies on automatically generated headers such as common-cmds.h +# Dependencies on automatically generated headers such as command-list.h # should _not_ be included here, since they are necessary even when # building an object for the first time. @@ -2652,7 +2653,7 @@ sparse: $(SP_OBJ) style: git clang-format --style file --diff --extensions c,h -check: common-cmds.h +check: command-list.h @if sparse; \ then \ echo >&2 "Use 'make sparse' instead"; \ @@ -2900,7 +2901,7 @@ clean: profile-clean coverage-clean $(RM) $(TEST_PROGRAMS) $(NO_INSTALL) $(RM) -r bin-wrappers $(dep_dirs) $(RM) -r po/build/ - $(RM) *.pyc *.pyo */*.pyc */*.pyo common-cmds.h $(ETAGS_TARGET) tags cscope* + $(RM) *.pyc *.pyo */*.pyc */*.pyo command-list.h $(ETAGS_TARGET) tags cscope* $(RM) -r $(GIT_TARNAME) .doc-tmp-dir $(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz $(RM) $(htmldocs).tar.gz $(manpages).tar.gz @@ -1,9 +1,12 @@ #include "cache.h" +#include "alias.h" #include "config.h" +#include "string-list.h" struct config_alias_data { const char *alias; char *v; + struct string_list *list; }; static int config_alias_cb(const char *key, const char *value, void *d) @@ -11,8 +14,16 @@ static int config_alias_cb(const char *key, const char *value, void *d) struct config_alias_data *data = d; const char *p; - if (skip_prefix(key, "alias.", &p) && !strcasecmp(p, data->alias)) - return git_config_string((const char **)&data->v, key, value); + if (!skip_prefix(key, "alias.", &p)) + return 0; + + if (data->alias) { + if (!strcasecmp(p, data->alias)) + return git_config_string((const char **)&data->v, + key, value); + } else if (data->list) { + string_list_append(data->list, p); + } return 0; } @@ -26,6 +37,13 @@ char *alias_lookup(const char *alias) return data.v; } +void list_aliases(struct string_list *list) +{ + struct config_alias_data data = { NULL, NULL, list }; + + read_early_config(config_alias_cb, &data); +} + #define SPLIT_CMDLINE_BAD_ENDING 1 #define SPLIT_CMDLINE_UNCLOSED_QUOTE 2 static const char *split_cmdline_errors[] = { diff --git a/alias.h b/alias.h new file mode 100644 index 0000000000..79933f2457 --- /dev/null +++ b/alias.h @@ -0,0 +1,12 @@ +#ifndef __ALIAS_H__ +#define __ALIAS_H__ + +struct string_list; + +char *alias_lookup(const char *alias); +int split_cmdline(char *cmdline, const char ***argv); +/* Takes a negative value returned by split_cmdline */ +const char *split_cmdline_strerror(int cmdline_errno); +void list_aliases(struct string_list *list); + +#endif @@ -2375,7 +2375,7 @@ static void update_pre_post_images(struct image *preimage, if (postlen ? postlen < new_buf - postimage->buf : postimage->len < new_buf - postimage->buf) - die("BUG: caller miscounted postlen: asked %d, orig = %d, used = %d", + BUG("caller miscounted postlen: asked %d, orig = %d, used = %d", (int)postlen, (int) postimage->len, (int)(new_buf - postimage->buf)); /* Fix the length of the whole thing */ @@ -3509,7 +3509,7 @@ static int load_current(struct apply_state *state, unsigned mode = patch->new_mode; if (!patch->is_new) - die("BUG: patch to %s is not a creation", patch->old_name); + BUG("patch to %s is not a creation", patch->old_name); pos = cache_name_pos(name, strlen(name)); if (pos < 0) @@ -3860,9 +3860,9 @@ static int check_unsafe_path(struct patch *patch) if (!patch->is_delete) new_name = patch->new_name; - if (old_name && !verify_path(old_name)) + if (old_name && !verify_path(old_name, patch->old_mode)) return error(_("invalid path '%s'"), old_name); - if (new_name && !verify_path(new_name)) + if (new_name && !verify_path(new_name, patch->new_mode)) return error(_("invalid path '%s'"), new_name); return 0; } @@ -4058,7 +4058,7 @@ static int build_fake_ancestor(struct apply_state *state, struct patch *list) { struct patch *patch; struct index_state result = { NULL }; - static struct lock_file lock; + struct lock_file lock = LOCK_INIT; int res; /* Once we start supporting the reverse patch, it may be diff --git a/archive-tar.c b/archive-tar.c index f93409324f..b6f58ddf38 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -441,7 +441,7 @@ static int write_tar_filter_archive(const struct archiver *ar, int r; if (!ar->data) - die("BUG: tar-filter archiver called with no filter defined"); + BUG("tar-filter archiver called with no filter defined"); strbuf_addstr(&cmd, ar->data); if (args->compression_level >= 0) diff --git a/argv-array.c b/argv-array.c index cb5bcd2c06..f352ea9357 100644 --- a/argv-array.c +++ b/argv-array.c @@ -21,12 +21,13 @@ static void argv_array_push_nodup(struct argv_array *array, const char *value) array->argv[array->argc] = NULL; } -void argv_array_push(struct argv_array *array, const char *value) +const char *argv_array_push(struct argv_array *array, const char *value) { argv_array_push_nodup(array, xstrdup(value)); + return array->argv[array->argc - 1]; } -void argv_array_pushf(struct argv_array *array, const char *fmt, ...) +const char *argv_array_pushf(struct argv_array *array, const char *fmt, ...) { va_list ap; struct strbuf v = STRBUF_INIT; @@ -36,6 +37,7 @@ void argv_array_pushf(struct argv_array *array, const char *fmt, ...) va_end(ap); argv_array_push_nodup(array, strbuf_detach(&v, NULL)); + return array->argv[array->argc - 1]; } void argv_array_pushl(struct argv_array *array, ...) diff --git a/argv-array.h b/argv-array.h index 750c30d2f2..a39ba43f57 100644 --- a/argv-array.h +++ b/argv-array.h @@ -12,9 +12,9 @@ struct argv_array { #define ARGV_ARRAY_INIT { empty_argv, 0, 0 } void argv_array_init(struct argv_array *); -void argv_array_push(struct argv_array *, const char *); +const char *argv_array_push(struct argv_array *, const char *); __attribute__((format (printf,2,3))) -void argv_array_pushf(struct argv_array *, const char *fmt, ...); +const char *argv_array_pushf(struct argv_array *, const char *fmt, ...); LAST_ARG_MUST_BE_NULL void argv_array_pushl(struct argv_array *, ...); void argv_array_pushv(struct argv_array *, const char **); @@ -157,7 +157,7 @@ static void all_attrs_init(struct attr_hashmap *map, struct attr_check *check) size = hashmap_get_size(&map->map); if (size < check->all_attrs_nr) - die("BUG: interned attributes shouldn't be deleted"); + BUG("interned attributes shouldn't be deleted"); /* * If the number of attributes in the global dictionary has increased @@ -541,7 +541,7 @@ static void check_vector_remove(struct attr_check *check) break; if (i >= check_vector.nr) - die("BUG: no entry found"); + BUG("no entry found"); /* shift entries over */ for (; i < check_vector.nr - 1; i++) @@ -599,11 +599,11 @@ struct attr_check *attr_check_initl(const char *one, ...) const struct git_attr *attr; param = va_arg(params, const char *); if (!param) - die("BUG: counted %d != ended at %d", + BUG("counted %d != ended at %d", check->nr, cnt); attr = git_attr(param); if (!attr) - die("BUG: %s: not a valid attribute name", param); + BUG("%s: not a valid attribute name", param); check->items[cnt].attr = attr; } va_end(params); @@ -714,7 +714,7 @@ void git_attr_set_direction(enum git_attr_direction new_direction, struct index_state *istate) { if (is_bare_repository() && new_direction != GIT_ATTR_INDEX) - die("BUG: non-INDEX attr direction in a bare repo"); + BUG("non-INDEX attr direction in a bare repo"); if (new_direction != direction) drop_all_attr_stacks(); @@ -1806,7 +1806,7 @@ void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blam l->item = c; if (add_decoration(&sb->revs->children, &c->parents->item->object, l)) - die("BUG: not unique item in first-parent chain"); + BUG("not unique item in first-parent chain"); c = c->parents->item; } @@ -3,12 +3,13 @@ #include "config.h" #include "branch.h" #include "refs.h" +#include "refspec.h" #include "remote.h" #include "commit.h" #include "worktree.h" struct tracking { - struct refspec spec; + struct refspec_item spec; char *src; const char *remote; int matches; @@ -218,8 +219,8 @@ int validate_new_branchname(const char *name, struct strbuf *ref, int force) static int check_tracking_branch(struct remote *remote, void *cb_data) { char *tracking_branch = cb_data; - struct refspec query; - memset(&query, 0, sizeof(struct refspec)); + struct refspec_item query; + memset(&query, 0, sizeof(struct refspec_item)); query.dst = tracking_branch; return !remote_find_tracking(remote, &query); } diff --git a/builtin/add.c b/builtin/add.c index c9e2619a9a..8a155dd41e 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -265,8 +265,6 @@ static int edit_patch(int argc, const char **argv, const char *prefix) return 0; } -static struct lock_file lock_file; - static const char ignore_error[] = N_("The following paths are ignored by one of your .gitignore files:\n"); @@ -393,6 +391,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) int add_new_files; int require_pathspec; char *seen = NULL; + struct lock_file lock_file = LOCK_INIT; git_config(add_config, NULL); diff --git a/builtin/am.c b/builtin/am.c index a1ff235fbc..2fc2d1e82c 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -403,11 +403,11 @@ static void am_load(struct am_state *state) struct strbuf sb = STRBUF_INIT; if (read_state_file(&sb, state, "next", 1) < 0) - die("BUG: state file 'next' does not exist"); + BUG("state file 'next' does not exist"); state->cur = strtol(sb.buf, NULL, 10); if (read_state_file(&sb, state, "last", 1) < 0) - die("BUG: state file 'last' does not exist"); + BUG("state file 'last' does not exist"); state->last = strtol(sb.buf, NULL, 10); if (read_author_script(state) < 0) @@ -986,7 +986,7 @@ static int split_mail(struct am_state *state, enum patch_format patch_format, case PATCH_FORMAT_MBOXRD: return split_mail_mbox(state, paths, keep_cr, 1); default: - die("BUG: invalid patch_format"); + BUG("invalid patch_format"); } return -1; } @@ -1041,7 +1041,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format, str = "b"; break; default: - die("BUG: invalid value for state->keep"); + BUG("invalid value for state->keep"); } write_state_text(state, "keep", str); @@ -1058,7 +1058,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format, str = "t"; break; default: - die("BUG: invalid value for state->scissors"); + BUG("invalid value for state->scissors"); } write_state_text(state, "scissors", str); @@ -1216,7 +1216,7 @@ static int parse_mail(struct am_state *state, const char *mail) mi.keep_non_patch_brackets_in_subject = 1; break; default: - die("BUG: invalid value for state->keep"); + BUG("invalid value for state->keep"); } if (state->message_id) @@ -1232,7 +1232,7 @@ static int parse_mail(struct am_state *state, const char *mail) mi.use_scissors = 1; break; default: - die("BUG: invalid value for state->scissors"); + BUG("invalid value for state->scissors"); } mi.input = xfopen(mail, "r"); @@ -1463,7 +1463,7 @@ static int run_apply(const struct am_state *state, const char *index_file) int options = 0; if (init_apply_state(&apply_state, NULL)) - die("BUG: init_apply_state() failed"); + BUG("init_apply_state() failed"); argv_array_push(&apply_opts, "apply"); argv_array_pushv(&apply_opts, state->git_apply_opts.argv); @@ -1489,7 +1489,7 @@ static int run_apply(const struct am_state *state, const char *index_file) apply_state.apply_verbosity = verbosity_silent; if (check_apply_state(&apply_state, force_apply)) - die("BUG: check_apply_state() failed"); + BUG("check_apply_state() failed"); argv_array_push(&apply_paths, am_path(state, "patch")); @@ -1542,7 +1542,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa char *their_tree_name; if (get_oid("HEAD", &our_tree) < 0) - hashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN); + oidcpy(&our_tree, the_hash_algo->empty_tree); if (build_fake_ancestor(state, index_path)) return error("could not build fake ancestor"); @@ -2042,7 +2042,7 @@ static void am_skip(struct am_state *state) am_rerere_clear(); if (get_oid("HEAD", &head)) - hashcpy(head.hash, EMPTY_TREE_SHA1_BIN); + oidcpy(&head, the_hash_algo->empty_tree); if (clean_index(&head, &head)) die(_("failed to clean index")); @@ -2105,11 +2105,11 @@ static void am_abort(struct am_state *state) curr_branch = resolve_refdup("HEAD", 0, &curr_head, NULL); has_curr_head = curr_branch && !is_null_oid(&curr_head); if (!has_curr_head) - hashcpy(curr_head.hash, EMPTY_TREE_SHA1_BIN); + oidcpy(&curr_head, the_hash_algo->empty_tree); has_orig_head = !get_oid("ORIG_HEAD", &orig_head); if (!has_orig_head) - hashcpy(orig_head.hash, EMPTY_TREE_SHA1_BIN); + oidcpy(&orig_head, the_hash_algo->empty_tree); clean_index(&curr_head, &orig_head); @@ -2407,7 +2407,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) ret = show_patch(&state); break; default: - die("BUG: invalid resume value"); + BUG("invalid resume value"); } am_state_release(&state); diff --git a/builtin/blame.c b/builtin/blame.c index bfdf7cc132..4202584f97 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -7,6 +7,7 @@ #include "cache.h" #include "config.h" +#include "color.h" #include "builtin.h" #include "commit.h" #include "diff.h" @@ -23,6 +24,7 @@ #include "dir.h" #include "progress.h" #include "blame.h" +#include "string-list.h" static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"); @@ -46,6 +48,8 @@ static int xdl_opts; static int abbrev = -1; static int no_whole_file_rename; static int show_progress; +static char repeated_meta_color[COLOR_MAXLEN]; +static int coloring_mode; static struct date_mode blame_date_mode = { DATE_ISO8601 }; static size_t blame_date_width; @@ -316,10 +320,12 @@ static const char *format_time(timestamp_t time, const char *tz_str, #define OUTPUT_PORCELAIN 010 #define OUTPUT_SHOW_NAME 020 #define OUTPUT_SHOW_NUMBER 040 -#define OUTPUT_SHOW_SCORE 0100 -#define OUTPUT_NO_AUTHOR 0200 +#define OUTPUT_SHOW_SCORE 0100 +#define OUTPUT_NO_AUTHOR 0200 #define OUTPUT_SHOW_EMAIL 0400 -#define OUTPUT_LINE_PORCELAIN 01000 +#define OUTPUT_LINE_PORCELAIN 01000 +#define OUTPUT_COLOR_LINE 02000 +#define OUTPUT_SHOW_AGE_WITH_COLOR 04000 static void emit_porcelain_details(struct blame_origin *suspect, int repeat) { @@ -367,6 +373,63 @@ static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent, putchar('\n'); } +static struct color_field { + timestamp_t hop; + char col[COLOR_MAXLEN]; +} *colorfield; +static int colorfield_nr, colorfield_alloc; + +static void parse_color_fields(const char *s) +{ + struct string_list l = STRING_LIST_INIT_DUP; + struct string_list_item *item; + enum { EXPECT_DATE, EXPECT_COLOR } next = EXPECT_COLOR; + + colorfield_nr = 0; + + /* Ideally this would be stripped and split at the same time? */ + string_list_split(&l, s, ',', -1); + ALLOC_GROW(colorfield, colorfield_nr + 1, colorfield_alloc); + + for_each_string_list_item(item, &l) { + switch (next) { + case EXPECT_DATE: + colorfield[colorfield_nr].hop = approxidate(item->string); + next = EXPECT_COLOR; + colorfield_nr++; + ALLOC_GROW(colorfield, colorfield_nr + 1, colorfield_alloc); + break; + case EXPECT_COLOR: + if (color_parse(item->string, colorfield[colorfield_nr].col)) + die(_("expecting a color: %s"), item->string); + next = EXPECT_DATE; + break; + } + } + + if (next == EXPECT_COLOR) + die (_("must end with a color")); + + colorfield[colorfield_nr].hop = TIME_MAX; +} + +static void setup_default_color_by_age(void) +{ + parse_color_fields("blue,12 month ago,white,1 month ago,red"); +} + +static void determine_line_heat(struct blame_entry *ent, const char **dest_color) +{ + int i = 0; + struct commit_info ci; + get_commit_info(ent->suspect->commit, &ci, 1); + + while (i < colorfield_nr && ci.author_time > colorfield[i].hop) + i++; + + *dest_color = colorfield[i].col; +} + static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int opt) { int cnt; @@ -375,15 +438,35 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int struct commit_info ci; char hex[GIT_MAX_HEXSZ + 1]; int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP); + const char *default_color = NULL, *color = NULL, *reset = NULL; get_commit_info(suspect->commit, &ci, 1); oid_to_hex_r(hex, &suspect->commit->object.oid); cp = blame_nth_line(sb, ent->lno); + + if (opt & OUTPUT_SHOW_AGE_WITH_COLOR) { + determine_line_heat(ent, &default_color); + color = default_color; + reset = GIT_COLOR_RESET; + } + for (cnt = 0; cnt < ent->num_lines; cnt++) { char ch; int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev; + if (opt & OUTPUT_COLOR_LINE) { + if (cnt > 0) { + color = repeated_meta_color; + reset = GIT_COLOR_RESET; + } else { + color = default_color ? default_color : NULL; + reset = default_color ? GIT_COLOR_RESET : NULL; + } + } + if (color) + fputs(color, stdout); + if (suspect->commit->object.flags & UNINTERESTING) { if (blank_boundary) memset(hex, ' ', length); @@ -433,6 +516,8 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int printf(" %*d) ", max_digits, ent->lno + 1 + cnt); } + if (reset) + fputs(reset, stdout); do { ch = *cp++; putchar(ch); @@ -607,6 +692,30 @@ static int git_blame_config(const char *var, const char *value, void *cb) parse_date_format(value, &blame_date_mode); return 0; } + if (!strcmp(var, "color.blame.repeatedlines")) { + if (color_parse_mem(value, strlen(value), repeated_meta_color)) + warning(_("invalid color '%s' in color.blame.repeatedLines"), + value); + return 0; + } + if (!strcmp(var, "color.blame.highlightrecent")) { + parse_color_fields(value); + return 0; + } + + if (!strcmp(var, "blame.coloring")) { + if (!strcmp(value, "repeatedLines")) { + coloring_mode |= OUTPUT_COLOR_LINE; + } else if (!strcmp(value, "highlightRecent")) { + coloring_mode |= OUTPUT_SHOW_AGE_WITH_COLOR; + } else if (!strcmp(value, "none")) { + coloring_mode &= ~(OUTPUT_COLOR_LINE | + OUTPUT_SHOW_AGE_WITH_COLOR); + } else { + warning(_("invalid value for blame.coloring")); + return 0; + } + } if (git_diff_heuristic_config(var, value, cb) < 0) return -1; @@ -690,6 +799,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix) OPT_BIT('s', NULL, &output_option, N_("Suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR), OPT_BIT('e', "show-email", &output_option, N_("Show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL), OPT_BIT('w', NULL, &xdl_opts, N_("Ignore whitespace differences"), XDF_IGNORE_WHITESPACE), + OPT_BIT(0, "color-lines", &output_option, N_("color redundant metadata from previous line differently"), OUTPUT_COLOR_LINE), + OPT_BIT(0, "color-by-age", &output_option, N_("color lines by age"), OUTPUT_SHOW_AGE_WITH_COLOR), /* * The following two options are parsed by parse_revision_opt() @@ -714,6 +825,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) unsigned int range_i; long anchor; + setup_default_color_by_age(); git_config(git_blame_config, &output_option); init_revisions(&revs, NULL); revs.date_mode = blame_date_mode; @@ -949,8 +1061,17 @@ parse_done: blame_coalesce(&sb); - if (!(output_option & OUTPUT_PORCELAIN)) + if (!(output_option & (OUTPUT_COLOR_LINE | OUTPUT_SHOW_AGE_WITH_COLOR))) + output_option |= coloring_mode; + + if (!(output_option & OUTPUT_PORCELAIN)) { find_alignment(&sb, &output_option); + if (!*repeated_meta_color && + (output_option & OUTPUT_COLOR_LINE)) + strcpy(repeated_meta_color, GIT_COLOR_CYAN); + } + if (output_option & OUTPUT_ANNOTATE_COMPAT) + output_option &= ~(OUTPUT_COLOR_LINE | OUTPUT_SHOW_AGE_WITH_COLOR); output(&sb, output_option); free((void *)sb.final_buf); diff --git a/builtin/branch.c b/builtin/branch.c index cc089f9efb..d53f6e2ad4 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -500,7 +500,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int if (!skip_prefix(oldref.buf, "refs/heads/", &interpreted_oldname) || !skip_prefix(newref.buf, "refs/heads/", &interpreted_newname)) { - die("BUG: expected prefix missing for refs"); + BUG("expected prefix missing for refs"); } if (copy) diff --git a/builtin/cat-file.c b/builtin/cat-file.c index b8ecbea98e..665b581949 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -312,7 +312,7 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d die("could not convert '%s' %s", oid_to_hex(oid), data->rest); } else - die("BUG: invalid cmdmode: %c", opt->cmdmode); + BUG("invalid cmdmode: %c", opt->cmdmode); batch_write(opt, contents, size); free(contents); } else if (stream_blob_to_fd(1, oid, NULL, 0) < 0) @@ -387,7 +387,7 @@ static void batch_one_object(const char *obj_name, struct batch_options *opt, (uintmax_t)strlen(obj_name), obj_name); break; default: - die("BUG: unknown get_sha1_with_context result %d\n", + BUG("unknown get_sha1_with_context result %d\n", result); break; } diff --git a/builtin/checkout.c b/builtin/checkout.c index 2b3b768eff..2e1d2376d2 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -527,6 +527,7 @@ static int merge_working_tree(const struct checkout_opts *opts, init_tree_desc(&trees[1], tree->buffer, tree->size); ret = unpack_trees(2, trees, &topts); + clear_unpack_trees_porcelain(&topts); if (ret == -1) { /* * Unpack couldn't do a trivial merge; either diff --git a/builtin/clone.c b/builtin/clone.c index 84f1473d19..99e73dae85 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -14,6 +14,7 @@ #include "parse-options.h" #include "fetch-pack.h" #include "refs.h" +#include "refspec.h" #include "tree.h" #include "tree-walk.h" #include "unpack-trees.h" @@ -546,7 +547,7 @@ static struct ref *find_remote_branch(const struct ref *refs, const char *branch } static struct ref *wanted_peer_refs(const struct ref *refs, - struct refspec *refspec) + struct refspec_item *refspec) { struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD")); struct ref *local_refs = head; @@ -823,7 +824,7 @@ static void write_refspec_config(const char *src_ref_prefix, } else if (remote_head_points_at) { const char *head = remote_head_points_at->name; if (!skip_prefix(head, "refs/heads/", &head)) - die("BUG: remote HEAD points at non-head?"); + BUG("remote HEAD points at non-head?"); strbuf_addf(&value, "+%s:%s%s", remote_head_points_at->name, branch_top->buf, head); @@ -894,8 +895,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) int err = 0, complete_refs_before_fetch = 1; int submodule_progress; - struct refspec *refspec; - const char *fetch_pattern; + struct refspec_item refspec; fetch_if_missing = 0; @@ -1077,8 +1077,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (option_required_reference.nr || option_optional_reference.nr) setup_reference(); - fetch_pattern = value.buf; - refspec = parse_fetch_refspec(1, &fetch_pattern); + refspec_item_init(&refspec, value.buf, REFSPEC_FETCH); strbuf_reset(&value); @@ -1138,7 +1137,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) refs = transport_get_remote_refs(transport, NULL); if (refs) { - mapped_refs = wanted_peer_refs(refs, refspec); + mapped_refs = wanted_peer_refs(refs, &refspec); /* * transport_get_remote_refs() may return refs with null sha-1 * in mapped_refs (see struct transport->get_refs_list @@ -1232,6 +1231,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix) strbuf_release(&value); junk_mode = JUNK_LEAVE_ALL; - free(refspec); + refspec_item_clear(&refspec); return err; } diff --git a/builtin/commit.c b/builtin/commit.c index 5240f11225..a842fea666 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -143,6 +143,16 @@ static int opt_parse_m(const struct option *opt, const char *arg, int unset) return 0; } +static int opt_parse_rename_score(const struct option *opt, const char *arg, int unset) +{ + const char **value = opt->value; + if (arg != NULL && *arg == '=') + arg = arg + 1; + + *value = arg; + return 0; +} + static void determine_whence(struct wt_status *s) { if (file_exists(git_path_merge_head())) @@ -495,7 +505,7 @@ static int is_a_merge(const struct commit *current_head) static void assert_split_ident(struct ident_split *id, const struct strbuf *buf) { if (split_ident_line(id, buf->buf, buf->len) || !id->date_begin) - die("BUG: unable to parse our own ident: %s", buf->buf); + BUG("unable to parse our own ident: %s", buf->buf); } static void export_one(const char *var, const char *s, const char *e, int hack) @@ -1259,11 +1269,31 @@ static int git_status_config(const char *k, const char *v, void *cb) return error(_("Invalid untracked files mode '%s'"), v); return 0; } + if (!strcmp(k, "diff.renamelimit")) { + if (s->rename_limit == -1) + s->rename_limit = git_config_int(k, v); + return 0; + } + if (!strcmp(k, "status.renamelimit")) { + s->rename_limit = git_config_int(k, v); + return 0; + } + if (!strcmp(k, "diff.renames")) { + if (s->detect_rename == -1) + s->detect_rename = git_config_rename(k, v); + return 0; + } + if (!strcmp(k, "status.renames")) { + s->detect_rename = git_config_rename(k, v); + return 0; + } return git_diff_ui_config(k, v, NULL); } int cmd_status(int argc, const char **argv, const char *prefix) { + static int no_renames = -1; + static const char *rename_score_arg = (const char *)-1; static struct wt_status s; int fd; struct object_id oid; @@ -1297,6 +1327,10 @@ int cmd_status(int argc, const char **argv, const char *prefix) N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, OPT_COLUMN(0, "column", &s.colopts, N_("list untracked files in columns")), + OPT_BOOL(0, "no-renames", &no_renames, N_("do not detect renames")), + { OPTION_CALLBACK, 'M', "find-renames", &rename_score_arg, + N_("n"), N_("detect renames, optionally set similarity index"), + PARSE_OPT_OPTARG, opt_parse_rename_score }, OPT_END(), }; @@ -1336,6 +1370,14 @@ int cmd_status(int argc, const char **argv, const char *prefix) s.ignore_submodule_arg = ignore_submodule_arg; s.status_format = status_format; s.verbose = verbose; + if (no_renames != -1) + s.detect_rename = !no_renames; + if ((intptr_t)rename_score_arg != -1) { + if (s.detect_rename < DIFF_DETECT_RENAME) + s.detect_rename = DIFF_DETECT_RENAME; + if (rename_score_arg) + s.rename_score = parse_rename_score(&rename_score_arg); + } wt_status_collect(&s); diff --git a/builtin/config.c b/builtin/config.c index 69e7270356..b29d26dede 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -398,7 +398,7 @@ static char *normalize_value(const char *key, const char *value) return xstrdup(value); } - die("BUG: cannot normalize type %d", type); + BUG("cannot normalize type %d", type); } static int get_color_found; @@ -602,6 +602,9 @@ int cmd_config(int argc, const char **argv, const char *prefix) if (use_local_config && nongit) die(_("--local can only be used inside a git repository")); + if (given_config_source.blob && nongit) + die(_("--blob can only be used inside a git repository")); + if (given_config_source.file && !strcmp(given_config_source.file, "-")) { given_config_source.file = NULL; diff --git a/builtin/count-objects.c b/builtin/count-objects.c index b054713e1a..d51e2ce1ec 100644 --- a/builtin/count-objects.c +++ b/builtin/count-objects.c @@ -66,7 +66,7 @@ static int count_loose(const struct object_id *oid, const char *path, void *data else { loose_size += on_disk_bytes(st); loose++; - if (verbose && has_sha1_pack(oid->hash)) + if (verbose && has_object_pack(oid)) packed_loose++; } return 0; diff --git a/builtin/describe.c b/builtin/describe.c index a4160e7f5d..cf1ae77d7c 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -612,7 +612,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix) suffix = broken; } } else if (dirty) { - static struct lock_file index_lock; + struct lock_file index_lock = LOCK_INIT; struct rev_info revs; struct argv_array args = ARGV_ARRAY_INIT; int fd, result; diff --git a/builtin/difftool.c b/builtin/difftool.c index c439b64207..bc97d4aef2 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -610,7 +610,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, continue; if (!indices_loaded) { - static struct lock_file lock; + struct lock_file lock = LOCK_INIT; strbuf_reset(&buf); strbuf_addf(&buf, "%s/wtindex", tmpdir); if (hold_lock_file_for_update(&lock, buf.buf, 0) < 0 || diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 68a762fbea..6c9768742f 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -7,6 +7,7 @@ #include "cache.h" #include "config.h" #include "refs.h" +#include "refspec.h" #include "commit.h" #include "object.h" #include "tag.h" @@ -35,8 +36,7 @@ static int use_done_feature; static int no_data; static int full_tree; static struct string_list extra_refs = STRING_LIST_INIT_NODUP; -static struct refspec *refspecs; -static int refspecs_nr; +static struct refspec refspecs = REFSPEC_INIT_FETCH; static int anonymize; static int parse_opt_signed_tag_mode(const struct option *opt, @@ -156,15 +156,14 @@ static void anonymize_path(struct strbuf *out, const char *path, } } -/* Since intptr_t is C99, we do not use it here */ -static inline uint32_t *mark_to_ptr(uint32_t mark) +static inline void *mark_to_ptr(uint32_t mark) { - return ((uint32_t *)NULL) + mark; + return (void *)(uintptr_t)mark; } static inline uint32_t ptr_to_mark(void * mark) { - return (uint32_t *)mark - (uint32_t *)NULL; + return (uint32_t)(uintptr_t)mark; } static inline void mark_object(struct object *object, uint32_t mark) @@ -517,7 +516,7 @@ static void anonymize_ident_line(const char **beg, const char **end) /* skip "committer", "author", "tagger", etc */ end_of_header = strchr(*beg, ' '); if (!end_of_header) - die("BUG: malformed line fed to anonymize_ident_line: %.*s", + BUG("malformed line fed to anonymize_ident_line: %.*s", (int)(*end - *beg), *beg); end_of_header++; strbuf_add(out, *beg, end_of_header - *beg); @@ -829,9 +828,9 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info) if (dwim_ref(e->name, strlen(e->name), &oid, &full_name) != 1) continue; - if (refspecs) { + if (refspecs.nr) { char *private; - private = apply_refspecs(refspecs, refspecs_nr, full_name); + private = apply_refspecs(&refspecs, full_name); if (private) { free(full_name); full_name = private; @@ -977,8 +976,8 @@ static void import_marks(char *input_file) static void handle_deletes(void) { int i; - for (i = 0; i < refspecs_nr; i++) { - struct refspec *refspec = &refspecs[i]; + for (i = 0; i < refspecs.nr; i++) { + struct refspec_item *refspec = &refspecs.items[i]; if (*refspec->src) continue; @@ -1039,18 +1038,12 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) usage_with_options (fast_export_usage, options); if (refspecs_list.nr) { - const char **refspecs_str; int i; - ALLOC_ARRAY(refspecs_str, refspecs_list.nr); for (i = 0; i < refspecs_list.nr; i++) - refspecs_str[i] = refspecs_list.items[i].string; - - refspecs_nr = refspecs_list.nr; - refspecs = parse_fetch_refspec(refspecs_nr, refspecs_str); + refspec_append(&refspecs, refspecs_list.items[i].string); string_list_clear(&refspecs_list, 1); - free(refspecs_str); } if (use_done_feature) @@ -1089,7 +1082,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) if (use_done_feature) printf("done\n"); - free_refspec(refspecs_nr, refspecs); + refspec_clear(&refspecs); return 0; } diff --git a/builtin/fetch.c b/builtin/fetch.c index 0a4512b0fb..ea5b9669ad 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -5,6 +5,7 @@ #include "config.h" #include "repository.h" #include "refs.h" +#include "refspec.h" #include "commit.h" #include "builtin.h" #include "string-list.h" @@ -59,8 +60,7 @@ static const char *submodule_prefix = ""; static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT; static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND; static int shown_url = 0; -static int refmap_alloc, refmap_nr; -static const char **refmap_array; +static struct refspec refmap = REFSPEC_INIT_FETCH; static struct list_objects_filter_options filter_options; static struct string_list server_options = STRING_LIST_INIT_DUP; @@ -108,14 +108,12 @@ static int gitmodules_fetch_config(const char *var, const char *value, void *cb) static int parse_refmap_arg(const struct option *opt, const char *arg, int unset) { - ALLOC_GROW(refmap_array, refmap_nr + 1, refmap_alloc); - /* * "git fetch --refmap='' origin foo" * can be used to tell the command not to store anywhere */ - if (*arg) - refmap_array[refmap_nr++] = arg; + refspec_append(&refmap, arg); + return 0; } @@ -204,7 +202,7 @@ static void add_merge_config(struct ref **head, for (i = 0; i < branch->merge_nr; i++) { struct ref *rm, **old_tail = *tail; - struct refspec refspec; + struct refspec_item refspec; for (rm = *head; rm; rm = rm->next) { if (branch_merge_matches(branch, i, rm->name)) { @@ -341,7 +339,7 @@ static void find_non_local_tags(struct transport *transport, } static struct ref *get_ref_map(struct transport *transport, - struct refspec *refspecs, int refspec_count, + struct refspec *rs, int tags, int *autotags) { int i; @@ -355,29 +353,26 @@ static struct ref *get_ref_map(struct transport *transport, const struct ref *remote_refs; - for (i = 0; i < refspec_count; i++) { - if (!refspecs[i].exact_sha1) { - const char *glob = strchr(refspecs[i].src, '*'); - if (glob) - argv_array_pushf(&ref_prefixes, "%.*s", - (int)(glob - refspecs[i].src), - refspecs[i].src); - else - expand_ref_prefix(&ref_prefixes, refspecs[i].src); - } + if (rs->nr) + refspec_ref_prefixes(rs, &ref_prefixes); + else if (transport->remote && transport->remote->fetch.nr) + refspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes); + + if (ref_prefixes.argc && + (tags == TAGS_SET || (tags == TAGS_DEFAULT && !rs->nr))) { + argv_array_push(&ref_prefixes, "refs/tags/"); } remote_refs = transport_get_remote_refs(transport, &ref_prefixes); argv_array_clear(&ref_prefixes); - if (refspec_count) { + if (rs->nr) { struct refspec *fetch_refspec; - int fetch_refspec_nr; - for (i = 0; i < refspec_count; i++) { - get_fetch_map(remote_refs, &refspecs[i], &tail, 0); - if (refspecs[i].dst && refspecs[i].dst[0]) + for (i = 0; i < rs->nr; i++) { + get_fetch_map(remote_refs, &rs->items[i], &tail, 0); + if (rs->items[i].dst && rs->items[i].dst[0]) *autotags = 1; } /* Merge everything on the command line (but not --tags) */ @@ -404,17 +399,14 @@ static struct ref *get_ref_map(struct transport *transport, * by ref_remove_duplicates() in favor of one of these * opportunistic entries with FETCH_HEAD_IGNORE. */ - if (refmap_array) { - fetch_refspec = parse_fetch_refspec(refmap_nr, refmap_array); - fetch_refspec_nr = refmap_nr; - } else { - fetch_refspec = transport->remote->fetch; - fetch_refspec_nr = transport->remote->fetch_refspec_nr; - } + if (refmap.nr) + fetch_refspec = &refmap; + else + fetch_refspec = &transport->remote->fetch; - for (i = 0; i < fetch_refspec_nr; i++) - get_fetch_map(ref_map, &fetch_refspec[i], &oref_tail, 1); - } else if (refmap_array) { + for (i = 0; i < fetch_refspec->nr; i++) + get_fetch_map(ref_map, &fetch_refspec->items[i], &oref_tail, 1); + } else if (refmap.nr) { die("--refmap option is only meaningful with command-line refspec(s)."); } else { /* Use the defaults */ @@ -422,16 +414,16 @@ static struct ref *get_ref_map(struct transport *transport, struct branch *branch = branch_get(NULL); int has_merge = branch_has_merge_config(branch); if (remote && - (remote->fetch_refspec_nr || + (remote->fetch.nr || /* Note: has_merge implies non-NULL branch->remote_name */ (has_merge && !strcmp(branch->remote_name, remote->name)))) { - for (i = 0; i < remote->fetch_refspec_nr; i++) { - get_fetch_map(remote_refs, &remote->fetch[i], &tail, 0); - if (remote->fetch[i].dst && - remote->fetch[i].dst[0]) + for (i = 0; i < remote->fetch.nr; i++) { + get_fetch_map(remote_refs, &remote->fetch.items[i], &tail, 0); + if (remote->fetch.items[i].dst && + remote->fetch.items[i].dst[0]) *autotags = 1; if (!i && !has_merge && ref_map && - !remote->fetch[0].pattern) + !remote->fetch.items[0].pattern) ref_map->fetch_head_status = FETCH_HEAD_MERGE; } /* @@ -966,11 +958,11 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map) return ret; } -static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map, - const char *raw_url) +static int prune_refs(struct refspec *rs, struct ref *ref_map, + const char *raw_url) { int url_len, i, result = 0; - struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map); + struct ref *ref, *stale_refs = get_stale_heads(rs, ref_map); char *url; int summary_width = transport_summary_width(stale_refs); const char *dangling_msg = dry_run @@ -1116,7 +1108,7 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map) } static int do_fetch(struct transport *transport, - struct refspec *refs, int ref_count) + struct refspec *rs) { struct string_list existing_refs = STRING_LIST_INIT_DUP; struct ref *ref_map; @@ -1140,7 +1132,7 @@ static int do_fetch(struct transport *transport, goto cleanup; } - ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags); + ref_map = get_ref_map(transport, rs, tags, &autotags); if (!update_head_ok) check_not_current_branch(ref_map); @@ -1164,11 +1156,10 @@ static int do_fetch(struct transport *transport, * explicitly (via command line or configuration); we * don't care whether --tags was specified. */ - if (ref_count) { - prune_refs(refs, ref_count, ref_map, transport->url); + if (rs->nr) { + prune_refs(rs, ref_map, transport->url); } else { - prune_refs(transport->remote->fetch, - transport->remote->fetch_refspec_nr, + prune_refs(&transport->remote->fetch, ref_map, transport->url); } @@ -1357,10 +1348,8 @@ static inline void fetch_one_setup_partial(struct remote *remote) static int fetch_one(struct remote *remote, int argc, const char **argv, int prune_tags_ok) { - static const char **refs = NULL; - struct refspec *refspec; - int ref_nr = 0; - int j = 0; + struct refspec rs = REFSPEC_INIT_FETCH; + int i; int exit_code; int maybe_prune_tags; int remote_via_config = remote_is_configured(remote, 0); @@ -1393,29 +1382,24 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru maybe_prune_tags = prune_tags_ok && prune_tags; if (maybe_prune_tags && remote_via_config) - add_prune_tags_to_fetch_refspec(remote); - - if (argc > 0 || (maybe_prune_tags && !remote_via_config)) { - size_t nr_alloc = st_add3(argc, maybe_prune_tags, 1); - refs = xcalloc(nr_alloc, sizeof(const char *)); - if (maybe_prune_tags) { - refs[j++] = xstrdup("refs/tags/*:refs/tags/*"); - ref_nr++; - } - } - - if (argc > 0) { - int i; - for (i = 0; i < argc; i++) { - if (!strcmp(argv[i], "tag")) { - i++; - if (i >= argc) - die(_("You need to specify a tag name.")); - refs[j++] = xstrfmt("refs/tags/%s:refs/tags/%s", - argv[i], argv[i]); - } else - refs[j++] = argv[i]; - ref_nr++; + refspec_append(&remote->fetch, TAG_REFSPEC); + + if (maybe_prune_tags && (argc || !remote_via_config)) + refspec_append(&rs, TAG_REFSPEC); + + for (i = 0; i < argc; i++) { + if (!strcmp(argv[i], "tag")) { + char *tag; + i++; + if (i >= argc) + die(_("You need to specify a tag name.")); + + tag = xstrfmt("refs/tags/%s:refs/tags/%s", + argv[i], argv[i]); + refspec_append(&rs, tag); + free(tag); + } else { + refspec_append(&rs, argv[i]); } } @@ -1424,9 +1408,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru sigchain_push_common(unlock_pack_on_signal); atexit(unlock_pack); - refspec = parse_fetch_refspec(ref_nr, refs); - exit_code = do_fetch(gtransport, refspec, ref_nr); - free_refspec(ref_nr, refspec); + exit_code = do_fetch(gtransport, &rs); + refspec_clear(&rs); transport_disconnect(gtransport); gtransport = NULL; return exit_code; diff --git a/builtin/fsck.c b/builtin/fsck.c index 9d59d7d5a2..3ad4f160f9 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -228,7 +228,7 @@ static void check_reachable_object(struct object *obj) if (!(obj->flags & HAS_OBJ)) { if (is_promisor_object(&obj->oid)) return; - if (has_sha1_pack(obj->oid.hash)) + if (has_object_pack(&obj->oid)) return; /* it is in pack - forget about it */ printf("missing %s %s\n", printable_type(obj), describe_object(obj)); @@ -340,7 +340,7 @@ static void check_connectivity(void) } } -static int fsck_obj(struct object *obj) +static int fsck_obj(struct object *obj, void *buffer, unsigned long size) { int err; @@ -354,7 +354,7 @@ static int fsck_obj(struct object *obj) if (fsck_walk(obj, NULL, &fsck_obj_options)) objerror(obj, "broken links"); - err = fsck_object(obj, NULL, 0, &fsck_obj_options); + err = fsck_object(obj, buffer, size, &fsck_obj_options); if (err) goto out; @@ -399,7 +399,7 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type, } obj->flags &= ~(REACHABLE | SEEN); obj->flags |= HAS_OBJ; - return fsck_obj(obj); + return fsck_obj(obj, buffer, size); } static int default_refs; @@ -507,44 +507,42 @@ static void get_default_heads(void) } } -static struct object *parse_loose_object(const struct object_id *oid, - const char *path) +static int fsck_loose(const struct object_id *oid, const char *path, void *data) { struct object *obj; - void *contents; enum object_type type; unsigned long size; + void *contents; int eaten; - if (read_loose_object(path, oid, &type, &size, &contents) < 0) - return NULL; + if (read_loose_object(path, oid, &type, &size, &contents) < 0) { + errors_found |= ERROR_OBJECT; + error("%s: object corrupt or missing: %s", + oid_to_hex(oid), path); + return 0; /* keep checking other objects */ + } if (!contents && type != OBJ_BLOB) - die("BUG: read_loose_object streamed a non-blob"); + BUG("read_loose_object streamed a non-blob"); obj = parse_object_buffer(oid, type, size, contents, &eaten); - - if (!eaten) - free(contents); - return obj; -} - -static int fsck_loose(const struct object_id *oid, const char *path, void *data) -{ - struct object *obj = parse_loose_object(oid, path); - if (!obj) { errors_found |= ERROR_OBJECT; - error("%s: object corrupt or missing: %s", + error("%s: object could not be parsed: %s", oid_to_hex(oid), path); + if (!eaten) + free(contents); return 0; /* keep checking other objects */ } obj->flags &= ~(REACHABLE | SEEN); obj->flags |= HAS_OBJ; - if (fsck_obj(obj)) + if (fsck_obj(obj, contents, size)) errors_found |= ERROR_OBJECT; - return 0; + + if (!eaten) + free(contents); + return 0; /* keep checking other objects, even if we saw an error */ } static int fsck_cruft(const char *basename, const char *path, void *data) @@ -756,6 +754,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) } stop_progress(&progress); } + + if (fsck_finish(&fsck_obj_options)) + errors_found |= ERROR_OBJECT; } for (i = 0; i < argc; i++) { diff --git a/builtin/gc.c b/builtin/gc.c index c4777b2449..ccfb1ceaeb 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -373,7 +373,7 @@ static int need_to_gc(void) /* return NULL on success, else hostname running the gc */ static const char *lock_repo_for_gc(int force, pid_t* ret_pid) { - static struct lock_file lock; + struct lock_file lock = LOCK_INIT; char my_host[HOST_NAME_MAX + 1]; struct strbuf sb = STRBUF_INIT; struct stat st; diff --git a/builtin/grep.c b/builtin/grep.c index 850bcffd99..ee753a403e 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -488,7 +488,8 @@ static int grep_cache(struct grep_opt *opt, struct repository *repo, strbuf_addstr(&name, repo->submodule_prefix); } - repo_read_index(repo); + if (repo_read_index(repo) < 0) + die("index file corrupt"); for (nr = 0; nr < repo->index->cache_nr; nr++) { const struct cache_entry *ce = repo->index->cache[nr]; diff --git a/builtin/help.c b/builtin/help.c index 2d51071429..58e0a5507f 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -9,6 +9,7 @@ #include "run-command.h" #include "column.h" #include "help.h" +#include "alias.h" #ifndef DEFAULT_HELP_FORMAT #define DEFAULT_HELP_FORMAT "man" @@ -36,6 +37,7 @@ static const char *html_path; static int show_all = 0; static int show_guides = 0; +static int verbose; static unsigned int colopts; static enum help_format help_format = HELP_FORMAT_NONE; static int exclude_guides; @@ -48,6 +50,7 @@ static struct option builtin_help_options[] = { HELP_FORMAT_WEB), OPT_SET_INT('i', "info", &help_format, N_("show info page"), HELP_FORMAT_INFO), + OPT__VERBOSE(&verbose, N_("print command description")), OPT_END(), }; @@ -400,38 +403,6 @@ static void show_html_page(const char *git_cmd) open_html(page_path.buf); } -static struct { - const char *name; - const char *help; -} common_guides[] = { - { "attributes", N_("Defining attributes per path") }, - { "everyday", N_("Everyday Git With 20 Commands Or So") }, - { "glossary", N_("A Git glossary") }, - { "ignore", N_("Specifies intentionally untracked files to ignore") }, - { "modules", N_("Defining submodule properties") }, - { "revisions", N_("Specifying revisions and ranges for Git") }, - { "tutorial", N_("A tutorial introduction to Git (for version 1.5.1 or newer)") }, - { "workflows", N_("An overview of recommended workflows with Git") }, -}; - -static void list_common_guides_help(void) -{ - int i, longest = 0; - - for (i = 0; i < ARRAY_SIZE(common_guides); i++) { - if (longest < strlen(common_guides[i].name)) - longest = strlen(common_guides[i].name); - } - - puts(_("The common Git guides are:\n")); - for (i = 0; i < ARRAY_SIZE(common_guides); i++) { - printf(" %s ", common_guides[i].name); - mput_char(' ', longest - strlen(common_guides[i].name)); - puts(_(common_guides[i].help)); - } - putchar('\n'); -} - static const char *check_git_cmd(const char* cmd) { char *alias; @@ -463,6 +434,11 @@ int cmd_help(int argc, const char **argv, const char *prefix) if (show_all) { git_config(git_help_config, NULL); + if (verbose) { + setup_pager(); + list_all_cmds_help(); + return 0; + } printf(_("usage: %s%s"), _(git_usage_string), "\n\n"); load_command_list("git-", &main_cmds, &other_cmds); list_commands(colopts, &main_cmds, &other_cmds); diff --git a/builtin/index-pack.c b/builtin/index-pack.c index e2f670bef9..4ab31ed388 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -837,6 +837,9 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, blob->object.flags |= FLAG_CHECKED; else die(_("invalid blob object %s"), oid_to_hex(oid)); + if (do_fsck_object && + fsck_object(&blob->object, (void *)data, size, &fsck_options)) + die(_("fsck error in packed object")); } else { struct object *obj; int eaten; @@ -854,7 +857,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, die(_("invalid %s"), type_name(type)); if (do_fsck_object && fsck_object(obj, buf, size, &fsck_options)) - die(_("Error in object")); + die(_("fsck error in packed object")); if (strict && fsck_walk(obj, NULL, &fsck_options)) die(_("Not all child objects of %s are reachable"), oid_to_hex(&obj->oid)); @@ -866,7 +869,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, if (obj->type == OBJ_COMMIT) { struct commit *commit = (struct commit *) obj; if (detach_commit_buffer(commit, NULL) != data) - die("BUG: parse_object_buffer transmogrified our buffer"); + BUG("parse_object_buffer transmogrified our buffer"); } obj->flags |= FLAG_CHECKED; } @@ -1015,7 +1018,7 @@ static struct base_data *find_unresolved_deltas_1(struct base_data *base, if (!compare_and_swap_type(&child->real_type, OBJ_REF_DELTA, base->obj->real_type)) - die("BUG: child->real_type != OBJ_REF_DELTA"); + BUG("child->real_type != OBJ_REF_DELTA"); resolve_delta(child, base, result); if (base->ref_first == base->ref_last && base->ofs_last == -1) @@ -1479,6 +1482,9 @@ static void final(const char *final_pack_name, const char *curr_pack_name, } else chmod(final_index_name, 0444); + if (do_fsck_object) + add_packed_git(final_index_name, strlen(final_index_name), 0); + if (!from_stdin) { printf("%s\n", sha1_to_hex(hash)); } else { @@ -1543,12 +1549,13 @@ static void read_v2_anomalous_offsets(struct packed_git *p, { const uint32_t *idx1, *idx2; uint32_t i; + const uint32_t hashwords = the_hash_algo->rawsz / sizeof(uint32_t); /* The address of the 4-byte offset table */ idx1 = (((const uint32_t *)p->index_data) + 2 /* 8-byte header */ + 256 /* fan out */ - + 5 * p->num_objects /* 20-byte SHA-1 table */ + + hashwords * p->num_objects /* object ID table */ + p->num_objects /* CRC32 table */ ); @@ -1820,6 +1827,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) pack_hash); else close(input_fd); + + if (do_fsck_object && fsck_finish(&fsck_options)) + die(_("fsck error in pack objects")); + free(objects); strbuf_release(&index_name_buf); if (pack_name == NULL) diff --git a/builtin/init-db.c b/builtin/init-db.c index 2542c5244e..5a5844c153 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -391,7 +391,7 @@ int init_db(const char *git_dir, const char *real_git_dir, else if (get_shared_repository() == PERM_EVERYBODY) xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY); else - die("BUG: invalid value for shared_repository"); + BUG("invalid value for shared_repository"); git_config_set("core.sharedrepository", buf); git_config_set("receive.denyNonFastforwards", "true"); } diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 40d9138c46..88bb2019ad 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -166,7 +166,7 @@ static void show_killed_files(const struct index_state *istate, */ pos = index_name_pos(istate, ent->name, ent->len); if (0 <= pos) - die("BUG: killed-file %.*s not found", + BUG("killed-file %.*s not found", ent->len, ent->name); pos = -pos - 1; while (pos < istate->cache_nr && diff --git a/builtin/merge.c b/builtin/merge.c index 4be71dd81b..b00d6f4821 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -14,6 +14,7 @@ #include "run-command.h" #include "diff.h" #include "refs.h" +#include "refspec.h" #include "commit.h" #include "diffcore.h" #include "revision.h" @@ -34,6 +35,7 @@ #include "string-list.h" #include "packfile.h" #include "tag.h" +#include "alias.h" #define DEFAULT_TWOHEAD (1<<0) #define DEFAULT_OCTOPUS (1<<1) @@ -280,7 +282,7 @@ out: return rc; } -static void read_empty(unsigned const char *sha1, int verbose) +static void read_empty(const struct object_id *oid, int verbose) { int i = 0; const char *args[7]; @@ -290,15 +292,15 @@ static void read_empty(unsigned const char *sha1, int verbose) args[i++] = "-v"; args[i++] = "-m"; args[i++] = "-u"; - args[i++] = EMPTY_TREE_SHA1_HEX; - args[i++] = sha1_to_hex(sha1); + args[i++] = empty_tree_oid_hex(); + args[i++] = oid_to_hex(oid); args[i] = NULL; if (run_command_v_opt(args, RUN_GIT_CMD)) die(_("read-tree failed")); } -static void reset_hard(unsigned const char *sha1, int verbose) +static void reset_hard(const struct object_id *oid, int verbose) { int i = 0; const char *args[6]; @@ -308,7 +310,7 @@ static void reset_hard(unsigned const char *sha1, int verbose) args[i++] = "-v"; args[i++] = "--reset"; args[i++] = "-u"; - args[i++] = sha1_to_hex(sha1); + args[i++] = oid_to_hex(oid); args[i] = NULL; if (run_command_v_opt(args, RUN_GIT_CMD)) @@ -324,7 +326,7 @@ static void restore_state(const struct object_id *head, if (is_null_oid(stash)) return; - reset_hard(head->hash, 1); + reset_hard(head, 1); args[2] = oid_to_hex(stash); @@ -647,7 +649,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, struct commit_list *remoteheads, struct commit *head) { - static struct lock_file lock; + struct lock_file lock = LOCK_INIT; const char *head_arg = "HEAD"; hold_locked_index(&lock, LOCK_DIE_ON_ERROR); @@ -805,7 +807,7 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads) { struct object_id result_tree, result_commit; struct commit_list *parents, **pptr = &parents; - static struct lock_file lock; + struct lock_file lock = LOCK_INIT; hold_locked_index(&lock, LOCK_DIE_ON_ERROR); refresh_cache(REFRESH_QUIET); @@ -1297,7 +1299,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (remoteheads->next) die(_("Can merge only exactly one commit into empty head")); remote_head_oid = &remoteheads->item->object.oid; - read_empty(remote_head_oid->hash, 0); + read_empty(remote_head_oid, 0); update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR); goto done; diff --git a/builtin/mv.c b/builtin/mv.c index 7a63667d64..80bb967a63 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -72,7 +72,6 @@ static const char *add_slash(const char *path) return path; } -static struct lock_file lock_file; #define SUBMODULE_WITH_GITDIR ((const char *)1) static void prepare_move_submodule(const char *src, int first, @@ -131,6 +130,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; struct stat st; struct string_list src_for_dst = STRING_LIST_INIT_NODUP; + struct lock_file lock_file = LOCK_INIT; git_config(git_default_config, NULL); diff --git a/builtin/notes.c b/builtin/notes.c index 4d34994c34..6981e2d906 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -461,7 +461,7 @@ static int add(int argc, const char **argv, const char *prefix) if (d.buf.len || allow_empty) { write_note_data(&d, &new_note); if (add_note(t, &object, &new_note, combine_notes_overwrite)) - die("BUG: combine_notes_overwrite failed"); + BUG("combine_notes_overwrite failed"); commit_notes(t, "Notes added by 'git notes add'"); } else { fprintf(stderr, _("Removing note for object %s\n"), @@ -544,7 +544,7 @@ static int copy(int argc, const char **argv, const char *prefix) } if (add_note(t, &object, from_note, combine_notes_overwrite)) - die("BUG: combine_notes_overwrite failed"); + BUG("combine_notes_overwrite failed"); commit_notes(t, "Notes added by 'git notes copy'"); out: free_notes(t); @@ -621,7 +621,7 @@ static int append_edit(int argc, const char **argv, const char *prefix) if (d.buf.len || allow_empty) { write_note_data(&d, &new_note); if (add_note(t, &object, &new_note, combine_notes_overwrite)) - die("BUG: combine_notes_overwrite failed"); + BUG("combine_notes_overwrite failed"); logmsg = xstrfmt("Notes added by 'git notes %s'", argv[0]); } else { fprintf(stderr, _("Removing note for object %s\n"), @@ -831,7 +831,7 @@ static int merge(int argc, const char **argv, const char *prefix) const char *short_ref = NULL; if (!skip_prefix(o.local_ref, "refs/notes/", &short_ref)) - die("BUG: local ref %s is outside of refs/notes/", + BUG("local ref %s is outside of refs/notes/", o.local_ref); strbuf_addf(&merge_key, "notes.%s.mergeStrategy", short_ref); diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index bd6ac48968..71056d8294 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -279,6 +279,7 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent enum object_type type; void *buf; struct git_istream *st = NULL; + const unsigned hashsz = the_hash_algo->rawsz; if (!usable_delta) { if (oe_type(entry) == OBJ_BLOB && @@ -335,7 +336,7 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent dheader[pos] = ofs & 127; while (ofs >>= 7) dheader[--pos] = 128 | (--ofs & 127); - if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) { + if (limit && hdrlen + sizeof(dheader) - pos + datalen + hashsz >= limit) { if (st) close_istream(st); free(buf); @@ -347,19 +348,19 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent } else if (type == OBJ_REF_DELTA) { /* * Deltas with a base reference contain - * an additional 20 bytes for the base sha1. + * additional bytes for the base object ID. */ - if (limit && hdrlen + 20 + datalen + 20 >= limit) { + if (limit && hdrlen + hashsz + datalen + hashsz >= limit) { if (st) close_istream(st); free(buf); return 0; } hashwrite(f, header, hdrlen); - hashwrite(f, DELTA(entry)->idx.oid.hash, 20); - hdrlen += 20; + hashwrite(f, DELTA(entry)->idx.oid.hash, hashsz); + hdrlen += hashsz; } else { - if (limit && hdrlen + datalen + 20 >= limit) { + if (limit && hdrlen + datalen + hashsz >= limit) { if (st) close_istream(st); free(buf); @@ -391,6 +392,7 @@ static off_t write_reuse_object(struct hashfile *f, struct object_entry *entry, unsigned char header[MAX_PACK_OBJECT_HEADER], dheader[MAX_PACK_OBJECT_HEADER]; unsigned hdrlen; + const unsigned hashsz = the_hash_algo->rawsz; unsigned long entry_size = SIZE(entry); if (DELTA(entry)) @@ -427,7 +429,7 @@ static off_t write_reuse_object(struct hashfile *f, struct object_entry *entry, dheader[pos] = ofs & 127; while (ofs >>= 7) dheader[--pos] = 128 | (--ofs & 127); - if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) { + if (limit && hdrlen + sizeof(dheader) - pos + datalen + hashsz >= limit) { unuse_pack(&w_curs); return 0; } @@ -436,16 +438,16 @@ static off_t write_reuse_object(struct hashfile *f, struct object_entry *entry, hdrlen += sizeof(dheader) - pos; reused_delta++; } else if (type == OBJ_REF_DELTA) { - if (limit && hdrlen + 20 + datalen + 20 >= limit) { + if (limit && hdrlen + hashsz + datalen + hashsz >= limit) { unuse_pack(&w_curs); return 0; } hashwrite(f, header, hdrlen); - hashwrite(f, DELTA(entry)->idx.oid.hash, 20); - hdrlen += 20; + hashwrite(f, DELTA(entry)->idx.oid.hash, hashsz); + hdrlen += hashsz; reused_delta++; } else { - if (limit && hdrlen + datalen + 20 >= limit) { + if (limit && hdrlen + datalen + hashsz >= limit) { unuse_pack(&w_curs); return 0; } @@ -769,7 +771,7 @@ static off_t write_reused_pack(struct hashfile *f) die_errno("unable to seek in reused packfile"); if (reuse_packfile_offset < 0) - reuse_packfile_offset = reuse_packfile->pack_size - 20; + reuse_packfile_offset = reuse_packfile->pack_size - the_hash_algo->rawsz; total = to_write = reuse_packfile_offset - sizeof(struct pack_header); @@ -1033,7 +1035,7 @@ static int want_object_in_pack(const struct object_id *oid, int want; struct list_head *pos; - if (!exclude && local && has_loose_object_nonlocal(oid->hash)) + if (!exclude && local && has_loose_object_nonlocal(oid)) return 0; /* @@ -1467,7 +1469,7 @@ static void check_object(struct object_entry *entry) if (reuse_delta && !entry->preferred_base) base_ref = use_pack(p, &w_curs, entry->in_pack_offset + used, NULL); - entry->in_pack_header_size = used + 20; + entry->in_pack_header_size = used + the_hash_algo->rawsz; break; case OBJ_OFS_DELTA: buf = use_pack(p, &w_curs, @@ -1668,7 +1670,7 @@ static void break_delta_chains(struct object_entry *entry) * is a bug. */ if (cur->dfs_state != DFS_NONE) - die("BUG: confusing delta dfs state in first pass: %d", + BUG("confusing delta dfs state in first pass: %d", cur->dfs_state); /* @@ -1725,7 +1727,7 @@ static void break_delta_chains(struct object_entry *entry) if (cur->dfs_state == DFS_DONE) break; else if (cur->dfs_state != DFS_ACTIVE) - die("BUG: confusing delta dfs state in second pass: %d", + BUG("confusing delta dfs state in second pass: %d", cur->dfs_state); /* @@ -1949,7 +1951,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, /* Now some size filtering heuristics. */ trg_size = SIZE(trg_entry); if (!DELTA(trg_entry)) { - max_size = trg_size/2 - 20; + max_size = trg_size/2 - the_hash_algo->rawsz; ref_depth = 1; } else { max_size = DELTA_SIZE(trg_entry); diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c index 354478a127..0494dceff7 100644 --- a/builtin/pack-redundant.c +++ b/builtin/pack-redundant.c @@ -20,7 +20,7 @@ static int load_all_packs, verbose, alt_odb; struct llist_item { struct llist_item *next; - const unsigned char *sha1; + const struct object_id *oid; }; static struct llist { struct llist_item *front; @@ -90,14 +90,14 @@ static struct llist * llist_copy(struct llist *list) return ret; new_item = ret->front = llist_item_get(); - new_item->sha1 = list->front->sha1; + new_item->oid = list->front->oid; old_item = list->front->next; while (old_item) { prev = new_item; new_item = llist_item_get(); prev->next = new_item; - new_item->sha1 = old_item->sha1; + new_item->oid = old_item->oid; old_item = old_item->next; } new_item->next = NULL; @@ -108,10 +108,10 @@ static struct llist * llist_copy(struct llist *list) static inline struct llist_item *llist_insert(struct llist *list, struct llist_item *after, - const unsigned char *sha1) + const struct object_id *oid) { struct llist_item *new_item = llist_item_get(); - new_item->sha1 = sha1; + new_item->oid = oid; new_item->next = NULL; if (after != NULL) { @@ -131,21 +131,21 @@ static inline struct llist_item *llist_insert(struct llist *list, } static inline struct llist_item *llist_insert_back(struct llist *list, - const unsigned char *sha1) + const struct object_id *oid) { - return llist_insert(list, list->back, sha1); + return llist_insert(list, list->back, oid); } static inline struct llist_item *llist_insert_sorted_unique(struct llist *list, - const unsigned char *sha1, struct llist_item *hint) + const struct object_id *oid, struct llist_item *hint) { struct llist_item *prev = NULL, *l; l = (hint == NULL) ? list->front : hint; while (l) { - int cmp = hashcmp(l->sha1, sha1); + int cmp = oidcmp(l->oid, oid); if (cmp > 0) { /* we insert before this entry */ - return llist_insert(list, prev, sha1); + return llist_insert(list, prev, oid); } if (!cmp) { /* already exists */ return l; @@ -154,11 +154,11 @@ static inline struct llist_item *llist_insert_sorted_unique(struct llist *list, l = l->next; } /* insert at the end */ - return llist_insert_back(list, sha1); + return llist_insert_back(list, oid); } /* returns a pointer to an item in front of sha1 */ -static inline struct llist_item * llist_sorted_remove(struct llist *list, const unsigned char *sha1, struct llist_item *hint) +static inline struct llist_item * llist_sorted_remove(struct llist *list, const struct object_id *oid, struct llist_item *hint) { struct llist_item *prev, *l; @@ -166,7 +166,7 @@ redo_from_start: l = (hint == NULL) ? list->front : hint; prev = NULL; while (l) { - int cmp = hashcmp(l->sha1, sha1); + int cmp = oidcmp(l->oid, oid); if (cmp > 0) /* not in list, since sorted */ return prev; if (!cmp) { /* found */ @@ -201,7 +201,7 @@ static void llist_sorted_difference_inplace(struct llist *A, b = B->front; while (b) { - hint = llist_sorted_remove(A, b->sha1, hint); + hint = llist_sorted_remove(A, b->oid, hint); b = b->next; } } @@ -252,13 +252,14 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2) unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step; const unsigned char *p1_base, *p2_base; struct llist_item *p1_hint = NULL, *p2_hint = NULL; + const unsigned int hashsz = the_hash_algo->rawsz; p1_base = p1->pack->index_data; p2_base = p2->pack->index_data; p1_base += 256 * 4 + ((p1->pack->index_version < 2) ? 4 : 8); p2_base += 256 * 4 + ((p2->pack->index_version < 2) ? 4 : 8); - p1_step = (p1->pack->index_version < 2) ? 24 : 20; - p2_step = (p2->pack->index_version < 2) ? 24 : 20; + p1_step = hashsz + ((p1->pack->index_version < 2) ? 4 : 0); + p2_step = hashsz + ((p2->pack->index_version < 2) ? 4 : 0); while (p1_off < p1->pack->num_objects * p1_step && p2_off < p2->pack->num_objects * p2_step) @@ -267,9 +268,11 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2) /* cmp ~ p1 - p2 */ if (cmp == 0) { p1_hint = llist_sorted_remove(p1->unique_objects, - p1_base + p1_off, p1_hint); + (const struct object_id *)(p1_base + p1_off), + p1_hint); p2_hint = llist_sorted_remove(p2->unique_objects, - p1_base + p1_off, p2_hint); + (const struct object_id *)(p1_base + p1_off), + p2_hint); p1_off += p1_step; p2_off += p2_step; continue; @@ -359,13 +362,14 @@ static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2) size_t ret = 0; unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step; const unsigned char *p1_base, *p2_base; + const unsigned int hashsz = the_hash_algo->rawsz; p1_base = p1->index_data; p2_base = p2->index_data; p1_base += 256 * 4 + ((p1->index_version < 2) ? 4 : 8); p2_base += 256 * 4 + ((p2->index_version < 2) ? 4 : 8); - p1_step = (p1->index_version < 2) ? 24 : 20; - p2_step = (p2->index_version < 2) ? 24 : 20; + p1_step = hashsz + ((p1->index_version < 2) ? 4 : 0); + p2_step = hashsz + ((p2->index_version < 2) ? 4 : 0); while (p1_off < p1->num_objects * p1_step && p2_off < p2->num_objects * p2_step) @@ -499,7 +503,7 @@ static void load_all_objects(void) l = pl->all_objects->front; while (l) { hint = llist_insert_sorted_unique(all_objects, - l->sha1, hint); + l->oid, hint); l = l->next; } pl = pl->next; @@ -558,9 +562,9 @@ static struct pack_list * add_pack(struct packed_git *p) base = p->index_data; base += 256 * 4 + ((p->index_version < 2) ? 4 : 8); - step = (p->index_version < 2) ? 24 : 20; + step = the_hash_algo->rawsz + ((p->index_version < 2) ? 4 : 0); while (off < p->num_objects * step) { - llist_insert_back(l.all_objects, base + off); + llist_insert_back(l.all_objects, (const struct object_id *)(base + off)); off += step; } /* this list will be pruned in cmp_two_packs later */ @@ -601,8 +605,8 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix) int i; struct pack_list *min, *red, *pl; struct llist *ignore; - unsigned char *sha1; - char buf[42]; /* 40 byte sha1 + \n + \0 */ + struct object_id *oid; + char buf[GIT_MAX_HEXSZ + 2]; /* hex hash + \n + \0 */ if (argc == 2 && !strcmp(argv[1], "-h")) usage(pack_redundant_usage); @@ -650,10 +654,10 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix) llist_init(&ignore); if (!isatty(0)) { while (fgets(buf, sizeof(buf), stdin)) { - sha1 = xmalloc(20); - if (get_sha1_hex(buf, sha1)) - die("Bad sha1 on stdin: %s", buf); - llist_insert_sorted_unique(ignore, sha1, NULL); + oid = xmalloc(sizeof(*oid)); + if (get_oid_hex(buf, oid)) + die("Bad object ID on stdin: %s", buf); + llist_insert_sorted_unique(ignore, oid, NULL); } } llist_sorted_difference_inplace(all_objects, ignore); diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c index 419238171d..4ff525e50f 100644 --- a/builtin/prune-packed.c +++ b/builtin/prune-packed.c @@ -25,7 +25,7 @@ static int prune_object(const struct object_id *oid, const char *path, { int *opts = data; - if (!has_sha1_pack(oid->hash)) + if (!has_object_pack(oid)) return 0; if (*opts & PRUNE_PACKED_DRY_RUN) diff --git a/builtin/pull.c b/builtin/pull.c index c719a4f9d7..1f2ecf3a88 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -15,6 +15,7 @@ #include "remote.h" #include "dir.h" #include "refs.h" +#include "refspec.h" #include "revision.h" #include "submodule.h" #include "submodule-config.h" @@ -543,7 +544,7 @@ static int run_fetch(const char *repo, const char **refspecs) argv_array_push(&args, repo); argv_array_pushv(&args, refspecs); } else if (*refspecs) - die("BUG: refspecs without repo?"); + BUG("refspecs without repo?"); ret = run_command_v_opt(args.argv, RUN_GIT_CMD); argv_array_clear(&args); return ret; @@ -679,12 +680,12 @@ static const char *get_upstream_branch(const char *remote) */ static const char *get_tracking_branch(const char *remote, const char *refspec) { - struct refspec *spec; + struct refspec_item spec; const char *spec_src; const char *merge_branch; - spec = parse_fetch_refspec(1, &refspec); - spec_src = spec->src; + refspec_item_init(&spec, refspec, REFSPEC_FETCH); + spec_src = spec.src; if (!*spec_src || !strcmp(spec_src, "HEAD")) spec_src = "HEAD"; else if (skip_prefix(spec_src, "heads/", &spec_src)) @@ -704,7 +705,7 @@ static const char *get_tracking_branch(const char *remote, const char *refspec) } else merge_branch = NULL; - free_refspec(1, spec); + refspec_item_clear(&spec); return merge_branch; } diff --git a/builtin/push.c b/builtin/push.c index ac3705370e..9cd8e8cd56 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -4,6 +4,7 @@ #include "cache.h" #include "config.h" #include "refs.h" +#include "refspec.h" #include "run-command.h" #include "builtin.h" #include "remote.h" @@ -56,19 +57,10 @@ static enum transport_family family; static struct push_cas_option cas; -static const char **refspec; -static int refspec_nr; -static int refspec_alloc; +static struct refspec rs = REFSPEC_INIT_PUSH; static struct string_list push_options_config = STRING_LIST_INIT_DUP; -static void add_refspec(const char *ref) -{ - refspec_nr++; - ALLOC_GROW(refspec, refspec_nr, refspec_alloc); - refspec[refspec_nr-1] = ref; -} - static const char *map_refspec(const char *ref, struct remote *remote, struct ref *local_refs) { @@ -78,12 +70,11 @@ static const char *map_refspec(const char *ref, if (count_refspec_match(ref, local_refs, &matched) != 1) return ref; - if (remote->push) { - struct refspec query; - memset(&query, 0, sizeof(struct refspec)); + if (remote->push.nr) { + struct refspec_item query; + memset(&query, 0, sizeof(struct refspec_item)); query.src = matched->name; - if (!query_refspecs(remote->push, remote->push_refspec_nr, &query) && - query.dst) { + if (!query_refspecs(&remote->push, &query) && query.dst) { struct strbuf buf = STRBUF_INIT; strbuf_addf(&buf, "%s%s:%s", query.force ? "+" : "", @@ -138,7 +129,7 @@ static void set_refspecs(const char **refs, int nr, const char *repo) } ref = map_refspec(ref, remote, local_refs); } - add_refspec(ref); + refspec_append(&rs, ref); } } @@ -226,7 +217,7 @@ static void setup_push_upstream(struct remote *remote, struct branch *branch, } strbuf_addf(&refspec, "%s:%s", branch->refname, branch->merge[0]->src); - add_refspec(refspec.buf); + refspec_append(&rs, refspec.buf); } static void setup_push_current(struct remote *remote, struct branch *branch) @@ -236,7 +227,7 @@ static void setup_push_current(struct remote *remote, struct branch *branch) if (!branch) die(_(message_detached_head_die), remote->name); strbuf_addf(&refspec, "%s:%s", branch->refname, branch->refname); - add_refspec(refspec.buf); + refspec_append(&rs, refspec.buf); } static int is_workflow_triangular(struct remote *remote) @@ -253,7 +244,7 @@ static void setup_default_push_refspecs(struct remote *remote) switch (push_default) { default: case PUSH_DEFAULT_MATCHING: - add_refspec(":"); + refspec_append(&rs, ":"); break; case PUSH_DEFAULT_UNSPECIFIED: @@ -341,7 +332,8 @@ static void advise_ref_needs_force(void) advise(_(message_advice_ref_needs_force)); } -static int push_with_options(struct transport *transport, int flags) +static int push_with_options(struct transport *transport, struct refspec *rs, + int flags) { int err; unsigned int reject_reasons; @@ -363,8 +355,7 @@ static int push_with_options(struct transport *transport, int flags) if (verbosity > 0) fprintf(stderr, _("Pushing to %s\n"), transport->url); - err = transport_push(transport, refspec_nr, refspec, flags, - &reject_reasons); + err = transport_push(transport, rs, flags, &reject_reasons); if (err != 0) { fprintf(stderr, "%s", push_get_color(PUSH_COLOR_ERROR)); error(_("failed to push some refs to '%s'"), transport->url); @@ -397,6 +388,7 @@ static int do_push(const char *repo, int flags, struct remote *remote = pushremote_get(repo); const char **url; int url_nr; + struct refspec *push_refspec = &rs; if (!remote) { if (repo) @@ -417,27 +409,9 @@ static int do_push(const char *repo, int flags, if (push_options->nr) flags |= TRANSPORT_PUSH_OPTIONS; - if ((flags & TRANSPORT_PUSH_ALL) && refspec) { - if (!strcmp(*refspec, "refs/tags/*")) - return error(_("--all and --tags are incompatible")); - return error(_("--all can't be combined with refspecs")); - } - - if ((flags & TRANSPORT_PUSH_MIRROR) && refspec) { - if (!strcmp(*refspec, "refs/tags/*")) - return error(_("--mirror and --tags are incompatible")); - return error(_("--mirror can't be combined with refspecs")); - } - - if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) == - (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) { - return error(_("--all and --mirror are incompatible")); - } - - if (!refspec && !(flags & TRANSPORT_PUSH_ALL)) { - if (remote->push_refspec_nr) { - refspec = remote->push_refspec; - refspec_nr = remote->push_refspec_nr; + if (!push_refspec->nr && !(flags & TRANSPORT_PUSH_ALL)) { + if (remote->push.nr) { + push_refspec = &remote->push; } else if (!(flags & TRANSPORT_PUSH_MIRROR)) setup_default_push_refspecs(remote); } @@ -449,7 +423,7 @@ static int do_push(const char *repo, int flags, transport_get(remote, url[i]); if (flags & TRANSPORT_PUSH_OPTIONS) transport->push_options = push_options; - if (push_with_options(transport, flags)) + if (push_with_options(transport, push_refspec, flags)) errs++; } } else { @@ -457,7 +431,7 @@ static int do_push(const char *repo, int flags, transport_get(remote, NULL); if (flags & TRANSPORT_PUSH_OPTIONS) transport->push_options = push_options; - if (push_with_options(transport, flags)) + if (push_with_options(transport, push_refspec, flags)) errs++; } return !!errs; @@ -625,6 +599,20 @@ int cmd_push(int argc, const char **argv, const char *prefix) die(_("--delete is incompatible with --all, --mirror and --tags")); if (deleterefs && argc < 2) die(_("--delete doesn't make sense without any refs")); + if (flags & TRANSPORT_PUSH_ALL) { + if (tags) + die(_("--all and --tags are incompatible")); + if (argc >= 2) + die(_("--all can't be combined with refspecs")); + } + if (flags & TRANSPORT_PUSH_MIRROR) { + if (tags) + die(_("--mirror and --tags are incompatible")); + if (argc >= 2) + die(_("--mirror can't be combined with refspecs")); + } + if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR)) + die(_("--all and --mirror are incompatible")); if (recurse_submodules == RECURSE_SUBMODULES_CHECK) flags |= TRANSPORT_RECURSE_SUBMODULES_CHECK; @@ -634,7 +622,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) flags |= TRANSPORT_RECURSE_SUBMODULES_ONLY; if (tags) - add_refspec("refs/tags/*"); + refspec_append(&rs, "refs/tags/*"); if (argc > 0) { repo = argv[0]; diff --git a/builtin/read-tree.c b/builtin/read-tree.c index bf87a2710b..ebc43eb805 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -107,8 +107,6 @@ static int git_read_tree_config(const char *var, const char *value, void *cb) return git_default_config(var, value, cb); } -static struct lock_file lock_file; - int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) { int i, stage = 0; @@ -116,6 +114,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) struct tree_desc t[MAX_UNPACK_TREES]; struct unpack_trees_options opts; int prefix_set = 0; + struct lock_file lock_file = LOCK_INIT; const struct option read_tree_options[] = { { OPTION_CALLBACK, 0, "index-output", NULL, N_("file"), N_("write resulting index to <file>"), diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 0dd163280d..68d36e0a56 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -454,21 +454,21 @@ static void hmac_sha1(unsigned char *out, /* RFC 2104 2. (6) & (7) */ git_SHA1_Init(&ctx); git_SHA1_Update(&ctx, k_opad, sizeof(k_opad)); - git_SHA1_Update(&ctx, out, 20); + git_SHA1_Update(&ctx, out, GIT_SHA1_RAWSZ); git_SHA1_Final(out, &ctx); } static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp) { struct strbuf buf = STRBUF_INIT; - unsigned char sha1[20]; + unsigned char sha1[GIT_SHA1_RAWSZ]; strbuf_addf(&buf, "%s:%"PRItime, path, stamp); hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));; strbuf_release(&buf); /* RFC 2104 5. HMAC-SHA1-80 */ - strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, 20, sha1_to_hex(sha1)); + strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, GIT_SHA1_HEXSZ, sha1_to_hex(sha1)); return strbuf_detach(&buf, NULL); } @@ -876,7 +876,7 @@ static void refuse_unconfigured_deny_delete_current(void) static int command_singleton_iterator(void *cb_data, struct object_id *oid); static int update_shallow_ref(struct command *cmd, struct shallow_info *si) { - static struct lock_file shallow_lock; + struct lock_file shallow_lock = LOCK_INIT; struct oid_array extra = OID_ARRAY_INIT; struct check_connected_options opt = CHECK_CONNECTED_INIT; uint32_t mask = 1 << (cmd->index % 32); @@ -968,7 +968,7 @@ static const char *push_to_deploy(unsigned char *sha1, return "Working directory has unstaged changes"; /* diff-index with either HEAD or an empty tree */ - diff_index[4] = head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX; + diff_index[4] = head_has_history() ? "HEAD" : empty_tree_oid_hex(); child_process_init(&child); child.argv = diff_index; @@ -1378,7 +1378,7 @@ static void warn_if_skipped_connectivity_check(struct command *commands, } } if (!checked_connectivity) - die("BUG: connectivity check skipped???"); + BUG("connectivity check skipped???"); } static void execute_commands_non_atomic(struct command *commands, diff --git a/builtin/remote.c b/builtin/remote.c index 0bbf9f4c9e..1a82d850a2 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -7,6 +7,7 @@ #include "strbuf.h" #include "run-command.h" #include "refs.h" +#include "refspec.h" #include "argv-array.h" static const char * const builtin_remote_usage[] = { @@ -336,10 +337,10 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat struct ref *ref, *stale_refs; int i; - for (i = 0; i < states->remote->fetch_refspec_nr; i++) - if (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1)) + for (i = 0; i < states->remote->fetch.nr; i++) + if (get_fetch_map(remote_refs, &states->remote->fetch.items[i], &tail, 1)) die(_("Could not get fetch map for refspec %s"), - states->remote->fetch_refspec[i]); + states->remote->fetch.raw[i]); states->new_refs.strdup_strings = 1; states->tracked.strdup_strings = 1; @@ -350,8 +351,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat else string_list_append(&states->tracked, abbrev_branch(ref->name)); } - stale_refs = get_stale_heads(states->remote->fetch, - states->remote->fetch_refspec_nr, fetch_map); + stale_refs = get_stale_heads(&states->remote->fetch, fetch_map); for (ref = stale_refs; ref; ref = ref->next) { struct string_list_item *item = string_list_append(&states->stale, abbrev_branch(ref->name)); @@ -391,8 +391,7 @@ static int get_push_ref_states(const struct ref *remote_refs, local_refs = get_local_heads(); push_map = copy_ref_list(remote_refs); - match_push_refs(local_refs, &push_map, remote->push_refspec_nr, - remote->push_refspec, MATCH_REFS_NONE); + match_push_refs(local_refs, &push_map, &remote->push, MATCH_REFS_NONE); states->push.strdup_strings = 1; for (ref = push_map; ref; ref = ref->next) { @@ -438,14 +437,14 @@ static int get_push_ref_states_noquery(struct ref_states *states) return 0; states->push.strdup_strings = 1; - if (!remote->push_refspec_nr) { + if (!remote->push.nr) { item = string_list_append(&states->push, _("(matching)")); info = item->util = xcalloc(1, sizeof(struct push_info)); info->status = PUSH_STATUS_NOTQUERIED; info->dest = xstrdup(item->string); } - for (i = 0; i < remote->push_refspec_nr; i++) { - struct refspec *spec = remote->push + i; + for (i = 0; i < remote->push.nr; i++) { + const struct refspec_item *spec = &remote->push.items[i]; if (spec->matching) item = string_list_append(&states->push, _("(matching)")); else if (strlen(spec->src)) @@ -465,7 +464,7 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat { struct ref *ref, *matches; struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map; - struct refspec refspec; + struct refspec_item refspec; refspec.force = 0; refspec.pattern = 1; @@ -518,7 +517,7 @@ static int add_branch_for_removal(const char *refname, const struct object_id *oid, int flags, void *cb_data) { struct branches_for_remote *branches = cb_data; - struct refspec refspec; + struct refspec_item refspec; struct known_remote *kr; memset(&refspec, 0, sizeof(refspec)); @@ -589,12 +588,12 @@ static int migrate_file(struct remote *remote) git_config_set_multivar(buf.buf, remote->url[i], "^$", 0); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.push", remote->name); - for (i = 0; i < remote->push_refspec_nr; i++) - git_config_set_multivar(buf.buf, remote->push_refspec[i], "^$", 0); + for (i = 0; i < remote->push.raw_nr; i++) + git_config_set_multivar(buf.buf, remote->push.raw[i], "^$", 0); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.fetch", remote->name); - for (i = 0; i < remote->fetch_refspec_nr; i++) - git_config_set_multivar(buf.buf, remote->fetch_refspec[i], "^$", 0); + for (i = 0; i < remote->fetch.raw_nr; i++) + git_config_set_multivar(buf.buf, remote->fetch.raw[i], "^$", 0); if (remote->origin == REMOTE_REMOTES) unlink_or_warn(git_path("remotes/%s", remote->name)); else if (remote->origin == REMOTE_BRANCHES) @@ -649,11 +648,11 @@ static int mv(int argc, const char **argv) strbuf_addf(&buf, "remote.%s.fetch", rename.new_name); git_config_set_multivar(buf.buf, NULL, NULL, 1); strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name); - for (i = 0; i < oldremote->fetch_refspec_nr; i++) { + for (i = 0; i < oldremote->fetch.raw_nr; i++) { char *ptr; strbuf_reset(&buf2); - strbuf_addstr(&buf2, oldremote->fetch_refspec[i]); + strbuf_addstr(&buf2, oldremote->fetch.raw[i]); ptr = strstr(buf2.buf, old_remote_context.buf); if (ptr) { refspec_updated = 1; @@ -837,7 +836,7 @@ static int append_ref_to_tracked_list(const char *refname, const struct object_id *oid, int flags, void *cb_data) { struct ref_states *states = cb_data; - struct refspec refspec; + struct refspec_item refspec; if (flags & REF_ISSYMREF) return 0; diff --git a/builtin/reset.c b/builtin/reset.c index 7f1c3f02a3..a862c70fab 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -314,7 +314,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid); if (unborn) { /* reset on unborn branch: treat as reset to empty tree */ - hashcpy(oid.hash, EMPTY_TREE_SHA1_BIN); + oidcpy(&oid, the_hash_algo->empty_tree); } else if (!pathspec.nr) { struct commit *commit; if (get_oid_committish(rev, &oid)) diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 36b2087782..55c0b90441 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -887,8 +887,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) if (read_cache() < 0) die(_("Could not read the index")); if (the_index.split_index) { - const unsigned char *sha1 = the_index.split_index->base_sha1; - const char *path = git_path("sharedindex.%s", sha1_to_hex(sha1)); + const struct object_id *oid = &the_index.split_index->base_oid; + const char *path = git_path("sharedindex.%s", oid_to_hex(oid)); strbuf_reset(&buf); puts(relative_path(path, prefix, &buf)); } diff --git a/builtin/rm.c b/builtin/rm.c index 5b6fc7ee81..65b448ef8e 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -233,8 +233,6 @@ static int check_local_mod(struct object_id *head, int index_only) return errs; } -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; @@ -251,6 +249,7 @@ static struct option builtin_rm_options[] = { int cmd_rm(int argc, const char **argv, const char *prefix) { + struct lock_file lock_file = LOCK_INIT; int i; struct pathspec pathspec; char *seen; diff --git a/builtin/send-pack.c b/builtin/send-pack.c index b5427f75e3..4923b1058c 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -126,8 +126,7 @@ static int send_pack_config(const char *k, const char *v, void *cb) int cmd_send_pack(int argc, const char **argv, const char *prefix) { - int i, nr_refspecs = 0; - const char **refspecs = NULL; + struct refspec rs = REFSPEC_INIT_PUSH; const char *remote_name = NULL; struct remote *remote = NULL; const char *dest = NULL; @@ -189,8 +188,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, send_pack_usage, 0); if (argc > 0) { dest = argv[0]; - refspecs = (const char **)(argv + 1); - nr_refspecs = argc - 1; + refspec_appendn(&rs, argv + 1, argc - 1); } if (!dest) @@ -209,31 +207,23 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) args.push_options = push_options.nr ? &push_options : NULL; if (from_stdin) { - struct argv_array all_refspecs = ARGV_ARRAY_INIT; - - for (i = 0; i < nr_refspecs; i++) - argv_array_push(&all_refspecs, refspecs[i]); - if (args.stateless_rpc) { const char *buf; while ((buf = packet_read_line(0, NULL))) - argv_array_push(&all_refspecs, buf); + refspec_append(&rs, buf); } else { struct strbuf line = STRBUF_INIT; while (strbuf_getline(&line, stdin) != EOF) - argv_array_push(&all_refspecs, line.buf); + refspec_append(&rs, line.buf); strbuf_release(&line); } - - refspecs = all_refspecs.argv; - nr_refspecs = all_refspecs.argc; } /* * --all and --mirror are incompatible; neither makes sense * with any refspecs. */ - if ((nr_refspecs > 0 && (send_all || args.send_mirror)) || + if ((rs.nr > 0 && (send_all || args.send_mirror)) || (send_all && args.send_mirror)) usage_with_options(send_pack_usage, options); @@ -275,8 +265,6 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) BUG("unknown protocol version"); } - transport_verify_remote_names(nr_refspecs, refspecs); - local_refs = get_local_heads(); flags = MATCH_REFS_NONE; @@ -287,7 +275,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) flags |= MATCH_REFS_MIRROR; /* match them up */ - if (match_push_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags)) + if (match_push_refs(local_refs, &remote_refs, &rs, flags)) return -1; if (!is_empty_cas(&cas)) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index c2403a915f..bd250ca216 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -12,6 +12,7 @@ #include "run-command.h" #include "remote.h" #include "refs.h" +#include "refspec.h" #include "connect.h" #include "revision.h" #include "diffcore.h" @@ -1065,7 +1066,7 @@ static int module_deinit(int argc, const char **argv, const char *prefix) } static int clone_submodule(const char *path, const char *gitdir, const char *url, - const char *depth, struct string_list *reference, + const char *depth, struct string_list *reference, int dissociate, int quiet, int progress) { struct child_process cp = CHILD_PROCESS_INIT; @@ -1084,6 +1085,8 @@ static int clone_submodule(const char *path, const char *gitdir, const char *url argv_array_pushl(&cp.args, "--reference", item->string, NULL); } + if (dissociate) + argv_array_push(&cp.args, "--dissociate"); if (gitdir && *gitdir) argv_array_pushl(&cp.args, "--separate-git-dir", gitdir, NULL); @@ -1199,6 +1202,7 @@ static int module_clone(int argc, const char **argv, const char *prefix) char *p, *path = NULL, *sm_gitdir; struct strbuf sb = STRBUF_INIT; struct string_list reference = STRING_LIST_INIT_NODUP; + int dissociate = 0; char *sm_alternate = NULL, *error_strategy = NULL; struct option module_clone_options[] = { @@ -1217,6 +1221,8 @@ static int module_clone(int argc, const char **argv, const char *prefix) OPT_STRING_LIST(0, "reference", &reference, N_("repo"), N_("reference repository")), + OPT_BOOL(0, "dissociate", &dissociate, + N_("use --reference only while cloning")), OPT_STRING(0, "depth", &depth, N_("string"), N_("depth for shallow clones")), @@ -1256,7 +1262,7 @@ static int module_clone(int argc, const char **argv, const char *prefix) prepare_possible_alternates(name, &reference); - if (clone_submodule(path, sm_gitdir, url, depth, &reference, + if (clone_submodule(path, sm_gitdir, url, depth, &reference, dissociate, quiet, progress)) die(_("clone of '%s' into submodule path '%s' failed"), url, path); @@ -1308,6 +1314,7 @@ struct submodule_update_clone { int quiet; int recommend_shallow; struct string_list references; + int dissociate; const char *depth; const char *recursive_prefix; const char *prefix; @@ -1323,7 +1330,7 @@ struct submodule_update_clone { int failed_clones_nr, failed_clones_alloc; }; #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \ - SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, \ + SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \ NULL, NULL, NULL, \ STRING_LIST_INIT_DUP, 0, NULL, 0, 0} @@ -1450,6 +1457,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, for_each_string_list_item(item, &suc->references) argv_array_pushl(&child->args, "--reference", item->string, NULL); } + if (suc->dissociate) + argv_array_push(&child->args, "--dissociate"); if (suc->depth) argv_array_push(&child->args, suc->depth); @@ -1583,6 +1592,8 @@ static int update_clone(int argc, const char **argv, const char *prefix) N_("rebase, merge, checkout or none")), OPT_STRING_LIST(0, "reference", &suc.references, N_("repo"), N_("reference repository")), + OPT_BOOL(0, "dissociate", &suc.dissociate, + N_("use --reference only while cloning")), OPT_STRING(0, "depth", &suc.depth, "<depth>", N_("Create a shallow clone truncated to the " "specified number of revisions")), @@ -1743,13 +1754,14 @@ static int push_check(int argc, const char **argv, const char *prefix) /* Check the refspec */ if (argc > 2) { - int i, refspec_nr = argc - 2; + int i; struct ref *local_refs = get_local_heads(); - struct refspec *refspec = parse_push_refspec(refspec_nr, - argv + 2); + struct refspec refspec = REFSPEC_INIT_PUSH; - for (i = 0; i < refspec_nr; i++) { - struct refspec *rs = refspec + i; + refspec_appendn(&refspec, argv + 2, argc - 2); + + for (i = 0; i < refspec.nr; i++) { + const struct refspec_item *rs = &refspec.items[i]; if (rs->pattern || rs->matching) continue; @@ -1776,7 +1788,7 @@ static int push_check(int argc, const char **argv, const char *prefix) rs->src); } } - free_refspec(refspec_nr, refspec); + refspec_clear(&refspec); } free(head); @@ -1825,6 +1837,29 @@ static int is_active(int argc, const char **argv, const char *prefix) return !is_submodule_active(the_repository, argv[1]); } +/* + * Exit non-zero if any of the submodule names given on the command line is + * invalid. If no names are given, filter stdin to print only valid names + * (which is primarily intended for testing). + */ +static int check_name(int argc, const char **argv, const char *prefix) +{ + if (argc > 1) { + while (*++argv) { + if (check_submodule_name(*argv) < 0) + return 1; + } + } else { + struct strbuf buf = STRBUF_INIT; + while (strbuf_getline(&buf, stdin) != EOF) { + if (!check_submodule_name(buf.buf)) + printf("%s\n", buf.buf); + } + strbuf_release(&buf); + } + return 0; +} + #define SUPPORT_SUPER_PREFIX (1<<0) struct cmd_struct { @@ -1850,6 +1885,7 @@ static struct cmd_struct commands[] = { {"push-check", push_check, 0}, {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX}, {"is-active", is_active, 0}, + {"check-name", check_name, 0}, }; int cmd_submodule__helper(int argc, const char **argv, const char *prefix) diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index cfe9019f80..6e81ca8ca2 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -210,7 +210,7 @@ static int check_object(struct object *obj, int type, void *data, struct fsck_op if (!obj_buf) die("Whoops! Cannot find object '%s'", oid_to_hex(&obj->oid)); if (fsck_object(obj, obj_buf->buffer, obj_buf->size, &fsck_options)) - die("Error in object"); + die("fsck error in packed object"); fsck_options.walk = check_object; if (fsck_walk(obj, NULL, &fsck_options)) die("Error on reachable objects of %s", oid_to_hex(&obj->oid)); @@ -572,8 +572,11 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix) unpack_all(); the_hash_algo->update_fn(&ctx, buffer, offset); the_hash_algo->final_fn(oid.hash, &ctx); - if (strict) + if (strict) { write_rest(); + if (fsck_finish(&fsck_options)) + die(_("fsck error in pack objects")); + } if (hashcmp(fill(the_hash_algo->rawsz), oid.hash)) die("final sha1 did not match"); use(the_hash_algo->rawsz); diff --git a/builtin/update-index.c b/builtin/update-index.c index 10d070a76f..a8709a26ec 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -364,10 +364,9 @@ static int process_directory(const char *path, int len, struct stat *st) return error("%s: is a directory - add files inside instead", path); } -static int process_path(const char *path) +static int process_path(const char *path, struct stat *st, int stat_errno) { int pos, len; - struct stat st; const struct cache_entry *ce; len = strlen(path); @@ -391,13 +390,13 @@ static int process_path(const char *path) * First things first: get the stat information, to decide * what to do about the pathname! */ - if (lstat(path, &st) < 0) - return process_lstat_error(path, errno); + if (stat_errno) + return process_lstat_error(path, stat_errno); - if (S_ISDIR(st.st_mode)) - return process_directory(path, len, &st); + if (S_ISDIR(st->st_mode)) + return process_directory(path, len, st); - return add_one_path(ce, path, len, &st); + return add_one_path(ce, path, len, st); } static int add_cacheinfo(unsigned int mode, const struct object_id *oid, @@ -406,7 +405,7 @@ static int add_cacheinfo(unsigned int mode, const struct object_id *oid, int size, len, option; struct cache_entry *ce; - if (!verify_path(path)) + if (!verify_path(path, mode)) return error("Invalid path '%s'", path); len = strlen(path); @@ -449,7 +448,18 @@ static void chmod_path(char flip, const char *path) static void update_one(const char *path) { - if (!verify_path(path)) { + int stat_errno = 0; + struct stat st; + + if (mark_valid_only || mark_skip_worktree_only || force_remove || + mark_fsmonitor_only) + st.st_mode = 0; + else if (lstat(path, &st) < 0) { + st.st_mode = 0; + stat_errno = errno; + } /* else stat is valid */ + + if (!verify_path(path, st.st_mode)) { fprintf(stderr, "Ignoring path %s\n", path); return; } @@ -475,7 +485,7 @@ static void update_one(const char *path) report("remove '%s'", path); return; } - if (process_path(path)) + if (process_path(path, &st, stat_errno)) die("Unable to process path %s", path); report("add '%s'", path); } @@ -545,7 +555,7 @@ static void read_index_info(int nul_term_line) path_name = uq.buf; } - if (!verify_path(path_name)) { + if (!verify_path(path_name, mode)) { fprintf(stderr, "Ignoring path %s\n", path_name); continue; } @@ -1164,7 +1174,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) report(_("Untracked cache enabled for '%s'"), get_git_work_tree()); break; default: - die("BUG: bad untracked_cache value: %d", untracked_cache); + BUG("bad untracked_cache value: %d", untracked_cache); } if (fsmonitor > 0) { diff --git a/bulk-checkin.c b/bulk-checkin.c index c0bc8de107..b7e131c47a 100644 --- a/bulk-checkin.c +++ b/bulk-checkin.c @@ -230,7 +230,7 @@ static int deflate_to_pack(struct bulk_checkin_state *state, * pack, and write into it. */ if (!idx) - die("BUG: should not happen"); + BUG("should not happen"); hashfile_truncate(state->f, &checkpoint); state->offset = checkpoint.offset; finish_bulk_checkin(state); @@ -409,7 +409,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs) int create_bundle(struct bundle_header *header, const char *path, int argc, const char **argv) { - static struct lock_file lock; + struct lock_file lock = LOCK_INIT; int bundle_fd = -1; int bundle_to_stdout; int ref_count = 0; diff --git a/cache-tree.c b/cache-tree.c index 6a555f4d43..25663825b5 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -385,7 +385,7 @@ static int update_one(struct cache_tree *it, /* * "sub" can be an empty tree if all subentries are i-t-a. */ - if (contains_ita && !oidcmp(oid, &empty_tree_oid)) + if (contains_ita && is_empty_tree_oid(oid)) continue; strbuf_grow(&buffer, entlen + 100); @@ -523,7 +523,7 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p) if (0 <= it->entry_count) { if (size < rawsz) goto free_return; - memcpy(it->oid.hash, (const unsigned char*)buf, rawsz); + oidread(&it->oid, (const unsigned char *)buf); buf += rawsz; size -= rawsz; } @@ -324,7 +324,7 @@ struct index_state { drop_cache_tree : 1; struct hashmap name_hash; struct hashmap dir_hash; - unsigned char sha1[20]; + struct object_id oid; struct untracked_cache *untracked; uint64_t fsmonitor_last_update; struct ewah_bitmap *fsmonitor_dirty; @@ -642,7 +642,7 @@ extern int unmerged_index(const struct index_state *); */ extern int index_has_changes(struct strbuf *sb); -extern int verify_path(const char *path); +extern int verify_path(const char *path, unsigned mode); extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change); extern int index_dir_exists(struct index_state *istate, const char *name, int namelen); extern void adjust_dirname_case(struct index_state *istate, char *name); @@ -1017,21 +1017,10 @@ static inline void oidclr(struct object_id *oid) memset(oid->hash, 0, GIT_MAX_RAWSZ); } - -#define EMPTY_TREE_SHA1_HEX \ - "4b825dc642cb6eb9a060e54bf8d69288fbee4904" -#define EMPTY_TREE_SHA1_BIN_LITERAL \ - "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \ - "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04" -extern const struct object_id empty_tree_oid; -#define EMPTY_TREE_SHA1_BIN (empty_tree_oid.hash) - -#define EMPTY_BLOB_SHA1_HEX \ - "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391" -#define EMPTY_BLOB_SHA1_BIN_LITERAL \ - "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \ - "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91" -extern const struct object_id empty_blob_oid; +static inline void oidread(struct object_id *oid, const unsigned char *hash) +{ + memcpy(oid->hash, hash, the_hash_algo->rawsz); +} static inline int is_empty_blob_sha1(const unsigned char *sha1) { @@ -1053,6 +1042,9 @@ static inline int is_empty_tree_oid(const struct object_id *oid) return !oidcmp(oid, the_hash_algo->empty_tree); } +const char *empty_tree_oid_hex(void); +const char *empty_blob_oid_hex(void); + /* set default permissions by passing mode arguments to open(2) */ int git_mkstemps_mode(char *pattern, int suffix_len, int mode); int git_mkstemp_mode(char *pattern, int mode); @@ -1168,7 +1160,15 @@ int normalize_path_copy(char *dst, const char *src); int longest_ancestor_length(const char *path, struct string_list *prefixes); char *strip_path_suffix(const char *path, const char *suffix); int daemon_avoid_alias(const char *path); -extern int is_ntfs_dotgit(const char *name); + +/* + * These functions match their is_hfs_dotgit() counterparts; see utf8.h for + * details. + */ +int is_ntfs_dotgit(const char *name); +int is_ntfs_dotgitmodules(const char *name); +int is_ntfs_dotgitignore(const char *name); +int is_ntfs_dotgitattributes(const char *name); /* * Returns true iff "str" could be confused as a command-line option when @@ -1260,7 +1260,7 @@ extern int has_object_file_with_flags(const struct object_id *oid, int flags); * with the specified name. This function does not respect replace * references. */ -extern int has_loose_object_nonlocal(const unsigned char *sha1); +extern int has_loose_object_nonlocal(const struct object_id *oid); extern void assert_oid_type(const struct object_id *oid, enum object_type expect); @@ -1291,7 +1291,6 @@ static inline int hex2chr(const char *s) #define FALLBACK_DEFAULT_ABBREV 7 struct object_context { - unsigned char tree[20]; unsigned mode; /* * symlink_path is only used by get_tree_entry_follow_symlinks, @@ -1558,7 +1557,6 @@ struct pack_window { struct pack_entry { off_t offset; - unsigned char sha1[20]; struct packed_git *p; }; @@ -1828,11 +1826,6 @@ extern int ws_blank_line(const char *line, int len, unsigned ws_rule); void overlay_tree_on_index(struct index_state *istate, const char *tree_name, const char *prefix); -char *alias_lookup(const char *alias); -int split_cmdline(char *cmdline, const char ***argv); -/* Takes a negative value returned by split_cmdline */ -const char *split_cmdline_strerror(int cmdline_errno); - /* setup.c */ struct startup_info { int have_repository; diff --git a/checkout.c b/checkout.c index ac42630f74..bdefc888ba 100644 --- a/checkout.c +++ b/checkout.c @@ -1,5 +1,6 @@ #include "cache.h" #include "remote.h" +#include "refspec.h" #include "checkout.h" struct tracking_name_data { @@ -12,8 +13,8 @@ struct tracking_name_data { static int check_tracking_name(struct remote *remote, void *cb_data) { struct tracking_name_data *cb = cb_data; - struct refspec query; - memset(&query, 0, sizeof(struct refspec)); + struct refspec_item query; + memset(&query, 0, sizeof(struct refspec_item)); query.src = cb->src_ref; if (remote_find_tracking(remote, &query) || get_oid(query.dst, cb->dst_oid)) { diff --git a/ci/lib-travisci.sh b/ci/lib-travisci.sh index 109ef280da..ceecc889ca 100755 --- a/ci/lib-travisci.sh +++ b/ci/lib-travisci.sh @@ -99,6 +99,9 @@ export DEFAULT_TEST_TARGET=prove export GIT_PROVE_OPTS="--timer --jobs 3 --state=failed,slow,save" export GIT_TEST_OPTS="--verbose-log -x" export GIT_TEST_CLONE_2GB=YesPlease +if [ "$jobname" = linux-gcc ]; then + export CC=gcc-8 +fi case "$jobname" in linux-clang|linux-gcc) @@ -174,7 +174,7 @@ static char *color_output(char *out, int len, const struct color *c, char type) break; case COLOR_ANSI: if (len < 2) - die("BUG: color parsing ran out of space"); + BUG("color parsing ran out of space"); *out++ = type; *out++ = '0' + c->value; break; @@ -256,7 +256,7 @@ int color_parse_mem(const char *value, int value_len, char *dst) #undef OUT #define OUT(x) do { \ if (dst == end) \ - die("BUG: color parsing ran out of space"); \ + BUG("color parsing ran out of space"); \ *dst++ = (x); \ } while(0) @@ -214,7 +214,7 @@ void print_columns(const struct string_list *list, unsigned int colopts, display_table(list, colopts, &nopts); break; default: - die("BUG: invalid layout mode %d", COL_LAYOUT(colopts)); + BUG("invalid layout mode %d", COL_LAYOUT(colopts)); } } diff --git a/command-list.txt b/command-list.txt index 835c5890be..e1c26c1bb7 100644 --- a/command-list.txt +++ b/command-list.txt @@ -1,23 +1,58 @@ -# common commands are grouped by themes -# these groups are output by 'git help' in the order declared here. -# map each common command in the command list to one of these groups. -### common groups (do not change this line) -init start a working area (see also: git help tutorial) -worktree work on the current change (see also: git help everyday) -info examine the history and state (see also: git help revisions) -history grow, mark and tweak your common history -remote collaborate (see also: git help workflows) - -### command list (do not change this line) -# command name category [deprecated] [common] +# Command classification list +# --------------------------- +# All supported commands, builtin or external, must be described in +# here. This info is used to list commands in various places. Each +# command is on one line followed by one or more attributes. +# +# The first attribute group is mandatory and indicates the command +# type. This group includes: +# +# mainporcelain +# ancillarymanipulators +# ancillaryinterrogators +# foreignscminterface +# plumbingmanipulators +# plumbinginterrogators +# synchingrepositories +# synchelpers +# purehelpers +# +# The type names are self explanatory. But if you want to see what +# command belongs to what group to get a better picture, have a look +# at "git" man page, "GIT COMMANDS" section. +# +# Commands of type mainporcelain can also optionally have one of these +# attributes: +# +# init +# worktree +# info +# history +# remote +# +# These commands are considered "common" and will show up in "git +# help" output in groups. Uncommon porcelain commands must not +# specify any of these attributes. +# +# "complete" attribute is used to mark that the command should be +# completable by git-completion.bash. Note that by default, +# mainporcelain commands are completable so you don't need this +# attribute. +# +# As part of the Git man page list, the man(5/7) guides are also +# specified here, which can only have "guide" attribute and nothing +# else. +# +### command list (do not change this line, also do not change alignment) +# command name category [category] [category] git-add mainporcelain worktree git-am mainporcelain git-annotate ancillaryinterrogators -git-apply plumbingmanipulators +git-apply plumbingmanipulators complete git-archimport foreignscminterface git-archive mainporcelain git-bisect mainporcelain info -git-blame ancillaryinterrogators +git-blame ancillaryinterrogators complete git-branch mainporcelain history git-bundle mainporcelain git-cat-file plumbinginterrogators @@ -27,7 +62,7 @@ git-check-mailmap purehelpers git-checkout mainporcelain history git-checkout-index plumbingmanipulators git-check-ref-format purehelpers -git-cherry ancillaryinterrogators +git-cherry ancillaryinterrogators complete git-cherry-pick mainporcelain git-citool mainporcelain git-clean mainporcelain @@ -36,7 +71,7 @@ git-column purehelpers git-commit mainporcelain history git-commit-graph plumbingmanipulators git-commit-tree plumbingmanipulators -git-config ancillarymanipulators +git-config ancillarymanipulators complete git-count-objects ancillaryinterrogators git-credential purehelpers git-credential-cache purehelpers @@ -50,7 +85,7 @@ git-diff mainporcelain history git-diff-files plumbinginterrogators git-diff-index plumbinginterrogators git-diff-tree plumbinginterrogators -git-difftool ancillaryinterrogators +git-difftool ancillaryinterrogators complete git-fast-export ancillarymanipulators git-fast-import ancillarymanipulators git-fetch mainporcelain remote @@ -59,20 +94,20 @@ git-filter-branch ancillarymanipulators git-fmt-merge-msg purehelpers git-for-each-ref plumbinginterrogators git-format-patch mainporcelain -git-fsck ancillaryinterrogators +git-fsck ancillaryinterrogators complete git-gc mainporcelain git-get-tar-commit-id ancillaryinterrogators git-grep mainporcelain info git-gui mainporcelain git-hash-object plumbingmanipulators -git-help ancillaryinterrogators +git-help ancillaryinterrogators complete git-http-backend synchingrepositories git-http-fetch synchelpers git-http-push synchelpers git-imap-send foreignscminterface git-index-pack plumbingmanipulators git-init mainporcelain init -git-instaweb ancillaryinterrogators +git-instaweb ancillaryinterrogators complete git-interpret-trailers purehelpers gitk mainporcelain git-log mainporcelain info @@ -86,7 +121,7 @@ git-merge-base plumbinginterrogators git-merge-file plumbingmanipulators git-merge-index plumbingmanipulators git-merge-one-file purehelpers -git-mergetool ancillarymanipulators +git-mergetool ancillarymanipulators complete git-merge-tree ancillaryinterrogators git-mktag plumbingmanipulators git-mktree plumbingmanipulators @@ -107,28 +142,29 @@ git-quiltimport foreignscminterface git-read-tree plumbingmanipulators git-rebase mainporcelain history git-receive-pack synchelpers -git-reflog ancillarymanipulators -git-remote ancillarymanipulators -git-repack ancillarymanipulators -git-replace ancillarymanipulators -git-request-pull foreignscminterface +git-reflog ancillarymanipulators complete +git-remote ancillarymanipulators complete +git-repack ancillarymanipulators complete +git-replace ancillarymanipulators complete +git-request-pull foreignscminterface complete git-rerere ancillaryinterrogators git-reset mainporcelain worktree git-revert mainporcelain git-rev-list plumbinginterrogators git-rev-parse ancillaryinterrogators git-rm mainporcelain worktree -git-send-email foreignscminterface +git-send-email foreignscminterface complete git-send-pack synchingrepositories git-shell synchelpers git-shortlog mainporcelain git-show mainporcelain info -git-show-branch ancillaryinterrogators +git-show-branch ancillaryinterrogators complete git-show-index plumbinginterrogators git-show-ref plumbinginterrogators git-sh-i18n purehelpers git-sh-setup purehelpers git-stash mainporcelain +git-stage complete git-status mainporcelain info git-stripspace purehelpers git-submodule mainporcelain @@ -147,6 +183,22 @@ git-verify-commit ancillaryinterrogators git-verify-pack plumbinginterrogators git-verify-tag ancillaryinterrogators gitweb ancillaryinterrogators -git-whatchanged ancillaryinterrogators +git-whatchanged ancillaryinterrogators complete git-worktree mainporcelain git-write-tree plumbingmanipulators +gitattributes guide +gitcli guide +gitcore-tutorial guide +gitcvs-migration guide +gitdiffcore guide +giteveryday guide +gitglossary guide +githooks guide +gitignore guide +gitmodules guide +gitnamespaces guide +gitrepository-layout guide +gitrevisions guide +gittutorial-2 guide +gittutorial guide +gitworkflows guide @@ -358,7 +358,7 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s if (tail <= bufptr + tree_entry_len + 1 || memcmp(bufptr, "tree ", 5) || bufptr[tree_entry_len] != '\n') return error("bogus commit object %s", oid_to_hex(&item->object.oid)); - if (get_sha1_hex(bufptr + 5, parent.hash) < 0) + if (get_oid_hex(bufptr + 5, &parent) < 0) return error("bad tree pointer in commit %s", oid_to_hex(&item->object.oid)); item->maybe_tree = lookup_tree(&parent); @@ -370,7 +370,7 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s struct commit *new_parent; if (tail <= bufptr + parent_entry_len + 1 || - get_sha1_hex(bufptr + 7, parent.hash) || + get_oid_hex(bufptr + 7, &parent) || bufptr[parent_entry_len] != '\n') return error("bad parents in commit %s", oid_to_hex(&item->object.oid)); bufptr += parent_entry_len + 1; @@ -19,7 +19,6 @@ struct commit_list { struct commit { struct object object; void *util; - unsigned int index; timestamp_t date; struct commit_list *parents; @@ -30,6 +29,7 @@ struct commit { */ struct tree *maybe_tree; uint32_t graph_pos; + unsigned int index; }; extern int save_commit_buffer; @@ -103,7 +103,7 @@ static int config_buf_ungetc(int c, struct config_source *conf) if (conf->u.buf.pos > 0) { conf->u.buf.pos--; if (conf->u.buf.buf[conf->u.buf.pos] != c) - die("BUG: config_buf can only ungetc the same character"); + BUG("config_buf can only ungetc the same character"); return c; } @@ -190,7 +190,7 @@ static int prepare_include_condition_pattern(struct strbuf *pat) strbuf_realpath(&path, cf->path, 1); slash = find_last_dir_sep(path.buf); if (!slash) - die("BUG: how is this possible?"); + BUG("how is this possible?"); strbuf_splice(pat, 0, 1, path.buf, slash - path.buf); prefix = slash - path.buf + 1 /* slash */; } else if (!is_absolute_path(pat->buf)) @@ -1814,7 +1814,7 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha l_item->value_index = e->value_list.nr - 1; if (!cf) - die("BUG: configset_add_value has no source"); + BUG("configset_add_value has no source"); if (cf->name) { kv_info->filename = strintern(cf->name); kv_info->linenr = cf->linenr; @@ -2333,6 +2333,19 @@ struct config_store_data { unsigned int key_seen:1, section_seen:1, is_keys_section:1; }; +static void config_store_data_clear(struct config_store_data *store) +{ + free(store->key); + if (store->value_regex != NULL && + store->value_regex != CONFIG_REGEX_NONE) { + regfree(store->value_regex); + free(store->value_regex); + } + free(store->parsed); + free(store->seen); + memset(store, 0, sizeof(*store)); +} + static int matches(const char *key, const char *value, const struct config_store_data *store) { @@ -2359,7 +2372,7 @@ static int store_aux_event(enum config_event_t type, if (type == CONFIG_EVENT_SECTION) { if (cf->var.len < 2 || cf->var.buf[cf->var.len - 1] != '.') - BUG("Invalid section name '%s'", cf->var.buf); + return error("invalid section name '%s'", cf->var.buf); /* Is this the section we were looking for? */ store->is_keys_section = @@ -2667,7 +2680,6 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, fd = hold_lock_file_for_update(&lock, config_filename, 0); if (fd < 0) { error_errno("could not lock config file %s", config_filename); - free(store.key); ret = CONFIG_NO_LOCK; goto out_free; } @@ -2677,8 +2689,6 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, */ in_fd = open(config_filename, O_RDONLY); if ( in_fd < 0 ) { - free(store.key); - if ( ENOENT != errno ) { error_errno("opening %s", config_filename); ret = CONFIG_INVALID_FILE; /* same as "invalid config file" */ @@ -2690,7 +2700,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, goto out_free; } - store.key = (char *)key; + free(store.key); + store.key = xstrdup(key); if (write_section(fd, key, &store) < 0 || write_pair(fd, key, value, &store) < 0) goto write_err_out; @@ -2715,7 +2726,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, if (regcomp(store.value_regex, value_regex, REG_EXTENDED)) { error("invalid pattern: %s", value_regex); - free(store.value_regex); + FREE_AND_NULL(store.value_regex); ret = CONFIG_INVALID_PATTERN; goto out_free; } @@ -2740,23 +2751,10 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, config_filename, &store, &opts)) { error("invalid config file %s", config_filename); - free(store.key); - if (store.value_regex != NULL && - store.value_regex != CONFIG_REGEX_NONE) { - regfree(store.value_regex); - free(store.value_regex); - } ret = CONFIG_INVALID_FILE; goto out_free; } - free(store.key); - if (store.value_regex != NULL && - store.value_regex != CONFIG_REGEX_NONE) { - regfree(store.value_regex); - free(store.value_regex); - } - /* if nothing to unset, or too many matches, error out */ if ((store.seen_nr == 0 && value == NULL) || (store.seen_nr > 1 && multi_replace == 0)) { @@ -2887,6 +2885,7 @@ out_free: munmap(contents, contents_sz); if (in_fd >= 0) close(in_fd); + config_store_data_clear(&store); return ret; write_err_out: @@ -3127,6 +3126,7 @@ out: rollback_lock_file(&lock); out_no_rollback: free(filename_buf); + config_store_data_clear(&store); return ret; } @@ -3208,7 +3208,7 @@ const char *current_config_origin_type(void) else if(cf) type = cf->origin_type; else - die("BUG: current_config_origin_type called outside config callback"); + BUG("current_config_origin_type called outside config callback"); switch (type) { case CONFIG_ORIGIN_BLOB: @@ -3222,7 +3222,7 @@ const char *current_config_origin_type(void) case CONFIG_ORIGIN_CMDLINE: return "command line"; default: - die("BUG: unknown config origin type"); + BUG("unknown config origin type"); } } @@ -3234,7 +3234,7 @@ const char *current_config_name(void) else if (cf) name = cf->name; else - die("BUG: current_config_name called outside config callback"); + BUG("current_config_name called outside config callback"); return name ? name : ""; } @@ -14,6 +14,7 @@ #include "strbuf.h" #include "version.h" #include "protocol.h" +#include "alias.h" static char *server_capabilities_v1; static struct argv_array server_capabilities_v2 = ARGV_ARRAY_INIT; diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 961a0ed76f..12814e9bbf 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -94,6 +94,70 @@ __git () ${__git_dir:+--git-dir="$__git_dir"} "$@" 2>/dev/null } +# Removes backslash escaping, single quotes and double quotes from a word, +# stores the result in the variable $dequoted_word. +# 1: The word to dequote. +__git_dequote () +{ + local rest="$1" len ch + + dequoted_word="" + + while test -n "$rest"; do + len=${#dequoted_word} + dequoted_word="$dequoted_word${rest%%[\\\'\"]*}" + rest="${rest:$((${#dequoted_word}-$len))}" + + case "${rest:0:1}" in + \\) + ch="${rest:1:1}" + case "$ch" in + $'\n') + ;; + *) + dequoted_word="$dequoted_word$ch" + ;; + esac + rest="${rest:2}" + ;; + \') + rest="${rest:1}" + len=${#dequoted_word} + dequoted_word="$dequoted_word${rest%%\'*}" + rest="${rest:$((${#dequoted_word}-$len+1))}" + ;; + \") + rest="${rest:1}" + while test -n "$rest" ; do + len=${#dequoted_word} + dequoted_word="$dequoted_word${rest%%[\\\"]*}" + rest="${rest:$((${#dequoted_word}-$len))}" + case "${rest:0:1}" in + \\) + ch="${rest:1:1}" + case "$ch" in + \"|\\|\$|\`) + dequoted_word="$dequoted_word$ch" + ;; + $'\n') + ;; + *) + dequoted_word="$dequoted_word\\$ch" + ;; + esac + rest="${rest:2}" + ;; + \") + rest="${rest:1}" + break + ;; + esac + done + ;; + esac + done +} + # The following function is based on code from: # # bash_completion - programmable completion functions for bash 3.2+ @@ -346,6 +410,24 @@ __gitcomp_nl () __gitcomp_nl_append "$@" } +# Fills the COMPREPLY array with prefiltered paths without any additional +# processing. +# Callers must take care of providing only paths that match the current path +# to be completed and adding any prefix path components, if necessary. +# 1: List of newline-separated matching paths, complete with all prefix +# path componens. +__gitcomp_file_direct () +{ + local IFS=$'\n' + + COMPREPLY=($1) + + # use a hack to enable file mode in bash < 4 + compopt -o filenames +o nospace 2>/dev/null || + compgen -f /non-existing-dir/ >/dev/null || + true +} + # Generates completion reply with compgen from newline-separated possible # completion filenames. # It accepts 1 to 3 arguments: @@ -365,7 +447,8 @@ __gitcomp_file () # use a hack to enable file mode in bash < 4 compopt -o filenames +o nospace 2>/dev/null || - compgen -f /non-existing-dir/ > /dev/null + compgen -f /non-existing-dir/ >/dev/null || + true } # Execute 'git ls-files', unless the --committable option is specified, in @@ -375,10 +458,12 @@ __gitcomp_file () __git_ls_files_helper () { if [ "$2" == "--committable" ]; then - __git -C "$1" diff-index --name-only --relative HEAD + __git -C "$1" -c core.quotePath=false diff-index \ + --name-only --relative HEAD -- "${3//\\/\\\\}*" else # NOTE: $2 is not quoted in order to support multiple options - __git -C "$1" ls-files --exclude-standard $2 + __git -C "$1" -c core.quotePath=false ls-files \ + --exclude-standard $2 -- "${3//\\/\\\\}*" fi } @@ -389,12 +474,103 @@ __git_ls_files_helper () # If provided, only files within the specified directory are listed. # Sub directories are never recursed. Path must have a trailing # slash. +# 3: List only paths matching this path component (optional). __git_index_files () { - local root="${2-.}" file + local root="$2" match="$3" + + __git_ls_files_helper "$root" "$1" "$match" | + awk -F / -v pfx="${2//\\/\\\\}" '{ + paths[$1] = 1 + } + END { + for (p in paths) { + if (substr(p, 1, 1) != "\"") { + # No special characters, easy! + print pfx p + continue + } + + # The path is quoted. + p = dequote(p) + if (p == "") + continue + + # Even when a directory name itself does not contain + # any special characters, it will still be quoted if + # any of its (stripped) trailing path components do. + # Because of this we may have seen the same direcory + # both quoted and unquoted. + if (p in paths) + # We have seen the same directory unquoted, + # skip it. + continue + else + print pfx p + } + } + function dequote(p, bs_idx, out, esc, esc_idx, dec) { + # Skip opening double quote. + p = substr(p, 2) + + # Interpret backslash escape sequences. + while ((bs_idx = index(p, "\\")) != 0) { + out = out substr(p, 1, bs_idx - 1) + esc = substr(p, bs_idx + 1, 1) + p = substr(p, bs_idx + 2) + + if ((esc_idx = index("abtvfr\"\\", esc)) != 0) { + # C-style one-character escape sequence. + out = out substr("\a\b\t\v\f\r\"\\", + esc_idx, 1) + } else if (esc == "n") { + # Uh-oh, a newline character. + # We cant reliably put a pathname + # containing a newline into COMPREPLY, + # and the newline would create a mess. + # Skip this path. + return "" + } else { + # Must be a \nnn octal value, then. + dec = esc * 64 + \ + substr(p, 1, 1) * 8 + \ + substr(p, 2, 1) + out = out sprintf("%c", dec) + p = substr(p, 3) + } + } + # Drop closing double quote, if there is one. + # (There isnt any if this is a directory, as it was + # already stripped with the trailing path components.) + if (substr(p, length(p), 1) == "\"") + out = out substr(p, 1, length(p) - 1) + else + out = out p + + return out + }' +} + +# __git_complete_index_file requires 1 argument: +# 1: the options to pass to ls-file +# +# The exception is --committable, which finds the files appropriate commit. +__git_complete_index_file () +{ + local dequoted_word pfx="" cur_ + + __git_dequote "$cur" + + case "$dequoted_word" in + ?*/*) + pfx="${dequoted_word%/*}/" + cur_="${dequoted_word##*/}" + ;; + *) + cur_="$dequoted_word" + esac - __git_ls_files_helper "$root" "$1" | - cut -f1 -d/ | sort | uniq + __gitcomp_file_direct "$(__git_index_files "$1" "$pfx" "$cur_")" } # Lists branches from the local repository. @@ -713,26 +889,6 @@ __git_complete_revlist_file () esac } - -# __git_complete_index_file requires 1 argument: -# 1: the options to pass to ls-file -# -# The exception is --committable, which finds the files appropriate commit. -__git_complete_index_file () -{ - local pfx="" cur_="$cur" - - case "$cur_" in - ?*/*) - pfx="${cur_%/*}" - cur_="${cur_##*/}" - pfx="${pfx}/" - ;; - esac - - __gitcomp_file "$(__git_index_files "$1" ${pfx:+"$pfx"})" "$pfx" "$cur_" -} - __git_complete_file () { __git_complete_revlist_file @@ -833,127 +989,11 @@ __git_complete_strategy () return 1 } -__git_commands () { - if test -n "${GIT_TESTING_COMMAND_COMPLETION:-}" - then - printf "%s" "${GIT_TESTING_COMMAND_COMPLETION}" - else - git help -a|egrep '^ [a-zA-Z0-9]' - fi -} - -__git_list_all_commands () -{ - local i IFS=" "$'\n' - for i in $(__git_commands) - do - case $i in - *--*) : helper pattern;; - *) echo $i;; - esac - done -} - __git_all_commands= __git_compute_all_commands () { test -n "$__git_all_commands" || - __git_all_commands=$(__git_list_all_commands) -} - -__git_list_porcelain_commands () -{ - local i IFS=" "$'\n' - __git_compute_all_commands - for i in $__git_all_commands - do - case $i in - *--*) : helper pattern;; - applymbox) : ask gittus;; - applypatch) : ask gittus;; - archimport) : import;; - cat-file) : plumbing;; - check-attr) : plumbing;; - check-ignore) : plumbing;; - check-mailmap) : plumbing;; - check-ref-format) : plumbing;; - checkout-index) : plumbing;; - column) : internal helper;; - commit-graph) : plumbing;; - commit-tree) : plumbing;; - count-objects) : infrequent;; - credential) : credentials;; - credential-*) : credentials helper;; - cvsexportcommit) : export;; - cvsimport) : import;; - cvsserver) : daemon;; - daemon) : daemon;; - diff-files) : plumbing;; - diff-index) : plumbing;; - diff-tree) : plumbing;; - fast-import) : import;; - fast-export) : export;; - fsck-objects) : plumbing;; - fetch-pack) : plumbing;; - fmt-merge-msg) : plumbing;; - for-each-ref) : plumbing;; - hash-object) : plumbing;; - http-*) : transport;; - index-pack) : plumbing;; - init-db) : deprecated;; - local-fetch) : plumbing;; - ls-files) : plumbing;; - ls-remote) : plumbing;; - ls-tree) : plumbing;; - mailinfo) : plumbing;; - mailsplit) : plumbing;; - merge-*) : plumbing;; - mktree) : plumbing;; - mktag) : plumbing;; - pack-objects) : plumbing;; - pack-redundant) : plumbing;; - pack-refs) : plumbing;; - parse-remote) : plumbing;; - patch-id) : plumbing;; - prune) : plumbing;; - prune-packed) : plumbing;; - quiltimport) : import;; - read-tree) : plumbing;; - receive-pack) : plumbing;; - remote-*) : transport;; - rerere) : plumbing;; - rev-list) : plumbing;; - rev-parse) : plumbing;; - runstatus) : plumbing;; - sh-setup) : internal;; - shell) : daemon;; - show-ref) : plumbing;; - send-pack) : plumbing;; - show-index) : plumbing;; - ssh-*) : transport;; - stripspace) : plumbing;; - symbolic-ref) : plumbing;; - unpack-file) : plumbing;; - unpack-objects) : plumbing;; - update-index) : plumbing;; - update-ref) : plumbing;; - update-server-info) : daemon;; - upload-archive) : plumbing;; - upload-pack) : plumbing;; - write-tree) : plumbing;; - var) : infrequent;; - verify-pack) : infrequent;; - verify-tag) : plumbing;; - *) echo $i;; - esac - done -} - -__git_porcelain_commands= -__git_compute_porcelain_commands () -{ - test -n "$__git_porcelain_commands" || - __git_porcelain_commands=$(__git_list_porcelain_commands) + __git_all_commands=$(git --list-cmds=main,others,alias,nohelpers) } # Lists all set config variables starting with the given section prefix, @@ -971,11 +1011,6 @@ __git_pretty_aliases () __git_get_config_variables "pretty" } -__git_aliases () -{ - __git_get_config_variables "alias" -} - # __git_aliased_command requires 1 argument __git_aliased_command () { @@ -1583,13 +1618,12 @@ _git_help () return ;; esac - __git_compute_all_commands - __gitcomp "$__git_all_commands $(__git_aliases) - attributes cli core-tutorial cvs-migration - diffcore everyday gitk glossary hooks ignore modules - namespaces repository-layout revisions tutorial tutorial-2 - workflows - " + if test -n "$GIT_TESTING_ALL_COMMAND_LIST" + then + __gitcomp "$GIT_TESTING_ALL_COMMAND_LIST $(git --list-cmds=alias,list-guide) gitk" + else + __gitcomp "$(git --list-cmds=main,nohelpers,alias,list-guide) gitk" + fi } _git_init () @@ -3058,7 +3092,7 @@ __git_complete_common () { __git_cmds_with_parseopt_helper= __git_support_parseopt_helper () { test -n "$__git_cmds_with_parseopt_helper" || - __git_cmds_with_parseopt_helper="$(__git --list-parseopt-builtins)" + __git_cmds_with_parseopt_helper="$(__git --list-cmds=parseopt)" case " $__git_cmds_with_parseopt_helper " in *" $1 "*) @@ -3144,8 +3178,14 @@ __git_main () --help " ;; - *) __git_compute_porcelain_commands - __gitcomp "$__git_porcelain_commands $(__git_aliases)" ;; + *) + if test -n "$GIT_TESTING_PORCELAIN_COMMAND_LIST" + then + __gitcomp "$GIT_TESTING_PORCELAIN_COMMAND_LIST" + else + __gitcomp "$(git --list-cmds=list-mainporcelain,others,nohelpers,alias,list-complete,config)" + fi + ;; esac return fi @@ -3232,6 +3272,15 @@ if [[ -n ${ZSH_VERSION-} ]]; then compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0 } + __gitcomp_file_direct () + { + emulate -L zsh + + local IFS=$'\n' + compset -P '*[=:]' + compadd -Q -f -- ${=1} && _ret=0 + } + __gitcomp_file () { emulate -L zsh diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh index c3521fbfc4..53cb0f934f 100644 --- a/contrib/completion/git-completion.zsh +++ b/contrib/completion/git-completion.zsh @@ -93,6 +93,15 @@ __gitcomp_nl_append () compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0 } +__gitcomp_file_direct () +{ + emulate -L zsh + + local IFS=$'\n' + compset -P '*[=:]' + compadd -Q -f -- ${=1} && _ret=0 +} + __gitcomp_file () { emulate -L zsh diff --git a/contrib/credential/netrc/Makefile b/contrib/credential/netrc/Makefile index 51b76138a5..0ffa407191 100644 --- a/contrib/credential/netrc/Makefile +++ b/contrib/credential/netrc/Makefile @@ -1,5 +1,5 @@ test: - ./test.pl + ./t-git-credential-netrc.sh testverbose: - ./test.pl -d -v + ./t-git-credential-netrc.sh -d -v diff --git a/contrib/credential/netrc/git-credential-netrc b/contrib/credential/netrc/git-credential-netrc index 1571a7b269..0b9a94102e 100755 --- a/contrib/credential/netrc/git-credential-netrc +++ b/contrib/credential/netrc/git-credential-netrc @@ -2,11 +2,13 @@ use strict; use warnings; +use autodie; use Getopt::Long; use File::Basename; +use Git; -my $VERSION = "0.1"; +my $VERSION = "0.2"; my %options = ( help => 0, @@ -54,6 +56,7 @@ GetOptions(\%options, "insecure|k", "verbose|v", "file|f=s@", + 'gpg|g:s', ); if ($options{help}) { @@ -62,27 +65,31 @@ if ($options{help}) { print <<EOHIPPUS; -$0 [-f AUTHFILE1] [-f AUTHFILEN] [-d] [-v] [-k] get +$0 [(-f <authfile>)...] [-g <program>] [-d] [-v] [-k] get Version $VERSION by tzz\@lifelogs.com. License: BSD. Options: - -f|--file AUTHFILE : specify netrc-style files. Files with the .gpg extension - will be decrypted by GPG before parsing. Multiple -f - arguments are OK. They are processed in order, and the - first matching entry found is returned via the credential - helper protocol (see below). + -f|--file <authfile>: specify netrc-style files. Files with the .gpg + extension will be decrypted by GPG before parsing. + Multiple -f arguments are OK. They are processed in + order, and the first matching entry found is returned + via the credential helper protocol (see below). - When no -f option is given, .authinfo.gpg, .netrc.gpg, - .authinfo, and .netrc files in your home directory are used - in this order. + When no -f option is given, .authinfo.gpg, .netrc.gpg, + .authinfo, and .netrc files in your home directory are + used in this order. - -k|--insecure : ignore bad file ownership or permissions + -g|--gpg <program> : specify the program for GPG. By default, this is the + value of gpg.program in the git repository or global + option or gpg. - -d|--debug : turn on debugging (developer info) + -k|--insecure : ignore bad file ownership or permissions - -v|--verbose : be more verbose (show files and information found) + -d|--debug : turn on debugging (developer info) + + -v|--verbose : be more verbose (show files and information found) To enable this credential helper: @@ -99,8 +106,9 @@ in the path.) git config credential.helper '$shortname -f AUTHFILE -v' -Only "get" mode is supported by this credential helper. It opens every AUTHFILE -and looks for the first entry that matches the requested search criteria: +Only "get" mode is supported by this credential helper. It opens every +<authfile> and looks for the first entry that matches the requested search +criteria: 'port|protocol': The protocol that will be used (e.g., https). (protocol=X) @@ -120,7 +128,7 @@ host=github.com protocol=https username=tzz -this credential helper will look for the first entry in every AUTHFILE that +this credential helper will look for the first entry in every <authfile> that matches machine github.com port https login tzz @@ -137,8 +145,8 @@ Then, the helper will print out whatever tokens it got from the entry, including back to "protocol". Any redundant entry tokens (part of the original query) are skipped. -Again, note that only the first matching entry from all the AUTHFILEs, processed -in the sequence given on the command line, is used. +Again, note that only the first matching entry from all the <authfile>s, +processed in the sequence given on the command line, is used. Netrc/authinfo tokens can be quoted as 'STRING' or "STRING". @@ -152,7 +160,7 @@ EOHIPPUS my $mode = shift @ARGV; # Credentials must get a parameter, so die if it's missing. -die "Syntax: $0 [-f AUTHFILE1] [-f AUTHFILEN] [-d] get" unless defined $mode; +die "Syntax: $0 [(-f <authfile>)...] [-d] get" unless defined $mode; # Only support 'get' mode; with any other unsupported ones we just exit. exit 0 unless $mode eq 'get'; @@ -172,6 +180,8 @@ unless (scalar @$files) { $files = $options{file} = [ map { glob $_ } @candidates ]; } +load_config(\%options); + my $query = read_credential_data_from_stdin(); FILE: @@ -233,7 +243,7 @@ sub load_netrc { my $io; if ($gpgmode) { - my @cmd = (qw(gpg --decrypt), $file); + my @cmd = ($options{'gpg'}, qw(--decrypt), $file); log_verbose("Using GPG to open $file: [@cmd]"); open $io, "-|", @cmd; } else { @@ -410,6 +420,14 @@ sub print_credential_data { printf "%s=%s\n", $git_token, $entry->{$git_token}; } } +sub load_config { + # load settings from git config + my $options = shift; + # set from command argument, gpg.program option, or default to gpg + $options->{'gpg'} //= Git->repository()->config('gpg.program') + // 'gpg'; + log_verbose("using $options{'gpg'} for GPG operations"); +} sub log_verbose { return unless $options{verbose}; printf STDERR @_; diff --git a/contrib/credential/netrc/t-git-credential-netrc.sh b/contrib/credential/netrc/t-git-credential-netrc.sh new file mode 100755 index 0000000000..58191a62f8 --- /dev/null +++ b/contrib/credential/netrc/t-git-credential-netrc.sh @@ -0,0 +1,31 @@ +#!/bin/sh +( + cd ../../../t + test_description='git-credential-netrc' + . ./test-lib.sh + + if ! test_have_prereq PERL; then + skip_all='skipping perl interface tests, perl not available' + test_done + fi + + perl -MTest::More -e 0 2>/dev/null || { + skip_all="Perl Test::More unavailable, skipping test" + test_done + } + + # set up test repository + + test_expect_success \ + 'set up test repository' \ + 'git config --add gpg.program test.git-config-gpg' + + # The external test will outputs its own plan + test_external_has_tap=1 + + test_external \ + 'git-credential-netrc' \ + perl "$TEST_DIRECTORY"/../contrib/credential/netrc/test.pl + + test_done +) diff --git a/contrib/credential/netrc/test.command-option-gpg b/contrib/credential/netrc/test.command-option-gpg new file mode 100755 index 0000000000..d8f1285d41 --- /dev/null +++ b/contrib/credential/netrc/test.command-option-gpg @@ -0,0 +1,2 @@ +#!/bin/sh +echo machine command-option-gpg login username password password diff --git a/contrib/credential/netrc/test.git-config-gpg b/contrib/credential/netrc/test.git-config-gpg new file mode 100755 index 0000000000..65cf594c20 --- /dev/null +++ b/contrib/credential/netrc/test.git-config-gpg @@ -0,0 +1,2 @@ +#!/bin/sh +echo machine git-config-gpg login username password password diff --git a/contrib/credential/netrc/test.netrc.gpg b/contrib/credential/netrc/test.netrc.gpg new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/contrib/credential/netrc/test.netrc.gpg diff --git a/contrib/credential/netrc/test.pl b/contrib/credential/netrc/test.pl index 169b6463c3..1e1001030e 100755 --- a/contrib/credential/netrc/test.pl +++ b/contrib/credential/netrc/test.pl @@ -1,83 +1,115 @@ #!/usr/bin/perl +use lib (split(/:/, $ENV{GITPERLLIB})); use warnings; use strict; -use Test; +use Test::More qw(no_plan); +use File::Basename; +use File::Spec::Functions qw(:DEFAULT rel2abs); use IPC::Open2; -BEGIN { plan tests => 15 } +BEGIN { + # t-git-credential-netrc.sh kicks off our testing, so we have to go + # from there. + Test::More->builder->current_test(1); + Test::More->builder->no_ending(1); +} my @global_credential_args = @ARGV; -my $netrc = './test.netrc'; -print "# Testing insecure file, nothing should be found\n"; +my $scriptDir = dirname rel2abs $0; +my ($netrc, $netrcGpg, $gcNetrc) = map { catfile $scriptDir, $_; } + qw(test.netrc + test.netrc.gpg + git-credential-netrc); +local $ENV{PATH} = join ':' + , $scriptDir + , $ENV{PATH} + ? $ENV{PATH} + : (); + +diag "Testing insecure file, nothing should be found\n"; chmod 0644, $netrc; my $cred = run_credential(['-f', $netrc, 'get'], { host => 'github.com' }); -ok(scalar keys %$cred, 0, "Got 0 keys from insecure file"); +ok(scalar keys %$cred == 0, "Got 0 keys from insecure file"); -print "# Testing missing file, nothing should be found\n"; +diag "Testing missing file, nothing should be found\n"; chmod 0644, $netrc; $cred = run_credential(['-f', '///nosuchfile///', 'get'], { host => 'github.com' }); -ok(scalar keys %$cred, 0, "Got 0 keys from missing file"); +ok(scalar keys %$cred == 0, "Got 0 keys from missing file"); chmod 0600, $netrc; -print "# Testing with invalid data\n"; +diag "Testing with invalid data\n"; $cred = run_credential(['-f', $netrc, 'get'], "bad data"); -ok(scalar keys %$cred, 4, "Got first found keys with bad data"); +ok(scalar keys %$cred == 4, "Got first found keys with bad data"); -print "# Testing netrc file for a missing corovamilkbar entry\n"; +diag "Testing netrc file for a missing corovamilkbar entry\n"; $cred = run_credential(['-f', $netrc, 'get'], { host => 'corovamilkbar' }); -ok(scalar keys %$cred, 0, "Got no corovamilkbar keys"); +ok(scalar keys %$cred == 0, "Got no corovamilkbar keys"); -print "# Testing netrc file for a github.com entry\n"; +diag "Testing netrc file for a github.com entry\n"; $cred = run_credential(['-f', $netrc, 'get'], { host => 'github.com' }); -ok(scalar keys %$cred, 2, "Got 2 Github keys"); +ok(scalar keys %$cred == 2, "Got 2 Github keys"); -ok($cred->{password}, 'carolknows', "Got correct Github password"); -ok($cred->{username}, 'carol', "Got correct Github username"); +is($cred->{password}, 'carolknows', "Got correct Github password"); +is($cred->{username}, 'carol', "Got correct Github username"); -print "# Testing netrc file for a username-specific entry\n"; +diag "Testing netrc file for a username-specific entry\n"; $cred = run_credential(['-f', $netrc, 'get'], { host => 'imap', username => 'bob' }); -ok(scalar keys %$cred, 2, "Got 2 username-specific keys"); +ok(scalar keys %$cred == 2, "Got 2 username-specific keys"); -ok($cred->{password}, 'bobwillknow', "Got correct user-specific password"); -ok($cred->{protocol}, 'imaps', "Got correct user-specific protocol"); +is($cred->{password}, 'bobwillknow', "Got correct user-specific password"); +is($cred->{protocol}, 'imaps', "Got correct user-specific protocol"); -print "# Testing netrc file for a host:port-specific entry\n"; +diag "Testing netrc file for a host:port-specific entry\n"; $cred = run_credential(['-f', $netrc, 'get'], { host => 'imap2:1099' }); -ok(scalar keys %$cred, 2, "Got 2 host:port-specific keys"); +ok(scalar keys %$cred == 2, "Got 2 host:port-specific keys"); -ok($cred->{password}, 'tzzknow', "Got correct host:port-specific password"); -ok($cred->{username}, 'tzz', "Got correct host:port-specific username"); +is($cred->{password}, 'tzzknow', "Got correct host:port-specific password"); +is($cred->{username}, 'tzz', "Got correct host:port-specific username"); -print "# Testing netrc file that 'host:port kills host' entry\n"; +diag "Testing netrc file that 'host:port kills host' entry\n"; $cred = run_credential(['-f', $netrc, 'get'], { host => 'imap2' }); -ok(scalar keys %$cred, 2, "Got 2 'host:port kills host' keys"); +ok(scalar keys %$cred == 2, "Got 2 'host:port kills host' keys"); + +is($cred->{password}, 'bobwillknow', "Got correct 'host:port kills host' password"); +is($cred->{username}, 'bob', "Got correct 'host:port kills host' username"); + +diag 'Testing netrc file decryption by git config gpg.program setting\n'; +$cred = run_credential( ['-f', $netrcGpg, 'get'] + , { host => 'git-config-gpg' } + ); + +ok(scalar keys %$cred == 2, 'Got keys decrypted by git config option'); + +diag 'Testing netrc file decryption by gpg option\n'; +$cred = run_credential( ['-f', $netrcGpg, '-g', 'test.command-option-gpg', 'get'] + , { host => 'command-option-gpg' } + ); -ok($cred->{password}, 'bobwillknow', "Got correct 'host:port kills host' password"); -ok($cred->{username}, 'bob', "Got correct 'host:port kills host' username"); +ok(scalar keys %$cred == 2, 'Got keys decrypted by command option'); sub run_credential { my $args = shift @_; my $data = shift @_; my $pid = open2(my $chld_out, my $chld_in, - './git-credential-netrc', @global_credential_args, + $gcNetrc, @global_credential_args, @$args); die "Couldn't open pipe to netrc credential helper: $!" unless $pid; diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index d60b4315ed..e800d9f5c9 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -63,6 +63,8 @@ foreach my $tar_file (@ARGV) my $have_top_dir = 1; my ($top_dir, %files); + my $next_path = ''; + while (read(I, $_, 512) == 512) { my ($name, $mode, $uid, $gid, $size, $mtime, $chksum, $typeflag, $linkname, $magic, @@ -70,6 +72,13 @@ foreach my $tar_file (@ARGV) $prefix) = unpack 'Z100 Z8 Z8 Z8 Z12 Z12 Z8 Z1 Z100 Z6 Z2 Z32 Z32 Z8 Z8 Z*', $_; + + unless ($next_path eq '') { + # Recover name from previous extended header + $name = $next_path; + $next_path = ''; + } + last unless length($name); if ($name eq '././@LongLink') { # GNU tar extension @@ -90,13 +99,31 @@ foreach my $tar_file (@ARGV) Z8 Z1 Z100 Z6 Z2 Z32 Z32 Z8 Z8 Z*', $_; } - next if $name =~ m{/\z}; $mode = oct $mode; $size = oct $size; $mtime = oct $mtime; next if $typeflag == 5; # directory - if ($typeflag != 1) { # handle hard links later + if ($typeflag eq 'x') { # extended header + # If extended header, check for path + my $pax_header = ''; + while ($size > 0 && read(I, $_, 512) == 512) { + $pax_header = $pax_header . substr($_, 0, $size); + $size -= 512; + } + + my @lines = split /\n/, $pax_header; + foreach my $line (@lines) { + my ($len, $entry) = split / /, $line; + my ($key, $value) = split /=/, $entry; + if ($key eq 'path') { + $next_path = $value; + } + } + next; + } elsif ($name =~ m{/\z}) { # directory + next; + } elsif ($typeflag != 1) { # handle hard links later print FI "blob\n", "mark :$next_mark\n"; if ($typeflag == 2) { # symbolic link print FI "data ", length($linkname), "\n", @@ -185,7 +185,7 @@ struct date_mode *date_mode_from_type(enum date_mode_type type) { static struct date_mode mode; if (type == DATE_STRFTIME) - die("BUG: cannot create anonymous strftime date_mode struct"); + BUG("cannot create anonymous strftime date_mode struct"); mode.type = type; mode.local = 0; return &mode; @@ -177,7 +177,7 @@ static int parse_submodule_params(struct diff_options *options, const char *valu return 0; } -static int git_config_rename(const char *var, const char *value) +int git_config_rename(const char *var, const char *value) { if (!value) return DIFF_DETECT_RENAME; @@ -1184,7 +1184,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, fputs(o->stat_sep, o->file); break; default: - die("BUG: unknown diff symbol"); + BUG("unknown diff symbol"); } strbuf_release(&sb); } @@ -1343,7 +1343,7 @@ static struct diff_tempfile *claim_diff_tempfile(void) { for (i = 0; i < ARRAY_SIZE(diff_temp); i++) if (!diff_temp[i].name) return diff_temp + i; - die("BUG: diff is failing to clean up its tempfiles"); + BUG("diff is failing to clean up its tempfiles"); } static void remove_tempfile(void) @@ -3472,7 +3472,7 @@ static int reuse_worktree_file(const char *name, const struct object_id *oid, in * objects however would tend to be slower as they need * to be individually opened and inflated. */ - if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(oid->hash)) + if (!FAST_WORKING_DIRECTORY && !want_file && has_object_pack(oid)) return 0; /* @@ -3841,7 +3841,7 @@ static const char *diff_abbrev_oid(const struct object_id *oid, int abbrev) if (abbrev < 0) abbrev = FALLBACK_DEFAULT_ABBREV; if (abbrev > GIT_SHA1_HEXSZ) - die("BUG: oid abbreviation out of range: %d", abbrev); + BUG("oid abbreviation out of range: %d", abbrev); if (abbrev) hex[abbrev] = '\0'; return hex; @@ -3898,13 +3898,14 @@ static void fill_metainfo(struct strbuf *msg, *must_show_header = 0; } if (one && two && oidcmp(&one->oid, &two->oid)) { - int abbrev = o->flags.full_index ? 40 : DEFAULT_ABBREV; + const unsigned hexsz = the_hash_algo->hexsz; + int abbrev = o->flags.full_index ? hexsz : DEFAULT_ABBREV; if (o->flags.binary) { mmfile_t mf; if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) || (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two))) - abbrev = 40; + abbrev = hexsz; } strbuf_addf(msg, "%s%sindex %s..%s", line_prefix, set, diff_abbrev_oid(&one->oid, abbrev), @@ -4139,6 +4140,11 @@ void diff_setup_done(struct diff_options *options) DIFF_FORMAT_NAME_STATUS | DIFF_FORMAT_CHECKDIFF | DIFF_FORMAT_NO_OUTPUT; + /* + * This must be signed because we're comparing against a potentially + * negative value. + */ + const int hexsz = the_hash_algo->hexsz; if (options->set_default) options->set_default(options); @@ -4219,8 +4225,8 @@ void diff_setup_done(struct diff_options *options) */ read_cache(); } - if (40 < options->abbrev) - options->abbrev = 40; /* full */ + if (hexsz < options->abbrev) + options->abbrev = hexsz; /* full */ /* * It does not make sense to show the first hit we happened @@ -4335,7 +4341,7 @@ static int stat_opt(struct diff_options *options, const char **av) int argcount = 1; if (!skip_prefix(arg, "--stat", &arg)) - die("BUG: stat option does not begin with --stat: %s", arg); + BUG("stat option does not begin with --stat: %s", arg); end = (char *)arg; switch (*arg) { @@ -4798,8 +4804,8 @@ int diff_opt_parse(struct diff_options *options, options->abbrev = strtoul(arg, NULL, 10); if (options->abbrev < MINIMUM_ABBREV) options->abbrev = MINIMUM_ABBREV; - else if (40 < options->abbrev) - options->abbrev = 40; + else if (the_hash_algo->hexsz < options->abbrev) + options->abbrev = the_hash_algo->hexsz; } else if ((argcount = parse_long_opt("src-prefix", av, &optarg))) { options->a_prefix = optarg; @@ -5520,7 +5526,7 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o) struct diff_queue_struct *q = &diff_queued_diff; if (WSEH_NEW & WS_RULE_MASK) - die("BUG: WS rules bit mask overlaps with diff symbol flags"); + BUG("WS rules bit mask overlaps with diff symbol flags"); if (o->color_moved) o->emitted_symbols = &esm; @@ -6054,7 +6060,7 @@ size_t fill_textconv(struct userdiff_driver *driver, } if (!driver->textconv) - die("BUG: fill_textconv called with non-textconv driver"); + BUG("fill_textconv called with non-textconv driver"); if (driver->textconv_cache && df->oid_valid) { *outbuf = notes_cache_get(driver->textconv_cache, @@ -324,6 +324,7 @@ extern int git_diff_ui_config(const char *var, const char *value, void *cb); extern void diff_setup(struct diff_options *); extern int diff_opt_parse(struct diff_options *, const char **, int, const char *); extern void diff_setup_done(struct diff_options *); +extern int git_config_rename(const char *var, const char *value); #define DIFF_DETECT_RENAME 1 #define DIFF_DETECT_COPY 2 diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c index 239ce5122b..800a899c86 100644 --- a/diffcore-pickaxe.c +++ b/diffcore-pickaxe.c @@ -215,7 +215,6 @@ static void regcomp_or_die(regex_t *regex, const char *needle, int cflags) /* The POSIX.2 people are surely sick */ char errbuf[1024]; regerror(err, regex, errbuf, 1024); - regfree(regex); die("invalid regex: %s", errbuf); } } diff --git a/dir-iterator.c b/dir-iterator.c index 34182a9a1c..f2dcd82fde 100644 --- a/dir-iterator.c +++ b/dir-iterator.c @@ -188,7 +188,7 @@ struct dir_iterator *dir_iterator_begin(const char *path) struct dir_iterator *dir_iterator = &iter->base; if (!path || !*path) - die("BUG: empty path passed to dir_iterator_begin()"); + BUG("empty path passed to dir_iterator_begin()"); strbuf_init(&iter->base.path, PATH_MAX); strbuf_addstr(&iter->base.path, path); @@ -829,7 +829,7 @@ static int add_excludes(const char *fname, const char *base, int baselen, if (size == 0) { if (oid_stat) { fill_stat_data(&oid_stat->stat, &st); - oidcpy(&oid_stat->oid, &empty_blob_oid); + oidcpy(&oid_stat->oid, the_hash_algo->empty_blob); oid_stat->valid = 1; } close(fd); @@ -1241,11 +1241,11 @@ static void prep_exclude(struct dir_struct *dir, (!untracked || !untracked->valid || /* * .. and .gitignore does not exist before - * (i.e. null exclude_sha1). Then we can skip + * (i.e. null exclude_oid). Then we can skip * loading .gitignore, which would result in * ENOENT anyway. */ - !is_null_sha1(untracked->exclude_sha1))) { + !is_null_oid(&untracked->exclude_oid))) { /* * dir->basebuf gets reused by the traversal, but we * need fname to remain unchanged to ensure the src @@ -1276,9 +1276,9 @@ static void prep_exclude(struct dir_struct *dir, * order, though, if you do that. */ if (untracked && - hashcmp(oid_stat.oid.hash, untracked->exclude_sha1)) { + oidcmp(&oid_stat.oid, &untracked->exclude_oid)) { invalidate_gitignore(dir->untracked, untracked); - hashcpy(untracked->exclude_sha1, oid_stat.oid.hash); + oidcpy(&untracked->exclude_oid, &oid_stat.oid); } dir->exclude_stack = stk; current = stk->baselen; @@ -2623,9 +2623,10 @@ static void write_one_dir(struct untracked_cache_dir *untracked, stat_data_to_disk(&stat_data, &untracked->stat_data); strbuf_add(&wd->sb_stat, &stat_data, sizeof(stat_data)); } - if (!is_null_sha1(untracked->exclude_sha1)) { + if (!is_null_oid(&untracked->exclude_oid)) { ewah_set(wd->sha1_valid, i); - strbuf_add(&wd->sb_sha1, untracked->exclude_sha1, 20); + strbuf_add(&wd->sb_sha1, untracked->exclude_oid.hash, + the_hash_algo->rawsz); } intlen = encode_varint(untracked->untracked_nr, intbuf); @@ -2826,16 +2827,16 @@ static void read_stat(size_t pos, void *cb) ud->valid = 1; } -static void read_sha1(size_t pos, void *cb) +static void read_oid(size_t pos, void *cb) { struct read_data *rd = cb; struct untracked_cache_dir *ud = rd->ucd[pos]; - if (rd->data + 20 > rd->end) { + if (rd->data + the_hash_algo->rawsz > rd->end) { rd->data = rd->end + 1; return; } - hashcpy(ud->exclude_sha1, rd->data); - rd->data += 20; + hashcpy(ud->exclude_oid.hash, rd->data); + rd->data += the_hash_algo->rawsz; } static void load_oid_stat(struct oid_stat *oid_stat, const unsigned char *data, @@ -2918,7 +2919,7 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long ewah_each_bit(rd.check_only, set_check_only, &rd); rd.data = next + len; ewah_each_bit(rd.valid, read_stat, &rd); - ewah_each_bit(rd.sha1_valid, read_sha1, &rd); + ewah_each_bit(rd.sha1_valid, read_oid, &rd); next = rd.data; done: @@ -2993,7 +2994,7 @@ void untracked_cache_invalidate_path(struct index_state *istate, { if (!istate->untracked || !istate->untracked->root) return; - if (!safe_path && !verify_path(path)) + if (!safe_path && !verify_path(path, 0)) return; invalidate_one_component(istate->untracked, istate->untracked->root, path, strlen(path)); @@ -3,6 +3,7 @@ /* See Documentation/technical/api-directory-listing.txt */ +#include "cache.h" #include "strbuf.h" struct dir_entry { @@ -118,8 +119,8 @@ struct untracked_cache_dir { /* all data except 'dirs' in this struct are good */ unsigned int valid : 1; unsigned int recurse : 1; - /* null SHA-1 means this directory does not have .gitignore */ - unsigned char exclude_sha1[20]; + /* null object ID means this directory does not have .gitignore */ + struct object_id exclude_oid; char name[FLEX_ARRAY]; }; diff --git a/fast-import.c b/fast-import.c index b2338fa8eb..4d55910ab9 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1817,7 +1817,7 @@ static void dump_marks_helper(FILE *f, static void dump_marks(void) { - static struct lock_file mark_lock; + struct lock_file mark_lock = LOCK_INIT; FILE *f; if (!export_marks_file || (import_marks_file && !import_marks_file_done)) diff --git a/fetch-pack.c b/fetch-pack.c index 490c38f833..a320ce9872 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -1198,14 +1198,29 @@ static int send_fetch_request(int fd_out, const struct fetch_pack_args *args, else if (is_repository_shallow() || args->deepen) die(_("Server does not support shallow requests")); + /* Add filter */ + if (server_supports_feature("fetch", "filter", 0) && + args->filter_options.choice) { + print_verbose(args, _("Server supports filter")); + packet_buf_write(&req_buf, "filter %s", + args->filter_options.filter_spec); + } else if (args->filter_options.choice) { + warning("filtering not recognized by server, ignoring"); + } + /* add wants */ add_wants(wants, &req_buf); - /* Add all of the common commits we've found in previous rounds */ - add_common(&req_buf, common); + if (args->no_dependents) { + packet_buf_write(&req_buf, "done"); + ret = 1; + } else { + /* Add all of the common commits we've found in previous rounds */ + add_common(&req_buf, common); - /* Add initial haves */ - ret = add_haves(&req_buf, haves_to_send, in_vain); + /* Add initial haves */ + ret = add_haves(&req_buf, haves_to_send, in_vain); + } /* Send request */ packet_buf_flush(&req_buf); @@ -10,6 +10,13 @@ #include "utf8.h" #include "sha1-array.h" #include "decorate.h" +#include "oidset.h" +#include "packfile.h" +#include "submodule-config.h" +#include "config.h" + +static struct oidset gitmodules_found = OIDSET_INIT; +static struct oidset gitmodules_done = OIDSET_INIT; #define FSCK_FATAL -1 #define FSCK_INFO -2 @@ -44,6 +51,7 @@ FUNC(MISSING_TAG_ENTRY, ERROR) \ FUNC(MISSING_TAG_OBJECT, ERROR) \ FUNC(MISSING_TREE, ERROR) \ + FUNC(MISSING_TREE_OBJECT, ERROR) \ FUNC(MISSING_TYPE, ERROR) \ FUNC(MISSING_TYPE_ENTRY, ERROR) \ FUNC(MULTIPLE_AUTHORS, ERROR) \ @@ -51,6 +59,11 @@ FUNC(TREE_NOT_SORTED, ERROR) \ FUNC(UNKNOWN_TYPE, ERROR) \ FUNC(ZERO_PADDED_DATE, ERROR) \ + FUNC(GITMODULES_MISSING, ERROR) \ + FUNC(GITMODULES_BLOB, ERROR) \ + FUNC(GITMODULES_PARSE, ERROR) \ + FUNC(GITMODULES_NAME, ERROR) \ + FUNC(GITMODULES_SYMLINK, ERROR) \ /* warnings */ \ FUNC(BAD_FILEMODE, WARN) \ FUNC(EMPTY_NAME, WARN) \ @@ -563,10 +576,18 @@ static int fsck_tree(struct tree *item, struct fsck_options *options) has_empty_name |= !*name; has_dot |= !strcmp(name, "."); has_dotdot |= !strcmp(name, ".."); - has_dotgit |= (!strcmp(name, ".git") || - is_hfs_dotgit(name) || - is_ntfs_dotgit(name)); + has_dotgit |= is_hfs_dotgit(name) || is_ntfs_dotgit(name); has_zero_pad |= *(char *)desc.buffer == '0'; + + if (is_hfs_dotgitmodules(name) || is_ntfs_dotgitmodules(name)) { + if (!S_ISLNK(mode)) + oidset_insert(&gitmodules_found, oid); + else + retval += report(options, &item->object, + FSCK_MSG_GITMODULES_SYMLINK, + ".gitmodules is a symbolic link"); + } + if (update_tree_entry_gently(&desc)) { retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree"); break; @@ -713,30 +734,31 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option static int fsck_commit_buffer(struct commit *commit, const char *buffer, unsigned long size, struct fsck_options *options) { - unsigned char tree_sha1[20], sha1[20]; + struct object_id tree_oid, oid; struct commit_graft *graft; unsigned parent_count, parent_line_count = 0, author_count; int err; const char *buffer_begin = buffer; + const char *p; if (verify_headers(buffer, size, &commit->object, options)) return -1; if (!skip_prefix(buffer, "tree ", &buffer)) return report(options, &commit->object, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line"); - if (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\n') { + if (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\n') { err = report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1"); if (err) return err; } - buffer += 41; + buffer = p + 1; while (skip_prefix(buffer, "parent ", &buffer)) { - if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n') { + if (parse_oid_hex(buffer, &oid, &p) || *p != '\n') { err = report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1"); if (err) return err; } - buffer += 41; + buffer = p + 1; parent_line_count++; } graft = lookup_commit_graft(&commit->object.oid); @@ -775,7 +797,7 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer, if (err) return err; if (!get_commit_tree(commit)) { - err = report(options, &commit->object, FSCK_MSG_BAD_TREE, "could not load commit's tree %s", sha1_to_hex(tree_sha1)); + err = report(options, &commit->object, FSCK_MSG_BAD_TREE, "could not load commit's tree %s", oid_to_hex(&tree_oid)); if (err) return err; } @@ -801,11 +823,12 @@ static int fsck_commit(struct commit *commit, const char *data, static int fsck_tag_buffer(struct tag *tag, const char *data, unsigned long size, struct fsck_options *options) { - unsigned char sha1[20]; + struct object_id oid; int ret = 0; const char *buffer; char *to_free = NULL, *eol; struct strbuf sb = STRBUF_INIT; + const char *p; if (data) buffer = data; @@ -836,12 +859,12 @@ static int fsck_tag_buffer(struct tag *tag, const char *data, ret = report(options, &tag->object, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line"); goto done; } - if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n') { + if (parse_oid_hex(buffer, &oid, &p) || *p != '\n') { ret = report(options, &tag->object, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1"); if (ret) goto done; } - buffer += 41; + buffer = p + 1; if (!skip_prefix(buffer, "type ", &buffer)) { ret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE_ENTRY, "invalid format - expected 'type' line"); @@ -903,6 +926,66 @@ static int fsck_tag(struct tag *tag, const char *data, return fsck_tag_buffer(tag, data, size, options); } +struct fsck_gitmodules_data { + struct object *obj; + struct fsck_options *options; + int ret; +}; + +static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata) +{ + struct fsck_gitmodules_data *data = vdata; + const char *subsection, *key; + int subsection_len; + char *name; + + if (parse_config_key(var, "submodule", &subsection, &subsection_len, &key) < 0 || + !subsection) + return 0; + + name = xmemdupz(subsection, subsection_len); + if (check_submodule_name(name) < 0) + data->ret |= report(data->options, data->obj, + FSCK_MSG_GITMODULES_NAME, + "disallowed submodule name: %s", + name); + free(name); + + return 0; +} + +static int fsck_blob(struct blob *blob, const char *buf, + unsigned long size, struct fsck_options *options) +{ + struct fsck_gitmodules_data data; + + if (!oidset_contains(&gitmodules_found, &blob->object.oid)) + return 0; + oidset_insert(&gitmodules_done, &blob->object.oid); + + if (!buf) { + /* + * A missing buffer here is a sign that the caller found the + * blob too gigantic to load into memory. Let's just consider + * that an error. + */ + return report(options, &blob->object, + FSCK_MSG_GITMODULES_PARSE, + ".gitmodules too large to parse"); + } + + data.obj = &blob->object; + data.options = options; + data.ret = 0; + if (git_config_from_mem(fsck_gitmodules_fn, CONFIG_ORIGIN_BLOB, + ".gitmodules", buf, size, &data)) + data.ret |= report(options, &blob->object, + FSCK_MSG_GITMODULES_PARSE, + "could not parse gitmodules blob"); + + return data.ret; +} + int fsck_object(struct object *obj, void *data, unsigned long size, struct fsck_options *options) { @@ -910,7 +993,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size, return report(options, obj, FSCK_MSG_BAD_OBJECT_SHA1, "no valid object to fsck"); if (obj->type == OBJ_BLOB) - return 0; + return fsck_blob((struct blob *)obj, data, size, options); if (obj->type == OBJ_TREE) return fsck_tree((struct tree *) obj, options); if (obj->type == OBJ_COMMIT) @@ -934,3 +1017,52 @@ int fsck_error_function(struct fsck_options *o, error("object %s: %s", describe_object(o, obj), message); return 1; } + +int fsck_finish(struct fsck_options *options) +{ + int ret = 0; + struct oidset_iter iter; + const struct object_id *oid; + + oidset_iter_init(&gitmodules_found, &iter); + while ((oid = oidset_iter_next(&iter))) { + struct blob *blob; + enum object_type type; + unsigned long size; + char *buf; + + if (oidset_contains(&gitmodules_done, oid)) + continue; + + blob = lookup_blob(oid); + if (!blob) { + ret |= report(options, &blob->object, + FSCK_MSG_GITMODULES_BLOB, + "non-blob found at .gitmodules"); + continue; + } + + buf = read_object_file(oid, &type, &size); + if (!buf) { + if (is_promisor_object(&blob->object.oid)) + continue; + ret |= report(options, &blob->object, + FSCK_MSG_GITMODULES_MISSING, + "unable to read .gitmodules blob"); + continue; + } + + if (type == OBJ_BLOB) + ret |= fsck_blob(blob, buf, size, options); + else + ret |= report(options, &blob->object, + FSCK_MSG_GITMODULES_BLOB, + "non-blob found at .gitmodules"); + free(buf); + } + + + oidset_clear(&gitmodules_found); + oidset_clear(&gitmodules_done); + return ret; +} @@ -53,4 +53,11 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options); int fsck_object(struct object *obj, void *data, unsigned long size, struct fsck_options *options); +/* + * Some fsck checks are context-dependent, and may end up queued; run this + * after completing all fsck_object() calls in order to resolve any remaining + * checks. + */ +int fsck_finish(struct fsck_options *options); + #endif diff --git a/fsmonitor.c b/fsmonitor.c index ed3d1a074d..665bd2d425 100644 --- a/fsmonitor.c +++ b/fsmonitor.c @@ -97,19 +97,13 @@ void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate) static int query_fsmonitor(int version, uint64_t last_update, struct strbuf *query_result) { struct child_process cp = CHILD_PROCESS_INIT; - char ver[64]; - char date[64]; - const char *argv[4]; - if (!(argv[0] = core_fsmonitor)) + if (!core_fsmonitor) return -1; - snprintf(ver, sizeof(ver), "%d", version); - snprintf(date, sizeof(date), "%" PRIuMAX, (uintmax_t)last_update); - argv[1] = ver; - argv[2] = date; - argv[3] = NULL; - cp.argv = argv; + argv_array_push(&cp.args, core_fsmonitor); + argv_array_pushf(&cp.args, "%d", version); + argv_array_pushf(&cp.args, "%" PRIuMAX, (uintmax_t)last_update); cp.use_shell = 1; cp.dir = get_git_work_tree(); diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh index eeea4b67ea..8d6d8b45ce 100755 --- a/generate-cmdlist.sh +++ b/generate-cmdlist.sh @@ -1,50 +1,90 @@ #!/bin/sh -echo "/* Automatically generated by generate-cmdlist.sh */ -struct cmdname_help { - char name[16]; - char help[80]; - unsigned char group; -}; +die () { + echo "$@" >&2 + exit 1 +} + +command_list () { + grep -v '^#' "$1" +} -static const char *common_cmd_groups[] = {" - -grps=grps$$.tmp -match=match$$.tmp -trap "rm -f '$grps' '$match'" 0 1 2 3 15 - -sed -n ' - 1,/^### common groups/b - /^### command list/q - /^#/b - /^[ ]*$/b - h;s/^[^ ][^ ]*[ ][ ]*\(.*\)/ N_("\1"),/p - g;s/^\([^ ][^ ]*\)[ ].*/\1/w '$grps' - ' "$1" -printf '};\n\n' - -n=0 -substnum= -while read grp -do - echo "^git-..*[ ]$grp" - substnum="$substnum${substnum:+;}s/[ ]$grp/$n/" - n=$(($n+1)) -done <"$grps" >"$match" - -printf 'static struct cmdname_help common_cmds[] = {\n' -grep -f "$match" "$1" | -sed 's/^git-//' | -sort | -while read cmd tags -do - tag=$(echo "$tags" | sed "$substnum; s/[^0-9]//g") +get_categories () { + tr ' ' '\n'| + grep -v '^$' | + sort | + uniq +} + +category_list () { + command_list "$1" | + cut -c 40- | + get_categories +} + +get_synopsis () { sed -n ' - /^NAME/,/git-'"$cmd"'/H + /^NAME/,/'"$1"'/H ${ x - s/.*git-'"$cmd"' - \(.*\)/ {"'"$cmd"'", N_("\1"), '$tag'},/ + s/.*'"$1"' - \(.*\)/N_("\1")/ p - }' "Documentation/git-$cmd.txt" -done -echo "};" + }' "Documentation/$1.txt" +} + +define_categories () { + echo + echo "/* Command categories */" + bit=0 + category_list "$1" | + while read cat + do + echo "#define CAT_$cat (1UL << $bit)" + bit=$(($bit+1)) + done + test "$bit" -gt 32 && die "Urgh.. too many categories?" +} + +define_category_names () { + echo + echo "/* Category names */" + echo "static const char *category_names[] = {" + bit=0 + category_list "$1" | + while read cat + do + echo " \"$cat\", /* (1UL << $bit) */" + bit=$(($bit+1)) + done + echo " NULL" + echo "};" +} + +print_command_list () { + echo "static struct cmdname_help command_list[] = {" + + command_list "$1" | + while read cmd rest + do + printf " { \"$cmd\", $(get_synopsis $cmd), 0" + for cat in $(echo "$rest" | get_categories) + do + printf " | CAT_$cat" + done + echo " }," + done + echo "};" +} + +echo "/* Automatically generated by generate-cmdlist.sh */ +struct cmdname_help { + const char *name; + const char *help; + uint32_t category; +}; +" +define_categories "$1" +echo +define_category_names "$1" +echo +print_command_list "$1" diff --git a/git-add--interactive.perl b/git-add--interactive.perl index c1f52e457f..36f38ced90 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -205,8 +205,15 @@ my $status_head = sprintf($status_fmt, __('staged'), __('unstaged'), __('path')) } } -sub get_empty_tree { - return '4b825dc642cb6eb9a060e54bf8d69288fbee4904'; +{ + my $empty_tree; + sub get_empty_tree { + return $empty_tree if defined $empty_tree; + + $empty_tree = run_cmd_pipe(qw(git hash-object -t tree /dev/null)); + chomp $empty_tree; + return $empty_tree; + } } sub get_diff_reference { diff --git a/git-compat-util.h b/git-compat-util.h index f9e4c5f9bc..9a64998b24 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -1006,6 +1006,23 @@ static inline int sane_iscase(int x, int is_lower) return (x & 0x20) == 0; } +/* + * Like skip_prefix, but compare case-insensitively. Note that the comparison + * is done via tolower(), so it is strictly ASCII (no multi-byte characters or + * locale-specific conversions). + */ +static inline int skip_iprefix(const char *str, const char *prefix, + const char **out) +{ + do { + if (!*prefix) { + *out = str; + return 1; + } + } while (tolower(*str++) == tolower(*prefix++)); + return 0; +} + static inline int strtoul_ui(char const *s, int base, unsigned int *result) { unsigned long ul; @@ -1057,7 +1074,7 @@ int git_qsort_s(void *base, size_t nmemb, size_t size, #define QSORT_S(base, n, compar, ctx) do { \ if (qsort_s((base), (n), sizeof(*(base)), compar, ctx)) \ - die("BUG: qsort_s() failed"); \ + BUG("qsort_s() failed"); \ } while (0) #ifndef REG_STARTEND @@ -1116,6 +1133,9 @@ static inline int regexec_buf(const regex_t *preg, const char *buf, size_t size, #define HAVE_VARIADIC_MACROS 1 #endif +/* usage.c: only to be used for testing BUG() implementation (see test-tool) */ +extern int BUG_exit_code; + #ifdef HAVE_VARIADIC_MACROS __attribute__((format (printf, 3, 4))) NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...); diff --git a/git-filter-branch.sh b/git-filter-branch.sh index 64f21547c1..ccceaf19a7 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -11,6 +11,8 @@ # The following functions will also be available in the commit filter: functions=$(cat << \EOF +EMPTY_TREE=$(git hash-object -t tree /dev/null) + warn () { echo "$*" >&2 } @@ -46,7 +48,7 @@ git_commit_non_empty_tree() { if test $# = 3 && test "$1" = $(git rev-parse "$3^{tree}"); then map "$3" - elif test $# = 1 && test "$1" = 4b825dc642cb6eb9a060e54bf8d69288fbee4904; then + elif test $# = 1 && test "$1" = $EMPTY_TREE; then : else git commit-tree "$@" diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh index 9879c59395..f6d9852d2f 100755 --- a/git-merge-one-file.sh +++ b/git-merge-one-file.sh @@ -120,7 +120,7 @@ case "${1:-.}${2:-.}${3:-.}" in case "$1" in '') echo "Added $4 in both, but differently." - orig=$(git unpack-file e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) + orig=$(git unpack-file $(git hash-object /dev/null)) ;; *) echo "Auto-merging $4" @@ -2099,11 +2099,11 @@ class P4Submit(Command, P4UserMap): commits = [] if self.master: - commitish = self.master + committish = self.master else: - commitish = 'HEAD' + committish = 'HEAD' - for line in read_pipe_lines(["git", "rev-list", "--no-merges", "%s..%s" % (self.origin, commitish)]): + for line in read_pipe_lines(["git", "rev-list", "--no-merges", "%s..%s" % (self.origin, committish)]): commits.append(line.strip()) commits.reverse() diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index cbf44f8648..0c92ac00fa 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -81,6 +81,8 @@ rewritten_pending="$state_dir"/rewritten-pending # and leaves CR at the end instead. cr=$(printf "\015") +empty_tree=$(git hash-object -t tree /dev/null) + strategy_args=${strategy:+--strategy=$strategy} test -n "$strategy_opts" && eval ' @@ -244,7 +246,7 @@ is_empty_commit() { die "$(eval_gettext "\$sha1: not a commit that can be picked")" } ptree=$(git rev-parse -q --verify "$1"^^{tree} 2>/dev/null) || - ptree=4b825dc642cb6eb9a060e54bf8d69288fbee4904 + ptree=$empty_tree test "$tree" = "$ptree" } @@ -894,6 +896,8 @@ init_revisions_and_shortrevisions () { else revisions=$onto...$orig_head shortrevisions=$shorthead + test -z "$squash_onto" || + echo "$squash_onto" >"$state_dir"/squash-onto fi } @@ -948,7 +952,7 @@ EOF die "Could not skip unnecessary pick commands" checkout_onto - if test -z "$rebase_root" && test ! -d "$rewritten" + if test ! -d "$rewritten" then require_clean_work_tree "rebase" exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \ diff --git a/git-send-email.perl b/git-send-email.perl index 7157397fd0..8ec70e58ed 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -1330,9 +1330,14 @@ sub file_name_is_absolute { return File::Spec::Functions::file_name_is_absolute($path); } -# Returns 1 if the message was sent, and 0 otherwise. -# In actuality, the whole program dies when there -# is an error sending a message. +# Prepares the email, then asks the user what to do. +# +# If the user chooses to send the email, it's sent and 1 is returned. +# If the user chooses not to send the email, 0 is returned. +# If the user decides they want to make further edits, -1 is returned and the +# caller is expected to call send_message again after the edits are performed. +# +# If an error occurs sending the email, this just dies. sub send_message { my @recipients = unique_email_list(@to); @@ -1404,15 +1409,17 @@ Message-Id: $message_id EOF } - # TRANSLATORS: Make sure to include [y] [n] [q] [a] in your + # TRANSLATORS: Make sure to include [y] [n] [e] [q] [a] in your # translation. The program will only accept English input # at this point. - $_ = ask(__("Send this email? ([y]es|[n]o|[q]uit|[a]ll): "), - valid_re => qr/^(?:yes|y|no|n|quit|q|all|a)/i, + $_ = ask(__("Send this email? ([y]es|[n]o|[e]dit|[q]uit|[a]ll): "), + valid_re => qr/^(?:yes|y|no|n|edit|e|quit|q|all|a)/i, default => $ask_default); die __("Send this email reply required") unless defined $_; if (/^n/i) { return 0; + } elsif (/^e/i) { + return -1; } elsif (/^q/i) { cleanup_compose_files(); exit(0); @@ -1552,7 +1559,12 @@ $references = $initial_in_reply_to || ''; $subject = $initial_subject; $message_num = 0; -foreach my $t (@files) { +# Prepares the email, prompts the user, sends it out +# Returns 0 if an edit was done and the function should be called again, or 1 +# otherwise. +sub process_file { + my ($t) = @_; + open my $fh, "<", $t or die sprintf(__("can't open file %s"), $t); my $author = undef; @@ -1760,6 +1772,10 @@ foreach my $t (@files) { } my $message_was_sent = send_message(); + if ($message_was_sent == -1) { + do_edit($t); + return 0; + } # set up for the next message if ($thread && $message_was_sent && @@ -1781,6 +1797,14 @@ foreach my $t (@files) { undef $auth; sleep($relogin_delay) if defined $relogin_delay; } + + return 1; +} + +foreach my $t (@files) { + while (!process_file($t)) { + # user edited the file + } } # Execute a command (e.g. $to_cmd) to get a list of email addresses diff --git a/git-submodule.sh b/git-submodule.sh index 24914963ca..78073cd87d 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -42,6 +42,7 @@ prefix= custom_name= depth= progress= +dissociate= die_if_unmatched () { @@ -117,6 +118,9 @@ cmd_add() -q|--quiet) GIT_QUIET=1 ;; + --progress) + progress=1 + ;; --reference) case "$2" in '') usage ;; esac reference_path=$2 @@ -125,6 +129,9 @@ cmd_add() --reference=*) reference_path="${1#--reference=}" ;; + --dissociate) + dissociate=1 + ;; --name) case "$2" in '') usage ;; esac custom_name=$2 @@ -229,6 +236,11 @@ Use -f if you really want to add it." >&2 sm_name="$sm_path" fi + if ! git submodule--helper check-name "$sm_name" + then + die "$(eval_gettext "'$sm_name' is not a valid submodule name")" + fi + # perhaps the path exists and is already a git repo, else clone it if test -e "$sm_path" then @@ -255,7 +267,7 @@ or you are unsure what this means choose another name with the '--name' option." eval_gettextln "Reactivating local git directory for submodule '\$sm_name'." fi fi - git submodule--helper clone ${GIT_QUIET:+--quiet} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" ${reference:+"$reference"} ${depth:+"$depth"} || exit + git submodule--helper clone ${GIT_QUIET:+--quiet} ${progress:+"--progress"} --prefix "$wt_prefix" --path "$sm_path" --name "$sm_name" --url "$realrepo" ${reference:+"$reference"} ${dissociate:+"--dissociate"} ${depth:+"$depth"} || exit ( sanitize_submodule_env cd "$sm_path" && @@ -465,7 +477,7 @@ cmd_update() GIT_QUIET=1 ;; --progress) - progress="--progress" + progress=1 ;; -i|--init) init=1 @@ -490,6 +502,9 @@ cmd_update() --reference=*) reference="$1" ;; + --dissociate) + dissociate=1 + ;; -m|--merge) update="merge" ;; @@ -542,14 +557,15 @@ cmd_update() { git submodule--helper update-clone ${GIT_QUIET:+--quiet} \ - ${progress:+"$progress"} \ + ${progress:+"--progress"} \ ${wt_prefix:+--prefix "$wt_prefix"} \ ${prefix:+--recursive-prefix "$prefix"} \ ${update:+--update "$update"} \ ${reference:+"$reference"} \ + ${dissociate:+"--dissociate"} \ ${depth:+--depth "$depth"} \ - ${recommend_shallow:+"$recommend_shallow"} \ - ${jobs:+$jobs} \ + $recommend_shallow \ + $jobs \ "$@" || echo "#unmatched" $? } | { err= @@ -614,7 +630,7 @@ cmd_update() # is not reachable from a ref. is_tip_reachable "$sm_path" "$sha1" || fetch_in_submodule "$sm_path" $depth || - die "$(eval_gettext "Unable to fetch in submodule path '\$displaypath'")" + say "$(eval_gettext "Unable to fetch in submodule path '\$displaypath'")" # Now we tried the usual fetch, but $sha1 may # not be reachable from any of the refs @@ -3,6 +3,7 @@ #include "exec-cmd.h" #include "help.h" #include "run-command.h" +#include "alias.h" #define RUN_SETUP (1<<0) #define RUN_SETUP_GENTLY (1<<1) @@ -36,7 +37,66 @@ const char git_more_info_string[] = static int use_pager = -1; -static void list_builtins(unsigned int exclude_option, char sep); +static void list_builtins(struct string_list *list, unsigned int exclude_option); + +static void exclude_helpers_from_list(struct string_list *list) +{ + int i = 0; + + while (i < list->nr) { + if (strstr(list->items[i].string, "--")) + unsorted_string_list_delete_item(list, i, 0); + else + i++; + } +} + +static int match_token(const char *spec, int len, const char *token) +{ + int token_len = strlen(token); + + return len == token_len && !strncmp(spec, token, token_len); +} + +static int list_cmds(const char *spec) +{ + struct string_list list = STRING_LIST_INIT_DUP; + int i; + + while (*spec) { + const char *sep = strchrnul(spec, ','); + int len = sep - spec; + + if (match_token(spec, len, "builtins")) + list_builtins(&list, 0); + else if (match_token(spec, len, "main")) + list_all_main_cmds(&list); + else if (match_token(spec, len, "others")) + list_all_other_cmds(&list); + else if (match_token(spec, len, "nohelpers")) + exclude_helpers_from_list(&list); + else if (match_token(spec, len, "alias")) + list_aliases(&list); + else if (match_token(spec, len, "config")) + list_cmds_by_config(&list); + else if (len > 5 && !strncmp(spec, "list-", 5)) { + struct strbuf sb = STRBUF_INIT; + + strbuf_add(&sb, spec + 5, len - 5); + list_cmds_by_category(&list, sb.buf); + strbuf_release(&sb); + } + else + die(_("unsupported command listing type '%s'"), spec); + spec += len; + if (*spec == ',') + spec++; + } + for (i = 0; i < list.nr; i++) + puts(list.items[i].string); + string_list_clear(&list, 0); + return 0; +} static void commit_pager_choice(void) { switch (use_pager) { @@ -223,12 +283,19 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) } (*argv)++; (*argc)--; - } else if (!strcmp(cmd, "--list-builtins")) { - list_builtins(0, '\n'); - exit(0); - } else if (!strcmp(cmd, "--list-parseopt-builtins")) { - list_builtins(NO_PARSEOPT, ' '); - exit(0); + } else if (skip_prefix(cmd, "--list-cmds=", &cmd)) { + if (!strcmp(cmd, "parseopt")) { + struct string_list list = STRING_LIST_INIT_DUP; + int i; + + list_builtins(&list, NO_PARSEOPT); + for (i = 0; i < list.nr; i++) + printf("%s ", list.items[i].string); + string_list_clear(&list, 0); + exit(0); + } else { + exit(list_cmds(cmd)); + } } else { fprintf(stderr, _("unknown option: %s\n"), cmd); usage(git_usage_string); @@ -511,14 +578,14 @@ int is_builtin(const char *s) return !!get_builtin(s); } -static void list_builtins(unsigned int exclude_option, char sep) +static void list_builtins(struct string_list *out, unsigned int exclude_option) { int i; for (i = 0; i < ARRAY_SIZE(commands); i++) { if (exclude_option && (commands[i].option & exclude_option)) continue; - printf("%s%c", commands[i].cmd, sep); + string_list_append(out, commands[i].cmd); } } @@ -404,7 +404,7 @@ static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt) die("Couldn't allocate PCRE JIT stack"); pcre_assign_jit_stack(p->pcre1_extra_info, NULL, p->pcre1_jit_stack); } else if (p->pcre1_jit_on != 0) { - die("BUG: The pcre1_jit_on variable should be 0 or 1, not %d", + BUG("The pcre1_jit_on variable should be 0 or 1, not %d", p->pcre1_jit_on); } #endif @@ -550,7 +550,7 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt die("Couldn't allocate PCRE2 match context"); pcre2_jit_stack_assign(p->pcre2_match_context, NULL, p->pcre2_jit_stack); } else if (p->pcre2_jit_on != 0) { - die("BUG: The pcre2_jit_on variable should be 0 or 1, not %d", + BUG("The pcre2_jit_on variable should be 0 or 1, not %d", p->pcre1_jit_on); } } @@ -636,7 +636,6 @@ static void compile_fixed_regexp(struct grep_pat *p, struct grep_opt *opt) if (err) { char errbuf[1024]; regerror(err, &p->regexp, errbuf, sizeof(errbuf)); - regfree(&p->regexp); compile_regexp_failed(p, errbuf); } } @@ -701,7 +700,6 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt) if (err) { char errbuf[1024]; regerror(err, &p->regexp, errbuf, 1024); - regfree(&p->regexp); compile_regexp_failed(p, errbuf); } } @@ -917,10 +915,10 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt) for (p = opt->header_list; p; p = p->next) { if (p->token != GREP_PATTERN_HEAD) - die("BUG: a non-header pattern in grep header list."); + BUG("a non-header pattern in grep header list."); if (p->field < GREP_HEADER_FIELD_MIN || GREP_HEADER_FIELD_MAX <= p->field) - die("BUG: unknown header field %d", p->field); + BUG("unknown header field %d", p->field); compile_regexp(p, opt); } @@ -933,7 +931,7 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt) h = compile_pattern_atom(&pp); if (!h || pp != p->next) - die("BUG: malformed header expr"); + BUG("malformed header expr"); if (!header_group[p->field]) { header_group[p->field] = h; continue; @@ -1652,7 +1650,7 @@ static int fill_textconv_grep(struct userdiff_driver *driver, fill_filespec(df, &null_oid, 0, 0100644); break; default: - die("BUG: attempt to textconv something without a path?"); + BUG("attempt to textconv something without a path?"); } /* @@ -1748,7 +1746,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle case GREP_BINARY_TEXT: break; default: - die("BUG: unknown binary handling mode"); + BUG("unknown binary handling mode"); } } @@ -2072,7 +2070,7 @@ static int grep_source_load(struct grep_source *gs) case GREP_SOURCE_BUF: return gs->buf ? 0 : -1; } - die("BUG: invalid grep_source type to load"); + BUG("invalid grep_source type to load"); } void grep_source_load_driver(struct grep_source *gs) @@ -5,13 +5,127 @@ #include "run-command.h" #include "levenshtein.h" #include "help.h" -#include "common-cmds.h" +#include "command-list.h" #include "string-list.h" #include "column.h" #include "version.h" #include "refs.h" #include "parse-options.h" +struct category_description { + uint32_t category; + const char *desc; +}; +static uint32_t common_mask = + CAT_init | CAT_worktree | CAT_info | + CAT_history | CAT_remote; +static struct category_description common_categories[] = { + { CAT_init, N_("start a working area (see also: git help tutorial)") }, + { CAT_worktree, N_("work on the current change (see also: git help everyday)") }, + { CAT_info, N_("examine the history and state (see also: git help revisions)") }, + { CAT_history, N_("grow, mark and tweak your common history") }, + { CAT_remote, N_("collaborate (see also: git help workflows)") }, + { 0, NULL } +}; +static struct category_description main_categories[] = { + { CAT_mainporcelain, N_("Main Porcelain Commands") }, + { CAT_ancillarymanipulators, N_("Ancillary Commands / Manipulators") }, + { CAT_ancillaryinterrogators, N_("Ancillary Commands / Interrogators") }, + { CAT_foreignscminterface, N_("Interacting with Others") }, + { CAT_plumbingmanipulators, N_("Low-level Commands / Manipulators") }, + { CAT_plumbinginterrogators, N_("Low-level Commands / Interrogators") }, + { CAT_synchingrepositories, N_("Low-level Commands / Synching Repositories") }, + { CAT_purehelpers, N_("Low-level Commands / Internal Helpers") }, + { 0, NULL } +}; + +static const char *drop_prefix(const char *name, uint32_t category) +{ + const char *new_name; + + if (skip_prefix(name, "git-", &new_name)) + return new_name; + if (category == CAT_guide && skip_prefix(name, "git", &new_name)) + return new_name; + return name; + +} + +static void extract_cmds(struct cmdname_help **p_cmds, uint32_t mask) +{ + int i, nr = 0; + struct cmdname_help *cmds; + + if (ARRAY_SIZE(command_list) == 0) + BUG("empty command_list[] is a sign of broken generate-cmdlist.sh"); + + ALLOC_ARRAY(cmds, ARRAY_SIZE(command_list) + 1); + + for (i = 0; i < ARRAY_SIZE(command_list); i++) { + const struct cmdname_help *cmd = command_list + i; + + if (!(cmd->category & mask)) + continue; + + cmds[nr] = *cmd; + cmds[nr].name = drop_prefix(cmd->name, cmd->category); + + nr++; + } + cmds[nr].name = NULL; + *p_cmds = cmds; +} + +static void print_command_list(const struct cmdname_help *cmds, + uint32_t mask, int longest) +{ + int i; + + for (i = 0; cmds[i].name; i++) { + if (cmds[i].category & mask) { + printf(" %s ", cmds[i].name); + mput_char(' ', longest - strlen(cmds[i].name)); + puts(_(cmds[i].help)); + } + } +} + +static int cmd_name_cmp(const void *elem1, const void *elem2) +{ + const struct cmdname_help *e1 = elem1; + const struct cmdname_help *e2 = elem2; + + return strcmp(e1->name, e2->name); +} + +static void print_cmd_by_category(const struct category_description *catdesc) +{ + struct cmdname_help *cmds; + int longest = 0; + int i, nr = 0; + uint32_t mask = 0; + + for (i = 0; catdesc[i].desc; i++) + mask |= catdesc[i].category; + + extract_cmds(&cmds, mask); + + for (i = 0; cmds[i].name; i++, nr++) { + if (longest < strlen(cmds[i].name)) + longest = strlen(cmds[i].name); + } + QSORT(cmds, nr, cmd_name_cmp); + + for (i = 0; catdesc[i].desc; i++) { + uint32_t mask = catdesc[i].category; + const char *desc = catdesc[i].desc; + + printf("\n%s\n", _(desc)); + print_command_list(cmds, mask, longest); + } + free(cmds); +} + void add_cmdname(struct cmdnames *cmds, const char *name, int len) { struct cmdname *ent; @@ -190,44 +304,116 @@ void list_commands(unsigned int colopts, } } -static int cmd_group_cmp(const void *elem1, const void *elem2) +void list_common_cmds_help(void) { - const struct cmdname_help *e1 = elem1; - const struct cmdname_help *e2 = elem2; + puts(_("These are common Git commands used in various situations:")); + print_cmd_by_category(common_categories); +} - if (e1->group < e2->group) - return -1; - if (e1->group > e2->group) - return 1; - return strcmp(e1->name, e2->name); +void list_all_main_cmds(struct string_list *list) +{ + struct cmdnames main_cmds, other_cmds; + int i; + + memset(&main_cmds, 0, sizeof(main_cmds)); + memset(&other_cmds, 0, sizeof(other_cmds)); + load_command_list("git-", &main_cmds, &other_cmds); + + for (i = 0; i < main_cmds.cnt; i++) + string_list_append(list, main_cmds.names[i]->name); + + clean_cmdnames(&main_cmds); + clean_cmdnames(&other_cmds); } -void list_common_cmds_help(void) +void list_all_other_cmds(struct string_list *list) { - int i, longest = 0; - int current_grp = -1; + struct cmdnames main_cmds, other_cmds; + int i; - for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { - if (longest < strlen(common_cmds[i].name)) - longest = strlen(common_cmds[i].name); - } + memset(&main_cmds, 0, sizeof(main_cmds)); + memset(&other_cmds, 0, sizeof(other_cmds)); + load_command_list("git-", &main_cmds, &other_cmds); - QSORT(common_cmds, ARRAY_SIZE(common_cmds), cmd_group_cmp); + for (i = 0; i < other_cmds.cnt; i++) + string_list_append(list, other_cmds.names[i]->name); - puts(_("These are common Git commands used in various situations:")); + clean_cmdnames(&main_cmds); + clean_cmdnames(&other_cmds); +} + +void list_cmds_by_category(struct string_list *list, + const char *cat) +{ + int i, n = ARRAY_SIZE(command_list); + uint32_t cat_id = 0; - for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { - if (common_cmds[i].group != current_grp) { - printf("\n%s\n", _(common_cmd_groups[common_cmds[i].group])); - current_grp = common_cmds[i].group; + for (i = 0; category_names[i]; i++) { + if (!strcmp(cat, category_names[i])) { + cat_id = 1UL << i; + break; } + } + if (!cat_id) + die(_("unsupported command listing type '%s'"), cat); + + for (i = 0; i < n; i++) { + struct cmdname_help *cmd = command_list + i; - printf(" %s ", common_cmds[i].name); - mput_char(' ', longest - strlen(common_cmds[i].name)); - puts(_(common_cmds[i].help)); + if (!(cmd->category & cat_id)) + continue; + string_list_append(list, drop_prefix(cmd->name, cmd->category)); } } +void list_cmds_by_config(struct string_list *list) +{ + const char *cmd_list; + + /* + * There's no actual repository setup at this point (and even + * if there is, we don't really care; only global config + * matters). If we accidentally set up a repository, it's ok + * too since the caller (git --list-cmds=) should exit shortly + * anyway. + */ + if (git_config_get_string_const("completion.commands", &cmd_list)) + return; + + string_list_sort(list); + string_list_remove_duplicates(list, 0); + + while (*cmd_list) { + struct strbuf sb = STRBUF_INIT; + const char *p = strchrnul(cmd_list, ' '); + + strbuf_add(&sb, cmd_list, p - cmd_list); + if (*cmd_list == '-') + string_list_remove(list, cmd_list + 1, 0); + else + string_list_insert(list, sb.buf); + strbuf_release(&sb); + while (*p == ' ') + p++; + cmd_list = p; + } +} + +void list_common_guides_help(void) +{ + struct category_description catdesc[] = { + { CAT_guide, N_("The common Git guides are:") }, + { 0, NULL } + }; + print_cmd_by_category(catdesc); + putchar('\n'); +} + +void list_all_cmds_help(void) +{ + print_cmd_by_category(main_categories); +} + int is_in_cmdlist(struct cmdnames *c, const char *s) { int i; @@ -285,6 +471,7 @@ const char *help_unknown_cmd(const char *cmd) { int i, n, best_similarity = 0; struct cmdnames main_cmds, other_cmds; + struct cmdname_help *common_cmds; memset(&main_cmds, 0, sizeof(main_cmds)); memset(&other_cmds, 0, sizeof(other_cmds)); @@ -299,6 +486,8 @@ const char *help_unknown_cmd(const char *cmd) QSORT(main_cmds.names, main_cmds.cnt, cmdname_compare); uniq(&main_cmds); + extract_cmds(&common_cmds, common_mask); + /* This abuses cmdname->len for levenshtein distance */ for (i = 0, n = 0; i < main_cmds.cnt; i++) { int cmp = 0; /* avoid compiler stupidity */ @@ -313,10 +502,10 @@ const char *help_unknown_cmd(const char *cmd) die(_(bad_interpreter_advice), cmd, cmd); /* Does the candidate appear in common_cmds list? */ - while (n < ARRAY_SIZE(common_cmds) && + while (common_cmds[n].name && (cmp = strcmp(common_cmds[n].name, candidate)) < 0) n++; - if ((n < ARRAY_SIZE(common_cmds)) && !cmp) { + if (common_cmds[n].name && !cmp) { /* Yes, this is one of the common commands */ n++; /* use the entry from common_cmds[] */ if (starts_with(candidate, cmd)) { @@ -329,6 +518,7 @@ const char *help_unknown_cmd(const char *cmd) main_cmds.names[i]->len = levenshtein(cmd, candidate, 0, 2, 1, 3) + 1; } + FREE_AND_NULL(common_cmds); QSORT(main_cmds.names, main_cmds.cnt, levenshtein_compare); @@ -1,6 +1,8 @@ #ifndef HELP_H #define HELP_H +struct string_list; + struct cmdnames { int alloc; int cnt; @@ -17,6 +19,14 @@ static inline void mput_char(char c, unsigned int num) } extern void list_common_cmds_help(void); +extern void list_all_cmds_help(void); +extern void list_common_guides_help(void); + +extern void list_all_main_cmds(struct string_list *list); +extern void list_all_other_cmds(struct string_list *list); +extern void list_cmds_by_category(struct string_list *list, + const char *category); +extern void list_cmds_by_config(struct string_list *list); extern const char *help_unknown_cmd(const char *cmd); extern void load_command_list(const char *prefix, struct cmdnames *main_cmds, diff --git a/http-push.c b/http-push.c index 2669f4bfa1..7e38522098 100644 --- a/http-push.c +++ b/http-push.c @@ -1692,8 +1692,7 @@ int cmd_main(int argc, const char **argv) { struct transfer_request *request; struct transfer_request *next_request; - int nr_refspec = 0; - const char **refspec = NULL; + struct refspec rs = REFSPEC_INIT_PUSH; struct remote_lock *ref_lock = NULL; struct remote_lock *info_ref_lock = NULL; struct rev_info revs; @@ -1756,8 +1755,7 @@ int cmd_main(int argc, const char **argv) } continue; } - refspec = argv; - nr_refspec = argc - i; + refspec_appendn(&rs, argv, argc - i); break; } @@ -1768,7 +1766,7 @@ int cmd_main(int argc, const char **argv) if (!repo->url) usage(http_push_usage); - if (delete_branch && nr_refspec != 1) + if (delete_branch && rs.nr != 1) die("You must specify only one branch name when deleting a remote branch"); setup_git_directory(); @@ -1814,18 +1812,18 @@ int cmd_main(int argc, const char **argv) /* Remove a remote branch if -d or -D was specified */ if (delete_branch) { - if (delete_remote_branch(refspec[0], force_delete) == -1) { + const char *branch = rs.items[i].src; + if (delete_remote_branch(branch, force_delete) == -1) { fprintf(stderr, "Unable to delete remote branch %s\n", - refspec[0]); + branch); if (helper_status) - printf("error %s cannot remove\n", refspec[0]); + printf("error %s cannot remove\n", branch); } goto cleanup; } /* match them up */ - if (match_push_refs(local_refs, &remote_refs, - nr_refspec, (const char **) refspec, push_all)) { + if (match_push_refs(local_refs, &remote_refs, &rs, push_all)) { rc = -1; goto cleanup; } @@ -1788,7 +1788,7 @@ static int http_request(const char *url, curl_easy_setopt(slot->curl, CURLOPT_URL, url); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers); - curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "gzip"); + curl_easy_setopt(slot->curl, CURLOPT_ENCODING, ""); ret = run_one_slot(slot, &results); @@ -1846,7 +1846,7 @@ static int update_url_from_redirect(struct strbuf *base, return 0; if (!skip_prefix(asked, base->buf, &tail)) - die("BUG: update_url_from_redirect: %s is not a superset of %s", + BUG("update_url_from_redirect: %s is not a superset of %s", asked, base->buf); new_len = got->len; @@ -1894,7 +1894,7 @@ static int http_request_reauth(const char *url, strbuf_reset(result); break; default: - die("BUG: HTTP_KEEP_ERROR is only supported with strbufs"); + BUG("HTTP_KEEP_ERROR is only supported with strbufs"); } } @@ -2038,7 +2038,8 @@ int http_get_info_packs(const char *base_url, struct packed_git **packs_head) int ret = 0, i = 0; char *url, *data; struct strbuf buf = STRBUF_INIT; - unsigned char sha1[20]; + unsigned char hash[GIT_MAX_RAWSZ]; + const unsigned hexsz = the_hash_algo->hexsz; end_url_with_slash(&buf, base_url); strbuf_addstr(&buf, "objects/info/packs"); @@ -2054,13 +2055,13 @@ int http_get_info_packs(const char *base_url, struct packed_git **packs_head) switch (data[i]) { case 'P': i++; - if (i + 52 <= buf.len && + if (i + hexsz + 12 <= buf.len && starts_with(data + i, " pack-") && - starts_with(data + i + 46, ".pack\n")) { - get_sha1_hex(data + i + 6, sha1); - fetch_and_setup_pack_index(packs_head, sha1, + starts_with(data + i + hexsz + 6, ".pack\n")) { + get_sha1_hex(data + i + 6, hash); + fetch_and_setup_pack_index(packs_head, hash, base_url); - i += 51; + i += hexsz + 11; break; } default: @@ -2082,6 +2083,7 @@ void release_http_pack_request(struct http_pack_request *preq) preq->packfile = NULL; } preq->slot = NULL; + strbuf_release(&preq->tmpfile); free(preq->url); free(preq); } @@ -2104,19 +2106,19 @@ int finish_http_pack_request(struct http_pack_request *preq) lst = &((*lst)->next); *lst = (*lst)->next; - if (!strip_suffix(preq->tmpfile, ".pack.temp", &len)) - die("BUG: pack tmpfile does not end in .pack.temp?"); - tmp_idx = xstrfmt("%.*s.idx.temp", (int)len, preq->tmpfile); + if (!strip_suffix(preq->tmpfile.buf, ".pack.temp", &len)) + BUG("pack tmpfile does not end in .pack.temp?"); + tmp_idx = xstrfmt("%.*s.idx.temp", (int)len, preq->tmpfile.buf); argv_array_push(&ip.args, "index-pack"); argv_array_pushl(&ip.args, "-o", tmp_idx, NULL); - argv_array_push(&ip.args, preq->tmpfile); + argv_array_push(&ip.args, preq->tmpfile.buf); ip.git_cmd = 1; ip.no_stdin = 1; ip.no_stdout = 1; if (run_command(&ip)) { - unlink(preq->tmpfile); + unlink(preq->tmpfile.buf); unlink(tmp_idx); free(tmp_idx); return -1; @@ -2124,7 +2126,7 @@ int finish_http_pack_request(struct http_pack_request *preq) unlink(sha1_pack_index_name(p->sha1)); - if (finalize_object_file(preq->tmpfile, sha1_pack_name(p->sha1)) + if (finalize_object_file(preq->tmpfile.buf, sha1_pack_name(p->sha1)) || finalize_object_file(tmp_idx, sha1_pack_index_name(p->sha1))) { free(tmp_idx); return -1; @@ -2143,6 +2145,7 @@ struct http_pack_request *new_http_pack_request( struct http_pack_request *preq; preq = xcalloc(1, sizeof(*preq)); + strbuf_init(&preq->tmpfile, 0); preq->target = target; end_url_with_slash(&buf, base_url); @@ -2150,12 +2153,11 @@ struct http_pack_request *new_http_pack_request( sha1_to_hex(target->sha1)); preq->url = strbuf_detach(&buf, NULL); - snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp", - sha1_pack_name(target->sha1)); - preq->packfile = fopen(preq->tmpfile, "a"); + strbuf_addf(&preq->tmpfile, "%s.temp", sha1_pack_name(target->sha1)); + preq->packfile = fopen(preq->tmpfile.buf, "a"); if (!preq->packfile) { error("Unable to open local file %s for pack", - preq->tmpfile); + preq->tmpfile.buf); goto abort; } @@ -2182,6 +2184,7 @@ struct http_pack_request *new_http_pack_request( return preq; abort: + strbuf_release(&preq->tmpfile); free(preq->url); free(preq); return NULL; @@ -2201,7 +2204,7 @@ static size_t fwrite_sha1_file(char *ptr, size_t eltsize, size_t nmemb, CURLcode c = curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code); if (c != CURLE_OK) - die("BUG: curl_easy_getinfo for HTTP code failed: %s", + BUG("curl_easy_getinfo for HTTP code failed: %s", curl_easy_strerror(c)); if (slot->http_code >= 300) return size; @@ -2232,7 +2235,7 @@ struct http_object_request *new_http_object_request(const char *base_url, { char *hex = sha1_to_hex(sha1); struct strbuf filename = STRBUF_INIT; - char prevfile[PATH_MAX]; + struct strbuf prevfile = STRBUF_INIT; int prevlocal; char prev_buf[PREV_BUF_SIZE]; ssize_t prev_read = 0; @@ -2240,40 +2243,41 @@ struct http_object_request *new_http_object_request(const char *base_url, struct http_object_request *freq; freq = xcalloc(1, sizeof(*freq)); + strbuf_init(&freq->tmpfile, 0); hashcpy(freq->sha1, sha1); freq->localfile = -1; sha1_file_name(the_repository, &filename, sha1); - snprintf(freq->tmpfile, sizeof(freq->tmpfile), - "%s.temp", filename.buf); + strbuf_addf(&freq->tmpfile, "%s.temp", filename.buf); - snprintf(prevfile, sizeof(prevfile), "%s.prev", filename.buf); - unlink_or_warn(prevfile); - rename(freq->tmpfile, prevfile); - unlink_or_warn(freq->tmpfile); + strbuf_addf(&prevfile, "%s.prev", filename.buf); + unlink_or_warn(prevfile.buf); + rename(freq->tmpfile.buf, prevfile.buf); + unlink_or_warn(freq->tmpfile.buf); strbuf_release(&filename); if (freq->localfile != -1) error("fd leakage in start: %d", freq->localfile); - freq->localfile = open(freq->tmpfile, + freq->localfile = open(freq->tmpfile.buf, O_WRONLY | O_CREAT | O_EXCL, 0666); /* * This could have failed due to the "lazy directory creation"; * try to mkdir the last path component. */ if (freq->localfile < 0 && errno == ENOENT) { - char *dir = strrchr(freq->tmpfile, '/'); + char *dir = strrchr(freq->tmpfile.buf, '/'); if (dir) { *dir = 0; - mkdir(freq->tmpfile, 0777); + mkdir(freq->tmpfile.buf, 0777); *dir = '/'; } - freq->localfile = open(freq->tmpfile, + freq->localfile = open(freq->tmpfile.buf, O_WRONLY | O_CREAT | O_EXCL, 0666); } if (freq->localfile < 0) { - error_errno("Couldn't create temporary file %s", freq->tmpfile); + error_errno("Couldn't create temporary file %s", + freq->tmpfile.buf); goto abort; } @@ -2287,7 +2291,7 @@ struct http_object_request *new_http_object_request(const char *base_url, * If a previous temp file is present, process what was already * fetched. */ - prevlocal = open(prevfile, O_RDONLY); + prevlocal = open(prevfile.buf, O_RDONLY); if (prevlocal != -1) { do { prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE); @@ -2304,7 +2308,8 @@ struct http_object_request *new_http_object_request(const char *base_url, } while (prev_read > 0); close(prevlocal); } - unlink_or_warn(prevfile); + unlink_or_warn(prevfile.buf); + strbuf_release(&prevfile); /* * Reset inflate/SHA1 if there was an error reading the previous temp @@ -2319,7 +2324,7 @@ struct http_object_request *new_http_object_request(const char *base_url, lseek(freq->localfile, 0, SEEK_SET); if (ftruncate(freq->localfile, 0) < 0) { error_errno("Couldn't truncate temporary file %s", - freq->tmpfile); + freq->tmpfile.buf); goto abort; } } @@ -2349,6 +2354,7 @@ struct http_object_request *new_http_object_request(const char *base_url, return freq; abort: + strbuf_release(&prevfile); free(freq->url); free(freq); return NULL; @@ -2376,24 +2382,24 @@ int finish_http_object_request(struct http_object_request *freq) if (freq->http_code == 416) { warning("requested range invalid; we may already have all the data."); } else if (freq->curl_result != CURLE_OK) { - if (stat(freq->tmpfile, &st) == 0) + if (stat(freq->tmpfile.buf, &st) == 0) if (st.st_size == 0) - unlink_or_warn(freq->tmpfile); + unlink_or_warn(freq->tmpfile.buf); return -1; } git_inflate_end(&freq->stream); git_SHA1_Final(freq->real_sha1, &freq->c); if (freq->zret != Z_STREAM_END) { - unlink_or_warn(freq->tmpfile); + unlink_or_warn(freq->tmpfile.buf); return -1; } if (hashcmp(freq->sha1, freq->real_sha1)) { - unlink_or_warn(freq->tmpfile); + unlink_or_warn(freq->tmpfile.buf); return -1; } sha1_file_name(the_repository, &filename, freq->sha1); - freq->rename = finalize_object_file(freq->tmpfile, filename.buf); + freq->rename = finalize_object_file(freq->tmpfile.buf, filename.buf); strbuf_release(&filename); return freq->rename; @@ -2401,7 +2407,7 @@ int finish_http_object_request(struct http_object_request *freq) void abort_http_object_request(struct http_object_request *freq) { - unlink_or_warn(freq->tmpfile); + unlink_or_warn(freq->tmpfile.buf); release_http_object_request(freq); } @@ -2421,4 +2427,5 @@ void release_http_object_request(struct http_object_request *freq) release_active_slot(freq->slot); freq->slot = NULL; } + strbuf_release(&freq->tmpfile); } @@ -207,7 +207,7 @@ struct http_pack_request { struct packed_git *target; struct packed_git **lst; FILE *packfile; - char tmpfile[PATH_MAX]; + struct strbuf tmpfile; struct active_request_slot *slot; }; @@ -219,7 +219,7 @@ extern void release_http_pack_request(struct http_pack_request *preq); /* Helpers for fetching object */ struct http_object_request { char *url; - char tmpfile[PATH_MAX]; + struct strbuf tmpfile; int localfile; CURLcode curl_result; char errorstr[CURL_ERROR_SIZE]; diff --git a/imap-send.c b/imap-send.c index 3573cbfb0f..b4eb886e2a 100644 --- a/imap-send.c +++ b/imap-send.c @@ -511,7 +511,7 @@ static int nfsnprintf(char *buf, int blen, const char *fmt, ...) va_start(va, fmt); if (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen) - die("BUG: buffer too small. Please report a bug."); + BUG("buffer too small. Please report a bug."); va_end(va); return ret; } diff --git a/lockfile.c b/lockfile.c index efcb7d7dfe..8e8ab4f29f 100644 --- a/lockfile.c +++ b/lockfile.c @@ -193,7 +193,7 @@ char *get_locked_file_path(struct lock_file *lk) strbuf_addstr(&ret, get_tempfile_path(lk->tempfile)); if (ret.len <= LOCK_SUFFIX_LEN || strcmp(ret.buf + ret.len - LOCK_SUFFIX_LEN, LOCK_SUFFIX)) - die("BUG: get_locked_file_path() called for malformed lock object"); + BUG("get_locked_file_path() called for malformed lock object"); /* remove ".lock": */ strbuf_setlen(&ret, ret.len - LOCK_SUFFIX_LEN); return strbuf_detach(&ret, NULL); diff --git a/log-tree.c b/log-tree.c index 724bae0de2..4aef85331e 100644 --- a/log-tree.c +++ b/log-tree.c @@ -387,11 +387,15 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit, graph_show_oneline(opt->graph); } if (opt->mime_boundary && maybe_multipart) { - static char subject_buffer[1024]; - static char buffer[1024]; + static struct strbuf subject_buffer = STRBUF_INIT; + static struct strbuf buffer = STRBUF_INIT; struct strbuf filename = STRBUF_INIT; *need_8bit_cte_p = -1; /* NEVER */ - snprintf(subject_buffer, sizeof(subject_buffer) - 1, + + strbuf_reset(&subject_buffer); + strbuf_reset(&buffer); + + strbuf_addf(&subject_buffer, "%s" "MIME-Version: 1.0\n" "Content-Type: multipart/mixed;" @@ -406,13 +410,13 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit, extra_headers ? extra_headers : "", mime_boundary_leader, opt->mime_boundary, mime_boundary_leader, opt->mime_boundary); - extra_headers = subject_buffer; + extra_headers = subject_buffer.buf; if (opt->numbered_files) strbuf_addf(&filename, "%d", opt->nr); else fmt_output_commit(&filename, commit, opt); - snprintf(buffer, sizeof(buffer) - 1, + strbuf_addf(&buffer, "\n--%s%s\n" "Content-Type: text/x-patch;" " name=\"%s\"\n" @@ -423,7 +427,7 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit, filename.buf, opt->no_inline ? "attachment" : "inline", filename.buf); - opt->diffopt.stat_sep = buffer; + opt->diffopt.stat_sep = buffer.buf; strbuf_release(&filename); } *extra_headers_p = extra_headers; diff --git a/mailinfo.c b/mailinfo.c index d04142ccc7..3281a37d51 100644 --- a/mailinfo.c +++ b/mailinfo.c @@ -716,7 +716,7 @@ static void flush_inbody_header_accum(struct mailinfo *mi) if (!mi->inbody_header_accum.len) return; if (!check_header(mi, &mi->inbody_header_accum, mi->s_hdr_data, 0)) - die("BUG: inbody_header_accum, if not empty, must always contain a valid in-body header"); + BUG("inbody_header_accum, if not empty, must always contain a valid in-body header"); strbuf_reset(&mi->inbody_header_accum); } diff --git a/merge-recursive.c b/merge-recursive.c index 35df695fa4..ac27abbd4c 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -23,6 +23,7 @@ #include "merge-recursive.h" #include "dir.h" #include "submodule.h" +#include "revision.h" struct path_hashmap_entry { struct hashmap_entry e; @@ -337,10 +338,10 @@ static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree) init_tree_desc(desc, tree->buffer, tree->size); } -static int git_merge_trees(struct merge_options *o, - struct tree *common, - struct tree *head, - struct tree *merge) +static int unpack_trees_start(struct merge_options *o, + struct tree *common, + struct tree *head, + struct tree *merge) { int rc; struct tree_desc t[3]; @@ -356,6 +357,7 @@ static int git_merge_trees(struct merge_options *o, o->unpack_opts.fn = threeway_merge; o->unpack_opts.src_index = &the_index; o->unpack_opts.dst_index = &tmp_index; + o->unpack_opts.aggressive = !merge_detect_rename(o); setup_unpack_trees_porcelain(&o->unpack_opts, "merge"); init_tree_desc_from_tree(t+0, common); @@ -378,6 +380,12 @@ static int git_merge_trees(struct merge_options *o, return rc; } +static void unpack_trees_finish(struct merge_options *o) +{ + discard_index(&o->orig_index); + clear_unpack_trees_porcelain(&o->unpack_opts); +} + struct tree *write_tree_from_memory(struct merge_options *o) { struct tree *result = NULL; @@ -391,7 +399,7 @@ struct tree *write_tree_from_memory(struct merge_options *o) fprintf(stderr, "BUG: %d %.*s\n", ce_stage(ce), (int)ce_namelen(ce), ce->name); } - die("BUG: unmerged index entries in merge-recursive.c"); + BUG("unmerged index entries in merge-recursive.c"); } if (!active_cache_tree) @@ -1081,6 +1089,185 @@ static int merge_3way(struct merge_options *o, return merge_status; } +static int find_first_merges(struct object_array *result, const char *path, + struct commit *a, struct commit *b) +{ + int i, j; + struct object_array merges = OBJECT_ARRAY_INIT; + struct commit *commit; + int contains_another; + + char merged_revision[42]; + const char *rev_args[] = { "rev-list", "--merges", "--ancestry-path", + "--all", merged_revision, NULL }; + struct rev_info revs; + struct setup_revision_opt rev_opts; + + memset(result, 0, sizeof(struct object_array)); + memset(&rev_opts, 0, sizeof(rev_opts)); + + /* get all revisions that merge commit a */ + xsnprintf(merged_revision, sizeof(merged_revision), "^%s", + oid_to_hex(&a->object.oid)); + init_revisions(&revs, NULL); + rev_opts.submodule = path; + /* FIXME: can't handle linked worktrees in submodules yet */ + revs.single_worktree = path != NULL; + setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts); + + /* save all revisions from the above list that contain b */ + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); + while ((commit = get_revision(&revs)) != NULL) { + struct object *o = &(commit->object); + if (in_merge_bases(b, commit)) + add_object_array(o, NULL, &merges); + } + reset_revision_walk(); + + /* Now we've got all merges that contain a and b. Prune all + * merges that contain another found merge and save them in + * result. + */ + for (i = 0; i < merges.nr; i++) { + struct commit *m1 = (struct commit *) merges.objects[i].item; + + contains_another = 0; + for (j = 0; j < merges.nr; j++) { + struct commit *m2 = (struct commit *) merges.objects[j].item; + if (i != j && in_merge_bases(m2, m1)) { + contains_another = 1; + break; + } + } + + if (!contains_another) + add_object_array(merges.objects[i].item, NULL, result); + } + + object_array_clear(&merges); + return result->nr; +} + +static void print_commit(struct commit *commit) +{ + struct strbuf sb = STRBUF_INIT; + struct pretty_print_context ctx = {0}; + ctx.date_mode.type = DATE_NORMAL; + format_commit_message(commit, " %h: %m %s", &sb, &ctx); + fprintf(stderr, "%s\n", sb.buf); + strbuf_release(&sb); +} + +static int merge_submodule(struct merge_options *o, + struct object_id *result, const char *path, + const struct object_id *base, const struct object_id *a, + const struct object_id *b) +{ + struct commit *commit_base, *commit_a, *commit_b; + int parent_count; + struct object_array merges; + + int i; + int search = !o->call_depth; + + /* store a in result in case we fail */ + oidcpy(result, a); + + /* we can not handle deletion conflicts */ + if (is_null_oid(base)) + return 0; + if (is_null_oid(a)) + return 0; + if (is_null_oid(b)) + return 0; + + if (add_submodule_odb(path)) { + output(o, 1, _("Failed to merge submodule %s (not checked out)"), path); + return 0; + } + + if (!(commit_base = lookup_commit_reference(base)) || + !(commit_a = lookup_commit_reference(a)) || + !(commit_b = lookup_commit_reference(b))) { + output(o, 1, _("Failed to merge submodule %s (commits not present)"), path); + return 0; + } + + /* check whether both changes are forward */ + if (!in_merge_bases(commit_base, commit_a) || + !in_merge_bases(commit_base, commit_b)) { + output(o, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path); + return 0; + } + + /* Case #1: a is contained in b or vice versa */ + if (in_merge_bases(commit_a, commit_b)) { + oidcpy(result, b); + if (show(o, 3)) { + output(o, 3, _("Fast-forwarding submodule %s to the following commit:"), path); + output_commit_title(o, commit_b); + } else if (show(o, 2)) + output(o, 2, _("Fast-forwarding submodule %s to %s"), path, oid_to_hex(b)); + else + ; /* no output */ + + return 1; + } + if (in_merge_bases(commit_b, commit_a)) { + oidcpy(result, a); + if (show(o, 3)) { + output(o, 3, _("Fast-forwarding submodule %s to the following commit:"), path); + output_commit_title(o, commit_a); + } else if (show(o, 2)) + output(o, 2, _("Fast-forwarding submodule %s to %s"), path, oid_to_hex(a)); + else + ; /* no output */ + + return 1; + } + + /* + * Case #2: There are one or more merges that contain a and b in + * the submodule. If there is only one, then present it as a + * suggestion to the user, but leave it marked unmerged so the + * user needs to confirm the resolution. + */ + + /* Skip the search if makes no sense to the calling context. */ + if (!search) + return 0; + + /* find commit which merges them */ + parent_count = find_first_merges(&merges, path, commit_a, commit_b); + switch (parent_count) { + case 0: + output(o, 1, _("Failed to merge submodule %s (merge following commits not found)"), path); + break; + + case 1: + output(o, 1, _("Failed to merge submodule %s (not fast-forward)"), path); + output(o, 2, _("Found a possible merge resolution for the submodule:\n")); + print_commit((struct commit *) merges.objects[0].item); + output(o, 2, _( + "If this is correct simply add it to the index " + "for example\n" + "by using:\n\n" + " git update-index --cacheinfo 160000 %s \"%s\"\n\n" + "which will accept this suggestion.\n"), + oid_to_hex(&merges.objects[0].item->oid), path); + break; + + default: + output(o, 1, _("Failed to merge submodule %s (multiple merges found)"), path); + for (i = 0; i < merges.nr; i++) + print_commit((struct commit *) merges.objects[i].item); + } + + object_array_clear(&merges); + return 0; +} + static int merge_file_1(struct merge_options *o, const struct diff_filespec *one, const struct diff_filespec *a, @@ -1144,12 +1331,11 @@ static int merge_file_1(struct merge_options *o, return ret; result->clean = (merge_status == 0); } else if (S_ISGITLINK(a->mode)) { - result->clean = merge_submodule(&result->oid, + result->clean = merge_submodule(o, &result->oid, one->path, &one->oid, &a->oid, - &b->oid, - !o->call_depth); + &b->oid); } else if (S_ISLNK(a->mode)) { switch (o->recursive_variant) { case MERGE_RECURSIVE_NORMAL: @@ -1165,7 +1351,7 @@ static int merge_file_1(struct merge_options *o, break; } } else - die("BUG: unsupported object type in the tree"); + BUG("unsupported object type in the tree"); } if (result->merge) @@ -1603,7 +1789,15 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o, diff_setup(&opts); opts.flags.recursive = 1; opts.flags.rename_empty = 0; - opts.detect_rename = DIFF_DETECT_RENAME; + opts.detect_rename = merge_detect_rename(o); + /* + * We do not have logic to handle the detection of copies. In + * fact, it may not even make sense to add such logic: would we + * really want a change to a base file to be propagated through + * multiple other files by a merge? + */ + if (opts.detect_rename > DIFF_DETECT_RENAME) + opts.detect_rename = DIFF_DETECT_RENAME; opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit : o->diff_rename_limit >= 0 ? o->diff_rename_limit : 1000; @@ -2410,7 +2604,7 @@ static int process_renames(struct merge_options *o, const char *ren2_dst = ren2->pair->two->path; enum rename_type rename_type; if (strcmp(ren1_src, ren2_src) != 0) - die("BUG: ren1_src != ren2_src"); + BUG("ren1_src != ren2_src"); ren2->dst_entry->processed = 1; ren2->processed = 1; if (strcmp(ren1_dst, ren2_dst) != 0) { @@ -2444,7 +2638,7 @@ static int process_renames(struct merge_options *o, ren2 = lookup->util; ren2_dst = ren2->pair->two->path; if (strcmp(ren1_dst, ren2_dst) != 0) - die("BUG: ren1_dst != ren2_dst"); + BUG("ren1_dst != ren2_dst"); clean_merge = 0; ren2->processed = 1; @@ -2643,7 +2837,7 @@ static int handle_renames(struct merge_options *o, ri->head_renames = NULL; ri->merge_renames = NULL; - if (!o->detect_rename) + if (!merge_detect_rename(o)) return 1; head_pairs = get_diffpairs(o, common, head); @@ -3048,7 +3242,7 @@ static int process_entry(struct merge_options *o, */ remove_file(o, 1, path, !a_mode); } else - die("BUG: fatal merge failure, shouldn't happen."); + BUG("fatal merge failure, shouldn't happen."); return clean_merge; } @@ -3079,13 +3273,14 @@ int merge_trees(struct merge_options *o, return 1; } - code = git_merge_trees(o, common, head, merge); + code = unpack_trees_start(o, common, head, merge); if (code != 0) { if (show(o, 4) || o->call_depth) err(o, _("merging of trees %s and %s failed"), oid_to_hex(&head->object.oid), oid_to_hex(&merge->object.oid)); + unpack_trees_finish(o); return -1; } @@ -3126,7 +3321,7 @@ int merge_trees(struct merge_options *o, for (i = 0; i < entries->nr; i++) { struct stage_data *e = entries->items[i].util; if (!e->processed) - die("BUG: unprocessed path??? %s", + BUG("unprocessed path??? %s", entries->items[i].string); } @@ -3138,20 +3333,15 @@ cleanup: hashmap_free(&o->current_file_dir_set, 1); - if (clean < 0) + if (clean < 0) { + unpack_trees_finish(o); return clean; + } } else clean = 1; - /* Free the extra index left from git_merge_trees() */ - /* - * FIXME: Need to also free data allocated by - * setup_unpack_trees_porcelain() tucked away in o->unpack_opts.msgs, - * but the problem is that only half of it refers to dynamically - * allocated data, while the other half points at static strings. - */ - discard_index(&o->orig_index); + unpack_trees_finish(o); if (o->call_depth && !(*result = write_tree_from_memory(o))) return -1; @@ -3325,9 +3515,18 @@ int merge_recursive_generic(struct merge_options *o, static void merge_recursive_config(struct merge_options *o) { + char *value = NULL; git_config_get_int("merge.verbosity", &o->verbosity); git_config_get_int("diff.renamelimit", &o->diff_rename_limit); git_config_get_int("merge.renamelimit", &o->merge_rename_limit); + if (!git_config_get_string("diff.renames", &value)) { + o->diff_detect_rename = git_config_rename("diff.renames", value); + free(value); + } + if (!git_config_get_string("merge.renames", &value)) { + o->merge_detect_rename = git_config_rename("merge.renames", value); + free(value); + } git_config(git_xmerge_config, NULL); } @@ -3340,7 +3539,8 @@ void init_merge_options(struct merge_options *o) o->diff_rename_limit = -1; o->merge_rename_limit = -1; o->renormalize = 0; - o->detect_rename = 1; + o->diff_detect_rename = -1; + o->merge_detect_rename = -1; merge_recursive_config(o); merge_verbosity = getenv("GIT_MERGE_VERBOSITY"); if (merge_verbosity) @@ -3391,16 +3591,16 @@ int parse_merge_opt(struct merge_options *o, const char *s) else if (!strcmp(s, "no-renormalize")) o->renormalize = 0; else if (!strcmp(s, "no-renames")) - o->detect_rename = 0; + o->merge_detect_rename = 0; else if (!strcmp(s, "find-renames")) { - o->detect_rename = 1; + o->merge_detect_rename = 1; o->rename_score = 0; } else if (skip_prefix(s, "find-renames=", &arg) || skip_prefix(s, "rename-threshold=", &arg)) { if ((o->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0) return -1; - o->detect_rename = 1; + o->merge_detect_rename = 1; } else return -1; diff --git a/merge-recursive.h b/merge-recursive.h index 248093e407..fa7bc6b683 100644 --- a/merge-recursive.h +++ b/merge-recursive.h @@ -18,7 +18,8 @@ struct merge_options { unsigned renormalize : 1; long xdl_opts; int verbosity; - int detect_rename; + int diff_detect_rename; + int merge_detect_rename; int diff_rename_limit; int merge_rename_limit; int rename_score; @@ -57,6 +58,12 @@ struct collision_entry { unsigned reported_already:1; }; +static inline int merge_detect_rename(struct merge_options *o) +{ + return o->merge_detect_rename >= 0 ? o->merge_detect_rename : + o->diff_detect_rename >= 0 ? o->diff_detect_rename : 1; +} + /* merge_trees() but with recursive ancestor consolidation */ int merge_recursive(struct merge_options *o, struct commit *h1, @@ -11,10 +11,7 @@ static const char *merge_argument(struct commit *commit) { - if (commit) - return oid_to_hex(&commit->object.oid); - else - return EMPTY_TREE_SHA1_HEX; + return oid_to_hex(commit ? &commit->object.oid : the_hash_algo->empty_tree); } int index_has_changes(struct strbuf *sb) @@ -94,8 +91,24 @@ int checkout_fast_forward(const struct object_id *head, return -1; memset(&trees, 0, sizeof(trees)); - memset(&opts, 0, sizeof(opts)); memset(&t, 0, sizeof(t)); + + trees[nr_trees] = parse_tree_indirect(head); + if (!trees[nr_trees++]) { + rollback_lock_file(&lock_file); + return -1; + } + trees[nr_trees] = parse_tree_indirect(remote); + if (!trees[nr_trees++]) { + rollback_lock_file(&lock_file); + return -1; + } + for (i = 0; i < nr_trees; i++) { + parse_tree(trees[i]); + init_tree_desc(t+i, trees[i]->buffer, trees[i]->size); + } + + memset(&opts, 0, sizeof(opts)); if (overwrite_ignore) { memset(&dir, 0, sizeof(dir)); dir.flags |= DIR_SHOW_IGNORED; @@ -112,24 +125,13 @@ int checkout_fast_forward(const struct object_id *head, opts.fn = twoway_merge; setup_unpack_trees_porcelain(&opts, "merge"); - trees[nr_trees] = parse_tree_indirect(head); - if (!trees[nr_trees++]) { - rollback_lock_file(&lock_file); - return -1; - } - trees[nr_trees] = parse_tree_indirect(remote); - if (!trees[nr_trees++]) { - rollback_lock_file(&lock_file); - return -1; - } - for (i = 0; i < nr_trees; i++) { - parse_tree(trees[i]); - init_tree_desc(t+i, trees[i]->buffer, trees[i]->size); - } if (unpack_trees(nr_trees, t, &opts)) { rollback_lock_file(&lock_file); + clear_unpack_trees_porcelain(&opts); return -1; } + clear_unpack_trees_porcelain(&opts); + if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) return error(_("unable to write new index file")); return 0; diff --git a/notes-merge.c b/notes-merge.c index e06d71ea47..d613e06574 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -442,7 +442,7 @@ static int merge_one_change(struct notes_merge_options *o, printf("Using remote notes for %s\n", oid_to_hex(&p->obj)); if (add_note(t, &p->obj, &p->remote, combine_notes_overwrite)) - die("BUG: combine_notes_overwrite failed"); + BUG("combine_notes_overwrite failed"); return 0; case NOTES_MERGE_RESOLVE_UNION: if (o->verbosity >= 2) @@ -490,7 +490,7 @@ static int merge_changes(struct notes_merge_options *o, trace_printf("\t\t\tno local change, adopted remote\n"); if (add_note(t, &p->obj, &p->remote, combine_notes_overwrite)) - die("BUG: combine_notes_overwrite failed"); + BUG("combine_notes_overwrite failed"); } else { /* need file-level merge between local and remote */ trace_printf("\t\t\tneed content-level merge\n"); diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c index 72d9daec7e..7b2dc3e7dc 100644 --- a/pack-bitmap-write.c +++ b/pack-bitmap-write.c @@ -477,7 +477,7 @@ static void write_selected_commits_v1(struct hashfile *f, sha1_pos(stored->commit->object.oid.hash, index, index_nr, sha1_access); if (commit_pos < 0) - die("BUG: trying to write commit not in index"); + BUG("trying to write commit not in index"); hashwrite_be32(f, commit_pos); hashwrite_u8(f, stored->xor_offset); diff --git a/pack-bitmap.c b/pack-bitmap.c index c9e90d1bb5..06771113fb 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c @@ -255,7 +255,7 @@ static char *pack_bitmap_filename(struct packed_git *p) size_t len; if (!strip_suffix(p->pack_name, ".pack", &len)) - die("BUG: pack_name does not end in .pack"); + BUG("pack_name does not end in .pack"); return xstrfmt("%.*s.bitmap", (int)len, p->pack_name); } @@ -723,13 +723,13 @@ int prepare_bitmap_walk(struct rev_info *revs) revs->ignore_missing_links = 0; if (haves_bitmap == NULL) - die("BUG: failed to perform bitmap walk"); + BUG("failed to perform bitmap walk"); } wants_bitmap = find_objects(revs, wants, haves_bitmap); if (!wants_bitmap) - die("BUG: failed to perform bitmap walk"); + BUG("failed to perform bitmap walk"); if (haves_bitmap) bitmap_and_not(wants_bitmap, haves_bitmap); diff --git a/pack-objects.c b/pack-objects.c index e0c4600568..92708522e7 100644 --- a/pack-objects.c +++ b/pack-objects.c @@ -60,7 +60,7 @@ static void rehash_objects(struct packing_data *pdata) &found); if (found) - die("BUG: Duplicate object in hash"); + BUG("Duplicate object in hash"); pdata->index[ix] = i + 1; entry++; diff --git a/packfile.c b/packfile.c index 9e7e693fb5..1a714fbde9 100644 --- a/packfile.c +++ b/packfile.c @@ -84,6 +84,7 @@ static int check_packed_git_idx(const char *path, struct packed_git *p) uint32_t version, nr, i, *index; int fd = git_open(path); struct stat st; + const unsigned int hashsz = the_hash_algo->rawsz; if (fd < 0) return -1; @@ -92,7 +93,7 @@ static int check_packed_git_idx(const char *path, struct packed_git *p) return -1; } idx_size = xsize_t(st.st_size); - if (idx_size < 4 * 256 + 20 + 20) { + if (idx_size < 4 * 256 + hashsz + hashsz) { close(fd); return error("index file %s is too small", path); } @@ -129,11 +130,11 @@ static int check_packed_git_idx(const char *path, struct packed_git *p) /* * Total size: * - 256 index entries 4 bytes each - * - 24-byte entries * nr (20-byte sha1 + 4-byte offset) - * - 20-byte SHA1 of the packfile - * - 20-byte SHA1 file checksum + * - 24-byte entries * nr (object ID + 4-byte offset) + * - hash of the packfile + * - file checksum */ - if (idx_size != 4*256 + nr * 24 + 20 + 20) { + if (idx_size != 4*256 + nr * (hashsz + 4) + hashsz + hashsz) { munmap(idx_map, idx_size); return error("wrong index v1 file size in %s", path); } @@ -142,16 +143,16 @@ static int check_packed_git_idx(const char *path, struct packed_git *p) * Minimum size: * - 8 bytes of header * - 256 index entries 4 bytes each - * - 20-byte sha1 entry * nr + * - object ID entry * nr * - 4-byte crc entry * nr * - 4-byte offset entry * nr - * - 20-byte SHA1 of the packfile - * - 20-byte SHA1 file checksum + * - hash of the packfile + * - file checksum * And after the 4-byte offset table might be a * variable sized table containing 8-byte entries * for offsets larger than 2^31. */ - unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20; + unsigned long min_size = 8 + 4*256 + nr*(hashsz + 4 + 4) + hashsz + hashsz; unsigned long max_size = min_size; if (nr) max_size += (nr - 1)*8; @@ -188,7 +189,7 @@ int open_pack_index(struct packed_git *p) return 0; if (!strip_suffix(p->pack_name, ".pack", &len)) - die("BUG: pack_name does not end in .pack"); + BUG("pack_name does not end in .pack"); idx_name = xstrfmt("%.*s.idx", (int)len, p->pack_name); ret = check_packed_git_idx(idx_name, p); free(idx_name); @@ -317,7 +318,7 @@ void close_all_packs(struct raw_object_store *o) for (p = o->packed_git; p; p = p->next) if (p->do_not_close) - die("BUG: want to close pack marked 'do-not-close'"); + BUG("want to close pack marked 'do-not-close'"); else close_pack(p); } @@ -444,10 +445,11 @@ static int open_packed_git_1(struct packed_git *p) { struct stat st; struct pack_header hdr; - unsigned char sha1[20]; - unsigned char *idx_sha1; + unsigned char hash[GIT_MAX_RAWSZ]; + unsigned char *idx_hash; long fd_flag; ssize_t read_result; + const unsigned hashsz = the_hash_algo->rawsz; if (!p->index_data && open_pack_index(p)) return error("packfile %s index unavailable", p->pack_name); @@ -507,15 +509,15 @@ static int open_packed_git_1(struct packed_git *p) " while index indicates %"PRIu32" objects", p->pack_name, ntohl(hdr.hdr_entries), p->num_objects); - if (lseek(p->pack_fd, p->pack_size - sizeof(sha1), SEEK_SET) == -1) + if (lseek(p->pack_fd, p->pack_size - hashsz, SEEK_SET) == -1) return error("end of packfile %s is unavailable", p->pack_name); - read_result = read_in_full(p->pack_fd, sha1, sizeof(sha1)); + read_result = read_in_full(p->pack_fd, hash, hashsz); if (read_result < 0) return error_errno("error reading from %s", p->pack_name); - if (read_result != sizeof(sha1)) + if (read_result != hashsz) return error("packfile %s signature is unavailable", p->pack_name); - idx_sha1 = ((unsigned char *)p->index_data) + p->index_size - 40; - if (hashcmp(sha1, idx_sha1)) + idx_hash = ((unsigned char *)p->index_data) + p->index_size - hashsz * 2; + if (hashcmp(hash, idx_hash)) return error("packfile %s does not match index", p->pack_name); return 0; } @@ -530,7 +532,7 @@ static int open_packed_git(struct packed_git *p) static int in_window(struct pack_window *win, off_t offset) { - /* We must promise at least 20 bytes (one hash) after the + /* We must promise at least one full hash after the * offset is available from this window, otherwise the offset * is not actually in this window and a different window (which * has that one hash excess) must be used. This is to support @@ -538,7 +540,7 @@ static int in_window(struct pack_window *win, off_t offset) */ off_t win_off = win->offset; return win_off <= offset - && (offset + 20) <= (win_off + win->len); + && (offset + the_hash_algo->rawsz) <= (win_off + win->len); } unsigned char *use_pack(struct packed_git *p, @@ -555,7 +557,7 @@ unsigned char *use_pack(struct packed_git *p, */ if (!p->pack_size && p->pack_fd == -1 && open_packed_git(p)) die("packfile %s cannot be accessed", p->pack_name); - if (offset > (p->pack_size - 20)) + if (offset > (p->pack_size - the_hash_algo->rawsz)) die("offset beyond end of packfile (truncated pack?)"); if (offset < 0) die(_("offset before end of packfile (broken .idx?)")); @@ -675,7 +677,8 @@ struct packed_git *add_packed_git(const char *path, size_t path_len, int local) p->pack_size = st.st_size; p->pack_local = local; p->mtime = st.st_mtime; - if (path_len < 40 || get_sha1_hex(path + path_len - 40, p->sha1)) + if (path_len < the_hash_algo->hexsz || + get_sha1_hex(path + path_len - the_hash_algo->hexsz, p->sha1)) hashclr(p->sha1); return p; } @@ -1028,7 +1031,8 @@ const struct packed_git *has_packed_and_bad(const unsigned char *sha1) for (p = the_repository->objects->packed_git; p; p = p->next) for (i = 0; i < p->num_bad_objects; i++) - if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i)) + if (!hashcmp(sha1, + p->bad_object_sha1 + the_hash_algo->rawsz * i)) return p; return NULL; } @@ -1066,7 +1070,7 @@ static off_t get_delta_base(struct packed_git *p, } else if (type == OBJ_REF_DELTA) { /* The base entry _must_ be in the same pack */ base_offset = find_pack_entry_one(base_info, p); - *curpos += 20; + *curpos += the_hash_algo->rawsz; } else die("I am totally screwed"); return base_offset; @@ -1567,7 +1571,7 @@ void *unpack_entry(struct repository *r, struct packed_git *p, off_t obj_offset, case OBJ_OFS_DELTA: case OBJ_REF_DELTA: if (data) - die("BUG: unpack_entry: left loop at a valid delta"); + BUG("unpack_entry: left loop at a valid delta"); break; case OBJ_COMMIT: case OBJ_TREE: @@ -1677,6 +1681,7 @@ int bsearch_pack(const struct object_id *oid, const struct packed_git *p, uint32 { const unsigned char *index_fanout = p->index_data; const unsigned char *index_lookup; + const unsigned int hashsz = the_hash_algo->rawsz; int index_lookup_width; if (!index_fanout) @@ -1684,10 +1689,10 @@ int bsearch_pack(const struct object_id *oid, const struct packed_git *p, uint32 index_lookup = index_fanout + 4 * 256; if (p->index_version == 1) { - index_lookup_width = 24; + index_lookup_width = hashsz + 4; index_lookup += 4; } else { - index_lookup_width = 20; + index_lookup_width = hashsz; index_fanout += 8; index_lookup += 8; } @@ -1700,6 +1705,7 @@ const unsigned char *nth_packed_object_sha1(struct packed_git *p, uint32_t n) { const unsigned char *index = p->index_data; + const unsigned int hashsz = the_hash_algo->rawsz; if (!index) { if (open_pack_index(p)) return NULL; @@ -1709,10 +1715,10 @@ const unsigned char *nth_packed_object_sha1(struct packed_git *p, return NULL; index += 4 * 256; if (p->index_version == 1) { - return index + 24 * n + 4; + return index + (hashsz + 4) * n + 4; } else { index += 8; - return index + 20 * n; + return index + hashsz * n; } } @@ -1744,12 +1750,13 @@ void check_pack_index_ptr(const struct packed_git *p, const void *vptr) off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n) { const unsigned char *index = p->index_data; + const unsigned int hashsz = the_hash_algo->rawsz; index += 4 * 256; if (p->index_version == 1) { - return ntohl(*((uint32_t *)(index + 24 * n))); + return ntohl(*((uint32_t *)(index + (hashsz + 4) * n))); } else { uint32_t off; - index += 8 + p->num_objects * (20 + 4); + index += 8 + p->num_objects * (hashsz + 4); off = ntohl(*((uint32_t *)(index + 4 * n))); if (!(off & 0x80000000)) return off; @@ -1811,7 +1818,7 @@ struct packed_git *find_sha1_pack(const unsigned char *sha1, } -static int fill_pack_entry(const unsigned char *sha1, +static int fill_pack_entry(const struct object_id *oid, struct pack_entry *e, struct packed_git *p) { @@ -1820,11 +1827,12 @@ static int fill_pack_entry(const unsigned char *sha1, if (p->num_bad_objects) { unsigned i; for (i = 0; i < p->num_bad_objects; i++) - if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i)) + if (!hashcmp(oid->hash, + p->bad_object_sha1 + the_hash_algo->rawsz * i)) return 0; } - offset = find_pack_entry_one(sha1, p); + offset = find_pack_entry_one(oid->hash, p); if (!offset) return 0; @@ -1839,11 +1847,10 @@ static int fill_pack_entry(const unsigned char *sha1, return 0; e->offset = offset; e->p = p; - hashcpy(e->sha1, sha1); return 1; } -int find_pack_entry(struct repository *r, const unsigned char *sha1, struct pack_entry *e) +int find_pack_entry(struct repository *r, const struct object_id *oid, struct pack_entry *e) { struct list_head *pos; @@ -1853,7 +1860,7 @@ int find_pack_entry(struct repository *r, const unsigned char *sha1, struct pack list_for_each(pos, &r->objects->packed_git_mru) { struct packed_git *p = list_entry(pos, struct packed_git, mru); - if (fill_pack_entry(sha1, e, p)) { + if (fill_pack_entry(oid, e, p)) { list_move(&p->mru, &r->objects->packed_git_mru); return 1; } @@ -1861,10 +1868,10 @@ int find_pack_entry(struct repository *r, const unsigned char *sha1, struct pack return 0; } -int has_sha1_pack(const unsigned char *sha1) +int has_object_pack(const struct object_id *oid) { struct pack_entry e; - return find_pack_entry(the_repository, sha1, &e); + return find_pack_entry(the_repository, oid, &e); } int has_pack_index(const unsigned char *sha1) diff --git a/packfile.h b/packfile.h index bfd0b5399d..e0a38aba93 100644 --- a/packfile.h +++ b/packfile.h @@ -137,9 +137,9 @@ extern const struct packed_git *has_packed_and_bad(const unsigned char *sha1); * Iff a pack file in the given repository contains the object named by sha1, * return true and store its location to e. */ -extern int find_pack_entry(struct repository *r, const unsigned char *sha1, struct pack_entry *e); +extern int find_pack_entry(struct repository *r, const struct object_id *oid, struct pack_entry *e); -extern int has_sha1_pack(const unsigned char *sha1); +extern int has_object_pack(const struct object_id *oid); extern int has_pack_index(const unsigned char *sha1); @@ -2,6 +2,7 @@ #include "config.h" #include "run-command.h" #include "sigchain.h" +#include "alias.h" #ifndef DEFAULT_PAGER #define DEFAULT_PAGER "less" @@ -1306,7 +1306,7 @@ static int only_spaces_and_periods(const char *path, size_t len, size_t skip) int is_ntfs_dotgit(const char *name) { - int len; + size_t len; for (len = 0; ; len++) if (!name[len] || name[len] == '\\' || is_dir_sep(name[len])) { @@ -1323,6 +1323,90 @@ int is_ntfs_dotgit(const char *name) } } +static int is_ntfs_dot_generic(const char *name, + const char *dotgit_name, + size_t len, + const char *dotgit_ntfs_shortname_prefix) +{ + int saw_tilde; + size_t i; + + if ((name[0] == '.' && !strncasecmp(name + 1, dotgit_name, len))) { + i = len + 1; +only_spaces_and_periods: + for (;;) { + char c = name[i++]; + if (!c) + return 1; + if (c != ' ' && c != '.') + return 0; + } + } + + /* + * Is it a regular NTFS short name, i.e. shortened to 6 characters, + * followed by ~1, ... ~4? + */ + if (!strncasecmp(name, dotgit_name, 6) && name[6] == '~' && + name[7] >= '1' && name[7] <= '4') { + i = 8; + goto only_spaces_and_periods; + } + + /* + * Is it a fall-back NTFS short name (for details, see + * https://en.wikipedia.org/wiki/8.3_filename? + */ + for (i = 0, saw_tilde = 0; i < 8; i++) + if (name[i] == '\0') + return 0; + else if (saw_tilde) { + if (name[i] < '0' || name[i] > '9') + return 0; + } else if (name[i] == '~') { + if (name[++i] < '1' || name[i] > '9') + return 0; + saw_tilde = 1; + } else if (i >= 6) + return 0; + else if (name[i] < 0) { + /* + * We know our needles contain only ASCII, so we clamp + * here to make the results of tolower() sane. + */ + return 0; + } else if (tolower(name[i]) != dotgit_ntfs_shortname_prefix[i]) + return 0; + + goto only_spaces_and_periods; +} + +/* + * Inline helper to make sure compiler resolves strlen() on literals at + * compile time. + */ +static inline int is_ntfs_dot_str(const char *name, const char *dotgit_name, + const char *dotgit_ntfs_shortname_prefix) +{ + return is_ntfs_dot_generic(name, dotgit_name, strlen(dotgit_name), + dotgit_ntfs_shortname_prefix); +} + +int is_ntfs_dotgitmodules(const char *name) +{ + return is_ntfs_dot_str(name, "gitmodules", "gi7eba"); +} + +int is_ntfs_dotgitignore(const char *name) +{ + return is_ntfs_dot_str(name, "gitignore", "gi250a"); +} + +int is_ntfs_dotgitattributes(const char *name) +{ + return is_ntfs_dot_str(name, "gitattributes", "gi7d29"); +} + int looks_like_command_line_option(const char *str) { return str && str[0] == '-'; diff --git a/pathspec.c b/pathspec.c index 82eb39cd67..27cd606786 100644 --- a/pathspec.c +++ b/pathspec.c @@ -198,7 +198,7 @@ static void parse_pathspec_attr_match(struct pathspec_item *item, const char *va } if (item->attr_check->nr != item->attr_match_nr) - die("BUG: should have same number of entries"); + BUG("should have same number of entries"); string_list_clear(&list, 0); } @@ -422,7 +422,7 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags, if (pathspec_prefix >= 0 && (prefixlen || (prefix && *prefix))) - die("BUG: 'prefix' magic is supposed to be used at worktree's root"); + BUG("'prefix' magic is supposed to be used at worktree's root"); if ((magic & PATHSPEC_LITERAL) && (magic & PATHSPEC_GLOB)) die(_("%s: 'literal' and 'glob' are incompatible"), elt); @@ -486,7 +486,7 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags, /* sanity checks, pathspec matchers assume these are sane */ if (item->nowildcard_len > item->len || item->prefix > item->len) { - die ("BUG: error initializing pathspec_item"); + BUG("error initializing pathspec_item"); } } @@ -545,7 +545,7 @@ void parse_pathspec(struct pathspec *pathspec, if ((flags & PATHSPEC_PREFER_CWD) && (flags & PATHSPEC_PREFER_FULL)) - die("BUG: PATHSPEC_PREFER_CWD and PATHSPEC_PREFER_FULL are incompatible"); + BUG("PATHSPEC_PREFER_CWD and PATHSPEC_PREFER_FULL are incompatible"); /* No arguments with prefix -> prefix pathspec */ if (!entry) { @@ -553,7 +553,7 @@ void parse_pathspec(struct pathspec *pathspec, return; if (!(flags & PATHSPEC_PREFER_CWD)) - die("BUG: PATHSPEC_PREFER_CWD requires arguments"); + BUG("PATHSPEC_PREFER_CWD requires arguments"); pathspec->items = item = xcalloc(1, sizeof(*item)); item->match = xstrdup(prefix); @@ -609,7 +609,7 @@ void parse_pathspec(struct pathspec *pathspec, if (pathspec->magic & PATHSPEC_MAXDEPTH) { if (flags & PATHSPEC_KEEP_ORDER) - die("BUG: PATHSPEC_MAXDEPTH_VALID and PATHSPEC_KEEP_ORDER are incompatible"); + BUG("PATHSPEC_MAXDEPTH_VALID and PATHSPEC_KEEP_ORDER are incompatible"); QSORT(pathspec->items, pathspec->nr, pathspec_item_cmp); } } diff --git a/pkt-line.c b/pkt-line.c index 555eb2a507..a593c08aad 100644 --- a/pkt-line.c +++ b/pkt-line.c @@ -277,7 +277,7 @@ static int get_packet_data(int fd, char **src_buf, size_t *src_size, ssize_t ret; if (fd >= 0 && src_buf && *src_buf) - die("BUG: multiple sources given to packet_read"); + BUG("multiple sources given to packet_read"); /* Read up to "size" bytes from our source, whatever it is. */ if (src_buf && *src_buf) { diff --git a/prio-queue.c b/prio-queue.c index 126d096727..a078451872 100644 --- a/prio-queue.c +++ b/prio-queue.c @@ -20,7 +20,7 @@ void prio_queue_reverse(struct prio_queue *queue) int i, j; if (queue->compare != NULL) - die("BUG: prio_queue_reverse() on non-LIFO queue"); + BUG("prio_queue_reverse() on non-LIFO queue"); for (i = 0; i < (j = (queue->nr - 1) - i); i++) swap(queue, i, j); } diff --git a/read-cache.c b/read-cache.c index fa3df2e72e..372588260e 100644 --- a/read-cache.c +++ b/read-cache.c @@ -752,7 +752,7 @@ struct cache_entry *make_cache_entry(unsigned int mode, int size, len; struct cache_entry *ce, *ret; - if (!verify_path(path)) { + if (!verify_path(path, mode)) { error("Invalid path '%s'", path); return NULL; } @@ -817,7 +817,7 @@ int ce_same_name(const struct cache_entry *a, const struct cache_entry *b) * Also, we don't want double slashes or slashes at the * end that can make pathnames ambiguous. */ -static int verify_dotfile(const char *rest) +static int verify_dotfile(const char *rest, unsigned mode) { /* * The first character was '.', but that @@ -831,8 +831,13 @@ static int verify_dotfile(const char *rest) switch (*rest) { /* - * ".git" followed by NUL or slash is bad. This - * shares the path end test with the ".." case. + * ".git" followed by NUL or slash is bad. Note that we match + * case-insensitively here, even if ignore_case is not set. + * This outlaws ".GIT" everywhere out of an abundance of caution, + * since there's really no good reason to allow it. + * + * Once we've seen ".git", we can also find ".gitmodules", etc (also + * case-insensitively). */ case 'g': case 'G': @@ -840,8 +845,15 @@ static int verify_dotfile(const char *rest) break; if (rest[2] != 't' && rest[2] != 'T') break; - rest += 2; - /* fallthrough */ + if (rest[3] == '\0' || is_dir_sep(rest[3])) + return 0; + if (S_ISLNK(mode)) { + rest += 3; + if (skip_iprefix(rest, "modules", &rest) && + (*rest == '\0' || is_dir_sep(*rest))) + return 0; + } + break; case '.': if (rest[1] == '\0' || is_dir_sep(rest[1])) return 0; @@ -849,7 +861,7 @@ static int verify_dotfile(const char *rest) return 1; } -int verify_path(const char *path) +int verify_path(const char *path, unsigned mode) { char c; @@ -862,12 +874,25 @@ int verify_path(const char *path) return 1; if (is_dir_sep(c)) { inside: - if (protect_hfs && is_hfs_dotgit(path)) - return 0; - if (protect_ntfs && is_ntfs_dotgit(path)) - return 0; + if (protect_hfs) { + if (is_hfs_dotgit(path)) + return 0; + if (S_ISLNK(mode)) { + if (is_hfs_dotgitmodules(path)) + return 0; + } + } + if (protect_ntfs) { + if (is_ntfs_dotgit(path)) + return 0; + if (S_ISLNK(mode)) { + if (is_ntfs_dotgitmodules(path)) + return 0; + } + } + c = *path++; - if ((c == '.' && !verify_dotfile(path)) || + if ((c == '.' && !verify_dotfile(path, mode)) || is_dir_sep(c) || c == '\0') return 0; } @@ -1184,7 +1209,7 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e if (!ok_to_add) return -1; - if (!verify_path(ce->name)) + if (!verify_path(ce->name, ce->ce_mode)) return error("Invalid path '%s'", ce->name); if (!skip_df_check && @@ -1806,7 +1831,7 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist) if (verify_hdr(hdr, mmap_size) < 0) goto unmap; - hashcpy(istate->sha1, (const unsigned char *)hdr + mmap_size - the_hash_algo->rawsz); + hashcpy(istate->oid.hash, (const unsigned char *)hdr + mmap_size - the_hash_algo->rawsz); istate->version = ntohl(hdr->hdr_version); istate->cache_nr = ntohl(hdr->hdr_entries); istate->cache_alloc = alloc_nr(istate->cache_nr); @@ -1878,7 +1903,7 @@ int read_index_from(struct index_state *istate, const char *path, uint64_t start = getnanotime(); struct split_index *split_index; int ret; - char *base_sha1_hex; + char *base_oid_hex; char *base_path; /* istate->initialized covers both .git/index and .git/sharedindex.xxx */ @@ -1889,7 +1914,7 @@ int read_index_from(struct index_state *istate, const char *path, trace_performance_since(start, "read cache %s", path); split_index = istate->split_index; - if (!split_index || is_null_sha1(split_index->base_sha1)) { + if (!split_index || is_null_oid(&split_index->base_oid)) { post_read_index_from(istate); return ret; } @@ -1899,13 +1924,13 @@ int read_index_from(struct index_state *istate, const char *path, else split_index->base = xcalloc(1, sizeof(*split_index->base)); - base_sha1_hex = sha1_to_hex(split_index->base_sha1); - base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_sha1_hex); + base_oid_hex = oid_to_hex(&split_index->base_oid); + base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_oid_hex); ret = do_read_index(split_index->base, base_path, 1); - if (hashcmp(split_index->base_sha1, split_index->base->sha1)) + if (oidcmp(&split_index->base_oid, &split_index->base->oid)) die("broken index, expect %s in %s, got %s", - base_sha1_hex, base_path, - sha1_to_hex(split_index->base->sha1)); + base_oid_hex, base_path, + oid_to_hex(&split_index->base->oid)); freshen_shared_index(base_path, 0); merge_base_index(istate); @@ -2194,7 +2219,7 @@ static int verify_index_from(const struct index_state *istate, const char *path) if (n != the_hash_algo->rawsz) goto out; - if (hashcmp(istate->sha1, hash)) + if (hashcmp(istate->oid.hash, hash)) goto out; close(fd); @@ -2373,7 +2398,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, return -1; } - if (ce_flush(&c, newfd, istate->sha1)) + if (ce_flush(&c, newfd, istate->oid.hash)) return -1; if (close_tempfile_gently(tempfile)) { error(_("could not close '%s'"), tempfile->filename.buf); @@ -2497,10 +2522,10 @@ static int write_shared_index(struct index_state *istate, return ret; } ret = rename_tempfile(temp, - git_path("sharedindex.%s", sha1_to_hex(si->base->sha1))); + git_path("sharedindex.%s", oid_to_hex(&si->base->oid))); if (!ret) { - hashcpy(si->base_sha1, si->base->sha1); - clean_shared_index_files(sha1_to_hex(si->base->sha1)); + oidcpy(&si->base_oid, &si->base->oid); + clean_shared_index_files(oid_to_hex(&si->base->oid)); } return ret; @@ -2554,13 +2579,13 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock, if (!si || alternate_index_output || (istate->cache_changed & ~EXTMASK)) { if (si) - hashclr(si->base_sha1); + oidclr(&si->base_oid); ret = do_write_locked_index(istate, lock, flags); goto out; } if (git_env_bool("GIT_TEST_SPLIT_INDEX", 0)) { - int v = si->base_sha1[0]; + int v = si->base_oid.hash[0]; if ((v & 15) < 6) istate->cache_changed |= SPLIT_INDEX_ORDERED; } @@ -2575,7 +2600,7 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock, temp = mks_tempfile(git_path("sharedindex_XXXXXX")); if (!temp) { - hashclr(si->base_sha1); + oidclr(&si->base_oid); ret = do_write_locked_index(istate, lock, flags); goto out; } @@ -2595,7 +2620,7 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock, /* Freshen the shared index only if the split-index was written */ if (!ret && !new_shared_index) { const char *shared_index = git_path("sharedindex.%s", - sha1_to_hex(si->base_sha1)); + oid_to_hex(&si->base_oid)); freshen_shared_index(shared_index, 1); } diff --git a/ref-filter.c b/ref-filter.c index dba826e718..01c1a82075 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -824,7 +824,7 @@ static int grab_objectname(const char *name, const struct object_id *oid, v->s = xstrdup(find_unique_abbrev(oid, atom->u.objectname.length)); return 1; } else - die("BUG: unknown %%(objectname) option"); + BUG("unknown %%(objectname) option"); } return 0; } @@ -1372,7 +1372,7 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname, else *s = ""; } else - die("BUG: unhandled RR_* enum"); + BUG("unhandled RR_* enum"); } char *get_head_description(void) @@ -660,7 +660,7 @@ static int write_pseudoref(const char *pseudoref, const struct object_id *oid, { const char *filename; int fd; - static struct lock_file lock; + struct lock_file lock = LOCK_INIT; struct strbuf buf = STRBUF_INIT; int ret = -1; @@ -670,8 +670,7 @@ static int write_pseudoref(const char *pseudoref, const struct object_id *oid, strbuf_addf(&buf, "%s\n", oid_to_hex(oid)); filename = git_path("%s", pseudoref); - fd = hold_lock_file_for_update_timeout(&lock, filename, - LOCK_DIE_ON_ERROR, + fd = hold_lock_file_for_update_timeout(&lock, filename, 0, get_files_ref_lock_timeout_ms()); if (fd < 0) { strbuf_addf(err, "could not open '%s' for writing: %s", @@ -682,10 +681,21 @@ static int write_pseudoref(const char *pseudoref, const struct object_id *oid, if (old_oid) { struct object_id actual_old_oid; - if (read_ref(pseudoref, &actual_old_oid)) - die("could not read ref '%s'", pseudoref); - if (oidcmp(&actual_old_oid, old_oid)) { - strbuf_addf(err, "unexpected sha1 when writing '%s'", pseudoref); + if (read_ref(pseudoref, &actual_old_oid)) { + if (!is_null_oid(old_oid)) { + strbuf_addf(err, "could not read ref '%s'", + pseudoref); + rollback_lock_file(&lock); + goto done; + } + } else if (is_null_oid(old_oid)) { + strbuf_addf(err, "ref '%s' already exists", + pseudoref); + rollback_lock_file(&lock); + goto done; + } else if (oidcmp(&actual_old_oid, old_oid)) { + strbuf_addf(err, "unexpected object ID when writing '%s'", + pseudoref); rollback_lock_file(&lock); goto done; } @@ -706,24 +716,28 @@ done: static int delete_pseudoref(const char *pseudoref, const struct object_id *old_oid) { - static struct lock_file lock; const char *filename; filename = git_path("%s", pseudoref); if (old_oid && !is_null_oid(old_oid)) { + struct lock_file lock = LOCK_INIT; int fd; struct object_id actual_old_oid; fd = hold_lock_file_for_update_timeout( - &lock, filename, LOCK_DIE_ON_ERROR, + &lock, filename, 0, get_files_ref_lock_timeout_ms()); - if (fd < 0) - die_errno(_("Could not open '%s' for writing"), filename); + if (fd < 0) { + error_errno(_("could not open '%s' for writing"), + filename); + return -1; + } if (read_ref(pseudoref, &actual_old_oid)) die("could not read ref '%s'", pseudoref); if (oidcmp(&actual_old_oid, old_oid)) { - warning("Unexpected sha1 when deleting %s", pseudoref); + error("unexpected object ID when deleting '%s'", + pseudoref); rollback_lock_file(&lock); return -1; } @@ -960,10 +974,10 @@ void ref_transaction_free(struct ref_transaction *transaction) /* OK */ break; case REF_TRANSACTION_PREPARED: - die("BUG: free called on a prepared reference transaction"); + BUG("free called on a prepared reference transaction"); break; default: - die("BUG: unexpected reference transaction state"); + BUG("unexpected reference transaction state"); break; } @@ -985,7 +999,7 @@ struct ref_update *ref_transaction_add_update( struct ref_update *update; if (transaction->state != REF_TRANSACTION_OPEN) - die("BUG: update called for transaction that is not open"); + BUG("update called for transaction that is not open"); FLEX_ALLOC_STR(update, refname, refname); ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc); @@ -1035,7 +1049,7 @@ int ref_transaction_create(struct ref_transaction *transaction, struct strbuf *err) { if (!new_oid || is_null_oid(new_oid)) - die("BUG: create called without valid new_oid"); + BUG("create called without valid new_oid"); return ref_transaction_update(transaction, refname, new_oid, &null_oid, flags, msg, err); } @@ -1047,7 +1061,7 @@ int ref_transaction_delete(struct ref_transaction *transaction, struct strbuf *err) { if (old_oid && is_null_oid(old_oid)) - die("BUG: delete called with old_oid set to zeros"); + BUG("delete called with old_oid set to zeros"); return ref_transaction_update(transaction, refname, &null_oid, old_oid, flags, msg, err); @@ -1060,7 +1074,7 @@ int ref_transaction_verify(struct ref_transaction *transaction, struct strbuf *err) { if (!old_oid) - die("BUG: verify called with old_oid set to NULL"); + BUG("verify called with old_oid set to NULL"); return ref_transaction_update(transaction, refname, NULL, old_oid, flags, NULL, err); @@ -1148,8 +1162,8 @@ char *shorten_unambiguous_ref(const char *refname, int strict) for (i = 0; i < nr_rules; i++) { assert(offset < total_len); scanf_fmts[i] = (char *)&scanf_fmts[nr_rules] + offset; - offset += snprintf(scanf_fmts[i], total_len - offset, - ref_rev_parse_rules[i], 2, "%s") + 1; + offset += xsnprintf(scanf_fmts[i], total_len - offset, + ref_rev_parse_rules[i], 2, "%s") + 1; } } @@ -1658,7 +1672,7 @@ static struct ref_store *ref_store_init(const char *gitdir, struct ref_store *refs; if (!be) - die("BUG: reference backend %s is unknown", be_name); + BUG("reference backend %s is unknown", be_name); refs = be->init(gitdir, flags); return refs; @@ -1689,7 +1703,7 @@ static void register_ref_store_map(struct hashmap *map, hashmap_init(map, ref_store_hash_cmp, NULL, 0); if (hashmap_put(map, alloc_ref_store_hash_entry(name, refs))) - die("BUG: %s ref_store '%s' initialized twice", type, name); + BUG("%s ref_store '%s' initialized twice", type, name); } struct ref_store *get_submodule_ref_store(const char *submodule) @@ -1835,7 +1849,7 @@ int ref_update_reject_duplicates(struct string_list *refnames, refnames->items[i].string); return 1; } else if (cmp > 0) { - die("BUG: ref_update_reject_duplicates() received unsorted list"); + BUG("ref_update_reject_duplicates() received unsorted list"); } } return 0; @@ -1851,13 +1865,13 @@ int ref_transaction_prepare(struct ref_transaction *transaction, /* Good. */ break; case REF_TRANSACTION_PREPARED: - die("BUG: prepare called twice on reference transaction"); + BUG("prepare called twice on reference transaction"); break; case REF_TRANSACTION_CLOSED: - die("BUG: prepare called on a closed reference transaction"); + BUG("prepare called on a closed reference transaction"); break; default: - die("BUG: unexpected reference transaction state"); + BUG("unexpected reference transaction state"); break; } @@ -1884,10 +1898,10 @@ int ref_transaction_abort(struct ref_transaction *transaction, ret = refs->be->transaction_abort(refs, transaction, err); break; case REF_TRANSACTION_CLOSED: - die("BUG: abort called on a closed reference transaction"); + BUG("abort called on a closed reference transaction"); break; default: - die("BUG: unexpected reference transaction state"); + BUG("unexpected reference transaction state"); break; } @@ -1912,10 +1926,10 @@ int ref_transaction_commit(struct ref_transaction *transaction, /* Fall through to finish. */ break; case REF_TRANSACTION_CLOSED: - die("BUG: commit called on a closed reference transaction"); + BUG("commit called on a closed reference transaction"); break; default: - die("BUG: unexpected reference transaction state"); + BUG("unexpected reference transaction state"); break; } @@ -1996,7 +2010,7 @@ int refs_verify_refname_available(struct ref_store *refs, } if (ok != ITER_DONE) - die("BUG: error while iterating over references"); + BUG("error while iterating over references"); extra_refname = find_descendant_ref(dirname.buf, extras, skip); if (extra_refname) diff --git a/refs/files-backend.c b/refs/files-backend.c index 49d8f67bf1..a9a066dcfb 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -121,7 +121,7 @@ static void files_assert_main_repository(struct files_ref_store *refs, if (refs->store_flags & REF_STORE_MAIN) return; - die("BUG: operation %s only allowed for main ref store", caller); + BUG("operation %s only allowed for main ref store", caller); } /* @@ -137,13 +137,13 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store, struct files_ref_store *refs; if (ref_store->be != &refs_be_files) - die("BUG: ref_store is type \"%s\" not \"files\" in %s", + BUG("ref_store is type \"%s\" not \"files\" in %s", ref_store->be->name, caller); refs = (struct files_ref_store *)ref_store; if ((refs->store_flags & required_flags) != required_flags) - die("BUG: operation %s requires abilities 0x%x, but only have 0x%x", + BUG("operation %s requires abilities 0x%x, but only have 0x%x", caller, required_flags, refs->store_flags); return refs; @@ -162,7 +162,7 @@ static void files_reflog_path(struct files_ref_store *refs, strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, refname); break; default: - die("BUG: unknown ref type %d of ref %s", + BUG("unknown ref type %d of ref %s", ref_type(refname), refname); } } @@ -180,7 +180,7 @@ static void files_ref_path(struct files_ref_store *refs, strbuf_addf(sb, "%s/%s", refs->gitcommondir, refname); break; default: - die("BUG: unknown ref type %d of ref %s", + BUG("unknown ref type %d of ref %s", ref_type(refname), refname); } } @@ -2006,7 +2006,7 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store, } if (!ret && sb.len) - die("BUG: reverse reflog parser had leftover data"); + BUG("reverse reflog parser had leftover data"); fclose(logfp); strbuf_release(&sb); @@ -2084,7 +2084,7 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator) static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator, struct object_id *peeled) { - die("BUG: ref_iterator_peel() called for reflog_iterator"); + BUG("ref_iterator_peel() called for reflog_iterator"); } static int files_reflog_iterator_abort(struct ref_iterator *ref_iterator) @@ -2869,7 +2869,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store, assert(err); if (transaction->state != REF_TRANSACTION_OPEN) - die("BUG: commit called for transaction that is not open"); + BUG("commit called for transaction that is not open"); /* Fail if a refname appears more than once in the transaction: */ for (i = 0; i < transaction->nr; i++) @@ -2895,7 +2895,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store, */ if (refs_for_each_rawref(&refs->base, ref_present, &affected_refnames)) - die("BUG: initial ref transaction called with existing refs"); + BUG("initial ref transaction called with existing refs"); packed_transaction = ref_store_transaction_begin(refs->packed_ref_store, err); if (!packed_transaction) { @@ -2908,7 +2908,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store, if ((update->flags & REF_HAVE_OLD) && !is_null_oid(&update->old_oid)) - die("BUG: initial ref transaction with old_sha1 set"); + BUG("initial ref transaction with old_sha1 set"); if (refs_verify_refname_available(&refs->base, update->refname, &affected_refnames, NULL, err)) { @@ -2991,7 +2991,7 @@ static int files_reflog_expire(struct ref_store *ref_store, { struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_WRITE, "reflog_expire"); - static struct lock_file reflog_lock; + struct lock_file reflog_lock = LOCK_INIT; struct expire_reflog_cb cb; struct ref_lock *lock; struct strbuf log_file_sb = STRBUF_INIT; diff --git a/refs/iterator.c b/refs/iterator.c index bd35da4e62..2ac91ac340 100644 --- a/refs/iterator.c +++ b/refs/iterator.c @@ -54,7 +54,7 @@ static int empty_ref_iterator_advance(struct ref_iterator *ref_iterator) static int empty_ref_iterator_peel(struct ref_iterator *ref_iterator, struct object_id *peeled) { - die("BUG: peel called for empty iterator"); + BUG("peel called for empty iterator"); } static int empty_ref_iterator_abort(struct ref_iterator *ref_iterator) @@ -177,7 +177,7 @@ static int merge_ref_iterator_peel(struct ref_iterator *ref_iterator, (struct merge_ref_iterator *)ref_iterator; if (!iter->current) { - die("BUG: peel called before advance for merge iterator"); + BUG("peel called before advance for merge iterator"); } return ref_iterator_peel(*iter->current, peeled); } @@ -338,7 +338,7 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator) * trimming, report it as a bug: */ if (strlen(iter->iter0->refname) <= iter->trim) - die("BUG: attempt to trim too many characters"); + BUG("attempt to trim too many characters"); iter->base.refname = iter->iter0->refname + iter->trim; } else { iter->base.refname = iter->iter0->refname; diff --git a/refs/packed-backend.c b/refs/packed-backend.c index 369c34f886..cec3fb9e00 100644 --- a/refs/packed-backend.c +++ b/refs/packed-backend.c @@ -221,13 +221,13 @@ static struct packed_ref_store *packed_downcast(struct ref_store *ref_store, struct packed_ref_store *refs; if (ref_store->be != &refs_be_packed) - die("BUG: ref_store is type \"%s\" not \"packed\" in %s", + BUG("ref_store is type \"%s\" not \"packed\" in %s", ref_store->be->name, caller); refs = (struct packed_ref_store *)ref_store; if ((refs->store_flags & required_flags) != required_flags) - die("BUG: unallowed operation (%s), requires %x, has %x\n", + BUG("unallowed operation (%s), requires %x, has %x\n", caller, required_flags, refs->store_flags); return refs; @@ -1036,7 +1036,7 @@ void packed_refs_unlock(struct ref_store *ref_store) "packed_refs_unlock"); if (!is_lock_file_locked(&refs->lock)) - die("BUG: packed_refs_unlock() called when not locked"); + BUG("packed_refs_unlock() called when not locked"); rollback_lock_file(&refs->lock); } @@ -1089,7 +1089,7 @@ static int write_with_updates(struct packed_ref_store *refs, char *packed_refs_path; if (!is_lock_file_locked(&refs->lock)) - die("BUG: write_with_updates() called while unlocked"); + BUG("write_with_updates() called while unlocked"); /* * If packed-refs is a symlink, we want to overwrite the @@ -1563,21 +1563,21 @@ static int packed_create_symref(struct ref_store *ref_store, const char *refname, const char *target, const char *logmsg) { - die("BUG: packed reference store does not support symrefs"); + BUG("packed reference store does not support symrefs"); } static int packed_rename_ref(struct ref_store *ref_store, const char *oldrefname, const char *newrefname, const char *logmsg) { - die("BUG: packed reference store does not support renaming references"); + BUG("packed reference store does not support renaming references"); } static int packed_copy_ref(struct ref_store *ref_store, const char *oldrefname, const char *newrefname, const char *logmsg) { - die("BUG: packed reference store does not support copying references"); + BUG("packed reference store does not support copying references"); } static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_store) @@ -1610,7 +1610,7 @@ static int packed_create_reflog(struct ref_store *ref_store, const char *refname, int force_create, struct strbuf *err) { - die("BUG: packed reference store does not support reflogs"); + BUG("packed reference store does not support reflogs"); } static int packed_delete_reflog(struct ref_store *ref_store, diff --git a/refs/ref-cache.c b/refs/ref-cache.c index e90bd3e727..9b110c8494 100644 --- a/refs/ref-cache.c +++ b/refs/ref-cache.c @@ -23,7 +23,7 @@ struct ref_dir *get_ref_dir(struct ref_entry *entry) dir = &entry->u.subdir; if (entry->flag & REF_INCOMPLETE) { if (!dir->cache->fill_ref_dir) - die("BUG: incomplete ref_store without fill_ref_dir function"); + BUG("incomplete ref_store without fill_ref_dir function"); dir->cache->fill_ref_dir(dir->cache->ref_store, dir, entry->name); entry->flag &= ~REF_INCOMPLETE; diff --git a/refspec.c b/refspec.c new file mode 100644 index 0000000000..c59a4ccf1e --- /dev/null +++ b/refspec.c @@ -0,0 +1,223 @@ +#include "cache.h" +#include "argv-array.h" +#include "refs.h" +#include "refspec.h" + +static struct refspec_item s_tag_refspec = { + 0, + 1, + 0, + 0, + "refs/tags/*", + "refs/tags/*" +}; + +/* See TAG_REFSPEC for the string version */ +const struct refspec_item *tag_refspec = &s_tag_refspec; + +/* + * Parses the provided refspec 'refspec' and populates the refspec_item 'item'. + * Returns 1 if successful and 0 if the refspec is invalid. + */ +static int parse_refspec(struct refspec_item *item, const char *refspec, int fetch) +{ + size_t llen; + int is_glob; + const char *lhs, *rhs; + int flags; + + is_glob = 0; + + lhs = refspec; + if (*lhs == '+') { + item->force = 1; + lhs++; + } + + rhs = strrchr(lhs, ':'); + + /* + * Before going on, special case ":" (or "+:") as a refspec + * for pushing matching refs. + */ + if (!fetch && rhs == lhs && rhs[1] == '\0') { + item->matching = 1; + return 1; + } + + if (rhs) { + size_t rlen = strlen(++rhs); + is_glob = (1 <= rlen && strchr(rhs, '*')); + item->dst = xstrndup(rhs, rlen); + } + + llen = (rhs ? (rhs - lhs - 1) : strlen(lhs)); + if (1 <= llen && memchr(lhs, '*', llen)) { + if ((rhs && !is_glob) || (!rhs && fetch)) + return 0; + is_glob = 1; + } else if (rhs && is_glob) { + return 0; + } + + item->pattern = is_glob; + item->src = xstrndup(lhs, llen); + flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0); + + if (fetch) { + struct object_id unused; + + /* LHS */ + if (!*item->src) + ; /* empty is ok; it means "HEAD" */ + else if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(item->src, &unused)) + item->exact_sha1 = 1; /* ok */ + else if (!check_refname_format(item->src, flags)) + ; /* valid looking ref is ok */ + else + return 0; + /* RHS */ + if (!item->dst) + ; /* missing is ok; it is the same as empty */ + else if (!*item->dst) + ; /* empty is ok; it means "do not store" */ + else if (!check_refname_format(item->dst, flags)) + ; /* valid looking ref is ok */ + else + return 0; + } else { + /* + * LHS + * - empty is allowed; it means delete. + * - when wildcarded, it must be a valid looking ref. + * - otherwise, it must be an extended SHA-1, but + * there is no existing way to validate this. + */ + if (!*item->src) + ; /* empty is ok */ + else if (is_glob) { + if (check_refname_format(item->src, flags)) + return 0; + } + else + ; /* anything goes, for now */ + /* + * RHS + * - missing is allowed, but LHS then must be a + * valid looking ref. + * - empty is not allowed. + * - otherwise it must be a valid looking ref. + */ + if (!item->dst) { + if (check_refname_format(item->src, flags)) + return 0; + } else if (!*item->dst) { + return 0; + } else { + if (check_refname_format(item->dst, flags)) + return 0; + } + } + + return 1; +} + +void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch) +{ + memset(item, 0, sizeof(*item)); + + if (!parse_refspec(item, refspec, fetch)) + die("Invalid refspec '%s'", refspec); +} + +void refspec_item_clear(struct refspec_item *item) +{ + FREE_AND_NULL(item->src); + FREE_AND_NULL(item->dst); + item->force = 0; + item->pattern = 0; + item->matching = 0; + item->exact_sha1 = 0; +} + +void refspec_init(struct refspec *rs, int fetch) +{ + memset(rs, 0, sizeof(*rs)); + rs->fetch = fetch; +} + +void refspec_append(struct refspec *rs, const char *refspec) +{ + struct refspec_item item; + + refspec_item_init(&item, refspec, rs->fetch); + + ALLOC_GROW(rs->items, rs->nr + 1, rs->alloc); + rs->items[rs->nr++] = item; + + ALLOC_GROW(rs->raw, rs->raw_nr + 1, rs->raw_alloc); + rs->raw[rs->raw_nr++] = xstrdup(refspec); +} + +void refspec_appendn(struct refspec *rs, const char **refspecs, int nr) +{ + int i; + for (i = 0; i < nr; i++) + refspec_append(rs, refspecs[i]); +} + +void refspec_clear(struct refspec *rs) +{ + int i; + + for (i = 0; i < rs->nr; i++) + refspec_item_clear(&rs->items[i]); + + FREE_AND_NULL(rs->items); + rs->alloc = 0; + rs->nr = 0; + + for (i = 0; i < rs->raw_nr; i++) + free((char *)rs->raw[i]); + FREE_AND_NULL(rs->raw); + rs->raw_alloc = 0; + rs->raw_nr = 0; + + rs->fetch = 0; +} + +int valid_fetch_refspec(const char *fetch_refspec_str) +{ + struct refspec_item refspec; + int ret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH); + refspec_item_clear(&refspec); + return ret; +} + +void refspec_ref_prefixes(const struct refspec *rs, + struct argv_array *ref_prefixes) +{ + int i; + for (i = 0; i < rs->nr; i++) { + const struct refspec_item *item = &rs->items[i]; + const char *prefix = NULL; + + if (rs->fetch == REFSPEC_FETCH) + prefix = item->src; + else if (item->dst) + prefix = item->dst; + else if (item->src && !item->exact_sha1) + prefix = item->src; + + if (prefix) { + if (item->pattern) { + const char *glob = strchr(prefix, '*'); + argv_array_pushf(ref_prefixes, "%.*s", + (int)(glob - prefix), + prefix); + } else { + expand_ref_prefix(ref_prefixes, prefix); + } + } + } +} diff --git a/refspec.h b/refspec.h new file mode 100644 index 0000000000..01b700e094 --- /dev/null +++ b/refspec.h @@ -0,0 +1,48 @@ +#ifndef REFSPEC_H +#define REFSPEC_H + +#define TAG_REFSPEC "refs/tags/*:refs/tags/*" +extern const struct refspec_item *tag_refspec; + +struct refspec_item { + unsigned force : 1; + unsigned pattern : 1; + unsigned matching : 1; + unsigned exact_sha1 : 1; + + char *src; + char *dst; +}; + +#define REFSPEC_FETCH 1 +#define REFSPEC_PUSH 0 + +#define REFSPEC_INIT_FETCH { .fetch = REFSPEC_FETCH } +#define REFSPEC_INIT_PUSH { .fetch = REFSPEC_PUSH } + +struct refspec { + struct refspec_item *items; + int alloc; + int nr; + + const char **raw; + int raw_alloc; + int raw_nr; + + int fetch; +}; + +void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch); +void refspec_item_clear(struct refspec_item *item); +void refspec_init(struct refspec *rs, int fetch); +void refspec_append(struct refspec *rs, const char *refspec); +void refspec_appendn(struct refspec *rs, const char **refspecs, int nr); +void refspec_clear(struct refspec *rs); + +int valid_fetch_refspec(const char *refspec); + +struct argv_array; +void refspec_ref_prefixes(const struct refspec *rs, + struct argv_array *ref_prefixes); + +#endif /* REFSPEC_H */ diff --git a/remote-curl.c b/remote-curl.c index ceb05347bd..99b0bedc6d 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -684,7 +684,7 @@ retry: curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0); curl_easy_setopt(slot->curl, CURLOPT_POST, 1); curl_easy_setopt(slot->curl, CURLOPT_URL, rpc->service_url); - curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "gzip"); + curl_easy_setopt(slot->curl, CURLOPT_ENCODING, ""); if (large_request) { /* The request body is large and the size cannot be predicted. @@ -1259,6 +1259,7 @@ static int proxy_request(struct proxy_state *p) slot = get_active_slot(); + curl_easy_setopt(slot->curl, CURLOPT_ENCODING, ""); curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0); curl_easy_setopt(slot->curl, CURLOPT_POST, 1); curl_easy_setopt(slot->curl, CURLOPT_URL, p->service_url); @@ -2,6 +2,7 @@ #include "config.h" #include "remote.h" #include "refs.h" +#include "refspec.h" #include "commit.h" #include "diff.h" #include "revision.h" @@ -13,18 +14,6 @@ enum map_direction { FROM_SRC, FROM_DST }; -static struct refspec s_tag_refspec = { - 0, - 1, - 0, - 0, - "refs/tags/*", - "refs/tags/*" -}; - -/* See TAG_REFSPEC for the string version */ -const struct refspec *tag_refspec = &s_tag_refspec; - struct counted_string { size_t len; const char *s; @@ -88,33 +77,6 @@ static const char *alias_url(const char *url, struct rewrites *r) return xstrfmt("%s%s", r->rewrite[longest_i]->base, url + longest->len); } -static void add_push_refspec(struct remote *remote, const char *ref) -{ - ALLOC_GROW(remote->push_refspec, - remote->push_refspec_nr + 1, - remote->push_refspec_alloc); - remote->push_refspec[remote->push_refspec_nr++] = ref; -} - -static void add_fetch_refspec(struct remote *remote, const char *ref) -{ - ALLOC_GROW(remote->fetch_refspec, - remote->fetch_refspec_nr + 1, - remote->fetch_refspec_alloc); - remote->fetch_refspec[remote->fetch_refspec_nr++] = ref; -} - -void add_prune_tags_to_fetch_refspec(struct remote *remote) -{ - int nr = remote->fetch_refspec_nr; - int bufsize = nr + 1; - int size = sizeof(struct refspec); - - remote->fetch = xrealloc(remote->fetch, size * bufsize); - memcpy(&remote->fetch[nr], tag_refspec, size); - add_fetch_refspec(remote, xstrdup(TAG_REFSPEC)); -} - static void add_url(struct remote *remote, const char *url) { ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc); @@ -186,9 +148,12 @@ static struct remote *make_remote(const char *name, int len) ret = xcalloc(1, sizeof(struct remote)); ret->prune = -1; /* unspecified */ ret->prune_tags = -1; /* unspecified */ + ret->name = xstrndup(name, len); + refspec_init(&ret->push, REFSPEC_PUSH); + refspec_init(&ret->fetch, REFSPEC_FETCH); + ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc); remotes[remotes_nr++] = ret; - ret->name = xstrndup(name, len); hashmap_entry_init(ret, lookup_entry.hash); replaced = hashmap_put(&remotes_hash, ret); @@ -286,9 +251,9 @@ static void read_remotes_file(struct remote *remote) if (skip_prefix(buf.buf, "URL:", &v)) add_url_alias(remote, xstrdup(skip_spaces(v))); else if (skip_prefix(buf.buf, "Push:", &v)) - add_push_refspec(remote, xstrdup(skip_spaces(v))); + refspec_append(&remote->push, skip_spaces(v)); else if (skip_prefix(buf.buf, "Pull:", &v)) - add_fetch_refspec(remote, xstrdup(skip_spaces(v))); + refspec_append(&remote->fetch, skip_spaces(v)); } strbuf_release(&buf); fclose(f); @@ -327,15 +292,19 @@ static void read_branches_file(struct remote *remote) frag = "master"; add_url_alias(remote, strbuf_detach(&buf, NULL)); - add_fetch_refspec(remote, xstrfmt("refs/heads/%s:refs/heads/%s", - frag, remote->name)); + strbuf_addf(&buf, "refs/heads/%s:refs/heads/%s", + frag, remote->name); + refspec_append(&remote->fetch, buf.buf); /* * Cogito compatible push: push current HEAD to remote #branch * (master if missing) */ - add_push_refspec(remote, xstrfmt("HEAD:refs/heads/%s", frag)); + strbuf_reset(&buf); + strbuf_addf(&buf, "HEAD:refs/heads/%s", frag); + refspec_append(&remote->push, buf.buf); remote->fetch_tags = 1; /* always auto-follow */ + strbuf_release(&buf); } static int handle_config(const char *key, const char *value, void *cb) @@ -420,12 +389,14 @@ static int handle_config(const char *key, const char *value, void *cb) const char *v; if (git_config_string(&v, key, value)) return -1; - add_push_refspec(remote, v); + refspec_append(&remote->push, v); + free((char *)v); } else if (!strcmp(subkey, "fetch")) { const char *v; if (git_config_string(&v, key, value)) return -1; - add_fetch_refspec(remote, v); + refspec_append(&remote->fetch, v); + free((char *)v); } else if (!strcmp(subkey, "receivepack")) { const char *v; if (git_config_string(&v, key, value)) @@ -499,158 +470,6 @@ static void read_config(void) alias_all_urls(); } -static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify) -{ - int i; - struct refspec *rs = xcalloc(nr_refspec, sizeof(*rs)); - - for (i = 0; i < nr_refspec; i++) { - size_t llen; - int is_glob; - const char *lhs, *rhs; - int flags; - - is_glob = 0; - - lhs = refspec[i]; - if (*lhs == '+') { - rs[i].force = 1; - lhs++; - } - - rhs = strrchr(lhs, ':'); - - /* - * Before going on, special case ":" (or "+:") as a refspec - * for pushing matching refs. - */ - if (!fetch && rhs == lhs && rhs[1] == '\0') { - rs[i].matching = 1; - continue; - } - - if (rhs) { - size_t rlen = strlen(++rhs); - is_glob = (1 <= rlen && strchr(rhs, '*')); - rs[i].dst = xstrndup(rhs, rlen); - } - - llen = (rhs ? (rhs - lhs - 1) : strlen(lhs)); - if (1 <= llen && memchr(lhs, '*', llen)) { - if ((rhs && !is_glob) || (!rhs && fetch)) - goto invalid; - is_glob = 1; - } else if (rhs && is_glob) { - goto invalid; - } - - rs[i].pattern = is_glob; - rs[i].src = xstrndup(lhs, llen); - flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0); - - if (fetch) { - struct object_id unused; - - /* LHS */ - if (!*rs[i].src) - ; /* empty is ok; it means "HEAD" */ - else if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(rs[i].src, &unused)) - rs[i].exact_sha1 = 1; /* ok */ - else if (!check_refname_format(rs[i].src, flags)) - ; /* valid looking ref is ok */ - else - goto invalid; - /* RHS */ - if (!rs[i].dst) - ; /* missing is ok; it is the same as empty */ - else if (!*rs[i].dst) - ; /* empty is ok; it means "do not store" */ - else if (!check_refname_format(rs[i].dst, flags)) - ; /* valid looking ref is ok */ - else - goto invalid; - } else { - /* - * LHS - * - empty is allowed; it means delete. - * - when wildcarded, it must be a valid looking ref. - * - otherwise, it must be an extended SHA-1, but - * there is no existing way to validate this. - */ - if (!*rs[i].src) - ; /* empty is ok */ - else if (is_glob) { - if (check_refname_format(rs[i].src, flags)) - goto invalid; - } - else - ; /* anything goes, for now */ - /* - * RHS - * - missing is allowed, but LHS then must be a - * valid looking ref. - * - empty is not allowed. - * - otherwise it must be a valid looking ref. - */ - if (!rs[i].dst) { - if (check_refname_format(rs[i].src, flags)) - goto invalid; - } else if (!*rs[i].dst) { - goto invalid; - } else { - if (check_refname_format(rs[i].dst, flags)) - goto invalid; - } - } - } - return rs; - - invalid: - if (verify) { - /* - * nr_refspec must be greater than zero and i must be valid - * since it is only possible to reach this point from within - * the for loop above. - */ - free_refspec(i+1, rs); - return NULL; - } - die("Invalid refspec '%s'", refspec[i]); -} - -int valid_fetch_refspec(const char *fetch_refspec_str) -{ - struct refspec *refspec; - - refspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1); - free_refspec(1, refspec); - return !!refspec; -} - -struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec) -{ - return parse_refspec_internal(nr_refspec, refspec, 1, 0); -} - -struct refspec *parse_push_refspec(int nr_refspec, const char **refspec) -{ - return parse_refspec_internal(nr_refspec, refspec, 0, 0); -} - -void free_refspec(int nr_refspec, struct refspec *refspec) -{ - int i; - - if (!refspec) - return; - - for (i = 0; i < nr_refspec; i++) { - free(refspec[i].src); - free(refspec[i].dst); - } - free(refspec); -} - static int valid_remote_nick(const char *name) { if (!name[0] || is_dot_or_dotdot(name)) @@ -705,9 +524,8 @@ const char *remote_ref_for_branch(struct branch *branch, int for_push, pushremote_for_branch(branch, NULL); struct remote *remote = remote_get(remote_name); - if (remote && remote->push_refspec_nr && - (dst = apply_refspecs(remote->push, - remote->push_refspec_nr, + if (remote && remote->push.nr && + (dst = apply_refspecs(&remote->push, branch->refname))) { if (explicit) *explicit = 1; @@ -744,8 +562,6 @@ static struct remote *remote_get_1(const char *name, add_url_alias(ret, name); if (!valid_remote(ret)) return NULL; - ret->fetch = parse_fetch_refspec(ret->fetch_refspec_nr, ret->fetch_refspec); - ret->push = parse_push_refspec(ret->push_refspec_nr, ret->push_refspec); return ret; } @@ -776,12 +592,6 @@ int for_each_remote(each_remote_fn fn, void *priv) struct remote *r = remotes[i]; if (!r) continue; - if (!r->fetch) - r->fetch = parse_fetch_refspec(r->fetch_refspec_nr, - r->fetch_refspec); - if (!r->push) - r->push = parse_push_refspec(r->push_refspec_nr, - r->push_refspec); result = fn(r, priv); } return result; @@ -887,7 +697,9 @@ static int match_name_with_pattern(const char *key, const char *name, return ret; } -static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct refspec *query, struct string_list *results) +static void query_refspecs_multiple(struct refspec *rs, + struct refspec_item *query, + struct string_list *results) { int i; int find_src = !query->src; @@ -895,8 +707,8 @@ static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct if (find_src && !query->dst) error("query_refspecs_multiple: need either src or dst"); - for (i = 0; i < ref_count; i++) { - struct refspec *refspec = &refs[i]; + for (i = 0; i < rs->nr; i++) { + struct refspec_item *refspec = &rs->items[i]; const char *key = find_src ? refspec->dst : refspec->src; const char *value = find_src ? refspec->src : refspec->dst; const char *needle = find_src ? query->dst : query->src; @@ -913,7 +725,7 @@ static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct } } -int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query) +int query_refspecs(struct refspec *rs, struct refspec_item *query) { int i; int find_src = !query->src; @@ -923,8 +735,8 @@ int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query) if (find_src && !query->dst) return error("query_refspecs: need either src or dst"); - for (i = 0; i < ref_count; i++) { - struct refspec *refspec = &refs[i]; + for (i = 0; i < rs->nr; i++) { + struct refspec_item *refspec = &rs->items[i]; const char *key = find_src ? refspec->dst : refspec->src; const char *value = find_src ? refspec->src : refspec->dst; @@ -944,23 +756,22 @@ int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query) return -1; } -char *apply_refspecs(struct refspec *refspecs, int nr_refspec, - const char *name) +char *apply_refspecs(struct refspec *rs, const char *name) { - struct refspec query; + struct refspec_item query; - memset(&query, 0, sizeof(struct refspec)); + memset(&query, 0, sizeof(struct refspec_item)); query.src = (char *)name; - if (query_refspecs(refspecs, nr_refspec, &query)) + if (query_refspecs(rs, &query)) return NULL; return query.dst; } -int remote_find_tracking(struct remote *remote, struct refspec *refspec) +int remote_find_tracking(struct remote *remote, struct refspec_item *refspec) { - return query_refspecs(remote->fetch, remote->fetch_refspec_nr, refspec); + return query_refspecs(&remote->fetch, refspec); } static struct ref *alloc_ref_with_prefix(const char *prefix, size_t prefixlen, @@ -1167,7 +978,7 @@ static char *guess_ref(const char *name, struct ref *peer) } static int match_explicit_lhs(struct ref *src, - struct refspec *rs, + struct refspec_item *rs, struct ref **match, int *allocated_match) { @@ -1193,7 +1004,7 @@ static int match_explicit_lhs(struct ref *src, static int match_explicit(struct ref *src, struct ref *dst, struct ref ***dst_tail, - struct refspec *rs) + struct refspec_item *rs) { struct ref *matched_src, *matched_dst; int allocated_src; @@ -1262,36 +1073,37 @@ static int match_explicit(struct ref *src, struct ref *dst, } static int match_explicit_refs(struct ref *src, struct ref *dst, - struct ref ***dst_tail, struct refspec *rs, - int rs_nr) + struct ref ***dst_tail, struct refspec *rs) { int i, errs; - for (i = errs = 0; i < rs_nr; i++) - errs += match_explicit(src, dst, dst_tail, &rs[i]); + for (i = errs = 0; i < rs->nr; i++) + errs += match_explicit(src, dst, dst_tail, &rs->items[i]); return errs; } -static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref *ref, - int send_mirror, int direction, const struct refspec **ret_pat) +static char *get_ref_match(const struct refspec *rs, const struct ref *ref, + int send_mirror, int direction, + const struct refspec_item **ret_pat) { - const struct refspec *pat; + const struct refspec_item *pat; char *name; int i; int matching_refs = -1; - for (i = 0; i < rs_nr; i++) { - if (rs[i].matching && - (matching_refs == -1 || rs[i].force)) { + for (i = 0; i < rs->nr; i++) { + const struct refspec_item *item = &rs->items[i]; + if (item->matching && + (matching_refs == -1 || item->force)) { matching_refs = i; continue; } - if (rs[i].pattern) { - const char *dst_side = rs[i].dst ? rs[i].dst : rs[i].src; + if (item->pattern) { + const char *dst_side = item->dst ? item->dst : item->src; int match; if (direction == FROM_SRC) - match = match_name_with_pattern(rs[i].src, ref->name, dst_side, &name); + match = match_name_with_pattern(item->src, ref->name, dst_side, &name); else - match = match_name_with_pattern(dst_side, ref->name, rs[i].src, &name); + match = match_name_with_pattern(dst_side, ref->name, item->src, &name); if (match) { matching_refs = i; break; @@ -1301,7 +1113,7 @@ static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref if (matching_refs == -1) return NULL; - pat = rs + matching_refs; + pat = &rs->items[matching_refs]; if (pat->matching) { /* * "matching refs"; traditionally we pushed everything @@ -1443,22 +1255,20 @@ static void prepare_ref_index(struct string_list *ref_index, struct ref *ref) * but we can catch some errors early before even talking to the * remote side. */ -int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names) +int check_push_refs(struct ref *src, struct refspec *rs) { - struct refspec *refspec = parse_push_refspec(nr_refspec, refspec_names); int ret = 0; int i; - for (i = 0; i < nr_refspec; i++) { - struct refspec *rs = refspec + i; + for (i = 0; i < rs->nr; i++) { + struct refspec_item *item = &rs->items[i]; - if (rs->pattern || rs->matching) + if (item->pattern || item->matching) continue; - ret |= match_explicit_lhs(src, rs, NULL, NULL); + ret |= match_explicit_lhs(src, item, NULL, NULL); } - free_refspec(nr_refspec, refspec); return ret; } @@ -1471,32 +1281,29 @@ int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names) * dst (e.g. pushing to a new branch, done in match_explicit_refs). */ int match_push_refs(struct ref *src, struct ref **dst, - int nr_refspec, const char **refspec, int flags) + struct refspec *rs, int flags) { - struct refspec *rs; int send_all = flags & MATCH_REFS_ALL; int send_mirror = flags & MATCH_REFS_MIRROR; int send_prune = flags & MATCH_REFS_PRUNE; int errs; - static const char *default_refspec[] = { ":", NULL }; struct ref *ref, **dst_tail = tail_ref(dst); struct string_list dst_ref_index = STRING_LIST_INIT_NODUP; - if (!nr_refspec) { - nr_refspec = 1; - refspec = default_refspec; - } - rs = parse_push_refspec(nr_refspec, (const char **) refspec); - errs = match_explicit_refs(src, *dst, &dst_tail, rs, nr_refspec); + /* If no refspec is provided, use the default ":" */ + if (!rs->nr) + refspec_append(rs, ":"); + + errs = match_explicit_refs(src, *dst, &dst_tail, rs); /* pick the remainder */ for (ref = src; ref; ref = ref->next) { struct string_list_item *dst_item; struct ref *dst_peer; - const struct refspec *pat = NULL; + const struct refspec_item *pat = NULL; char *dst_name; - dst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_SRC, &pat); + dst_name = get_ref_match(rs, ref, send_mirror, FROM_SRC, &pat); if (!dst_name) continue; @@ -1545,7 +1352,7 @@ int match_push_refs(struct ref *src, struct ref **dst, /* We're already sending something to this ref. */ continue; - src_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_DST, NULL); + src_name = get_ref_match(rs, ref, send_mirror, FROM_DST, NULL); if (src_name) { if (!src_ref_index.nr) prepare_ref_index(&src_ref_index, src); @@ -1557,6 +1364,7 @@ int match_push_refs(struct ref *src, struct ref **dst, } string_list_clear(&src_ref_index, 0); } + if (errs) return -1; return 0; @@ -1753,7 +1561,7 @@ static const char *tracking_for_push_dest(struct remote *remote, { char *ret; - ret = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname); + ret = apply_refspecs(&remote->fetch, refname); if (!ret) return error_buf(err, _("push destination '%s' on remote '%s' has no local tracking branch"), @@ -1771,12 +1579,11 @@ static const char *branch_get_push_1(struct branch *branch, struct strbuf *err) _("branch '%s' has no remote for pushing"), branch->name); - if (remote->push_refspec_nr) { + if (remote->push.nr) { char *dst; const char *ret; - dst = apply_refspecs(remote->push, remote->push_refspec_nr, - branch->refname); + dst = apply_refspecs(&remote->push, branch->refname); if (!dst) return error_buf(err, _("push refspecs for '%s' do not include '%s'"), @@ -1819,7 +1626,7 @@ static const char *branch_get_push_1(struct branch *branch, struct strbuf *err) } } - die("BUG: unhandled push situation"); + BUG("unhandled push situation"); } const char *branch_get_push(struct branch *branch, struct strbuf *err) @@ -1849,7 +1656,7 @@ static int ignore_symref_update(const char *refname) * local symbolic ref. */ static struct ref *get_expanded_map(const struct ref *remote_refs, - const struct refspec *refspec) + const struct refspec_item *refspec) { const struct ref *ref; struct ref *ret = NULL; @@ -1914,7 +1721,7 @@ static struct ref *get_local_ref(const char *name) } int get_fetch_map(const struct ref *remote_refs, - const struct refspec *refspec, + const struct refspec_item *refspec, struct ref ***tail, int missing_ok) { @@ -2252,8 +2059,7 @@ struct ref *guess_remote_head(const struct ref *head, struct stale_heads_info { struct string_list *ref_names; struct ref **stale_refs_tail; - struct refspec *refs; - int ref_count; + struct refspec *rs; }; static int get_stale_heads_cb(const char *refname, const struct object_id *oid, @@ -2261,12 +2067,12 @@ static int get_stale_heads_cb(const char *refname, const struct object_id *oid, { struct stale_heads_info *info = cb_data; struct string_list matches = STRING_LIST_INIT_DUP; - struct refspec query; + struct refspec_item query; int i, stale = 1; - memset(&query, 0, sizeof(struct refspec)); + memset(&query, 0, sizeof(struct refspec_item)); query.dst = (char *)refname; - query_refspecs_multiple(info->refs, info->ref_count, &query, &matches); + query_refspecs_multiple(info->rs, &query, &matches); if (matches.nr == 0) goto clean_exit; /* No matches */ @@ -2294,7 +2100,7 @@ clean_exit: return 0; } -struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map) +struct ref *get_stale_heads(struct refspec *rs, struct ref *fetch_map) { struct ref *ref, *stale_refs = NULL; struct string_list ref_names = STRING_LIST_INIT_NODUP; @@ -2302,8 +2108,7 @@ struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fet info.ref_names = &ref_names; info.stale_refs_tail = &stale_refs; - info.refs = refs; - info.ref_count = ref_count; + info.rs = rs; for (ref = fetch_map; ref; ref = ref->next) string_list_append(&ref_names, ref->name); string_list_sort(&ref_names); @@ -2387,7 +2192,7 @@ static int remote_tracking(struct remote *remote, const char *refname, { char *dst; - dst = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname); + dst = apply_refspecs(&remote->fetch, refname); if (!dst) return -1; /* no tracking ref for refname at remote */ if (read_ref(dst, oid)) @@ -3,6 +3,7 @@ #include "parse-options.h" #include "hashmap.h" +#include "refspec.h" enum { REMOTE_UNCONFIGURED = 0, @@ -27,15 +28,9 @@ struct remote { int pushurl_nr; int pushurl_alloc; - const char **push_refspec; - struct refspec *push; - int push_refspec_nr; - int push_refspec_alloc; + struct refspec push; - const char **fetch_refspec; - struct refspec *fetch; - int fetch_refspec_nr; - int fetch_refspec_alloc; + struct refspec fetch; /* * -1 to never fetch tags @@ -68,18 +63,6 @@ int for_each_remote(each_remote_fn fn, void *priv); int remote_has_url(struct remote *remote, const char *url); -struct refspec { - unsigned force : 1; - unsigned pattern : 1; - unsigned matching : 1; - unsigned exact_sha1 : 1; - - char *src; - char *dst; -}; - -extern const struct refspec *tag_refspec; - struct ref { struct ref *next; struct object_id old_oid; @@ -177,19 +160,12 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid); */ struct ref *ref_remove_duplicates(struct ref *ref_map); -int valid_fetch_refspec(const char *refspec); -struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec); -extern struct refspec *parse_push_refspec(int nr_refspec, const char **refspec); +int query_refspecs(struct refspec *rs, struct refspec_item *query); +char *apply_refspecs(struct refspec *rs, const char *name); -void free_refspec(int nr_refspec, struct refspec *refspec); - -extern int query_refspecs(struct refspec *specs, int nr, struct refspec *query); -char *apply_refspecs(struct refspec *refspecs, int nr_refspec, - const char *name); - -int check_push_refs(struct ref *src, int nr_refspec, const char **refspec); +int check_push_refs(struct ref *src, struct refspec *rs); int match_push_refs(struct ref *src, struct ref **dst, - int nr_refspec, const char **refspec, int all); + struct refspec *rs, int flags); void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, int force_update); @@ -205,7 +181,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, * missing_ok is usually false, but when we are adding branch.$name.merge * it is Ok if the branch is not at the remote anymore. */ -int get_fetch_map(const struct ref *remote_refs, const struct refspec *refspec, +int get_fetch_map(const struct ref *remote_refs, const struct refspec_item *refspec, struct ref ***tail, int missing_ok); struct ref *get_remote_ref(const struct ref *remote_refs, const char *name); @@ -213,7 +189,7 @@ struct ref *get_remote_ref(const struct ref *remote_refs, const char *name); /* * For the given remote, reads the refspec's src and sets the other fields. */ -int remote_find_tracking(struct remote *remote, struct refspec *refspec); +int remote_find_tracking(struct remote *remote, struct refspec_item *refspec); struct branch { const char *name; @@ -223,7 +199,7 @@ struct branch { const char *pushremote_name; const char **merge_name; - struct refspec **merge; + struct refspec_item **merge; int merge_nr; int merge_alloc; @@ -292,7 +268,7 @@ struct ref *guess_remote_head(const struct ref *head, int all); /* Return refs which no longer exist on remote */ -struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map); +struct ref *get_stale_heads(struct refspec *rs, struct ref *fetch_map); /* * Compare-and-swap @@ -315,8 +291,4 @@ extern int parseopt_push_cas_option(const struct option *, const char *arg, int extern int is_empty_cas(const struct push_cas_option *); void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *); -#define TAG_REFSPEC "refs/tags/*:refs/tags/*" - -void add_prune_tags_to_fetch_refspec(struct remote *remote); - #endif diff --git a/repository.c b/repository.c index beff3caa9e..02fe884603 100644 --- a/repository.c +++ b/repository.c @@ -238,7 +238,8 @@ void repo_clear(struct repository *repo) if (repo->index) { discard_index(repo->index); - FREE_AND_NULL(repo->index); + if (repo->index != &the_index) + FREE_AND_NULL(repo->index); } } @@ -703,10 +703,9 @@ out: return ret; } -static struct lock_file index_lock; - static void update_paths(struct string_list *update) { + struct lock_file index_lock = LOCK_INIT; int i; hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); diff --git a/resolve-undo.c b/resolve-undo.c index aed95b4b35..fc5b3b83d9 100644 --- a/resolve-undo.c +++ b/resolve-undo.c @@ -90,7 +90,7 @@ struct string_list *resolve_undo_read(const char *data, unsigned long size) continue; if (size < rawsz) goto error; - memcpy(ui->oid[i].hash, (const unsigned char *)data, rawsz); + oidread(&ui->oid[i], (const unsigned char *)data); size -= rawsz; data += rawsz; } diff --git a/revision.c b/revision.c index 4e0e193e57..40fd91ff2b 100644 --- a/revision.c +++ b/revision.c @@ -52,12 +52,9 @@ static void mark_tree_contents_uninteresting(struct tree *tree) { struct tree_desc desc; struct name_entry entry; - struct object *obj = &tree->object; - if (!has_object_file(&obj->oid)) + if (parse_tree_gently(tree, 1) < 0) return; - if (parse_tree(tree) < 0) - die("bad tree %s", oid_to_hex(&obj->oid)); init_tree_desc(&desc, tree->buffer, tree->size); while (tree_entry(&desc, &entry)) { @@ -95,50 +92,63 @@ void mark_tree_uninteresting(struct tree *tree) mark_tree_contents_uninteresting(tree); } -void mark_parents_uninteresting(struct commit *commit) +struct commit_stack { + struct commit **items; + size_t nr, alloc; +}; +#define COMMIT_STACK_INIT { NULL, 0, 0 } + +static void commit_stack_push(struct commit_stack *stack, struct commit *commit) { - struct commit_list *parents = NULL, *l; + ALLOC_GROW(stack->items, stack->nr + 1, stack->alloc); + stack->items[stack->nr++] = commit; +} - for (l = commit->parents; l; l = l->next) - commit_list_insert(l->item, &parents); +static struct commit *commit_stack_pop(struct commit_stack *stack) +{ + return stack->nr ? stack->items[--stack->nr] : NULL; +} - while (parents) { - struct commit *commit = pop_commit(&parents); +static void commit_stack_clear(struct commit_stack *stack) +{ + FREE_AND_NULL(stack->items); + stack->nr = stack->alloc = 0; +} - while (commit) { - /* - * A missing commit is ok iff its parent is marked - * uninteresting. - * - * We just mark such a thing parsed, so that when - * it is popped next time around, we won't be trying - * to parse it and get an error. - */ - if (!commit->object.parsed && - !has_object_file(&commit->object.oid)) - commit->object.parsed = 1; +static void mark_one_parent_uninteresting(struct commit *commit, + struct commit_stack *pending) +{ + struct commit_list *l; - if (commit->object.flags & UNINTERESTING) - break; + if (commit->object.flags & UNINTERESTING) + return; + commit->object.flags |= UNINTERESTING; + + /* + * Normally we haven't parsed the parent + * yet, so we won't have a parent of a parent + * here. However, it may turn out that we've + * reached this commit some other way (where it + * wasn't uninteresting), in which case we need + * to mark its parents recursively too.. + */ + for (l = commit->parents; l; l = l->next) + commit_stack_push(pending, l->item); +} - commit->object.flags |= UNINTERESTING; +void mark_parents_uninteresting(struct commit *commit) +{ + struct commit_stack pending = COMMIT_STACK_INIT; + struct commit_list *l; - /* - * Normally we haven't parsed the parent - * yet, so we won't have a parent of a parent - * here. However, it may turn out that we've - * reached this commit some other way (where it - * wasn't uninteresting), in which case we need - * to mark its parents recursively too.. - */ - if (!commit->parents) - break; + for (l = commit->parents; l; l = l->next) + mark_one_parent_uninteresting(l->item, &pending); - for (l = commit->parents->next; l; l = l->next) - commit_list_insert(l->item, &parents); - commit = commit->parents->item; - } - } + while (pending.nr > 0) + mark_one_parent_uninteresting(commit_stack_pop(&pending), + &pending); + + commit_stack_clear(&pending); } static void add_pending_object_with_path(struct rev_info *revs, @@ -1752,6 +1762,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg const char *arg = argv[0]; const char *optarg; int argcount; + const unsigned hexsz = the_hash_algo->hexsz; /* pseudo revision arguments */ if (!strcmp(arg, "--all") || !strcmp(arg, "--branches") || @@ -2039,8 +2050,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->abbrev = strtoul(optarg, NULL, 10); if (revs->abbrev < MINIMUM_ABBREV) revs->abbrev = MINIMUM_ABBREV; - else if (revs->abbrev > 40) - revs->abbrev = 40; + else if (revs->abbrev > hexsz) + revs->abbrev = hexsz; } else if (!strcmp(arg, "--abbrev-commit")) { revs->abbrev_commit = 1; revs->abbrev_commit_given = 1; @@ -2108,7 +2119,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->ignore_missing = 1; } else if (!strcmp(arg, "--exclude-promisor-objects")) { if (fetch_if_missing) - die("BUG: exclude_promisor_objects can only be used when fetch_if_missing is 0"); + BUG("exclude_promisor_objects can only be used when fetch_if_missing is 0"); revs->exclude_promisor_objects = 1; } else { int opts = diff_opt_parse(&revs->diffopt, argv, argc, revs->prefix); @@ -2174,7 +2185,7 @@ static int handle_revision_pseudo_opt(const char *submodule, * supported right now, so stick to single worktree. */ if (!revs->single_worktree) - die("BUG: --single-worktree cannot be used together with submodule"); + BUG("--single-worktree cannot be used together with submodule"); refs = get_submodule_ref_store(submodule); } else refs = get_main_ref_store(the_repository); @@ -3087,7 +3098,7 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi { if (commit->object.flags & SHOWN) return commit_ignore; - if (revs->unpacked && has_sha1_pack(commit->object.oid.hash)) + if (revs->unpacked && has_object_pack(&commit->object.oid)) return commit_ignore; if (commit->object.flags & UNINTERESTING) return commit_ignore; diff --git a/run-command.c b/run-command.c index 12c94c1dbe..84b883c213 100644 --- a/run-command.c +++ b/run-command.c @@ -245,7 +245,7 @@ int sane_execvp(const char *file, char * const argv[]) static const char **prepare_shell_cmd(struct argv_array *out, const char **argv) { if (!argv[0]) - die("BUG: shell command is empty"); + BUG("shell command is empty"); if (strcspn(argv[0], "|&;<>()$`\\\"' \t\n*?[#~=%") != strlen(argv[0])) { #ifndef GIT_WINDOWS_NATIVE @@ -383,7 +383,7 @@ static void child_err_spew(struct child_process *cmd, struct child_err *cerr) static void prepare_cmd(struct argv_array *out, const struct child_process *cmd) { if (!cmd->argv[0]) - die("BUG: command is empty"); + BUG("command is empty"); /* * Add SHELL_PATH so in the event exec fails with ENOEXEC we can @@ -471,15 +471,12 @@ struct atfork_state { sigset_t old; }; -#ifndef NO_PTHREADS -static void bug_die(int err, const char *msg) -{ - if (err) { - errno = err; - die_errno("BUG: %s", msg); - } -} -#endif +#define CHECK_BUG(err, msg) \ + do { \ + int e = (err); \ + if (e) \ + BUG("%s: %s", msg, strerror(e)); \ + } while(0) static void atfork_prepare(struct atfork_state *as) { @@ -491,9 +488,9 @@ static void atfork_prepare(struct atfork_state *as) if (sigprocmask(SIG_SETMASK, &all, &as->old)) die_errno("sigprocmask"); #else - bug_die(pthread_sigmask(SIG_SETMASK, &all, &as->old), + CHECK_BUG(pthread_sigmask(SIG_SETMASK, &all, &as->old), "blocking all signals"); - bug_die(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &as->cs), + CHECK_BUG(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &as->cs), "disabling cancellation"); #endif } @@ -504,9 +501,9 @@ static void atfork_parent(struct atfork_state *as) if (sigprocmask(SIG_SETMASK, &as->old, NULL)) die_errno("sigprocmask"); #else - bug_die(pthread_setcancelstate(as->cs, NULL), + CHECK_BUG(pthread_setcancelstate(as->cs, NULL), "re-enabling cancellation"); - bug_die(pthread_sigmask(SIG_SETMASK, &as->old, NULL), + CHECK_BUG(pthread_sigmask(SIG_SETMASK, &as->old, NULL), "restoring signal mask"); #endif } @@ -967,7 +964,7 @@ int run_command(struct child_process *cmd) int code; if (cmd->out < 0 || cmd->err < 0) - die("BUG: run_command with a pipe can cause deadlock"); + BUG("run_command with a pipe can cause deadlock"); code = start_command(cmd); if (code) @@ -1557,7 +1554,7 @@ static void pp_init(struct parallel_processes *pp, pp->data = data; if (!get_next_task) - die("BUG: you need to specify a get_next_task function"); + BUG("you need to specify a get_next_task function"); pp->get_next_task = get_next_task; pp->start_failure = start_failure ? start_failure : default_start_failure; @@ -1619,7 +1616,7 @@ static int pp_start_one(struct parallel_processes *pp) if (pp->children[i].state == GIT_CP_FREE) break; if (i == pp->max_processes) - die("BUG: bookkeeping is hard"); + BUG("bookkeeping is hard"); code = pp->get_next_task(&pp->children[i].process, &pp->children[i].err, diff --git a/sequencer.c b/sequencer.c index 1ce63261a3..560fc9b67d 100644 --- a/sequencer.c +++ b/sequencer.c @@ -27,6 +27,7 @@ #include "worktree.h" #include "oidmap.h" #include "oidset.h" +#include "alias.h" #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" @@ -124,6 +125,12 @@ static GIT_PATH_FUNC(rebase_path_rewritten_pending, "rebase-merge/rewritten-pending") /* + * The path of the file containig the OID of the "squash onto" commit, i.e. + * the dummy commit used for `reset [new root]`. + */ +static GIT_PATH_FUNC(rebase_path_squash_onto, "rebase-merge/squash-onto") + +/* * The path of the file listing refs that need to be deleted after the rebase * finishes. This is used by the `label` command to record the need for cleanup. */ @@ -469,7 +476,8 @@ static int fast_forward_to(const struct object_id *to, const struct object_id *f transaction = ref_transaction_begin(&err); if (!transaction || ref_transaction_update(transaction, "HEAD", - to, unborn ? &null_oid : from, + to, unborn && !is_rebase_i(opts) ? + &null_oid : from, 0, sb.buf, &err) || ref_transaction_commit(transaction, &err)) { ref_transaction_free(transaction); @@ -561,9 +569,23 @@ static int do_recursive_merge(struct commit *base, struct commit *next, return !clean; } +static struct object_id *get_cache_tree_oid(void) +{ + if (!active_cache_tree) + active_cache_tree = cache_tree(); + + if (!cache_tree_fully_valid(active_cache_tree)) + if (cache_tree_update(&the_index, 0)) { + error(_("unable to update cache tree")); + return NULL; + } + + return &active_cache_tree->oid; +} + static int is_index_unchanged(void) { - struct object_id head_oid; + struct object_id head_oid, *cache_tree_oid; struct commit *head_commit; if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) @@ -582,15 +604,10 @@ static int is_index_unchanged(void) if (parse_commit(head_commit)) return -1; - if (!active_cache_tree) - active_cache_tree = cache_tree(); - - if (!cache_tree_fully_valid(active_cache_tree)) - if (cache_tree_update(&the_index, 0)) - return error(_("unable to update cache tree")); + if (!(cache_tree_oid = get_cache_tree_oid())) + return -1; - return !oidcmp(&active_cache_tree->oid, - get_commit_tree_oid(head_commit)); + return !oidcmp(cache_tree_oid, get_commit_tree_oid(head_commit)); } static int write_author_script(const char *message) @@ -682,6 +699,52 @@ static char *get_author(const char *message) return NULL; } +/* Read author-script and return an ident line (author <email> timestamp) */ +static const char *read_author_ident(struct strbuf *buf) +{ + const char *keys[] = { + "GIT_AUTHOR_NAME=", "GIT_AUTHOR_EMAIL=", "GIT_AUTHOR_DATE=" + }; + char *in, *out, *eol; + int i = 0, len; + + if (strbuf_read_file(buf, rebase_path_author_script(), 256) <= 0) + return NULL; + + /* dequote values and construct ident line in-place */ + for (in = out = buf->buf; i < 3 && in - buf->buf < buf->len; i++) { + if (!skip_prefix(in, keys[i], (const char **)&in)) { + warning("could not parse '%s' (looking for '%s'", + rebase_path_author_script(), keys[i]); + return NULL; + } + + eol = strchrnul(in, '\n'); + *eol = '\0'; + sq_dequote(in); + len = strlen(in); + + if (i > 0) /* separate values by spaces */ + *(out++) = ' '; + if (i == 1) /* email needs to be surrounded by <...> */ + *(out++) = '<'; + memmove(out, in, len); + out += len; + if (i == 1) /* email needs to be surrounded by <...> */ + *(out++) = '>'; + in = eol + 1; + } + + if (i < 3) { + warning("could not parse '%s' (looking for '%s')", + rebase_path_author_script(), keys[i]); + return NULL; + } + + buf->len = out - buf->buf; + return buf->buf; +} + static const char staged_changes_advice[] = N_("you have staged changes in your working tree\n" "If these changes are meant to be squashed into the previous commit, run:\n" @@ -701,6 +764,7 @@ N_("you have staged changes in your working tree\n" #define AMEND_MSG (1<<2) #define CLEANUP_MSG (1<<3) #define VERIFY_MSG (1<<4) +#define CREATE_ROOT_COMMIT (1<<5) /* * If we are cherry-pick, and if the merge did not result in @@ -720,6 +784,40 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts, struct child_process cmd = CHILD_PROCESS_INIT; const char *value; + if (flags & CREATE_ROOT_COMMIT) { + struct strbuf msg = STRBUF_INIT, script = STRBUF_INIT; + const char *author = is_rebase_i(opts) ? + read_author_ident(&script) : NULL; + struct object_id root_commit, *cache_tree_oid; + int res = 0; + + if (!defmsg) + BUG("root commit without message"); + + if (!(cache_tree_oid = get_cache_tree_oid())) + res = -1; + + if (!res) + res = strbuf_read_file(&msg, defmsg, 0); + + if (res <= 0) + res = error_errno(_("could not read '%s'"), defmsg); + else + res = commit_tree(msg.buf, msg.len, cache_tree_oid, + NULL, &root_commit, author, + opts->gpg_sign); + + strbuf_release(&msg); + strbuf_release(&script); + if (!res) { + update_ref(NULL, "CHERRY_PICK_HEAD", &root_commit, NULL, + REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR); + res = update_ref(NULL, "HEAD", &root_commit, NULL, 0, + UPDATE_REFS_MSG_ON_ERR); + } + return res < 0 ? error(_("writing root commit")) : 0; + } + cmd.git_cmd = 1; if (is_rebase_i(opts)) { @@ -1150,7 +1248,7 @@ static int try_to_commit(struct strbuf *msg, const char *author, if (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ? get_commit_tree_oid(current_head) : - &empty_tree_oid, &tree)) { + the_hash_algo->empty_tree, &tree)) { res = 1; /* run 'git commit' to display error message */ goto out; } @@ -1210,7 +1308,8 @@ static int do_commit(const char *msg_file, const char *author, { int res = 1; - if (!(flags & EDIT_MSG) && !(flags & VERIFY_MSG)) { + if (!(flags & EDIT_MSG) && !(flags & VERIFY_MSG) && + !(flags & CREATE_ROOT_COMMIT)) { struct object_id oid; struct strbuf sb = STRBUF_INIT; @@ -1363,6 +1462,22 @@ static int is_fixup(enum todo_command command) return command == TODO_FIXUP || command == TODO_SQUASH; } +/* Does this command create a (non-merge) commit? */ +static int is_pick_or_similar(enum todo_command command) +{ + switch (command) { + case TODO_PICK: + case TODO_REVERT: + case TODO_EDIT: + case TODO_REWORD: + case TODO_FIXUP: + case TODO_SQUASH: + return 1; + default: + return 0; + } +} + static int update_squash_messages(enum todo_command command, struct commit *commit, struct replay_opts *opts) { @@ -1516,9 +1631,16 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, return error(_("your index file is unmerged.")); } else { unborn = get_oid("HEAD", &head); - if (unborn) + /* Do we want to generate a root commit? */ + if (is_pick_or_similar(command) && opts->have_squash_onto && + !oidcmp(&head, &opts->squash_onto)) { + if (is_fixup(command)) + return error(_("cannot fixup root commit")); + flags |= CREATE_ROOT_COMMIT; + unborn = 1; + } else if (unborn) oidcpy(&head, the_hash_algo->empty_tree); - if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD", + if (index_differs_from(unborn ? empty_tree_oid_hex() : "HEAD", NULL, 0)) return error_dirty_index(opts); } @@ -2142,6 +2264,12 @@ static int read_populate_opts(struct replay_opts *opts) } } + if (read_oneliner(&buf, rebase_path_squash_onto(), 0)) { + if (get_oid_hex(buf.buf, &opts->squash_onto) < 0) + return error(_("unusable squash-onto")); + opts->have_squash_onto = 1; + } + return 0; } @@ -2634,18 +2762,34 @@ static int do_reset(const char *name, int len, struct replay_opts *opts) if (hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) return -1; - /* Determine the length of the label */ - for (i = 0; i < len; i++) - if (isspace(name[i])) - len = i; - - strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name); - if (get_oid(ref_name.buf, &oid) && - get_oid(ref_name.buf + strlen("refs/rewritten/"), &oid)) { - error(_("could not read '%s'"), ref_name.buf); - rollback_lock_file(&lock); - strbuf_release(&ref_name); - return -1; + if (len == 10 && !strncmp("[new root]", name, len)) { + if (!opts->have_squash_onto) { + const char *hex; + if (commit_tree("", 0, the_hash_algo->empty_tree, + NULL, &opts->squash_onto, + NULL, NULL)) + return error(_("writing fake root commit")); + opts->have_squash_onto = 1; + hex = oid_to_hex(&opts->squash_onto); + if (write_message(hex, strlen(hex), + rebase_path_squash_onto(), 0)) + return error(_("writing squash-onto")); + } + oidcpy(&oid, &opts->squash_onto); + } else { + /* Determine the length of the label */ + for (i = 0; i < len; i++) + if (isspace(name[i])) + len = i; + + strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name); + if (get_oid(ref_name.buf, &oid) && + get_oid(ref_name.buf + strlen("refs/rewritten/"), &oid)) { + error(_("could not read '%s'"), ref_name.buf); + rollback_lock_file(&lock); + strbuf_release(&ref_name); + return -1; + } } memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts)); @@ -2741,6 +2885,18 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len, goto leave_merge; } + if (opts->have_squash_onto && + !oidcmp(&head_commit->object.oid, &opts->squash_onto)) { + /* + * When the user tells us to "merge" something into a + * "[new root]", let's simply fast-forward to the merge head. + */ + rollback_lock_file(&lock); + ret = fast_forward_to(&merge_commit->object.oid, + &head_commit->object.oid, 0, opts); + goto leave_merge; + } + if (commit) { const char *message = get_commit_buffer(commit, NULL); const char *body; @@ -3850,7 +4006,8 @@ static int make_script_with_merges(struct pretty_print_context *pp, } if (!commit) - fprintf(out, "%s onto\n", cmd_reset); + fprintf(out, "%s %s\n", cmd_reset, + rebase_cousins ? "onto" : "[new root]"); else { const char *to = NULL; diff --git a/sequencer.h b/sequencer.h index a800cb5755..c5787c6b56 100644 --- a/sequencer.h +++ b/sequencer.h @@ -48,6 +48,10 @@ struct replay_opts { struct strbuf current_fixups; int current_fixup_count; + /* placeholder commit for -i --root */ + struct object_id squash_onto; + int have_squash_onto; + /* Only used by REPLAY_NONE */ struct rev_info *revs; }; diff --git a/server-info.c b/server-info.c index 83460ec0d6..7ce6dcd67b 100644 --- a/server-info.c +++ b/server-info.c @@ -92,8 +92,6 @@ static struct pack_info { int old_num; int new_num; int nr_alloc; - int nr_heads; - unsigned char (*head)[20]; } **info; static int num_pack; static const char *objdir; @@ -225,12 +223,9 @@ static void init_pack_info(const char *infofile, int force) else stale = 1; - for (i = 0; i < num_pack; i++) { - if (stale) { + for (i = 0; i < num_pack; i++) + if (stale) info[i]->old_num = -1; - info[i]->nr_heads = 0; - } - } /* renumber them */ QSORT(info, num_pack, compare_info); @@ -539,7 +539,7 @@ void read_gitfile_error_die(int error_code, const char *path, const char *dir) case READ_GITFILE_ERR_NOT_A_REPO: die(_("not a git repository: %s"), dir); default: - die("BUG: unknown error code"); + BUG("unknown error code"); } } @@ -1086,7 +1086,7 @@ const char *setup_git_directory_gently(int *nongit_ok) "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."), dir.buf); default: - die("BUG: unhandled setup_git_directory_1() result"); + BUG("unhandled setup_git_directory_1() result"); } if (prefix) diff --git a/sha1-array.c b/sha1-array.c index 838b3bf847..265941fbf4 100644 --- a/sha1-array.c +++ b/sha1-array.c @@ -41,9 +41,26 @@ void oid_array_clear(struct oid_array *array) array->sorted = 0; } + +int oid_array_for_each(struct oid_array *array, + for_each_oid_fn fn, + void *data) +{ + int i; + + /* No oid_array_sort() here! See the api-oid-array.txt docs! */ + + for (i = 0; i < array->nr; i++) { + int ret = fn(array->oid + i, data); + if (ret) + return ret; + } + return 0; +} + int oid_array_for_each_unique(struct oid_array *array, - for_each_oid_fn fn, - void *data) + for_each_oid_fn fn, + void *data) { int i; diff --git a/sha1-array.h b/sha1-array.h index 04b0756334..232bf95017 100644 --- a/sha1-array.h +++ b/sha1-array.h @@ -16,8 +16,11 @@ void oid_array_clear(struct oid_array *array); typedef int (*for_each_oid_fn)(const struct object_id *oid, void *data); +int oid_array_for_each(struct oid_array *array, + for_each_oid_fn fn, + void *data); int oid_array_for_each_unique(struct oid_array *array, - for_each_oid_fn fn, - void *data); + for_each_oid_fn fn, + void *data); #endif /* SHA1_ARRAY_H */ diff --git a/sha1-file.c b/sha1-file.c index f66059ed7d..555e780f4b 100644 --- a/sha1-file.c +++ b/sha1-file.c @@ -36,12 +36,21 @@ /* The maximum size for an object header. */ #define MAX_HEADER_LEN 32 + +#define EMPTY_TREE_SHA1_BIN_LITERAL \ + "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \ + "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04" + +#define EMPTY_BLOB_SHA1_BIN_LITERAL \ + "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \ + "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91" + const unsigned char null_sha1[GIT_MAX_RAWSZ]; const struct object_id null_oid; -const struct object_id empty_tree_oid = { +static const struct object_id empty_tree_oid = { EMPTY_TREE_SHA1_BIN_LITERAL }; -const struct object_id empty_blob_oid = { +static const struct object_id empty_blob_oid = { EMPTY_BLOB_SHA1_BIN_LITERAL }; @@ -101,6 +110,18 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = { }, }; +const char *empty_tree_oid_hex(void) +{ + static char buf[GIT_MAX_HEXSZ + 1]; + return oid_to_hex_r(buf, the_hash_algo->empty_tree); +} + +const char *empty_blob_oid_hex(void) +{ + static char buf[GIT_MAX_HEXSZ + 1]; + return oid_to_hex_r(buf, the_hash_algo->empty_blob); +} + /* * This is meant to hold a *small* number of objects that you would * want read_sha1_file() to be able to return, but yet you do not want @@ -108,7 +129,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = { * application). */ static struct cached_object { - unsigned char sha1[20]; + struct object_id oid; enum object_type type; void *buf; unsigned long size; @@ -116,22 +137,22 @@ static struct cached_object { static int cached_object_nr, cached_object_alloc; static struct cached_object empty_tree = { - EMPTY_TREE_SHA1_BIN_LITERAL, + { EMPTY_TREE_SHA1_BIN_LITERAL }, OBJ_TREE, "", 0 }; -static struct cached_object *find_cached_object(const unsigned char *sha1) +static struct cached_object *find_cached_object(const struct object_id *oid) { int i; struct cached_object *co = cached_objects; for (i = 0; i < cached_object_nr; i++, co++) { - if (!hashcmp(co->sha1, sha1)) + if (!oidcmp(&co->oid, oid)) return co; } - if (!hashcmp(sha1, empty_tree.sha1)) + if (!oidcmp(oid, the_hash_algo->empty_tree)) return &empty_tree; return NULL; } @@ -710,42 +731,42 @@ int check_and_freshen_file(const char *fn, int freshen) return 1; } -static int check_and_freshen_local(const unsigned char *sha1, int freshen) +static int check_and_freshen_local(const struct object_id *oid, int freshen) { static struct strbuf buf = STRBUF_INIT; strbuf_reset(&buf); - sha1_file_name(the_repository, &buf, sha1); + sha1_file_name(the_repository, &buf, oid->hash); return check_and_freshen_file(buf.buf, freshen); } -static int check_and_freshen_nonlocal(const unsigned char *sha1, int freshen) +static int check_and_freshen_nonlocal(const struct object_id *oid, int freshen) { struct alternate_object_database *alt; prepare_alt_odb(the_repository); for (alt = the_repository->objects->alt_odb_list; alt; alt = alt->next) { - const char *path = alt_sha1_path(alt, sha1); + const char *path = alt_sha1_path(alt, oid->hash); if (check_and_freshen_file(path, freshen)) return 1; } return 0; } -static int check_and_freshen(const unsigned char *sha1, int freshen) +static int check_and_freshen(const struct object_id *oid, int freshen) { - return check_and_freshen_local(sha1, freshen) || - check_and_freshen_nonlocal(sha1, freshen); + return check_and_freshen_local(oid, freshen) || + check_and_freshen_nonlocal(oid, freshen); } -int has_loose_object_nonlocal(const unsigned char *sha1) +int has_loose_object_nonlocal(const struct object_id *oid) { - return check_and_freshen_nonlocal(sha1, 0); + return check_and_freshen_nonlocal(oid, 0); } -static int has_loose_object(const unsigned char *sha1) +static int has_loose_object(const struct object_id *oid) { - return check_and_freshen(sha1, 0); + return check_and_freshen(oid, 0); } static void mmap_limit_check(size_t length) @@ -1250,7 +1271,7 @@ int oid_object_info_extended(struct repository *r, const struct object_id *oid, oi = &blank_oi; if (!(flags & OBJECT_INFO_SKIP_CACHED)) { - struct cached_object *co = find_cached_object(real->hash); + struct cached_object *co = find_cached_object(real); if (co) { if (oi->typep) *(oi->typep) = co->type; @@ -1270,7 +1291,7 @@ int oid_object_info_extended(struct repository *r, const struct object_id *oid, } while (1) { - if (find_pack_entry(r, real->hash, &e)) + if (find_pack_entry(r, real, &e)) break; if (flags & OBJECT_INFO_IGNORE_LOOSE) @@ -1283,7 +1304,7 @@ int oid_object_info_extended(struct repository *r, const struct object_id *oid, /* Not a loose object; someone else may have just packed it. */ if (!(flags & OBJECT_INFO_QUICK)) { reprepare_packed_git(r); - if (find_pack_entry(r, real->hash, &e)) + if (find_pack_entry(r, real, &e)) break; } @@ -1363,7 +1384,7 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type, struct cached_object *co; hash_object_file(buf, len, type_name(type), oid); - if (has_sha1_file(oid->hash) || find_cached_object(oid->hash)) + if (has_sha1_file(oid->hash) || find_cached_object(oid)) return 0; ALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc); co = &cached_objects[cached_object_nr++]; @@ -1371,7 +1392,7 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type, co->type = type; co->buf = xmalloc(len); memcpy(co->buf, buf, len); - hashcpy(co->sha1, oid->hash); + oidcpy(&co->oid, oid); return 0; } @@ -1667,15 +1688,15 @@ static int write_loose_object(const struct object_id *oid, char *hdr, return finalize_object_file(tmp_file.buf, filename.buf); } -static int freshen_loose_object(const unsigned char *sha1) +static int freshen_loose_object(const struct object_id *oid) { - return check_and_freshen(sha1, 1); + return check_and_freshen(oid, 1); } -static int freshen_packed_object(const unsigned char *sha1) +static int freshen_packed_object(const struct object_id *oid) { struct pack_entry e; - if (!find_pack_entry(the_repository, sha1, &e)) + if (!find_pack_entry(the_repository, oid, &e)) return 0; if (e.p->freshened) return 1; @@ -1695,7 +1716,7 @@ int write_object_file(const void *buf, unsigned long len, const char *type, * it out into .git/objects/??/?{38} file. */ write_object_file_prepare(buf, len, type, oid, hdr, &hdrlen); - if (freshen_packed_object(oid->hash) || freshen_loose_object(oid->hash)) + if (freshen_packed_object(oid) || freshen_loose_object(oid)) return 0; return write_loose_object(oid, hdr, hdrlen, buf, len, 0); } @@ -1714,7 +1735,7 @@ int hash_object_file_literally(const void *buf, unsigned long len, if (!(flags & HASH_WRITE_OBJECT)) goto cleanup; - if (freshen_packed_object(oid->hash) || freshen_loose_object(oid->hash)) + if (freshen_packed_object(oid) || freshen_loose_object(oid)) goto cleanup; status = write_loose_object(oid, header, hdrlen, buf, len, 0); @@ -1732,7 +1753,7 @@ int force_object_loose(const struct object_id *oid, time_t mtime) int hdrlen; int ret; - if (has_loose_object(oid->hash)) + if (has_loose_object(oid)) return 0; buf = read_object(oid->hash, &type, &len); if (!buf) @@ -2232,7 +2253,7 @@ int read_loose_object(const char *path, goto out; } - if (*type == OBJ_BLOB) { + if (*type == OBJ_BLOB && *size > big_file_threshold) { if (check_stream_sha1(&stream, hdr, *size, path, expected_oid->hash) < 0) goto out; } else { diff --git a/sha1-lookup.c b/sha1-lookup.c index 8d0b1db3e2..796ab68da8 100644 --- a/sha1-lookup.c +++ b/sha1-lookup.c @@ -81,7 +81,7 @@ int sha1_pos(const unsigned char *sha1, void *table, size_t nr, mi = (nr - 1) * (miv - lov) / (hiv - lov); if (lo <= mi && mi < hi) break; - die("BUG: assertion failed in binary search"); + BUG("assertion failed in binary search"); } } } diff --git a/sha1-name.c b/sha1-name.c index 80030b159a..60d9ef3c7e 100644 --- a/sha1-name.c +++ b/sha1-name.c @@ -346,7 +346,6 @@ static int show_ambiguous_object(const struct object_id *oid, void *data) struct strbuf desc = STRBUF_INIT; int type; - if (ds->fn && !ds->fn(oid, ds->cb_data)) return 0; @@ -373,6 +372,40 @@ static int show_ambiguous_object(const struct object_id *oid, void *data) return 0; } +static int collect_ambiguous(const struct object_id *oid, void *data) +{ + oid_array_append(data, oid); + return 0; +} + +static int sort_ambiguous(const void *a, const void *b) +{ + int a_type = oid_object_info(the_repository, a, NULL); + int b_type = oid_object_info(the_repository, b, NULL); + int a_type_sort; + int b_type_sort; + + /* + * Sorts by hash within the same object type, just as + * oid_array_for_each_unique() would do. + */ + if (a_type == b_type) + return oidcmp(a, b); + + /* + * Between object types show tags, then commits, and finally + * trees and blobs. + * + * The object_type enum is commit, tree, blob, tag, but we + * want tag, commit, tree blob. Cleverly (perhaps too + * cleverly) do that with modulus, since the enum assigns 1 to + * commit, so tag becomes 0. + */ + a_type_sort = a_type % 4; + b_type_sort = b_type % 4; + return a_type_sort > b_type_sort ? 1 : -1; +} + static int get_short_oid(const char *name, int len, struct object_id *oid, unsigned flags) { @@ -384,7 +417,7 @@ static int get_short_oid(const char *name, int len, struct object_id *oid, return -1; if (HAS_MULTI_BITS(flags & GET_OID_DISAMBIGUATORS)) - die("BUG: multiple get_short_oid disambiguator flags"); + BUG("multiple get_short_oid disambiguator flags"); if (flags & GET_OID_COMMIT) ds.fn = disambiguate_commit_only; @@ -404,6 +437,8 @@ static int get_short_oid(const char *name, int len, struct object_id *oid, status = finish_object_disambiguation(&ds, oid); if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) { + struct oid_array collect = OID_ARRAY_INIT; + error(_("short SHA1 %s is ambiguous"), ds.hex_pfx); /* @@ -416,18 +451,17 @@ static int get_short_oid(const char *name, int len, struct object_id *oid, ds.fn = NULL; advise(_("The candidates are:")); - for_each_abbrev(ds.hex_pfx, show_ambiguous_object, &ds); + for_each_abbrev(ds.hex_pfx, collect_ambiguous, &collect); + QSORT(collect.oid, collect.nr, sort_ambiguous); + + if (oid_array_for_each(&collect, show_ambiguous_object, &ds)) + BUG("show_ambiguous_object shouldn't return non-zero"); + oid_array_clear(&collect); } return status; } -static int collect_ambiguous(const struct object_id *oid, void *data) -{ - oid_array_append(data, oid); - return 0; -} - int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data) { struct oid_array collect = OID_ARRAY_INIT; @@ -1685,8 +1719,8 @@ static int get_oid_with_context_1(const char *name, if (new_filename) filename = new_filename; if (flags & GET_OID_FOLLOW_SYMLINKS) { - ret = get_tree_entry_follow_symlinks(tree_oid.hash, - filename, oid->hash, &oc->symlink_path, + ret = get_tree_entry_follow_symlinks(&tree_oid, + filename, oid, &oc->symlink_path, &oc->mode); } else { ret = get_tree_entry(&tree_oid, filename, oid, @@ -1698,7 +1732,6 @@ static int get_oid_with_context_1(const char *name, name, len); } } - hashcpy(oc->tree, tree_oid.hash); if (flags & GET_OID_RECORD_PATH) oc->path = xstrdup(filename); @@ -1729,6 +1762,6 @@ void maybe_die_on_misspelt_object_name(const char *name, const char *prefix) int get_oid_with_context(const char *str, unsigned flags, struct object_id *oid, struct object_context *oc) { if (flags & GET_OID_FOLLOW_SYMLINKS && flags & GET_OID_ONLY_TO_DIE) - die("BUG: incompatible flags for get_sha1_with_context"); + BUG("incompatible flags for get_sha1_with_context"); return get_oid_with_context_1(str, flags, NULL, oid, oc); } @@ -20,7 +20,7 @@ static char *alternate_shallow_file; void set_alternate_shallow_file(const char *path, int override) { if (is_shallow != -1) - die("BUG: is_repository_shallow must not be called before set_alternate_shallow_file"); + BUG("is_repository_shallow must not be called before set_alternate_shallow_file"); if (alternate_shallow_file && !override) return; free(alternate_shallow_file); @@ -218,7 +218,7 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av, static void check_shallow_file_for_update(void) { if (is_shallow == -1) - die("BUG: shallow must be initialized by now"); + BUG("shallow must be initialized by now"); if (!stat_validity_check(&shallow_stat, git_path_shallow())) die("shallow file has changed since we read it"); @@ -353,7 +353,7 @@ void advertise_shallow_grafts(int fd) */ void prune_shallow(int show_only) { - static struct lock_file shallow_lock; + struct lock_file shallow_lock = LOCK_INIT; struct strbuf sb = STRBUF_INIT; int fd; @@ -446,7 +446,7 @@ static uint32_t *paint_alloc(struct paint_info *info) void *p; if (!info->pool_count || size > info->end - info->free) { if (size > POOL_SIZE) - die("BUG: pool size too small for %d in paint_alloc()", + BUG("pool size too small for %d in paint_alloc()", size); info->pool_count++; REALLOC_ARRAY(info->pools, info->pool_count); @@ -3,6 +3,7 @@ #include "exec-cmd.h" #include "strbuf.h" #include "run-command.h" +#include "alias.h" #define COMMAND_DIR "git-shell-commands" #define HELP_COMMAND COMMAND_DIR "/help" diff --git a/sigchain.c b/sigchain.c index 2ac43bbd28..022677b6ab 100644 --- a/sigchain.c +++ b/sigchain.c @@ -13,7 +13,7 @@ static struct sigchain_signal signals[SIGCHAIN_MAX_SIGNALS]; static void check_signum(int sig) { if (sig < 1 || sig >= SIGCHAIN_MAX_SIGNALS) - die("BUG: signal out of range: %d", sig); + BUG("signal out of range: %d", sig); } int sigchain_push(int sig, sigchain_fun f) diff --git a/split-index.c b/split-index.c index 3eb8ff1b43..660c75f31f 100644 --- a/split-index.c +++ b/split-index.c @@ -18,12 +18,12 @@ int read_link_extension(struct index_state *istate, struct split_index *si; int ret; - if (sz < 20) + if (sz < the_hash_algo->rawsz) return error("corrupt link extension (too short)"); si = init_split_index(istate); - hashcpy(si->base_sha1, data); - data += 20; - sz -= 20; + hashcpy(si->base_oid.hash, data); + data += the_hash_algo->rawsz; + sz -= the_hash_algo->rawsz; if (!sz) return 0; si->delete_bitmap = ewah_new(); @@ -45,7 +45,7 @@ int write_link_extension(struct strbuf *sb, struct index_state *istate) { struct split_index *si = istate->split_index; - strbuf_add(sb, si->base_sha1, 20); + strbuf_add(sb, si->base_oid.hash, the_hash_algo->rawsz); if (!si->delete_bitmap && !si->replace_bitmap) return 0; ewah_serialize_strbuf(si->delete_bitmap, sb); diff --git a/split-index.h b/split-index.h index 43d66826eb..7a435ca2c9 100644 --- a/split-index.h +++ b/split-index.h @@ -1,12 +1,14 @@ #ifndef SPLIT_INDEX_H #define SPLIT_INDEX_H +#include "cache.h" + struct index_state; struct strbuf; struct ewah_bitmap; struct split_index { - unsigned char base_sha1[20]; + struct object_id base_oid; struct index_state *base; struct ewah_bitmap *delete_bitmap; struct ewah_bitmap *replace_bitmap; @@ -334,12 +334,12 @@ void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap) len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, cp); va_end(cp); if (len < 0) - die("BUG: your vsnprintf is broken (returned %d)", len); + BUG("your vsnprintf is broken (returned %d)", len); if (len > strbuf_avail(sb)) { strbuf_grow(sb, len); len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); if (len > strbuf_avail(sb)) - die("BUG: your vsnprintf is broken (insatiable)"); + BUG("your vsnprintf is broken (insatiable)"); } strbuf_setlen(sb, sb->len + len); } diff --git a/submodule-config.c b/submodule-config.c index d87c3ff63a..388ef1f892 100644 --- a/submodule-config.c +++ b/submodule-config.c @@ -44,7 +44,7 @@ static int config_path_cmp(const void *unused_cmp_data, const struct submodule_entry *b = entry_or_key; return strcmp(a->config->path, b->config->path) || - hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1); + oidcmp(&a->config->gitmodules_oid, &b->config->gitmodules_oid); } static int config_name_cmp(const void *unused_cmp_data, @@ -56,7 +56,7 @@ static int config_name_cmp(const void *unused_cmp_data, const struct submodule_entry *b = entry_or_key; return strcmp(a->config->name, b->config->name) || - hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1); + oidcmp(&a->config->gitmodules_oid, &b->config->gitmodules_oid); } static struct submodule_cache *submodule_cache_alloc(void) @@ -109,17 +109,17 @@ void submodule_cache_free(struct submodule_cache *cache) free(cache); } -static unsigned int hash_sha1_string(const unsigned char *sha1, - const char *string) +static unsigned int hash_oid_string(const struct object_id *oid, + const char *string) { - return memhash(sha1, 20) + strhash(string); + return memhash(oid->hash, the_hash_algo->rawsz) + strhash(string); } static void cache_put_path(struct submodule_cache *cache, struct submodule *submodule) { - unsigned int hash = hash_sha1_string(submodule->gitmodules_sha1, - submodule->path); + unsigned int hash = hash_oid_string(&submodule->gitmodules_oid, + submodule->path); struct submodule_entry *e = xmalloc(sizeof(*e)); hashmap_entry_init(e, hash); e->config = submodule; @@ -129,8 +129,8 @@ static void cache_put_path(struct submodule_cache *cache, static void cache_remove_path(struct submodule_cache *cache, struct submodule *submodule) { - unsigned int hash = hash_sha1_string(submodule->gitmodules_sha1, - submodule->path); + unsigned int hash = hash_oid_string(&submodule->gitmodules_oid, + submodule->path); struct submodule_entry e; struct submodule_entry *removed; hashmap_entry_init(&e, hash); @@ -142,8 +142,8 @@ static void cache_remove_path(struct submodule_cache *cache, static void cache_add(struct submodule_cache *cache, struct submodule *submodule) { - unsigned int hash = hash_sha1_string(submodule->gitmodules_sha1, - submodule->name); + unsigned int hash = hash_oid_string(&submodule->gitmodules_oid, + submodule->name); struct submodule_entry *e = xmalloc(sizeof(*e)); hashmap_entry_init(e, hash); e->config = submodule; @@ -151,14 +151,14 @@ static void cache_add(struct submodule_cache *cache, } static const struct submodule *cache_lookup_path(struct submodule_cache *cache, - const unsigned char *gitmodules_sha1, const char *path) + const struct object_id *gitmodules_oid, const char *path) { struct submodule_entry *entry; - unsigned int hash = hash_sha1_string(gitmodules_sha1, path); + unsigned int hash = hash_oid_string(gitmodules_oid, path); struct submodule_entry key; struct submodule key_config; - hashcpy(key_config.gitmodules_sha1, gitmodules_sha1); + oidcpy(&key_config.gitmodules_oid, gitmodules_oid); key_config.path = path; hashmap_entry_init(&key, hash); @@ -171,14 +171,14 @@ static const struct submodule *cache_lookup_path(struct submodule_cache *cache, } static struct submodule *cache_lookup_name(struct submodule_cache *cache, - const unsigned char *gitmodules_sha1, const char *name) + const struct object_id *gitmodules_oid, const char *name) { struct submodule_entry *entry; - unsigned int hash = hash_sha1_string(gitmodules_sha1, name); + unsigned int hash = hash_oid_string(gitmodules_oid, name); struct submodule_entry key; struct submodule key_config; - hashcpy(key_config.gitmodules_sha1, gitmodules_sha1); + oidcpy(&key_config.gitmodules_oid, gitmodules_oid); key_config.name = name; hashmap_entry_init(&key, hash); @@ -190,6 +190,31 @@ static struct submodule *cache_lookup_name(struct submodule_cache *cache, return NULL; } +int check_submodule_name(const char *name) +{ + /* Disallow empty names */ + if (!*name) + return -1; + + /* + * Look for '..' as a path component. Check both '/' and '\\' as + * separators rather than is_dir_sep(), because we want the name rules + * to be consistent across platforms. + */ + goto in_component; /* always start inside component */ + while (*name) { + char c = *name++; + if (c == '/' || c == '\\') { +in_component: + if (name[0] == '.' && name[1] == '.' && + (!name[2] || name[2] == '/' || name[2] == '\\')) + return -1; + } + } + + return 0; +} + static int name_and_item_from_var(const char *var, struct strbuf *name, struct strbuf *item) { @@ -201,18 +226,24 @@ static int name_and_item_from_var(const char *var, struct strbuf *name, return 0; strbuf_add(name, subsection, subsection_len); + if (check_submodule_name(name->buf) < 0) { + warning(_("ignoring suspicious submodule name: %s"), name->buf); + strbuf_release(name); + return 0; + } + strbuf_addstr(item, key); return 1; } static struct submodule *lookup_or_create_by_name(struct submodule_cache *cache, - const unsigned char *gitmodules_sha1, const char *name) + const struct object_id *gitmodules_oid, const char *name) { struct submodule *submodule; struct strbuf name_buf = STRBUF_INIT; - submodule = cache_lookup_name(cache, gitmodules_sha1, name); + submodule = cache_lookup_name(cache, gitmodules_oid, name); if (submodule) return submodule; @@ -230,7 +261,7 @@ static struct submodule *lookup_or_create_by_name(struct submodule_cache *cache, submodule->branch = NULL; submodule->recommend_shallow = -1; - hashcpy(submodule->gitmodules_sha1, gitmodules_sha1); + oidcpy(&submodule->gitmodules_oid, gitmodules_oid); cache_add(cache, submodule); @@ -341,12 +372,12 @@ int parse_push_recurse_submodules_arg(const char *opt, const char *arg) return parse_push_recurse(opt, arg, 1); } -static void warn_multiple_config(const unsigned char *treeish_name, +static void warn_multiple_config(const struct object_id *treeish_name, const char *name, const char *option) { const char *commit_string = "WORKTREE"; if (treeish_name) - commit_string = sha1_to_hex(treeish_name); + commit_string = oid_to_hex(treeish_name); warning("%s:.gitmodules, multiple configurations found for " "'submodule.%s.%s'. Skipping second one!", commit_string, name, option); @@ -354,8 +385,8 @@ static void warn_multiple_config(const unsigned char *treeish_name, struct parse_config_parameter { struct submodule_cache *cache; - const unsigned char *treeish_name; - const unsigned char *gitmodules_sha1; + const struct object_id *treeish_name; + const struct object_id *gitmodules_oid; int overwrite; }; @@ -371,7 +402,7 @@ static int parse_config(const char *var, const char *value, void *data) return 0; submodule = lookup_or_create_by_name(me->cache, - me->gitmodules_sha1, + me->gitmodules_oid, name.buf); if (!strcmp(item.buf, "path")) { @@ -389,7 +420,7 @@ static int parse_config(const char *var, const char *value, void *data) } } else if (!strcmp(item.buf, "fetchrecursesubmodules")) { /* when parsing worktree configurations we can die early */ - int die_on_error = is_null_sha1(me->gitmodules_sha1); + int die_on_error = is_null_oid(me->gitmodules_oid); if (!me->overwrite && submodule->fetch_recurse != RECURSE_SUBMODULES_NONE) warn_multiple_config(me->treeish_name, submodule->name, @@ -511,10 +542,10 @@ static const struct submodule *config_from(struct submodule_cache *cache, switch (lookup_type) { case lookup_name: - submodule = cache_lookup_name(cache, oid.hash, key); + submodule = cache_lookup_name(cache, &oid, key); break; case lookup_path: - submodule = cache_lookup_path(cache, oid.hash, key); + submodule = cache_lookup_path(cache, &oid, key); break; } if (submodule) @@ -526,8 +557,8 @@ static const struct submodule *config_from(struct submodule_cache *cache, /* fill the submodule config into the cache */ parameter.cache = cache; - parameter.treeish_name = treeish_name->hash; - parameter.gitmodules_sha1 = oid.hash; + parameter.treeish_name = treeish_name; + parameter.gitmodules_oid = &oid; parameter.overwrite = 0; git_config_from_mem(parse_config, CONFIG_ORIGIN_SUBMODULE_BLOB, rev.buf, config, config_size, ¶meter); @@ -536,9 +567,9 @@ static const struct submodule *config_from(struct submodule_cache *cache, switch (lookup_type) { case lookup_name: - return cache_lookup_name(cache, oid.hash, key); + return cache_lookup_name(cache, &oid, key); case lookup_path: - return cache_lookup_path(cache, oid.hash, key); + return cache_lookup_path(cache, &oid, key); default: return NULL; } @@ -567,7 +598,7 @@ static int gitmodules_cb(const char *var, const char *value, void *data) parameter.cache = repo->submodule_cache; parameter.treeish_name = NULL; - parameter.gitmodules_sha1 = null_sha1; + parameter.gitmodules_oid = &null_oid; parameter.overwrite = 1; return parse_config(var, value, ¶meter); diff --git a/submodule-config.h b/submodule-config.h index 6f686184e8..ca1f94e2d2 100644 --- a/submodule-config.h +++ b/submodule-config.h @@ -1,6 +1,7 @@ #ifndef SUBMODULE_CONFIG_CACHE_H #define SUBMODULE_CONFIG_CACHE_H +#include "cache.h" #include "hashmap.h" #include "submodule.h" #include "strbuf.h" @@ -17,13 +18,13 @@ struct submodule { const char *ignore; const char *branch; struct submodule_update_strategy update_strategy; - /* the sha1 blob id of the responsible .gitmodules file */ - unsigned char gitmodules_sha1[20]; + /* the object id of the responsible .gitmodules file */ + struct object_id gitmodules_oid; int recommend_shallow; }; #define SUBMODULE_INIT { NULL, NULL, NULL, RECURSE_SUBMODULES_NONE, \ - NULL, NULL, SUBMODULE_UPDATE_STRATEGY_INIT, {0}, -1 }; + NULL, NULL, SUBMODULE_UPDATE_STRATEGY_INIT, { { 0 } }, -1 }; struct submodule_cache; struct repository; @@ -47,4 +48,11 @@ const struct submodule *submodule_from_path(struct repository *r, const char *path); void submodule_free(struct repository *r); +/* + * Returns 0 if the name is syntactically acceptable as a submodule "name" + * (e.g., that may be found in the subsection of a .gitmodules file) and -1 + * otherwise. + */ +int check_submodule_name(const char *name); + #endif /* SUBMODULE_CONFIG_H */ diff --git a/submodule.c b/submodule.c index 8fd8e5d178..edc3a206d7 100644 --- a/submodule.c +++ b/submodule.c @@ -153,7 +153,8 @@ void stage_updated_gitmodules(struct index_state *istate) die(_("staging updated .gitmodules failed")); } -static int add_submodule_odb(const char *path) +/* TODO: remove this function, use repo_submodule_init instead. */ +int add_submodule_odb(const char *path) { struct strbuf objects_directory = STRBUF_INIT; int ret = 0; @@ -968,7 +969,7 @@ int find_unpushed_submodules(struct oid_array *commits, static int push_submodule(const char *path, const struct remote *remote, - const char **refspec, int refspec_nr, + const struct refspec *rs, const struct string_list *push_options, int dry_run) { @@ -991,8 +992,8 @@ static int push_submodule(const char *path, if (remote->origin != REMOTE_UNCONFIGURED) { int i; argv_array_push(&cp.args, remote->name); - for (i = 0; i < refspec_nr; i++) - argv_array_push(&cp.args, refspec[i]); + for (i = 0; i < rs->raw_nr; i++) + argv_array_push(&cp.args, rs->raw[i]); } prepare_submodule_repo_env(&cp.env_array); @@ -1013,7 +1014,7 @@ static int push_submodule(const char *path, */ static void submodule_push_check(const char *path, const char *head, const struct remote *remote, - const char **refspec, int refspec_nr) + const struct refspec *rs) { struct child_process cp = CHILD_PROCESS_INIT; int i; @@ -1023,8 +1024,8 @@ static void submodule_push_check(const char *path, const char *head, argv_array_push(&cp.args, head); argv_array_push(&cp.args, remote->name); - for (i = 0; i < refspec_nr; i++) - argv_array_push(&cp.args, refspec[i]); + for (i = 0; i < rs->raw_nr; i++) + argv_array_push(&cp.args, rs->raw[i]); prepare_submodule_repo_env(&cp.env_array); cp.git_cmd = 1; @@ -1043,7 +1044,7 @@ static void submodule_push_check(const char *path, const char *head, int push_unpushed_submodules(struct oid_array *commits, const struct remote *remote, - const char **refspec, int refspec_nr, + const struct refspec *rs, const struct string_list *push_options, int dry_run) { @@ -1069,8 +1070,7 @@ int push_unpushed_submodules(struct oid_array *commits, for (i = 0; i < needs_pushing.nr; i++) submodule_push_check(needs_pushing.items[i].string, - head, remote, - refspec, refspec_nr); + head, remote, rs); free(head); } @@ -1078,7 +1078,7 @@ int push_unpushed_submodules(struct oid_array *commits, for (i = 0; i < needs_pushing.nr; i++) { const char *path = needs_pushing.items[i].string; fprintf(stderr, "Pushing submodule '%s'\n", path); - if (!push_submodule(path, remote, refspec, refspec_nr, + if (!push_submodule(path, remote, rs, push_options, dry_run)) { fprintf(stderr, "Unable to push submodule '%s'\n", path); ret = 0; @@ -1400,7 +1400,7 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked) buf.buf[0] == '2') { /* T = line type, XY = status, SSSS = submodule state */ if (buf.len < strlen("T XY SSSS")) - die("BUG: invalid status --porcelain=2 line %s", + BUG("invalid status --porcelain=2 line %s", buf.buf); if (buf.buf[5] == 'S' && buf.buf[8] == 'U') @@ -1569,7 +1569,7 @@ static void submodule_reset_index(const char *path) get_super_prefix_or_empty(), path); argv_array_pushl(&cp.args, "read-tree", "-u", "--reset", NULL); - argv_array_push(&cp.args, EMPTY_TREE_SHA1_HEX); + argv_array_push(&cp.args, empty_tree_oid_hex()); if (run_command(&cp)) die("could not reset submodule index"); @@ -1609,7 +1609,7 @@ int submodule_move_head(const char *path, sub = submodule_from_path(the_repository, &null_oid, path); if (!sub) - die("BUG: could not get submodule information for '%s'", path); + BUG("could not get submodule information for '%s'", path); if (old_head && !(flags & SUBMODULE_MOVE_HEAD_FORCE)) { /* Check if the submodule has a dirty index. */ @@ -1661,9 +1661,9 @@ int submodule_move_head(const char *path, argv_array_push(&cp.args, "-m"); if (!(flags & SUBMODULE_MOVE_HEAD_FORCE)) - argv_array_push(&cp.args, old_head ? old_head : EMPTY_TREE_SHA1_HEX); + argv_array_push(&cp.args, old_head ? old_head : empty_tree_oid_hex()); - argv_array_push(&cp.args, new_head ? new_head : EMPTY_TREE_SHA1_HEX); + argv_array_push(&cp.args, new_head ? new_head : empty_tree_oid_hex()); if (run_command(&cp)) { ret = -1; @@ -1701,171 +1701,6 @@ out: return ret; } -static int find_first_merges(struct object_array *result, const char *path, - struct commit *a, struct commit *b) -{ - int i, j; - struct object_array merges = OBJECT_ARRAY_INIT; - struct commit *commit; - int contains_another; - - char merged_revision[42]; - const char *rev_args[] = { "rev-list", "--merges", "--ancestry-path", - "--all", merged_revision, NULL }; - struct rev_info revs; - struct setup_revision_opt rev_opts; - - memset(result, 0, sizeof(struct object_array)); - memset(&rev_opts, 0, sizeof(rev_opts)); - - /* get all revisions that merge commit a */ - xsnprintf(merged_revision, sizeof(merged_revision), "^%s", - oid_to_hex(&a->object.oid)); - init_revisions(&revs, NULL); - rev_opts.submodule = path; - /* FIXME: can't handle linked worktrees in submodules yet */ - revs.single_worktree = path != NULL; - setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts); - - /* save all revisions from the above list that contain b */ - if (prepare_revision_walk(&revs)) - die("revision walk setup failed"); - while ((commit = get_revision(&revs)) != NULL) { - struct object *o = &(commit->object); - if (in_merge_bases(b, commit)) - add_object_array(o, NULL, &merges); - } - reset_revision_walk(); - - /* Now we've got all merges that contain a and b. Prune all - * merges that contain another found merge and save them in - * result. - */ - for (i = 0; i < merges.nr; i++) { - struct commit *m1 = (struct commit *) merges.objects[i].item; - - contains_another = 0; - for (j = 0; j < merges.nr; j++) { - struct commit *m2 = (struct commit *) merges.objects[j].item; - if (i != j && in_merge_bases(m2, m1)) { - contains_another = 1; - break; - } - } - - if (!contains_another) - add_object_array(merges.objects[i].item, NULL, result); - } - - object_array_clear(&merges); - return result->nr; -} - -static void print_commit(struct commit *commit) -{ - struct strbuf sb = STRBUF_INIT; - struct pretty_print_context ctx = {0}; - ctx.date_mode.type = DATE_NORMAL; - format_commit_message(commit, " %h: %m %s", &sb, &ctx); - fprintf(stderr, "%s\n", sb.buf); - strbuf_release(&sb); -} - -#define MERGE_WARNING(path, msg) \ - warning("Failed to merge submodule %s (%s)", path, msg); - -int merge_submodule(struct object_id *result, const char *path, - const struct object_id *base, const struct object_id *a, - const struct object_id *b, int search) -{ - struct commit *commit_base, *commit_a, *commit_b; - int parent_count; - struct object_array merges; - - int i; - - /* store a in result in case we fail */ - oidcpy(result, a); - - /* we can not handle deletion conflicts */ - if (is_null_oid(base)) - return 0; - if (is_null_oid(a)) - return 0; - if (is_null_oid(b)) - return 0; - - if (add_submodule_odb(path)) { - MERGE_WARNING(path, "not checked out"); - return 0; - } - - if (!(commit_base = lookup_commit_reference(base)) || - !(commit_a = lookup_commit_reference(a)) || - !(commit_b = lookup_commit_reference(b))) { - MERGE_WARNING(path, "commits not present"); - return 0; - } - - /* check whether both changes are forward */ - if (!in_merge_bases(commit_base, commit_a) || - !in_merge_bases(commit_base, commit_b)) { - MERGE_WARNING(path, "commits don't follow merge-base"); - return 0; - } - - /* Case #1: a is contained in b or vice versa */ - if (in_merge_bases(commit_a, commit_b)) { - oidcpy(result, b); - return 1; - } - if (in_merge_bases(commit_b, commit_a)) { - oidcpy(result, a); - return 1; - } - - /* - * Case #2: There are one or more merges that contain a and b in - * the submodule. If there is only one, then present it as a - * suggestion to the user, but leave it marked unmerged so the - * user needs to confirm the resolution. - */ - - /* Skip the search if makes no sense to the calling context. */ - if (!search) - return 0; - - /* find commit which merges them */ - parent_count = find_first_merges(&merges, path, commit_a, commit_b); - switch (parent_count) { - case 0: - MERGE_WARNING(path, "merge following commits not found"); - break; - - case 1: - MERGE_WARNING(path, "not fast-forward"); - fprintf(stderr, "Found a possible merge resolution " - "for the submodule:\n"); - print_commit((struct commit *) merges.objects[0].item); - fprintf(stderr, - "If this is correct simply add it to the index " - "for example\n" - "by using:\n\n" - " git update-index --cacheinfo 160000 %s \"%s\"\n\n" - "which will accept this suggestion.\n", - oid_to_hex(&merges.objects[0].item->oid), path); - break; - - default: - MERGE_WARNING(path, "multiple merges found"); - for (i = 0; i < merges.nr; i++) - print_commit((struct commit *) merges.objects[i].item); - } - - object_array_clear(&merges); - return 0; -} - /* * Embeds a single submodules git directory into the superprojects git dir, * non recursively. @@ -1967,7 +1802,7 @@ void absorb_git_dir_into_superproject(const char *prefix, struct strbuf sb = STRBUF_INIT; if (flags & ~ABSORB_GITDIR_RECURSE_SUBMODULES) - die("BUG: we don't know how to pass the flags down?"); + BUG("we don't know how to pass the flags down?"); strbuf_addstr(&sb, get_super_prefix_or_empty()); strbuf_addstr(&sb, path); @@ -2045,7 +1880,7 @@ const char *get_superproject_working_tree(void) if (super_sub_len > cwd_len || strcmp(&cwd[cwd_len - super_sub_len], super_sub)) - die (_("BUG: returned path string doesn't match cwd?")); + BUG("returned path string doesn't match cwd?"); super_wt = xstrdup(cwd); super_wt[cwd_len - super_sub_len] = '\0'; diff --git a/submodule.h b/submodule.h index e5526f6aaa..a2871d0312 100644 --- a/submodule.h +++ b/submodule.h @@ -89,10 +89,8 @@ extern int submodule_uses_gitfile(const char *path); #define SUBMODULE_REMOVAL_IGNORE_UNTRACKED (1<<1) #define SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED (1<<2) extern int bad_to_remove_submodule(const char *path, unsigned flags); -extern int merge_submodule(struct object_id *result, const char *path, - const struct object_id *base, - const struct object_id *a, - const struct object_id *b, int search); + +int add_submodule_odb(const char *path); /* Checks if there are submodule changes in a..b. */ extern int submodule_touches_in_range(struct object_id *a, @@ -100,9 +98,10 @@ extern int submodule_touches_in_range(struct object_id *a, extern int find_unpushed_submodules(struct oid_array *commits, const char *remotes_name, struct string_list *needs_pushing); +struct refspec; extern int push_unpushed_submodules(struct oid_array *commits, const struct remote *remote, - const char **refspec, int refspec_nr, + const struct refspec *rs, const struct string_list *push_options, int dry_run); /* diff --git a/t/diff-lib.sh b/t/diff-lib.sh index c211dc40ee..2de880f7a5 100644 --- a/t/diff-lib.sh +++ b/t/diff-lib.sh @@ -1,6 +1,6 @@ : -sanitize_diff_raw='/^:/s/ '"\($_x40\)"' '"\($_x40\)"' \([A-Z]\)[0-9]* / \1 \2 \3# /' +sanitize_diff_raw='/^:/s/ '"\($OID_REGEX\)"' '"\($OID_REGEX\)"' \([A-Z]\)[0-9]* / \1 \2 \3# /' compare_diff_raw () { # When heuristics are improved, the score numbers would change. # Ignore them while comparing. @@ -12,7 +12,7 @@ compare_diff_raw () { test_cmp .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2 } -sanitize_diff_raw_z='/^:/s/ '"$_x40"' '"$_x40"' \([A-Z]\)[0-9]*$/ X X \1#/' +sanitize_diff_raw_z='/^:/s/ '"$OID_REGEX"' '"$OID_REGEX"' \([A-Z]\)[0-9]*$/ X X \1#/' compare_diff_raw_z () { # When heuristics are improved, the score numbers would change. # Ignore them while comparing. diff --git a/t/helper/test-dump-split-index.c b/t/helper/test-dump-split-index.c index 4e2fdb5e30..63c689d6ee 100644 --- a/t/helper/test-dump-split-index.c +++ b/t/helper/test-dump-split-index.c @@ -14,13 +14,13 @@ int cmd__dump_split_index(int ac, const char **av) int i; do_read_index(&the_index, av[1], 1); - printf("own %s\n", sha1_to_hex(the_index.sha1)); + printf("own %s\n", oid_to_hex(&the_index.oid)); si = the_index.split_index; if (!si) { printf("not a split index\n"); return 0; } - printf("base %s\n", sha1_to_hex(si->base_sha1)); + printf("base %s\n", oid_to_hex(&si->base_oid)); for (i = 0; i < the_index.cache_nr; i++) { struct cache_entry *ce = the_index.cache[i]; printf("%06o %s %d\t%s\n", ce->ce_mode, diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c index d7c55c2355..bd92fb305a 100644 --- a/t/helper/test-dump-untracked-cache.c +++ b/t/helper/test-dump-untracked-cache.c @@ -23,7 +23,7 @@ static void dump(struct untracked_cache_dir *ucd, struct strbuf *base) len = base->len; strbuf_addf(base, "%s/", ucd->name); printf("%s %s", base->buf, - sha1_to_hex(ucd->exclude_sha1)); + oid_to_hex(&ucd->exclude_oid)); if (ucd->recurse) fputs(" recurse", stdout); if (ucd->check_only) diff --git a/t/helper/test-example-decorate.c b/t/helper/test-example-decorate.c index 081115bf8e..a20a6161e4 100644 --- a/t/helper/test-example-decorate.c +++ b/t/helper/test-example-decorate.c @@ -30,10 +30,10 @@ int cmd__example_decorate(int argc, const char **argv) two = lookup_unknown_object(two_oid.hash); ret = add_decoration(&n, one, &decoration_a); if (ret) - die("BUG: when adding a brand-new object, NULL should be returned"); + BUG("when adding a brand-new object, NULL should be returned"); ret = add_decoration(&n, two, NULL); if (ret) - die("BUG: when adding a brand-new object, NULL should be returned"); + BUG("when adding a brand-new object, NULL should be returned"); /* * When re-adding an already existing object, the old decoration is @@ -41,10 +41,10 @@ int cmd__example_decorate(int argc, const char **argv) */ ret = add_decoration(&n, one, NULL); if (ret != &decoration_a) - die("BUG: when readding an already existing object, existing decoration should be returned"); + BUG("when readding an already existing object, existing decoration should be returned"); ret = add_decoration(&n, two, &decoration_b); if (ret) - die("BUG: when readding an already existing object, existing decoration should be returned"); + BUG("when readding an already existing object, existing decoration should be returned"); /* * Lookup returns the added declarations, or NULL if the object was @@ -52,14 +52,14 @@ int cmd__example_decorate(int argc, const char **argv) */ ret = lookup_decoration(&n, one); if (ret) - die("BUG: lookup should return added declaration"); + BUG("lookup should return added declaration"); ret = lookup_decoration(&n, two); if (ret != &decoration_b) - die("BUG: lookup should return added declaration"); + BUG("lookup should return added declaration"); three = lookup_unknown_object(three_oid.hash); ret = lookup_decoration(&n, three); if (ret) - die("BUG: lookup for unknown object should return NULL"); + BUG("lookup for unknown object should return NULL"); /* * The user can also loop through all entries. @@ -69,7 +69,7 @@ int cmd__example_decorate(int argc, const char **argv) objects_noticed++; } if (objects_noticed != 2) - die("BUG: should have 2 objects"); + BUG("should have 2 objects"); return 0; } diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c index e115d44ac2..ae091d9b3e 100644 --- a/t/helper/test-path-utils.c +++ b/t/helper/test-path-utils.c @@ -1,6 +1,7 @@ #include "test-tool.h" #include "cache.h" #include "string-list.h" +#include "utf8.h" /* * A "string_list_each_func_t" function that normalizes an entry from @@ -171,6 +172,11 @@ static struct test_data dirname_data[] = { { NULL, NULL } }; +static int is_dotgitmodules(const char *path) +{ + return is_hfs_dotgitmodules(path) || is_ntfs_dotgitmodules(path); +} + int cmd__path_utils(int argc, const char **argv) { if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) { @@ -271,6 +277,20 @@ int cmd__path_utils(int argc, const char **argv) if (argc == 2 && !strcmp(argv[1], "dirname")) return test_function(dirname_data, posix_dirname, argv[1]); + if (argc > 2 && !strcmp(argv[1], "is_dotgitmodules")) { + int res = 0, expect = 1, i; + for (i = 2; i < argc; i++) + if (!strcmp("--not", argv[i])) + expect = !expect; + else if (expect != is_dotgitmodules(argv[i])) + res = error("'%s' is %s.gitmodules", argv[i], + expect ? "not " : ""); + else + fprintf(stderr, "ok: '%s' is %s.gitmodules\n", + argv[i], expect ? "" : "not "); + return !!res; + } + fprintf(stderr, "%s: unknown function name: %s\n", argv[0], argv[1] ? argv[1] : "(there was none)"); return 1; diff --git a/t/helper/test-scrap-cache-tree.c b/t/helper/test-scrap-cache-tree.c index d26d3e7c8b..393f1604ff 100644 --- a/t/helper/test-scrap-cache-tree.c +++ b/t/helper/test-scrap-cache-tree.c @@ -4,10 +4,10 @@ #include "tree.h" #include "cache-tree.h" -static struct lock_file index_lock; - int cmd__scrap_cache_tree(int ac, const char **av) { + struct lock_file index_lock = LOCK_INIT; + setup_git_directory(); hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); if (read_cache() < 0) diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c index 87066ced62..805a45de9c 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -48,6 +48,7 @@ int cmd_main(int argc, const char **argv) { int i; + BUG_exit_code = 99; if (argc < 2) die("I need a test name!"); diff --git a/t/helper/test-write-cache.c b/t/helper/test-write-cache.c index 017dc30380..8837717d36 100644 --- a/t/helper/test-write-cache.c +++ b/t/helper/test-write-cache.c @@ -2,22 +2,18 @@ #include "cache.h" #include "lockfile.h" -static struct lock_file index_lock; - int cmd__write_cache(int argc, const char **argv) { - int i, cnt = 1, lockfd; + struct lock_file index_lock = LOCK_INIT; + int i, cnt = 1; if (argc == 2) cnt = strtol(argv[1], NULL, 0); setup_git_directory(); read_cache(); for (i = 0; i < cnt; i++) { - lockfd = hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); - if (0 <= lockfd) { - write_locked_index(&the_index, &index_lock, COMMIT_LOCK); - } else { - rollback_lock_file(&index_lock); - } + hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); + if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK)) + die("unable to write index file"); } return 0; diff --git a/t/lib-diff-alternative.sh b/t/lib-diff-alternative.sh index 8b4dbf22d2..8d1e408bb5 100644 --- a/t/lib-diff-alternative.sh +++ b/t/lib-diff-alternative.sh @@ -59,9 +59,11 @@ int main(int argc, char **argv) } EOF - cat >expect <<\EOF + file1=$(git rev-parse --short $(git hash-object file1)) + file2=$(git rev-parse --short $(git hash-object file2)) + cat >expect <<EOF diff --git a/file1 b/file2 -index 6faa5a3..e3af329 100644 +index $file1..$file2 100644 --- a/file1 +++ b/file2 @@ -1,26 +1,25 @@ @@ -136,9 +138,11 @@ e f EOF - cat >expect <<\EOF + uniq1=$(git rev-parse --short $(git hash-object uniq1)) + uniq2=$(git rev-parse --short $(git hash-object uniq2)) + cat >expect <<EOF diff --git a/uniq1 b/uniq2 -index b414108..0fdf397 100644 +index $uniq1..$uniq2 100644 --- a/uniq1 +++ b/uniq2 @@ -1,6 +1,6 @@ diff --git a/t/lib-pack.sh b/t/lib-pack.sh index 501078249d..c4d907a450 100644 --- a/t/lib-pack.sh +++ b/t/lib-pack.sh @@ -79,6 +79,18 @@ pack_obj () { ;; esac + # If it's not a delta, we can convince pack-objects to generate a pack + # with just our entry, and then strip off the header (12 bytes) and + # trailer (20 bytes). + if test -z "$2" + then + echo "$1" | git pack-objects --stdout >pack_obj.tmp && + size=$(wc -c <pack_obj.tmp) && + dd if=pack_obj.tmp bs=1 count=$((size - 20 - 12)) skip=12 && + rm -f pack_obj.tmp + return + fi + echo >&2 "BUG: don't know how to print $1${2:+ (from $2)}" return 1 } diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 7fd87dd544..af61d083b4 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -839,7 +839,7 @@ test_expect_success 'writing tree out with git write-tree' ' ' # we know the shape and contents of the tree and know the object ID for it. -test_expect_success 'validate object ID of a known tree' ' +test_expect_success SHA1 'validate object ID of a known tree' ' test "$tree" = 7bb943559a305bdd6bdee2cef6e5df2413c3d30a ' @@ -882,7 +882,7 @@ test_expect_success 'showing stage with git ls-files --stage' ' git ls-files --stage >current ' -test_expect_success 'validate git ls-files output for a known tree' ' +test_expect_success SHA1 'validate git ls-files output for a known tree' ' cat >expected <<-\EOF && 100644 f87290f8eb2cbbea7857214459a0739927eab154 0 path0 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0 path0sym @@ -900,7 +900,7 @@ test_expect_success 'writing tree out with git write-tree' ' tree=$(git write-tree) ' -test_expect_success 'validate object ID for a known tree' ' +test_expect_success SHA1 'validate object ID for a known tree' ' test "$tree" = 087704a96baf1c2d1c869a8b084481e121c88b5b ' @@ -908,7 +908,7 @@ test_expect_success 'showing tree with git ls-tree' ' git ls-tree $tree >current ' -test_expect_success 'git ls-tree output for a known tree' ' +test_expect_success SHA1 'git ls-tree output for a known tree' ' cat >expected <<-\EOF && 100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0 120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym @@ -924,7 +924,7 @@ test_expect_success 'showing tree with git ls-tree -r' ' git ls-tree -r $tree >current ' -test_expect_success 'git ls-tree -r output for a known tree' ' +test_expect_success SHA1 'git ls-tree -r output for a known tree' ' cat >expected <<-\EOF && 100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0 120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym @@ -943,7 +943,7 @@ test_expect_success 'showing tree with git ls-tree -r -t' ' git ls-tree -r -t $tree >current ' -test_expect_success 'git ls-tree -r output for a known tree' ' +test_expect_success SHA1 'git ls-tree -r output for a known tree' ' cat >expected <<-\EOF && 100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0 120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym @@ -964,7 +964,7 @@ test_expect_success 'writing partial tree out with git write-tree --prefix' ' ptree=$(git write-tree --prefix=path3) ' -test_expect_success 'validate object ID for a known tree' ' +test_expect_success SHA1 'validate object ID for a known tree' ' test "$ptree" = 21ae8269cacbe57ae09138dcc3a2887f904d02b3 ' @@ -972,7 +972,7 @@ test_expect_success 'writing partial tree out with git write-tree --prefix' ' ptree=$(git write-tree --prefix=path3/subp3) ' -test_expect_success 'validate object ID for a known tree' ' +test_expect_success SHA1 'validate object ID for a known tree' ' test "$ptree" = 3c5e5399f3a333eddecce7a9b9465b63f65f51e2 ' @@ -1006,7 +1006,7 @@ test_expect_success 'git read-tree followed by write-tree should be idempotent' test "$newtree" = "$tree" ' -test_expect_success 'validate git diff-files output for a know cache/work tree state' ' +test_expect_success SHA1 'validate git diff-files output for a know cache/work tree state' ' cat >expected <<\EOF && :100644 100644 f87290f8eb2cbbea7857214459a0739927eab154 0000000000000000000000000000000000000000 M path0 :120000 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0000000000000000000000000000000000000000 M path0sym @@ -1033,21 +1033,21 @@ test_expect_success 'no diff after checkout and git update-index --refresh' ' ################################################################ P=087704a96baf1c2d1c869a8b084481e121c88b5b -test_expect_success 'git commit-tree records the correct tree in a commit' ' +test_expect_success SHA1 'git commit-tree records the correct tree in a commit' ' commit0=$(echo NO | git commit-tree $P) && tree=$(git show --pretty=raw $commit0 | sed -n -e "s/^tree //p" -e "/^author /q") && test "z$tree" = "z$P" ' -test_expect_success 'git commit-tree records the correct parent in a commit' ' +test_expect_success SHA1 'git commit-tree records the correct parent in a commit' ' commit1=$(echo NO | git commit-tree $P -p $commit0) && parent=$(git show --pretty=raw $commit1 | sed -n -e "s/^parent //p" -e "/^author /q") && test "z$commit0" = "z$parent" ' -test_expect_success 'git commit-tree omits duplicated parent in a commit' ' +test_expect_success SHA1 'git commit-tree omits duplicated parent in a commit' ' commit2=$(echo NO | git commit-tree $P -p $commit0 -p $commit0) && parent=$(git show --pretty=raw $commit2 | sed -n -e "s/^parent //p" -e "/^author /q" | diff --git a/t/t0012-help.sh b/t/t0012-help.sh index 487b92a5de..bc27df7f38 100755 --- a/t/t0012-help.sh +++ b/t/t0012-help.sh @@ -25,6 +25,15 @@ test_expect_success "setup" ' EOF ' +# make sure to exercise these code paths, the output is a bit tricky +# to verify +test_expect_success 'basic help commands' ' + git help >/dev/null && + git help -a >/dev/null && + git help -g >/dev/null && + git help -av >/dev/null +' + test_expect_success "works for commands and guides by default" ' configure_help && git help status && @@ -49,8 +58,23 @@ test_expect_success "--help does not work for guides" " test_i18ncmp expect actual " +test_expect_success 'git help' ' + git help >help.output && + test_i18ngrep "^ clone " help.output && + test_i18ngrep "^ add " help.output && + test_i18ngrep "^ log " help.output && + test_i18ngrep "^ commit " help.output && + test_i18ngrep "^ fetch " help.output +' +test_expect_success 'git help -g' ' + git help -g >help.output && + test_i18ngrep "^ attributes " help.output && + test_i18ngrep "^ everyday " help.output && + test_i18ngrep "^ tutorial " help.output +' + test_expect_success 'generate builtin list' ' - git --list-builtins >builtins + git --list-cmds=builtins >builtins ' while read builtin diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index f46e3c4995..21a8b53132 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -349,4 +349,90 @@ test_submodule_relative_url "(null)" "ssh://hostname:22/repo" "../subrepo" "ssh: test_submodule_relative_url "(null)" "user@host:path/to/repo" "../subrepo" "user@host:path/to/subrepo" test_submodule_relative_url "(null)" "user@host:repo" "../subrepo" "user@host:subrepo" +test_expect_success 'match .gitmodules' ' + test-tool path-utils is_dotgitmodules \ + .gitmodules \ + \ + .git${u200c}modules \ + \ + .Gitmodules \ + .gitmoduleS \ + \ + ".gitmodules " \ + ".gitmodules." \ + ".gitmodules " \ + ".gitmodules. " \ + ".gitmodules ." \ + ".gitmodules.." \ + ".gitmodules " \ + ".gitmodules. " \ + ".gitmodules . " \ + ".gitmodules ." \ + \ + ".Gitmodules " \ + ".Gitmodules." \ + ".Gitmodules " \ + ".Gitmodules. " \ + ".Gitmodules ." \ + ".Gitmodules.." \ + ".Gitmodules " \ + ".Gitmodules. " \ + ".Gitmodules . " \ + ".Gitmodules ." \ + \ + GITMOD~1 \ + gitmod~1 \ + GITMOD~2 \ + gitmod~3 \ + GITMOD~4 \ + \ + "GITMOD~1 " \ + "gitmod~2." \ + "GITMOD~3 " \ + "gitmod~4. " \ + "GITMOD~1 ." \ + "gitmod~2 " \ + "GITMOD~3. " \ + "gitmod~4 . " \ + \ + GI7EBA~1 \ + gi7eba~9 \ + \ + GI7EB~10 \ + GI7EB~11 \ + GI7EB~99 \ + GI7EB~10 \ + GI7E~100 \ + GI7E~101 \ + GI7E~999 \ + ~1000000 \ + ~9999999 \ + \ + --not \ + ".gitmodules x" \ + ".gitmodules .x" \ + \ + " .gitmodules" \ + \ + ..gitmodules \ + \ + gitmodules \ + \ + .gitmodule \ + \ + ".gitmodules x " \ + ".gitmodules .x" \ + \ + GI7EBA~ \ + GI7EBA~0 \ + GI7EBA~~1 \ + GI7EBA~X \ + Gx7EBA~1 \ + GI7EBX~1 \ + \ + GI7EB~1 \ + GI7EB~01 \ + GI7EB~1X +' + test_done diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh index 4ae0995cd9..0c61268fd2 100755 --- a/t/t0090-cache-tree.sh +++ b/t/t0090-cache-tree.sh @@ -9,7 +9,7 @@ cache-tree extension. cmp_cache_tree () { test-tool dump-cache-tree | sed -e '/#(ref)/d' >actual && - sed "s/$_x40/SHA/" <actual >filtered && + sed "s/$OID_REGEX/SHA/" <actual >filtered && test_cmp "$1" filtered } diff --git a/t/t1000-read-tree-m-3way.sh b/t/t1000-read-tree-m-3way.sh index 3c4d2d6045..013c5a7bc3 100755 --- a/t/t1000-read-tree-m-3way.sh +++ b/t/t1000-read-tree-m-3way.sh @@ -128,7 +128,7 @@ cat >expected <<\EOF EOF check_result () { - git ls-files --stage | sed -e 's/ '"$_x40"' / X /' >current && + git ls-files --stage | sed -e 's/ '"$OID_REGEX"' / X /' >current && test_cmp expected current } diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh index 5ededd8e40..1057a96b24 100755 --- a/t/t1001-read-tree-m-2way.sh +++ b/t/t1001-read-tree-m-2way.sh @@ -30,7 +30,7 @@ read_tree_twoway () { compare_change () { sed -n >current \ -e '/^--- /d; /^+++ /d; /^@@ /d;' \ - -e 's/^\([-+][0-7][0-7][0-7][0-7][0-7][0-7]\) '"$_x40"' /\1 X /p' \ + -e 's/^\([-+][0-7][0-7][0-7][0-7][0-7][0-7]\) '"$OID_REGEX"' /\1 X /p' \ "$1" test_cmp expected current } diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh index 7ca2e65d10..9c05f5e1f5 100755 --- a/t/t1002-read-tree-m-u-2way.sh +++ b/t/t1002-read-tree-m-u-2way.sh @@ -16,7 +16,7 @@ compare_change () { -e '1{/^diff --git /d;}' \ -e '2{/^index /d;}' \ -e '/^--- /d; /^+++ /d; /^@@ /d;' \ - -e 's/^\(.[0-7][0-7][0-7][0-7][0-7][0-7]\) '"$_x40"' /\1 X /' "$1" + -e 's/^\(.[0-7][0-7][0-7][0-7][0-7][0-7]\) '"$OID_REGEX"' /\1 X /' "$1" test_cmp expected current } diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh index 2ac3b940c6..13dd510b2e 100755 --- a/t/t1006-cat-file.sh +++ b/t/t1006-cat-file.sh @@ -236,8 +236,8 @@ test_expect_success "--batch-check for an empty line" ' ' test_expect_success 'empty --batch-check notices missing object' ' - echo "$_z40 missing" >expect && - echo "$_z40" | git cat-file --batch-check="" >actual && + echo "$ZERO_OID missing" >expect && + echo "$ZERO_OID" | git cat-file --batch-check="" >actual && test_cmp expect actual ' @@ -294,8 +294,8 @@ test_expect_success 'setup blobs which are likely to delta' ' test_expect_success 'confirm that neither loose blob is a delta' ' cat >expect <<-EOF && - $_z40 - $_z40 + $ZERO_OID + $ZERO_OID EOF git cat-file --batch-check="%(deltabase)" <blobs >actual && test_cmp expect actual diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh index 532682f51c..a37753047e 100755 --- a/t/t1007-hash-object.sh +++ b/t/t1007-hash-object.sh @@ -9,13 +9,13 @@ echo_without_newline() { } test_blob_does_not_exist() { - test_expect_success 'blob does not exist in database' " + test_expect_success SHA1 'blob does not exist in database' " test_must_fail git cat-file blob $1 " } test_blob_exists() { - test_expect_success 'blob exists in database' " + test_expect_success SHA1 'blob exists in database' " git cat-file blob $1 " } @@ -73,19 +73,19 @@ test_expect_success "Can't use --path with --no-filters" ' push_repo -test_expect_success 'hash a file' ' +test_expect_success SHA1 'hash a file' ' test $hello_sha1 = $(git hash-object hello) ' test_blob_does_not_exist $hello_sha1 -test_expect_success 'hash from stdin' ' +test_expect_success SHA1 'hash from stdin' ' test $example_sha1 = $(git hash-object --stdin < example) ' test_blob_does_not_exist $example_sha1 -test_expect_success 'hash a file and write to database' ' +test_expect_success SHA1 'hash a file and write to database' ' test $hello_sha1 = $(git hash-object -w hello) ' @@ -161,7 +161,7 @@ pop_repo for args in "-w --stdin" "--stdin -w"; do push_repo - test_expect_success "hash from stdin and write to database ($args)" ' + test_expect_success SHA1 "hash from stdin and write to database ($args)" ' test $example_sha1 = $(git hash-object $args < example) ' @@ -176,14 +176,14 @@ example" sha1s="$hello_sha1 $example_sha1" -test_expect_success "hash two files with names on stdin" ' +test_expect_success SHA1 "hash two files with names on stdin" ' test "$sha1s" = "$(echo_without_newline "$filenames" | git hash-object --stdin-paths)" ' for args in "-w --stdin-paths" "--stdin-paths -w"; do push_repo - test_expect_success "hash two files with names on stdin and write to database ($args)" ' + test_expect_success SHA1 "hash two files with names on stdin and write to database ($args)" ' test "$sha1s" = "$(echo_without_newline "$filenames" | git hash-object $args)" ' diff --git a/t/t1012-read-tree-df.sh b/t/t1012-read-tree-df.sh index a6a04b6b90..57f0770df1 100755 --- a/t/t1012-read-tree-df.sh +++ b/t/t1012-read-tree-df.sh @@ -32,7 +32,7 @@ settree () { checkindex () { git ls-files -s | - sed "s|^[0-7][0-7]* $_x40 \([0-3]\) |\1 |" >current && + sed "s|^[0-7][0-7]* $OID_REGEX \([0-3]\) |\1 |" >current && cat >expect && test_cmp expect current } diff --git a/t/t1307-config-blob.sh b/t/t1307-config-blob.sh index eed31ffa30..37dc689d8c 100755 --- a/t/t1307-config-blob.sh +++ b/t/t1307-config-blob.sh @@ -73,4 +73,8 @@ test_expect_success 'can parse blob ending with CR' ' test_cmp expect actual ' +test_expect_success 'config --blob outside of a repository is an error' ' + test_must_fail nongit git config --blob=foo --list +' + test_done diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index 664a3a4e4e..e1fd0f0ca8 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -6,7 +6,7 @@ test_description='Test git update-ref and basic ref logging' . ./test-lib.sh -Z=$_z40 +Z=$ZERO_OID m=refs/heads/master n_dir=refs/heads/gu @@ -457,6 +457,66 @@ test_expect_success 'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER test OTHER = $(git cat-file blob "master@{2005-05-26 23:42}:F") ' +# Test adding and deleting pseudorefs + +test_expect_success 'given old value for missing pseudoref, do not create' ' + test_must_fail git update-ref PSEUDOREF $A $B 2>err && + test_path_is_missing .git/PSEUDOREF && + grep "could not read ref" err +' + +test_expect_success 'create pseudoref' ' + git update-ref PSEUDOREF $A && + test $A = $(cat .git/PSEUDOREF) +' + +test_expect_success 'overwrite pseudoref with no old value given' ' + git update-ref PSEUDOREF $B && + test $B = $(cat .git/PSEUDOREF) +' + +test_expect_success 'overwrite pseudoref with correct old value' ' + git update-ref PSEUDOREF $C $B && + test $C = $(cat .git/PSEUDOREF) +' + +test_expect_success 'do not overwrite pseudoref with wrong old value' ' + test_must_fail git update-ref PSEUDOREF $D $E 2>err && + test $C = $(cat .git/PSEUDOREF) && + grep "unexpected object ID" err +' + +test_expect_success 'delete pseudoref' ' + git update-ref -d PSEUDOREF && + test_path_is_missing .git/PSEUDOREF +' + +test_expect_success 'do not delete pseudoref with wrong old value' ' + git update-ref PSEUDOREF $A && + test_must_fail git update-ref -d PSEUDOREF $B 2>err && + test $A = $(cat .git/PSEUDOREF) && + grep "unexpected object ID" err +' + +test_expect_success 'delete pseudoref with correct old value' ' + git update-ref -d PSEUDOREF $A && + test_path_is_missing .git/PSEUDOREF +' + +test_expect_success 'create pseudoref with old OID zero' ' + git update-ref PSEUDOREF $A $Z && + test $A = $(cat .git/PSEUDOREF) +' + +test_expect_success 'do not overwrite pseudoref with old OID zero' ' + test_when_finished git update-ref -d PSEUDOREF && + test_must_fail git update-ref PSEUDOREF $B $Z 2>err && + test $A = $(cat .git/PSEUDOREF) && + grep "already exists" err +' + +# Test --stdin + a=refs/heads/a b=refs/heads/b c=refs/heads/c diff --git a/t/t1407-worktree-ref-store.sh b/t/t1407-worktree-ref-store.sh index 2211f9831f..4623ae15c4 100755 --- a/t/t1407-worktree-ref-store.sh +++ b/t/t1407-worktree-ref-store.sh @@ -50,13 +50,13 @@ test_expect_success 'create_symref(FOO, refs/heads/master)' ' ' test_expect_success 'for_each_reflog()' ' - echo $_z40 > .git/logs/PSEUDO-MAIN && + echo $ZERO_OID > .git/logs/PSEUDO-MAIN && mkdir -p .git/logs/refs/bisect && - echo $_z40 > .git/logs/refs/bisect/random && + echo $ZERO_OID > .git/logs/refs/bisect/random && - echo $_z40 > .git/worktrees/wt/logs/PSEUDO-WT && + echo $ZERO_OID > .git/worktrees/wt/logs/PSEUDO-WT && mkdir -p .git/worktrees/wt/logs/refs/bisect && - echo $_z40 > .git/worktrees/wt/logs/refs/bisect/wt-random && + echo $ZERO_OID > .git/worktrees/wt/logs/refs/bisect/wt-random && $RWT for-each-reflog | cut -c 42- | sort >actual && cat >expected <<-\EOF && diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index cb4b66e29d..91fd71444d 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -713,7 +713,7 @@ test_expect_success 'fsck notices dangling objects' ' test_expect_success 'fsck $name notices bogus $name' ' test_must_fail git fsck bogus && - test_must_fail git fsck $_z40 + test_must_fail git fsck $ZERO_OID ' test_expect_success 'bogus head does not fallback to all heads' ' @@ -723,7 +723,7 @@ test_expect_success 'bogus head does not fallback to all heads' ' blob=$(git rev-parse :foo) && test_when_finished "git rm --cached foo" && remove_object $blob && - test_must_fail git fsck $_z40 >out 2>&1 && + test_must_fail git fsck $ZERO_OID >out 2>&1 && ! grep $blob out ' diff --git a/t/t1501-work-tree.sh b/t/t1501-work-tree.sh index 9c0bc65250..afcdfafe45 100755 --- a/t/t1501-work-tree.sh +++ b/t/t1501-work-tree.sh @@ -238,10 +238,10 @@ test_expect_success '_gently() groks relative GIT_DIR & GIT_WORK_TREE' ' test_expect_success 'diff-index respects work tree under .git dir' ' cat >diff-index-cached.expected <<-EOF && - :000000 100644 $_z40 $EMPTY_BLOB A sub/dir/tracked + :000000 100644 $ZERO_OID $EMPTY_BLOB A sub/dir/tracked EOF cat >diff-index.expected <<-EOF && - :000000 100644 $_z40 $_z40 A sub/dir/tracked + :000000 100644 $ZERO_OID $ZERO_OID A sub/dir/tracked EOF ( @@ -257,7 +257,7 @@ test_expect_success 'diff-index respects work tree under .git dir' ' test_expect_success 'diff-files respects work tree under .git dir' ' cat >diff-files.expected <<-EOF && - :100644 100644 $EMPTY_BLOB $_z40 M sub/dir/tracked + :100644 100644 $EMPTY_BLOB $ZERO_OID M sub/dir/tracked EOF ( diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh index 711704ba5a..96fe3754c8 100755 --- a/t/t1512-rev-parse-disambiguation.sh +++ b/t/t1512-rev-parse-disambiguation.sh @@ -22,6 +22,12 @@ one tagged as v1.0.0. They all have one regular file each. . ./test-lib.sh +if ! test_have_prereq SHA1 +then + skip_all='not using SHA-1 for objects' + test_done +fi + test_expect_success 'blob and tree' ' test_tick && ( @@ -361,4 +367,25 @@ test_expect_success 'core.disambiguate does not override context' ' git -c core.disambiguate=committish rev-parse $sha1^{tree} ' +test_expect_success C_LOCALE_OUTPUT 'ambiguous commits are printed by type first, then hash order' ' + test_must_fail git rev-parse 0000 2>stderr && + grep ^hint: stderr >hints && + grep 0000 hints >objects && + cat >expected <<-\EOF && + tag + commit + tree + blob + EOF + awk "{print \$3}" <objects >objects.types && + uniq <objects.types >objects.types.uniq && + test_cmp expected objects.types.uniq && + for type in tag commit tree blob + do + grep $type objects >$type.objects && + sort $type.objects >$type.objects.sorted && + test_cmp $type.objects.sorted $type.objects + done +' + test_done diff --git a/t/t1601-index-bogus.sh b/t/t1601-index-bogus.sh index 73cc9323cd..4171f1e141 100755 --- a/t/t1601-index-bogus.sh +++ b/t/t1601-index-bogus.sh @@ -4,7 +4,7 @@ test_description='test handling of bogus index entries' . ./test-lib.sh test_expect_success 'create tree with null sha1' ' - tree=$(printf "160000 commit $_z40\\tbroken\\n" | git mktree) + tree=$(printf "160000 commit $ZERO_OID\\tbroken\\n" | git mktree) ' test_expect_success 'read-tree refuses to read null sha1' ' diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh index e4f4c4df4e..1e81b33b2e 100755 --- a/t/t1700-split-index.sh +++ b/t/t1700-split-index.sh @@ -426,7 +426,7 @@ test_expect_success 'writing split index with null sha1 does not write cache tre git commit -m "commit" && { git ls-tree HEAD && - printf "160000 commit $_z40\\tbroken\\n" + printf "160000 commit $ZERO_OID\\tbroken\\n" } >broken-tree && echo "add broken entry" >msg && diff --git a/t/t2011-checkout-invalid-head.sh b/t/t2011-checkout-invalid-head.sh index c5501b008c..0e8d56aa76 100755 --- a/t/t2011-checkout-invalid-head.sh +++ b/t/t2011-checkout-invalid-head.sh @@ -15,7 +15,7 @@ test_expect_success 'checkout should not start branch from a tree' ' ' test_expect_success 'checkout master from invalid HEAD' ' - echo $_z40 >.git/HEAD && + echo $ZERO_OID >.git/HEAD && git checkout master -- ' diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh index 2240498924..d2e49f7632 100755 --- a/t/t2025-worktree-add.sh +++ b/t/t2025-worktree-add.sh @@ -477,7 +477,7 @@ post_checkout_hook () { test_expect_success '"add" invokes post-checkout hook (branch)' ' post_checkout_hook && { - echo $_z40 $(git rev-parse HEAD) 1 && + echo $ZERO_OID $(git rev-parse HEAD) 1 && echo $(pwd)/.git/worktrees/gumby && echo $(pwd)/gumby } >hook.expect && @@ -488,7 +488,7 @@ test_expect_success '"add" invokes post-checkout hook (branch)' ' test_expect_success '"add" invokes post-checkout hook (detached)' ' post_checkout_hook && { - echo $_z40 $(git rev-parse HEAD) 1 && + echo $ZERO_OID $(git rev-parse HEAD) 1 && echo $(pwd)/.git/worktrees/grumpy && echo $(pwd)/grumpy } >hook.expect && @@ -506,7 +506,7 @@ test_expect_success '"add --no-checkout" suppresses post-checkout hook' ' test_expect_success '"add" in other worktree invokes post-checkout hook' ' post_checkout_hook && { - echo $_z40 $(git rev-parse HEAD) 1 && + echo $ZERO_OID $(git rev-parse HEAD) 1 && echo $(pwd)/.git/worktrees/guppy && echo $(pwd)/guppy } >hook.expect && @@ -518,7 +518,7 @@ test_expect_success '"add" in bare repo invokes post-checkout hook' ' rm -rf bare && git clone --bare . bare && { - echo $_z40 $(git --git-dir=bare rev-parse HEAD) 1 && + echo $ZERO_OID $(git --git-dir=bare rev-parse HEAD) 1 && echo $(pwd)/bare/worktrees/goozy && echo $(pwd)/goozy } >hook.expect && diff --git a/t/t2027-worktree-list.sh b/t/t2027-worktree-list.sh index 720063bf0d..bb6fb9b12c 100755 --- a/t/t2027-worktree-list.sh +++ b/t/t2027-worktree-list.sh @@ -116,7 +116,7 @@ test_expect_success 'broken main worktree still at the top' ' git worktree add linked && cat >expected <<-EOF && worktree $(pwd) - HEAD $_z40 + HEAD $ZERO_OID EOF cd linked && diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh index 1db7e6a1ab..2242cd098e 100755 --- a/t/t2107-update-index-basic.sh +++ b/t/t2107-update-index-basic.sh @@ -37,7 +37,7 @@ test_expect_success '--cacheinfo does not accept blob null sha1' ' echo content >file && git add file && git rev-parse :file >expect && - test_must_fail git update-index --cacheinfo 100644 $_z40 file && + test_must_fail git update-index --cacheinfo 100644 $ZERO_OID file && git rev-parse :file >actual && test_cmp expect actual ' @@ -47,7 +47,7 @@ test_expect_success '--cacheinfo does not accept gitlink null sha1' ' (cd submodule && test_commit foo) && git add submodule && git rev-parse :submodule >expect && - test_must_fail git update-index --cacheinfo 160000 $_z40 submodule && + test_must_fail git update-index --cacheinfo 160000 $ZERO_OID submodule && git rev-parse :submodule >actual && test_cmp expect actual ' diff --git a/t/t2201-add-update-typechange.sh b/t/t2201-add-update-typechange.sh index 954fc51e5b..a4eec0a346 100755 --- a/t/t2201-add-update-typechange.sh +++ b/t/t2201-add-update-typechange.sh @@ -75,35 +75,35 @@ test_expect_success modify ' git ls-tree -r HEAD | sed -e "s/^/:/" -e " / caskly/{ - s/ caskly/ $_z40 D&/ + s/ caskly/ $ZERO_OID D&/ s/blob/000000/ } / nitfol/{ - s/ nitfol/ $_z40 $T_letter&/ + s/ nitfol/ $ZERO_OID $T_letter&/ s/blob/100644/ } / rezrov.bozbar/{ - s/ rezrov.bozbar/ $_z40 D&/ + s/ rezrov.bozbar/ $ZERO_OID D&/ s/blob/000000/ } / xyzzy/{ - s/ xyzzy/ $_z40 D&/ + s/ xyzzy/ $ZERO_OID D&/ s/blob/000000/ } / yomin/{ - s/ yomin/ $_z40 T&/ + s/ yomin/ $ZERO_OID T&/ s/blob/160000/ } " } >expect && { cat expect - echo ":100644 160000 $_empty $_z40 T yonk" - echo ":100644 000000 $_empty $_z40 D zifmia" + echo ":100644 160000 $_empty $ZERO_OID T yonk" + echo ":100644 000000 $_empty $ZERO_OID D zifmia" } >expect-files && { cat expect - echo ":000000 160000 $_z40 $_z40 A yonk" + echo ":000000 160000 $ZERO_OID $ZERO_OID A yonk" } >expect-index && { echo "100644 $_empty 0 nitfol" diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh index 78236dc7d8..04d840a544 100755 --- a/t/t2203-add-intent.sh +++ b/t/t2203-add-intent.sh @@ -27,12 +27,12 @@ test_expect_success 'git status' ' test_expect_success 'git status with porcelain v2' ' git status --porcelain=v2 | grep -v "^?" >actual && - nam1=d00491fd7e5bb6fa28c517a0bb32b8b506539d4d && - nam2=ce013625030ba8dba906f756967f9e9ca394464a && + nam1=$(echo 1 | git hash-object --stdin) && + nam2=$(git hash-object elif) && cat >expect <<-EOF && - 1 DA N... 100644 000000 100644 $nam1 $_z40 1.t - 1 A. N... 000000 100644 100644 $_z40 $nam2 elif - 1 .A N... 000000 000000 100644 $_z40 $_z40 file + 1 DA N... 100644 000000 100644 $nam1 $ZERO_OID 1.t + 1 A. N... 000000 100644 100644 $ZERO_OID $nam2 elif + 1 .A N... 000000 000000 100644 $ZERO_OID $ZERO_OID file EOF test_cmp expect actual ' @@ -181,7 +181,7 @@ test_expect_success 'rename detection finds the right names' ' EOF test_cmp expected.2 actual.2 && - hash=12f00e90b6ef79117ce6e650416b8cf517099b78 && + hash=$(git hash-object third) && git status --porcelain=v2 | grep -v "^?" >actual.3 && cat >expected.3 <<-EOF && 2 .R N... 100644 100644 100644 $hash $hash R100 third first @@ -212,7 +212,7 @@ test_expect_success 'double rename detection in status' ' EOF test_cmp expected.2 actual.2 && - hash=12f00e90b6ef79117ce6e650416b8cf517099b78 && + hash=$(git hash-object third) && git status --porcelain=v2 | grep -v "^?" >actual.3 && cat >expected.3 <<-EOF && 2 R. N... 100644 100644 100644 $hash $hash R100 second first diff --git a/t/t3034-merge-recursive-rename-options.sh b/t/t3034-merge-recursive-rename-options.sh index b9c4028496..3d9fae68c4 100755 --- a/t/t3034-merge-recursive-rename-options.sh +++ b/t/t3034-merge-recursive-rename-options.sh @@ -309,4 +309,22 @@ test_expect_success 'last wins in --find-renames=<m> --rename-threshold=<n>' ' check_threshold_0 ' +test_expect_success 'merge.renames disables rename detection' ' + git read-tree --reset -u HEAD && + git -c merge.renames=false merge-recursive $tail && + check_no_renames +' + +test_expect_success 'merge.renames defaults to diff.renames' ' + git read-tree --reset -u HEAD && + git -c diff.renames=false merge-recursive $tail && + check_no_renames +' + +test_expect_success 'merge.renames overrides diff.renames' ' + git read-tree --reset -u HEAD && + test_must_fail git -c diff.renames=false -c merge.renames=true merge-recursive $tail && + $check_50 +' + test_done diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh index 325114f8fe..18baf49a49 100755 --- a/t/t3100-ls-tree-restrict.sh +++ b/t/t3100-ls-tree-restrict.sh @@ -32,7 +32,7 @@ test_expect_success \ echo $tree' test_output () { - sed -e "s/ $_x40 / X /" <current >check + sed -e "s/ $OID_REGEX / X /" <current >check test_cmp expected check } diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh index 327ded4000..12bf31022a 100755 --- a/t/t3101-ls-tree-dirname.sh +++ b/t/t3101-ls-tree-dirname.sh @@ -40,7 +40,7 @@ test_expect_success 'setup' ' ' test_output () { - sed -e "s/ $_x40 / X /" <current >check && + sed -e "s/ $OID_REGEX / X /" <current >check && test_cmp expected check } diff --git a/t/t3103-ls-tree-misc.sh b/t/t3103-ls-tree-misc.sh index 09dcf043fd..14520913af 100755 --- a/t/t3103-ls-tree-misc.sh +++ b/t/t3103-ls-tree-misc.sh @@ -17,7 +17,8 @@ test_expect_success 'setup' ' ' test_expect_success 'ls-tree fails with non-zero exit code on broken tree' ' - rm -f .git/objects/5f/cffbd6e4c5c5b8d81f5e9314b20e338e3ffff5 && + tree=$(git rev-parse HEAD:a) && + rm -f .git/objects/$(echo $tree | sed -e "s,^\(..\),\1/,") && test_must_fail git ls-tree -r HEAD ' diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index c0ef946811..b52c655c37 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -47,7 +47,7 @@ test_expect_success 'git branch HEAD should fail' ' ' cat >expect <<EOF -$_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master +$ZERO_OID $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master EOF test_expect_success 'git branch -l d/e/f should create a branch and a log' ' GIT_COMMITTER_DATE="2005-05-26 23:30" \ @@ -901,7 +901,7 @@ test_expect_success '--set-upstream-to notices an error to set branch as own ups # Keep this test last, as it changes the current branch cat >expect <<EOF -$_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master +$ZERO_OID $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master EOF test_expect_success 'git checkout -b g/h/i -l should create a branch and a log' ' GIT_COMMITTER_DATE="2005-05-26 23:30" \ diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 59c766540e..c65826ddac 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -1204,10 +1204,6 @@ test_expect_success 'drop' ' test A = $(git cat-file commit HEAD^^ | sed -ne \$p) ' -cat >expect <<EOF -Successfully rebased and updated refs/heads/missing-commit. -EOF - test_expect_success 'rebase -i respects rebase.missingCommitsCheck = ignore' ' test_config rebase.missingCommitsCheck ignore && rebase_setup_and_clean missing-commit && @@ -1215,7 +1211,9 @@ test_expect_success 'rebase -i respects rebase.missingCommitsCheck = ignore' ' FAKE_LINES="1 2 3 4" \ git rebase -i --root 2>actual && test D = $(git cat-file commit HEAD | sed -ne \$p) && - test_i18ncmp expect actual + test_i18ngrep \ + "Successfully rebased and updated refs/heads/missing-commit" \ + actual ' cat >expect <<EOF @@ -1227,15 +1225,24 @@ To avoid this message, use "drop" to explicitly remove a commit. Use 'git config rebase.missingCommitsCheck' to change the level of warnings. The possible behaviours are: ignore, warn, error. +Rebasing (1/4) +Rebasing (2/4) +Rebasing (3/4) +Rebasing (4/4) Successfully rebased and updated refs/heads/missing-commit. EOF +cr_to_nl () { + tr '\015' '\012' +} + test_expect_success 'rebase -i respects rebase.missingCommitsCheck = warn' ' test_config rebase.missingCommitsCheck warn && rebase_setup_and_clean missing-commit && set_fake_editor && FAKE_LINES="1 2 3 4" \ - git rebase -i --root 2>actual && + git rebase -i --root 2>actual.2 && + cr_to_nl <actual.2 >actual && test_i18ncmp expect actual && test D = $(git cat-file commit HEAD | sed -ne \$p) ' diff --git a/t/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh index e7438ad06a..99b2aac921 100755 --- a/t/t3421-rebase-topology-linear.sh +++ b/t/t3421-rebase-topology-linear.sh @@ -328,9 +328,9 @@ test_run_rebase () { test_cmp_rev c HEAD " } -test_run_rebase failure '' -test_run_rebase failure -m -test_run_rebase failure -i +test_run_rebase success '' +test_run_rebase success -m +test_run_rebase success -i test_run_rebase failure -p test_run_rebase () { diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh index 3d4dfdf7be..ce6de6f491 100755 --- a/t/t3430-rebase-merges.sh +++ b/t/t3430-rebase-merges.sh @@ -241,4 +241,76 @@ test_expect_success 'refuse to merge ancestors of HEAD' ' test_cmp_rev HEAD $before ' +test_expect_success 'root commits' ' + git checkout --orphan unrelated && + (GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \ + test_commit second-root) && + test_commit third-root && + cat >script-from-scratch <<-\EOF && + pick third-root + label first-branch + reset [new root] + pick second-root + merge first-branch # Merge the 3rd root + EOF + test_config sequence.editor \""$PWD"/replace-editor.sh\" && + test_tick && + git rebase -i --force --root -r && + test "Parsnip" = "$(git show -s --format=%an HEAD^)" && + test $(git rev-parse second-root^0) != $(git rev-parse HEAD^) && + test $(git rev-parse second-root:second-root.t) = \ + $(git rev-parse HEAD^:second-root.t) && + test_cmp_graph HEAD <<-\EOF && + * Merge the 3rd root + |\ + | * third-root + * second-root + EOF + + : fast forward if possible && + before="$(git rev-parse --verify HEAD)" && + test_might_fail git config --unset sequence.editor && + test_tick && + git rebase -i --root -r && + test_cmp_rev HEAD $before +' + +test_expect_success 'a "merge" into a root commit is a fast-forward' ' + head=$(git rev-parse HEAD) && + cat >script-from-scratch <<-EOF && + reset [new root] + merge $head + EOF + test_config sequence.editor \""$PWD"/replace-editor.sh\" && + test_tick && + git rebase -i -r HEAD^ && + test_cmp_rev HEAD $head +' + +test_expect_success 'A root commit can be a cousin, treat it that way' ' + git checkout --orphan khnum && + test_commit yama && + git checkout -b asherah master && + test_commit shamkat && + git merge --allow-unrelated-histories khnum && + test_tick && + git rebase -f -r HEAD^ && + ! test_cmp_rev HEAD^2 khnum && + test_cmp_graph HEAD^.. <<-\EOF && + * Merge branch '\''khnum'\'' into asherah + |\ + | * yama + o shamkat + EOF + test_tick && + git rebase --rebase-merges=rebase-cousins HEAD^ && + test_cmp_graph HEAD^.. <<-\EOF + * Merge branch '\''khnum'\'' into asherah + |\ + | * yama + |/ + o shamkat + EOF +' + test_done diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh index 21b4f194a2..b42cd66d3a 100755 --- a/t/t3510-cherry-pick-sequence.sh +++ b/t/t3510-cherry-pick-sequence.sh @@ -122,7 +122,7 @@ test_expect_success '--quit keeps HEAD and conflicted index intact' ' { git rev-list HEAD | git diff-tree --root --stdin | - sed "s/$_x40/OBJID/g" + sed "s/$OID_REGEX/OBJID/g" } >actual && test_cmp expect actual ' @@ -220,7 +220,7 @@ test_expect_success 'cherry-pick still writes sequencer state when one commit is { git rev-list HEAD | git diff-tree --root --stdin | - sed "s/$_x40/OBJID/g" + sed "s/$OID_REGEX/OBJID/g" } >actual && cat >expect <<-\EOF && OBJID @@ -317,7 +317,7 @@ test_expect_success '--continue after resolving conflicts' ' { git rev-list HEAD | git diff-tree --root --stdin | - sed "s/$_x40/OBJID/g" + sed "s/$OID_REGEX/OBJID/g" } >actual.log && test_cmp expect foo && test_cmp expect.log actual.log @@ -334,7 +334,7 @@ test_expect_success '--continue after resolving conflicts and committing' ' { git rev-list HEAD | git diff-tree --root --stdin | - sed "s/$_x40/OBJID/g" + sed "s/$OID_REGEX/OBJID/g" } >actual && cat >expect <<-\EOF && OBJID diff --git a/t/t3702-add-edit.sh b/t/t3702-add-edit.sh index 3cb74ca296..c6af7f82b5 100755 --- a/t/t3702-add-edit.sh +++ b/t/t3702-add-edit.sh @@ -40,7 +40,6 @@ test_expect_success 'setup' ' cat > expected-patch << EOF diff --git a/file b/file -index b9834b5..9020acb 100644 --- a/file +++ b/file @@ -1,11 +1,6 @@ @@ -80,7 +79,6 @@ EOF cat > expected << EOF diff --git a/file b/file -index b9834b5..ef6e94c 100644 --- a/file +++ b/file @@ -1,10 +1,12 @@ @@ -100,7 +98,7 @@ EOF echo "#!$SHELL_PATH" >fake-editor.sh cat >> fake-editor.sh <<\EOF -mv -f "$1" orig-patch && +egrep -v '^index' "$1" >orig-patch && mv -f patch "$1" EOF @@ -113,7 +111,8 @@ test_expect_success 'add -e' ' git add -e && test_cmp second-part file && test_cmp orig-patch expected-patch && - git diff --cached > out && + git diff --cached >actual && + grep -v index actual >out && test_cmp out expected ' diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh index 3ea5b9bb3f..597b0637d1 100755 --- a/t/t3905-stash-include-untracked.sh +++ b/t/t3905-stash-include-untracked.sh @@ -35,24 +35,26 @@ test_expect_success 'stash save --include-untracked cleaned the untracked files' test_cmp expect actual ' +tracked=$(git rev-parse --short $(echo 1 | git hash-object --stdin)) +untracked=$(git rev-parse --short $(echo untracked | git hash-object --stdin)) cat > expect.diff <<EOF diff --git a/HEAD b/HEAD new file mode 100644 -index 0000000..d00491f +index 0000000..$tracked --- /dev/null +++ b/HEAD @@ -0,0 +1 @@ +1 diff --git a/file2 b/file2 new file mode 100644 -index 0000000..d00491f +index 0000000..$tracked --- /dev/null +++ b/file2 @@ -0,0 +1 @@ +1 diff --git a/untracked/untracked b/untracked/untracked new file mode 100644 -index 0000000..5a72eb2 +index 0000000..$untracked --- /dev/null +++ b/untracked/untracked @@ -0,0 +1 @@ @@ -109,10 +111,11 @@ test_expect_success 'stash save -u dirty index' ' git stash -u ' +blob=$(git rev-parse --short $(echo 4 | git hash-object --stdin)) cat > expect <<EOF diff --git a/file3 b/file3 new file mode 100644 -index 0000000..b8626c4 +index 0000000..$blob --- /dev/null +++ b/file3 @@ -0,0 +1 @@ diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh index a5e8b83083..3a6c21e825 100755 --- a/t/t4002-diff-basic.sh +++ b/t/t4002-diff-basic.sh @@ -131,7 +131,7 @@ cmp_diff_files_output () { # object ID for the changed files because it wants you to look at the # filesystem. sed <"$2" >.test-tmp \ - -e '/^:000000 /d;s/'$_x40'\( [MCRNDU][0-9]*\) /'$_z40'\1 /' && + -e '/^:000000 /d;s/'$OID_REGEX'\( [MCRNDU][0-9]*\) /'$ZERO_OID'\1 /' && test_cmp "$1" .test-tmp } diff --git a/t/t4006-diff-mode.sh b/t/t4006-diff-mode.sh index 76f643b2c2..a8e01eccd1 100755 --- a/t/t4006-diff-mode.sh +++ b/t/t4006-diff-mode.sh @@ -8,7 +8,7 @@ test_description='Test mode change diffs. ' . ./test-lib.sh -sed_script='s/\(:100644 100755\) \('"$_x40"'\) \2 /\1 X X /' +sed_script='s/\(:100644 100755\) \('"$OID_REGEX"'\) \2 /\1 X X /' test_expect_success 'setup' ' echo frotz >rezrov && diff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh index dae327fabb..b187b7f6c6 100755 --- a/t/t4007-rename-3.sh +++ b/t/t4007-rename-3.sh @@ -17,6 +17,7 @@ test_expect_success 'prepare reference tree' ' echo $tree ' +blob=$(git hash-object "$TEST_DIRECTORY/diff-lib/COPYING") test_expect_success 'prepare work tree' ' cp path0/COPYING path1/COPYING && git update-index --add --remove path0/COPYING path1/COPYING @@ -26,8 +27,8 @@ test_expect_success 'prepare work tree' ' # path1 both have COPYING and the latter is a copy of path0/COPYING. # Comparing the full tree with cache should tell us so. -cat >expected <<\EOF -:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100 path0/COPYING path1/COPYING +cat >expected <<EOF +:100644 100644 $blob $blob C100 path0/COPYING path1/COPYING EOF test_expect_success 'copy detection' ' @@ -46,8 +47,8 @@ test_expect_success 'copy detection, cached' ' # path1/COPYING suddenly appearing from nowhere, not detected as # a copy from path0/COPYING. -cat >expected <<\EOF -:000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A path1/COPYING +cat >expected <<EOF +:000000 100644 $ZERO_OID $blob A path1/COPYING EOF test_expect_success 'copy, limited to a subtree' ' @@ -64,8 +65,8 @@ test_expect_success 'tweak work tree' ' # path0/COPYING. Showing the full tree with cache should tell us about # the rename. -cat >expected <<\EOF -:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100 path0/COPYING path1/COPYING +cat >expected <<EOF +:100644 100644 $blob $blob R100 path0/COPYING path1/COPYING EOF test_expect_success 'rename detection' ' @@ -78,8 +79,8 @@ test_expect_success 'rename detection' ' # path0/COPYING. When we say we care only about path1, we should just # see path1/COPYING appearing from nowhere. -cat >expected <<\EOF -:000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A path1/COPYING +cat >expected <<EOF +:000000 100644 $ZERO_OID $blob A path1/COPYING EOF test_expect_success 'rename, limited to a subtree' ' diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh index 9dd1bc5e16..b1ccd4102e 100755 --- a/t/t4008-diff-break-rewrite.sh +++ b/t/t4008-diff-break-rewrite.sh @@ -27,29 +27,32 @@ Further, with -B and -M together, these should turn into two renames. test_expect_success setup ' cat "$TEST_DIRECTORY"/diff-lib/README >file0 && cat "$TEST_DIRECTORY"/diff-lib/COPYING >file1 && + blob0_id=$(git hash-object file0) && + blob1_id=$(git hash-object file1) && git update-index --add file0 file1 && git tag reference $(git write-tree) ' test_expect_success 'change file1 with copy-edit of file0 and remove file0' ' sed -e "s/git/GIT/" file0 >file1 && + blob2_id=$(git hash-object file1) && rm -f file0 && git update-index --remove file0 file1 ' test_expect_success 'run diff with -B (#1)' ' git diff-index -B --cached reference >current && - cat >expect <<-\EOF && - :100644 000000 548142c327a6790ff8821d67c2ee1eff7a656b52 0000000000000000000000000000000000000000 D file0 - :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 2fbedd0b5d4b8126e4750c3bee305e8ff79f80ec M100 file1 + cat >expect <<-EOF && + :100644 000000 $blob0_id $ZERO_OID D file0 + :100644 100644 $blob1_id $blob2_id M100 file1 EOF compare_diff_raw expect current ' test_expect_success 'run diff with -B and -M (#2)' ' git diff-index -B -M reference >current && - cat >expect <<-\EOF && - :100644 100644 548142c327a6790ff8821d67c2ee1eff7a656b52 2fbedd0b5d4b8126e4750c3bee305e8ff79f80ec R100 file0 file1 + cat >expect <<-EOF && + :100644 100644 $blob0_id $blob2_id R100 file0 file1 EOF compare_diff_raw expect current ' @@ -66,18 +69,18 @@ test_expect_success 'swap file0 and file1' ' test_expect_success 'run diff with -B (#3)' ' git diff-index -B reference >current && - cat >expect <<-\EOF && - :100644 100644 548142c327a6790ff8821d67c2ee1eff7a656b52 6ff87c4664981e4397625791c8ea3bbb5f2279a3 M100 file0 - :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 548142c327a6790ff8821d67c2ee1eff7a656b52 M100 file1 + cat >expect <<-EOF && + :100644 100644 $blob0_id $blob1_id M100 file0 + :100644 100644 $blob1_id $blob0_id M100 file1 EOF compare_diff_raw expect current ' test_expect_success 'run diff with -B and -M (#4)' ' git diff-index -B -M reference >current && - cat >expect <<-\EOF && - :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100 file1 file0 - :100644 100644 548142c327a6790ff8821d67c2ee1eff7a656b52 548142c327a6790ff8821d67c2ee1eff7a656b52 R100 file0 file1 + cat >expect <<-EOF && + :100644 100644 $blob1_id $blob1_id R100 file1 file0 + :100644 100644 $blob0_id $blob0_id R100 file0 file1 EOF compare_diff_raw expect current ' @@ -85,14 +88,15 @@ test_expect_success 'run diff with -B and -M (#4)' ' test_expect_success 'make file0 into something completely different' ' rm -f file0 && test_ln_s_add frotz file0 && + slink_id=$(printf frotz | git hash-object --stdin) && git update-index file1 ' test_expect_success 'run diff with -B (#5)' ' git diff-index -B reference >current && - cat >expect <<-\EOF && - :100644 120000 548142c327a6790ff8821d67c2ee1eff7a656b52 67be421f88824578857624f7b3dc75e99a8a1481 T file0 - :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 548142c327a6790ff8821d67c2ee1eff7a656b52 M100 file1 + cat >expect <<-EOF && + :100644 120000 $blob0_id $slink_id T file0 + :100644 100644 $blob1_id $blob0_id M100 file1 EOF compare_diff_raw expect current ' @@ -103,9 +107,9 @@ test_expect_success 'run diff with -B -M (#6)' ' # file0 changed from regular to symlink. file1 is the same as the preimage # of file0. Because the change does not make file0 disappear, file1 is # denoted as a copy of file0 - cat >expect <<-\EOF && - :100644 120000 548142c327a6790ff8821d67c2ee1eff7a656b52 67be421f88824578857624f7b3dc75e99a8a1481 T file0 - :100644 100644 548142c327a6790ff8821d67c2ee1eff7a656b52 548142c327a6790ff8821d67c2ee1eff7a656b52 C file0 file1 + cat >expect <<-EOF && + :100644 120000 $blob0_id $slink_id T file0 + :100644 100644 $blob0_id $blob0_id C file0 file1 EOF compare_diff_raw expect current ' @@ -115,9 +119,9 @@ test_expect_success 'run diff with -M (#7)' ' # This should not mistake file0 as the copy source of new file1 # due to type differences. - cat >expect <<-\EOF && - :100644 120000 548142c327a6790ff8821d67c2ee1eff7a656b52 67be421f88824578857624f7b3dc75e99a8a1481 T file0 - :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 548142c327a6790ff8821d67c2ee1eff7a656b52 M file1 + cat >expect <<-EOF && + :100644 120000 $blob0_id $slink_id T file0 + :100644 100644 $blob1_id $blob0_id M file1 EOF compare_diff_raw expect current ' @@ -128,25 +132,26 @@ test_expect_success 'file1 edited to look like file0 and file0 rename-edited to git checkout-index -f -u -a && sed -e "s/git/GIT/" file0 >file1 && sed -e "s/git/GET/" file0 >file2 && + blob3_id=$(git hash-object file2) && rm -f file0 && git update-index --add --remove file0 file1 file2 ' test_expect_success 'run diff with -B (#8)' ' git diff-index -B reference >current && - cat >expect <<-\EOF && - :100644 000000 548142c327a6790ff8821d67c2ee1eff7a656b52 0000000000000000000000000000000000000000 D file0 - :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 2fbedd0b5d4b8126e4750c3bee305e8ff79f80ec M100 file1 - :000000 100644 0000000000000000000000000000000000000000 69a939f651686f56322566e2fd76715947a24162 A file2 + cat >expect <<-EOF && + :100644 000000 $blob0_id $ZERO_OID D file0 + :100644 100644 $blob1_id $blob2_id M100 file1 + :000000 100644 $ZERO_OID $blob3_id A file2 EOF compare_diff_raw expect current ' test_expect_success 'run diff with -B -C (#9)' ' git diff-index -B -C reference >current && - cat >expect <<-\EOF && - :100644 100644 548142c327a6790ff8821d67c2ee1eff7a656b52 2fbedd0b5d4b8126e4750c3bee305e8ff79f80ec C095 file0 file1 - :100644 100644 548142c327a6790ff8821d67c2ee1eff7a656b52 69a939f651686f56322566e2fd76715947a24162 R095 file0 file2 + cat >expect <<-EOF && + :100644 100644 $blob0_id $blob2_id C095 file0 file1 + :100644 100644 $blob0_id $blob3_id R095 file0 file2 EOF compare_diff_raw expect current ' diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 6ea08fd5e9..028d5507a6 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -578,7 +578,11 @@ test_expect_success 'excessive subject' ' rm -rf patches/ && git checkout side && + before=$(git hash-object file) && + before=$(git rev-parse --short $before) && for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >>file && + after=$(git hash-object file) && + after=$(git rev-parse --short $after) && git update-index file && git commit -m "This is an excessively long subject line for a message due to the habit some projects have of not having a short, one-line subject at the start of the commit message, but rather sticking a whole paragraph right at the start as the only thing in the commit message. It had better not become the filename for the patch." && git format-patch -o patches/ master..side && @@ -586,7 +590,6 @@ test_expect_success 'excessive subject' ' ' test_expect_success 'cover-letter inherits diff options' ' - git mv file foo && git commit -m foo && git format-patch --no-renames --cover-letter -1 && @@ -616,7 +619,7 @@ test_expect_success 'shortlog of cover-letter wraps overly-long onelines' ' ' cat > expect << EOF -index 40f36c6..2dc5c23 100644 +index $before..$after 100644 --- a/file +++ b/file @@ -13,4 +13,20 @@ C @@ -640,7 +643,7 @@ test_expect_success 'format-patch respects -U' ' cat > expect << EOF diff --git a/file b/file -index 40f36c6..2dc5c23 100644 +index $before..$after 100644 --- a/file +++ b/file @@ -14,3 +14,19 @@ C @@ -1523,14 +1526,14 @@ test_expect_success 'cover letter auto user override' ' test_expect_success 'format-patch --zero-commit' ' git format-patch --zero-commit --stdout v2..v1 >patch2 && grep "^From " patch2 | sort | uniq >actual && - echo "From $_z40 Mon Sep 17 00:00:00 2001" >expect && + echo "From $ZERO_OID Mon Sep 17 00:00:00 2001" >expect && test_cmp expect actual ' test_expect_success 'From line has expected format' ' git format-patch --stdout v2..v1 >patch2 && grep "^From " patch2 >from && - grep "^From $_x40 Mon Sep 17 00:00:00 2001$" patch2 >filtered && + grep "^From $OID_REGEX Mon Sep 17 00:00:00 2001$" patch2 >filtered && test_cmp from filtered ' diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh index 044620186d..e009826fcb 100755 --- a/t/t4020-diff-external.sh +++ b/t/t4020-diff-external.sh @@ -13,6 +13,8 @@ test_expect_success setup ' test_tick && echo second >file && + before=$(git hash-object file) && + before=$(git rev-parse --short $before) && git add file && git commit -m second && @@ -26,7 +28,7 @@ test_expect_success 'GIT_EXTERNAL_DIFF environment' ' read path oldfile oldhex oldmode newfile newhex newmode && test "z$path" = zfile && test "z$oldmode" = z100644 && - test "z$newhex" = "z$_z40" && + test "z$newhex" = "z$ZERO_OID" && test "z$newmode" = z100644 && oh=$(git rev-parse --verify HEAD:file) && test "z$oh" = "z$oldhex" @@ -55,7 +57,7 @@ test_expect_success SYMLINKS 'typechange diff' ' read path oldfile oldhex oldmode newfile newhex newmode && test "z$path" = zfile && test "z$oldmode" = z100644 && - test "z$newhex" = "z$_z40" && + test "z$newhex" = "z$ZERO_OID" && test "z$newmode" = z120000 && oh=$(git rev-parse --verify HEAD:file) && test "z$oh" = "z$oldhex" @@ -73,7 +75,7 @@ test_expect_success 'diff.external' ' read path oldfile oldhex oldmode newfile newhex newmode && test "z$path" = zfile && test "z$oldmode" = z100644 && - test "z$newhex" = "z$_z40" && + test "z$newhex" = "z$ZERO_OID" && test "z$newmode" = z100644 && oh=$(git rev-parse --verify HEAD:file) && test "z$oh" = "z$oldhex" @@ -104,7 +106,7 @@ test_expect_success 'diff attribute' ' read path oldfile oldhex oldmode newfile newhex newmode && test "z$path" = zfile && test "z$oldmode" = z100644 && - test "z$newhex" = "z$_z40" && + test "z$newhex" = "z$ZERO_OID" && test "z$newmode" = z100644 && oh=$(git rev-parse --verify HEAD:file) && test "z$oh" = "z$oldhex" @@ -137,7 +139,7 @@ test_expect_success 'diff attribute' ' read path oldfile oldhex oldmode newfile newhex newmode && test "z$path" = zfile && test "z$oldmode" = z100644 && - test "z$newhex" = "z$_z40" && + test "z$newhex" = "z$ZERO_OID" && test "z$newmode" = z100644 && oh=$(git rev-parse --verify HEAD:file) && test "z$oh" = "z$oldhex" @@ -180,9 +182,13 @@ test_expect_success 'no diff with -diff' ' echo NULZbetweenZwords | perl -pe 'y/Z/\000/' > file test_expect_success 'force diff with "diff"' ' + after=$(git hash-object file) && + after=$(git rev-parse --short $after) && echo >.gitattributes "file diff" && git diff >actual && - test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual + sed -e "s/^index .*/index $before..$after 100644/" \ + "$TEST_DIRECTORY"/t4020/diff.NUL >expected-diff && + test_cmp expected-diff actual ' test_expect_success 'GIT_EXTERNAL_DIFF with more than one changed files' ' @@ -237,7 +243,7 @@ test_expect_success 'diff --cached' ' git update-index --assume-unchanged file && echo second >file && git diff --cached >actual && - test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual + test_cmp expected-diff actual ' test_expect_success 'clean up crlf leftovers' ' diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh index cb51d9f9d4..6d1c3d949c 100755 --- a/t/t4022-diff-rewrite.sh +++ b/t/t4022-diff-rewrite.sh @@ -13,6 +13,8 @@ test_expect_success setup ' "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM" \ <"$TEST_DIRECTORY"/../COPYING >test && echo "to be deleted" >test2 && + blob=$(git hash-object test2) && + blob=$(git rev-parse --short $blob) && git add test2 ' @@ -27,7 +29,7 @@ test_expect_success 'detect rewrite' ' cat >expect <<EOF diff --git a/test2 b/test2 deleted file mode 100644 -index 4202011..0000000 +index $blob..0000000 --- a/test2 +++ /dev/null @@ -1 +0,0 @@ @@ -43,7 +45,7 @@ test_expect_success 'show deletion diff without -D' ' cat >expect <<EOF diff --git a/test2 b/test2 deleted file mode 100644 -index 4202011..0000000 +index $blob..0000000 EOF test_expect_success 'suppress deletion diff with -D' ' diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh index 2ffd11a142..6304130ad4 100755 --- a/t/t4027-diff-submodule.sh +++ b/t/t4027-diff-submodule.sh @@ -31,7 +31,7 @@ test_expect_success setup ' cd sub && git rev-list HEAD ) && - echo ":160000 160000 $3 $_z40 M sub" >expect && + echo ":160000 160000 $3 $ZERO_OID M sub" >expect && subtip=$3 subprev=$2 ' @@ -250,7 +250,7 @@ test_expect_success 'conflicted submodule setup' ' # 39 efs c=fffffffffffffffffffffffffffffffffffffff && ( - echo "000000 $_z40 0 sub" && + echo "000000 $ZERO_OID 0 sub" && echo "160000 1$c 1 sub" && echo "160000 2$c 2 sub" && echo "160000 3$c 3 sub" @@ -265,7 +265,7 @@ index 2ffffff,3ffffff..0000000 ++Subproject commit 0000000000000000000000000000000000000000'\'' && hh=$(git rev-parse HEAD) && - sed -e "s/$_z40/$hh/" expect.nosub >expect.withsub + sed -e "s/$ZERO_OID/$hh/" expect.nosub >expect.withsub ' diff --git a/t/t4029-diff-trailing-space.sh b/t/t4029-diff-trailing-space.sh index 3ccc237a8d..32b6e9a4e7 100755 --- a/t/t4029-diff-trailing-space.sh +++ b/t/t4029-diff-trailing-space.sh @@ -6,7 +6,7 @@ test_description='diff honors config option, diff.suppressBlankEmpty' . ./test-lib.sh -cat <<\EOF > exp || +cat <<\EOF >expected || diff --git a/f b/f index 5f6a263..8cb8bae 100644 --- a/f @@ -18,22 +18,26 @@ index 5f6a263..8cb8bae 100644 EOF exit 1 -test_expect_success \ - "$test_description" \ - 'printf "\nx\n" > f && - git add f && - git commit -q -m. f && - printf "\ny\n" > f && - git config --bool diff.suppressBlankEmpty true && - git diff f > actual && - test_cmp exp actual && - perl -i.bak -p -e "s/^\$/ /" exp && - git config --bool diff.suppressBlankEmpty false && - git diff f > actual && - test_cmp exp actual && - git config --bool --unset diff.suppressBlankEmpty && - git diff f > actual && - test_cmp exp actual - ' +test_expect_success "$test_description" ' + printf "\nx\n" > f && + before=$(git hash-object f) && + before=$(git rev-parse --short $before) && + git add f && + git commit -q -m. f && + printf "\ny\n" > f && + after=$(git hash-object f) && + after=$(git rev-parse --short $after) && + sed -e "s/^index .*/index $before..$after 100644/" expected >exp && + git config --bool diff.suppressBlankEmpty true && + git diff f > actual && + test_cmp exp actual && + perl -i.bak -p -e "s/^\$/ /" exp && + git config --bool diff.suppressBlankEmpty false && + git diff f > actual && + test_cmp exp actual && + git config --bool --unset diff.suppressBlankEmpty && + git diff f > actual && + test_cmp exp actual +' test_done diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh index aad6c7f78d..4cb9f0e523 100755 --- a/t/t4030-diff-textconv.sh +++ b/t/t4030-diff-textconv.sh @@ -148,7 +148,8 @@ test_expect_success 'diffstat does not run textconv' ' # restore working setup echo file diff=foo >.gitattributes -cat >expect.typechange <<'EOF' +symlink=$(git rev-parse --short $(printf frotz | git hash-object --stdin)) +cat >expect.typechange <<EOF --- a/file +++ /dev/null @@ -1,2 +0,0 @@ @@ -156,7 +157,7 @@ cat >expect.typechange <<'EOF' -1 diff --git a/file b/file new file mode 120000 -index 0000000..67be421 +index 0000000..$symlink --- /dev/null +++ b/file @@ -0,0 +1 @@ diff --git a/t/t4042-diff-textconv-caching.sh b/t/t4042-diff-textconv-caching.sh index 04a44d5c61..bf33aedf4b 100755 --- a/t/t4042-diff-textconv-caching.sh +++ b/t/t4042-diff-textconv-caching.sh @@ -15,9 +15,13 @@ test_expect_success 'setup' ' echo bar content 1 >bar.bin && git add . && git commit -m one && + foo1=$(git rev-parse --short HEAD:foo.bin) && + bar1=$(git rev-parse --short HEAD:bar.bin) && echo foo content 2 >foo.bin && echo bar content 2 >bar.bin && git commit -a -m two && + foo2=$(git rev-parse --short HEAD:foo.bin) && + bar2=$(git rev-parse --short HEAD:bar.bin) && echo "*.bin diff=magic" >.gitattributes && git config diff.magic.textconv ./helper && git config diff.magic.cachetextconv true @@ -25,14 +29,14 @@ test_expect_success 'setup' ' cat >expect <<EOF diff --git a/bar.bin b/bar.bin -index fcf9166..28283d5 100644 +index $bar1..$bar2 100644 --- a/bar.bin +++ b/bar.bin @@ -1 +1 @@ -converted: bar content 1 +converted: bar content 2 diff --git a/foo.bin b/foo.bin -index d5b9fe3..1345db2 100644 +index $foo1..$foo2 100644 --- a/foo.bin +++ b/foo.bin @@ -1 +1 @@ @@ -59,7 +63,7 @@ test_expect_success 'cached textconv does not run helper' ' cat >expect <<EOF diff --git a/bar.bin b/bar.bin -index fcf9166..28283d5 100644 +index $bar1..$bar2 100644 --- a/bar.bin +++ b/bar.bin @@ -1,2 +1,2 @@ @@ -67,7 +71,7 @@ index fcf9166..28283d5 100644 -converted: bar content 1 +converted: bar content 2 diff --git a/foo.bin b/foo.bin -index d5b9fe3..1345db2 100644 +index $foo1..$foo2 100644 --- a/foo.bin +++ b/foo.bin @@ -1,2 +1,2 @@ @@ -84,7 +88,7 @@ test_expect_success 'changing textconv invalidates cache' ' cat >expect <<EOF diff --git a/bar.bin b/bar.bin -index fcf9166..28283d5 100644 +index $bar1..$bar2 100644 --- a/bar.bin +++ b/bar.bin @@ -1,2 +1,2 @@ @@ -92,7 +96,7 @@ index fcf9166..28283d5 100644 -converted: bar content 1 +converted: bar content 2 diff --git a/foo.bin b/foo.bin -index d5b9fe3..1345db2 100644 +index $foo1..$foo2 100644 --- a/foo.bin +++ b/foo.bin @@ -1 +1 @@ diff --git a/t/t4044-diff-index-unique-abbrev.sh b/t/t4044-diff-index-unique-abbrev.sh index d5ce72be63..647905e01f 100755 --- a/t/t4044-diff-index-unique-abbrev.sh +++ b/t/t4044-diff-index-unique-abbrev.sh @@ -3,6 +3,12 @@ test_description='test unique sha1 abbreviation on "index from..to" line' . ./test-lib.sh +if ! test_have_prereq SHA1 +then + skip_all='not using SHA-1 for objects' + test_done +fi + cat >expect_initial <<EOF 100644 blob 51d2738463ea4ca66f8691c91e33ce64b7d41bb1 foo EOF diff --git a/t/t4045-diff-relative.sh b/t/t4045-diff-relative.sh index 6471a68701..36f8ed8a81 100755 --- a/t/t4045-diff-relative.sh +++ b/t/t4045-diff-relative.sh @@ -8,6 +8,7 @@ test_expect_success 'setup' ' echo content >file1 && mkdir subdir && echo other content >subdir/file2 && + blob=$(git hash-object subdir/file2) && git add . && git commit -m one ' @@ -17,10 +18,11 @@ check_diff () { shift expect=$1 shift + short_blob=$(git rev-parse --short $blob) cat >expected <<-EOF diff --git a/$expect b/$expect new file mode 100644 - index 0000000..25c05ef + index 0000000..$short_blob --- /dev/null +++ b/$expect @@ -0,0 +1 @@ @@ -68,7 +70,7 @@ check_raw () { expect=$1 shift cat >expected <<-EOF - :000000 100644 0000000000000000000000000000000000000000 25c05ef3639d2d270e7fe765a67668f098092bc5 A $expect + :000000 100644 0000000000000000000000000000000000000000 $blob A $expect EOF test_expect_success "--raw $*" " git -C '$dir' diff --no-abbrev --raw $* HEAD^ >actual && diff --git a/t/t4046-diff-unmerged.sh b/t/t4046-diff-unmerged.sh index d0f14475ca..ff7cfd884a 100755 --- a/t/t4046-diff-unmerged.sh +++ b/t/t4046-diff-unmerged.sh @@ -37,7 +37,7 @@ test_expect_success 'diff-files -0' ' for path in $paths do >"$path" && - echo ":000000 100644 $_z40 $_z40 U $path" + echo ":000000 100644 $ZERO_OID $ZERO_OID U $path" done >diff-files-0.expect && git diff-files -0 >diff-files-0.actual && test_cmp diff-files-0.expect diff-files-0.actual @@ -47,9 +47,9 @@ test_expect_success 'diff-files -1' ' for path in $paths do >"$path" && - echo ":000000 100644 $_z40 $_z40 U $path" && + echo ":000000 100644 $ZERO_OID $ZERO_OID U $path" && case "$path" in - x??) echo ":100644 100644 $blob1 $_z40 M $path" + x??) echo ":100644 100644 $blob1 $ZERO_OID M $path" esac done >diff-files-1.expect && git diff-files -1 >diff-files-1.actual && @@ -60,9 +60,9 @@ test_expect_success 'diff-files -2' ' for path in $paths do >"$path" && - echo ":000000 100644 $_z40 $_z40 U $path" && + echo ":000000 100644 $ZERO_OID $ZERO_OID U $path" && case "$path" in - ?x?) echo ":100644 100644 $blob2 $_z40 M $path" + ?x?) echo ":100644 100644 $blob2 $ZERO_OID M $path" esac done >diff-files-2.expect && git diff-files -2 >diff-files-2.actual && @@ -75,9 +75,9 @@ test_expect_success 'diff-files -3' ' for path in $paths do >"$path" && - echo ":000000 100644 $_z40 $_z40 U $path" && + echo ":000000 100644 $ZERO_OID $ZERO_OID U $path" && case "$path" in - ??x) echo ":100644 100644 $blob3 $_z40 M $path" + ??x) echo ":100644 100644 $blob3 $ZERO_OID M $path" esac done >diff-files-3.expect && git diff-files -3 >diff-files-3.actual && diff --git a/t/t4054-diff-bogus-tree.sh b/t/t4054-diff-bogus-tree.sh index 18f42c5fff..fcae82fffa 100755 --- a/t/t4054-diff-bogus-tree.sh +++ b/t/t4054-diff-bogus-tree.sh @@ -19,37 +19,37 @@ test_expect_success 'create tree with matching file' ' ' test_expect_success 'raw diff shows null sha1 (addition)' ' - echo ":000000 100644 $_z40 $_z40 A foo" >expect && + echo ":000000 100644 $ZERO_OID $ZERO_OID A foo" >expect && git diff-tree $EMPTY_TREE $bogus_tree >actual && test_cmp expect actual ' test_expect_success 'raw diff shows null sha1 (removal)' ' - echo ":100644 000000 $_z40 $_z40 D foo" >expect && + echo ":100644 000000 $ZERO_OID $ZERO_OID D foo" >expect && git diff-tree $bogus_tree $EMPTY_TREE >actual && test_cmp expect actual ' test_expect_success 'raw diff shows null sha1 (modification)' ' - echo ":100644 100644 $blob $_z40 M foo" >expect && + echo ":100644 100644 $blob $ZERO_OID M foo" >expect && git diff-tree $good_tree $bogus_tree >actual && test_cmp expect actual ' test_expect_success 'raw diff shows null sha1 (other direction)' ' - echo ":100644 100644 $_z40 $blob M foo" >expect && + echo ":100644 100644 $ZERO_OID $blob M foo" >expect && git diff-tree $bogus_tree $good_tree >actual && test_cmp expect actual ' test_expect_success 'raw diff shows null sha1 (reverse)' ' - echo ":100644 100644 $_z40 $blob M foo" >expect && + echo ":100644 100644 $ZERO_OID $blob M foo" >expect && git diff-tree -R $good_tree $bogus_tree >actual && test_cmp expect actual ' test_expect_success 'raw diff shows null sha1 (index)' ' - echo ":100644 100644 $_z40 $blob M foo" >expect && + echo ":100644 100644 $ZERO_OID $blob M foo" >expect && git diff-index $bogus_tree >actual && test_cmp expect actual ' diff --git a/t/t4058-diff-duplicates.sh b/t/t4058-diff-duplicates.sh index 0a23242cb6..c24ee175ef 100755 --- a/t/t4058-diff-duplicates.sh +++ b/t/t4058-diff-duplicates.sh @@ -59,12 +59,12 @@ test_expect_success 'create trees with duplicate entries' ' test_expect_success 'diff-tree between trees' ' { - printf ":000000 100644 $_z40 $blob_two A\touter/inner\n" && - printf ":000000 100644 $_z40 $blob_two A\touter/inner\n" && - printf ":000000 100644 $_z40 $blob_two A\touter/inner\n" && - printf ":100644 000000 $blob_two $_z40 D\touter/inner\n" && - printf ":100644 000000 $blob_two $_z40 D\touter/inner\n" && - printf ":100644 000000 $blob_two $_z40 D\touter/inner\n" + printf ":000000 100644 $ZERO_OID $blob_two A\touter/inner\n" && + printf ":000000 100644 $ZERO_OID $blob_two A\touter/inner\n" && + printf ":000000 100644 $ZERO_OID $blob_two A\touter/inner\n" && + printf ":100644 000000 $blob_two $ZERO_OID D\touter/inner\n" && + printf ":100644 000000 $blob_two $ZERO_OID D\touter/inner\n" && + printf ":100644 000000 $blob_two $ZERO_OID D\touter/inner\n" } >expect && git diff-tree -r --no-abbrev one two >actual && test_cmp expect actual diff --git a/t/t4150-am.sh b/t/t4150-am.sh index 1eccfb71d0..1ebc587f8f 100755 --- a/t/t4150-am.sh +++ b/t/t4150-am.sh @@ -140,8 +140,8 @@ test_expect_success setup ' echo "# User $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>" && echo "# Date $test_tick 25200" && echo "# $(git show --pretty="%aD" -s second)" && - echo "# Node ID $_z40" && - echo "# Parent $_z40" && + echo "# Node ID $ZERO_OID" && + echo "# Parent $ZERO_OID" && cat msg && echo && git diff-tree --no-commit-id -p second diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh index eaf18c81cb..8417e5a4b1 100755 --- a/t/t4200-rerere.sh +++ b/t/t4200-rerere.sh @@ -243,7 +243,7 @@ rerere_gc_custom_expiry_test () { five_days="$1" right_now="$2" test_expect_success "rerere gc with custom expiry ($five_days, $right_now)" ' rm -fr .git/rr-cache && - rr=.git/rr-cache/$_z40 && + rr=.git/rr-cache/$ZERO_OID && mkdir -p "$rr" && >"$rr/preimage" && >"$rr/postimage" && diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh index ff6649ed9a..58c2773676 100755 --- a/t/t4201-shortlog.sh +++ b/t/t4201-shortlog.sh @@ -59,7 +59,7 @@ test_expect_success 'setup' ' fuzz() { file=$1 && sed " - s/$_x40/OBJECT_NAME/g + s/$OID_REGEX/OBJECT_NAME/g s/$_x35/OBJID/g s/^ \{6\}[CTa].*/ SUBJECT/g s/^ \{8\}[^ ].*/ CONTINUATION/g diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh index 591f35daaf..2052cadb11 100755 --- a/t/t4205-log-pretty-formats.sh +++ b/t/t4205-log-pretty-formats.sh @@ -516,22 +516,22 @@ test_expect_success 'log decoration properly follows tag chain' ' git commit --amend -m shorter && git log --no-walk --tags --pretty="%H %d" --decorate=full >actual && cat <<-EOF >expected && - $head1 (tag: refs/tags/tag2) $head2 (tag: refs/tags/message-one) $old_head1 (tag: refs/tags/message-two) + $head1 (tag: refs/tags/tag2) EOF - sort actual >actual1 && + sort -k3 actual >actual1 && test_cmp expected actual1 ' test_expect_success 'clean log decoration' ' git log --no-walk --tags --pretty="%H %D" --decorate=full >actual && cat >expected <<-EOF && - $head1 tag: refs/tags/tag2 $head2 tag: refs/tags/message-one $old_head1 tag: refs/tags/message-two + $head1 tag: refs/tags/tag2 EOF - sort actual >actual1 && + sort -k3 actual >actual1 && test_cmp expected actual1 ' diff --git a/t/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh index a1705f70cf..62f335b2d9 100755 --- a/t/t4208-log-magic-pathspec.sh +++ b/t/t4208-log-magic-pathspec.sh @@ -45,8 +45,9 @@ test_expect_success 'git log -- :' ' ' test_expect_success 'git log HEAD -- :/' ' + initial=$(git rev-parse --short HEAD^) && cat >expected <<-EOF && - 24b24cf initial + $initial initial EOF (cd sub && git log --oneline HEAD -- :/ >../actual) && test_cmp expected actual diff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh index 08c210f035..fca001eb9b 100755 --- a/t/t5150-request-pull.sh +++ b/t/t5150-request-pull.sh @@ -81,7 +81,7 @@ test_expect_success 'setup: two scripts for reading pull requests' ' cat <<-EOT >fuzz.sed #!/bin/sed -nf s/$downstream_url_for_sed/URL/g - s/$_x40/OBJECT_NAME/g + s/$OID_REGEX/OBJECT_NAME/g s/A U Thor/AUTHOR/g s/[-0-9]\{10\} [:0-9]\{8\} [-+][0-9]\{4\}/DATE/g s/ [^ ].*/ SUBJECT/g diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index 54eff03851..87a590c4a9 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -471,9 +471,11 @@ test_expect_success 'pack-objects in too-many-packs mode' ' test_expect_success \ 'fake a SHA1 hash collision' \ - 'test -f .git/objects/c8/2de19312b6c3695c0c18f70709a6c535682a67 && - cp -f .git/objects/9d/235ed07cd19811a6ceb342de82f190e49c9f68 \ - .git/objects/c8/2de19312b6c3695c0c18f70709a6c535682a67' + 'long_a=$(git hash-object a | sed -e "s!^..!&/!") && + long_b=$(git hash-object b | sed -e "s!^..!&/!") && + test -f .git/objects/$long_b && + cp -f .git/objects/$long_a \ + .git/objects/$long_b' test_expect_success \ 'make sure index-pack detects the SHA1 collision' \ diff --git a/t/t5308-pack-detect-duplicates.sh b/t/t5308-pack-detect-duplicates.sh index 156ae9e9d3..6845c1f3c3 100755 --- a/t/t5308-pack-detect-duplicates.sh +++ b/t/t5308-pack-detect-duplicates.sh @@ -4,6 +4,12 @@ test_description='handling of duplicate objects in incoming packfiles' . ./test-lib.sh . "$TEST_DIRECTORY"/lib-pack.sh +if ! test_have_prereq SHA1 +then + skip_all='not using SHA-1 for objects' + test_done +fi + # The sha1s we have in our pack. It's important that these have the same # starting byte, so that they end up in the same fanout section of the index. # That lets us make sure we are exercising the binary search with both sets. diff --git a/t/t5309-pack-delta-cycles.sh b/t/t5309-pack-delta-cycles.sh index 3e7861b075..491556dad9 100755 --- a/t/t5309-pack-delta-cycles.sh +++ b/t/t5309-pack-delta-cycles.sh @@ -4,6 +4,12 @@ test_description='test index-pack handling of delta cycles in packfiles' . ./test-lib.sh . "$TEST_DIRECTORY"/lib-pack.sh +if ! test_have_prereq SHA1 +then + skip_all='not using SHA-1 for objects' + test_done +fi + # Two similar-ish objects that we have computed deltas between. A=01d7713666f4de822776c7622c10f1b07de280dc B=e68fe8129b546b101aee9510c5328e7f21ca1d18 diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 3e8940eee5..f4d28288f0 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -634,7 +634,7 @@ test_expect_success 'pushing valid refs triggers post-receive and post-update ho orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) && newmaster=$(git show-ref -s --verify refs/heads/master) && orgnext=$(cd testrepo && git show-ref -s --verify refs/heads/next) && - newnext=$_z40 && + newnext=$ZERO_OID && git push testrepo refs/heads/master:refs/heads/master :refs/heads/next && ( cd testrepo/.git && @@ -672,15 +672,15 @@ test_expect_success 'deleting dangling ref triggers hooks with correct args' ' ( cd testrepo/.git && cat >pre-receive.expect <<-EOF && - $_z40 $_z40 refs/heads/master + $ZERO_OID $ZERO_OID refs/heads/master EOF cat >update.expect <<-EOF && - refs/heads/master $_z40 $_z40 + refs/heads/master $ZERO_OID $ZERO_OID EOF cat >post-receive.expect <<-EOF && - $_z40 $_z40 refs/heads/master + $ZERO_OID $ZERO_OID refs/heads/master EOF cat >post-update.expect <<-EOF && @@ -703,12 +703,12 @@ test_expect_success 'deletion of a non-existent ref is not fed to post-receive a cd testrepo/.git && cat >pre-receive.expect <<-EOF && $orgmaster $newmaster refs/heads/master - $_z40 $_z40 refs/heads/nonexistent + $ZERO_OID $ZERO_OID refs/heads/nonexistent EOF cat >update.expect <<-EOF && refs/heads/master $orgmaster $newmaster - refs/heads/nonexistent $_z40 $_z40 + refs/heads/nonexistent $ZERO_OID $ZERO_OID EOF cat >post-receive.expect <<-EOF && @@ -732,11 +732,11 @@ test_expect_success 'deletion of a non-existent ref alone does trigger post-rece ( cd testrepo/.git && cat >pre-receive.expect <<-EOF && - $_z40 $_z40 refs/heads/nonexistent + $ZERO_OID $ZERO_OID refs/heads/nonexistent EOF cat >update.expect <<-EOF && - refs/heads/nonexistent $_z40 $_z40 + refs/heads/nonexistent $ZERO_OID $ZERO_OID EOF test_cmp pre-receive.expect pre-receive.actual && @@ -751,7 +751,7 @@ test_expect_success 'mixed ref updates, deletes, invalid deletes trigger hooks w orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) && newmaster=$(git show-ref -s --verify refs/heads/master) && orgnext=$(cd testrepo && git show-ref -s --verify refs/heads/next) && - newnext=$_z40 && + newnext=$ZERO_OID && orgpu=$(cd testrepo && git show-ref -s --verify refs/heads/pu) && newpu=$(git show-ref -s --verify refs/heads/master) && git push testrepo refs/heads/master:refs/heads/master \ @@ -763,14 +763,14 @@ test_expect_success 'mixed ref updates, deletes, invalid deletes trigger hooks w $orgmaster $newmaster refs/heads/master $orgnext $newnext refs/heads/next $orgpu $newpu refs/heads/pu - $_z40 $_z40 refs/heads/nonexistent + $ZERO_OID $ZERO_OID refs/heads/nonexistent EOF cat >update.expect <<-EOF && refs/heads/master $orgmaster $newmaster refs/heads/next $orgnext $newnext refs/heads/pu $orgpu $newpu - refs/heads/nonexistent $_z40 $_z40 + refs/heads/nonexistent $ZERO_OID $ZERO_OID EOF cat >post-receive.expect <<-EOF && diff --git a/t/t5527-fetch-odd-refs.sh b/t/t5527-fetch-odd-refs.sh index 207899a99f..3b0cb98422 100755 --- a/t/t5527-fetch-odd-refs.sh +++ b/t/t5527-fetch-odd-refs.sh @@ -27,7 +27,7 @@ test_expect_success 'suffix ref is ignored during fetch' ' ' test_expect_success 'try to create repo with absurdly long refname' ' - ref240=$_z40/$_z40/$_z40/$_z40/$_z40/$_z40 && + ref240=$ZERO_OID/$ZERO_OID/$ZERO_OID/$ZERO_OID/$ZERO_OID/$ZERO_OID && ref1440=$ref240/$ref240/$ref240/$ref240/$ref240/$ref240 && git init long && ( diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh index f5721b4a59..913089b144 100755 --- a/t/t5551-http-fetch-smart.sh +++ b/t/t5551-http-fetch-smart.sh @@ -26,14 +26,14 @@ setup_askpass_helper cat >exp <<EOF > GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 > Accept: */* -> Accept-Encoding: gzip +> Accept-Encoding: ENCODINGS > Pragma: no-cache < HTTP/1.1 200 OK < Pragma: no-cache < Cache-Control: no-cache, max-age=0, must-revalidate < Content-Type: application/x-git-upload-pack-advertisement > POST /smart/repo.git/git-upload-pack HTTP/1.1 -> Accept-Encoding: gzip +> Accept-Encoding: ENCODINGS > Content-Type: application/x-git-upload-pack-request > Accept: application/x-git-upload-pack-result > Content-Length: xxx @@ -79,8 +79,13 @@ test_expect_success 'clone http repository' ' /^< Date: /d /^< Content-Length: /d /^< Transfer-Encoding: /d - " >act && - test_cmp exp act + " >actual && + sed -e "s/^> Accept-Encoding: .*/> Accept-Encoding: ENCODINGS/" \ + actual >actual.smudged && + test_cmp exp actual.smudged && + + grep "Accept-Encoding:.*gzip" actual >actual.gzip && + test_line_count = 2 actual.gzip ' test_expect_success 'fetch changes via http' ' diff --git a/t/t5571-pre-push-hook.sh b/t/t5571-pre-push-hook.sh index ba975bb355..ac53d63869 100755 --- a/t/t5571-pre-push-hook.sh +++ b/t/t5571-pre-push-hook.sh @@ -78,8 +78,8 @@ test_expect_success 'push to default' ' cat >expected <<EOF parent1 repo1 -refs/tags/one $COMMIT1 refs/tags/tag1 $_z40 -HEAD~ $COMMIT2 refs/heads/prev $_z40 +refs/tags/one $COMMIT1 refs/tags/tag1 $ZERO_OID +HEAD~ $COMMIT2 refs/heads/prev $ZERO_OID EOF test_expect_success 'push non-branches' ' @@ -90,7 +90,7 @@ test_expect_success 'push non-branches' ' cat >expected <<EOF parent1 repo1 -(delete) $_z40 refs/heads/prev $COMMIT2 +(delete) $ZERO_OID refs/heads/prev $COMMIT2 EOF test_expect_success 'push delete' ' @@ -101,7 +101,7 @@ test_expect_success 'push delete' ' cat >expected <<EOF repo1 repo1 -HEAD $COMMIT3 refs/heads/other $_z40 +HEAD $COMMIT3 refs/heads/other $ZERO_OID EOF test_expect_success 'push to URL' ' diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh index 011a5796db..75ec79e6cb 100755 --- a/t/t5701-git-serve.sh +++ b/t/t5701-git-serve.sh @@ -194,4 +194,18 @@ test_expect_success 'sending server-options' ' test_cmp actual expect ' +test_expect_success 'unexpected lines are not allowed in fetch request' ' + git init server && + + test-pkt-line pack >in <<-EOF && + command=fetch + 0001 + this-is-not-a-command + 0000 + EOF + + test_must_fail git -C server serve --stateless-rpc <in >/dev/null 2>err && + grep "unexpected line: .this-is-not-a-command." err +' + test_done diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh index dbfd0691c0..a4fe6508bd 100755 --- a/t/t5702-protocol-v2.sh +++ b/t/t5702-protocol-v2.sh @@ -233,6 +233,132 @@ test_expect_success 'server-options are sent when fetching' ' grep "server-option=world" log ' +test_expect_success 'upload-pack respects config using protocol v2' ' + git init server && + write_script server/.git/hook <<-\EOF && + touch hookout + "$@" + EOF + test_commit -C server one && + + test_config_global uploadpack.packobjectshook ./hook && + test_path_is_missing server/.git/hookout && + git -c protocol.version=2 clone "file://$(pwd)/server" client && + test_path_is_file server/.git/hookout +' + +test_expect_success 'setup filter tests' ' + rm -rf server client && + git init server && + + # 1 commit to create a file, and 1 commit to modify it + test_commit -C server message1 a.txt && + test_commit -C server message2 a.txt && + git -C server config protocol.version 2 && + git -C server config uploadpack.allowfilter 1 && + git -C server config uploadpack.allowanysha1inwant 1 && + git -C server config protocol.version 2 +' + +test_expect_success 'partial clone' ' + GIT_TRACE_PACKET="$(pwd)/trace" git -c protocol.version=2 \ + clone --filter=blob:none "file://$(pwd)/server" client && + grep "version 2" trace && + + # Ensure that the old version of the file is missing + git -C client rev-list master --quiet --objects --missing=print \ + >observed.oids && + grep "$(git -C server rev-parse message1:a.txt)" observed.oids && + + # Ensure that client passes fsck + git -C client fsck +' + +test_expect_success 'dynamically fetch missing object' ' + rm "$(pwd)/trace" && + GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \ + cat-file -p $(git -C server rev-parse message1:a.txt) && + grep "version 2" trace +' + +test_expect_success 'partial fetch' ' + rm -rf client "$(pwd)/trace" && + git init client && + SERVER="file://$(pwd)/server" && + test_config -C client extensions.partialClone "$SERVER" && + + GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \ + fetch --filter=blob:none "$SERVER" master:refs/heads/other && + grep "version 2" trace && + + # Ensure that the old version of the file is missing + git -C client rev-list other --quiet --objects --missing=print \ + >observed.oids && + grep "$(git -C server rev-parse message1:a.txt)" observed.oids && + + # Ensure that client passes fsck + git -C client fsck +' + +test_expect_success 'do not advertise filter if not configured to do so' ' + SERVER="file://$(pwd)/server" && + + rm "$(pwd)/trace" && + git -C server config uploadpack.allowfilter 1 && + GIT_TRACE_PACKET="$(pwd)/trace" git -c protocol.version=2 \ + ls-remote "$SERVER" && + grep "fetch=.*filter" trace && + + rm "$(pwd)/trace" && + git -C server config uploadpack.allowfilter 0 && + GIT_TRACE_PACKET="$(pwd)/trace" git -c protocol.version=2 \ + ls-remote "$SERVER" && + grep "fetch=" trace >fetch_capabilities && + ! grep filter fetch_capabilities +' + +test_expect_success 'partial clone warns if filter is not advertised' ' + rm -rf client && + git -C server config uploadpack.allowfilter 0 && + git -c protocol.version=2 \ + clone --filter=blob:none "file://$(pwd)/server" client 2>err && + test_i18ngrep "filtering not recognized by server, ignoring" err +' + +test_expect_success 'even with handcrafted request, filter does not work if not advertised' ' + git -C server config uploadpack.allowfilter 0 && + + # Custom request that tries to filter even though it is not advertised. + test-pkt-line pack >in <<-EOF && + command=fetch + 0001 + want $(git -C server rev-parse master) + filter blob:none + 0000 + EOF + + test_must_fail git -C server serve --stateless-rpc <in >/dev/null 2>err && + grep "unexpected line: .filter blob:none." err && + + # Exercise to ensure that if advertised, filter works + git -C server config uploadpack.allowfilter 1 && + git -C server serve --stateless-rpc <in >/dev/null +' + +test_expect_success 'default refspec is used to filter ref when fetchcing' ' + test_when_finished "rm -f log" && + + GIT_TRACE_PACKET="$(pwd)/log" git -C file_child -c protocol.version=2 \ + fetch origin && + + git -C file_child log -1 --format=%s three >actual && + git -C file_parent log -1 --format=%s three >expect && + test_cmp expect actual && + + grep "ref-prefix refs/heads/" log && + grep "ref-prefix refs/tags/" log +' + # Test protocol v2 with 'http://' transport # . "$TEST_DIRECTORY"/lib-httpd.sh diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh index 98be78b4a2..ec42c2f779 100755 --- a/t/t6006-rev-list-format.sh +++ b/t/t6006-rev-list-format.sh @@ -447,8 +447,8 @@ test_expect_success '--abbrev' ' git log -1 --format="%h %h %h" HEAD >actual1 && git log -1 --abbrev=5 --format="%h %h %h" HEAD >actual2 && git log -1 --abbrev=5 --format="%H %H %H" HEAD >actual3 && - sed -e "s/$_x40/LONG/g" -e "s/$_x05/SHORT/g" <actual2 >fuzzy2 && - sed -e "s/$_x40/LONG/g" -e "s/$_x05/SHORT/g" <actual3 >fuzzy3 && + sed -e "s/$OID_REGEX/LONG/g" -e "s/$_x05/SHORT/g" <actual2 >fuzzy2 && + sed -e "s/$OID_REGEX/LONG/g" -e "s/$_x05/SHORT/g" <actual3 >fuzzy3 && test_cmp expect2 fuzzy2 && test_cmp expect3 fuzzy3 && ! test_cmp actual1 actual2 diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh index 2a0fbb87b1..b5a1190ffe 100755 --- a/t/t6012-rev-list-simplify.sh +++ b/t/t6012-rev-list-simplify.sh @@ -9,7 +9,7 @@ note () { } unnote () { - git name-rev --tags --stdin | sed -e "s|$_x40 (tags/\([^)]*\)) |\1 |g" + git name-rev --tags --stdin | sed -e "s|$OID_REGEX (tags/\([^)]*\)) |\1 |g" } test_expect_success setup ' diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh index d174bfed30..aa3e249639 100755 --- a/t/t6050-replace.sh +++ b/t/t6050-replace.sh @@ -4,8 +4,6 @@ # test_description='Tests replace refs functionality' -exec </dev/null - . ./test-lib.sh . "$TEST_DIRECTORY/lib-gpg.sh" diff --git a/t/t6111-rev-list-treesame.sh b/t/t6111-rev-list-treesame.sh index 32474c23d3..4244638285 100755 --- a/t/t6111-rev-list-treesame.sh +++ b/t/t6111-rev-list-treesame.sh @@ -20,7 +20,7 @@ note () { } unnote () { - git name-rev --tags --stdin | sed -e "s|$_x40 (tags/\([^)]*\))\([ ]\)|\1\2|g" + git name-rev --tags --stdin | sed -e "s|$OID_REGEX (tags/\([^)]*\))\([ ]\)|\1\2|g" } test_expect_success setup ' diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index bae78c4e89..84dd1cb690 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh @@ -383,7 +383,7 @@ test_expect_success 'describe complains about tree object' ' ' test_expect_success 'describe complains about missing object' ' - test_must_fail git describe $_z40 + test_must_fail git describe $ZERO_OID ' test_done diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index 295d1475bd..48379aa0ee 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -310,7 +310,7 @@ test_expect_success 'exercise strftime with odd fields' ' echo >expected && git for-each-ref --format="%(authordate:format:)" refs/heads >actual && test_cmp expected actual && - long="long format -- $_z40$_z40$_z40$_z40$_z40$_z40$_z40" && + long="long format -- $ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID" && echo $long >expected && git for-each-ref --format="%(authordate:format:$long)" refs/heads >actual && test_cmp expected actual diff --git a/t/t6301-for-each-ref-errors.sh b/t/t6301-for-each-ref-errors.sh index c734ce2388..49cc65bb58 100755 --- a/t/t6301-for-each-ref-errors.sh +++ b/t/t6301-for-each-ref-errors.sh @@ -4,7 +4,7 @@ test_description='for-each-ref errors for broken refs' . ./test-lib.sh -ZEROS=$_z40 +ZEROS=$ZERO_OID MISSING=abababababababababababababababababababab test_expect_success setup ' diff --git a/t/t7009-filter-branch-null-sha1.sh b/t/t7009-filter-branch-null-sha1.sh index a8d9ec4987..9ba9f24ad2 100755 --- a/t/t7009-filter-branch-null-sha1.sh +++ b/t/t7009-filter-branch-null-sha1.sh @@ -12,7 +12,7 @@ test_expect_success 'setup: base commits' ' test_expect_success 'setup: a commit with a bogus null sha1 in the tree' ' { git ls-tree HEAD && - printf "160000 commit $_z40\\tbroken\\n" + printf "160000 commit $ZERO_OID\\tbroken\\n" } >broken-tree && echo "add broken entry" >msg && diff --git a/t/t7011-skip-worktree-reading.sh b/t/t7011-skip-worktree-reading.sh index 84f41451ec..37525cae3a 100755 --- a/t/t7011-skip-worktree-reading.sh +++ b/t/t7011-skip-worktree-reading.sh @@ -118,7 +118,7 @@ test_expect_success 'grep with skip-worktree file' ' test "$(git grep --no-ext-grep test)" = "1:test" ' -echo ":000000 100644 $_z40 $EMPTY_BLOB A 1" > expected +echo ":000000 100644 $ZERO_OID $EMPTY_BLOB A 1" > expected test_expect_success 'diff-index does not examine skip-worktree absent entries' ' setup_absent && git diff-index HEAD -- 1 > result && diff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh index 8f795327a0..b9a86d3347 100755 --- a/t/t7064-wtstatus-pv2.sh +++ b/t/t7064-wtstatus-pv2.sh @@ -46,11 +46,11 @@ test_expect_success 'before initial commit, things added' ' cat >expect <<-EOF && # branch.oid (initial) # branch.head master - 1 A. N... 000000 100644 100644 $_z40 $OID_A dir1/file_a - 1 A. N... 000000 100644 100644 $_z40 $OID_B dir1/file_b - 1 A. N... 000000 100644 100644 $_z40 $OID_X file_x - 1 A. N... 000000 100644 100644 $_z40 $OID_Y file_y - 1 A. N... 000000 100644 100644 $_z40 $OID_Z file_z + 1 A. N... 000000 100644 100644 $ZERO_OID $OID_A dir1/file_a + 1 A. N... 000000 100644 100644 $ZERO_OID $OID_B dir1/file_b + 1 A. N... 000000 100644 100644 $ZERO_OID $OID_X file_x + 1 A. N... 000000 100644 100644 $ZERO_OID $OID_Y file_y + 1 A. N... 000000 100644 100644 $ZERO_OID $OID_Z file_z ? actual ? expect EOF @@ -63,11 +63,11 @@ test_expect_success 'before initial commit, things added (-z)' ' lf_to_nul >expect <<-EOF && # branch.oid (initial) # branch.head master - 1 A. N... 000000 100644 100644 $_z40 $OID_A dir1/file_a - 1 A. N... 000000 100644 100644 $_z40 $OID_B dir1/file_b - 1 A. N... 000000 100644 100644 $_z40 $OID_X file_x - 1 A. N... 000000 100644 100644 $_z40 $OID_Y file_y - 1 A. N... 000000 100644 100644 $_z40 $OID_Z file_z + 1 A. N... 000000 100644 100644 $ZERO_OID $OID_A dir1/file_a + 1 A. N... 000000 100644 100644 $ZERO_OID $OID_B dir1/file_b + 1 A. N... 000000 100644 100644 $ZERO_OID $OID_X file_x + 1 A. N... 000000 100644 100644 $ZERO_OID $OID_Y file_y + 1 A. N... 000000 100644 100644 $ZERO_OID $OID_Z file_z ? actual ? expect EOF @@ -128,7 +128,7 @@ test_expect_success 'after first commit, stage existing changes' ' # branch.oid $H0 # branch.head master 1 M. N... 100644 100644 100644 $OID_X $OID_X1 file_x - 1 D. N... 100644 000000 000000 $OID_Z $_z40 file_z + 1 D. N... 100644 000000 000000 $OID_Z $ZERO_OID file_z ? actual ? expect EOF @@ -145,7 +145,7 @@ test_expect_success 'rename causes 2 path lines' ' # branch.oid $H0 # branch.head master 1 M. N... 100644 100644 100644 $OID_X $OID_X1 file_x - 1 D. N... 100644 000000 000000 $OID_Z $_z40 file_z + 1 D. N... 100644 000000 000000 $OID_Z $ZERO_OID file_z 2 R. N... 100644 100644 100644 $OID_Y $OID_Y R100 renamed_yQfile_y ? actual ? expect @@ -163,7 +163,7 @@ test_expect_success 'rename causes 2 path lines (-z)' ' # branch.oid $H0 # branch.head master 1 M. N... 100644 100644 100644 $OID_X $OID_X1 file_x - 1 D. N... 100644 000000 000000 $OID_Z $_z40 file_z + 1 D. N... 100644 000000 000000 $OID_Z $ZERO_OID file_z 2 R. N... 100644 100644 100644 $OID_Y $OID_Y R100 renamed_yQfile_y ? actual ? expect @@ -246,8 +246,8 @@ test_expect_success 'verify --intent-to-add output' ' git add --intent-to-add intent1.add intent2.add && cat >expect <<-EOF && - 1 .A N... 000000 000000 100644 $_z40 $_z40 intent1.add - 1 .A N... 000000 000000 100644 $_z40 $_z40 intent2.add + 1 .A N... 000000 000000 100644 $ZERO_OID $ZERO_OID intent1.add + 1 .A N... 000000 000000 100644 $ZERO_OID $ZERO_OID intent2.add EOF git status --porcelain=v2 >actual && @@ -280,7 +280,7 @@ test_expect_success 'verify AA (add-add) conflict' ' cat >expect <<-EOF && # branch.oid $HM # branch.head AA_M - u AA N... 000000 100644 100644 100644 $_z40 $OID_AA_B $OID_AA_A conflict.txt + u AA N... 000000 100644 100644 100644 $ZERO_OID $OID_AA_B $OID_AA_A conflict.txt EOF git status --porcelain=v2 --branch --untracked-files=all >actual && @@ -373,7 +373,7 @@ test_expect_success 'verify upstream fields in branch header' ' ## Test upstream-gone case. Fake this by pointing origin/master at ## a non-existing commit. OLD=$(git rev-parse origin/master) && - NEW=$_z40 && + NEW=$ZERO_OID && mv .git/packed-refs .git/old-packed-refs && sed "s/$OLD/$NEW/g" <.git/old-packed-refs >.git/packed-refs && @@ -469,8 +469,8 @@ test_expect_success 'create and add submodule, submodule appears clean (A. S...) # branch.head master # branch.upstream origin/master # branch.ab +0 -0 - 1 A. N... 000000 100644 100644 $_z40 $HMOD .gitmodules - 1 A. S... 000000 160000 160000 $_z40 $HSUB sub1 + 1 A. N... 000000 100644 100644 $ZERO_OID $HMOD .gitmodules + 1 A. S... 000000 160000 160000 $ZERO_OID $HSUB sub1 EOF git status --porcelain=v2 --branch --untracked-files=all >actual && @@ -494,8 +494,8 @@ test_expect_success 'untracked changes in added submodule (AM S..U)' ' # branch.head master # branch.upstream origin/master # branch.ab +0 -0 - 1 A. N... 000000 100644 100644 $_z40 $HMOD .gitmodules - 1 AM S..U 000000 160000 160000 $_z40 $HSUB sub1 + 1 A. N... 000000 100644 100644 $ZERO_OID $HMOD .gitmodules + 1 AM S..U 000000 160000 160000 $ZERO_OID $HSUB sub1 EOF git status --porcelain=v2 --branch --untracked-files=all >actual && @@ -519,8 +519,8 @@ test_expect_success 'staged changes in added submodule (AM S.M.)' ' # branch.head master # branch.upstream origin/master # branch.ab +0 -0 - 1 A. N... 000000 100644 100644 $_z40 $HMOD .gitmodules - 1 AM S.M. 000000 160000 160000 $_z40 $HSUB sub1 + 1 A. N... 000000 100644 100644 $ZERO_OID $HMOD .gitmodules + 1 AM S.M. 000000 160000 160000 $ZERO_OID $HSUB sub1 EOF git status --porcelain=v2 --branch --untracked-files=all >actual && @@ -546,8 +546,8 @@ test_expect_success 'staged and unstaged changes in added (AM S.M.)' ' # branch.head master # branch.upstream origin/master # branch.ab +0 -0 - 1 A. N... 000000 100644 100644 $_z40 $HMOD .gitmodules - 1 AM S.M. 000000 160000 160000 $_z40 $HSUB sub1 + 1 A. N... 000000 100644 100644 $ZERO_OID $HMOD .gitmodules + 1 AM S.M. 000000 160000 160000 $ZERO_OID $HSUB sub1 EOF git status --porcelain=v2 --branch --untracked-files=all >actual && @@ -573,8 +573,8 @@ test_expect_success 'staged and untracked changes in added submodule (AM S.MU)' # branch.head master # branch.upstream origin/master # branch.ab +0 -0 - 1 A. N... 000000 100644 100644 $_z40 $HMOD .gitmodules - 1 AM S.MU 000000 160000 160000 $_z40 $HSUB sub1 + 1 A. N... 000000 100644 100644 $ZERO_OID $HMOD .gitmodules + 1 AM S.MU 000000 160000 160000 $ZERO_OID $HSUB sub1 EOF git status --porcelain=v2 --branch --untracked-files=all >actual && @@ -600,8 +600,8 @@ test_expect_success 'commit within the submodule appears as new commit in super # branch.head master # branch.upstream origin/master # branch.ab +0 -0 - 1 A. N... 000000 100644 100644 $_z40 $HMOD .gitmodules - 1 AM SC.. 000000 160000 160000 $_z40 $HSUB sub1 + 1 A. N... 000000 100644 100644 $ZERO_OID $HMOD .gitmodules + 1 AM SC.. 000000 160000 160000 $ZERO_OID $HSUB sub1 EOF git status --porcelain=v2 --branch --untracked-files=all >actual && diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index 152104412f..2f532529b8 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -126,6 +126,22 @@ test_expect_success 'submodule add' ' test_cmp empty untracked ' +test_create_repo parent && +test_commit -C parent one + +test_expect_success 'redirected submodule add does not show progress' ' + git -C addtest submodule add "file://$submodurl/parent" submod-redirected \ + 2>err && + ! grep % err && + test_i18ngrep ! "Checking connectivity" err +' + +test_expect_success 'redirected submodule add --progress does show progress' ' + git -C addtest submodule add --progress "file://$submodurl/parent" \ + submod-redirected-progress 2>err && \ + grep % err +' + test_expect_success 'submodule add to .gitignored path fails' ' ( cd addtest-ignore && diff --git a/t/t7408-submodule-reference.sh b/t/t7408-submodule-reference.sh index e159fc5035..08d9add05e 100755 --- a/t/t7408-submodule-reference.sh +++ b/t/t7408-submodule-reference.sh @@ -59,6 +59,16 @@ test_expect_success 'submodule add --reference uses alternates' ' test_alternate_is_used super/.git/modules/sub/objects/info/alternates super/sub ' +test_expect_success 'submodule add --reference with --dissociate does not use alternates' ' + ( + cd super && + git submodule add --reference ../B --dissociate "file://$base_dir/A" sub-dissociate && + git commit -m B-super-added && + git repack -ad + ) && + test_path_is_missing super/.git/modules/sub-dissociate/objects/info/alternates +' + test_expect_success 'that reference gets used with add' ' ( cd super/sub && @@ -82,6 +92,13 @@ test_expect_success 'updating superproject keeps alternates' ' test_alternate_is_used super-clone/.git/modules/sub/objects/info/alternates super-clone/sub ' +test_expect_success 'updating superproject with --dissociate does not keep alternates' ' + test_when_finished "rm -rf super-clone" && + git clone super super-clone && + git -C super-clone submodule update --init --reference ../B --dissociate && + test_path_is_missing super-clone/.git/modules/sub/objects/info/alternates +' + test_expect_success 'submodules use alternates when cloning a superproject' ' test_when_finished "rm -rf super-clone" && git clone --reference super --recursive super super-clone && diff --git a/t/t7415-submodule-names.sh b/t/t7415-submodule-names.sh new file mode 100755 index 0000000000..a770d92a55 --- /dev/null +++ b/t/t7415-submodule-names.sh @@ -0,0 +1,154 @@ +#!/bin/sh + +test_description='check handling of .. in submodule names + +Exercise the name-checking function on a variety of names, and then give a +real-world setup that confirms we catch this in practice. +' +. ./test-lib.sh +. "$TEST_DIRECTORY"/lib-pack.sh + +test_expect_success 'check names' ' + cat >expect <<-\EOF && + valid + valid/with/paths + EOF + + git submodule--helper check-name >actual <<-\EOF && + valid + valid/with/paths + + ../foo + /../foo + ..\foo + \..\foo + foo/.. + foo/../ + foo\.. + foo\..\ + foo/../bar + EOF + + test_cmp expect actual +' + +test_expect_success 'create innocent subrepo' ' + git init innocent && + git -C innocent commit --allow-empty -m foo +' + +test_expect_success 'submodule add refuses invalid names' ' + test_must_fail \ + git submodule add --name ../../modules/evil "$PWD/innocent" evil +' + +test_expect_success 'add evil submodule' ' + git submodule add "$PWD/innocent" evil && + + mkdir modules && + cp -r .git/modules/evil modules && + write_script modules/evil/hooks/post-checkout <<-\EOF && + echo >&2 "RUNNING POST CHECKOUT" + EOF + + git config -f .gitmodules submodule.evil.update checkout && + git config -f .gitmodules --rename-section \ + submodule.evil submodule.../../modules/evil && + git add modules && + git commit -am evil +' + +# This step seems like it shouldn't be necessary, since the payload is +# contained entirely in the evil submodule. But due to the vagaries of the +# submodule code, checking out the evil module will fail unless ".git/modules" +# exists. Adding another submodule (with a name that sorts before "evil") is an +# easy way to make sure this is the case in the victim clone. +test_expect_success 'add other submodule' ' + git submodule add "$PWD/innocent" another-module && + git add another-module && + git commit -am another +' + +test_expect_success 'clone evil superproject' ' + git clone --recurse-submodules . victim >output 2>&1 && + ! grep "RUNNING POST CHECKOUT" output +' + +test_expect_success 'fsck detects evil superproject' ' + test_must_fail git fsck +' + +test_expect_success 'transfer.fsckObjects detects evil superproject (unpack)' ' + rm -rf dst.git && + git init --bare dst.git && + git -C dst.git config transfer.fsckObjects true && + test_must_fail git push dst.git HEAD +' + +test_expect_success 'transfer.fsckObjects detects evil superproject (index)' ' + rm -rf dst.git && + git init --bare dst.git && + git -C dst.git config transfer.fsckObjects true && + git -C dst.git config transfer.unpackLimit 1 && + test_must_fail git push dst.git HEAD +' + +# Normally our packs contain commits followed by trees followed by blobs. This +# reverses the order, which requires backtracking to find the context of a +# blob. We'll start with a fresh gitmodules-only tree to make it simpler. +test_expect_success 'create oddly ordered pack' ' + git checkout --orphan odd && + git rm -rf --cached . && + git add .gitmodules && + git commit -m odd && + { + pack_header 3 && + pack_obj $(git rev-parse HEAD:.gitmodules) && + pack_obj $(git rev-parse HEAD^{tree}) && + pack_obj $(git rev-parse HEAD) + } >odd.pack && + pack_trailer odd.pack +' + +test_expect_success 'transfer.fsckObjects handles odd pack (unpack)' ' + rm -rf dst.git && + git init --bare dst.git && + test_must_fail git -C dst.git unpack-objects --strict <odd.pack +' + +test_expect_success 'transfer.fsckObjects handles odd pack (index)' ' + rm -rf dst.git && + git init --bare dst.git && + test_must_fail git -C dst.git index-pack --strict --stdin <odd.pack +' + +test_expect_success 'fsck detects symlinked .gitmodules file' ' + git init symlink && + ( + cd symlink && + + # Make the tree directly to avoid index restrictions. + # + # Because symlinks store the target as a blob, choose + # a pathname that could be parsed as a .gitmodules file + # to trick naive non-symlink-aware checking. + tricky="[foo]bar=true" && + content=$(git hash-object -w ../.gitmodules) && + target=$(printf "$tricky" | git hash-object -w --stdin) && + tree=$( + { + printf "100644 blob $content\t$tricky\n" && + printf "120000 blob $target\t.gitmodules\n" + } | git mktree + ) && + commit=$(git commit-tree $tree) && + + # Check not only that we fail, but that it is due to the + # symlink detector; this grep string comes from the config + # variable name and will not be translated. + test_must_fail git fsck 2>output && + grep gitmodulesSymlink output + ) +' + +test_done diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh index 9edf6572ed..b4b74dbe29 100755 --- a/t/t7506-status-submodule.sh +++ b/t/t7506-status-submodule.sh @@ -18,7 +18,7 @@ test_create_repo_with_commit () { } sanitize_output () { - sed -e "s/$_x40/HASH/" -e "s/$_x40/HASH/" output >output2 && + sed -e "s/$OID_REGEX/HASH/" -e "s/$OID_REGEX/HASH/" output >output2 && mv output2 output } diff --git a/t/t7525-status-rename.sh b/t/t7525-status-rename.sh new file mode 100755 index 0000000000..ef8b1b3078 --- /dev/null +++ b/t/t7525-status-rename.sh @@ -0,0 +1,113 @@ +#!/bin/sh + +test_description='git status rename detection options' + +. ./test-lib.sh + +test_expect_success 'setup' ' + echo 1 >original && + git add . && + git commit -m"Adding original file." && + mv original renamed && + echo 2 >> renamed && + git add . && + cat >.gitignore <<-\EOF + .gitignore + expect* + actual* + EOF +' + +test_expect_success 'status no-options' ' + git status >actual && + test_i18ngrep "renamed:" actual +' + +test_expect_success 'status --no-renames' ' + git status --no-renames >actual && + test_i18ngrep "deleted:" actual && + test_i18ngrep "new file:" actual +' + +test_expect_success 'status.renames inherits from diff.renames false' ' + git -c diff.renames=false status >actual && + test_i18ngrep "deleted:" actual && + test_i18ngrep "new file:" actual +' + +test_expect_success 'status.renames inherits from diff.renames true' ' + git -c diff.renames=true status >actual && + test_i18ngrep "renamed:" actual +' + +test_expect_success 'status.renames overrides diff.renames false' ' + git -c diff.renames=true -c status.renames=false status >actual && + test_i18ngrep "deleted:" actual && + test_i18ngrep "new file:" actual +' + +test_expect_success 'status.renames overrides from diff.renames true' ' + git -c diff.renames=false -c status.renames=true status >actual && + test_i18ngrep "renamed:" actual +' + +test_expect_success 'status status.renames=false' ' + git -c status.renames=false status >actual && + test_i18ngrep "deleted:" actual && + test_i18ngrep "new file:" actual +' + +test_expect_success 'status status.renames=true' ' + git -c status.renames=true status >actual && + test_i18ngrep "renamed:" actual +' + +test_expect_success 'commit honors status.renames=false' ' + git -c status.renames=false commit --dry-run >actual && + test_i18ngrep "deleted:" actual && + test_i18ngrep "new file:" actual +' + +test_expect_success 'commit honors status.renames=true' ' + git -c status.renames=true commit --dry-run >actual && + test_i18ngrep "renamed:" actual +' + +test_expect_success 'status config overridden' ' + git -c status.renames=true status --no-renames >actual && + test_i18ngrep "deleted:" actual && + test_i18ngrep "new file:" actual +' + +test_expect_success 'status score=100%' ' + git status -M=100% >actual && + test_i18ngrep "deleted:" actual && + test_i18ngrep "new file:" actual && + + git status --find-rename=100% >actual && + test_i18ngrep "deleted:" actual && + test_i18ngrep "new file:" actual +' + +test_expect_success 'status score=01%' ' + git status -M=01% >actual && + test_i18ngrep "renamed:" actual && + + git status --find-rename=01% >actual && + test_i18ngrep "renamed:" actual +' + +test_expect_success 'copies not overridden by find-rename' ' + cp renamed copy && + git add copy && + + git -c status.renames=copies status -M=01% >actual && + test_i18ngrep "copied:" actual && + test_i18ngrep "renamed:" actual && + + git -c status.renames=copies status --find-rename=01% >actual && + test_i18ngrep "copied:" actual && + test_i18ngrep "renamed:" actual +' + +test_done diff --git a/t/t8012-blame-colors.sh b/t/t8012-blame-colors.sh new file mode 100755 index 0000000000..ed38f74de9 --- /dev/null +++ b/t/t8012-blame-colors.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +test_description='colored git blame' +. ./test-lib.sh + +PROG='git blame -c' +. "$TEST_DIRECTORY"/annotate-tests.sh + +test_expect_success 'colored blame colors contiguous lines' ' + git -c color.blame.repeatedLines=yellow blame --color-lines --abbrev=12 hello.c >actual.raw && + git -c color.blame.repeatedLines=yellow -c blame.coloring=repeatedLines blame --abbrev=12 hello.c >actual.raw.2 && + test_cmp actual.raw actual.raw.2 && + test_decode_color <actual.raw >actual && + grep "<YELLOW>" <actual >darkened && + grep "(F" darkened > F.expect && + grep "(H" darkened > H.expect && + test_line_count = 2 F.expect && + test_line_count = 3 H.expect +' + +test_expect_success 'color by age consistently colors old code' ' + git blame --color-by-age hello.c >actual.raw && + git -c blame.coloring=highlightRecent blame hello.c >actual.raw.2 && + test_cmp actual.raw actual.raw.2 && + test_decode_color <actual.raw >actual && + grep "<BLUE>" <actual >colored && + test_line_count = 10 colored +' + +test_expect_success 'blame color by age: new code is different' ' + cat >>hello.c <<-EOF && + void qfunc(); + EOF + git add hello.c && + GIT_AUTHOR_DATE="" git commit -m "new commit" && + + git -c color.blame.highlightRecent="yellow,1 month ago, cyan" blame --color-by-age hello.c >actual.raw && + test_decode_color <actual.raw >actual && + + grep "<YELLOW>" <actual >colored && + test_line_count = 10 colored && + + grep "<CYAN>" <actual >colored && + test_line_count = 1 colored && + grep qfunc colored +' + +test_done diff --git a/t/t9010-svn-fe.sh b/t/t9010-svn-fe.sh index 8eaaca6f99..0b20b07e68 100755 --- a/t/t9010-svn-fe.sh +++ b/t/t9010-svn-fe.sh @@ -473,7 +473,7 @@ test_expect_failure 'change file mode but keep old content' ' { git rev-list HEAD | git diff-tree --root --stdin | - sed "s/$_x40/OBJID/g" + sed "s/$OID_REGEX/OBJID/g" } >actual && git show HEAD:greeting >actual.blob && git show HEAD^:greeting >actual.target && @@ -573,7 +573,7 @@ test_expect_success 'NUL in log message, file content, and property name' ' { git rev-list HEAD | git diff-tree --root --stdin | - sed "s/$_x40/OBJID/g" + sed "s/$OID_REGEX/OBJID/g" } >actual && { git cat-file commit HEAD | nul_to_q && @@ -659,7 +659,7 @@ test_expect_success 'change file mode and reiterate content' ' { git rev-list HEAD | git diff-tree --root --stdin | - sed "s/$_x40/OBJID/g" + sed "s/$OID_REGEX/OBJID/g" } >actual && git show HEAD:greeting >actual.blob && git show HEAD^:greeting >actual.target && @@ -792,7 +792,7 @@ test_expect_success 'property deltas supported' ' { git rev-list HEAD | git diff-tree --stdin | - sed "s/$_x40/OBJID/g" + sed "s/$OID_REGEX/OBJID/g" } >actual && test_cmp expect actual ' @@ -846,7 +846,7 @@ test_expect_success 'properties on /' ' { git rev-list HEAD | git diff-tree --root --always --stdin | - sed "s/$_x40/OBJID/g" + sed "s/$OID_REGEX/OBJID/g" } >actual && test_cmp expect actual ' @@ -931,7 +931,7 @@ test_expect_success 'deltas for typechange' ' { git rev-list HEAD | git diff-tree --root --stdin | - sed "s/$_x40/OBJID/g" + sed "s/$OID_REGEX/OBJID/g" } >actual && test_cmp expect actual ' @@ -1030,7 +1030,7 @@ test_expect_success 'deltas need not consume the whole preimage' ' { git rev-list HEAD | git diff-tree --root --stdin | - sed "s/$_x40/OBJID/g" + sed "s/$OID_REGEX/OBJID/g" } >actual && test_cmp expect actual && git show HEAD:postimage >actual.3 && diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index dc79df7b04..9e7f96223d 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -1185,7 +1185,7 @@ test_expect_success PIPE 'N: empty directory reads as missing' ' test_cmp expect.response response && git rev-list read-empty | git diff-tree -r --root --stdin | - sed "s/$_x40/OBJNAME/g" >actual && + sed "s/$OID_REGEX/OBJNAME/g" >actual && test_cmp expect actual ' @@ -1271,7 +1271,7 @@ test_expect_success 'N: delete directory by copying' ' git fast-import <input && git rev-list N-delete | git diff-tree -r --stdin --root --always | - sed -e "s/$_x40/OBJID/g" >actual && + sed -e "s/$OID_REGEX/OBJID/g" >actual && test_cmp expect actual ' @@ -2602,7 +2602,7 @@ test_expect_success 'R: terminating "done" within commit' ' EOF git rev-list done-ends | git diff-tree -r --stdin --root --always | - sed -e "s/$_x40/OBJID/g" >actual && + sed -e "s/$OID_REGEX/OBJID/g" >actual && test_cmp expect actual ' diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 1b34caa1e1..36deb0b123 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -13,7 +13,7 @@ complete () return 0 } -# Be careful when updating this list: +# Be careful when updating these lists: # # (1) The build tree may have build artifact from different branch, or # the user's $PATH may have a random executable that may begin @@ -30,7 +30,8 @@ complete () # completion for "git <TAB>", and a plumbing is excluded. "add", # "filter-branch" and "ls-files" are listed for this. -GIT_TESTING_COMMAND_COMPLETION='add checkout check-attr filter-branch ls-files' +GIT_TESTING_ALL_COMMAND_LIST='add checkout check-attr filter-branch ls-files' +GIT_TESTING_PORCELAIN_COMMAND_LIST='add checkout filter-branch' . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" @@ -84,10 +85,11 @@ test_completion () then printf '%s\n' "$2" >expected else - sed -e 's/Z$//' >expected + sed -e 's/Z$//' |sort >expected fi && run_completion "$1" && - test_cmp expected out + sort out >out_sorted && + test_cmp expected out_sorted } # Test __gitcomp. @@ -400,6 +402,46 @@ test_expect_success '__gitdir - remote as argument' ' test_cmp expected "$actual" ' + +test_expect_success '__git_dequote - plain unquoted word' ' + __git_dequote unquoted-word && + verbose test unquoted-word = "$dequoted_word" +' + +# input: b\a\c\k\'\\\"s\l\a\s\h\es +# expected: back'\"slashes +test_expect_success '__git_dequote - backslash escaped' ' + __git_dequote "b\a\c\k\\'\''\\\\\\\"s\l\a\s\h\es" && + verbose test "back'\''\\\"slashes" = "$dequoted_word" +' + +# input: sin'gle\' '"quo'ted +# expected: single\ "quoted +test_expect_success '__git_dequote - single quoted' ' + __git_dequote "'"sin'gle\\\\' '\\\"quo'ted"'" && + verbose test '\''single\ "quoted'\'' = "$dequoted_word" +' + +# input: dou"ble\\" "\"\quot"ed +# expected: double\ "\quoted +test_expect_success '__git_dequote - double quoted' ' + __git_dequote '\''dou"ble\\" "\"\quot"ed'\'' && + verbose test '\''double\ "\quoted'\'' = "$dequoted_word" +' + +# input: 'open single quote +test_expect_success '__git_dequote - open single quote' ' + __git_dequote "'\''open single quote" && + verbose test "open single quote" = "$dequoted_word" +' + +# input: "open double quote +test_expect_success '__git_dequote - open double quote' ' + __git_dequote "\"open double quote" && + verbose test "open double quote" = "$dequoted_word" +' + + test_expect_success '__gitcomp_direct - puts everything into COMPREPLY as-is' ' sed -e "s/Z$//g" >expected <<-EOF && with-trailing-space Z @@ -1168,6 +1210,124 @@ test_expect_success 'teardown after ref completion' ' git remote remove other ' + +test_path_completion () +{ + test $# = 2 || error "bug in the test script: not 2 parameters to test_path_completion" + + local cur="$1" expected="$2" + echo "$expected" >expected && + ( + # In the following tests calling this function we only + # care about how __git_complete_index_file() deals with + # unusual characters in path names. By requesting only + # untracked files we dont have to bother adding any + # paths to the index in those tests. + __git_complete_index_file --others && + print_comp + ) && + test_cmp expected out +} + +test_expect_success 'setup for path completion tests' ' + mkdir simple-dir \ + "spaces in dir" \ + árvíztűrő && + touch simple-dir/simple-file \ + "spaces in dir/spaces in file" \ + "árvíztűrő/Сайн яваарай" && + if test_have_prereq !MINGW && + mkdir BS\\dir \ + '$'separators\034in\035dir'' && + touch BS\\dir/DQ\"file \ + '$'separators\034in\035dir/sep\036in\037file'' + then + test_set_prereq FUNNYNAMES + else + rm -rf BS\\dir '$'separators\034in\035dir'' + fi +' + +test_expect_success '__git_complete_index_file - simple' ' + test_path_completion simple simple-dir && # Bash is supposed to + # add the trailing /. + test_path_completion simple-dir/simple simple-dir/simple-file +' + +test_expect_success \ + '__git_complete_index_file - escaped characters on cmdline' ' + test_path_completion spac "spaces in dir" && # Bash will turn this + # into "spaces\ in\ dir" + test_path_completion "spaces\\ i" \ + "spaces in dir" && + test_path_completion "spaces\\ in\\ dir/s" \ + "spaces in dir/spaces in file" && + test_path_completion "spaces\\ in\\ dir/spaces\\ i" \ + "spaces in dir/spaces in file" +' + +test_expect_success \ + '__git_complete_index_file - quoted characters on cmdline' ' + # Testing with an opening but without a corresponding closing + # double quote is important. + test_path_completion \"spac "spaces in dir" && + test_path_completion "\"spaces i" \ + "spaces in dir" && + test_path_completion "\"spaces in dir/s" \ + "spaces in dir/spaces in file" && + test_path_completion "\"spaces in dir/spaces i" \ + "spaces in dir/spaces in file" +' + +test_expect_success '__git_complete_index_file - UTF-8 in ls-files output' ' + test_path_completion á árvíztűrő && + test_path_completion árvíztűrő/С "árvíztűrő/Сайн яваарай" +' + +test_expect_success FUNNYNAMES \ + '__git_complete_index_file - C-style escapes in ls-files output' ' + test_path_completion BS \ + BS\\dir && + test_path_completion BS\\\\d \ + BS\\dir && + test_path_completion BS\\\\dir/DQ \ + BS\\dir/DQ\"file && + test_path_completion BS\\\\dir/DQ\\\"f \ + BS\\dir/DQ\"file +' + +test_expect_success FUNNYNAMES \ + '__git_complete_index_file - \nnn-escaped characters in ls-files output' ' + test_path_completion sep '$'separators\034in\035dir'' && + test_path_completion '$'separators\034i'' \ + '$'separators\034in\035dir'' && + test_path_completion '$'separators\034in\035dir/sep'' \ + '$'separators\034in\035dir/sep\036in\037file'' && + test_path_completion '$'separators\034in\035dir/sep\036i'' \ + '$'separators\034in\035dir/sep\036in\037file'' +' + +test_expect_success FUNNYNAMES \ + '__git_complete_index_file - removing repeated quoted path components' ' + test_when_finished rm -r repeated-quoted && + mkdir repeated-quoted && # A directory whose name in itself + # would not be quoted ... + >repeated-quoted/0-file && + >repeated-quoted/1\"file && # ... but here the file makes the + # dirname quoted ... + >repeated-quoted/2-file && + >repeated-quoted/3\"file && # ... and here, too. + + # Still, we shold only list the directory name only once. + test_path_completion repeated repeated-quoted +' + +test_expect_success 'teardown after path completion tests' ' + rm -rf simple-dir "spaces in dir" árvíztűrő \ + BS\\dir '$'separators\034in\035dir'' +' + + test_expect_success '__git_get_config_variables' ' cat >expect <<-EOF && name-1 @@ -1191,17 +1351,6 @@ test_expect_success '__git_pretty_aliases' ' test_cmp expect actual ' -test_expect_success '__git_aliases' ' - cat >expect <<-EOF && - ci - co - EOF - test_config alias.ci commit && - test_config alias.co checkout && - __git_aliases >actual && - test_cmp expect actual -' - test_expect_success 'basic' ' run_completion "git " && # built-in @@ -1365,6 +1514,7 @@ test_expect_success 'complete files' ' echo "expected" > .gitignore && echo "out" >> .gitignore && + echo "out_sorted" >> .gitignore && git add .gitignore && test_completion "git commit " ".gitignore" && @@ -1510,13 +1660,6 @@ test_expect_success 'sourcing the completion script clears cached commands' ' verbose test -z "$__git_all_commands" ' -test_expect_success 'sourcing the completion script clears cached porcelain commands' ' - __git_compute_porcelain_commands && - verbose test -n "$__git_porcelain_commands" && - . "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" && - verbose test -z "$__git_porcelain_commands" -' - test_expect_success !GETTEXT_POISON 'sourcing the completion script clears cached merge strategies' ' __git_compute_merge_strategies && verbose test -n "$__git_merge_strategies" && diff --git a/t/test-lib.sh b/t/test-lib.sh index f3771ab195..28315706be 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -184,6 +184,8 @@ _x40="$_x35$_x05" # Zero SHA-1 _z40=0000000000000000000000000000000000000000 +OID_REGEX="$_x40" +ZERO_OID=$_z40 EMPTY_TREE=4b825dc642cb6eb9a060e54bf8d69288fbee4904 EMPTY_BLOB=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 @@ -195,7 +197,7 @@ LF=' # when case-folding filenames u200c=$(printf '\342\200\214') -export _x05 _x35 _x40 _z40 LF u200c EMPTY_TREE EMPTY_BLOB +export _x05 _x35 _x40 _z40 LF u200c EMPTY_TREE EMPTY_BLOB ZERO_OID OID_REGEX # Each test should start with something like this, after copyright notices: # @@ -1207,3 +1209,10 @@ test_lazy_prereq TIME_T_IS_64BIT 'test-tool date time_t-is64bit' test_lazy_prereq CURL ' curl --version ' + +# SHA1 is a test if the hash algorithm in use is SHA-1. This is both for tests +# which will not work with other hash algorithms and tests that work but don't +# test anything meaningful (e.g. special values which cause short collisions). +test_lazy_prereq SHA1 ' + test $(git hash-object /dev/null) = e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 +' diff --git a/templates/hooks--pre-commit.sample b/templates/hooks--pre-commit.sample index 68d62d5446..6a75641638 100755 --- a/templates/hooks--pre-commit.sample +++ b/templates/hooks--pre-commit.sample @@ -12,7 +12,7 @@ then against=HEAD else # Initial commit: diff against an empty tree object - against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 + against=$(git hash-object -t tree /dev/null) fi # If you want to allow non-ASCII filenames set this variable to true. diff --git a/tmp-objdir.c b/tmp-objdir.c index fea3f55545..91c00567f4 100644 --- a/tmp-objdir.c +++ b/tmp-objdir.c @@ -127,7 +127,7 @@ struct tmp_objdir *tmp_objdir_create(void) struct tmp_objdir *t; if (the_tmp_objdir) - die("BUG: only one tmp_objdir can be used at a time"); + BUG("only one tmp_objdir can be used at a time"); t = xmalloc(sizeof(*t)); strbuf_init(&t->path, 0); @@ -298,7 +298,7 @@ static void apply_arg_if_exists(struct trailer_item *in_tok, free_arg_item(arg_tok); break; default: - die("BUG: trailer.c: unhandled value %d", + BUG("trailer.c: unhandled value %d", arg_tok->conf.if_exists); } } @@ -323,7 +323,7 @@ static void apply_arg_if_missing(struct list_head *head, list_add(&to_add->list, head); break; default: - die("BUG: trailer.c: unhandled value %d", + BUG("trailer.c: unhandled value %d", arg_tok->conf.if_missing); } } @@ -557,7 +557,7 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb) warning(_("unknown value '%s' for key '%s'"), value, conf_key); break; default: - die("BUG: trailer.c: unhandled type %d", type); + BUG("trailer.c: unhandled type %d", type); } return 0; } diff --git a/transport-helper.c b/transport-helper.c index 11f1055b47..1f8ff7e942 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -11,6 +11,7 @@ #include "sigchain.h" #include "argv-array.h" #include "refs.h" +#include "refspec.h" #include "transport-internal.h" #include "protocol.h" @@ -35,8 +36,7 @@ struct helper_data { char *export_marks; char *import_marks; /* These go from remote name (as in "list") to private name */ - struct refspec *refspecs; - int refspec_nr; + struct refspec rs; /* Transport options for fetch-pack/send-pack (should one of * those be invoked). */ @@ -106,9 +106,6 @@ static struct child_process *get_helper(struct transport *transport) struct helper_data *data = transport->data; struct strbuf buf = STRBUF_INIT; struct child_process *helper; - const char **refspecs = NULL; - int refspec_nr = 0; - int refspec_alloc = 0; int duped; int code; @@ -138,6 +135,7 @@ static struct child_process *get_helper(struct transport *transport) data->helper = helper; data->no_disconnect_req = 0; + refspec_init(&data->rs, REFSPEC_FETCH); /* * Open the output as FILE* so strbuf_getline_*() family of @@ -183,11 +181,8 @@ static struct child_process *get_helper(struct transport *transport) data->export = 1; else if (!strcmp(capname, "check-connectivity")) data->check_connectivity = 1; - else if (!data->refspecs && skip_prefix(capname, "refspec ", &arg)) { - ALLOC_GROW(refspecs, - refspec_nr + 1, - refspec_alloc); - refspecs[refspec_nr++] = xstrdup(arg); + else if (skip_prefix(capname, "refspec ", &arg)) { + refspec_append(&data->rs, arg); } else if (!strcmp(capname, "connect")) { data->connect = 1; } else if (!strcmp(capname, "stateless-connect")) { @@ -206,14 +201,7 @@ static struct child_process *get_helper(struct transport *transport) capname); } } - if (refspecs) { - int i; - data->refspec_nr = refspec_nr; - data->refspecs = parse_fetch_refspec(refspec_nr, refspecs); - for (i = 0; i < refspec_nr; i++) - free((char *)refspecs[i]); - free(refspecs); - } else if (data->import || data->bidi_import || data->export) { + if (!data->rs.nr && (data->import || data->bidi_import || data->export)) { warning("This remote helper should implement refspec capability."); } strbuf_release(&buf); @@ -377,8 +365,7 @@ static int release_helper(struct transport *transport) { int res = 0; struct helper_data *data = transport->data; - free_refspec(data->refspec_nr, data->refspecs); - data->refspecs = NULL; + refspec_clear(&data->rs); res = disconnect_helper(transport); free(transport->data); return res; @@ -535,8 +522,8 @@ static int fetch_with_import(struct transport *transport, if (posn->status & REF_STATUS_UPTODATE) continue; name = posn->symref ? posn->symref : posn->name; - if (data->refspecs) - private = apply_refspecs(data->refspecs, data->refspec_nr, name); + if (data->rs.nr) + private = apply_refspecs(&data->rs, name); else private = xstrdup(name); if (private) { @@ -814,11 +801,11 @@ static int push_update_refs_status(struct helper_data *data, if (push_update_ref_status(&buf, &ref, remote_refs)) continue; - if (flags & TRANSPORT_PUSH_DRY_RUN || !data->refspecs || data->no_private_update) + if (flags & TRANSPORT_PUSH_DRY_RUN || !data->rs.nr || data->no_private_update) continue; /* propagate back the update to the remote namespace */ - private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name); + private = apply_refspecs(&data->rs, ref->name); if (!private) continue; update_ref("update by helper", private, &ref->new_oid, NULL, @@ -938,7 +925,7 @@ static int push_refs_with_export(struct transport *transport, struct string_list revlist_args = STRING_LIST_INIT_DUP; struct strbuf buf = STRBUF_INIT; - if (!data->refspecs) + if (!data->rs.nr) die("remote-helper doesn't support push; refspec needed"); set_common_push_options(transport, data->name, flags); @@ -955,7 +942,7 @@ static int push_refs_with_export(struct transport *transport, char *private; struct object_id oid; - private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name); + private = apply_refspecs(&data->rs, ref->name); if (private && !get_oid(private, &oid)) { strbuf_addf(&buf, "^%s", private); string_list_append_nodup(&revlist_args, diff --git a/transport.c b/transport.c index 2c4de32b33..a32da30dee 100644 --- a/transport.c +++ b/transport.c @@ -11,6 +11,7 @@ #include "bundle.h" #include "dir.h" #include "refs.h" +#include "refspec.h" #include "branch.h" #include "url.h" #include "submodule.h" @@ -390,7 +391,7 @@ int transport_refs_pushed(struct ref *ref) void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int verbose) { - struct refspec rs; + struct refspec_item rs; if (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE) return; @@ -619,29 +620,6 @@ void transport_print_push_status(const char *dest, struct ref *refs, free(head); } -void transport_verify_remote_names(int nr_heads, const char **heads) -{ - int i; - - for (i = 0; i < nr_heads; i++) { - const char *local = heads[i]; - const char *remote = strrchr(heads[i], ':'); - - if (*local == '+') - local++; - - /* A matching refspec is okay. */ - if (remote == local && remote[1] == '\0') - continue; - - remote = remote ? (remote + 1) : local; - if (check_refname_format(remote, - REFNAME_ALLOW_ONELEVEL|REFNAME_REFSPEC_PATTERN)) - die("remote part of refspec is not a valid name in %s", - heads[i]); - } -} - static int git_transport_push(struct transport *transport, struct ref *remote_refs, int flags) { struct git_transport_data *data = transport->data; @@ -737,7 +715,7 @@ void transport_take_over(struct transport *transport, struct git_transport_data *data; if (!transport->smart_options) - die("BUG: taking over transport requires non-NULL " + BUG("taking over transport requires non-NULL " "smart_options field."); data = xcalloc(1, sizeof(*data)); @@ -862,7 +840,7 @@ int is_transport_allowed(const char *type, int from_user) return from_user; } - die("BUG: invalid protocol_allow_config type"); + BUG("invalid protocol_allow_config type"); } void transport_check_allowed(const char *type) @@ -1093,11 +1071,10 @@ static int run_pre_push_hook(struct transport *transport, } int transport_push(struct transport *transport, - int refspec_nr, const char **refspec, int flags, + struct refspec *rs, int flags, unsigned int *reject_reasons) { *reject_reasons = 0; - transport_verify_remote_names(refspec_nr, refspec); if (transport_color_config() < 0) return -1; @@ -1111,38 +1088,17 @@ int transport_push(struct transport *transport, int porcelain = flags & TRANSPORT_PUSH_PORCELAIN; int pretend = flags & TRANSPORT_PUSH_DRY_RUN; int push_ret, ret, err; - struct refspec *tmp_rs; struct argv_array ref_prefixes = ARGV_ARRAY_INIT; - int i; - if (check_push_refs(local_refs, refspec_nr, refspec) < 0) + if (check_push_refs(local_refs, rs) < 0) return -1; - tmp_rs = parse_push_refspec(refspec_nr, refspec); - for (i = 0; i < refspec_nr; i++) { - const char *prefix = NULL; - - if (tmp_rs[i].dst) - prefix = tmp_rs[i].dst; - else if (tmp_rs[i].src && !tmp_rs[i].exact_sha1) - prefix = tmp_rs[i].src; - - if (prefix) { - const char *glob = strchr(prefix, '*'); - if (glob) - argv_array_pushf(&ref_prefixes, "%.*s", - (int)(glob - prefix), - prefix); - else - expand_ref_prefix(&ref_prefixes, prefix); - } - } + refspec_ref_prefixes(rs, &ref_prefixes); remote_refs = transport->vtable->get_refs_list(transport, 1, &ref_prefixes); argv_array_clear(&ref_prefixes); - free_refspec(refspec_nr, tmp_rs); if (flags & TRANSPORT_PUSH_ALL) match_flags |= MATCH_REFS_ALL; @@ -1153,10 +1109,8 @@ int transport_push(struct transport *transport, if (flags & TRANSPORT_PUSH_FOLLOW_TAGS) match_flags |= MATCH_REFS_FOLLOW_TAGS; - if (match_push_refs(local_refs, &remote_refs, - refspec_nr, refspec, match_flags)) { + if (match_push_refs(local_refs, &remote_refs, rs, match_flags)) return -1; - } if (transport->smart_options && transport->smart_options->cas && @@ -1185,7 +1139,7 @@ int transport_push(struct transport *transport, if (!push_unpushed_submodules(&commits, transport->remote, - refspec, refspec_nr, + rs, transport->push_options, pretend)) { oid_array_clear(&commits); diff --git a/transport.h b/transport.h index 73a7be3c8a..7792b08582 100644 --- a/transport.h +++ b/transport.h @@ -203,7 +203,7 @@ void transport_set_verbosity(struct transport *transport, int verbosity, #define REJECT_NEEDS_FORCE 0x10 int transport_push(struct transport *connection, - int refspec_nr, const char **refspec, int flags, + struct refspec *rs, int flags, unsigned int * reject_reasons); /* @@ -233,8 +233,6 @@ int transport_helper_init(struct transport *transport, const char *name); int bidirectional_transfer_loop(int input, int output); /* common methods used by transport.c and builtin/send-pack.c */ -void transport_verify_remote_names(int nr_heads, const char **heads); - void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int verbose); int transport_refs_pushed(struct ref *ref); diff --git a/tree-walk.c b/tree-walk.c index e11b3063af..8f5090862b 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -105,7 +105,7 @@ static void entry_extract(struct tree_desc *t, struct name_entry *a) static int update_tree_entry_internal(struct tree_desc *desc, struct strbuf *err) { const void *buf = desc->buffer; - const unsigned char *end = desc->entry.oid->hash + 20; + const unsigned char *end = desc->entry.oid->hash + the_hash_algo->rawsz; unsigned long size = desc->size; unsigned long len = end - (const unsigned char *)buf; @@ -488,7 +488,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info) struct dir_state { void *tree; unsigned long size; - unsigned char sha1[20]; + struct object_id oid; }; static int find_tree_entry(struct tree_desc *t, const char *name, struct object_id *result, unsigned *mode) @@ -576,7 +576,7 @@ int get_tree_entry(const struct object_id *tree_oid, const char *name, struct ob * See the code for enum follow_symlink_result for a description of * the return values. */ -enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_sha1, const char *name, unsigned char *result, struct strbuf *result_path, unsigned *mode) +enum follow_symlinks_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned *mode) { int retval = MISSING_OBJECT; struct dir_state *parents = NULL; @@ -589,7 +589,7 @@ enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_s init_tree_desc(&t, NULL, 0UL); strbuf_addstr(&namebuf, name); - hashcpy(current_tree_oid.hash, tree_sha1); + oidcpy(¤t_tree_oid, tree_oid); while (1) { int find_result; @@ -609,11 +609,11 @@ enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_s ALLOC_GROW(parents, parents_nr + 1, parents_alloc); parents[parents_nr].tree = tree; parents[parents_nr].size = size; - hashcpy(parents[parents_nr].sha1, root.hash); + oidcpy(&parents[parents_nr].oid, &root); parents_nr++; if (namebuf.buf[0] == '\0') { - hashcpy(result, root.hash); + oidcpy(result, &root); retval = FOUND; goto done; } @@ -663,7 +663,7 @@ enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_s /* We could end up here via a symlink to dir/.. */ if (namebuf.buf[0] == '\0') { - hashcpy(result, parents[parents_nr - 1].sha1); + oidcpy(result, &parents[parents_nr - 1].oid); retval = FOUND; goto done; } @@ -677,7 +677,7 @@ enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_s if (S_ISDIR(*mode)) { if (!remainder) { - hashcpy(result, current_tree_oid.hash); + oidcpy(result, ¤t_tree_oid); retval = FOUND; goto done; } @@ -687,7 +687,7 @@ enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_s 1 + first_slash - namebuf.buf); } else if (S_ISREG(*mode)) { if (!remainder) { - hashcpy(result, current_tree_oid.hash); + oidcpy(result, ¤t_tree_oid); retval = FOUND; } else { retval = NOT_DIR; diff --git a/tree-walk.h b/tree-walk.h index 4617deeb0e..805f58f00f 100644 --- a/tree-walk.h +++ b/tree-walk.h @@ -64,7 +64,7 @@ enum follow_symlinks_result { */ }; -enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_sha1, const char *name, unsigned char *result, struct strbuf *result_path, unsigned *mode); +enum follow_symlinks_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned *mode); struct traverse_info { const char *traverse_path; diff --git a/unpack-trees.c b/unpack-trees.c index 0f01be6004..3a85a02a77 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -1,5 +1,6 @@ #define NO_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" +#include "argv-array.h" #include "repository.h" #include "config.h" #include "dir.h" @@ -103,6 +104,8 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts, const char **msgs = opts->msgs; const char *msg; + argv_array_init(&opts->msgs_to_free); + if (!strcmp(cmd, "checkout")) msg = advice_commit_before_merge ? _("Your local changes to the following files would be overwritten by checkout:\n%%s" @@ -119,7 +122,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts, "Please commit your changes or stash them before you %s.") : _("Your local changes to the following files would be overwritten by %s:\n%%s"); msgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] = - xstrfmt(msg, cmd, cmd); + argv_array_pushf(&opts->msgs_to_free, msg, cmd, cmd); msgs[ERROR_NOT_UPTODATE_DIR] = _("Updating the following directories would lose untracked files in them:\n%s"); @@ -139,7 +142,8 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts, ? _("The following untracked working tree files would be removed by %s:\n%%s" "Please move or remove them before you %s.") : _("The following untracked working tree files would be removed by %s:\n%%s"); - msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = xstrfmt(msg, cmd, cmd); + msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = + argv_array_pushf(&opts->msgs_to_free, msg, cmd, cmd); if (!strcmp(cmd, "checkout")) msg = advice_commit_before_merge @@ -156,7 +160,8 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts, ? _("The following untracked working tree files would be overwritten by %s:\n%%s" "Please move or remove them before you %s.") : _("The following untracked working tree files would be overwritten by %s:\n%%s"); - msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = xstrfmt(msg, cmd, cmd); + msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = + argv_array_pushf(&opts->msgs_to_free, msg, cmd, cmd); /* * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we @@ -179,6 +184,12 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts, opts->unpack_rejects[i].strdup_strings = 1; } +void clear_unpack_trees_porcelain(struct unpack_trees_options *opts) +{ + argv_array_clear(&opts->msgs_to_free); + memset(opts->msgs, 0, sizeof(opts->msgs)); +} + static int do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce, unsigned int set, unsigned int clear) { @@ -398,7 +409,7 @@ static int check_updates(struct unpack_trees_options *o) if (ce->ce_flags & CE_UPDATE) { if (ce->ce_flags & CE_WT_REMOVE) - die("BUG: both update and delete flags are set on %s", + BUG("both update and delete flags are set on %s", ce->name); display_progress(progress, ++cnt); ce->ce_flags &= ~CE_UPDATE; @@ -1298,7 +1309,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options } else { o->result.split_index = init_split_index(&o->result); } - hashcpy(o->result.sha1, o->src_index->sha1); + oidcpy(&o->result.oid, &o->src_index->oid); o->merge_size = len; mark_all_ce_unused(o->src_index); diff --git a/unpack-trees.h b/unpack-trees.h index 41178ada94..c2b434c606 100644 --- a/unpack-trees.h +++ b/unpack-trees.h @@ -2,7 +2,7 @@ #define UNPACK_TREES_H #include "tree-walk.h" -#include "string-list.h" +#include "argv-array.h" #define MAX_UNPACK_TREES 8 @@ -33,6 +33,11 @@ enum unpack_trees_error_types { void setup_unpack_trees_porcelain(struct unpack_trees_options *opts, const char *cmd); +/* + * Frees resources allocated by setup_unpack_trees_porcelain(). + */ +void clear_unpack_trees_porcelain(struct unpack_trees_options *opts); + struct unpack_trees_options { unsigned int reset, merge, @@ -57,6 +62,7 @@ struct unpack_trees_options { struct pathspec *pathspec; merge_fn_t fn; const char *msgs[NB_UNPACK_TREES_ERROR_TYPES]; + struct argv_array msgs_to_free; /* * Store error messages in an array, each case * corresponding to a error message type diff --git a/upload-pack.c b/upload-pack.c index 87b4d32a6e..87c6722ea5 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -444,7 +444,7 @@ static int get_common_commits(void) break; default: got_common = 1; - memcpy(last_hex, oid_to_hex(&oid), 41); + oid_to_hex_r(last_hex, &oid); if (multi_ack == 2) packet_write_fmt(1, "ACK %s common\n", last_hex); else if (multi_ack) @@ -486,7 +486,7 @@ static int do_reachable_revlist(struct child_process *cmd, "rev-list", "--stdin", NULL, }; struct object *o; - char namebuf[42]; /* ^ + SHA-1 + LF */ + char namebuf[GIT_MAX_HEXSZ + 2]; /* ^ + hash + LF */ int i; cmd->argv = argv; @@ -555,15 +555,17 @@ static int get_reachable_list(struct object_array *src, struct child_process cmd = CHILD_PROCESS_INIT; int i; struct object *o; - char namebuf[42]; /* ^ + SHA-1 + LF */ + char namebuf[GIT_MAX_HEXSZ + 2]; /* ^ + hash + LF */ + const unsigned hexsz = the_hash_algo->hexsz; if (do_reachable_revlist(&cmd, src, reachable) < 0) return -1; - while ((i = read_in_full(cmd.out, namebuf, 41)) == 41) { + while ((i = read_in_full(cmd.out, namebuf, hexsz + 1)) == hexsz + 1) { struct object_id sha1; + const char *p; - if (namebuf[40] != '\n' || get_oid_hex(namebuf, &sha1)) + if (parse_oid_hex(namebuf, &sha1, &p) || *p != '\n') break; o = lookup_object(sha1.hash); @@ -895,11 +897,9 @@ static void receive_needs(void) } if (!skip_prefix(line, "want ", &arg) || - get_oid_hex(arg, &oid_buf)) + parse_oid_hex(arg, &oid_buf, &features)) die("git upload-pack: protocol error, " - "expected to get sha, not '%s'", line); - - features = arg + 40; + "expected to get object ID, not '%s'", line); if (parse_feature_request(features, "deepen-relative")) deepen_relative = 1; @@ -1205,6 +1205,7 @@ static void process_args(struct packet_reader *request, { while (packet_reader_read(request) != PACKET_READ_FLUSH) { const char *arg = request->line; + const char *p; /* process want */ if (parse_want(arg)) @@ -1251,8 +1252,13 @@ static void process_args(struct packet_reader *request, continue; } + if (allow_filter && skip_prefix(arg, "filter ", &p)) { + parse_list_objects_filter(&filter_options, p); + continue; + } + /* ignore unknown lines maybe? */ - die("unexpect line: '%s'", arg); + die("unexpected line: '%s'", arg); } } @@ -1376,6 +1382,8 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys, enum fetch_state state = FETCH_PROCESS_ARGS; struct upload_pack_data data; + git_config(upload_pack_config, NULL); + upload_pack_data_init(&data); use_sideband = LARGE_PACKET_MAX; @@ -1428,7 +1436,14 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys, int upload_pack_advertise(struct repository *r, struct strbuf *value) { - if (value) + if (value) { + int allow_filter_value; strbuf_addstr(value, "shallow"); + if (!repo_config_get_bool(the_repository, + "uploadpack.allowfilter", + &allow_filter_value) && + allow_filter_value) + strbuf_addstr(value, " filter"); + } return 1; } @@ -148,6 +148,7 @@ static const char *fmt_with_err(char *buf, int n, const char *fmt) } } str_error[j] = 0; + /* Truncation is acceptable here */ snprintf(buf, n, "%s: %s", fmt, str_error); return buf; } @@ -210,6 +211,9 @@ void warning(const char *warn, ...) va_end(params); } +/* Only set this, ever, from t/helper/, when verifying that bugs are caught. */ +int BUG_exit_code; + static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_list params) { char prefix[256]; @@ -221,6 +225,8 @@ static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_lis snprintf(prefix, sizeof(prefix), "BUG: "); vreportf(prefix, fmt, params); + if (BUG_exit_code) + exit(BUG_exit_code); abort(); } @@ -681,28 +681,33 @@ static ucs_char_t next_hfs_char(const char **in) } } -int is_hfs_dotgit(const char *path) +static int is_hfs_dot_generic(const char *path, + const char *needle, size_t needle_len) { ucs_char_t c; c = next_hfs_char(&path); if (c != '.') return 0; - c = next_hfs_char(&path); /* * there's a great deal of other case-folding that occurs - * in HFS+, but this is enough to catch anything that will - * convert to ".git" + * in HFS+, but this is enough to catch our fairly vanilla + * hard-coded needles. */ - if (c != 'g' && c != 'G') - return 0; - c = next_hfs_char(&path); - if (c != 'i' && c != 'I') - return 0; - c = next_hfs_char(&path); - if (c != 't' && c != 'T') - return 0; + for (; needle_len > 0; needle++, needle_len--) { + c = next_hfs_char(&path); + + /* + * We know our needles contain only ASCII, so we clamp here to + * make the results of tolower() sane. + */ + if (c > 127) + return 0; + if (tolower(c) != *needle) + return 0; + } + c = next_hfs_char(&path); if (c && !is_dir_sep(c)) return 0; @@ -710,6 +715,35 @@ int is_hfs_dotgit(const char *path) return 1; } +/* + * Inline wrapper to make sure the compiler resolves strlen() on literals at + * compile time. + */ +static inline int is_hfs_dot_str(const char *path, const char *needle) +{ + return is_hfs_dot_generic(path, needle, strlen(needle)); +} + +int is_hfs_dotgit(const char *path) +{ + return is_hfs_dot_str(path, "git"); +} + +int is_hfs_dotgitmodules(const char *path) +{ + return is_hfs_dot_str(path, "gitmodules"); +} + +int is_hfs_dotgitignore(const char *path) +{ + return is_hfs_dot_str(path, "gitignore"); +} + +int is_hfs_dotgitattributes(const char *path) +{ + return is_hfs_dot_str(path, "gitattributes"); +} + const char utf8_bom[] = "\357\273\277"; int skip_utf8_bom(char **text, size_t len) @@ -52,8 +52,13 @@ int mbs_chrlen(const char **text, size_t *remainder_p, const char *encoding); * The path should be NUL-terminated, but we will match variants of both ".git\0" * and ".git/..." (but _not_ ".../.git"). This makes it suitable for both fsck * and verify_path(). + * + * Likewise, the is_hfs_dotgitfoo() variants look for ".gitfoo". */ int is_hfs_dotgit(const char *path); +int is_hfs_dotgitmodules(const char *path); +int is_hfs_dotgitignore(const char *path); +int is_hfs_dotgitattributes(const char *path); typedef enum { ALIGN_LEFT, diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c index 3fd047a8b8..b5b8913cb0 100644 --- a/vcs-svn/fast_export.c +++ b/vcs-svn/fast_export.c @@ -320,7 +320,8 @@ const char *fast_export_read_path(const char *path, uint32_t *mode_out) err = fast_export_ls(path, mode_out, &buf); if (err) { if (errno != ENOENT) - die_errno("BUG: unexpected fast_export_ls error"); + BUG("unexpected fast_export_ls error: %s", + strerror(errno)); /* Treat missing paths as directories. */ *mode_out = S_IFDIR; return NULL; @@ -338,7 +339,8 @@ void fast_export_copy(uint32_t revision, const char *src, const char *dst) err = fast_export_ls_rev(revision, src, &mode, &data); if (err) { if (errno != ENOENT) - die_errno("BUG: unexpected fast_export_ls_rev error"); + BUG("unexpected fast_export_ls_rev error: %s", + strerror(errno)); fast_export_delete(dst); return; } diff --git a/worktree.c b/worktree.c index 28989cf06e..97cda5f97b 100644 --- a/worktree.c +++ b/worktree.c @@ -338,7 +338,7 @@ void update_worktree_location(struct worktree *wt, const char *path_) struct strbuf path = STRBUF_INIT; if (is_main_worktree(wt)) - die("BUG: can't relocate main worktree"); + BUG("can't relocate main worktree"); strbuf_realpath(&path, path_, 1); if (fspathcmp(wt->path, path.buf)) { @@ -643,9 +643,9 @@ int xsnprintf(char *dst, size_t max, const char *fmt, ...) va_end(ap); if (len < 0) - die("BUG: your snprintf is broken"); + BUG("your snprintf is broken"); if (len >= max) - die("BUG: attempt to snprintf into too-small buffer"); + BUG("attempt to snprintf into too-small buffer"); return len; } diff --git a/wt-status.c b/wt-status.c index 32f3bcaebd..d1c05145a4 100644 --- a/wt-status.c +++ b/wt-status.c @@ -138,6 +138,9 @@ void wt_status_prepare(struct wt_status *s) s->show_stash = 0; s->ahead_behind_flags = AHEAD_BEHIND_UNSPECIFIED; s->display_comment_prefix = 0; + s->detect_rename = -1; + s->rename_score = -1; + s->rename_limit = -1; } static void wt_longstatus_print_unmerged_header(struct wt_status *s) @@ -264,7 +267,7 @@ static const char *wt_status_unmerged_status_string(int stagemask) case 7: return _("both modified:"); default: - die("BUG: unhandled unmerged status %x", stagemask); + BUG("unhandled unmerged status %x", stagemask); } } @@ -377,7 +380,7 @@ static void wt_longstatus_print_change_data(struct wt_status *s, status = d->worktree_status; break; default: - die("BUG: unhandled change_type %d in wt_longstatus_print_change_data", + BUG("unhandled change_type %d in wt_longstatus_print_change_data", change_type); } @@ -395,7 +398,7 @@ static void wt_longstatus_print_change_data(struct wt_status *s, status_printf(s, color(WT_STATUS_HEADER, s), "\t"); what = wt_status_diff_status_string(status); if (!what) - die("BUG: unhandled diff status %c", status); + BUG("unhandled diff status %c", status); len = label_width - utf8_strwidth(what); assert(len >= 0); if (one_name != two_name) @@ -470,7 +473,7 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q, case DIFF_STATUS_COPIED: case DIFF_STATUS_RENAMED: if (d->rename_status) - die("BUG: multiple renames on the same target? how?"); + BUG("multiple renames on the same target? how?"); d->rename_source = xstrdup(p->one->path); d->rename_score = p->score * 100 / MAX_SCORE; d->rename_status = p->status; @@ -484,7 +487,7 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q, break; default: - die("BUG: unhandled diff-files status '%c'", p->status); + BUG("unhandled diff-files status '%c'", p->status); break; } @@ -547,7 +550,7 @@ static void wt_status_collect_updated_cb(struct diff_queue_struct *q, case DIFF_STATUS_COPIED: case DIFF_STATUS_RENAMED: if (d->rename_status) - die("BUG: multiple renames on the same target? how?"); + BUG("multiple renames on the same target? how?"); d->rename_source = xstrdup(p->one->path); d->rename_score = p->score * 100 / MAX_SCORE; d->rename_status = p->status; @@ -569,7 +572,7 @@ static void wt_status_collect_updated_cb(struct diff_queue_struct *q, break; default: - die("BUG: unhandled diff-index status '%c'", p->status); + BUG("unhandled diff-index status '%c'", p->status); break; } } @@ -592,6 +595,9 @@ static void wt_status_collect_changes_worktree(struct wt_status *s) } rev.diffopt.format_callback = wt_status_collect_changed_cb; rev.diffopt.format_callback_data = s; + rev.diffopt.detect_rename = s->detect_rename >= 0 ? s->detect_rename : rev.diffopt.detect_rename; + rev.diffopt.rename_limit = s->rename_limit >= 0 ? s->rename_limit : rev.diffopt.rename_limit; + rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score; copy_pathspec(&rev.prune_data, &s->pathspec); run_diff_files(&rev, 0); } @@ -603,7 +609,7 @@ static void wt_status_collect_changes_index(struct wt_status *s) init_revisions(&rev, NULL); memset(&opt, 0, sizeof(opt)); - opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference; + opt.def = s->is_initial ? empty_tree_oid_hex() : s->reference; setup_revisions(0, NULL, &rev, &opt); rev.diffopt.flags.override_submodule_config = 1; @@ -625,6 +631,9 @@ static void wt_status_collect_changes_index(struct wt_status *s) rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = wt_status_collect_updated_cb; rev.diffopt.format_callback_data = s; + rev.diffopt.detect_rename = s->detect_rename >= 0 ? s->detect_rename : rev.diffopt.detect_rename; + rev.diffopt.rename_limit = s->rename_limit >= 0 ? s->rename_limit : rev.diffopt.rename_limit; + rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score; copy_pathspec(&rev.prune_data, &s->pathspec); run_diff_index(&rev, 1); } @@ -978,10 +987,13 @@ static void wt_longstatus_print_verbose(struct wt_status *s) rev.diffopt.ita_invisible_in_index = 1; memset(&opt, 0, sizeof(opt)); - opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference; + opt.def = s->is_initial ? empty_tree_oid_hex() : s->reference; setup_revisions(0, NULL, &rev, &opt); rev.diffopt.output_format |= DIFF_FORMAT_PATCH; + rev.diffopt.detect_rename = s->detect_rename >= 0 ? s->detect_rename : rev.diffopt.detect_rename; + rev.diffopt.rename_limit = s->rename_limit >= 0 ? s->rename_limit : rev.diffopt.rename_limit; + rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score; rev.diffopt.file = s->fp; rev.diffopt.close_file = 0; /* @@ -2154,7 +2166,7 @@ static void wt_porcelain_v2_print_unmerged_entry( case 6: key = "AA"; break; /* both added */ case 7: key = "UU"; break; /* both modified */ default: - die("BUG: unhandled unmerged status %x", d->stagemask); + BUG("unhandled unmerged status %x", d->stagemask); } /* @@ -2181,7 +2193,7 @@ static void wt_porcelain_v2_print_unmerged_entry( sum |= (1 << (stage - 1)); } if (sum != d->stagemask) - die("BUG: observed stagemask 0x%x != expected stagemask 0x%x", sum, d->stagemask); + BUG("observed stagemask 0x%x != expected stagemask 0x%x", sum, d->stagemask); if (s->null_termination) path_index = it->string; @@ -2285,7 +2297,7 @@ void wt_status_print(struct wt_status *s) wt_porcelain_v2_print(s); break; case STATUS_FORMAT_UNSPECIFIED: - die("BUG: finalize_deferred_config() should have been called"); + BUG("finalize_deferred_config() should have been called"); break; case STATUS_FORMAT_NONE: case STATUS_FORMAT_LONG: diff --git a/wt-status.h b/wt-status.h index 430770b854..1673d146fa 100644 --- a/wt-status.h +++ b/wt-status.h @@ -89,7 +89,9 @@ struct wt_status { int show_stash; int hints; enum ahead_behind_flags ahead_behind_flags; - + int detect_rename; + int rename_score; + int rename_limit; enum wt_status_format status_format; unsigned char sha1_commit[GIT_MAX_RAWSZ]; /* when not Initial */ @@ -52,9 +52,9 @@ static void zlib_post_call(git_zstream *s) bytes_consumed = s->z.next_in - s->next_in; bytes_produced = s->z.next_out - s->next_out; if (s->z.total_out != s->total_out + bytes_produced) - die("BUG: total_out mismatch"); + BUG("total_out mismatch"); if (s->z.total_in != s->total_in + bytes_consumed) - die("BUG: total_in mismatch"); + BUG("total_in mismatch"); s->total_out = s->z.total_out; s->total_in = s->z.total_in; |
