diff options
313 files changed, 5063 insertions, 3047 deletions
diff --git a/.gitignore b/.gitignore index 66199edd4a..b5f9defed3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /GIT-CFLAGS /GIT-LDFLAGS /GIT-PREFIX +/GIT-PERL-DEFINES /GIT-PYTHON-VARS /GIT-SCRIPT-DEFINES /GIT-USER-AGENT @@ -75,7 +76,6 @@ /git-init-db /git-instaweb /git-log -/git-lost-found /git-ls-files /git-ls-remote /git-ls-tree @@ -105,7 +105,6 @@ /git-pack-refs /git-parse-remote /git-patch-id -/git-peek-remote /git-prune /git-prune-packed /git-pull @@ -131,7 +130,6 @@ /git-remote-testsvn /git-repack /git-replace -/git-repo-config /git-request-pull /git-rerere /git-reset @@ -159,7 +157,6 @@ /git-svn /git-symbolic-ref /git-tag -/git-tar-tree /git-unpack-file /git-unpack-objects /git-update-index diff --git a/Documentation/RelNotes/1.9.txt b/Documentation/RelNotes/1.9.txt new file mode 100644 index 0000000000..a966ab4887 --- /dev/null +++ b/Documentation/RelNotes/1.9.txt @@ -0,0 +1,186 @@ +Git v1.9 Release Notes +====================== + +Backward compatibility notes +---------------------------- + +"git submodule foreach $cmd $args" used to treat "$cmd $args" the same +way "ssh" did, concatenating them into a single string and letting the +shell unquote. Careless users who forget to sufficiently quote $args +gets their argument split at $IFS whitespaces by the shell, and got +unexpected results due to this. Starting from this release, the +command line is passed directly to the shell, if it has an argument. + +Read-only support for experimental loose-object format, in which users +could optionally choose to write in their loose objects for a short +while between v1.4.3 to v1.5.3 era, has been dropped. + +The meanings of "--tags" option to "git fetch" has changed; the +command fetches tags _in addition to_ what are fetched by the same +command line without the option. + +A handful of ancient commands that have long been deprecated are +finally gone (repo-config, tar-tree, lost-found, and peek-remote). + + +Backward compatibility notes (for Git 2.0) +------------------------------------------ + +When "git push [$there]" does not say what to push, we have used the +traditional "matching" semantics so far (all your branches were sent +to the remote as long as there already are branches of the same name +over there). In Git 2.0, the default will change to the "simple" +semantics, which pushes: + + - only the current branch to the branch with the same name, and only + when the current branch is set to integrate with that remote + branch, if you are pushing to the same remote as you fetch from; or + + - only the current branch to the branch with the same name, if you + are pushing to a remote that is not where you usually fetch from. + +Use the user preference configuration variable "push.default" to +change this. If you are an old-timer who is used to the "matching" +semantics, you can set the variable to "matching" to keep the +traditional behaviour. If you want to live in the future early, you +can set it to "simple" today without waiting for Git 2.0. + +When "git add -u" (and "git add -A") is run inside a subdirectory and +does not specify which paths to add on the command line, it +will operate on the entire tree in Git 2.0 for consistency +with "git commit -a" and other commands. There will be no +mechanism to make plain "git add -u" behave like "git add -u .". +Current users of "git add -u" (without a pathspec) should start +training their fingers to explicitly say "git add -u ." +before Git 2.0 comes. A warning is issued when these commands are +run without a pathspec and when you have local changes outside the +current directory, because the behaviour in Git 2.0 will be different +from today's version in such a situation. + +In Git 2.0, "git add <path>" will behave as "git add -A <path>", so +that "git add dir/" will notice paths you removed from the directory +and record the removal. Versions before Git 2.0, including this +release, will keep ignoring removals, but the users who rely on this +behaviour are encouraged to start using "git add --ignore-removal <path>" +now before 2.0 is released. + +The default prefix for "git svn" will change in Git 2.0. For a long +time, "git svn" created its remote-tracking branches directly under +refs/remotes, but it will place them under refs/remotes/origin/ unless +it is told otherwise with its --prefix option. + + +Updates since v1.8.5 +-------------------- + +Foreign interfaces, subsystems and ports. + + * The HTTP transport, when talking GSS-Negotiate, uses "100 + Continue" response to avoid having to rewind and resend a large + payload, which may not be always doable. + + * Various bugfixes to remote-bzr and remote-hg (in contrib/). + + +UI, Workflows & Features + + * "gitweb" learned to treat ref hierarchies other than refs/heads as + if they are additional branch namespaces (e.g. refs/changes/ in + Gerrit). + + * "git for-each-ref --format=..." learned a few formatting directives; + e.g. "%(color:red)%(HEAD)%(color:reset) %(refname:short) %(subject)". + + * The command string given to "git submodule foreach" is passed + directly to the shell, without being eval'ed. This is a backward + incompatible change that may break existing users. + + * "git log" and friends learned the "--exclude=<glob>" option, to + allow people to say "list history of all branches except those that + match this pattern" with "git log --exclude='*/*' --branches". + + * "git rev-parse --parseopt" learned a new "--stuck-long" option to + help scripts parse options with an optional parameter. + + * The "--tags" option to "git fetch" no longer tells the command to + fetch _only_ the tags. It instead fetches tags _in addition to_ + what are fetched by the same command line without the option. + + +Performance, Internal Implementation, etc. + + * The deprecated parse-options macro OPT_BOOLEAN has been removed; + use OPT_BOOL or OPT_COUNTUP in new code. + + * A few duplicate implementations of prefix/suffix string comparison + functions have been unified to starts_with() and ends_with(). + + * The new PERLLIB_EXTRA makefile variable can be used to specify + additional directories Perl modules (e.g. the ones necessary to run + git-svn) are installed on the platform when building. + + * "git merge-base" learned the "--fork-point" mode, that implements + the same logic used in "git pull --rebase" to find a suitable fork + point out of the reflog entries for the remote-tracking branch the + work has been based on. + + * A third-party "receive-pack" (the responder to "git push") can + advertise the "no-thin" capability to tell "git push" not to use + the thin-pack optimization. Our receive-pack has always been + capable of accepting and fattening a thin-pack, and will continue + not to ask "git push" to use a non-thin pack. + + +Also contains various documentation updates and code clean-ups. + + +Fixes since v1.8.5 +------------------ + +Unless otherwise noted, all the fixes since v1.8.5 in the maintenance +track are contained in this release (see the maintenance releases' notes +for details). + + * "git mv A B/", when B does not exist as a directory, should error + out, but it didn't. + (merge c57f628 mm/mv-file-to-no-such-dir-with-slash later to maint). + + * A workaround to an old bug in glibc prior to glibc 2.17 has been + retired; this would remove a side effect of the workaround that + corrupts system error messages in non-C locales. + + * SSL-related options were not passed correctly to underlying socket + layer in "git send-email". + (merge 5508f3e tr/send-email-ssl later to maint). + + * "git commit -v" appends the patch to the log message before + editing, and then removes the patch when the editor returned + control. However, the patch was not stripped correctly when the + first modified path was a submodule. + (merge 1a72cfd jl/commit-v-strip-marker later to maint). + + * "git fetch --depth=0" was a no-op, and was silently ignored. + Diagnose it as an error. + (merge 5594bca nd/transport-positive-depth-only later to maint). + + * Remote repository URL expressed in scp-style host:path notation are + parsed more carefully (e.g. "foo/bar:baz" is local, "[::1]:/~user" asks + to connect to user's home directory on host at address ::1. + (merge a2036d7 tb/clone-ssh-with-colon-for-port later to maint). + + * "git diff -- ':(icase)makefile'" was unnecessarily rejected at the + command line parser. + (merge 887c6c1 nd/magic-pathspec later to maint). + + * "git cat-file --batch-check=ok" did not check the existence of + the named object. + (merge 4ef8d1d sb/sha1-loose-object-info-check-existence later to maint). + + * "git am --abort" sometimes complained about not being able to write + a tree with an 0{40} object in it. + (merge 77b43ca jk/two-way-merge-corner-case-fix later to maint). + + * Two processes creating loose objects at the same time could have + failed unnecessarily when the name of their new objects started + with the same byte value, due to a race condition. + (merge b2476a6 jh/loose-object-dirs-creation-race later to maint). diff --git a/Documentation/config.txt b/Documentation/config.txt index ab26963d61..a4058063ce 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -2087,8 +2087,8 @@ remote.<name>.vcs:: remote.<name>.prune:: When set to true, fetching from this remote by default will also - remove any remote-tracking branches which no longer exist on the - remote (as if the `--prune` option was give on the command line). + remove any remote-tracking references that no longer exist on the + remote (as if the `--prune` option was given on the command line). Overrides `fetch.prune` settings, if any. remotes.<group>:: diff --git a/Documentation/diff-config.txt b/Documentation/diff-config.txt index 223b9310df..f07b4513ed 100644 --- a/Documentation/diff-config.txt +++ b/Documentation/diff-config.txt @@ -98,6 +98,11 @@ diff.mnemonicprefix:: diff.noprefix:: If set, 'git diff' does not show any source or destination prefix. +diff.orderfile:: + File indicating how to order files within a diff, using + one shell glob pattern per line. + Can be overridden by the '-O' option to linkgit:git-diff[1]. + diff.renameLimit:: The number of files to consider when performing the copy/rename detection; equivalent to the 'git diff' option '-l'. diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index bbed2cd79c..9b37b2a10b 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -432,6 +432,9 @@ endif::git-format-patch[] -O<orderfile>:: Output the patch in the order specified in the <orderfile>, which has one shell glob pattern per line. + This overrides the `diff.orderfile` configuration variable + (see linkgit:git-config[1]). To cancel `diff.orderfile`, + use `-O/dev/null`. ifndef::git-format-patch[] -R:: diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index ba1fe49582..f0ef7d02a5 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -41,17 +41,20 @@ ifndef::git-pull[] -p:: --prune:: - After fetching, remove any remote-tracking branches which - no longer exist on the remote. + After fetching, remove any remote-tracking references that no + longer exist on the remote. Tags are not subject to pruning + if they are fetched only because of the default tag + auto-following or due to a --tags option. However, if tags + are fetched due to an explicit refspec (either on the command + line or in the remote configuration, for example if the remote + was cloned with the --mirror option), then they are also + subject to pruning. endif::git-pull[] -ifdef::git-pull[] ---no-tags:: -endif::git-pull[] ifndef::git-pull[] -n:: ---no-tags:: endif::git-pull[] +--no-tags:: By default, tags that point at objects that are downloaded from the remote repository are fetched and stored locally. This option disables this automatic tag following. The default @@ -61,11 +64,12 @@ endif::git-pull[] ifndef::git-pull[] -t:: --tags:: - This is a short-hand for giving `refs/tags/*:refs/tags/*` - refspec from the command line, to ask all tags to be fetched - and stored locally. Because this acts as an explicit - refspec, the default refspecs (configured with the - remote.$name.fetch variable) are overridden and not used. + Fetch all tags from the remote (i.e., fetch remote tags + `refs/tags/*` into local tags with the same name), in addition + to whatever else would otherwise be fetched. Using this + option alone does not subject tags to pruning, even if --prune + is used (though tags may be pruned anyway if they are also the + destination of an explicit refspec; see '--prune'). --recurse-submodules[=yes|on-demand|no]:: This option controls if and under what conditions new commits of diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt index 322f5ed315..f6a16f4300 100644 --- a/Documentation/git-cat-file.txt +++ b/Documentation/git-cat-file.txt @@ -109,6 +109,11 @@ newline. The available atoms are: The size, in bytes, that the object takes up on disk. See the note about on-disk sizes in the `CAVEATS` section below. +`deltabase`:: + If the object is stored as a delta on-disk, this expands to the + 40-hex sha1 of the delta base object. Otherwise, expands to the + null sha1 (40 zeroes). See `CAVEATS` below. + `rest`:: If this atom is used in the output string, input lines are split at the first whitespace boundary. All characters before that @@ -152,10 +157,11 @@ should be taken in drawing conclusions about which refs or objects are responsible for disk usage. The size of a packed non-delta object may be much larger than the size of objects which delta against it, but the choice of which object is the base and which is the delta is arbitrary -and is subject to change during a repack. Note also that multiple copies -of an object may be present in the object database; in this case, it is -undefined which copy's size will be reported. +and is subject to change during a repack. +Note also that multiple copies of an object may be present in the object +database; in this case, it is undefined which copy's size or delta base +will be reported. GIT --- diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt index e08a028946..5809aa4eb9 100644 --- a/Documentation/git-fetch.txt +++ b/Documentation/git-fetch.txt @@ -24,19 +24,22 @@ The ref names and their object names of fetched refs are stored in `.git/FETCH_HEAD`. This information is left for a later merge operation done by 'git merge'. -When <refspec> stores the fetched result in remote-tracking branches, -the tags that point at these branches are automatically -followed. This is done by first fetching from the remote using -the given <refspec>s, and if the repository has objects that are -pointed by remote tags that it does not yet have, then fetch -those missing tags. If the other end has tags that point at -branches you are not interested in, you will not get them. +By default, tags are auto-followed. This means that when fetching +from a remote, any tags on the remote that point to objects that exist +in the local repository are fetched. The effect is to fetch tags that +point at branches that you are interested in. This default behavior +can be changed by using the --tags or --no-tags options, by +configuring remote.<name>.tagopt, or by using a refspec that fetches +tags explicitly. 'git fetch' can fetch from either a single named repository, or from several repositories at once if <group> is given and there is a remotes.<group> entry in the configuration file. (See linkgit:git-config[1]). +When no remote is specified, by default the `origin` remote will be used, +unless there's an upstream branch configured for the current branch. + OPTIONS ------- include::fetch-options.txt[] diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index e4c8e82660..2eba627170 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -393,7 +393,7 @@ git filter-branch --index-filter \ Checklist for Shrinking a Repository ------------------------------------ -git-filter-branch is often used to get rid of a subset of files, +git-filter-branch can be used to get rid of a subset of files, usually with some combination of `--index-filter` and `--subdirectory-filter`. People expect the resulting repository to be smaller than the original, but you need a few more steps to @@ -429,6 +429,37 @@ warned. (or if your git-gc is not new enough to support arguments to `--prune`, use `git repack -ad; git prune` instead). +Notes +----- + +git-filter-branch allows you to make complex shell-scripted rewrites +of your Git history, but you probably don't need this flexibility if +you're simply _removing unwanted data_ like large files or passwords. +For those operations you may want to consider +link:http://rtyley.github.io/bfg-repo-cleaner/[The BFG Repo-Cleaner], +a JVM-based alternative to git-filter-branch, typically at least +10-50x faster for those use-cases, and with quite different +characteristics: + +* Any particular version of a file is cleaned exactly _once_. The BFG, + unlike git-filter-branch, does not give you the opportunity to + handle a file differently based on where or when it was committed + within your history. This constraint gives the core performance + benefit of The BFG, and is well-suited to the task of cleansing bad + data - you don't care _where_ the bad data is, you just want it + _gone_. + +* By default The BFG takes full advantage of multi-core machines, + cleansing commit file-trees in parallel. git-filter-branch cleans + commits sequentially (ie in a single-threaded manner), though it + _is_ possible to write filters that include their own parallellism, + in the scripts executed against each commit. + +* The link:http://rtyley.github.io/bfg-repo-cleaner/#examples[command options] + are much more restrictive than git-filter branch, and dedicated just + to the tasks of removing unwanted data- e.g: + `--strip-blobs-bigger-than 1M`. + GIT --- Part of the linkgit:git[1] suite diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index f2e08d11c1..94f5c465da 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -91,7 +91,19 @@ objectname:: upstream:: The name of a local ref which can be considered ``upstream'' from the displayed ref. Respects `:short` in the same way as - `refname` above. + `refname` above. Additionally respects `:track` to show + "[ahead N, behind M]" and `:trackshort` to show the terse + version: ">" (ahead), "<" (behind), "<>" (ahead and behind), + or "=" (in sync). Has no effect if the ref does not have + tracking information associated with it. + +HEAD:: + '*' if HEAD matches current ref (the checked out branch), ' ' + otherwise. + +color:: + Change output color. Followed by `:<colorname>`, where names + are described in `color.branch.*`. In addition to the above, for commit and tag objects, the header field names (`tree`, `parent`, `object`, `type`, and `tag`) can diff --git a/Documentation/git-lost-found.txt b/Documentation/git-lost-found.txt deleted file mode 100644 index d54932889f..0000000000 --- a/Documentation/git-lost-found.txt +++ /dev/null @@ -1,74 +0,0 @@ -git-lost-found(1) -================= - -NAME ----- -git-lost-found - Recover lost refs that luckily have not yet been pruned - -SYNOPSIS --------- -[verse] -'git lost-found' - -DESCRIPTION ------------ - -*NOTE*: this command is deprecated. Use linkgit:git-fsck[1] with -the option '--lost-found' instead. - -Finds dangling commits and tags from the object database, and -creates refs to them in the .git/lost-found/ directory. Commits and -tags that dereference to commits are stored in .git/lost-found/commit, -and other objects are stored in .git/lost-found/other. - - -OUTPUT ------- -Prints to standard output the object names and one-line descriptions -of any commits or tags found. - -EXAMPLE -------- - -Suppose you run 'git tag -f' and mistype the tag to overwrite. -The ref to your tag is overwritten, but until you run 'git -prune', the tag itself is still there. - ------------- -$ git lost-found -[1ef2b196d909eed523d4f3c9bf54b78cdd6843c6] GIT 0.99.9c -... ------------- - -Also you can use gitk to browse how any tags found relate to each -other. - ------------- -$ gitk $(cd .git/lost-found/commit && echo ??*) ------------- - -After making sure you know which the object is the tag you are looking -for, you can reconnect it to your regular `refs` hierarchy by using -the `update-ref` command. - ------------- -$ git cat-file -t 1ef2b196 -tag -$ git cat-file tag 1ef2b196 -object fa41bbce8e38c67a218415de6cfa510c7e50032a -type commit -tag v0.99.9c -tagger Junio C Hamano <junkio@cox.net> 1131059594 -0800 - -GIT 0.99.9c - -This contains the following changes from the "master" branch, since -... -$ git update-ref refs/tags/not-lost-anymore 1ef2b196 -$ git rev-parse not-lost-anymore -1ef2b196d909eed523d4f3c9bf54b78cdd6843c6 ------------- - -GIT ---- -Part of the linkgit:git[1] suite diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt index 87842e33f8..808426faac 100644 --- a/Documentation/git-merge-base.txt +++ b/Documentation/git-merge-base.txt @@ -13,6 +13,7 @@ SYNOPSIS 'git merge-base' [-a|--all] --octopus <commit>... 'git merge-base' --is-ancestor <commit> <commit> 'git merge-base' --independent <commit>... +'git merge-base' --fork-point <ref> [<commit>] DESCRIPTION ----------- @@ -24,8 +25,8 @@ that does not have any better common ancestor is a 'best common ancestor', i.e. a 'merge base'. Note that there can be more than one merge base for a pair of commits. -OPERATION MODE --------------- +OPERATION MODES +--------------- As the most common special case, specifying only two commits on the command line means computing the merge base between the given two commits. @@ -56,6 +57,14 @@ from linkgit:git-show-branch[1] when used with the `--merge-base` option. and exit with status 0 if true, or with status 1 if not. Errors are signaled by a non-zero status that is not 1. +--fork-point:: + Find the point at which a branch (or any history that leads + to <commit>) forked from another branch (or any reference) + <ref>. This does not just look for the common ancestor of + the two commits, but also takes into account the reflog of + <ref> to see if the history leading to <commit> forked from + an earlier incarnation of the branch <ref> (see discussion + on this mode below). OPTIONS ------- @@ -137,6 +146,31 @@ In modern git, you can say this in a more direct way: instead. +Discussion on fork-point mode +----------------------------- + +After working on the `topic` branch created with `git checkout -b +topic origin/master`, the history of remote-tracking branch +`origin/master` may have been rewound and rebuilt, leading to a +history of this shape: + + o---B1 + / + ---o---o---B2--o---o---o---B (origin/master) + \ + B3 + \ + Derived (topic) + +where `origin/master` used to point at commits B3, B2, B1 and now it +points at B, and your `topic` branch was started on top of it back +when `origin/master` was at B3. This mode uses the reflog of +`origin/master` to find B3 as the fork point, so that the `topic` +can be rebased on top of the updated `origin/master` by: + + $ fork_point=$(git merge-base --fork-point origin/master topic) + $ git rebase --onto origin/master $fork_point topic + See also -------- diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt index d94edcd4b4..cdab9ed503 100644 --- a/Documentation/git-pack-objects.txt +++ b/Documentation/git-pack-objects.txt @@ -51,8 +51,7 @@ base-name:: <base-name> to determine the name of the created file. When this option is used, the two files are written in <base-name>-<SHA-1>.{pack,idx} files. <SHA-1> is a hash - of the sorted object names to make the resulting filename - based on the pack content, and written to the standard + based on the pack content and is written to the standard output of the command. --stdout:: diff --git a/Documentation/git-peek-remote.txt b/Documentation/git-peek-remote.txt deleted file mode 100644 index 87ea3fb054..0000000000 --- a/Documentation/git-peek-remote.txt +++ /dev/null @@ -1,43 +0,0 @@ -git-peek-remote(1) -================== - -NAME ----- -git-peek-remote - List the references in a remote repository - - -SYNOPSIS --------- -[verse] -'git peek-remote' [--upload-pack=<git-upload-pack>] [<host>:]<directory> - -DESCRIPTION ------------ -This command is deprecated; use 'git ls-remote' instead. - -OPTIONS -------- ---upload-pack=<git-upload-pack>:: - Use this to specify the path to 'git-upload-pack' on the - remote side, if it is not found on your $PATH. Some - installations of sshd ignores the user's environment - setup scripts for login shells (e.g. .bash_profile) and - your privately installed git may not be found on the system - default $PATH. Another workaround suggested is to set - up your $PATH in ".bashrc", but this flag is for people - who do not want to pay the overhead for non-interactive - shells, but prefer having a lean .bashrc file (they set most of - the things up in .bash_profile). - -<host>:: - A remote host that houses the repository. When this - part is specified, 'git-upload-pack' is invoked via - ssh. - -<directory>:: - The repository to sync from. - - -GIT ---- -Part of the linkgit:git[1] suite diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 9eec740910..2b7f4f939f 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -56,8 +56,13 @@ it can be any arbitrary "SHA-1 expression", such as `master~4` or + The <dst> tells which ref on the remote side is updated with this push. Arbitrary expressions cannot be used here, an actual ref must -be named. If `:`<dst> is omitted, the same ref as <src> will be -updated. +be named. +If `git push [<repository>]` without any `<refspec>` argument is set to +update some ref at the destination with `<src>` with +`remote.<repository>.push` configuration variable, `:<dst>` part can +be omitted---such a push will update a ref that `<src>` normally updates +without any `<refspec>` on the command line. Otherwise, missing +`:<dst>` means to update the same ref as the `<src>`. + The object referenced by <src> is used to update the <dst> reference on the remote side. By default this is only allowed if <dst> is not diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 94e07fdab5..2889be6bdc 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -324,6 +324,16 @@ fresh commits so it can be remerged successfully without needing to "revert the reversion" (see the link:howto/revert-a-faulty-merge.html[revert-a-faulty-merge How-To] for details). +--fork-point:: +--no-fork-point:: + Use 'git merge-base --fork-point' to find a better common ancestor + between `upstream` and `branch` when calculating which commits have + have been introduced by `branch` (see linkgit:git-merge-base[1]). ++ +If no non-option arguments are given on the command line, then the default is +`--fork-point @{u}` otherwise the `upstream` argument is interpreted literally +unless the `--fork-point` option is specified. + --ignore-whitespace:: --whitespace=<option>:: These flag are passed to the 'git apply' program diff --git a/Documentation/git-replace.txt b/Documentation/git-replace.txt index f373ab48d4..0a02f70657 100644 --- a/Documentation/git-replace.txt +++ b/Documentation/git-replace.txt @@ -10,7 +10,7 @@ SYNOPSIS [verse] 'git replace' [-f] <object> <replacement> 'git replace' -d <object>... -'git replace' -l [<pattern>] +'git replace' [--format=<format>] [-l [<pattern>]] DESCRIPTION ----------- @@ -70,6 +70,23 @@ OPTIONS Typing "git replace" without arguments, also lists all replace refs. +--format=<format>:: + When listing, use the specified <format>, which can be one of + 'short', 'medium' and 'long'. When omitted, the format + defaults to 'short'. + +FORMATS +------- + +The following format are available: + +* 'short': + <replaced sha1> +* 'medium': + <replaced sha1> -> <replacement sha1> +* 'long': + <replaced sha1> (<replaced type>) -> <replacement sha1> (<replacement type>) + CREATING REPLACEMENT OBJECTS ---------------------------- diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt deleted file mode 100644 index 9ec115b9e0..0000000000 --- a/Documentation/git-repo-config.txt +++ /dev/null @@ -1,23 +0,0 @@ -git-repo-config(1) -================== - -NAME ----- -git-repo-config - Get and set repository or global options - - -SYNOPSIS --------- -[verse] -'git repo-config' ... - - -DESCRIPTION ------------ - -This is a synonym for linkgit:git-config[1]. Please refer to the -documentation of that command. - -GIT ---- -Part of the linkgit:git[1] suite diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index d068a65377..0d2cdcde55 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -50,6 +50,10 @@ Options for --parseopt the first non-option argument. This can be used to parse sub-commands that take options themselves. +--stuck-long:: + Only meaningful in `--parseopt` mode. Output the options in their + long form if available, and with their arguments stuck. + Options for Filtering ~~~~~~~~~~~~~~~~~~~~~ @@ -173,6 +177,20 @@ shown. If the pattern does not contain a globbing character (`?`, character (`?`, `*`, or `[`), it is turned into a prefix match by appending `/*`. +--exclude=<glob-pattern>:: + Do not include refs matching '<glob-pattern>' that the next `--all`, + `--branches`, `--tags`, `--remotes`, or `--glob` would otherwise + consider. Repetitions of this option accumulate exclusion patterns + up to the next `--all`, `--branches`, `--tags`, `--remotes`, or + `--glob` option (other options or arguments do not clear + accumlated patterns). ++ +The patterns given should not begin with `refs/heads`, `refs/tags`, or +`refs/remotes` when applied to `--branches`, `--tags`, or `--remotes`, +respectively, and they must begin with `refs/` when applied to `--glob` +or `--all`. If a trailing '/{asterisk}' is intended, it must be given +explicitly. + --disambiguate=<prefix>:: Show every object whose name begins with the given prefix. The <prefix> must be at least 4 hexadecimal digits long to @@ -285,7 +303,9 @@ Each line of options has this format: `<flags>` are of `*`, `=`, `?` or `!`. * Use `=` if the option takes an argument. - * Use `?` to mean that the option is optional (though its use is discouraged). + * Use `?` to mean that the option takes an optional argument. You + probably want to use the `--stuck-long` mode to be able to + unambiguously parse the optional argument. * Use `*` to mean that this option should not be listed in the usage generated for the `-h` argument. It's shown for `--help-all` as diff --git a/Documentation/git-tar-tree.txt b/Documentation/git-tar-tree.txt deleted file mode 100644 index f7362dc2d1..0000000000 --- a/Documentation/git-tar-tree.txt +++ /dev/null @@ -1,82 +0,0 @@ -git-tar-tree(1) -=============== - -NAME ----- -git-tar-tree - Create a tar archive of the files in the named tree object - - -SYNOPSIS --------- -[verse] -'git tar-tree' [--remote=<repo>] <tree-ish> [ <base> ] - -DESCRIPTION ------------ -THIS COMMAND IS DEPRECATED. Use 'git archive' with `--format=tar` -option instead (and move the <base> argument to `--prefix=base/`). - -Creates a tar archive containing the tree structure for the named tree. -When <base> is specified it is added as a leading path to the files in the -generated tar archive. - -'git tar-tree' behaves differently when given a tree ID versus when given -a commit ID or tag ID. In the first case the current time is used as -modification time of each file in the archive. In the latter case the -commit time as recorded in the referenced commit object is used instead. -Additionally the commit ID is stored in a global extended pax header. -It can be extracted using 'git get-tar-commit-id'. - -OPTIONS -------- - -<tree-ish>:: - The tree or commit to produce tar archive for. If it is - the object name of a commit object. - -<base>:: - Leading path to the files in the resulting tar archive. - ---remote=<repo>:: - Instead of making a tar archive from local repository, - retrieve a tar archive from a remote repository. - -CONFIGURATION -------------- - -tar.umask:: - This variable can be used to restrict the permission bits of - tar archive entries. The default is 0002, which turns off the - world write bit. The special value "user" indicates that the - archiving user's umask will be used instead. See umask(2) for - details. - -EXAMPLES --------- -`git tar-tree HEAD junk | (cd /var/tmp/ && tar xf -)`:: - - Create a tar archive that contains the contents of the - latest commit on the current branch, and extracts it in - `/var/tmp/junk` directory. - -`git tar-tree v1.4.0 git-1.4.0 | gzip >git-1.4.0.tar.gz`:: - - Create a tarball for v1.4.0 release. - -`git tar-tree v1.4.0^{tree} git-1.4.0 | gzip >git-1.4.0.tar.gz`:: - - Create a tarball for v1.4.0 release, but without a - global extended pax header. - -`git tar-tree --remote=example.com:git.git v1.4.0 >git-1.4.0.tar`:: - - Get a tarball v1.4.0 from example.com. - -`git tar-tree HEAD:Documentation/ git-docs > git-1.4.0-docs.tar`:: - - Put everything in the current head's Documentation/ directory - into 'git-1.4.0-docs.tar', with the prefix 'git-docs/'. - -GIT ---- -Part of the linkgit:git[1] suite diff --git a/Documentation/git.txt b/Documentation/git.txt index cc0e5e2bfe..aec372646f 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -807,6 +807,15 @@ temporary file --- it is removed when 'GIT_EXTERNAL_DIFF' exits. + For a path that is unmerged, 'GIT_EXTERNAL_DIFF' is called with 1 parameter, <path>. ++ +For each path 'GIT_EXTERNAL_DIFF' is called, two environment variables, +'GIT_DIFF_PATH_COUNTER' and 'GIT_DIFF_PATH_TOTAL' are set. + +'GIT_DIFF_PATH_COUNTER':: + A 1-based counter incremented by one for every path. + +'GIT_DIFF_PATH_TOTAL':: + The total number of paths. other ~~~~~ diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt index 41bed2983f..3f33ca5507 100644 --- a/Documentation/gitcli.txt +++ b/Documentation/gitcli.txt @@ -72,11 +72,11 @@ scripting Git: * splitting short options to separate words (prefer `git foo -a -b` to `git foo -ab`, the latter may not even work). - * when a command line option takes an argument, use the 'sticked' form. In + * when a command line option takes an argument, use the 'stuck' form. In other words, write `git foo -oArg` instead of `git foo -o Arg` for short options, and `git foo --long-opt=Arg` instead of `git foo --long-opt Arg` for long options. An option that takes optional option-argument must be - written in the 'sticked' form. + written in the 'stuck' form. * when you give a revision parameter to a command, make sure the parameter is not ambiguous with a name of a file in the work tree. E.g. do not write @@ -165,7 +165,7 @@ $ git foo -o Arg ---------------------------- However, this is *NOT* allowed for switches with an optional value, where the -'sticked' form must be used: +'stuck' form must be used: ---------------------------- $ git describe --abbrev HEAD # correct $ git describe --abbrev=10 HEAD # correct diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt index f971960512..205e80ef88 100644 --- a/Documentation/gitignore.txt +++ b/Documentation/gitignore.txt @@ -79,8 +79,10 @@ PATTERN FORMAT - An optional prefix "`!`" which negates the pattern; any matching file excluded by a previous pattern will become - included again. If a negated pattern matches, this will - override lower precedence patterns sources. + included again. It is not possible to re-include a file if a parent + directory of that file is excluded. Git doesn't list excluded + directories for performance reasons, so any patterns on contained + files have no effect, no matter where they are defined. Put a backslash ("`\`") in front of the first "`!`" for patterns that begin with a literal "`!`", for example, "`\!important!.txt`". @@ -182,6 +184,19 @@ Another example: The second .gitignore prevents Git from ignoring `arch/foo/kernel/vmlinux.lds.S`. +Example to exclude everything except a specific directory `foo/bar` +(note the `/*` - without the slash, the wildcard would also exclude +everything within `foo/bar`): + +-------------------------------------------------------------- + $ cat .gitignore + # exclude everything except directory foo/bar + /* + !/foo + /foo/* + !/foo/bar +-------------------------------------------------------------- + SEE ALSO -------- linkgit:git-rm[1], diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt index f7be93f631..347a9f76ee 100644 --- a/Documentation/gitmodules.txt +++ b/Documentation/gitmodules.txt @@ -35,6 +35,8 @@ submodule.<name>.url:: linkgit:git-clone[1] or (if it begins with ./ or ../) a location relative to the superproject's origin repository. +In addition, there are a number of optional keys: + submodule.<name>.update:: Defines what to do when the submodule is updated by the superproject. If 'checkout' (the default), the new commit specified in the diff --git a/Documentation/gitweb.conf.txt b/Documentation/gitweb.conf.txt index e2113d93c9..db4154f9d5 100644 --- a/Documentation/gitweb.conf.txt +++ b/Documentation/gitweb.conf.txt @@ -849,6 +849,43 @@ time zones in the form of "+/-HHMM", such as "+0200". + Project specific override is not supported. +extra-branch-refs:: + List of additional directories under "refs" which are going to + be used as branch refs. For example if you have a gerrit setup + where all branches under refs/heads/ are official, + push-after-review ones and branches under refs/sandbox/, + refs/wip and refs/other are user ones where permissions are + much wider, then you might want to set this variable as + follows: ++ +-------------------------------------------------------------------------------- +$feature{'extra-branch-refs'}{'default'} = + ['sandbox', 'wip', 'other']; +-------------------------------------------------------------------------------- ++ +This feature can be configured on per-repository basis after setting +$feature{'extra-branch-refs'}{'override'} to true, via repository's +`gitweb.extraBranchRefs` configuration variable, which contains a +space separated list of refs. An example: ++ +-------------------------------------------------------------------------------- +[gitweb] + extraBranchRefs = sandbox wip other +-------------------------------------------------------------------------------- ++ +The gitweb.extraBranchRefs is actually a multi-valued configuration +variable, so following example is also correct and the result is the +same as of the snippet above: ++ +-------------------------------------------------------------------------------- +[gitweb] + extraBranchRefs = sandbox + extraBranchRefs = wip other +-------------------------------------------------------------------------------- ++ +It is an error to specify a ref that does not pass "git check-ref-format" +scrutiny. Duplicated values are filtered. + EXAMPLES -------- diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt index aa1c8880dd..378306f581 100644 --- a/Documentation/glossary-content.txt +++ b/Documentation/glossary-content.txt @@ -323,24 +323,26 @@ including Documentation/chapter_1/figure_1.jpg. A pathspec that begins with a colon `:` has special meaning. In the short form, the leading colon `:` is followed by zero or more "magic signature" letters (which optionally is terminated by another colon `:`), -and the remainder is the pattern to match against the path. The optional -colon that terminates the "magic signature" can be omitted if the pattern -begins with a character that cannot be a "magic signature" and is not a -colon. +and the remainder is the pattern to match against the path. +The "magic signature" consists of ASCII symbols that are neither +alphanumeric, glob, regex special charaters nor colon. +The optional colon that terminates the "magic signature" can be +omitted if the pattern begins with a character that does not belong to +"magic signature" symbol set and is not a colon. + In the long form, the leading colon `:` is followed by a open parenthesis `(`, a comma-separated list of zero or more "magic words", and a close parentheses `)`, and the remainder is the pattern to match against the path. + -The "magic signature" consists of an ASCII symbol that is not -alphanumeric. +A pathspec with only a colon means "there is no pathspec". This form +should not be combined with other pathspec. + -- -top `/`;; - The magic word `top` (mnemonic: `/`) makes the pattern match - from the root of the working tree, even when you are running - the command from inside a subdirectory. +top;; + The magic word `top` (magic signature: `/`) makes the pattern + match from the root of the working tree, even when you are + running the command from inside a subdirectory. literal;; Wildcards in the pattern such as `*` or `?` are treated @@ -377,14 +379,12 @@ full pathname may have special meaning: - Other consecutive asterisks are considered invalid. + Glob magic is incompatible with literal magic. + +exclude;; + After a path matches any non-exclude pathspec, it will be run + through all exclude pathspec (magic signature: `!`). If it + matches, the path is ignored. -- -+ -Currently only the slash `/` is recognized as the "magic signature", -but it is envisioned that we will support more types of magic in later -versions of Git. -+ -A pathspec with only a colon means "there is no pathspec". This form -should not be combined with other pathspec. [[def_parent]]parent:: A <<def_commit_object,commit object>> contains a (possibly empty) list diff --git a/Documentation/merge-strategies.txt b/Documentation/merge-strategies.txt index 49a9a7d53f..fb6e593e7c 100644 --- a/Documentation/merge-strategies.txt +++ b/Documentation/merge-strategies.txt @@ -113,3 +113,11 @@ subtree:: match the tree structure of A, instead of reading the trees at the same level. This adjustment is also done to the common ancestor tree. + +With the strategies that use 3-way merge (including the default, 'recursive'), +if a change is made on both branches, but later reverted on one of the +branches, that change will be present in the merged result; some people find +this behavior confusing. It occurs because only the heads and the merge base +are considered when performing a merge, not the individual commits. The merge +algorithm therefore considers the reverted change as no change at all, and +substitutes the changed version instead. diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 2991d70a4a..03533af715 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -153,6 +153,21 @@ parents) and `--max-parents=-1` (negative numbers denote no upper limit). is automatically prepended if missing. If pattern lacks '?', '{asterisk}', or '[', '/{asterisk}' at the end is implied. +--exclude=<glob-pattern>:: + + Do not include refs matching '<glob-pattern>' that the next `--all`, + `--branches`, `--tags`, `--remotes`, or `--glob` would otherwise + consider. Repetitions of this option accumulate exclusion patterns + up to the next `--all`, `--branches`, `--tags`, `--remotes`, or + `--glob` option (other options or arguments do not clear + accumlated patterns). ++ +The patterns given should not begin with `refs/heads`, `refs/tags`, or +`refs/remotes` when applied to `--branches`, `--tags`, or `--remotes`, +respectively, and they must begin with `refs/` when applied to `--glob` +or `--all`. If a trailing '/{asterisk}' is intended, it must be given +explicitly. + --ignore-missing:: Upon seeing an invalid object name in the input, pretend as if the bad input was not given. diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt index 0be2b5159f..be50cf4de3 100644 --- a/Documentation/technical/api-parse-options.txt +++ b/Documentation/technical/api-parse-options.txt @@ -29,9 +29,9 @@ that allow to change the behavior of a command. The parse-options API allows: -* 'sticked' and 'separate form' of options with arguments. - `-oArg` is sticked, `-o Arg` is separate form. - `--option=Arg` is sticked, `--option Arg` is separate form. +* 'stuck' and 'separate form' of options with arguments. + `-oArg` is stuck, `-o Arg` is separate form. + `--option=Arg` is stuck, `--option Arg` is separate form. * Long options may be 'abbreviated', as long as the abbreviation is unambiguous. diff --git a/Documentation/technical/api-remote.txt b/Documentation/technical/api-remote.txt index 4be87768f6..5d245aa9d1 100644 --- a/Documentation/technical/api-remote.txt +++ b/Documentation/technical/api-remote.txt @@ -58,16 +58,16 @@ default remote, given the current branch and configuration. struct refspec -------------- -A struct refspec holds the parsed interpretation of a refspec. If it -will force updates (starts with a '+'), force is true. If it is a -pattern (sides end with '*') pattern is true. src and dest are the two -sides (if a pattern, only the part outside of the wildcards); if there -is only one side, it is src, and dst is NULL; if sides exist but are -empty (i.e., the refspec either starts or ends with ':'), the -corresponding side is "". - -This parsing can be done to an array of strings to give an array of -struct refpsecs with parse_ref_spec(). +A struct refspec holds the parsed interpretation of a refspec. If it +will force updates (starts with a '+'), force is true. If it is a +pattern (sides end with '*') pattern is true. src and dest are the +two sides (including '*' characters if present); if there is only one +side, it is src, and dst is NULL; if sides exist but are empty (i.e., +the refspec either starts or ends with ':'), the corresponding side is +"". + +An array of strings can be parsed into an array of struct refspecs +using parse_fetch_refspec() or parse_push_refspec(). remote_find_tracking(), given a remote and a struct refspec with either src or dst filled out, will fill out the other such that the diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt index fd8ffa5df3..e3e792476e 100644 --- a/Documentation/technical/protocol-capabilities.txt +++ b/Documentation/technical/protocol-capabilities.txt @@ -72,14 +72,29 @@ interleaved with S-R-Q. thin-pack --------- -This capability means that the server can send a 'thin' pack, a pack -which does not contain base objects; if those base objects are available -on client side. Client requests 'thin-pack' capability when it -understands how to "thicken" it by adding required delta bases making -it self-contained. - -Client MUST NOT request 'thin-pack' capability if it cannot turn a thin -pack into a self-contained pack. +A thin pack is one with deltas which reference base objects not +contained within the pack (but are known to exist at the receiving +end). This can reduce the network traffic significantly, but it +requires the receiving end to know how to "thicken" these packs by +adding the missing bases to the pack. + +The upload-pack server advertises 'thin-pack' when it can generate +and send a thin pack. A client requests the 'thin-pack' capability +when it understands how to "thicken" it, notifying the server that +it can receive such a pack. A client MUST NOT request the +'thin-pack' capability if it cannot turn a thin pack into a +self-contained pack. + +Receive-pack, on the other hand, is assumed by default to be able to +handle thin packs, but can ask the client not to use the feature by +advertising the 'no-thin' capability. A client MUST NOT send a thin +pack if the server advertises the 'no-thin' capability. + +The reasons for this asymmetry are historical. The receive-pack +program did not exist until after the invention of thin packs, so +historically the reference implementation of receive-pack always +understood thin packs. Adding 'no-thin' later allowed receive-pack +to disable the feature in a backwards-compatible manner. side-band, side-band-64k diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 0ec4d003fa..b9532f0ce4 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.8.5.2 +DEF_VER=v1.8.5.GIT LF=' ' @@ -452,7 +452,6 @@ SCRIPT_SH += git-am.sh SCRIPT_SH += git-bisect.sh SCRIPT_SH += git-difftool--helper.sh SCRIPT_SH += git-filter-branch.sh -SCRIPT_SH += git-lost-found.sh SCRIPT_SH += git-merge-octopus.sh SCRIPT_SH += git-merge-one-file.sh SCRIPT_SH += git-merge-resolve.sh @@ -587,11 +586,8 @@ BUILT_INS += git-cherry$X BUILT_INS += git-cherry-pick$X BUILT_INS += git-format-patch$X BUILT_INS += git-fsck-objects$X -BUILT_INS += git-get-tar-commit-id$X BUILT_INS += git-init$X BUILT_INS += git-merge-subtree$X -BUILT_INS += git-peek-remote$X -BUILT_INS += git-repo-config$X BUILT_INS += git-show$X BUILT_INS += git-stage$X BUILT_INS += git-status$X @@ -932,6 +928,7 @@ BUILTIN_OBJS += builtin/fmt-merge-msg.o BUILTIN_OBJS += builtin/for-each-ref.o BUILTIN_OBJS += builtin/fsck.o BUILTIN_OBJS += builtin/gc.o +BUILTIN_OBJS += builtin/get-tar-commit-id.o BUILTIN_OBJS += builtin/grep.o BUILTIN_OBJS += builtin/hash-object.o BUILTIN_OBJS += builtin/help.o @@ -983,7 +980,6 @@ BUILTIN_OBJS += builtin/show-ref.o BUILTIN_OBJS += builtin/stripspace.o BUILTIN_OBJS += builtin/symbolic-ref.o BUILTIN_OBJS += builtin/tag.o -BUILTIN_OBJS += builtin/tar-tree.o BUILTIN_OBJS += builtin/unpack-file.o BUILTIN_OBJS += builtin/unpack-objects.o BUILTIN_OBJS += builtin/update-index.o @@ -1588,6 +1584,7 @@ PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH)) PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH)) TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH)) DIFF_SQ = $(subst ','\'',$(DIFF)) +PERLLIB_EXTRA_SQ = $(subst ','\'',$(PERLLIB_EXTRA)) LIBS = $(GITLIBS) $(EXTLIBS) @@ -1792,9 +1789,12 @@ perl/PM.stamp: FORCE perl/perl.mak: GIT-CFLAGS GIT-PREFIX perl/Makefile perl/Makefile.PL $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F) -$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl GIT-VERSION-FILE +PERL_DEFINES = $(PERL_PATH_SQ):$(PERLLIB_EXTRA_SQ) +$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl perl/perl.mak GIT-PERL-DEFINES GIT-VERSION-FILE $(QUIET_GEN)$(RM) $@ $@+ && \ INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \ + INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \ + INSTLIBDIR="$$INSTLIBDIR$${INSTLIBDIR_EXTRA:+:$$INSTLIBDIR_EXTRA}" && \ sed -e '1{' \ -e ' s|#!.*perl|#!$(PERL_PATH_SQ)|' \ -e ' h' \ @@ -1807,6 +1807,13 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl GIT-VERSION-FILE chmod +x $@+ && \ mv $@+ $@ +GIT-PERL-DEFINES: FORCE + @FLAGS='$(PERL_DEFINES)'; \ + if test x"$$FLAGS" != x"`cat $@ 2>/dev/null`" ; then \ + echo >&2 " * new perl-specific parameters"; \ + echo "$$FLAGS" >$@; \ + fi + .PHONY: gitweb gitweb: @@ -2494,7 +2501,8 @@ ifndef NO_TCLTK $(MAKE) -C git-gui clean endif $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-BUILD-OPTIONS - $(RM) GIT-USER-AGENT GIT-PREFIX GIT-SCRIPT-DEFINES GIT-PYTHON-VARS + $(RM) GIT-USER-AGENT GIT-PREFIX + $(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PYTHON-VARS .PHONY: all install profile-clean clean strip .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell @@ -1 +1 @@ -Documentation/RelNotes/1.8.5.2.txt
\ No newline at end of file +Documentation/RelNotes/1.9.txt
\ No newline at end of file @@ -143,7 +143,7 @@ static const char *real_path_internal(const char *path, int die_on_error) error_out: free(last_elem); if (*cwd && chdir(cwd)) - die_errno ("Could not change back to '%s'", cwd); + die_errno("Could not change back to '%s'", cwd); return retval; } @@ -215,23 +215,25 @@ const char *absolute_path(const char *path) */ const char *prefix_filename(const char *pfx, int pfx_len, const char *arg) { - static char path[PATH_MAX]; + static struct strbuf path = STRBUF_INIT; #ifndef GIT_WINDOWS_NATIVE if (!pfx_len || is_absolute_path(arg)) return arg; - memcpy(path, pfx, pfx_len); - strcpy(path + pfx_len, arg); + strbuf_reset(&path); + strbuf_add(&path, pfx, pfx_len); + strbuf_addstr(&path, arg); #else char *p; /* don't add prefix to absolute paths, but still replace '\' by '/' */ + strbuf_reset(&path); if (is_absolute_path(arg)) pfx_len = 0; else if (pfx_len) - memcpy(path, pfx, pfx_len); - strcpy(path + pfx_len, arg); - for (p = path + pfx_len; *p; p++) + strbuf_add(&path, pfx, pfx_len); + strbuf_addstr(&path, arg); + for (p = path.buf + pfx_len; *p; p++) if (*p == '\\') *p = '/'; #endif - return path; + return path.buf; } @@ -5,7 +5,7 @@ static char *alias_val; static int alias_lookup_cb(const char *k, const char *v, void *cb) { - if (!prefixcmp(k, "alias.") && !strcmp(k + 6, alias_key)) { + if (starts_with(k, "alias.") && !strcmp(k + 6, alias_key)) { if (!v) return config_error_nonbool(k); alias_val = xstrdup(v); @@ -211,7 +211,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src, name = cp; namelen = strcspn(name, blank); if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen && - !prefixcmp(name, ATTRIBUTE_MACRO_PREFIX)) { + starts_with(name, ATTRIBUTE_MACRO_PREFIX)) { if (!macro_ok) { fprintf(stderr, "%s not allowed: %s:%d\n", name, src, lineno); @@ -406,9 +406,9 @@ static int register_ref(const char *refname, const unsigned char *sha1, if (!strcmp(refname, "bad")) { current_bad_sha1 = xmalloc(20); hashcpy(current_bad_sha1, sha1); - } else if (!prefixcmp(refname, "good-")) { + } else if (starts_with(refname, "good-")) { sha1_array_append(&good_revs, sha1); - } else if (!prefixcmp(refname, "skip-")) { + } else if (starts_with(refname, "skip-")) { sha1_array_append(&skipped_revs, sha1); } @@ -50,7 +50,7 @@ static int should_setup_rebase(const char *origin) void install_branch_config(int flag, const char *local, const char *origin, const char *remote) { const char *shortname = remote + 11; - int remote_is_branch = !prefixcmp(remote, "refs/heads/"); + int remote_is_branch = starts_with(remote, "refs/heads/"); struct strbuf key = STRBUF_INIT; int rebasing = should_setup_rebase(origin); @@ -272,7 +272,7 @@ void create_branch(const char *head, break; case 1: /* Unique completion -- good, only if it is a real branch */ - if (prefixcmp(real_ref, "refs/heads/") && + if (!starts_with(real_ref, "refs/heads/") && validate_remote_tracking_branch(real_ref)) { if (explicit_tracking) die(_(upstream_not_branch), start_name); @@ -103,7 +103,6 @@ extern int cmd_remote(int argc, const char **argv, const char *prefix); extern int cmd_remote_ext(int argc, const char **argv, const char *prefix); extern int cmd_remote_fd(int argc, const char **argv, const char *prefix); extern int cmd_repack(int argc, const char **argv, const char *prefix); -extern int cmd_repo_config(int argc, const char **argv, const char *prefix); extern int cmd_rerere(int argc, const char **argv, const char *prefix); extern int cmd_reset(int argc, const char **argv, const char *prefix); extern int cmd_rev_list(int argc, const char **argv, const char *prefix); diff --git a/builtin/add.c b/builtin/add.c index 226f758869..2a2722fa10 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -540,11 +540,14 @@ int cmd_add(int argc, const char **argv, const char *prefix) PATHSPEC_FROMTOP | PATHSPEC_LITERAL | PATHSPEC_GLOB | - PATHSPEC_ICASE); + PATHSPEC_ICASE | + PATHSPEC_EXCLUDE); for (i = 0; i < pathspec.nr; i++) { const char *path = pathspec.items[i].match; - if (!seen[i] && + if (pathspec.items[i].magic & PATHSPEC_EXCLUDE) + continue; + if (!seen[i] && path[0] && ((pathspec.items[i].magic & (PATHSPEC_GLOB | PATHSPEC_ICASE)) || !file_exists(path))) { diff --git a/builtin/apply.c b/builtin/apply.c index ef32e4f624..b0d0986226 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -1409,10 +1409,10 @@ static void recount_diff(const char *line, int size, struct fragment *fragment) case '\\': continue; case '@': - ret = size < 3 || prefixcmp(line, "@@ "); + ret = size < 3 || !starts_with(line, "@@ "); break; case 'd': - ret = size < 5 || prefixcmp(line, "diff "); + ret = size < 5 || !starts_with(line, "diff "); break; default: ret = -1; @@ -1798,11 +1798,11 @@ static struct fragment *parse_binary_hunk(char **buf_p, *status_p = 0; - if (!prefixcmp(buffer, "delta ")) { + if (starts_with(buffer, "delta ")) { patch_method = BINARY_DELTA_DEFLATED; origlen = strtoul(buffer + 6, NULL, 10); } - else if (!prefixcmp(buffer, "literal ")) { + else if (starts_with(buffer, "literal ")) { patch_method = BINARY_LITERAL_DEFLATED; origlen = strtoul(buffer + 8, NULL, 10); } @@ -3627,12 +3627,12 @@ static int preimage_sha1_in_gitlink_patch(struct patch *p, unsigned char sha1[20 hunk->oldpos == 1 && hunk->oldlines == 1 && /* does preimage begin with the heading? */ (preimage = memchr(hunk->patch, '\n', hunk->size)) != NULL && - !prefixcmp(++preimage, heading) && + starts_with(++preimage, heading) && /* does it record full SHA-1? */ !get_sha1_hex(preimage + sizeof(heading) - 1, sha1) && preimage[sizeof(heading) + 40 - 1] == '\n' && /* does the abbreviated name on the index line agree with it? */ - !prefixcmp(preimage + sizeof(heading) - 1, p->old_sha1_prefix)) + starts_with(preimage + sizeof(heading) - 1, p->old_sha1_prefix)) return 0; /* it all looks fine */ /* we may have full object name on the index line */ diff --git a/builtin/archive.c b/builtin/archive.c index 49178f159e..a1e3b940c2 100644 --- a/builtin/archive.c +++ b/builtin/archive.c @@ -57,9 +57,9 @@ static int run_remote_archiver(int argc, const char **argv, if (!buf) die(_("git archive: expected ACK/NAK, got EOF")); if (strcmp(buf, "ACK")) { - if (!prefixcmp(buf, "NACK ")) + if (starts_with(buf, "NACK ")) die(_("git archive: NACK %s"), buf + 5); - if (!prefixcmp(buf, "ERR ")) + if (starts_with(buf, "ERR ")) die(_("remote error: %s"), buf + 4); die(_("git archive: protocol error")); } diff --git a/builtin/blame.c b/builtin/blame.c index 1407ae7eb2..e44a6bb30a 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -1551,8 +1551,7 @@ static void assign_blame(struct scoreboard *sb, int opt) */ origin_incref(suspect); commit = suspect->commit; - if (!commit->object.parsed) - parse_commit(commit); + parse_commit(commit); if (reverse || (!(commit->object.flags & UNINTERESTING) && !(revs->max_age != -1 && commit->date < revs->max_age))) @@ -1804,17 +1803,17 @@ static int prepare_lines(struct scoreboard *sb) static int read_ancestry(const char *graft_file) { FILE *fp = fopen(graft_file, "r"); - char buf[1024]; + struct strbuf buf = STRBUF_INIT; if (!fp) return -1; - while (fgets(buf, sizeof(buf), fp)) { + while (!strbuf_getwholeline(&buf, fp, '\n')) { /* The format is just "Commit Parent1 Parent2 ...\n" */ - int len = strlen(buf); - struct commit_graft *graft = read_graft_line(buf, len); + struct commit_graft *graft = read_graft_line(buf.buf, buf.len); if (graft) register_commit_graft(graft, 0); } fclose(fp); + strbuf_release(&buf); return 0; } diff --git a/builtin/branch.c b/builtin/branch.c index f157f92f48..b4d771673e 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -81,13 +81,13 @@ static int parse_branch_color_slot(const char *var, int ofs) static int git_branch_config(const char *var, const char *value, void *cb) { - if (!prefixcmp(var, "column.")) + if (starts_with(var, "column.")) return git_column_config(var, value, "branch", &colopts); if (!strcmp(var, "color.branch")) { branch_use_color = git_config_colorbool(var, value); return 0; } - if (!prefixcmp(var, "color.branch.")) { + if (starts_with(var, "color.branch.")) { int slot = parse_branch_color_slot(var, 13); if (slot < 0) return 0; @@ -502,7 +502,7 @@ static void add_verbose_info(struct strbuf *out, struct ref_item *item, const char *sub = _(" **** invalid ref ****"); struct commit *commit = item->commit; - if (commit && !parse_commit(commit)) { + if (!parse_commit(commit)) { pp_commit_easy(CMIT_FMT_ONELINE, commit, &subject); sub = subject.buf; } @@ -868,7 +868,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (!strcmp(head, "HEAD")) { detached = 1; } else { - if (prefixcmp(head, "refs/heads/")) + if (!starts_with(head, "refs/heads/")) die(_("HEAD not found below refs/heads!")); head += 11; } diff --git a/builtin/cat-file.c b/builtin/cat-file.c index b2ca775a80..d5a93e0e91 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -118,6 +118,7 @@ struct expand_data { unsigned long size; unsigned long disk_size; const char *rest; + unsigned char delta_base_sha1[20]; /* * If mark_query is true, we do not expand anything, but rather @@ -174,6 +175,11 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len, data->split_on_whitespace = 1; else if (data->rest) strbuf_addstr(sb, data->rest); + } else if (is_atom("deltabase", atom, len)) { + if (data->mark_query) + data->info.delta_base_sha1 = data->delta_base_sha1; + else + strbuf_addstr(sb, sha1_to_hex(data->delta_base_sha1)); } else die("unknown format element: %.*s", len, atom); } @@ -193,25 +199,28 @@ static size_t expand_format(struct strbuf *sb, const char *start, void *data) return end - start + 1; } -static void print_object_or_die(int fd, const unsigned char *sha1, - enum object_type type, unsigned long size) +static void print_object_or_die(int fd, struct expand_data *data) { - if (type == OBJ_BLOB) { + const unsigned char *sha1 = data->sha1; + + assert(data->info.typep); + + if (data->type == OBJ_BLOB) { if (stream_blob_to_fd(fd, sha1, NULL, 0) < 0) die("unable to stream %s to stdout", sha1_to_hex(sha1)); } else { - enum object_type rtype; - unsigned long rsize; + enum object_type type; + unsigned long size; void *contents; - contents = read_sha1_file(sha1, &rtype, &rsize); + contents = read_sha1_file(sha1, &type, &size); if (!contents) die("object %s disappeared", sha1_to_hex(sha1)); - if (rtype != type) + if (type != data->type) die("object %s changed type!?", sha1_to_hex(sha1)); - if (rsize != size) - die("object %s change size!?", sha1_to_hex(sha1)); + if (data->info.sizep && size != data->size) + die("object %s changed size!?", sha1_to_hex(sha1)); write_or_die(fd, contents, size); free(contents); @@ -238,7 +247,7 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt, return 0; } - if (sha1_object_info_extended(data->sha1, &data->info) < 0) { + if (sha1_object_info_extended(data->sha1, &data->info, LOOKUP_REPLACE_OBJECT) < 0) { printf("%s missing\n", obj_name); fflush(stdout); return 0; @@ -250,7 +259,7 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt, strbuf_release(&buf); if (opt->print_contents) { - print_object_or_die(1, data->sha1, data->type, data->size); + print_object_or_die(1, data); write_or_die(1, "\n", 1); } return 0; @@ -275,6 +284,13 @@ static int batch_objects(struct batch_options *opt) data.mark_query = 0; /* + * If we are printing out the object, then always fill in the type, + * since we will want to decide whether or not to stream. + */ + if (opt->print_contents) + data.info.typep = &data.type; + + /* * We are going to call get_sha1 on a potentially very large number of * objects. In most large cases, these will be actual object sha1s. The * cost to double-check that each one is not also a ref (just so we can diff --git a/builtin/checkout.c b/builtin/checkout.c index 54f80bd38a..5df3837e31 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -380,8 +380,8 @@ static void show_local_changes(struct object *head, static void describe_detached_head(const char *msg, struct commit *commit) { struct strbuf sb = STRBUF_INIT; - parse_commit(commit); - pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb); + if (!parse_commit(commit)) + pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb); fprintf(stderr, "%s %s... %s\n", msg, find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf); strbuf_release(&sb); @@ -677,12 +677,12 @@ static int add_pending_uninteresting_ref(const char *refname, static void describe_one_orphan(struct strbuf *sb, struct commit *commit) { - parse_commit(commit); strbuf_addstr(sb, " "); strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV)); strbuf_addch(sb, ' '); - pp_commit_easy(CMIT_FMT_ONELINE, commit, sb); + if (!parse_commit(commit)) + pp_commit_easy(CMIT_FMT_ONELINE, commit, sb); strbuf_addch(sb, '\n'); } @@ -781,7 +781,7 @@ static int switch_branches(const struct checkout_opts *opts, if (!(flag & REF_ISSYMREF)) old.path = NULL; - if (old.path && !prefixcmp(old.path, "refs/heads/")) + if (old.path && starts_with(old.path, "refs/heads/")) old.name = old.path + strlen("refs/heads/"); if (!new->name) { @@ -789,7 +789,7 @@ static int switch_branches(const struct checkout_opts *opts, new->commit = old.commit; if (!new->commit) die(_("You are on a branch yet to be born")); - parse_commit(new->commit); + parse_commit_or_die(new->commit); } ret = merge_working_tree(opts, &old, new, &writeout_error); @@ -816,7 +816,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb) return 0; } - if (!prefixcmp(var, "submodule.")) + if (starts_with(var, "submodule.")) return parse_submodule_config_option(var, value); return git_xmerge_config(var, value, NULL); @@ -995,7 +995,7 @@ static int parse_branchname_arg(int argc, const char **argv, /* not a commit */ *source_tree = parse_tree_indirect(rev); } else { - parse_commit(new->commit); + parse_commit_or_die(new->commit); *source_tree = new->commit->tree; } @@ -1151,9 +1151,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) const char *argv0 = argv[0]; if (!argc || !strcmp(argv0, "--")) die (_("--track needs a branch name")); - if (!prefixcmp(argv0, "refs/")) + if (starts_with(argv0, "refs/")) argv0 += 5; - if (!prefixcmp(argv0, "remotes/")) + if (starts_with(argv0, "remotes/")) argv0 += 8; argv0 = strchr(argv0, '/'); if (!argv0 || !argv0[1]) diff --git a/builtin/clean.c b/builtin/clean.c index 615cd57caf..2f26297142 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -100,7 +100,7 @@ static int parse_clean_color_slot(const char *var) static int git_clean_config(const char *var, const char *value, void *cb) { - if (!prefixcmp(var, "column.")) + if (starts_with(var, "column.")) return git_column_config(var, value, "clean", &colopts); /* honors the color.interactive* config variables which also @@ -109,7 +109,7 @@ static int git_clean_config(const char *var, const char *value, void *cb) clean_use_color = git_config_colorbool(var, value); return 0; } - if (!prefixcmp(var, "color.interactive.")) { + if (starts_with(var, "color.interactive.")) { int slot = parse_clean_color_slot(var + strlen("color.interactive.")); if (slot < 0) diff --git a/builtin/clone.c b/builtin/clone.c index 874e0fd0b6..f98f52980d 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -508,9 +508,9 @@ static void write_followtags(const struct ref *refs, const char *msg) { const struct ref *ref; for (ref = refs; ref; ref = ref->next) { - if (prefixcmp(ref->name, "refs/tags/")) + if (!starts_with(ref->name, "refs/tags/")) continue; - if (!suffixcmp(ref->name, "^{}")) + if (ends_with(ref->name, "^{}")) continue; if (!has_sha1_file(ref->old_sha1)) continue; @@ -578,7 +578,7 @@ static void update_remote_refs(const struct ref *refs, static void update_head(const struct ref *our, const struct ref *remote, const char *msg) { - if (our && !prefixcmp(our->name, "refs/heads/")) { + if (our && starts_with(our->name, "refs/heads/")) { /* Local default branch link */ create_symref("HEAD", our->name, NULL); if (!option_bare) { @@ -625,7 +625,7 @@ static int checkout(void) if (advice_detached_head) detach_advice(sha1_to_hex(sha1)); } else { - if (prefixcmp(head, "refs/heads/")) + if (!starts_with(head, "refs/heads/")) die(_("HEAD not found below refs/heads!")); } free(head); @@ -796,6 +796,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (option_local > 0 && !is_local) warning(_("--local is ignored")); + /* no need to be strict, transport_set_option() will validate it again */ + if (option_depth && atoi(option_depth) < 1) + die(_("depth %s is not a positive number"), option_depth); + if (argc == 2) dir = xstrdup(argv[1]); else diff --git a/builtin/column.c b/builtin/column.c index e125a55fc9..75818520e1 100644 --- a/builtin/column.c +++ b/builtin/column.c @@ -34,7 +34,7 @@ int cmd_column(int argc, const char **argv, const char *prefix) }; /* This one is special and must be the first one */ - if (argc > 1 && !prefixcmp(argv[1], "--command=")) { + if (argc > 1 && starts_with(argv[1], "--command=")) { command = argv[1] + 10; git_config(column_config, (void *)command); } else diff --git a/builtin/commit.c b/builtin/commit.c index 6ab4605cf5..3767478c6d 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -733,7 +733,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, eol = nl - sb.buf; else eol = sb.len; - if (!prefixcmp(sb.buf + previous, "\nConflicts:\n")) { + if (starts_with(sb.buf + previous, "\nConflicts:\n")) { ignore_footer = sb.len - previous; break; } @@ -904,7 +904,7 @@ static int rest_is_empty(struct strbuf *sb, int start) eol = sb->len; if (strlen(sign_off_header) <= eol - i && - !prefixcmp(sb->buf + i, sign_off_header)) { + starts_with(sb->buf + i, sign_off_header)) { i = eol; continue; } @@ -1183,7 +1183,7 @@ static int git_status_config(const char *k, const char *v, void *cb) { struct wt_status *s = cb; - if (!prefixcmp(k, "column.")) + if (starts_with(k, "column.")) return git_column_config(k, v, "status", &s->colopts); if (!strcmp(k, "status.submodulesummary")) { int is_bool; @@ -1211,7 +1211,7 @@ static int git_status_config(const char *k, const char *v, void *cb) s->display_comment_prefix = git_config_bool(k, v); return 0; } - if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) { + if (starts_with(k, "status.color.") || starts_with(k, "color.status.")) { int slot = parse_status_slot(k, 13); if (slot < 0) return 0; @@ -1338,7 +1338,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1, commit = lookup_commit(sha1); if (!commit) die(_("couldn't look up newly created commit")); - if (!commit || parse_commit(commit)) + if (parse_commit(commit)) die(_("could not parse newly created commit")); strbuf_addstr(&format, "format:%h] %s"); @@ -1377,7 +1377,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1, head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL); printf("[%s%s ", - !prefixcmp(head, "refs/heads/") ? + starts_with(head, "refs/heads/") ? head + 11 : !strcmp(head, "HEAD") ? _("detached HEAD") : @@ -1505,7 +1505,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) struct strbuf sb = STRBUF_INIT; struct strbuf author_ident = STRBUF_INIT; const char *index_file, *reflog_msg; - char *nl, *p; + char *nl; unsigned char sha1[20]; struct ref_lock *ref_lock; struct commit_list *parents = NULL, **pptr = &parents; @@ -1525,7 +1525,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) current_head = NULL; else { current_head = lookup_commit_or_die(sha1, "HEAD"); - if (!current_head || parse_commit(current_head)) + if (parse_commit(current_head)) die(_("could not parse HEAD commit")); } argc = parse_and_validate_options(argc, argv, builtin_commit_options, @@ -1601,11 +1601,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) } /* Truncate the message just before the diff, if any. */ - if (verbose) { - p = strstr(sb.buf, "\ndiff --git "); - if (p != NULL) - strbuf_setlen(&sb, p - sb.buf + 1); - } + if (verbose) + wt_status_truncate_message_at_cut_line(&sb); if (cleanup_mode != CLEANUP_NONE) stripspace(&sb, cleanup_mode == CLEANUP_ALL); diff --git a/builtin/config.c b/builtin/config.c index 20e89fe4e0..92ebf23f0a 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -671,9 +671,3 @@ int cmd_config(int argc, const char **argv, const char *prefix) return 0; } - -int cmd_repo_config(int argc, const char **argv, const char *prefix) -{ - fprintf(stderr, "WARNING: git repo-config is deprecated in favor of git config.\n"); - return cmd_config(argc, argv, prefix); -} diff --git a/builtin/describe.c b/builtin/describe.c index 6f62109887..7db43dae1b 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -141,7 +141,7 @@ static void add_to_known_names(const char *path, static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data) { - int is_tag = !prefixcmp(path, "refs/tags/"); + int is_tag = starts_with(path, "refs/tags/"); unsigned char peeled[20]; int is_annotated, prio; diff --git a/builtin/diff.c b/builtin/diff.c index fe0cc7f1b5..0f247d2400 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -16,6 +16,9 @@ #include "submodule.h" #include "sha1-array.h" +#define DIFF_NO_INDEX_EXPLICIT 1 +#define DIFF_NO_INDEX_IMPLICIT 2 + struct blobinfo { unsigned char sha1[20]; const char *name; @@ -259,7 +262,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) struct object_array ent = OBJECT_ARRAY_INIT; int blobs = 0, paths = 0; struct blobinfo blob[2]; - int nongit; + int nongit = 0, no_index = 0; int result = 0; /* @@ -285,14 +288,59 @@ int cmd_diff(int argc, const char **argv, const char *prefix) * Other cases are errors. */ - prefix = setup_git_directory_gently(&nongit); - gitmodules_config(); + /* Were we asked to do --no-index explicitly? */ + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "--")) { + i++; + break; + } + if (!strcmp(argv[i], "--no-index")) + no_index = DIFF_NO_INDEX_EXPLICIT; + if (argv[i][0] != '-') + break; + } + + if (!no_index) + prefix = setup_git_directory_gently(&nongit); + + /* + * Treat git diff with at least one path outside of the + * repo the same as if the command would have been executed + * outside of a git repository. In this case it behaves + * the same way as "git diff --no-index <a> <b>", which acts + * as a colourful "diff" replacement. + */ + if (nongit || ((argc == i + 2) && + (!path_inside_repo(prefix, argv[i]) || + !path_inside_repo(prefix, argv[i + 1])))) + no_index = DIFF_NO_INDEX_IMPLICIT; + + if (!no_index) + gitmodules_config(); git_config(git_diff_ui_config, NULL); init_revisions(&rev, prefix); - /* If this is a no-index diff, just run it and exit there. */ - diff_no_index(&rev, argc, argv, nongit, prefix); + if (no_index && argc != i + 2) { + if (no_index == DIFF_NO_INDEX_IMPLICIT) { + /* + * There was no --no-index and there were not two + * paths. It is possible that the user intended + * to do an inside-repository operation. + */ + fprintf(stderr, "Not a git repository\n"); + fprintf(stderr, + "To compare two paths outside a working tree:\n"); + } + /* Give the usage message for non-repository usage and exit. */ + usagef("git diff %s <path> <path>", + no_index == DIFF_NO_INDEX_EXPLICIT ? + "--no-index" : "[--no-index]"); + + } + if (no_index) + /* If this is a no-index diff, just run it and exit there. */ + diff_no_index(&rev, argc, argv, prefix); /* Otherwise, we are doing the usual "git" diff */ rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index; diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 78250eab08..b8d8a3aaf9 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -287,7 +287,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev) rev->diffopt.output_format = DIFF_FORMAT_CALLBACK; - parse_commit(commit); + parse_commit_or_die(commit); author = strstr(commit->buffer, "\nauthor "); if (!author) die ("Could not find author in commit %s", @@ -308,7 +308,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev) if (commit->parents && get_object_mark(&commit->parents->item->object) != 0 && !full_tree) { - parse_commit(commit->parents->item); + parse_commit_or_die(commit->parents->item); diff_tree_sha1(commit->parents->item->tree->object.sha1, commit->tree->object.sha1, "", &rev->diffopt); } @@ -476,7 +476,7 @@ static void handle_tag(const char *name, struct tag *tag) } } - if (!prefixcmp(name, "refs/tags/")) + if (starts_with(name, "refs/tags/")) name += 10; printf("tag %s\nfrom :%d\n%.*s%sdata %d\n%.*s\n", name, tagged_mark, diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index c8e858232a..8b8978a252 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -7,7 +7,7 @@ static const char fetch_pack_usage[] = "git fetch-pack [--all] [--stdin] [--quiet|-q] [--keep|-k] [--thin] " "[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] " -"[--no-progress] [-v] [<host>:]<directory> [<refs>...]"; +"[--no-progress] [--diag-url] [-v] [<host>:]<directory> [<refs>...]"; static void add_sought_entry_mem(struct ref ***sought, int *nr, int *alloc, const char *name, int namelen) @@ -48,11 +48,11 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) for (i = 1; i < argc && *argv[i] == '-'; i++) { const char *arg = argv[i]; - if (!prefixcmp(arg, "--upload-pack=")) { + if (starts_with(arg, "--upload-pack=")) { args.uploadpack = arg + 14; continue; } - if (!prefixcmp(arg, "--exec=")) { + if (starts_with(arg, "--exec=")) { args.uploadpack = arg + 7; continue; } @@ -81,11 +81,15 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) args.stdin_refs = 1; continue; } + if (!strcmp("--diag-url", arg)) { + args.diag_url = 1; + continue; + } if (!strcmp("-v", arg)) { args.verbose = 1; continue; } - if (!prefixcmp(arg, "--depth=")) { + if (starts_with(arg, "--depth=")) { args.depth = strtol(arg + 8, NULL, 0); continue; } @@ -146,10 +150,14 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) fd[0] = 0; fd[1] = 1; } else { + int flags = args.verbose ? CONNECT_VERBOSE : 0; + if (args.diag_url) + flags |= CONNECT_DIAG_URL; conn = git_connect(fd, dest, args.uploadpack, - args.verbose ? CONNECT_VERBOSE : 0); + flags); + if (!conn) + return args.diag_url ? 0 : 1; } - get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL); ref = fetch_pack(&args, fd, conn, ref, dest, diff --git a/builtin/fetch.c b/builtin/fetch.c index bd7a10164f..09825c84d7 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -44,6 +44,7 @@ static struct transport *gtransport; static struct transport *gsecondary; static const char *submodule_prefix = ""; static const char *recurse_submodules_default; +static int shown_url = 0; static int option_parse_recurse_submodules(const struct option *opt, const char *arg, int unset) @@ -160,48 +161,156 @@ static void add_merge_config(struct ref **head, } } +static int add_existing(const char *refname, const unsigned char *sha1, + int flag, void *cbdata) +{ + struct string_list *list = (struct string_list *)cbdata; + struct string_list_item *item = string_list_insert(list, refname); + item->util = xmalloc(20); + hashcpy(item->util, sha1); + return 0; +} + +static int will_fetch(struct ref **head, const unsigned char *sha1) +{ + struct ref *rm = *head; + while (rm) { + if (!hashcmp(rm->old_sha1, sha1)) + return 1; + rm = rm->next; + } + return 0; +} + static void find_non_local_tags(struct transport *transport, struct ref **head, - struct ref ***tail); + struct ref ***tail) +{ + struct string_list existing_refs = STRING_LIST_INIT_DUP; + struct string_list remote_refs = STRING_LIST_INIT_NODUP; + const struct ref *ref; + struct string_list_item *item = NULL; + + for_each_ref(add_existing, &existing_refs); + for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) { + if (!starts_with(ref->name, "refs/tags/")) + continue; + + /* + * The peeled ref always follows the matching base + * ref, so if we see a peeled ref that we don't want + * to fetch then we can mark the ref entry in the list + * as one to ignore by setting util to NULL. + */ + if (ends_with(ref->name, "^{}")) { + if (item && !has_sha1_file(ref->old_sha1) && + !will_fetch(head, ref->old_sha1) && + !has_sha1_file(item->util) && + !will_fetch(head, item->util)) + item->util = NULL; + item = NULL; + continue; + } + + /* + * If item is non-NULL here, then we previously saw a + * ref not followed by a peeled reference, so we need + * to check if it is a lightweight tag that we want to + * fetch. + */ + if (item && !has_sha1_file(item->util) && + !will_fetch(head, item->util)) + item->util = NULL; + + item = NULL; + + /* skip duplicates and refs that we already have */ + if (string_list_has_string(&remote_refs, ref->name) || + string_list_has_string(&existing_refs, ref->name)) + continue; + + item = string_list_insert(&remote_refs, ref->name); + item->util = (void *)ref->old_sha1; + } + string_list_clear(&existing_refs, 1); + + /* + * We may have a final lightweight tag that needs to be + * checked to see if it needs fetching. + */ + if (item && !has_sha1_file(item->util) && + !will_fetch(head, item->util)) + item->util = NULL; + + /* + * For all the tags in the remote_refs string list, + * add them to the list of refs to be fetched + */ + for_each_string_list_item(item, &remote_refs) { + /* Unless we have already decided to ignore this item... */ + if (item->util) + { + struct ref *rm = alloc_ref(item->string); + rm->peer_ref = alloc_ref(item->string); + hashcpy(rm->old_sha1, item->util); + **tail = rm; + *tail = &rm->next; + } + } + + string_list_clear(&remote_refs, 0); +} static struct ref *get_ref_map(struct transport *transport, - struct refspec *refs, int ref_count, int tags, - int *autotags) + struct refspec *refspecs, int refspec_count, + int tags, int *autotags) { int i; struct ref *rm; struct ref *ref_map = NULL; struct ref **tail = &ref_map; - const struct ref *remote_refs = transport_get_remote_refs(transport); + /* opportunistically-updated references: */ + struct ref *orefs = NULL, **oref_tail = &orefs; - if (ref_count || tags == TAGS_SET) { - struct ref **old_tail; + const struct ref *remote_refs = transport_get_remote_refs(transport); - for (i = 0; i < ref_count; i++) { - get_fetch_map(remote_refs, &refs[i], &tail, 0); - if (refs[i].dst && refs[i].dst[0]) + if (refspec_count) { + for (i = 0; i < refspec_count; i++) { + get_fetch_map(remote_refs, &refspecs[i], &tail, 0); + if (refspecs[i].dst && refspecs[i].dst[0]) *autotags = 1; } - /* Merge everything on the command line, but not --tags */ + /* Merge everything on the command line (but not --tags) */ for (rm = ref_map; rm; rm = rm->next) rm->fetch_head_status = FETCH_HEAD_MERGE; - if (tags == TAGS_SET) - get_fetch_map(remote_refs, tag_refspec, &tail, 0); /* - * For any refs that we happen to be fetching via command-line - * arguments, take the opportunity to update their configured - * counterparts. However, we do not want to mention these - * entries in FETCH_HEAD at all, as they would simply be - * duplicates of existing entries. + * For any refs that we happen to be fetching via + * command-line arguments, the destination ref might + * have been missing or have been different than the + * remote-tracking ref that would be derived from the + * configured refspec. In these cases, we want to + * take the opportunity to update their configured + * remote-tracking reference. However, we do not want + * to mention these entries in FETCH_HEAD at all, as + * they would simply be duplicates of existing + * entries, so we set them FETCH_HEAD_IGNORE below. + * + * We compute these entries now, based only on the + * refspecs specified on the command line. But we add + * them to the list following the refspecs resulting + * from the tags option so that one of the latter, + * which has FETCH_HEAD_NOT_FOR_MERGE, is not removed + * by ref_remove_duplicates() in favor of one of these + * opportunistic entries with FETCH_HEAD_IGNORE. */ - old_tail = tail; for (i = 0; i < transport->remote->fetch_refspec_nr; i++) get_fetch_map(ref_map, &transport->remote->fetch[i], - &tail, 1); - for (rm = *old_tail; rm; rm = rm->next) - rm->fetch_head_status = FETCH_HEAD_IGNORE; + &oref_tail, 1); + + if (tags == TAGS_SET) + get_fetch_map(remote_refs, tag_refspec, &tail, 0); } else { /* Use the defaults */ struct remote *remote = transport->remote; @@ -238,11 +347,21 @@ static struct ref *get_ref_map(struct transport *transport, tail = &ref_map->next; } } - if (tags == TAGS_DEFAULT && *autotags) + + if (tags == TAGS_SET) + /* also fetch all tags */ + get_fetch_map(remote_refs, tag_refspec, &tail, 0); + else if (tags == TAGS_DEFAULT && *autotags) find_non_local_tags(transport, &ref_map, &tail); - ref_remove_duplicates(ref_map); - return ref_map; + /* Now append any refs to be updated opportunistically: */ + *tail = orefs; + for (rm = orefs; rm; rm = rm->next) { + rm->fetch_head_status = FETCH_HEAD_IGNORE; + tail = &rm->next; + } + + return ref_remove_duplicates(ref_map); } #define STORE_REF_ERROR_OTHER 1 @@ -313,7 +432,7 @@ static int update_local_ref(struct ref *ref, } if (!is_null_sha1(ref->old_sha1) && - !prefixcmp(ref->name, "refs/tags/")) { + starts_with(ref->name, "refs/tags/")) { int r; r = s_update_ref("updating tag", ref, 0); strbuf_addf(display, "%c %-*s %-*s -> %s%s", @@ -336,10 +455,10 @@ static int update_local_ref(struct ref *ref, * more likely to follow a standard layout. */ const char *name = remote_ref ? remote_ref->name : ""; - if (!prefixcmp(name, "refs/tags/")) { + if (starts_with(name, "refs/tags/")) { msg = "storing tag"; what = _("[new tag]"); - } else if (!prefixcmp(name, "refs/heads/")) { + } else if (starts_with(name, "refs/heads/")) { msg = "storing head"; what = _("[new branch]"); } else { @@ -417,7 +536,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, { FILE *fp; struct commit *commit; - int url_len, i, shown_url = 0, rc = 0; + int url_len, i, rc = 0; struct strbuf note = STRBUF_INIT; const char *what, *kind; struct ref *rm; @@ -471,15 +590,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, kind = ""; what = ""; } - else if (!prefixcmp(rm->name, "refs/heads/")) { + else if (starts_with(rm->name, "refs/heads/")) { kind = "branch"; what = rm->name + 11; } - else if (!prefixcmp(rm->name, "refs/tags/")) { + else if (starts_with(rm->name, "refs/tags/")) { kind = "tag"; what = rm->name + 10; } - else if (!prefixcmp(rm->name, "refs/remotes/")) { + else if (starts_with(rm->name, "refs/remotes/")) { kind = "remote-tracking branch"; what = rm->name + 13; } @@ -590,17 +709,36 @@ 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) +static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map, + const char *raw_url) { - int result = 0; + int url_len, i, result = 0; struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map); + char *url; const char *dangling_msg = dry_run ? _(" (%s will become dangling)") : _(" (%s has become dangling)"); + if (raw_url) + url = transport_anonymize_url(raw_url); + else + url = xstrdup("foreign"); + + url_len = strlen(url); + for (i = url_len - 1; url[i] == '/' && 0 <= i; i--) + ; + + url_len = i + 1; + if (4 < i && !strncmp(".git", url + i - 3, 4)) + url_len = i - 3; + for (ref = stale_refs; ref; ref = ref->next) { if (!dry_run) result |= delete_ref(ref->name, NULL, 0); + if (verbosity >= 0 && !shown_url) { + fprintf(stderr, _("From %.*s\n"), url_len, url); + shown_url = 1; + } if (verbosity >= 0) { fprintf(stderr, " x %-*s %-*s -> %s\n", TRANSPORT_SUMMARY(_("[deleted]")), @@ -608,110 +746,11 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map) warn_dangling_symref(stderr, dangling_msg, ref->name); } } + free(url); free_refs(stale_refs); return result; } -static int add_existing(const char *refname, const unsigned char *sha1, - int flag, void *cbdata) -{ - struct string_list *list = (struct string_list *)cbdata; - struct string_list_item *item = string_list_insert(list, refname); - item->util = xmalloc(20); - hashcpy(item->util, sha1); - return 0; -} - -static int will_fetch(struct ref **head, const unsigned char *sha1) -{ - struct ref *rm = *head; - while (rm) { - if (!hashcmp(rm->old_sha1, sha1)) - return 1; - rm = rm->next; - } - return 0; -} - -static void find_non_local_tags(struct transport *transport, - struct ref **head, - struct ref ***tail) -{ - struct string_list existing_refs = STRING_LIST_INIT_DUP; - struct string_list remote_refs = STRING_LIST_INIT_NODUP; - const struct ref *ref; - struct string_list_item *item = NULL; - - for_each_ref(add_existing, &existing_refs); - for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) { - if (prefixcmp(ref->name, "refs/tags/")) - continue; - - /* - * The peeled ref always follows the matching base - * ref, so if we see a peeled ref that we don't want - * to fetch then we can mark the ref entry in the list - * as one to ignore by setting util to NULL. - */ - if (!suffixcmp(ref->name, "^{}")) { - if (item && !has_sha1_file(ref->old_sha1) && - !will_fetch(head, ref->old_sha1) && - !has_sha1_file(item->util) && - !will_fetch(head, item->util)) - item->util = NULL; - item = NULL; - continue; - } - - /* - * If item is non-NULL here, then we previously saw a - * ref not followed by a peeled reference, so we need - * to check if it is a lightweight tag that we want to - * fetch. - */ - if (item && !has_sha1_file(item->util) && - !will_fetch(head, item->util)) - item->util = NULL; - - item = NULL; - - /* skip duplicates and refs that we already have */ - if (string_list_has_string(&remote_refs, ref->name) || - string_list_has_string(&existing_refs, ref->name)) - continue; - - item = string_list_insert(&remote_refs, ref->name); - item->util = (void *)ref->old_sha1; - } - string_list_clear(&existing_refs, 1); - - /* - * We may have a final lightweight tag that needs to be - * checked to see if it needs fetching. - */ - if (item && !has_sha1_file(item->util) && - !will_fetch(head, item->util)) - item->util = NULL; - - /* - * For all the tags in the remote_refs string list, - * add them to the list of refs to be fetched - */ - for_each_string_list_item(item, &remote_refs) { - /* Unless we have already decided to ignore this item... */ - if (item->util) - { - struct ref *rm = alloc_ref(item->string); - rm->peer_ref = alloc_ref(item->string); - hashcpy(rm->old_sha1, item->util); - **tail = rm; - *tail = &rm->next; - } - } - - string_list_clear(&remote_refs, 0); -} - static void check_not_current_branch(struct ref *ref_map) { struct branch *current_branch = branch_get(NULL); @@ -824,39 +863,26 @@ static int do_fetch(struct transport *transport, if (tags == TAGS_DEFAULT && autotags) transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1"); - if (fetch_refs(transport, ref_map)) { - free_refs(ref_map); - retcode = 1; - goto cleanup; - } if (prune) { /* - * If --tags was specified, pretend that the user gave us - * the canonical tags refspec + * We only prune based on refspecs specified + * explicitly (via command line or configuration); we + * don't care whether --tags was specified. */ - if (tags == TAGS_SET) { - const char *tags_str = "refs/tags/*:refs/tags/*"; - struct refspec *tags_refspec, *refspec; - - /* Copy the refspec and add the tags to it */ - refspec = xcalloc(ref_count + 1, sizeof(struct refspec)); - tags_refspec = parse_fetch_refspec(1, &tags_str); - memcpy(refspec, refs, ref_count * sizeof(struct refspec)); - memcpy(&refspec[ref_count], tags_refspec, sizeof(struct refspec)); - ref_count++; - - prune_refs(refspec, ref_count, ref_map); - - ref_count--; - /* The rest of the strings belong to fetch_one */ - free_refspec(1, tags_refspec); - free(refspec); - } else if (ref_count) { - prune_refs(refs, ref_count, ref_map); + if (ref_count) { + prune_refs(refs, ref_count, ref_map, transport->url); } else { - prune_refs(transport->remote->fetch, transport->remote->fetch_refspec_nr, ref_map); + prune_refs(transport->remote->fetch, + transport->remote->fetch_refspec_nr, + ref_map, + transport->url); } } + if (fetch_refs(transport, ref_map)) { + free_refs(ref_map); + retcode = 1; + goto cleanup; + } free_refs(ref_map); /* if neither --no-tags nor --tags was specified, do automated tag @@ -892,7 +918,7 @@ static int get_remote_group(const char *key, const char *value, void *priv) { struct remote_group_data *g = priv; - if (!prefixcmp(key, "remotes.") && + if (starts_with(key, "remotes.") && !strcmp(key + 8, g->name)) { /* split list by white space */ int space = strcspn(value, " \t\n"); @@ -930,8 +956,8 @@ static void add_options_to_argv(struct argv_array *argv) { if (dry_run) argv_array_push(argv, "--dry-run"); - if (prune > 0) - argv_array_push(argv, "--prune"); + if (prune != -1) + argv_array_push(argv, prune ? "--prune" : "--no-prune"); if (update_head_ok) argv_array_push(argv, "--update-head-ok"); if (force) @@ -1075,6 +1101,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) } } + /* no need to be strict, transport_set_option() will validate it again */ + if (depth && atoi(depth) < 1) + die(_("depth %s is not a positive number"), depth); + if (recurse_submodules != RECURSE_SUBMODULES_OFF) { if (recurse_submodules_default) { int arg = parse_fetch_recurse_submodules_arg("--recurse-submodules-default", recurse_submodules_default); diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index 1c04070869..3906eda877 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -109,7 +109,7 @@ static int handle_line(char *line, struct merge_parents *merge_parents) if (len < 43 || line[40] != '\t') return 1; - if (!prefixcmp(line + 41, "not-for-merge")) + if (starts_with(line + 41, "not-for-merge")) return 0; if (line[41] != '\t') @@ -155,16 +155,16 @@ static int handle_line(char *line, struct merge_parents *merge_parents) if (pulling_head) { origin = src; src_data->head_status |= 1; - } else if (!prefixcmp(line, "branch ")) { + } else if (starts_with(line, "branch ")) { origin_data->is_local_branch = 1; origin = line + 7; string_list_append(&src_data->branch, origin); src_data->head_status |= 2; - } else if (!prefixcmp(line, "tag ")) { + } else if (starts_with(line, "tag ")) { origin = line; string_list_append(&src_data->tag, origin + 4); src_data->head_status |= 2; - } else if (!prefixcmp(line, "remote-tracking branch ")) { + } else if (starts_with(line, "remote-tracking branch ")) { origin = line + strlen("remote-tracking branch "); string_list_append(&src_data->r_branch, origin); src_data->head_status |= 2; @@ -605,7 +605,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out, resolve_refdup("HEAD", head_sha1, 1, NULL); if (!current_branch) die("No current branch"); - if (!prefixcmp(current_branch, "refs/heads/")) + if (starts_with(current_branch, "refs/heads/")) current_branch += 11; find_merge_parents(&merge_parents, in, head_sha1); diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 7557aa239e..51798b48b5 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -9,6 +9,7 @@ #include "quote.h" #include "parse-options.h" #include "remote.h" +#include "color.h" /* Quoting styles */ #define QUOTE_NONE 0 @@ -75,6 +76,8 @@ static struct { { "upstream" }, { "symref" }, { "flag" }, + { "HEAD" }, + { "color" }, }; /* @@ -90,6 +93,7 @@ static struct { static const char **used_atom; static cmp_type *used_atom_type; static int used_atom_cnt, need_tagged, need_symref; +static int need_color_reset_at_eol; /* * Used to parse format string and sort specifiers @@ -176,13 +180,21 @@ static const char *find_next(const char *cp) static int verify_format(const char *format) { const char *cp, *sp; + static const char color_reset[] = "color:reset"; + + need_color_reset_at_eol = 0; for (cp = format; *cp && (sp = find_next(cp)); ) { const char *ep = strchr(sp, ')'); + int at; + if (!ep) return error("malformed format string %s", sp); /* sp points at "%(" and ep points at the closing ")" */ - parse_atom(sp + 2, ep); + at = parse_atom(sp + 2, ep); cp = ep + 1; + + if (!memcmp(used_atom[at], "color:", 6)) + need_color_reset_at_eol = !!strcmp(used_atom[at], color_reset); } return 0; } @@ -441,7 +453,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru if (name[wholen] != 0 && strcmp(name + wholen, "name") && strcmp(name + wholen, "email") && - prefixcmp(name + wholen, "date")) + !starts_with(name + wholen, "date")) continue; if (!wholine) wholine = find_wholine(who, wholen, buf, sz); @@ -453,7 +465,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru v->s = copy_name(wholine); else if (!strcmp(name + wholen, "email")) v->s = copy_email(wholine); - else if (!prefixcmp(name + wholen, "date")) + else if (starts_with(name + wholen, "date")) grab_date(wholine, v, name); } @@ -475,7 +487,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru if (deref) name++; - if (!prefixcmp(name, "creatordate")) + if (starts_with(name, "creatordate")) grab_date(wholine, v, name); else if (!strcmp(name, "creator")) v->s = copy_line(wholine); @@ -649,20 +661,20 @@ static void populate_value(struct refinfo *ref) int deref = 0; const char *refname; const char *formatp; + struct branch *branch = NULL; if (*name == '*') { deref = 1; name++; } - if (!prefixcmp(name, "refname")) + if (starts_with(name, "refname")) refname = ref->refname; - else if (!prefixcmp(name, "symref")) + else if (starts_with(name, "symref")) refname = ref->symref ? ref->symref : ""; - else if (!prefixcmp(name, "upstream")) { - struct branch *branch; + else if (starts_with(name, "upstream")) { /* only local branches may have an upstream */ - if (prefixcmp(ref->refname, "refs/heads/")) + if (!starts_with(ref->refname, "refs/heads/")) continue; branch = branch_get(ref->refname + 11); @@ -670,8 +682,13 @@ static void populate_value(struct refinfo *ref) !branch->merge[0]->dst) continue; refname = branch->merge[0]->dst; - } - else if (!strcmp(name, "flag")) { + } else if (starts_with(name, "color:")) { + char color[COLOR_MAXLEN] = ""; + + color_parse(name + 6, "--format", color); + v->s = xstrdup(color); + continue; + } else if (!strcmp(name, "flag")) { char buf[256], *cp = buf; if (ref->flag & REF_ISSYMREF) cp = copy_advance(cp, ",symref"); @@ -684,20 +701,62 @@ static void populate_value(struct refinfo *ref) v->s = xstrdup(buf + 1); } continue; - } - else if (!deref && grab_objectname(name, ref->objectname, v)) + } else if (!deref && grab_objectname(name, ref->objectname, v)) { continue; - else + } else if (!strcmp(name, "HEAD")) { + const char *head; + unsigned char sha1[20]; + + head = resolve_ref_unsafe("HEAD", sha1, 1, NULL); + if (!strcmp(ref->refname, head)) + v->s = "*"; + else + v->s = " "; + continue; + } else continue; formatp = strchr(name, ':'); - /* look for "short" refname format */ if (formatp) { + int num_ours, num_theirs; + formatp++; if (!strcmp(formatp, "short")) refname = shorten_unambiguous_ref(refname, warn_ambiguous_refs); - else + else if (!strcmp(formatp, "track") && + starts_with(name, "upstream")) { + char buf[40]; + + stat_tracking_info(branch, &num_ours, &num_theirs); + if (!num_ours && !num_theirs) + v->s = ""; + else if (!num_ours) { + sprintf(buf, "[behind %d]", num_theirs); + v->s = xstrdup(buf); + } else if (!num_theirs) { + sprintf(buf, "[ahead %d]", num_ours); + v->s = xstrdup(buf); + } else { + sprintf(buf, "[ahead %d, behind %d]", + num_ours, num_theirs); + v->s = xstrdup(buf); + } + continue; + } else if (!strcmp(formatp, "trackshort") && + starts_with(name, "upstream")) { + assert(branch); + stat_tracking_info(branch, &num_ours, &num_theirs); + if (!num_ours && !num_theirs) + v->s = "="; + else if (!num_ours) + v->s = "<"; + else if (!num_theirs) + v->s = ">"; + else + v->s = "<>"; + continue; + } else die("unknown %.*s format %s", (int)(formatp - name), name, formatp); } @@ -875,11 +934,9 @@ static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs qsort(refs, num_refs, sizeof(struct refinfo *), compare_refs); } -static void print_value(struct refinfo *ref, int atom, int quote_style) +static void print_value(struct atom_value *v, int quote_style) { - struct atom_value *v; struct strbuf sb = STRBUF_INIT; - get_value(ref, atom, &v); switch (quote_style) { case QUOTE_NONE: fputs(v->s, stdout); @@ -946,15 +1003,26 @@ static void show_ref(struct refinfo *info, const char *format, int quote_style) const char *cp, *sp, *ep; for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) { + struct atom_value *atomv; + ep = strchr(sp, ')'); if (cp < sp) emit(cp, sp); - print_value(info, parse_atom(sp + 2, ep), quote_style); + get_value(info, parse_atom(sp + 2, ep), &atomv); + print_value(atomv, quote_style); } if (*cp) { sp = cp + strlen(cp); emit(cp, sp); } + if (need_color_reset_at_eol) { + struct atom_value resetv; + char color[COLOR_MAXLEN] = ""; + + color_parse("reset", "--format", color); + resetv.s = color; + print_value(&resetv, quote_style); + } putchar('\n'); } diff --git a/builtin/fsck.c b/builtin/fsck.c index 97ce678c6b..1affdd5e92 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -442,7 +442,7 @@ static void fsck_dir(int i, char *path) add_sha1_list(sha1, DIRENT_SORT_HINT(de)); continue; } - if (!prefixcmp(de->d_name, "tmp_obj_")) + if (starts_with(de->d_name, "tmp_obj_")) continue; fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name); } @@ -484,7 +484,7 @@ static int fsck_handle_reflog(const char *logname, const unsigned char *sha1, in static int is_branch(const char *refname) { - return !strcmp(refname, "HEAD") || !prefixcmp(refname, "refs/heads/"); + return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/"); } static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) @@ -566,7 +566,7 @@ static int fsck_head_link(void) if (!strcmp(head_points_at, "HEAD")) /* detached HEAD */ null_is_error = 1; - else if (prefixcmp(head_points_at, "refs/heads/")) + else if (!starts_with(head_points_at, "refs/heads/")) return error("HEAD points to something strange (%s)", head_points_at); if (is_null_sha1(head_sha1)) { diff --git a/builtin/gc.c b/builtin/gc.c index c14190f840..25f2237c08 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -222,7 +222,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid) time(NULL) - st.st_mtime <= 12 * 3600 && fscanf(fp, "%"PRIuMAX" %127c", &pid, locking_host) == 2 && /* be gentle to concurrent "gc" on remote hosts */ - (strcmp(locking_host, my_host) || !kill(pid, 0)); + (strcmp(locking_host, my_host) || !kill(pid, 0) || errno == EPERM); if (fp != NULL) fclose(fp); if (should_exit) { diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c new file mode 100644 index 0000000000..aa72596083 --- /dev/null +++ b/builtin/get-tar-commit-id.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2005, 2006 Rene Scharfe + */ +#include "cache.h" +#include "commit.h" +#include "tar.h" +#include "builtin.h" +#include "quote.h" + +static const char builtin_get_tar_commit_id_usage[] = +"git get-tar-commit-id < <tarfile>"; + +/* ustar header + extended global header content */ +#define RECORDSIZE (512) +#define HEADERSIZE (2 * RECORDSIZE) + +int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix) +{ + char buffer[HEADERSIZE]; + struct ustar_header *header = (struct ustar_header *)buffer; + char *content = buffer + RECORDSIZE; + ssize_t n; + + if (argc != 1) + usage(builtin_get_tar_commit_id_usage); + + n = read_in_full(0, buffer, HEADERSIZE); + if (n < HEADERSIZE) + die("git get-tar-commit-id: read error"); + if (header->typeflag[0] != 'g') + return 1; + if (memcmp(content, "52 comment=", 11)) + return 1; + + n = write_in_full(1, content + 11, 41); + if (n < 41) + die_errno("git get-tar-commit-id: write error"); + + return 0; +} diff --git a/builtin/help.c b/builtin/help.c index f1e236b912..cc17e670ce 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -100,7 +100,7 @@ static int check_emacsclient_version(void) */ finish_command(&ec_process); - if (prefixcmp(buffer.buf, "emacsclient")) { + if (!starts_with(buffer.buf, "emacsclient")) { strbuf_release(&buffer); return error(_("Failed to parse emacsclient version.")); } @@ -258,7 +258,7 @@ static int add_man_viewer_info(const char *var, const char *value) static int git_help_config(const char *var, const char *value, void *cb) { - if (!prefixcmp(var, "column.")) + if (starts_with(var, "column.")) return git_column_config(var, value, "help", &colopts); if (!strcmp(var, "help.format")) { if (!value) @@ -278,7 +278,7 @@ static int git_help_config(const char *var, const char *value, void *cb) add_man_viewer(value); return 0; } - if (!prefixcmp(var, "man.")) + if (starts_with(var, "man.")) return add_man_viewer_info(var, value); return git_default_config(var, value, cb); @@ -306,7 +306,7 @@ static const char *cmd_to_page(const char *git_cmd) { if (!git_cmd) return "git"; - else if (!prefixcmp(git_cmd, "git")) + else if (starts_with(git_cmd, "git")) return git_cmd; else if (is_git_command(git_cmd)) return prepend("git-", git_cmd); diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 9e9eb4b74e..2f37a38fbc 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1534,9 +1534,9 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) stat_only = 1; } else if (!strcmp(arg, "--keep")) { keep_msg = ""; - } else if (!prefixcmp(arg, "--keep=")) { + } else if (starts_with(arg, "--keep=")) { keep_msg = arg + 7; - } else if (!prefixcmp(arg, "--threads=")) { + } else if (starts_with(arg, "--threads=")) { char *end; nr_threads = strtoul(arg+10, &end, 0); if (!arg[10] || *end || nr_threads < 0) @@ -1547,7 +1547,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) "ignoring %s"), arg); nr_threads = 1; #endif - } else if (!prefixcmp(arg, "--pack_header=")) { + } else if (starts_with(arg, "--pack_header=")) { struct pack_header *hdr; char *c; @@ -1566,7 +1566,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) if (index_name || (i+1) >= argc) usage(index_pack_usage); index_name = argv[++i]; - } else if (!prefixcmp(arg, "--index-version=")) { + } else if (starts_with(arg, "--index-version=")) { char *c; opts.version = strtoul(arg + 16, &c, 10); if (opts.version > 2) diff --git a/builtin/init-db.c b/builtin/init-db.c index 78aa3872dd..b3f03cf0d6 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -266,7 +266,7 @@ static int create_default_files(const char *template_path) /* allow template config file to override the default */ if (log_all_ref_updates == -1) git_config_set("core.logallrefupdates", "true"); - if (prefixcmp(git_dir, work_tree) || + if (!starts_with(git_dir, work_tree) || strcmp(git_dir + strlen(work_tree), "/.git")) { git_config_set("core.worktree", work_tree); } diff --git a/builtin/log.c b/builtin/log.c index b708517a35..b97373da3b 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -391,7 +391,7 @@ static int git_log_config(const char *var, const char *value, void *cb) default_show_root = git_config_bool(var, value); return 0; } - if (!prefixcmp(var, "color.decorate.")) + if (starts_with(var, "color.decorate.")) return parse_decorate_color_config(var, 15, value); if (!strcmp(var, "log.mailmap")) { use_mailmap_config = git_config_bool(var, value); @@ -477,7 +477,7 @@ static int show_tag_object(const unsigned char *sha1, struct rev_info *rev) int new_offset = offset + 1; while (new_offset < size && buf[new_offset++] != '\n') ; /* do nothing */ - if (!prefixcmp(buf + offset, "tagger ")) + if (starts_with(buf + offset, "tagger ")) show_tagger(buf + offset + 7, new_offset - offset - 7, rev); offset = new_offset; @@ -882,7 +882,7 @@ static char *find_branch_name(struct rev_info *rev) ref = rev->cmdline.rev[positive].name; tip_sha1 = rev->cmdline.rev[positive].item->sha1; if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) && - !prefixcmp(full_ref, "refs/heads/") && + starts_with(full_ref, "refs/heads/") && !hashcmp(tip_sha1, branch_sha1)) branch = xstrdup(full_ref + strlen("refs/heads/")); free(full_ref); @@ -1388,7 +1388,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) unsigned char sha1[20]; const char *ref; ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL); - if (ref && !prefixcmp(ref, "refs/heads/")) + if (ref && starts_with(ref, "refs/heads/")) branch_name = xstrdup(ref + strlen("refs/heads/")); else branch_name = xstrdup(""); /* no branch */ diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index 25e83cfe9d..39e5144b9e 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -50,11 +50,11 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) const char *arg = argv[i]; if (*arg == '-') { - if (!prefixcmp(arg, "--upload-pack=")) { + if (starts_with(arg, "--upload-pack=")) { uploadpack = arg + 14; continue; } - if (!prefixcmp(arg, "--exec=")) { + if (starts_with(arg, "--exec=")) { uploadpack = arg + 7; continue; } diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c index 24a772d8e1..2c3cd8eab7 100644 --- a/builtin/mailinfo.c +++ b/builtin/mailinfo.c @@ -328,11 +328,11 @@ static int check_header(const struct strbuf *line, } /* for inbody stuff */ - if (!prefixcmp(line->buf, ">From") && isspace(line->buf[5])) { + if (starts_with(line->buf, ">From") && isspace(line->buf[5])) { ret = 1; /* Should this return 0? */ goto check_header_out; } - if (!prefixcmp(line->buf, "[PATCH]") && isspace(line->buf[7])) { + if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) { for (i = 0; header[i]; i++) { if (!memcmp("Subject", header[i], 7)) { handle_header(&hdr_data[i], line); @@ -361,7 +361,7 @@ static int is_rfc2822_header(const struct strbuf *line) char *cp = line->buf; /* Count mbox From headers as headers */ - if (!prefixcmp(cp, "From ") || !prefixcmp(cp, ">From ")) + if (starts_with(cp, "From ") || starts_with(cp, ">From ")) return 1; while ((ch = *cp++)) { @@ -671,11 +671,11 @@ static inline int patchbreak(const struct strbuf *line) size_t i; /* Beginning of a "diff -" header? */ - if (!prefixcmp(line->buf, "diff -")) + if (starts_with(line->buf, "diff -")) return 1; /* CVS "Index: " line? */ - if (!prefixcmp(line->buf, "Index: ")) + if (starts_with(line->buf, "Index: ")) return 1; /* @@ -685,7 +685,7 @@ static inline int patchbreak(const struct strbuf *line) if (line->len < 4) return 0; - if (!prefixcmp(line->buf, "---")) { + if (starts_with(line->buf, "---")) { /* space followed by a filename? */ if (line->buf[3] == ' ' && !isspace(line->buf[4])) return 1; @@ -986,7 +986,7 @@ static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch) static int git_mailinfo_config(const char *var, const char *value, void *unused) { - if (prefixcmp(var, "mailinfo.")) + if (!starts_with(var, "mailinfo.")) return git_default_config(var, value, unused); if (!strcmp(var, "mailinfo.scissors")) { use_scissors = git_config_bool(var, value); @@ -1020,7 +1020,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix) metainfo_charset = def_charset; else if (!strcmp(argv[1], "-n")) metainfo_charset = NULL; - else if (!prefixcmp(argv[1], "--encoding=")) + else if (starts_with(argv[1], "--encoding=")) metainfo_charset = argv[1] + 11; else if (!strcmp(argv[1], "--scissors")) use_scissors = 1; diff --git a/builtin/merge-base.c b/builtin/merge-base.c index e88eb93f14..0ecde8da30 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -1,6 +1,9 @@ #include "builtin.h" #include "cache.h" #include "commit.h" +#include "refs.h" +#include "diff.h" +#include "revision.h" #include "parse-options.h" static int show_merge_base(struct commit **rev, int rev_nr, int show_all) @@ -27,6 +30,7 @@ static const char * const merge_base_usage[] = { N_("git merge-base [-a|--all] --octopus <commit>..."), N_("git merge-base --independent <commit>..."), N_("git merge-base --is-ancestor <commit> <commit>"), + N_("git merge-base --fork-point <ref> [<commit>]"), NULL }; @@ -44,19 +48,36 @@ static struct commit *get_commit_reference(const char *arg) return r; } -static int handle_octopus(int count, const char **args, int reduce, int show_all) +static int handle_independent(int count, const char **args) { struct commit_list *revs = NULL; struct commit_list *result; int i; - if (reduce) - show_all = 1; + for (i = count - 1; i >= 0; i--) + commit_list_insert(get_commit_reference(args[i]), &revs); + + result = reduce_heads(revs); + if (!result) + return 1; + + while (result) { + printf("%s\n", sha1_to_hex(result->item->object.sha1)); + result = result->next; + } + return 0; +} + +static int handle_octopus(int count, const char **args, int show_all) +{ + struct commit_list *revs = NULL; + struct commit_list *result; + int i; for (i = count - 1; i >= 0; i--) commit_list_insert(get_commit_reference(args[i]), &revs); - result = reduce ? reduce_heads(revs) : get_octopus_merge_bases(revs); + result = reduce_heads(get_octopus_merge_bases(revs)); if (!result) return 1; @@ -85,37 +106,151 @@ static int handle_is_ancestor(int argc, const char **argv) return 1; } +struct rev_collect { + struct commit **commit; + int nr; + int alloc; + unsigned int initial : 1; +}; + +static void add_one_commit(unsigned char *sha1, struct rev_collect *revs) +{ + struct commit *commit; + + if (is_null_sha1(sha1)) + return; + + commit = lookup_commit(sha1); + if (!commit || + (commit->object.flags & TMP_MARK) || + parse_commit(commit)) + return; + + ALLOC_GROW(revs->commit, revs->nr + 1, revs->alloc); + revs->commit[revs->nr++] = commit; + commit->object.flags |= TMP_MARK; +} + +static int collect_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1, + const char *ident, unsigned long timestamp, + int tz, const char *message, void *cbdata) +{ + struct rev_collect *revs = cbdata; + + if (revs->initial) { + revs->initial = 0; + add_one_commit(osha1, revs); + } + add_one_commit(nsha1, revs); + return 0; +} + +static int handle_fork_point(int argc, const char **argv) +{ + unsigned char sha1[20]; + char *refname; + const char *commitname; + struct rev_collect revs; + struct commit *derived; + struct commit_list *bases; + int i, ret = 0; + + switch (dwim_ref(argv[0], strlen(argv[0]), sha1, &refname)) { + case 0: + die("No such ref: '%s'", argv[0]); + case 1: + break; /* good */ + default: + die("Ambiguous refname: '%s'", argv[0]); + } + + commitname = (argc == 2) ? argv[1] : "HEAD"; + if (get_sha1(commitname, sha1)) + die("Not a valid object name: '%s'", commitname); + + derived = lookup_commit_reference(sha1); + memset(&revs, 0, sizeof(revs)); + revs.initial = 1; + for_each_reflog_ent(refname, collect_one_reflog_ent, &revs); + + for (i = 0; i < revs.nr; i++) + revs.commit[i]->object.flags &= ~TMP_MARK; + + bases = get_merge_bases_many(derived, revs.nr, revs.commit, 0); + + /* + * There should be one and only one merge base, when we found + * a common ancestor among reflog entries. + */ + if (!bases || bases->next) { + ret = 1; + goto cleanup_return; + } + + /* And the found one must be one of the reflog entries */ + for (i = 0; i < revs.nr; i++) + if (&bases->item->object == &revs.commit[i]->object) + break; /* found */ + if (revs.nr <= i) { + ret = 1; /* not found */ + goto cleanup_return; + } + + printf("%s\n", sha1_to_hex(bases->item->object.sha1)); + +cleanup_return: + free_commit_list(bases); + return ret; +} + int cmd_merge_base(int argc, const char **argv, const char *prefix) { struct commit **rev; int rev_nr = 0; int show_all = 0; - int octopus = 0; - int reduce = 0; - int is_ancestor = 0; + int cmdmode = 0; struct option options[] = { OPT_BOOL('a', "all", &show_all, N_("output all common ancestors")), - OPT_BOOL(0, "octopus", &octopus, N_("find ancestors for a single n-way merge")), - OPT_BOOL(0, "independent", &reduce, N_("list revs not reachable from others")), - OPT_BOOL(0, "is-ancestor", &is_ancestor, - N_("is the first one ancestor of the other?")), + OPT_CMDMODE(0, "octopus", &cmdmode, + N_("find ancestors for a single n-way merge"), 'o'), + OPT_CMDMODE(0, "independent", &cmdmode, + N_("list revs not reachable from others"), 'r'), + OPT_CMDMODE(0, "is-ancestor", &cmdmode, + N_("is the first one ancestor of the other?"), 'a'), + OPT_CMDMODE(0, "fork-point", &cmdmode, + N_("find where <commit> forked from reflog of <ref>"), 'f'), OPT_END() }; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0); - if (!octopus && !reduce && argc < 2) - usage_with_options(merge_base_usage, options); - if (is_ancestor && (show_all || octopus || reduce)) - die("--is-ancestor cannot be used with other options"); - if (is_ancestor) + + if (cmdmode == 'a') { + if (argc < 2) + usage_with_options(merge_base_usage, options); + if (show_all) + die("--is-ancestor cannot be used with --all"); return handle_is_ancestor(argc, argv); - if (reduce && (show_all || octopus)) - die("--independent cannot be used with other options"); + } - if (octopus || reduce) - return handle_octopus(argc, argv, reduce, show_all); + if (cmdmode == 'r' && show_all) + die("--independent cannot be used with --all"); + + if (cmdmode == 'o') + return handle_octopus(argc, argv, show_all); + + if (cmdmode == 'r') + return handle_independent(argc, argv); + + if (cmdmode == 'f') { + if (argc < 1 || 2 < argc) + usage_with_options(merge_base_usage, options); + return handle_fork_point(argc, argv); + } + + if (argc < 2) + usage_with_options(merge_base_usage, options); rev = xmalloc(argc * sizeof(*rev)); while (argc-- > 0) diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c index 3a64f5d0bd..a90f28f34d 100644 --- a/builtin/merge-recursive.c +++ b/builtin/merge-recursive.c @@ -29,7 +29,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix) struct commit *result; init_merge_options(&o); - if (argv[0] && !suffixcmp(argv[0], "-subtree")) + if (argv[0] && ends_with(argv[0], "-subtree")) o.subtree_shift = ""; if (argc < 4) @@ -38,7 +38,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix) for (i = 1; i < argc; ++i) { const char *arg = argv[i]; - if (!prefixcmp(arg, "--")) { + if (starts_with(arg, "--")) { if (!arg[2]) break; if (parse_merge_opt(&o, arg + 2)) diff --git a/builtin/merge.c b/builtin/merge.c index 41fb66dec2..4941a6c36a 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -446,17 +446,17 @@ static void merge_name(const char *remote, struct strbuf *msg) die(_("'%s' does not point to a commit"), remote); if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) { - if (!prefixcmp(found_ref, "refs/heads/")) { + if (starts_with(found_ref, "refs/heads/")) { strbuf_addf(msg, "%s\t\tbranch '%s' of .\n", sha1_to_hex(branch_head), remote); goto cleanup; } - if (!prefixcmp(found_ref, "refs/tags/")) { + if (starts_with(found_ref, "refs/tags/")) { strbuf_addf(msg, "%s\t\ttag '%s' of .\n", sha1_to_hex(branch_head), remote); goto cleanup; } - if (!prefixcmp(found_ref, "refs/remotes/")) { + if (starts_with(found_ref, "refs/remotes/")) { strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n", sha1_to_hex(branch_head), remote); goto cleanup; @@ -570,8 +570,8 @@ static int git_merge_config(const char *k, const char *v, void *cb) { int status; - if (branch && !prefixcmp(k, "branch.") && - !prefixcmp(k + 7, branch) && + if (branch && starts_with(k, "branch.") && + starts_with(k + 7, branch) && !strcmp(k + 7 + strlen(branch), ".mergeoptions")) { free(branch_mergeoptions); branch_mergeoptions = xstrdup(v); @@ -1106,7 +1106,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * current branch. */ branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag); - if (branch && !prefixcmp(branch, "refs/heads/")) + if (branch && starts_with(branch, "refs/heads/")) branch += 11; if (!branch || is_null_sha1(head_sha1)) head_commit = NULL; diff --git a/builtin/mv.c b/builtin/mv.c index 2e0e61b651..08fbc033e4 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -16,9 +16,12 @@ static const char * const builtin_mv_usage[] = { NULL }; +#define DUP_BASENAME 1 +#define KEEP_TRAILING_SLASH 2 + static const char **internal_copy_pathspec(const char *prefix, const char **pathspec, - int count, int base_name) + int count, unsigned flags) { int i; const char **result = xmalloc((count + 1) * sizeof(const char *)); @@ -27,11 +30,12 @@ static const char **internal_copy_pathspec(const char *prefix, for (i = 0; i < count; i++) { int length = strlen(result[i]); int to_copy = length; - while (to_copy > 0 && is_dir_sep(result[i][to_copy - 1])) + while (!(flags & KEEP_TRAILING_SLASH) && + to_copy > 0 && is_dir_sep(result[i][to_copy - 1])) to_copy--; - if (to_copy != length || base_name) { + if (to_copy != length || flags & DUP_BASENAME) { char *it = xmemdupz(result[i], to_copy); - if (base_name) { + if (flags & DUP_BASENAME) { result[i] = xstrdup(basename(it)); free(it); } else @@ -87,16 +91,21 @@ int cmd_mv(int argc, const char **argv, const char *prefix) source = internal_copy_pathspec(prefix, argv, argc, 0); modes = xcalloc(argc, sizeof(enum update_mode)); - dest_path = internal_copy_pathspec(prefix, argv + argc, 1, 0); + /* + * Keep trailing slash, needed to let + * "git mv file no-such-dir/" error out. + */ + dest_path = internal_copy_pathspec(prefix, argv + argc, 1, + KEEP_TRAILING_SLASH); submodule_gitfile = xcalloc(argc, sizeof(char *)); if (dest_path[0][0] == '\0') /* special case: "." was normalized to "" */ - destination = internal_copy_pathspec(dest_path[0], argv, argc, 1); + destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME); else if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode)) { dest_path[0] = add_slash(dest_path[0]); - destination = internal_copy_pathspec(dest_path[0], argv, argc, 1); + destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME); } else { if (argc != 1) die("destination '%s' is not a directory", dest_path[0]); diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 20fcf8c696..0b21d7e5b2 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -27,8 +27,7 @@ static void name_rev(struct commit *commit, struct commit_list *parents; int parent_number = 1; - if (!commit->object.parsed) - parse_commit(commit); + parse_commit(commit); if (commit->date < cutoff) return; @@ -101,9 +100,9 @@ static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous) { if (shorten_unambiguous) refname = shorten_unambiguous_ref(refname, 0); - else if (!prefixcmp(refname, "refs/heads/")) + else if (starts_with(refname, "refs/heads/")) refname = refname + 11; - else if (!prefixcmp(refname, "refs/")) + else if (starts_with(refname, "refs/")) refname = refname + 5; return refname; } @@ -149,7 +148,7 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void int can_abbreviate_output = data->tags_only && data->name_only; int deref = 0; - if (data->tags_only && prefixcmp(path, "refs/tags/")) + if (data->tags_only && !starts_with(path, "refs/tags/")) return 0; if (data->ref_filter) { diff --git a/builtin/notes.c b/builtin/notes.c index d459e23c42..2b24d059b5 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -347,7 +347,7 @@ static struct notes_tree *init_notes_check(const char *subcommand) init_notes(NULL, NULL, NULL, 0); t = &default_notes_tree; - if (prefixcmp(t->ref, "refs/notes/")) + if (!starts_with(t->ref, "refs/notes/")) die("Refusing to %s notes in %s (outside of refs/notes/)", subcommand, t->ref); return t; diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 36273dd6f0..541667f102 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -737,8 +737,6 @@ static void write_pack_file(void) f = create_tmp_packfile(&pack_tmp_name); offset = write_pack_header(f, nr_remaining); - if (!offset) - die_errno("unable to write pack header"); nr_written = 0; for (; i < nr_objects; i++) { struct object_entry *e = write_order[i]; @@ -2032,7 +2030,7 @@ static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, vo { unsigned char peeled[20]; - if (!prefixcmp(path, "refs/tags/") && /* is a tag? */ + if (starts_with(path, "refs/tags/") && /* is a tag? */ !peel_ref(path, peeled) && /* peelable? */ locate_object_entry(peeled)) /* object packed? */ add_object_entry(sha1, OBJ_TAG, NULL, 0); diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c index fa6ce42f44..fcf5fb6129 100644 --- a/builtin/prune-packed.c +++ b/builtin/prune-packed.c @@ -10,58 +10,62 @@ static const char * const prune_packed_usage[] = { static struct progress *progress; -static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts) +static void prune_dir(int i, DIR *dir, struct strbuf *pathname, int opts) { struct dirent *de; char hex[40]; + int top_len = pathname->len; sprintf(hex, "%02x", i); while ((de = readdir(dir)) != NULL) { unsigned char sha1[20]; if (strlen(de->d_name) != 38) continue; - memcpy(hex+2, de->d_name, 38); + memcpy(hex + 2, de->d_name, 38); if (get_sha1_hex(hex, sha1)) continue; if (!has_sha1_pack(sha1)) continue; - memcpy(pathname + len, de->d_name, 38); + + strbuf_add(pathname, de->d_name, 38); if (opts & PRUNE_PACKED_DRY_RUN) - printf("rm -f %s\n", pathname); + printf("rm -f %s\n", pathname->buf); else - unlink_or_warn(pathname); + unlink_or_warn(pathname->buf); display_progress(progress, i + 1); + strbuf_setlen(pathname, top_len); } } void prune_packed_objects(int opts) { int i; - static char pathname[PATH_MAX]; const char *dir = get_object_directory(); - int len = strlen(dir); + struct strbuf pathname = STRBUF_INIT; + int top_len; + strbuf_addstr(&pathname, dir); if (opts & PRUNE_PACKED_VERBOSE) progress = start_progress_delay("Removing duplicate objects", 256, 95, 2); - if (len > PATH_MAX - 42) - die("impossible object directory"); - memcpy(pathname, dir, len); - if (len && pathname[len-1] != '/') - pathname[len++] = '/'; + if (pathname.len && pathname.buf[pathname.len - 1] != '/') + strbuf_addch(&pathname, '/'); + + top_len = pathname.len; for (i = 0; i < 256; i++) { DIR *d; display_progress(progress, i + 1); - sprintf(pathname + len, "%02x/", i); - d = opendir(pathname); + strbuf_setlen(&pathname, top_len); + strbuf_addf(&pathname, "%02x/", i); + d = opendir(pathname.buf); if (!d) continue; - prune_dir(i, d, pathname, len + 3, opts); + prune_dir(i, d, &pathname, opts); closedir(d); - pathname[len + 2] = '\0'; - rmdir(pathname); + strbuf_setlen(&pathname, top_len + 2); + rmdir(pathname.buf); } stop_progress(&progress); } diff --git a/builtin/prune.c b/builtin/prune.c index 6366917c6d..beb0dc942c 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -17,9 +17,8 @@ static int verbose; static unsigned long expire; static int show_progress = -1; -static int prune_tmp_object(const char *path, const char *filename) +static int prune_tmp_file(const char *fullpath) { - const char *fullpath = mkpath("%s/%s", path, filename); struct stat st; if (lstat(fullpath, &st)) return error("Could not stat '%s'", fullpath); @@ -32,9 +31,8 @@ static int prune_tmp_object(const char *path, const char *filename) return 0; } -static int prune_object(char *path, const char *filename, const unsigned char *sha1) +static int prune_object(const char *fullpath, const unsigned char *sha1) { - const char *fullpath = mkpath("%s/%s", path, filename); struct stat st; if (lstat(fullpath, &st)) return error("Could not stat '%s'", fullpath); @@ -50,9 +48,10 @@ static int prune_object(char *path, const char *filename, const unsigned char *s return 0; } -static int prune_dir(int i, char *path) +static int prune_dir(int i, struct strbuf *path) { - DIR *dir = opendir(path); + size_t baselen = path->len; + DIR *dir = opendir(path->buf); struct dirent *de; if (!dir) @@ -77,28 +76,39 @@ static int prune_dir(int i, char *path) if (lookup_object(sha1)) continue; - prune_object(path, de->d_name, sha1); + strbuf_addf(path, "/%s", de->d_name); + prune_object(path->buf, sha1); + strbuf_setlen(path, baselen); continue; } - if (!prefixcmp(de->d_name, "tmp_obj_")) { - prune_tmp_object(path, de->d_name); + if (starts_with(de->d_name, "tmp_obj_")) { + strbuf_addf(path, "/%s", de->d_name); + prune_tmp_file(path->buf); + strbuf_setlen(path, baselen); continue; } - fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name); + fprintf(stderr, "bad sha1 file: %s/%s\n", path->buf, de->d_name); } closedir(dir); if (!show_only) - rmdir(path); + rmdir(path->buf); return 0; } static void prune_object_dir(const char *path) { + struct strbuf buf = STRBUF_INIT; + size_t baselen; int i; + + strbuf_addstr(&buf, path); + strbuf_addch(&buf, '/'); + baselen = buf.len; + for (i = 0; i < 256; i++) { - static char dir[4096]; - sprintf(dir, "%s/%02x", path, i); - prune_dir(i, dir); + strbuf_addf(&buf, "%02x", i); + prune_dir(i, &buf); + strbuf_setlen(&buf, baselen); } } @@ -119,8 +129,8 @@ static void remove_temporary_files(const char *path) return; } while ((de = readdir(dir)) != NULL) - if (!prefixcmp(de->d_name, "tmp_")) - prune_tmp_object(path, de->d_name); + if (starts_with(de->d_name, "tmp_")) + prune_tmp_file(mkpath("%s/%s", path, de->d_name)); closedir(dir); } diff --git a/builtin/push.c b/builtin/push.c index 7b1b66c36a..0e50ddbb01 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -35,35 +35,75 @@ static void add_refspec(const char *ref) refspec[refspec_nr-1] = ref; } -static void set_refspecs(const char **refs, int nr) +static const char *map_refspec(const char *ref, + struct remote *remote, struct ref *local_refs) { + struct ref *matched = NULL; + + /* Does "ref" uniquely name our ref? */ + if (count_refspec_match(ref, local_refs, &matched) != 1) + return ref; + + if (remote->push) { + struct refspec query; + memset(&query, 0, sizeof(struct refspec)); + query.src = matched->name; + if (!query_refspecs(remote->push, remote->push_refspec_nr, &query) && + query.dst) { + struct strbuf buf = STRBUF_INIT; + strbuf_addf(&buf, "%s%s:%s", + query.force ? "+" : "", + query.src, query.dst); + return strbuf_detach(&buf, NULL); + } + } + + if (push_default == PUSH_DEFAULT_UPSTREAM && + !prefixcmp(matched->name, "refs/heads/")) { + struct branch *branch = branch_get(matched->name + 11); + if (branch->merge_nr == 1 && branch->merge[0]->src) { + struct strbuf buf = STRBUF_INIT; + strbuf_addf(&buf, "%s:%s", + ref, branch->merge[0]->src); + return strbuf_detach(&buf, NULL); + } + } + + return ref; +} + +static void set_refspecs(const char **refs, int nr, const char *repo) +{ + struct remote *remote = NULL; + struct ref *local_refs = NULL; int i; + for (i = 0; i < nr; i++) { const char *ref = refs[i]; if (!strcmp("tag", ref)) { - char *tag; - int len; + struct strbuf tagref = STRBUF_INIT; if (nr <= ++i) die(_("tag shorthand without <tag>")); - len = strlen(refs[i]) + 11; - if (deleterefs) { - tag = xmalloc(len+1); - strcpy(tag, ":refs/tags/"); - } else { - tag = xmalloc(len); - strcpy(tag, "refs/tags/"); + ref = refs[i]; + if (deleterefs) + strbuf_addf(&tagref, ":refs/tags/%s", ref); + else + strbuf_addf(&tagref, "refs/tags/%s", ref); + ref = strbuf_detach(&tagref, NULL); + } else if (deleterefs) { + struct strbuf delref = STRBUF_INIT; + if (strchr(ref, ':')) + die(_("--delete only accepts plain target ref names")); + strbuf_addf(&delref, ":%s", ref); + ref = strbuf_detach(&delref, NULL); + } else if (!strchr(ref, ':')) { + if (!remote) { + /* lazily grab remote and local_refs */ + remote = remote_get(repo); + local_refs = get_local_heads(); } - strcat(tag, refs[i]); - ref = tag; - } else if (deleterefs && !strchr(ref, ':')) { - char *delref; - int len = strlen(ref)+1; - delref = xmalloc(len+1); - strcpy(delref, ":"); - strcat(delref, ref); - ref = delref; - } else if (deleterefs) - die(_("--delete only accepts plain target ref names")); + ref = map_refspec(ref, remote, local_refs); + } add_refspec(ref); } } @@ -174,6 +214,13 @@ N_("push.default is unset; its implicit value is changing in\n" "\n" " git config --global push.default simple\n" "\n" + "When push.default is set to 'matching', git will push local branches\n" + "to the remote branches that already exist with the same name.\n" + "\n" + "In Git 2.0, Git will default to the more conservative 'simple'\n" + "behavior, which only pushes the current branch to the corresponding\n" + "remote branch that 'git pull' uses to update the current branch.\n" + "\n" "See 'git help config' and search for 'push.default' for further information.\n" "(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode\n" "'current' instead of 'simple' if you sometimes use older versions of Git)"); @@ -494,7 +541,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) if (argc > 0) { repo = argv[0]; - set_refspecs(argv + 1, argc - 1); + set_refspecs(argv + 1, argc - 1, repo); } rc = do_push(repo, flags); diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 67ce1ef105..e09994f4df 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -428,7 +428,7 @@ static const char *update(struct command *cmd) struct ref_lock *lock; /* only refs/... are allowed */ - if (prefixcmp(name, "refs/") || check_refname_format(name + 5, 0)) { + if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) { rp_error("refusing to create funny ref '%s' remotely", name); return "funny refname"; } @@ -459,7 +459,7 @@ static const char *update(struct command *cmd) } if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) { - if (deny_deletes && !prefixcmp(name, "refs/heads/")) { + if (deny_deletes && starts_with(name, "refs/heads/")) { rp_error("denying ref deletion for %s", name); return "deletion prohibited"; } @@ -483,7 +483,7 @@ static const char *update(struct command *cmd) if (deny_non_fast_forwards && !is_null_sha1(new_sha1) && !is_null_sha1(old_sha1) && - !prefixcmp(name, "refs/heads/")) { + starts_with(name, "refs/heads/")) { struct object *old_object, *new_object; struct commit *old_commit, *new_commit; diff --git a/builtin/reflog.c b/builtin/reflog.c index 6eb24c8da2..852cff60b7 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -610,12 +610,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) const char *arg = argv[i]; if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n")) cb.dry_run = 1; - else if (!prefixcmp(arg, "--expire=")) { + else if (starts_with(arg, "--expire=")) { if (parse_expiry_date(arg + 9, &cb.expire_total)) die(_("'%s' is not a valid timestamp"), arg); explicit_expiry |= EXPIRE_TOTAL; } - else if (!prefixcmp(arg, "--expire-unreachable=")) { + else if (starts_with(arg, "--expire-unreachable=")) { if (parse_expiry_date(arg + 21, &cb.expire_unreachable)) die(_("'%s' is not a valid timestamp"), arg); explicit_expiry |= EXPIRE_UNREACH; diff --git a/builtin/remote.c b/builtin/remote.c index 4e14891095..b3ab4cf8f6 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -6,6 +6,7 @@ #include "strbuf.h" #include "run-command.h" #include "refs.h" +#include "argv-array.h" static const char * const builtin_remote_usage[] = { N_("git remote [-v | --verbose]"), @@ -77,17 +78,6 @@ static const char * const builtin_remote_seturl_usage[] = { static int verbose; -static int show_all(void); -static int prune_remote(const char *remote, int dry_run); - -static inline int postfixcmp(const char *string, const char *postfix) -{ - int len1 = strlen(string), len2 = strlen(postfix); - if (len1 < len2) - return 1; - return strcmp(string + len1 - len2, postfix); -} - static int fetch_remote(const char *name) { const char *argv[] = { "fetch", name, NULL, NULL }; @@ -269,7 +259,7 @@ static const char *abbrev_ref(const char *name, const char *prefix) static int config_read_branches(const char *key, const char *value, void *cb) { - if (!prefixcmp(key, "branch.")) { + if (starts_with(key, "branch.")) { const char *orig_key = key; char *name; struct string_list_item *item; @@ -277,13 +267,13 @@ static int config_read_branches(const char *key, const char *value, void *cb) enum { REMOTE, MERGE, REBASE } type; key += 7; - if (!postfixcmp(key, ".remote")) { + if (ends_with(key, ".remote")) { name = xstrndup(key, strlen(key) - 7); type = REMOTE; - } else if (!postfixcmp(key, ".merge")) { + } else if (ends_with(key, ".merge")) { name = xstrndup(key, strlen(key) - 6); type = MERGE; - } else if (!postfixcmp(key, ".rebase")) { + } else if (ends_with(key, ".rebase")) { name = xstrndup(key, strlen(key) - 7); type = REBASE; } else @@ -309,8 +299,13 @@ static int config_read_branches(const char *key, const char *value, void *cb) space = strchr(value, ' '); } string_list_append(&info->merge, xstrdup(value)); - } else - info->rebase = git_config_bool(orig_key, value); + } else { + int v = git_config_maybe_bool(orig_key, value); + if (v >= 0) + info->rebase = v; + else if (!strcmp(value, "preserve")) + info->rebase = 1; + } } return 0; } @@ -534,9 +529,9 @@ static int add_branch_for_removal(const char *refname, } /* don't delete non-remote-tracking refs */ - if (prefixcmp(refname, "refs/remotes/")) { + if (!starts_with(refname, "refs/remotes/")) { /* advise user how to delete local branches */ - if (!prefixcmp(refname, "refs/heads/")) + if (starts_with(refname, "refs/heads/")) string_list_append(branches->skipped, abbrev_branch(refname)); /* silently skip over other non-remote refs */ @@ -571,7 +566,7 @@ static int read_remote_branches(const char *refname, const char *symref; strbuf_addf(&buf, "refs/remotes/%s/", rename->old); - if (!prefixcmp(refname, buf.buf)) { + if (starts_with(refname, buf.buf)) { item = string_list_append(rename->remote_branches, xstrdup(refname)); symref = resolve_ref_unsafe(refname, orig_sha1, 1, &flag); if (flag & REF_ISSYMREF) @@ -1084,6 +1079,64 @@ static int show_push_info_item(struct string_list_item *item, void *cb_data) return 0; } +static int get_one_entry(struct remote *remote, void *priv) +{ + struct string_list *list = priv; + struct strbuf url_buf = STRBUF_INIT; + const char **url; + int i, url_nr; + + if (remote->url_nr > 0) { + strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]); + string_list_append(list, remote->name)->util = + strbuf_detach(&url_buf, NULL); + } else + string_list_append(list, remote->name)->util = NULL; + if (remote->pushurl_nr) { + url = remote->pushurl; + url_nr = remote->pushurl_nr; + } else { + url = remote->url; + url_nr = remote->url_nr; + } + for (i = 0; i < url_nr; i++) + { + strbuf_addf(&url_buf, "%s (push)", url[i]); + string_list_append(list, remote->name)->util = + strbuf_detach(&url_buf, NULL); + } + + return 0; +} + +static int show_all(void) +{ + struct string_list list = STRING_LIST_INIT_NODUP; + int result; + + list.strdup_strings = 1; + result = for_each_remote(get_one_entry, &list); + + if (!result) { + int i; + + sort_string_list(&list); + for (i = 0; i < list.nr; i++) { + struct string_list_item *item = list.items + i; + if (verbose) + printf("%s\t%s\n", item->string, + item->util ? (const char *)item->util : ""); + else { + if (i && !strcmp((item - 1)->string, item->string)) + continue; + printf("%s\n", item->string); + } + } + } + string_list_clear(&list, 1); + return result; +} + static int show(int argc, const char **argv) { int no_query = 0, result = 0, query_flag = 0; @@ -1246,26 +1299,6 @@ static int set_head(int argc, const char **argv) return result; } -static int prune(int argc, const char **argv) -{ - int dry_run = 0, result = 0; - struct option options[] = { - OPT__DRY_RUN(&dry_run, N_("dry run")), - OPT_END() - }; - - argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage, - 0); - - if (argc < 1) - usage_with_options(builtin_remote_prune_usage, options); - - for (; argc; argc--, argv++) - result |= prune_remote(*argv, dry_run); - - return result; -} - static int prune_remote(const char *remote, int dry_run) { int result = 0, i; @@ -1304,6 +1337,26 @@ static int prune_remote(const char *remote, int dry_run) return result; } +static int prune(int argc, const char **argv) +{ + int dry_run = 0, result = 0; + struct option options[] = { + OPT__DRY_RUN(&dry_run, N_("dry run")), + OPT_END() + }; + + argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage, + 0); + + if (argc < 1) + usage_with_options(builtin_remote_prune_usage, options); + + for (; argc; argc--, argv++) + result |= prune_remote(*argv, dry_run); + + return result; +} + static int get_remote_default(const char *key, const char *value, void *priv) { if (strcmp(key, "remotes.default") == 0) { @@ -1315,42 +1368,42 @@ static int get_remote_default(const char *key, const char *value, void *priv) static int update(int argc, const char **argv) { - int i, prune = 0; + int i, prune = -1; struct option options[] = { OPT_BOOL('p', "prune", &prune, N_("prune remotes after fetching")), OPT_END() }; - const char **fetch_argv; - int fetch_argc = 0; + struct argv_array fetch_argv = ARGV_ARRAY_INIT; int default_defined = 0; - - fetch_argv = xmalloc(sizeof(char *) * (argc+5)); + int retval; argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage, PARSE_OPT_KEEP_ARGV0); - fetch_argv[fetch_argc++] = "fetch"; + argv_array_push(&fetch_argv, "fetch"); - if (prune) - fetch_argv[fetch_argc++] = "--prune"; + if (prune != -1) + argv_array_push(&fetch_argv, prune ? "--prune" : "--no-prune"); if (verbose) - fetch_argv[fetch_argc++] = "-v"; - fetch_argv[fetch_argc++] = "--multiple"; + argv_array_push(&fetch_argv, "-v"); + argv_array_push(&fetch_argv, "--multiple"); if (argc < 2) - fetch_argv[fetch_argc++] = "default"; + argv_array_push(&fetch_argv, "default"); for (i = 1; i < argc; i++) - fetch_argv[fetch_argc++] = argv[i]; + argv_array_push(&fetch_argv, argv[i]); - if (strcmp(fetch_argv[fetch_argc-1], "default") == 0) { + if (strcmp(fetch_argv.argv[fetch_argv.argc-1], "default") == 0) { git_config(get_remote_default, &default_defined); - if (!default_defined) - fetch_argv[fetch_argc-1] = "--all"; + if (!default_defined) { + argv_array_pop(&fetch_argv); + argv_array_push(&fetch_argv, "--all"); + } } - fetch_argv[fetch_argc] = NULL; - - return run_command_v_opt(fetch_argv, RUN_GIT_CMD); + retval = run_command_v_opt(fetch_argv.argv, RUN_GIT_CMD); + argv_array_clear(&fetch_argv); + return retval; } static int remove_all_fetch_refspecs(const char *remote, const char *key) @@ -1505,64 +1558,6 @@ static int set_url(int argc, const char **argv) return 0; } -static int get_one_entry(struct remote *remote, void *priv) -{ - struct string_list *list = priv; - struct strbuf url_buf = STRBUF_INIT; - const char **url; - int i, url_nr; - - if (remote->url_nr > 0) { - strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]); - string_list_append(list, remote->name)->util = - strbuf_detach(&url_buf, NULL); - } else - string_list_append(list, remote->name)->util = NULL; - if (remote->pushurl_nr) { - url = remote->pushurl; - url_nr = remote->pushurl_nr; - } else { - url = remote->url; - url_nr = remote->url_nr; - } - for (i = 0; i < url_nr; i++) - { - strbuf_addf(&url_buf, "%s (push)", url[i]); - string_list_append(list, remote->name)->util = - strbuf_detach(&url_buf, NULL); - } - - return 0; -} - -static int show_all(void) -{ - struct string_list list = STRING_LIST_INIT_NODUP; - int result; - - list.strdup_strings = 1; - result = for_each_remote(get_one_entry, &list); - - if (!result) { - int i; - - sort_string_list(&list); - for (i = 0; i < list.nr; i++) { - struct string_list_item *item = list.items + i; - if (verbose) - printf("%s\t%s\n", item->string, - item->util ? (const char *)item->util : ""); - else { - if (i && !strcmp((item - 1)->string, item->string)) - continue; - printf("%s\n", item->string); - } - } - } - string_list_clear(&list, 1); - return result; -} - int cmd_remote(int argc, const char **argv, const char *prefix) { struct option options[] = { diff --git a/builtin/repack.c b/builtin/repack.c index 91e2363c71..ba66c6e377 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -78,7 +78,7 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list) return; while ((e = readdir(dir)) != NULL) { - if (suffixcmp(e->d_name, ".pack")) + if (!ends_with(e->d_name, ".pack")) continue; len = strlen(e->d_name) - strlen(".pack"); diff --git a/builtin/replace.c b/builtin/replace.c index b1bd3ef994..2336325ce3 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -16,27 +16,69 @@ static const char * const git_replace_usage[] = { N_("git replace [-f] <object> <replacement>"), N_("git replace -d <object>..."), - N_("git replace -l [<pattern>]"), + N_("git replace [--format=<format>] [-l [<pattern>]]"), NULL }; +enum replace_format { + REPLACE_FORMAT_SHORT, + REPLACE_FORMAT_MEDIUM, + REPLACE_FORMAT_LONG +}; + +struct show_data { + const char *pattern; + enum replace_format format; +}; + static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { - const char *pattern = cb_data; + struct show_data *data = cb_data; - if (!fnmatch(pattern, refname, 0)) - printf("%s\n", refname); + if (!fnmatch(data->pattern, refname, 0)) { + if (data->format == REPLACE_FORMAT_SHORT) + printf("%s\n", refname); + else if (data->format == REPLACE_FORMAT_MEDIUM) + printf("%s -> %s\n", refname, sha1_to_hex(sha1)); + else { /* data->format == REPLACE_FORMAT_LONG */ + unsigned char object[20]; + enum object_type obj_type, repl_type; + + if (get_sha1(refname, object)) + return error("Failed to resolve '%s' as a valid ref.", refname); + + obj_type = sha1_object_info(object, NULL); + repl_type = sha1_object_info(sha1, NULL); + + printf("%s (%s) -> %s (%s)\n", refname, typename(obj_type), + sha1_to_hex(sha1), typename(repl_type)); + } + } return 0; } -static int list_replace_refs(const char *pattern) +static int list_replace_refs(const char *pattern, const char *format) { + struct show_data data; + if (pattern == NULL) pattern = "*"; + data.pattern = pattern; - for_each_replace_ref(show_reference, (void *) pattern); + if (format == NULL || *format == '\0' || !strcmp(format, "short")) + data.format = REPLACE_FORMAT_SHORT; + else if (!strcmp(format, "medium")) + data.format = REPLACE_FORMAT_MEDIUM; + else if (!strcmp(format, "long")) + data.format = REPLACE_FORMAT_LONG; + else + die("invalid replace format '%s'\n" + "valid formats are 'short', 'medium' and 'long'\n", + format); + + for_each_replace_ref(show_reference, (void *) &data); return 0; } @@ -127,19 +169,27 @@ static int replace_object(const char *object_ref, const char *replace_ref, int cmd_replace(int argc, const char **argv, const char *prefix) { int list = 0, delete = 0, force = 0; + const char *format = NULL; struct option options[] = { OPT_BOOL('l', "list", &list, N_("list replace refs")), OPT_BOOL('d', "delete", &delete, N_("delete replace refs")), OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")), + OPT_STRING(0, "format", &format, N_("format"), N_("use this format")), OPT_END() }; + read_replace_refs = 0; + argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0); if (list && delete) usage_msg_opt("-l and -d cannot be used together", git_replace_usage, options); + if (format && delete) + usage_msg_opt("--format and -d cannot be used together", + git_replace_usage, options); + if (force && (list || delete)) usage_msg_opt("-f cannot be used with -d or -l", git_replace_usage, options); @@ -157,6 +207,9 @@ int cmd_replace(int argc, const char **argv, const char *prefix) if (argc != 2) usage_msg_opt("bad number of arguments", git_replace_usage, options); + if (format) + usage_msg_opt("--format cannot be used when not listing", + git_replace_usage, options); return replace_object(argv[0], argv[1], force); } @@ -168,5 +221,5 @@ int cmd_replace(int argc, const char **argv, const char *prefix) usage_msg_opt("-f needs some arguments", git_replace_usage, options); - return list_replace_refs(argv[0]); + return list_replace_refs(argv[0], format); } diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index c76b89dc5b..aaeb611a97 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -9,6 +9,8 @@ #include "quote.h" #include "builtin.h" #include "parse-options.h" +#include "diff.h" +#include "revision.h" #define DO_REVS 1 #define DO_NOREV 2 @@ -30,6 +32,9 @@ static int abbrev_ref; static int abbrev_ref_strict; static int output_sq; +static int stuck_long; +static struct string_list *ref_excludes; + /* * Some arguments are relevant "revision" arguments, * others are about output format or other details. @@ -185,6 +190,8 @@ static int show_default(void) static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { + if (ref_excluded(ref_excludes, refname)) + return 0; show_rev(NORMAL, sha1, refname); return 0; } @@ -279,6 +286,7 @@ static int try_difference(const char *arg) exclude = n; } } + *dotdot = '.'; return 1; } *dotdot = '.'; @@ -302,8 +310,10 @@ static int try_parent_shorthands(const char *arg) return 0; *dotdot = 0; - if (get_sha1_committish(arg, sha1)) + if (get_sha1_committish(arg, sha1)) { + *dotdot = '^'; return 0; + } if (!parents_only) show_rev(NORMAL, sha1, arg); @@ -312,6 +322,7 @@ static int try_parent_shorthands(const char *arg) show_rev(parents_only ? NORMAL : REVERSED, parents->item->object.sha1, arg); + *dotdot = '^'; return 1; } @@ -320,12 +331,15 @@ static int parseopt_dump(const struct option *o, const char *arg, int unset) struct strbuf *parsed = o->value; if (unset) strbuf_addf(parsed, " --no-%s", o->long_name); - else if (o->short_name) + else if (o->short_name && (o->long_name == NULL || !stuck_long)) strbuf_addf(parsed, " -%c", o->short_name); else strbuf_addf(parsed, " --%s", o->long_name); if (arg) { - strbuf_addch(parsed, ' '); + if (!stuck_long) + strbuf_addch(parsed, ' '); + else if (o->long_name) + strbuf_addch(parsed, '='); sq_quote_buf(parsed, arg); } return 0; @@ -351,6 +365,8 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "stop-at-non-option", &stop_at_non_option, N_("stop parsing after the " "first non-option argument")), + OPT_BOOL(0, "stuck-long", &stuck_long, + N_("output in stuck long form")), OPT_END(), }; @@ -476,6 +492,7 @@ N_("git rev-parse --parseopt [options] -- [<args>...]\n" int cmd_rev_parse(int argc, const char **argv, const char *prefix) { int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0; + int has_dashdash = 0; int output_prefix = 0; unsigned char sha1[20]; const char *name = NULL; @@ -489,6 +506,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) if (argc > 1 && !strcmp("-h", argv[1])) usage(builtin_rev_parse_usage); + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "--")) { + has_dashdash = 1; + break; + } + } + prefix = setup_git_directory(); git_config(git_default_config, NULL); for (i = 1; i < argc; i++) { @@ -508,7 +532,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) } continue; } - if (!prefixcmp(arg, "-n")) { + if (starts_with(arg, "-n")) { if ((filter & DO_FLAGS) && (filter & DO_REVS)) show(arg); continue; @@ -560,7 +584,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--short") || - !prefixcmp(arg, "--short=")) { + starts_with(arg, "--short=")) { filter &= ~(DO_FLAGS|DO_NOREV); verify = 1; abbrev = DEFAULT_ABBREV; @@ -588,7 +612,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) symbolic = SHOW_SYMBOLIC_FULL; continue; } - if (!prefixcmp(arg, "--abbrev-ref") && + if (starts_with(arg, "--abbrev-ref") && (!arg[12] || arg[12] == '=')) { abbrev_ref = 1; abbrev_ref_strict = warn_ambiguous_refs; @@ -606,7 +630,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) for_each_ref(show_reference, NULL); continue; } - if (!prefixcmp(arg, "--disambiguate=")) { + if (starts_with(arg, "--disambiguate=")) { for_each_abbrev(arg + 15, show_abbrev, NULL); continue; } @@ -615,35 +639,46 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) for_each_ref_in("refs/bisect/good", anti_reference, NULL); continue; } - if (!prefixcmp(arg, "--branches=")) { + if (starts_with(arg, "--branches=")) { for_each_glob_ref_in(show_reference, arg + 11, "refs/heads/", NULL); + clear_ref_exclusion(&ref_excludes); continue; } if (!strcmp(arg, "--branches")) { for_each_branch_ref(show_reference, NULL); + clear_ref_exclusion(&ref_excludes); continue; } - if (!prefixcmp(arg, "--tags=")) { + if (starts_with(arg, "--tags=")) { for_each_glob_ref_in(show_reference, arg + 7, "refs/tags/", NULL); + clear_ref_exclusion(&ref_excludes); continue; } if (!strcmp(arg, "--tags")) { for_each_tag_ref(show_reference, NULL); + clear_ref_exclusion(&ref_excludes); continue; } - if (!prefixcmp(arg, "--glob=")) { + if (starts_with(arg, "--glob=")) { for_each_glob_ref(show_reference, arg + 7, NULL); + clear_ref_exclusion(&ref_excludes); continue; } - if (!prefixcmp(arg, "--remotes=")) { + if (starts_with(arg, "--remotes=")) { for_each_glob_ref_in(show_reference, arg + 10, "refs/remotes/", NULL); + clear_ref_exclusion(&ref_excludes); continue; } if (!strcmp(arg, "--remotes")) { for_each_remote_ref(show_reference, NULL); + clear_ref_exclusion(&ref_excludes); + continue; + } + if (starts_with(arg, "--exclude=")) { + add_ref_exclusion(&ref_excludes, arg + 10); continue; } if (!strcmp(arg, "--local-env-vars")) { @@ -724,19 +759,19 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) : "false"); continue; } - if (!prefixcmp(arg, "--since=")) { + if (starts_with(arg, "--since=")) { show_datestring("--max-age=", arg+8); continue; } - if (!prefixcmp(arg, "--after=")) { + if (starts_with(arg, "--after=")) { show_datestring("--max-age=", arg+8); continue; } - if (!prefixcmp(arg, "--before=")) { + if (starts_with(arg, "--before=")) { show_datestring("--min-age=", arg+9); continue; } - if (!prefixcmp(arg, "--until=")) { + if (starts_with(arg, "--until=")) { show_datestring("--min-age=", arg+8); continue; } @@ -765,6 +800,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) } if (verify) die_no_single_rev(quiet); + if (has_dashdash) + die("bad revision '%s'", arg); as_is = 1; if (!show_file(arg, output_prefix)) continue; diff --git a/builtin/send-pack.c b/builtin/send-pack.c index 4482f16efb..e7f0b97d31 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -115,15 +115,15 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) const char *arg = *argv; if (*arg == '-') { - if (!prefixcmp(arg, "--receive-pack=")) { + if (starts_with(arg, "--receive-pack=")) { receivepack = arg + 15; continue; } - if (!prefixcmp(arg, "--exec=")) { + if (starts_with(arg, "--exec=")) { receivepack = arg + 7; continue; } - if (!prefixcmp(arg, "--remote=")) { + if (starts_with(arg, "--remote=")) { remote_name = arg + 9; continue; } @@ -181,7 +181,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) exit(1); continue; } - if (!prefixcmp(arg, "--" CAS_OPT_NAME "=")) { + if (starts_with(arg, "--" CAS_OPT_NAME "=")) { if (parse_push_cas_option(&cas, strchr(arg, '=') + 1, 0) < 0) exit(1); diff --git a/builtin/shortlog.c b/builtin/shortlog.c index c226f767aa..4b7e53623f 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -65,7 +65,7 @@ static void insert_one_record(struct shortlog *log, eol = strchr(oneline, '\n'); if (!eol) eol = oneline + strlen(oneline); - if (!prefixcmp(oneline, "[PATCH")) { + if (starts_with(oneline, "[PATCH")) { char *eob = strchr(oneline, ']'); if (eob && (!eol || eob < eol)) oneline = eob + 1; @@ -95,7 +95,7 @@ static void read_from_stdin(struct shortlog *log) while (fgets(author, sizeof(author), stdin) != NULL) { if (!(author[0] == 'A' || author[0] == 'a') || - prefixcmp(author + 1, "uthor: ")) + !starts_with(author + 1, "uthor: ")) continue; while (fgets(oneline, sizeof(oneline), stdin) && oneline[0] != '\n') @@ -123,7 +123,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit) else eol++; - if (!prefixcmp(buffer, "author ")) + if (starts_with(buffer, "author ")) author = buffer + 7; buffer = eol; } diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 001f29ca1b..d9217ce1e1 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -227,8 +227,7 @@ static void join_revs(struct commit_list **list_p, parents = parents->next; if ((this_flag & flags) == flags) continue; - if (!p->object.parsed) - parse_commit(p); + parse_commit(p); if (mark_seen(p, seen_p) && !still_interesting) extra--; p->object.flags |= flags; @@ -285,7 +284,7 @@ static void show_one_commit(struct commit *commit, int no_name) pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty); pretty_str = pretty.buf; } - if (!prefixcmp(pretty_str, "[PATCH] ")) + if (starts_with(pretty_str, "[PATCH] ")) pretty_str += 8; if (!no_name) { @@ -396,7 +395,7 @@ static int append_head_ref(const char *refname, const unsigned char *sha1, int f { unsigned char tmp[20]; int ofs = 11; - if (prefixcmp(refname, "refs/heads/")) + if (!starts_with(refname, "refs/heads/")) return 0; /* If both heads/foo and tags/foo exists, get_sha1 would * get confused. @@ -410,7 +409,7 @@ static int append_remote_ref(const char *refname, const unsigned char *sha1, int { unsigned char tmp[20]; int ofs = 13; - if (prefixcmp(refname, "refs/remotes/")) + if (!starts_with(refname, "refs/remotes/")) return 0; /* If both heads/foo and tags/foo exists, get_sha1 would * get confused. @@ -422,7 +421,7 @@ static int append_remote_ref(const char *refname, const unsigned char *sha1, int static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { - if (prefixcmp(refname, "refs/tags/")) + if (!starts_with(refname, "refs/tags/")) return 0; return append_ref(refname + 5, sha1, 0); } @@ -453,9 +452,9 @@ static int append_matching_ref(const char *refname, const unsigned char *sha1, i return 0; if (fnmatch(match_ref_pattern, tail, 0)) return 0; - if (!prefixcmp(refname, "refs/heads/")) + if (starts_with(refname, "refs/heads/")) return append_head_ref(refname, sha1, flag, cb_data); - if (!prefixcmp(refname, "refs/tags/")) + if (starts_with(refname, "refs/tags/")) return append_tag_ref(refname, sha1, flag, cb_data); return append_ref(refname, sha1, 0); } @@ -480,11 +479,11 @@ static int rev_is_head(char *head, int headlen, char *name, if ((!head[0]) || (head_sha1 && sha1 && hashcmp(head_sha1, sha1))) return 0; - if (!prefixcmp(head, "refs/heads/")) + if (starts_with(head, "refs/heads/")) head += 11; - if (!prefixcmp(name, "refs/heads/")) + if (starts_with(name, "refs/heads/")) name += 11; - else if (!prefixcmp(name, "heads/")) + else if (starts_with(name, "heads/")) name += 6; return !strcmp(head, name); } @@ -813,7 +812,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) has_head++; } if (!has_head) { - int offset = !prefixcmp(head, "refs/heads/") ? 11 : 0; + int offset = starts_with(head, "refs/heads/") ? 11 : 0; append_one_rev(head + offset); } } diff --git a/builtin/show-ref.c b/builtin/show-ref.c index 9f3f5e370b..5ba1f30838 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -37,8 +37,8 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo if (tags_only || heads_only) { int match; - match = heads_only && !prefixcmp(refname, "refs/heads/"); - match |= tags_only && !prefixcmp(refname, "refs/tags/"); + match = heads_only && starts_with(refname, "refs/heads/"); + match |= tags_only && starts_with(refname, "refs/tags/"); if (!match) return 0; } @@ -210,7 +210,7 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix) while (*pattern) { unsigned char sha1[20]; - if (!prefixcmp(*pattern, "refs/") && + if (starts_with(*pattern, "refs/") && !read_ref(*pattern, sha1)) { if (!quiet) show_one(*pattern, sha1); diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c index 71286b4fae..b6a711d319 100644 --- a/builtin/symbolic-ref.c +++ b/builtin/symbolic-ref.c @@ -65,7 +65,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) break; case 2: if (!strcmp(argv[0], "HEAD") && - prefixcmp(argv[1], "refs/")) + !starts_with(argv[1], "refs/")) die("Refusing to point HEAD outside of refs/"); create_symref(argv[0], argv[1], msg); break; diff --git a/builtin/tag.c b/builtin/tag.c index ea55f1d1bd..74d3780b77 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -259,7 +259,7 @@ static int git_tag_config(const char *var, const char *value, void *cb) int status = git_gpg_config(var, value, cb); if (status) return status; - if (!prefixcmp(var, "column.")) + if (starts_with(var, "column.")) return git_column_config(var, value, "tag", &colopts); return git_default_config(var, value, cb); } diff --git a/builtin/tar-tree.c b/builtin/tar-tree.c deleted file mode 100644 index ba3ffe69a9..0000000000 --- a/builtin/tar-tree.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2005, 2006 Rene Scharfe - */ -#include "cache.h" -#include "commit.h" -#include "tar.h" -#include "builtin.h" -#include "quote.h" - -static const char tar_tree_usage[] = -"git tar-tree [--remote=<repo>] <tree-ish> [basedir]\n" -"*** Note that this command is now deprecated; use \"git archive\" instead."; - -static const char builtin_get_tar_commit_id_usage[] = -"git get-tar-commit-id < <tarfile>"; - -int cmd_tar_tree(int argc, const char **argv, const char *prefix) -{ - /* - * "git tar-tree" is now a wrapper around "git archive --format=tar" - * - * $0 --remote=<repo> arg... ==> - * git archive --format=tar --remote=<repo> arg... - * $0 tree-ish ==> - * git archive --format=tar tree-ish - * $0 tree-ish basedir ==> - * git archive --format-tar --prefix=basedir tree-ish - */ - const char **nargv = xcalloc(sizeof(*nargv), argc + 3); - struct strbuf sb = STRBUF_INIT; - char *basedir_arg; - int nargc = 0; - - nargv[nargc++] = "archive"; - nargv[nargc++] = "--format=tar"; - - if (2 <= argc && !prefixcmp(argv[1], "--remote=")) { - nargv[nargc++] = argv[1]; - argv++; - argc--; - } - - /* - * Because it's just a compatibility wrapper, tar-tree supports only - * the old behaviour of reading attributes from the work tree. - */ - nargv[nargc++] = "--worktree-attributes"; - - switch (argc) { - default: - usage(tar_tree_usage); - break; - case 3: - /* base-path */ - basedir_arg = xmalloc(strlen(argv[2]) + 11); - sprintf(basedir_arg, "--prefix=%s/", argv[2]); - nargv[nargc++] = basedir_arg; - /* fallthru */ - case 2: - /* tree-ish */ - nargv[nargc++] = argv[1]; - } - nargv[nargc] = NULL; - - fprintf(stderr, - "*** \"git tar-tree\" is now deprecated.\n" - "*** Running \"git archive\" instead.\n***"); - sq_quote_argv(&sb, nargv, 0); - strbuf_addch(&sb, '\n'); - fputs(sb.buf, stderr); - strbuf_release(&sb); - return cmd_archive(nargc, nargv, prefix); -} - -/* ustar header + extended global header content */ -#define RECORDSIZE (512) -#define HEADERSIZE (2 * RECORDSIZE) - -int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix) -{ - char buffer[HEADERSIZE]; - struct ustar_header *header = (struct ustar_header *)buffer; - char *content = buffer + RECORDSIZE; - ssize_t n; - - if (argc != 1) - usage(builtin_get_tar_commit_id_usage); - - n = read_in_full(0, buffer, HEADERSIZE); - if (n < HEADERSIZE) - die("git get-tar-commit-id: read error"); - if (header->typeflag[0] != 'g') - return 1; - if (memcmp(content, "52 comment=", 11)) - return 1; - - n = write_in_full(1, content + 11, 41); - if (n < 41) - die_errno("git get-tar-commit-id: write error"); - - return 0; -} diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 2217d7b3ae..62ff673f68 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -523,7 +523,7 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix) strict = 1; continue; } - if (!prefixcmp(arg, "--pack_header=")) { + if (starts_with(arg, "--pack_header=")) { struct pack_header *hdr; char *c; diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 702e90db2a..1292cfea11 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -229,15 +229,15 @@ static void update_refs_stdin(void) die("empty command in input"); else if (isspace(*cmd.buf)) die("whitespace before command: %s", cmd.buf); - else if (!prefixcmp(cmd.buf, "update ")) + else if (starts_with(cmd.buf, "update ")) parse_cmd_update(cmd.buf + 7); - else if (!prefixcmp(cmd.buf, "create ")) + else if (starts_with(cmd.buf, "create ")) parse_cmd_create(cmd.buf + 7); - else if (!prefixcmp(cmd.buf, "delete ")) + else if (starts_with(cmd.buf, "delete ")) parse_cmd_delete(cmd.buf + 7); - else if (!prefixcmp(cmd.buf, "verify ")) + else if (starts_with(cmd.buf, "verify ")) parse_cmd_verify(cmd.buf + 7); - else if (!prefixcmp(cmd.buf, "option ")) + else if (starts_with(cmd.buf, "option ")) parse_cmd_option(cmd.buf + 7); else die("unknown command: %s", cmd.buf); diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c index af2da35e7d..32ab94cd06 100644 --- a/builtin/upload-archive.c +++ b/builtin/upload-archive.c @@ -37,7 +37,7 @@ int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix) if (sent_argv.argc > MAX_ARGS) die("Too many options (>%d)", MAX_ARGS - 1); - if (prefixcmp(buf, arg_cmd)) + if (!starts_with(buf, arg_cmd)) die("'argument' token or flush expected"); argv_array_push(&sent_argv, buf + strlen(arg_cmd)); } @@ -7,6 +7,7 @@ #include "list-objects.h" #include "run-command.h" #include "refs.h" +#include "argv-array.h" static const char bundle_signature[] = "# v2 git bundle\n"; @@ -234,13 +235,13 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs) } int create_bundle(struct bundle_header *header, const char *path, - int argc, const char **argv) + int argc, const char **argv) { static struct lock_file lock; int bundle_fd = -1; int bundle_to_stdout; - const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *)); - const char **argv_pack = xmalloc(6 * sizeof(const char *)); + struct argv_array argv_boundary = ARGV_ARRAY_INIT; + struct argv_array argv_pack = ARGV_ARRAY_INIT; int i, ref_count = 0; struct strbuf buf = STRBUF_INIT; struct rev_info revs; @@ -262,13 +263,14 @@ int create_bundle(struct bundle_header *header, const char *path, init_revisions(&revs, NULL); /* write prerequisites */ - memcpy(argv_boundary + 3, argv + 1, argc * sizeof(const char *)); - argv_boundary[0] = "rev-list"; - argv_boundary[1] = "--boundary"; - argv_boundary[2] = "--pretty=oneline"; - argv_boundary[argc + 2] = NULL; + argv_array_pushl(&argv_boundary, + "rev-list", "--boundary", "--pretty=oneline", + NULL); + for (i = 1; i < argc; i++) + argv_array_push(&argv_boundary, argv[i]); + memset(&rls, 0, sizeof(rls)); - rls.argv = argv_boundary; + rls.argv = argv_boundary.argv; rls.out = -1; rls.git_cmd = 1; if (start_command(&rls)) @@ -383,14 +385,12 @@ int create_bundle(struct bundle_header *header, const char *path, write_or_die(bundle_fd, "\n", 1); /* write pack */ - argv_pack[0] = "pack-objects"; - argv_pack[1] = "--all-progress-implied"; - argv_pack[2] = "--stdout"; - argv_pack[3] = "--thin"; - argv_pack[4] = "--delta-base-offset"; - argv_pack[5] = NULL; + argv_array_pushl(&argv_pack, + "pack-objects", "--all-progress-implied", + "--stdout", "--thin", "--delta-base-offset", + NULL); memset(&rls, 0, sizeof(rls)); - rls.argv = argv_pack; + rls.argv = argv_pack.argv; rls.in = -1; rls.out = bundle_fd; rls.git_cmd = 1; @@ -760,11 +760,11 @@ int daemon_avoid_alias(const char *path); int offset_1st_component(const char *path); /* object replacement */ -#define READ_SHA1_FILE_REPLACE 1 +#define LOOKUP_REPLACE_OBJECT 1 extern void *read_sha1_file_extended(const unsigned char *sha1, enum object_type *type, unsigned long *size, unsigned flag); static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size) { - return read_sha1_file_extended(sha1, type, size, READ_SHA1_FILE_REPLACE); + return read_sha1_file_extended(sha1, type, size, LOOKUP_REPLACE_OBJECT); } extern const unsigned char *do_lookup_replace_object(const unsigned char *sha1); static inline const unsigned char *lookup_replace_object(const unsigned char *sha1) @@ -773,6 +773,12 @@ static inline const unsigned char *lookup_replace_object(const unsigned char *sh return sha1; return do_lookup_replace_object(sha1); } +static inline const unsigned char *lookup_replace_object_extended(const unsigned char *sha1, unsigned flag) +{ + if (!(flag & LOOKUP_REPLACE_OBJECT)) + return sha1; + return lookup_replace_object(sha1); +} /* Read and unpack a sha1 file into memory, write memory to a sha1 file */ extern int sha1_object_info(const unsigned char *, unsigned long *); @@ -1074,6 +1080,7 @@ struct object_info { enum object_type *typep; unsigned long *sizep; unsigned long *disk_sizep; + unsigned char *delta_base_sha1; /* Response */ enum { @@ -1098,7 +1105,7 @@ struct object_info { } packed; } u; }; -extern int sha1_object_info_extended(const unsigned char *, struct object_info *); +extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags); /* Dumb servers support */ extern int update_server_info(int); diff --git a/command-list.txt b/command-list.txt index 08b04e2c66..cf36c3d71e 100644 --- a/command-list.txt +++ b/command-list.txt @@ -64,7 +64,6 @@ git-init mainporcelain common git-instaweb ancillaryinterrogators gitk mainporcelain git-log mainporcelain common -git-lost-found ancillarymanipulators deprecated git-ls-files plumbinginterrogators git-ls-remote plumbinginterrogators git-ls-tree plumbinginterrogators @@ -88,7 +87,6 @@ git-pack-redundant plumbinginterrogators git-pack-refs ancillarymanipulators git-parse-remote synchelpers git-patch-id purehelpers -git-peek-remote purehelpers deprecated git-prune ancillarymanipulators git-prune-packed plumbingmanipulators git-pull mainporcelain common @@ -102,7 +100,6 @@ git-relink ancillarymanipulators git-remote ancillarymanipulators git-repack ancillarymanipulators git-replace ancillarymanipulators -git-repo-config ancillarymanipulators deprecated git-request-pull foreignscminterface git-rerere ancillaryinterrogators git-reset mainporcelain common @@ -127,7 +124,6 @@ git-submodule mainporcelain git-svn foreignscminterface git-symbolic-ref plumbingmanipulators git-tag mainporcelain common -git-tar-tree plumbinginterrogators deprecated git-unpack-file plumbinginterrogators git-unpack-objects plumbingmanipulators git-update-index plumbingmanipulators diff --git a/commit-slab.h b/commit-slab.h index d4c8286470..cc114b53b0 100644 --- a/commit-slab.h +++ b/commit-slab.h @@ -22,8 +22,17 @@ * * Initializes the indegree slab that associates an array of integers * to each commit. 'stride' specifies how big each array is. The slab - * that id initialied by the variant without "_with_stride" associates + * that is initialized by the variant without "_with_stride" associates * each commit with an array of one integer. + * + * - void clear_indegree(struct indegree *); + * + * Empties the slab. The slab can be reused with the same stride + * without calling init_indegree() again or can be reconfigured to a + * different stride by calling init_indegree_with_stride(). + * + * Call this function before the slab falls out of scope to avoid + * leaking memory. */ /* allocate ~512kB at once, allowing for malloc overhead */ @@ -31,6 +40,8 @@ #define COMMIT_SLAB_SIZE (512*1024-32) #endif +#define MAYBE_UNUSED __attribute__((__unused__)) + #define define_commit_slab(slabname, elemtype) \ \ struct slabname { \ @@ -41,8 +52,8 @@ struct slabname { \ }; \ static int stat_ ##slabname## realloc; \ \ -static void init_ ##slabname## _with_stride(struct slabname *s, \ - unsigned stride) \ +static MAYBE_UNUSED void init_ ##slabname## _with_stride(struct slabname *s, \ + unsigned stride) \ { \ unsigned int elem_size; \ if (!stride) \ @@ -54,12 +65,12 @@ static void init_ ##slabname## _with_stride(struct slabname *s, \ s->slab = NULL; \ } \ \ -static void init_ ##slabname(struct slabname *s) \ +static MAYBE_UNUSED void init_ ##slabname(struct slabname *s) \ { \ init_ ##slabname## _with_stride(s, 1); \ } \ \ -static void clear_ ##slabname(struct slabname *s) \ +static MAYBE_UNUSED void clear_ ##slabname(struct slabname *s) \ { \ int i; \ for (i = 0; i < s->slab_count; i++) \ @@ -69,8 +80,8 @@ static void clear_ ##slabname(struct slabname *s) \ s->slab = NULL; \ } \ \ -static elemtype *slabname## _at(struct slabname *s, \ - const struct commit *c) \ +static MAYBE_UNUSED elemtype *slabname## _at(struct slabname *s, \ + const struct commit *c) \ { \ int nth_slab, nth_slot; \ \ @@ -80,7 +91,7 @@ static elemtype *slabname## _at(struct slabname *s, \ if (s->slab_count <= nth_slab) { \ int i; \ s->slab = xrealloc(s->slab, \ - (nth_slab + 1) * sizeof(s->slab)); \ + (nth_slab + 1) * sizeof(*s->slab)); \ stat_ ##slabname## realloc++; \ for (i = s->slab_count; i <= nth_slab; i++) \ s->slab[i] = NULL; \ @@ -94,4 +105,16 @@ static elemtype *slabname## _at(struct slabname *s, \ \ static int stat_ ##slabname## realloc +/* + * Note that this seemingly redundant second declaration is required + * to allow a terminating semicolon, which makes instantiations look + * like function declarations. I.e., the expansion of + * + * define_commit_slab(indegree, int); + * + * ends in 'static int stat_indegreerealloc;'. This would otherwise + * be a syntax error according (at least) to ISO C. It's hard to + * catch because GCC silently parses it by default. + */ + #endif /* COMMIT_SLAB_H */ @@ -79,7 +79,7 @@ struct commit *lookup_commit_reference_by_name(const char *name) if (get_sha1_committish(name, sha1)) return NULL; commit = lookup_commit_reference(sha1); - if (!commit || parse_commit(commit)) + if (parse_commit(commit)) return NULL; return commit; } @@ -196,19 +196,19 @@ bad_graft_data: static int read_graft_file(const char *graft_file) { FILE *fp = fopen(graft_file, "r"); - char buf[1024]; + struct strbuf buf = STRBUF_INIT; if (!fp) return -1; - while (fgets(buf, sizeof(buf), fp)) { + while (!strbuf_getwholeline(&buf, fp, '\n')) { /* The format is just "Commit Parent1 Parent2 ...\n" */ - int len = strlen(buf); - struct commit_graft *graft = read_graft_line(buf, len); + struct commit_graft *graft = read_graft_line(buf.buf, buf.len); if (!graft) continue; if (register_commit_graft(graft, 1)) - error("duplicate graft data: %s", buf); + error("duplicate graft data: %s", buf.buf); } fclose(fp); + strbuf_release(&buf); return 0; } @@ -341,6 +341,13 @@ int parse_commit(struct commit *item) return ret; } +void parse_commit_or_die(struct commit *item) +{ + if (parse_commit(item)) + die("unable to parse commit %s", + item ? sha1_to_hex(item->object.sha1) : "(null)"); +} + int find_commit_subject(const char *commit_buffer, const char **subject) { const char *eol; @@ -559,7 +566,7 @@ static void record_author_date(struct author_date_slab *author_date, buf; buf = line_end + 1) { line_end = strchrnul(buf, '\n'); - if (prefixcmp(buf, "author ")) { + if (!starts_with(buf, "author ")) { if (!line_end[0] || line_end[1] == '\n') return; /* end of header */ continue; @@ -1106,7 +1113,7 @@ int parse_signed_commit(const unsigned char *sha1, next = next ? next + 1 : tail; if (in_signature && line[0] == ' ') sig = line + 1; - else if (!prefixcmp(line, gpg_sig_header) && + else if (starts_with(line, gpg_sig_header) && line[gpg_sig_header_len] == ' ') sig = line + gpg_sig_header_len + 1; if (sig) { @@ -1186,7 +1193,7 @@ static void parse_gpg_output(struct signature_check *sigc) for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) { const char *found, *next; - if (!prefixcmp(buf, sigcheck_gpg_status[i].check + 1)) { + if (starts_with(buf, sigcheck_gpg_status[i].check + 1)) { /* At the very beginning of the buffer */ found = buf + strlen(sigcheck_gpg_status[i].check + 1); } else { @@ -1349,7 +1356,7 @@ void free_commit_extra_headers(struct commit_extra_header *extra) } } -int commit_tree(const struct strbuf *msg, unsigned char *tree, +int commit_tree(const struct strbuf *msg, const unsigned char *tree, struct commit_list *parents, unsigned char *ret, const char *author, const char *sign_commit) { @@ -1478,7 +1485,7 @@ static const char commit_utf8_warn[] = "You may want to amend it after fixing the message, or set the config\n" "variable i18n.commitencoding to the encoding your project uses.\n"; -int commit_tree_extended(const struct strbuf *msg, unsigned char *tree, +int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree, struct commit_list *parents, unsigned char *ret, const char *author, const char *sign_commit, struct commit_extra_header *extra) @@ -49,6 +49,7 @@ struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_n int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size); int parse_commit(struct commit *item); +void parse_commit_or_die(struct commit *item); /* Find beginning and length of commit subject. */ int find_commit_subject(const char *commit_buffer, const char **subject); @@ -231,11 +232,11 @@ struct commit_extra_header { extern void append_merge_tag_headers(struct commit_list *parents, struct commit_extra_header ***tail); -extern int commit_tree(const struct strbuf *msg, unsigned char *tree, +extern int commit_tree(const struct strbuf *msg, const unsigned char *tree, struct commit_list *parents, unsigned char *ret, const char *author, const char *sign_commit); -extern int commit_tree_extended(const struct strbuf *msg, unsigned char *tree, +extern int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree, struct commit_list *parents, unsigned char *ret, const char *author, const char *sign_commit, struct commit_extra_header *); diff --git a/compat/vcbuild/scripts/clink.pl b/compat/vcbuild/scripts/clink.pl index 4374771df2..4374771df2 100644..100755 --- a/compat/vcbuild/scripts/clink.pl +++ b/compat/vcbuild/scripts/clink.pl diff --git a/compat/vcbuild/scripts/lib.pl b/compat/vcbuild/scripts/lib.pl index d8054e469f..d8054e469f 100644..100755 --- a/compat/vcbuild/scripts/lib.pl +++ b/compat/vcbuild/scripts/lib.pl @@ -969,25 +969,25 @@ static int git_default_mailmap_config(const char *var, const char *value) int git_default_config(const char *var, const char *value, void *dummy) { - if (!prefixcmp(var, "core.")) + if (starts_with(var, "core.")) return git_default_core_config(var, value); - if (!prefixcmp(var, "user.")) + if (starts_with(var, "user.")) return git_ident_config(var, value, dummy); - if (!prefixcmp(var, "i18n.")) + if (starts_with(var, "i18n.")) return git_default_i18n_config(var, value); - if (!prefixcmp(var, "branch.")) + if (starts_with(var, "branch.")) return git_default_branch_config(var, value); - if (!prefixcmp(var, "push.")) + if (starts_with(var, "push.")) return git_default_push_config(var, value); - if (!prefixcmp(var, "mailmap.")) + if (starts_with(var, "mailmap.")) return git_default_mailmap_config(var, value); - if (!prefixcmp(var, "advice.")) + if (starts_with(var, "advice.")) return git_default_advice_config(var, value); if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) { @@ -1210,15 +1210,14 @@ int git_config(config_fn_t fn, void *data) * Find all the stuff for git_config_set() below. */ -#define MAX_MATCHES 512 - static struct { int baselen; char *key; int do_not_match; regex_t *value_regex; int multi_replace; - size_t offset[MAX_MATCHES]; + size_t *offset; + unsigned int offset_alloc; enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state; int seen; } store; @@ -1241,11 +1240,11 @@ static int store_aux(const char *key, const char *value, void *cb) if (matches(key, value)) { if (store.seen == 1 && store.multi_replace == 0) { warning("%s has multiple values", key); - } else if (store.seen >= MAX_MATCHES) { - error("too many matches for %s", key); - return 1; } + ALLOC_GROW(store.offset, store.seen + 1, + store.offset_alloc); + store.offset[store.seen] = cf->do_ftell(cf); store.seen++; } @@ -1273,11 +1272,15 @@ static int store_aux(const char *key, const char *value, void *cb) * Do not increment matches: this is no match, but we * just made sure we are in the desired section. */ + ALLOC_GROW(store.offset, store.seen + 1, + store.offset_alloc); store.offset[store.seen] = cf->do_ftell(cf); /* fallthru */ case SECTION_END_SEEN: case START: if (matches(key, value)) { + ALLOC_GROW(store.offset, store.seen + 1, + store.offset_alloc); store.offset[store.seen] = cf->do_ftell(cf); store.state = KEY_SEEN; store.seen++; @@ -1285,6 +1288,9 @@ static int store_aux(const char *key, const char *value, void *cb) if (strrchr(key, '.') - key == store.baselen && !strncmp(key, store.key, store.baselen)) { store.state = SECTION_SEEN; + ALLOC_GROW(store.offset, + store.seen + 1, + store.offset_alloc); store.offset[store.seen] = cf->do_ftell(cf); } } @@ -1583,6 +1589,7 @@ int git_config_set_multivar_in_file(const char *config_filename, } } + ALLOC_GROW(store.offset, 1, store.offset_alloc); store.offset[0] = 0; store.state = START; store.seen = 0; @@ -1872,7 +1879,7 @@ int parse_config_key(const char *var, const char *dot; /* Does it start with "section." ? */ - if (prefixcmp(var, section) || var[section_len] != '.') + if (!starts_with(var, section) || var[section_len] != '.') return -1; /* diff --git a/config.mak.uname b/config.mak.uname index 82d549e48b..7d31fad241 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -198,6 +198,13 @@ ifeq ($(uname_S),OpenBSD) BASIC_LDFLAGS += -L/usr/local/lib HAVE_PATHS_H = YesPlease endif +ifeq ($(uname_S),MirBSD) + NO_STRCASESTR = YesPlease + NO_MEMMEM = YesPlease + USE_ST_TIMESPEC = YesPlease + NEEDS_LIBICONV = YesPlease + HAVE_PATHS_H = YesPlease +endif ifeq ($(uname_S),NetBSD) ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2) NEEDS_LIBICONV = YesPlease @@ -145,7 +145,7 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len, if (!len) break; - if (len > 4 && !prefixcmp(buffer, "ERR ")) + if (len > 4 && starts_with(buffer, "ERR ")) die("remote error: %s", buffer + 4); if (len < 42 || get_sha1_hex(buffer, old_sha1) || buffer[40] != ' ') @@ -232,10 +232,34 @@ int server_supports(const char *feature) enum protocol { PROTO_LOCAL = 1, + PROTO_FILE, PROTO_SSH, PROTO_GIT }; +int url_is_local_not_ssh(const char *url) +{ + const char *colon = strchr(url, ':'); + const char *slash = strchr(url, '/'); + return !colon || (slash && slash < colon) || + has_dos_drive_prefix(url); +} + +static const char *prot_name(enum protocol protocol) +{ + switch (protocol) { + case PROTO_LOCAL: + case PROTO_FILE: + return "file"; + case PROTO_SSH: + return "ssh"; + case PROTO_GIT: + return "git"; + default: + return "unkown protocol"; + } +} + static enum protocol get_protocol(const char *name) { if (!strcmp(name, "ssh")) @@ -247,7 +271,7 @@ static enum protocol get_protocol(const char *name) if (!strcmp(name, "ssh+git")) return PROTO_SSH; if (!strcmp(name, "file")) - return PROTO_LOCAL; + return PROTO_FILE; die("I don't handle protocol '%s'", name); } @@ -527,55 +551,31 @@ static struct child_process *git_proxy_connect(int fd[2], char *host) return proxy; } -#define MAX_CMD_LEN 1024 - -static char *get_port(char *host) +static const char *get_port_numeric(const char *p) { char *end; - char *p = strchr(host, ':'); - if (p) { long port = strtol(p + 1, &end, 10); if (end != p + 1 && *end == '\0' && 0 <= port && port < 65536) { - *p = '\0'; - return p+1; + return p; } } return NULL; } -static struct child_process no_fork; - /* - * This returns a dummy child_process if the transport protocol does not - * need fork(2), or a struct child_process object if it does. Once done, - * finish the connection with finish_connect() with the value returned from - * this function (it is safe to call finish_connect() with NULL to support - * the former case). - * - * If it returns, the connect is successful; it just dies on errors (this - * will hopefully be changed in a libification effort, to return NULL when - * the connection failed). + * Extract protocol and relevant parts from the specified connection URL. + * The caller must free() the returned strings. */ -struct child_process *git_connect(int fd[2], const char *url_orig, - const char *prog, int flags) +static enum protocol parse_connect_url(const char *url_orig, char **ret_host, + char **ret_path) { char *url; char *host, *path; char *end; - int c; - struct child_process *conn = &no_fork; + int separator = '/'; enum protocol protocol = PROTO_LOCAL; - int free_path = 0; - char *port = NULL; - const char **arg; - struct strbuf cmd; - - /* Without this we cannot rely on waitpid() to tell - * what happened to our children. - */ - signal(SIGCHLD, SIG_DFL); if (is_url(url_orig)) url = url_decode(url_orig); @@ -587,40 +587,33 @@ struct child_process *git_connect(int fd[2], const char *url_orig, *host = '\0'; protocol = get_protocol(url); host += 3; - c = '/'; } else { host = url; - c = ':'; + if (!url_is_local_not_ssh(url)) { + protocol = PROTO_SSH; + separator = ':'; + } } /* - * Don't do destructive transforms with git:// as that - * protocol code does '[]' unwrapping of its own. + * Don't do destructive transforms as protocol code does + * '[]' unwrapping in get_host_and_port() */ if (host[0] == '[') { end = strchr(host + 1, ']'); if (end) { - if (protocol != PROTO_GIT) { - *end = 0; - host++; - } end++; } else end = host; } else end = host; - path = strchr(end, c); - if (path && !has_dos_drive_prefix(end)) { - if (c == ':') { - if (host != url || path < strchrnul(host, '/')) { - protocol = PROTO_SSH; - *path++ = '\0'; - } else /* '/' in the host part, assume local path */ - path = end; - } - } else + if (protocol == PROTO_LOCAL) path = end; + else if (protocol == PROTO_FILE && has_dos_drive_prefix(end)) + path = end; /* "file://$(pwd)" may be "file://C:/projects/repo" */ + else + path = strchr(end, separator); if (!path || !*path) die("No path specified. See 'man git-pull' for valid url syntax"); @@ -629,33 +622,67 @@ struct child_process *git_connect(int fd[2], const char *url_orig, * null-terminate hostname and point path to ~ for URL's like this: * ssh://host.xz/~user/repo */ - if (protocol != PROTO_LOCAL && host != url) { - char *ptr = path; + + end = path; /* Need to \0 terminate host here */ + if (separator == ':') + path++; /* path starts after ':' */ + if (protocol == PROTO_GIT || protocol == PROTO_SSH) { if (path[1] == '~') path++; - else { - path = xstrdup(ptr); - free_path = 1; - } - - *ptr = '\0'; } - /* - * Add support for ssh port: ssh://host.xy:<port>/... + path = xstrdup(path); + *end = '\0'; + + *ret_host = xstrdup(host); + *ret_path = path; + free(url); + return protocol; +} + +static struct child_process no_fork; + +/* + * This returns a dummy child_process if the transport protocol does not + * need fork(2), or a struct child_process object if it does. Once done, + * finish the connection with finish_connect() with the value returned from + * this function (it is safe to call finish_connect() with NULL to support + * the former case). + * + * If it returns, the connect is successful; it just dies on errors (this + * will hopefully be changed in a libification effort, to return NULL when + * the connection failed). + */ +struct child_process *git_connect(int fd[2], const char *url, + const char *prog, int flags) +{ + char *hostandport, *path; + struct child_process *conn = &no_fork; + enum protocol protocol; + const char **arg; + struct strbuf cmd = STRBUF_INIT; + + /* Without this we cannot rely on waitpid() to tell + * what happened to our children. */ - if (protocol == PROTO_SSH && host != url) - port = get_port(end); + signal(SIGCHLD, SIG_DFL); - if (protocol == PROTO_GIT) { + protocol = parse_connect_url(url, &hostandport, &path); + if (flags & CONNECT_DIAG_URL) { + printf("Diag: url=%s\n", url ? url : "NULL"); + printf("Diag: protocol=%s\n", prot_name(protocol)); + printf("Diag: hostandport=%s\n", hostandport ? hostandport : "NULL"); + printf("Diag: path=%s\n", path ? path : "NULL"); + conn = NULL; + } else if (protocol == PROTO_GIT) { /* These underlying connection commands die() if they * cannot connect. */ - char *target_host = xstrdup(host); - if (git_use_proxy(host)) - conn = git_proxy_connect(fd, host); + char *target_host = xstrdup(hostandport); + if (git_use_proxy(hostandport)) + conn = git_proxy_connect(fd, hostandport); else - git_tcp_connect(fd, host, flags); + git_tcp_connect(fd, hostandport, flags); /* * Separate original protocol components prog and path * from extended host header with a NUL byte. @@ -668,55 +695,51 @@ struct child_process *git_connect(int fd[2], const char *url_orig, prog, path, 0, target_host, 0); free(target_host); - free(url); - if (free_path) - free(path); - return conn; - } - - conn = xcalloc(1, sizeof(*conn)); - - strbuf_init(&cmd, MAX_CMD_LEN); - strbuf_addstr(&cmd, prog); - strbuf_addch(&cmd, ' '); - sq_quote_buf(&cmd, path); - if (cmd.len >= MAX_CMD_LEN) - die("command line too long"); - - conn->in = conn->out = -1; - conn->argv = arg = xcalloc(7, sizeof(*arg)); - if (protocol == PROTO_SSH) { - const char *ssh = getenv("GIT_SSH"); - int putty = ssh && strcasestr(ssh, "plink"); - if (!ssh) ssh = "ssh"; - - *arg++ = ssh; - if (putty && !strcasestr(ssh, "tortoiseplink")) - *arg++ = "-batch"; - if (port) { - /* P is for PuTTY, p is for OpenSSH */ - *arg++ = putty ? "-P" : "-p"; - *arg++ = port; + } else { + conn = xcalloc(1, sizeof(*conn)); + + strbuf_addstr(&cmd, prog); + strbuf_addch(&cmd, ' '); + sq_quote_buf(&cmd, path); + + conn->in = conn->out = -1; + conn->argv = arg = xcalloc(7, sizeof(*arg)); + if (protocol == PROTO_SSH) { + const char *ssh = getenv("GIT_SSH"); + int putty = ssh && strcasestr(ssh, "plink"); + char *ssh_host = hostandport; + const char *port = NULL; + get_host_and_port(&ssh_host, &port); + port = get_port_numeric(port); + + if (!ssh) ssh = "ssh"; + + *arg++ = ssh; + if (putty && !strcasestr(ssh, "tortoiseplink")) + *arg++ = "-batch"; + if (port) { + /* P is for PuTTY, p is for OpenSSH */ + *arg++ = putty ? "-P" : "-p"; + *arg++ = port; + } + *arg++ = ssh_host; + } else { + /* remove repo-local variables from the environment */ + conn->env = local_repo_env; + conn->use_shell = 1; } - *arg++ = host; - } - else { - /* remove repo-local variables from the environment */ - conn->env = local_repo_env; - conn->use_shell = 1; - } - *arg++ = cmd.buf; - *arg = NULL; + *arg++ = cmd.buf; + *arg = NULL; - if (start_command(conn)) - die("unable to fork"); + if (start_command(conn)) + die("unable to fork"); - fd[0] = conn->out; /* read from child's stdout */ - fd[1] = conn->in; /* write to child's stdin */ - strbuf_release(&cmd); - free(url); - if (free_path) - free(path); + fd[0] = conn->out; /* read from child's stdout */ + fd[1] = conn->in; /* write to child's stdin */ + strbuf_release(&cmd); + } + free(hostandport); + free(path); return conn; } @@ -2,11 +2,13 @@ #define CONNECT_H #define CONNECT_VERBOSE (1u << 0) +#define CONNECT_DIAG_URL (1u << 1) extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags); extern int finish_connect(struct child_process *conn); extern int git_connection_is_socket(struct child_process *conn); extern int server_supports(const char *feature); extern int parse_feature_request(const char *features, const char *feature); extern const char *server_feature_value(const char *feature, int *len_ret); +extern int url_is_local_not_ssh(const char *url); #endif diff --git a/connected.c b/connected.c index fae8d64c12..51d8ba4bb7 100644 --- a/connected.c +++ b/connected.c @@ -38,7 +38,7 @@ int check_everything_connected_with_transport(sha1_iterate_fn fn, if (transport && transport->smart_options && transport->smart_options->self_contained_and_connected && transport->pack_lockfile && - !suffixcmp(transport->pack_lockfile, ".keep")) { + ends_with(transport->pack_lockfile, ".keep")) { struct strbuf idx_file = STRBUF_INIT; strbuf_addstr(&idx_file, transport->pack_lockfile); strbuf_setlen(&idx_file, idx_file.len - 5); /* ".keep" */ diff --git a/contrib/buildsystems/engine.pl b/contrib/buildsystems/engine.pl index 23da787dc5..23da787dc5 100644..100755 --- a/contrib/buildsystems/engine.pl +++ b/contrib/buildsystems/engine.pl diff --git a/contrib/buildsystems/generate b/contrib/buildsystems/generate index bc10f25ff2..bc10f25ff2 100644..100755 --- a/contrib/buildsystems/generate +++ b/contrib/buildsystems/generate diff --git a/contrib/buildsystems/parse.pl b/contrib/buildsystems/parse.pl index c9656ece99..c9656ece99 100644..100755 --- a/contrib/buildsystems/parse.pl +++ b/contrib/buildsystems/parse.pl diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index dba3c15700..4fe5ce31bd 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1,5 +1,3 @@ -#!bash -# # bash/zsh completion support for core Git. # # Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org> @@ -180,9 +178,9 @@ _get_comp_words_by_ref () } fi -__gitcompadd () +__gitcompappend () { - local i=0 + local i=${#COMPREPLY[@]} for x in $1; do if [[ "$x" == "$3"* ]]; then COMPREPLY[i++]="$2$x$4" @@ -190,6 +188,12 @@ __gitcompadd () done } +__gitcompadd () +{ + COMPREPLY=() + __gitcompappend "$@" +} + # Generates completion reply, appending a space to possible completion words, # if necessary. # It accepts 1 to 4 arguments: @@ -220,6 +224,14 @@ __gitcomp () esac } +# Variation of __gitcomp_nl () that appends to the existing list of +# completion candidates, COMPREPLY. +__gitcomp_nl_append () +{ + local IFS=$'\n' + __gitcompappend "$1" "${2-}" "${3-$cur}" "${4- }" +} + # Generates completion reply from newline-separated possible completion words # by appending a space to all of them. # It accepts 1 to 4 arguments: @@ -231,8 +243,8 @@ __gitcomp () # appended. __gitcomp_nl () { - local IFS=$'\n' - __gitcompadd "$1" "${2-}" "${3-$cur}" "${4- }" + COMPREPLY=() + __gitcomp_nl_append "$@" } # Generates completion reply with compgen from newline-separated possible @@ -673,7 +685,6 @@ __git_list_porcelain_commands () index-pack) : plumbing;; init-db) : deprecated;; local-fetch) : plumbing;; - lost-found) : infrequent;; ls-files) : plumbing;; ls-remote) : plumbing;; ls-tree) : plumbing;; @@ -687,14 +698,12 @@ __git_list_porcelain_commands () pack-refs) : plumbing;; parse-remote) : plumbing;; patch-id) : plumbing;; - peek-remote) : plumbing;; prune) : plumbing;; prune-packed) : plumbing;; quiltimport) : import;; read-tree) : plumbing;; receive-pack) : plumbing;; remote-*) : transport;; - repo-config) : deprecated;; rerere) : plumbing;; rev-list) : plumbing;; rev-parse) : plumbing;; @@ -707,7 +716,6 @@ __git_list_porcelain_commands () ssh-*) : transport;; stripspace) : plumbing;; symbolic-ref) : plumbing;; - tar-tree) : deprecated;; unpack-file) : plumbing;; unpack-objects) : plumbing;; update-index) : plumbing;; @@ -1833,6 +1841,7 @@ _git_config () branch.*) local pfx="${cur%.*}." cur_="${cur#*.}" __gitcomp_nl "$(__git_heads)" "$pfx" "$cur_" "." + __gitcomp_nl_append $'autosetupmerge\nautosetuprebase\n' "$pfx" "$cur_" return ;; guitool.*.*) @@ -1875,6 +1884,7 @@ _git_config () remote.*) local pfx="${cur%.*}." cur_="${cur#*.}" __gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "." + __gitcomp_nl_append "pushdefault" "$pfx" "$cur_" return ;; url.*.*) diff --git a/contrib/completion/git-completion.tcsh b/contrib/completion/git-completion.tcsh index eaacaf0c3e..6104a42a23 100644 --- a/contrib/completion/git-completion.tcsh +++ b/contrib/completion/git-completion.tcsh @@ -1,5 +1,3 @@ -#!tcsh -# # tcsh completion support for core Git. # # Copyright (C) 2012 Marc Khouzam <marc.khouzam@gmail.com> diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh index fac5e711eb..6b77968572 100644 --- a/contrib/completion/git-completion.zsh +++ b/contrib/completion/git-completion.zsh @@ -30,10 +30,10 @@ if [ -z "$script" ]; then local -a locations local e locations=( + $(dirname ${funcsourcetrace[1]%:*})/git-completion.bash '/etc/bash_completion.d/git' # fedora, old debian '/usr/share/bash-completion/completions/git' # arch, ubuntu, new debian '/usr/share/bash-completion/git' # gentoo - $(dirname ${funcsourcetrace[1]%:*})/git-completion.bash ) for e in $locations; do test -f $e && script="$e" && break @@ -76,6 +76,14 @@ __gitcomp_nl () compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0 } +__gitcomp_nl_append () +{ + emulate -L zsh + + local IFS=$'\n' + compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0 +} + __gitcomp_file () { emulate -L zsh diff --git a/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c b/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c index 635c96bc56..2a317fca44 100644 --- a/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c +++ b/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c @@ -60,7 +60,7 @@ #define gnome_keyring_memory_free gnome_keyring_free_password #define gnome_keyring_memory_strdup g_strdup -static const char* gnome_keyring_result_to_message(GnomeKeyringResult result) +static const char *gnome_keyring_result_to_message(GnomeKeyringResult result) { switch (result) { case GNOME_KEYRING_RESULT_OK: @@ -95,9 +95,9 @@ static const char* gnome_keyring_result_to_message(GnomeKeyringResult result) static void gnome_keyring_done_cb(GnomeKeyringResult result, gpointer user_data) { - gpointer *data = (gpointer*) user_data; - int *done = (int*) data[0]; - GnomeKeyringResult *r = (GnomeKeyringResult*) data[1]; + gpointer *data = (gpointer *)user_data; + int *done = (int *)data[0]; + GnomeKeyringResult *r = (GnomeKeyringResult *)data[1]; *r = result; *done = 1; @@ -130,34 +130,30 @@ static GnomeKeyringResult gnome_keyring_item_delete_sync(const char *keyring, gu /* * This credential struct and API is simplified from git's credential.{h,c} */ -struct credential -{ - char *protocol; - char *host; +struct credential { + char *protocol; + char *host; unsigned short port; - char *path; - char *username; - char *password; + char *path; + char *username; + char *password; }; -#define CREDENTIAL_INIT \ - { NULL,NULL,0,NULL,NULL,NULL } +#define CREDENTIAL_INIT { NULL, NULL, 0, NULL, NULL, NULL } -typedef int (*credential_op_cb)(struct credential*); +typedef int (*credential_op_cb)(struct credential *); -struct credential_operation -{ - char *name; +struct credential_operation { + char *name; credential_op_cb op; }; -#define CREDENTIAL_OP_END \ - { NULL,NULL } +#define CREDENTIAL_OP_END { NULL, NULL } /* ----------------- GNOME Keyring functions ----------------- */ /* create a special keyring option string, if path is given */ -static char* keyring_object(struct credential *c) +static char *keyring_object(struct credential *c) { if (!c->path) return NULL; @@ -170,7 +166,7 @@ static char* keyring_object(struct credential *c) static int keyring_get(struct credential *c) { - char* object = NULL; + char *object = NULL; GList *entries; GnomeKeyringNetworkPasswordData *password_data; GnomeKeyringResult result; @@ -204,7 +200,7 @@ static int keyring_get(struct credential *c) } /* pick the first one from the list */ - password_data = (GnomeKeyringNetworkPasswordData *) entries->data; + password_data = (GnomeKeyringNetworkPasswordData *)entries->data; gnome_keyring_memory_free(c->password); c->password = gnome_keyring_memory_strdup(password_data->password); @@ -221,7 +217,7 @@ static int keyring_get(struct credential *c) static int keyring_store(struct credential *c) { guint32 item_id; - char *object = NULL; + char *object = NULL; GnomeKeyringResult result; /* @@ -262,7 +258,7 @@ static int keyring_store(struct credential *c) static int keyring_erase(struct credential *c) { - char *object = NULL; + char *object = NULL; GList *entries; GnomeKeyringNetworkPasswordData *password_data; GnomeKeyringResult result; @@ -298,22 +294,20 @@ static int keyring_erase(struct credential *c) if (result == GNOME_KEYRING_RESULT_CANCELLED) return EXIT_SUCCESS; - if (result != GNOME_KEYRING_RESULT_OK) - { + if (result != GNOME_KEYRING_RESULT_OK) { g_critical("%s", gnome_keyring_result_to_message(result)); return EXIT_FAILURE; } /* pick the first one from the list (delete all matches?) */ - password_data = (GnomeKeyringNetworkPasswordData *) entries->data; + password_data = (GnomeKeyringNetworkPasswordData *)entries->data; result = gnome_keyring_item_delete_sync( password_data->keyring, password_data->item_id); gnome_keyring_network_password_list_free(entries); - if (result != GNOME_KEYRING_RESULT_OK) - { + if (result != GNOME_KEYRING_RESULT_OK) { g_critical("%s", gnome_keyring_result_to_message(result)); return EXIT_FAILURE; } @@ -325,9 +319,8 @@ static int keyring_erase(struct credential *c) * Table with helper operation callbacks, used by generic * credential helper main function. */ -static struct credential_operation const credential_helper_ops[] = -{ - { "get", keyring_get }, +static struct credential_operation const credential_helper_ops[] = { + { "get", keyring_get }, { "store", keyring_store }, { "erase", keyring_erase }, CREDENTIAL_OP_END @@ -353,24 +346,23 @@ static void credential_clear(struct credential *c) static int credential_read(struct credential *c) { - char *buf; + char *buf; size_t line_len; - char *key; - char *value; + char *key; + char *value; key = buf = gnome_keyring_memory_alloc(1024); - while (fgets(buf, 1024, stdin)) - { + while (fgets(buf, 1024, stdin)) { line_len = strlen(buf); if (line_len && buf[line_len-1] == '\n') - buf[--line_len]='\0'; + buf[--line_len] = '\0'; if (!line_len) break; - value = strchr(buf,'='); + value = strchr(buf, '='); if (!value) { g_warning("invalid credential line: %s", key); gnome_keyring_memory_free(buf); @@ -384,7 +376,7 @@ static int credential_read(struct credential *c) } else if (!strcmp(key, "host")) { g_free(c->host); c->host = g_strdup(value); - value = strrchr(c->host,':'); + value = strrchr(c->host, ':'); if (value) { *value++ = '\0'; c->port = atoi(value); @@ -398,7 +390,8 @@ static int credential_read(struct credential *c) } else if (!strcmp(key, "password")) { gnome_keyring_memory_free(c->password); c->password = gnome_keyring_memory_strdup(value); - while (*value) *value++ = '\0'; + while (*value) + *value++ = '\0'; } /* * Ignore other lines; we don't know what they mean, but @@ -429,16 +422,16 @@ static void credential_write(const struct credential *c) static void usage(const char *name) { struct credential_operation const *try_op = credential_helper_ops; - const char *basename = strrchr(name,'/'); + const char *basename = strrchr(name, '/'); basename = (basename) ? basename + 1 : name; fprintf(stderr, "usage: %s <", basename); while (try_op->name) { - fprintf(stderr,"%s",(try_op++)->name); + fprintf(stderr, "%s", (try_op++)->name); if (try_op->name) - fprintf(stderr,"%s","|"); + fprintf(stderr, "%s", "|"); } - fprintf(stderr,"%s",">\n"); + fprintf(stderr, "%s", ">\n"); } int main(int argc, char *argv[]) @@ -446,7 +439,7 @@ int main(int argc, char *argv[]) int ret = EXIT_SUCCESS; struct credential_operation const *try_op = credential_helper_ops; - struct credential cred = CREDENTIAL_INIT; + struct credential cred = CREDENTIAL_INIT; if (!argv[1]) { usage(argv[0]); diff --git a/contrib/examples/git-whatchanged.sh b/contrib/examples/git-whatchanged.sh index 1fb9feb348..2edbdc6d99 100755 --- a/contrib/examples/git-whatchanged.sh +++ b/contrib/examples/git-whatchanged.sh @@ -9,12 +9,12 @@ case "$0" in *whatchanged) count= test -z "$diff_tree_flags" && - diff_tree_flags=$(git-repo-config --get whatchanged.difftree) + diff_tree_flags=$(git config --get whatchanged.difftree) diff_tree_default_flags='-c -M --abbrev' ;; *show) count=-n1 test -z "$diff_tree_flags" && - diff_tree_flags=$(git-repo-config --get show.difftree) + diff_tree_flags=$(git config --get show.difftree) diff_tree_default_flags='--cc --always' ;; esac test -z "$diff_tree_flags" && diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 8ee410f843..8747b84334 100755 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -22,7 +22,6 @@ # For example, on debian the hook is stored in # /usr/share/git-core/contrib/hooks/post-receive-email: # -# chmod a+x post-receive-email # cd /path/to/your/repository.git # ln -sf /usr/share/git-core/contrib/hooks/post-receive-email hooks/post-receive # diff --git a/contrib/hooks/pre-auto-gc-battery b/contrib/hooks/pre-auto-gc-battery index 1f914c94aa..9d0c2d1990 100644..100755 --- a/contrib/hooks/pre-auto-gc-battery +++ b/contrib/hooks/pre-auto-gc-battery @@ -13,7 +13,6 @@ # For example, if the hook is stored in # /usr/share/git-core/contrib/hooks/pre-auto-gc-battery: # -# chmod a+x pre-auto-gc-battery # cd /path/to/your/repository.git # ln -sf /usr/share/git-core/contrib/hooks/pre-auto-gc-battery \ # hooks/pre-auto-gc diff --git a/contrib/hooks/setgitperms.perl b/contrib/hooks/setgitperms.perl index 2770a1b1d2..2770a1b1d2 100644..100755 --- a/contrib/hooks/setgitperms.perl +++ b/contrib/hooks/setgitperms.perl diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index d18b317b2f..d18b317b2f 100644..100755 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid diff --git a/contrib/mw-to-git/Makefile b/contrib/mw-to-git/Makefile index f206f9655b..a4b6f7a2cd 100644 --- a/contrib/mw-to-git/Makefile +++ b/contrib/mw-to-git/Makefile @@ -18,9 +18,13 @@ SCRIPT_PERL+=git-mw.perl GIT_ROOT_DIR=../.. HERE=contrib/mw-to-git/ +INSTALL = install + SCRIPT_PERL_FULL=$(patsubst %,$(HERE)/%,$(SCRIPT_PERL)) INSTLIBDIR=$(shell $(MAKE) -C $(GIT_ROOT_DIR)/perl \ -s --no-print-directory instlibdir) +DESTDIR_SQ = $(subst ','\'',$(DESTDIR)) +INSTLIBDIR_SQ = $(subst ','\'',$(INSTLIBDIR)) all: build @@ -30,7 +34,9 @@ test: all check: perlcritic test install_pm: - install $(GIT_MEDIAWIKI_PM) $(INSTLIBDIR)/$(GIT_MEDIAWIKI_PM) + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(INSTLIBDIR_SQ)/Git' + $(INSTALL) -m 644 $(GIT_MEDIAWIKI_PM) \ + '$(DESTDIR_SQ)$(INSTLIBDIR_SQ)/$(GIT_MEDIAWIKI_PM)' build: $(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL="$(SCRIPT_PERL_FULL)" \ @@ -43,7 +49,6 @@ install: install_pm clean: $(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL="$(SCRIPT_PERL_FULL)" \ clean-perl-script - rm $(INSTLIBDIR)/$(GIT_MEDIAWIKI_PM) perlcritic: perlcritic -5 $(SCRIPT_PERL) diff --git a/contrib/p4import/README b/contrib/p4import/README deleted file mode 100644 index b9892b6793..0000000000 --- a/contrib/p4import/README +++ /dev/null @@ -1 +0,0 @@ -Please see contrib/fast-import/git-p4 for a better Perforce importer. diff --git a/contrib/p4import/git-p4import.py b/contrib/p4import/git-p4import.py deleted file mode 100644 index 593d6a0682..0000000000 --- a/contrib/p4import/git-p4import.py +++ /dev/null @@ -1,365 +0,0 @@ -#!/usr/bin/env python -# -# This tool is copyright (c) 2006, Sean Estabrooks. -# It is released under the Gnu Public License, version 2. -# -# Import Perforce branches into Git repositories. -# Checking out the files is done by calling the standard p4 -# client which you must have properly configured yourself -# - -import marshal -import os -import sys -import time -import getopt - -if sys.hexversion < 0x02020000: - # The behavior of the marshal module changed significantly in 2.2 - sys.stderr.write("git-p4import.py: requires Python 2.2 or later.\n") - sys.exit(1) - -from signal import signal, \ - SIGPIPE, SIGINT, SIG_DFL, \ - default_int_handler - -signal(SIGPIPE, SIG_DFL) -s = signal(SIGINT, SIG_DFL) -if s != default_int_handler: - signal(SIGINT, s) - -def die(msg, *args): - for a in args: - msg = "%s %s" % (msg, a) - print "git-p4import fatal error:", msg - sys.exit(1) - -def usage(): - print "USAGE: git-p4import [-q|-v] [--authors=<file>] [-t <timezone>] [//p4repo/path <branch>]" - sys.exit(1) - -verbosity = 1 -logfile = "/dev/null" -ignore_warnings = False -stitch = 0 -tagall = True - -def report(level, msg, *args): - global verbosity - global logfile - for a in args: - msg = "%s %s" % (msg, a) - fd = open(logfile, "a") - fd.writelines(msg) - fd.close() - if level <= verbosity: - print msg - -class p4_command: - def __init__(self, _repopath): - try: - global logfile - self.userlist = {} - if _repopath[-1] == '/': - self.repopath = _repopath[:-1] - else: - self.repopath = _repopath - if self.repopath[-4:] != "/...": - self.repopath= "%s/..." % self.repopath - f=os.popen('p4 -V 2>>%s'%logfile, 'rb') - a = f.readlines() - if f.close(): - raise - except: - die("Could not find the \"p4\" command") - - def p4(self, cmd, *args): - global logfile - cmd = "%s %s" % (cmd, ' '.join(args)) - report(2, "P4:", cmd) - f=os.popen('p4 -G %s 2>>%s' % (cmd,logfile), 'rb') - list = [] - while 1: - try: - list.append(marshal.load(f)) - except EOFError: - break - self.ret = f.close() - return list - - def sync(self, id, force=False, trick=False, test=False): - if force: - ret = self.p4("sync -f %s@%s"%(self.repopath, id))[0] - elif trick: - ret = self.p4("sync -k %s@%s"%(self.repopath, id))[0] - elif test: - ret = self.p4("sync -n %s@%s"%(self.repopath, id))[0] - else: - ret = self.p4("sync %s@%s"%(self.repopath, id))[0] - if ret['code'] == "error": - data = ret['data'].upper() - if data.find('VIEW') > 0: - die("Perforce reports %s is not in client view"% self.repopath) - elif data.find('UP-TO-DATE') < 0: - die("Could not sync files from perforce", self.repopath) - - def changes(self, since=0): - try: - list = [] - for rec in self.p4("changes %s@%s,#head" % (self.repopath, since+1)): - list.append(rec['change']) - list.reverse() - return list - except: - return [] - - def authors(self, filename): - f=open(filename) - for l in f.readlines(): - self.userlist[l[:l.find('=')].rstrip()] = \ - (l[l.find('=')+1:l.find('<')].rstrip(),l[l.find('<')+1:l.find('>')]) - f.close() - for f,e in self.userlist.items(): - report(2, f, ":", e[0], " <", e[1], ">") - - def _get_user(self, id): - if not self.userlist.has_key(id): - try: - user = self.p4("users", id)[0] - self.userlist[id] = (user['FullName'], user['Email']) - except: - self.userlist[id] = (id, "") - return self.userlist[id] - - def _format_date(self, ticks): - symbol='+' - name = time.tzname[0] - offset = time.timezone - if ticks[8]: - name = time.tzname[1] - offset = time.altzone - if offset < 0: - offset *= -1 - symbol = '-' - localo = "%s%02d%02d %s" % (symbol, offset / 3600, offset % 3600, name) - tickso = time.strftime("%a %b %d %H:%M:%S %Y", ticks) - return "%s %s" % (tickso, localo) - - def where(self): - try: - return self.p4("where %s" % self.repopath)[-1]['path'] - except: - return "" - - def describe(self, num): - desc = self.p4("describe -s", num)[0] - self.msg = desc['desc'] - self.author, self.email = self._get_user(desc['user']) - self.date = self._format_date(time.localtime(long(desc['time']))) - return self - -class git_command: - def __init__(self): - try: - self.version = self.git("--version")[0][12:].rstrip() - except: - die("Could not find the \"git\" command") - try: - self.gitdir = self.get_single("rev-parse --git-dir") - report(2, "gdir:", self.gitdir) - except: - die("Not a git repository... did you forget to \"git init\" ?") - try: - self.cdup = self.get_single("rev-parse --show-cdup") - if self.cdup != "": - os.chdir(self.cdup) - self.topdir = os.getcwd() - report(2, "topdir:", self.topdir) - except: - die("Could not find top git directory") - - def git(self, cmd): - global logfile - report(2, "GIT:", cmd) - f=os.popen('git %s 2>>%s' % (cmd,logfile), 'rb') - r=f.readlines() - self.ret = f.close() - return r - - def get_single(self, cmd): - return self.git(cmd)[0].rstrip() - - def current_branch(self): - try: - testit = self.git("rev-parse --verify HEAD")[0] - return self.git("symbolic-ref HEAD")[0][11:].rstrip() - except: - return None - - def get_config(self, variable): - try: - return self.git("config --get %s" % variable)[0].rstrip() - except: - return None - - def set_config(self, variable, value): - try: - self.git("config %s %s"%(variable, value) ) - except: - die("Could not set %s to " % variable, value) - - def make_tag(self, name, head): - self.git("tag -f %s %s"%(name,head)) - - def top_change(self, branch): - try: - a=self.get_single("name-rev --tags refs/heads/%s" % branch) - loc = a.find(' tags/') + 6 - if a[loc:loc+3] != "p4/": - raise - return int(a[loc+3:][:-2]) - except: - return 0 - - def update_index(self): - self.git("ls-files -m -d -o -z | git update-index --add --remove -z --stdin") - - def checkout(self, branch): - self.git("checkout %s" % branch) - - def repoint_head(self, branch): - self.git("symbolic-ref HEAD refs/heads/%s" % branch) - - def remove_files(self): - self.git("ls-files | xargs rm") - - def clean_directories(self): - self.git("clean -d") - - def fresh_branch(self, branch): - report(1, "Creating new branch", branch) - self.git("ls-files | xargs rm") - os.remove(".git/index") - self.repoint_head(branch) - self.git("clean -d") - - def basedir(self): - return self.topdir - - def commit(self, author, email, date, msg, id): - self.update_index() - fd=open(".msg", "w") - fd.writelines(msg) - fd.close() - try: - current = self.get_single("rev-parse --verify HEAD") - head = "-p HEAD" - except: - current = "" - head = "" - tree = self.get_single("write-tree") - for r,l in [('DATE',date),('NAME',author),('EMAIL',email)]: - os.environ['GIT_AUTHOR_%s'%r] = l - os.environ['GIT_COMMITTER_%s'%r] = l - commit = self.get_single("commit-tree %s %s < .msg" % (tree,head)) - os.remove(".msg") - self.make_tag("p4/%s"%id, commit) - self.git("update-ref HEAD %s %s" % (commit, current) ) - -try: - opts, args = getopt.getopt(sys.argv[1:], "qhvt:", - ["authors=","help","stitch=","timezone=","log=","ignore","notags"]) -except getopt.GetoptError: - usage() - -for o, a in opts: - if o == "-q": - verbosity = 0 - if o == "-v": - verbosity += 1 - if o in ("--log"): - logfile = a - if o in ("--notags"): - tagall = False - if o in ("-h", "--help"): - usage() - if o in ("--ignore"): - ignore_warnings = True - -git = git_command() -branch=git.current_branch() - -for o, a in opts: - if o in ("-t", "--timezone"): - git.set_config("perforce.timezone", a) - if o in ("--stitch"): - git.set_config("perforce.%s.path" % branch, a) - stitch = 1 - -if len(args) == 2: - branch = args[1] - git.checkout(branch) - if branch == git.current_branch(): - die("Branch %s already exists!" % branch) - report(1, "Setting perforce to ", args[0]) - git.set_config("perforce.%s.path" % branch, args[0]) -elif len(args) != 0: - die("You must specify the perforce //depot/path and git branch") - -p4path = git.get_config("perforce.%s.path" % branch) -if p4path == None: - die("Do not know Perforce //depot/path for git branch", branch) - -p4 = p4_command(p4path) - -for o, a in opts: - if o in ("-a", "--authors"): - p4.authors(a) - -localdir = git.basedir() -if p4.where()[:len(localdir)] != localdir: - report(1, "**WARNING** Appears p4 client is misconfigured") - report(1, " for sync from %s to %s" % (p4.repopath, localdir)) - if ignore_warnings != True: - die("Reconfigure or use \"--ignore\" on command line") - -if stitch == 0: - top = git.top_change(branch) -else: - top = 0 -changes = p4.changes(top) -count = len(changes) -if count == 0: - report(1, "Already up to date...") - sys.exit(0) - -ptz = git.get_config("perforce.timezone") -if ptz: - report(1, "Setting timezone to", ptz) - os.environ['TZ'] = ptz - time.tzset() - -if stitch == 1: - git.remove_files() - git.clean_directories() - p4.sync(changes[0], force=True) -elif top == 0 and branch != git.current_branch(): - p4.sync(changes[0], test=True) - report(1, "Creating new initial commit"); - git.fresh_branch(branch) - p4.sync(changes[0], force=True) -else: - p4.sync(changes[0], trick=True) - -report(1, "processing %s changes from p4 (%s) to git (%s)" % (count, p4.repopath, branch)) -for id in changes: - report(1, "Importing changeset", id) - change = p4.describe(id) - p4.sync(id) - if tagall : - git.commit(change.author, change.email, change.date, change.msg, id) - else: - git.commit(change.author, change.email, change.date, change.msg, "import") - if stitch == 1: - git.clean_directories() - stitch = 0 diff --git a/contrib/p4import/git-p4import.txt b/contrib/p4import/git-p4import.txt deleted file mode 100644 index 9967587fe6..0000000000 --- a/contrib/p4import/git-p4import.txt +++ /dev/null @@ -1,167 +0,0 @@ -git-p4import(1) -=============== - -NAME ----- -git-p4import - Import a Perforce repository into git - - -SYNOPSIS --------- -[verse] -`git-p4import` [-q|-v] [--notags] [--authors <file>] [-t <timezone>] - <//p4repo/path> <branch> -`git-p4import` --stitch <//p4repo/path> -`git-p4import` - - -DESCRIPTION ------------ -Import a Perforce repository into an existing git repository. When -a <//p4repo/path> and <branch> are specified a new branch with the -given name will be created and the initial import will begin. - -Once the initial import is complete you can do an incremental import -of new commits from the Perforce repository. You do this by checking -out the appropriate git branch and then running `git-p4import` without -any options. - -The standard p4 client is used to communicate with the Perforce -repository; it must be configured correctly in order for `git-p4import` -to operate (see below). - - -OPTIONS -------- --q:: - Do not display any progress information. - --v:: - Give extra progress information. - -\--authors:: - Specify an authors file containing a mapping of Perforce user - ids to full names and email addresses (see Notes below). - -\--notags:: - Do not create a tag for each imported commit. - -\--stitch:: - Import the contents of the given perforce branch into the - currently checked out git branch. - -\--log:: - Store debugging information in the specified file. - --t:: - Specify that the remote repository is in the specified timezone. - Timezone must be in the format "US/Pacific" or "Europe/London" - etc. You only need to specify this once, it will be saved in - the git config file for the repository. - -<//p4repo/path>:: - The Perforce path that will be imported into the specified branch. - -<branch>:: - The new branch that will be created to hold the Perforce imports. - - -P4 Client ---------- -You must make the `p4` client command available in your $PATH and -configure it to communicate with the target Perforce repository. -Typically this means you must set the "$P4PORT" and "$P4CLIENT" -environment variables. - -You must also configure a `p4` client "view" which maps the Perforce -branch into the top level of your git repository, for example: - ------------- -Client: myhost - -Root: /home/sean/import - -Options: noallwrite clobber nocompress unlocked modtime rmdir - -View: - //public/jam/... //myhost/jam/... ------------- - -With the above `p4` client setup, you could import the "jam" -perforce branch into a branch named "jammy", like so: - ------------- -$ mkdir -p /home/sean/import/jam -$ cd /home/sean/import/jam -$ git init -$ git p4import //public/jam jammy ------------- - - -Multiple Branches ------------------ -Note that by creating multiple "views" you can use `git-p4import` -to import additional branches into the same git repository. -However, the `p4` client has a limitation in that it silently -ignores all but the last "view" that maps into the same local -directory. So the following will *not* work: - ------------- -View: - //public/jam/... //myhost/jam/... - //public/other/... //myhost/jam/... - //public/guest/... //myhost/jam/... ------------- - -If you want more than one Perforce branch to be imported into the -same directory you must employ a workaround. A simple option is -to adjust your `p4` client before each import to only include a -single view. - -Another option is to create multiple symlinks locally which all -point to the same directory in your git repository and then use -one per "view" instead of listing the actual directory. - - -Tags ----- -A git tag of the form p4/xx is created for every change imported from -the Perforce repository where xx is the Perforce changeset number. -Therefore after the import you can use git to access any commit by its -Perforce number, e.g. git show p4/327. - -The tag associated with the HEAD commit is also how `git-p4import` -determines if there are new changes to incrementally import from the -Perforce repository. - -If you import from a repository with many thousands of changes -you will have an equal number of p4/xxxx git tags. Git tags can -be expensive in terms of disk space and repository operations. -If you don't need to perform further incremental imports, you -may delete the tags. - - -Notes ------ -You can interrupt the import (e.g. ctrl-c) at any time and restart it -without worry. - -Author information is automatically determined by querying the -Perforce "users" table using the id associated with each change. -However, if you want to manually supply these mappings you can do -so with the "--authors" option. It accepts a file containing a list -of mappings with each line containing one mapping in the format: - ------------- - perforce_id = Full Name <email@address.com> ------------- - - -Author ------- -Written by Sean Estabrooks <seanlkml@sympatico.ca> - - -GIT ---- -Part of the gitlink:git[7] suite diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr index 054161ae21..332aba784b 100755 --- a/contrib/remote-helpers/git-remote-bzr +++ b/contrib/remote-helpers/git-remote-bzr @@ -44,8 +44,8 @@ import StringIO import atexit, shutil, hashlib, urlparse, subprocess NAME_RE = re.compile('^([^<>]+)') -AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]*)>$') -EMAIL_RE = re.compile('^([^<>]+[^ \\\t<>])?\\b(?:[ \\t<>]*?)\\b([^ \\t<>]+@[^ \\t<>]+)') +AUTHOR_RE = re.compile('^([^<>]+?)? ?[<>]([^<>]*)(?:$|>)') +EMAIL_RE = re.compile(r'([^ \t<>]+@[^ \t<>]+)') RAW_AUTHOR_RE = re.compile('^(\w+) (.+)? <(.*)> (\d+) ([+-]\d+)') def die(msg, *args): @@ -193,8 +193,7 @@ def fixup_user(user): else: m = EMAIL_RE.match(user) if m: - name = m.group(1) - mail = m.group(2) + mail = m.group(1) else: m = NAME_RE.match(user) if m: @@ -884,6 +883,16 @@ def main(args): global branches, peers global transports + marks = None + is_tmp = False + gitdir = os.environ.get('GIT_DIR', None) + + if len(args) < 3: + die('Not enough arguments.') + + if not gitdir: + die('GIT_DIR not set') + alias = args[1] url = args[2] @@ -892,7 +901,6 @@ def main(args): blob_marks = {} parsed_refs = {} files_cache = {} - marks = None branches = {} peers = {} transports = [] @@ -900,11 +908,8 @@ def main(args): if alias[5:] == url: is_tmp = True alias = hashlib.sha1(alias).hexdigest() - else: - is_tmp = False prefix = 'refs/bzr/%s' % alias - gitdir = os.environ['GIT_DIR'] dirname = os.path.join(gitdir, 'bzr', alias) if not is_tmp: diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg index c6026b9bed..eb89ef6779 100755 --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@ -51,8 +51,8 @@ import time as ptime # NAME_RE = re.compile('^([^<>]+)') -AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]*)>$') -EMAIL_RE = re.compile('^([^<>]+[^ \\\t<>])?\\b(?:[ \\t<>]*?)\\b([^ \\t<>]+@[^ \\t<>]+)') +AUTHOR_RE = re.compile('^([^<>]+?)? ?[<>]([^<>]*)(?:$|>)') +EMAIL_RE = re.compile(r'([^ \t<>]+@[^ \t<>]+)') AUTHOR_HG_RE = re.compile('^(.*?) ?<(.*?)(?:>(.+)?)?$') RAW_AUTHOR_RE = re.compile('^(\w+) (?:(.+)? )?<(.*)> (\d+) ([+-]\d+)') @@ -316,8 +316,7 @@ def fixup_user_git(user): else: m = EMAIL_RE.match(user) if m: - name = m.group(1) - mail = m.group(2) + mail = m.group(1) else: m = NAME_RE.match(user) if m: @@ -416,6 +415,9 @@ def get_repo(url, alias): local_path = os.path.join(dirname, 'clone') if not os.path.exists(local_path): hg.share(myui, shared_path, local_path, update=False) + else: + # make sure the shared path is always up-to-date + util.writefile(os.path.join(local_path, '.hg', 'sharedpath'), hg_path) repo = hg.repository(myui, local_path) try: @@ -538,7 +540,7 @@ def export_ref(repo, name, kind, head): print "commit %s" % ref print "mark :%d" % (note_mark) - print "committer remote-hg <> %s" % (ptime.strftime('%s %z')) + print "committer remote-hg <> %d %s" % (ptime.time(), gittz(ptime.timezone)) desc = "Notes for %s\n" % (name) print "data %d" % (len(desc)) print desc @@ -1165,6 +1167,16 @@ def main(args): global dry_run global notes, alias + marks = None + is_tmp = False + gitdir = os.environ.get('GIT_DIR', None) + + if len(args) < 3: + die('Not enough arguments.') + + if not gitdir: + die('GIT_DIR not set') + alias = args[1] url = args[2] peer = None @@ -1185,16 +1197,12 @@ def main(args): if alias[4:] == url: is_tmp = True alias = hashlib.sha1(alias).hexdigest() - else: - is_tmp = False - gitdir = os.environ['GIT_DIR'] dirname = os.path.join(gitdir, 'hg', alias) branches = {} bmarks = {} blob_marks = {} parsed_refs = {} - marks = None parsed_tags = {} filenodes = {} fake_bmark = None diff --git a/contrib/remote-helpers/test-bzr.sh b/contrib/remote-helpers/test-bzr.sh index 5c50251783..1e53ff9a58 100755 --- a/contrib/remote-helpers/test-bzr.sh +++ b/contrib/remote-helpers/test-bzr.sh @@ -5,7 +5,8 @@ test_description='Test remote-bzr' -. ./test-lib.sh +test -n "$TEST_DIRECTORY" || TEST_DIRECTORY=${0%/*}/../../t +. "$TEST_DIRECTORY"/test-lib.sh if ! test_have_prereq PYTHON then @@ -378,7 +379,7 @@ test_expect_success 'export utf-8 authors' ' git add content && git commit -m one && git remote add bzr "bzr::../bzrrepo" && - git push bzr + git push bzr master ) && ( diff --git a/contrib/remote-helpers/test-hg.sh b/contrib/remote-helpers/test-hg.sh index 72f745d63f..5d128a5da9 100755 --- a/contrib/remote-helpers/test-hg.sh +++ b/contrib/remote-helpers/test-hg.sh @@ -8,7 +8,8 @@ test_description='Test remote-hg' -. ./test-lib.sh +test -n "$TEST_DIRECTORY" || TEST_DIRECTORY=${0%/*}/../../t +. "$TEST_DIRECTORY"/test-lib.sh if ! test_have_prereq PYTHON then @@ -53,14 +54,14 @@ check_bookmark () { } check_push () { - local expected_ret=$1 ret=0 ref_ret=0 IFS=':' + expected_ret=$1 ret=0 ref_ret=0 shift git push origin "$@" 2>error ret=$? cat error - while read branch kind + while IFS=':' read branch kind do case "$kind" in 'new') @@ -82,7 +83,7 @@ check_push () { test $ref_ret -ne 0 && echo "match for '$branch' failed" && break done - if test $expected_ret -ne $ret -o $ref_ret -ne 0 + if test $expected_ret -ne $ret || test $ref_ret -ne 0 then return 1 fi @@ -205,16 +206,17 @@ test_expect_success 'authors' ' >../expected && author_test alpha "" "H G Wells <wells@example.com>" && - author_test beta "test" "test <unknown>" && - author_test beta "test <test@example.com> (comment)" "test <test@example.com>" && - author_test gamma "<test@example.com>" "Unknown <test@example.com>" && - author_test delta "name<test@example.com>" "name <test@example.com>" && - author_test epsilon "name <test@example.com" "name <test@example.com>" && - author_test zeta " test " "test <unknown>" && - author_test eta "test < test@example.com >" "test <test@example.com>" && - author_test theta "test >test@example.com>" "test <test@example.com>" && - author_test iota "test < test <at> example <dot> com>" "test <unknown>" && - author_test kappa "test@example.com" "Unknown <test@example.com>" + author_test beta "beta" "beta <unknown>" && + author_test gamma "gamma <test@example.com> (comment)" "gamma <test@example.com>" && + author_test delta "<delta@example.com>" "Unknown <delta@example.com>" && + author_test epsilon "epsilon<test@example.com>" "epsilon <test@example.com>" && + author_test zeta "zeta <test@example.com" "zeta <test@example.com>" && + author_test eta " eta " "eta <unknown>" && + author_test theta "theta < test@example.com >" "theta <test@example.com>" && + author_test iota "iota >test@example.com>" "iota <test@example.com>" && + author_test kappa "kappa < test <at> example <dot> com>" "kappa <unknown>" && + author_test lambda "lambda@example.com" "Unknown <lambda@example.com>" && + author_test mu "mu.mu@example.com" "Unknown <mu.mu@example.com>" ) && git clone "hg::hgrepo" gitrepo && @@ -335,6 +337,17 @@ test_expect_success 'remote cloning' ' check gitrepo HEAD zero ' +test_expect_success 'moving remote clone' ' + test_when_finished "rm -rf gitrepo*" && + + ( + git clone "hg::hgrepo" gitrepo && + mv gitrepo gitrepo2 && + cd gitrepo2 && + git fetch + ) +' + test_expect_success 'remote update bookmark' ' test_when_finished "rm -rf gitrepo*" && @@ -442,6 +455,74 @@ test_expect_success 'remote new bookmark multiple branch head' ' # cleanup previous stuff rm -rf hgrepo +test_expect_success 'fetch special filenames' ' + test_when_finished "rm -rf hgrepo gitrepo && LC_ALL=C" && + + LC_ALL=en_US.UTF-8 + export LC_ALL + + ( + hg init hgrepo && + cd hgrepo && + + echo test >> "æ rø" && + hg add "æ rø" && + echo test >> "ø~?" && + hg add "ø~?" && + hg commit -m add-utf-8 && + echo test >> "æ rø" && + hg commit -m test-utf-8 && + hg rm "ø~?" && + hg mv "æ rø" "ø~?" && + hg commit -m hg-mv-utf-8 + ) && + + ( + git clone "hg::hgrepo" gitrepo && + cd gitrepo && + git -c core.quotepath=false ls-files > ../actual + ) && + echo "ø~?" > expected && + test_cmp expected actual +' + +test_expect_success 'push special filenames' ' + test_when_finished "rm -rf hgrepo gitrepo && LC_ALL=C" && + + mkdir -p tmp && cd tmp && + + LC_ALL=en_US.UTF-8 + export LC_ALL + + ( + hg init hgrepo && + cd hgrepo && + + echo one >> content && + hg add content && + hg commit -m one + ) && + + ( + git clone "hg::hgrepo" gitrepo && + cd gitrepo && + + echo test >> "æ rø" && + git add "æ rø" && + git commit -m utf-8 && + + git push + ) && + + (cd hgrepo && + hg update && + hg manifest > ../actual + ) && + + printf "content\næ rø\n" > expected && + test_cmp expected actual +' + setup_big_push () { ( hg init hgrepo && @@ -1121,7 +1121,7 @@ static int is_foreign_ident(const char *str) { int i; - if (prefixcmp(str, "$Id: ")) + if (!starts_with(str, "$Id: ")) return 0; for (i = 5; str[i]; i++) { if (isspace(str[i]) && str[i+1] != '$') diff --git a/csum-file.c b/csum-file.c index 53f5375b6c..b00b215031 100644 --- a/csum-file.c +++ b/csum-file.c @@ -11,7 +11,7 @@ #include "progress.h" #include "csum-file.h" -static void flush(struct sha1file *f, void *buf, unsigned int count) +static void flush(struct sha1file *f, const void *buf, unsigned int count) { if (0 <= f->check_fd && count) { unsigned char check_buffer[8192]; @@ -86,13 +86,13 @@ int sha1close(struct sha1file *f, unsigned char *result, unsigned int flags) return fd; } -int sha1write(struct sha1file *f, void *buf, unsigned int count) +void sha1write(struct sha1file *f, const void *buf, unsigned int count) { while (count) { unsigned offset = f->offset; unsigned left = sizeof(f->buffer) - offset; unsigned nr = count > left ? left : count; - void *data; + const void *data; if (f->do_crc) f->crc32 = crc32(f->crc32, buf, nr); @@ -116,7 +116,6 @@ int sha1write(struct sha1file *f, void *buf, unsigned int count) } f->offset = offset; } - return 0; } struct sha1file *sha1fd(int fd, const char *name) diff --git a/csum-file.h b/csum-file.h index 3b540bdc21..bb543d52f1 100644 --- a/csum-file.h +++ b/csum-file.h @@ -34,7 +34,7 @@ extern struct sha1file *sha1fd(int fd, const char *name); extern struct sha1file *sha1fd_check(const char *name); extern struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp); extern int sha1close(struct sha1file *, unsigned char *, unsigned int); -extern int sha1write(struct sha1file *, void *, unsigned int); +extern void sha1write(struct sha1file *, const void *, unsigned int); extern void sha1flush(struct sha1file *f); extern void crc32_begin(struct sha1file *); extern uint32_t crc32_end(struct sha1file *); @@ -235,7 +235,7 @@ static int service_enabled; static int git_daemon_config(const char *var, const char *value, void *cb) { - if (!prefixcmp(var, "daemon.") && + if (starts_with(var, "daemon.") && !strcmp(var + 7, service_looking_at->config_name)) { service_enabled = git_config_bool(var, value); return 0; @@ -633,7 +633,7 @@ static int execute(void) for (i = 0; i < ARRAY_SIZE(daemon_service); i++) { struct daemon_service *s = &(daemon_service[i]); int namelen = strlen(s->name); - if (!prefixcmp(line, "git-") && + if (starts_with(line, "git-") && !strncmp(s->name, line + 4, namelen) && line[namelen + 4] == ' ') { /* @@ -1165,11 +1165,11 @@ int main(int argc, char **argv) for (i = 1; i < argc; i++) { char *arg = argv[i]; - if (!prefixcmp(arg, "--listen=")) { + if (starts_with(arg, "--listen=")) { string_list_append(&listen_addr, xstrdup_tolower(arg + 9)); continue; } - if (!prefixcmp(arg, "--port=")) { + if (starts_with(arg, "--port=")) { char *end; unsigned long n; n = strtoul(arg+7, &end, 0); @@ -1199,19 +1199,19 @@ int main(int argc, char **argv) export_all_trees = 1; continue; } - if (!prefixcmp(arg, "--access-hook=")) { + if (starts_with(arg, "--access-hook=")) { access_hook = arg + 14; continue; } - if (!prefixcmp(arg, "--timeout=")) { + if (starts_with(arg, "--timeout=")) { timeout = atoi(arg+10); continue; } - if (!prefixcmp(arg, "--init-timeout=")) { + if (starts_with(arg, "--init-timeout=")) { init_timeout = atoi(arg+15); continue; } - if (!prefixcmp(arg, "--max-connections=")) { + if (starts_with(arg, "--max-connections=")) { max_connections = atoi(arg+18); if (max_connections < 0) max_connections = 0; /* unlimited */ @@ -1221,7 +1221,7 @@ int main(int argc, char **argv) strict_paths = 1; continue; } - if (!prefixcmp(arg, "--base-path=")) { + if (starts_with(arg, "--base-path=")) { base_path = arg+12; continue; } @@ -1229,7 +1229,7 @@ int main(int argc, char **argv) base_path_relaxed = 1; continue; } - if (!prefixcmp(arg, "--interpolated-path=")) { + if (starts_with(arg, "--interpolated-path=")) { interpolated_path = arg+20; continue; } @@ -1241,11 +1241,11 @@ int main(int argc, char **argv) user_path = ""; continue; } - if (!prefixcmp(arg, "--user-path=")) { + if (starts_with(arg, "--user-path=")) { user_path = arg + 12; continue; } - if (!prefixcmp(arg, "--pid-file=")) { + if (starts_with(arg, "--pid-file=")) { pid_file = arg + 11; continue; } @@ -1254,35 +1254,35 @@ int main(int argc, char **argv) log_syslog = 1; continue; } - if (!prefixcmp(arg, "--user=")) { + if (starts_with(arg, "--user=")) { user_name = arg + 7; continue; } - if (!prefixcmp(arg, "--group=")) { + if (starts_with(arg, "--group=")) { group_name = arg + 8; continue; } - if (!prefixcmp(arg, "--enable=")) { + if (starts_with(arg, "--enable=")) { enable_service(arg + 9, 1); continue; } - if (!prefixcmp(arg, "--disable=")) { + if (starts_with(arg, "--disable=")) { enable_service(arg + 10, 0); continue; } - if (!prefixcmp(arg, "--allow-override=")) { + if (starts_with(arg, "--allow-override=")) { make_service_overridable(arg + 17, 1); continue; } - if (!prefixcmp(arg, "--forbid-override=")) { + if (starts_with(arg, "--forbid-override=")) { make_service_overridable(arg + 18, 0); continue; } - if (!prefixcmp(arg, "--informative-errors")) { + if (!strcmp(arg, "--informative-errors")) { informative_errors = 1; continue; } - if (!prefixcmp(arg, "--no-informative-errors")) { + if (!strcmp(arg, "--no-informative-errors")) { informative_errors = 0; continue; } diff --git a/diff-no-index.c b/diff-no-index.c index 00a8eefde9..33e5982a1c 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -183,54 +183,12 @@ static int queue_diff(struct diff_options *o, void diff_no_index(struct rev_info *revs, int argc, const char **argv, - int nongit, const char *prefix) + const char *prefix) { int i, prefixlen; - int no_index = 0; unsigned deprecated_show_diff_q_option_used = 0; const char *paths[2]; - /* Were we asked to do --no-index explicitly? */ - for (i = 1; i < argc; i++) { - if (!strcmp(argv[i], "--")) { - i++; - break; - } - if (!strcmp(argv[i], "--no-index")) - no_index = 1; - if (argv[i][0] != '-') - break; - } - - if (!no_index && !nongit) { - /* - * Inside a git repository, without --no-index. Only - * when a path outside the repository is given, - * e.g. "git diff /var/tmp/[12]", or "git diff - * Makefile /var/tmp/Makefile", allow it to be used as - * a colourful "diff" replacement. - */ - if ((argc != i + 2) || - (path_inside_repo(prefix, argv[i]) && - path_inside_repo(prefix, argv[i+1]))) - return; - } - if (argc != i + 2) { - if (!no_index) { - /* - * There was no --no-index and there were not two - * paths. It is possible that the user intended - * to do an inside-repository operation. - */ - fprintf(stderr, "Not a git repository\n"); - fprintf(stderr, - "To compare two paths outside a working tree:\n"); - } - /* Give the usage message for non-repository usage and exit. */ - usagef("git diff %s <path> <path>", - no_index ? "--no-index" : "[--no-index]"); - } - diff_setup(&revs->diffopt); for (i = 1; i < argc - 2; ) { int j; @@ -30,6 +30,7 @@ static int diff_use_color_default = -1; static int diff_context_default = 3; static const char *diff_word_regex_cfg; static const char *external_diff_cmd_cfg; +static const char *diff_order_file_cfg; int diff_auto_refresh_index = 1; static int diff_mnemonic_prefix; static int diff_no_prefix; @@ -201,6 +202,8 @@ int git_diff_ui_config(const char *var, const char *value, void *cb) return git_config_string(&external_diff_cmd_cfg, var, value); if (!strcmp(var, "diff.wordregex")) return git_config_string(&diff_word_regex_cfg, var, value); + if (!strcmp(var, "diff.orderfile")) + return git_config_pathname(&diff_order_file_cfg, var, value); if (!strcmp(var, "diff.ignoresubmodules")) handle_ignore_submodules_arg(&default_diff_options, value); @@ -235,7 +238,7 @@ int git_diff_basic_config(const char *var, const char *value, void *cb) if (userdiff_config(var, value) < 0) return -1; - if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) { + if (starts_with(var, "diff.color.") || starts_with(var, "color.diff.")) { int slot = parse_diff_color_slot(var, 11); if (slot < 0) return 0; @@ -264,7 +267,7 @@ int git_diff_basic_config(const char *var, const char *value, void *cb) return 0; } - if (!prefixcmp(var, "submodule.")) + if (starts_with(var, "submodule.")) return parse_submodule_config_option(var, value); return git_default_config(var, value, cb); @@ -1215,7 +1218,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) diff_words_append(line, len, &ecbdata->diff_words->plus); return; - } else if (!prefixcmp(line, "\\ ")) { + } else if (starts_with(line, "\\ ")) { /* * Eat the "no newline at eof" marker as if we * saw a "+" or "-" line with nothing on it, @@ -2387,9 +2390,9 @@ static void builtin_diff(const char *name_a, xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags); if (!diffopts) ; - else if (!prefixcmp(diffopts, "--unified=")) + else if (starts_with(diffopts, "--unified=")) xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10); - else if (!prefixcmp(diffopts, "-u")) + else if (starts_with(diffopts, "-u")) xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10); if (o->word_diff) init_diff_words_data(&ecbdata, o, one, two); @@ -2899,11 +2902,16 @@ static void run_external_diff(const char *pgm, struct diff_filespec *one, struct diff_filespec *two, const char *xfrm_msg, - int complete_rewrite) + int complete_rewrite, + struct diff_options *o) { const char *spawn_arg[10]; int retval; const char **arg = &spawn_arg[0]; + struct diff_queue_struct *q = &diff_queued_diff; + const char *env[3] = { NULL }; + char env_counter[50]; + char env_total[50]; if (one && two) { struct diff_tempfile *temp_one, *temp_two; @@ -2928,7 +2936,14 @@ static void run_external_diff(const char *pgm, } *arg = NULL; fflush(NULL); - retval = run_command_v_opt(spawn_arg, RUN_USING_SHELL); + + env[0] = env_counter; + snprintf(env_counter, sizeof(env_counter), "GIT_DIFF_PATH_COUNTER=%d", + ++o->diff_path_counter); + env[1] = env_total; + snprintf(env_total, sizeof(env_total), "GIT_DIFF_PATH_TOTAL=%d", q->nr); + + retval = run_command_v_opt_cd_env(spawn_arg, RUN_USING_SHELL, NULL, env); remove_tempfile(); if (retval) { fprintf(stderr, "external diff died, stopping at %s.\n", name); @@ -3042,7 +3057,7 @@ static void run_diff_cmd(const char *pgm, if (pgm) { run_external_diff(pgm, name, other, one, two, xfrm_msg, - complete_rewrite); + complete_rewrite, o); return; } if (one && two) @@ -3207,6 +3222,8 @@ void diff_setup(struct diff_options *options) options->detect_rename = diff_detect_rename_default; options->xdl_opts |= diff_algorithm; + options->orderfile = diff_order_file_cfg; + if (diff_no_prefix) { options->a_prefix = options->b_prefix = ""; } else if (!diff_mnemonic_prefix) { @@ -3317,6 +3334,8 @@ void diff_setup_done(struct diff_options *options) options->output_format = DIFF_FORMAT_NO_OUTPUT; DIFF_OPT_SET(options, EXIT_WITH_STATUS); } + + options->diff_path_counter = 0; } static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val) @@ -3391,10 +3410,10 @@ int parse_long_opt(const char *opt, const char **argv, if (arg[0] != '-' || arg[1] != '-') return 0; arg += strlen("--"); - if (prefixcmp(arg, opt)) + if (!starts_with(arg, opt)) return 0; arg += strlen(opt); - if (*arg == '=') { /* sticked form: --option=value */ + if (*arg == '=') { /* stuck form: --option=value */ *optarg = arg + 1; return 1; } @@ -3422,7 +3441,7 @@ static int stat_opt(struct diff_options *options, const char **av) switch (*arg) { case '-': - if (!prefixcmp(arg, "-width")) { + if (starts_with(arg, "-width")) { arg += strlen("-width"); if (*arg == '=') width = strtoul(arg + 1, &end, 10); @@ -3432,7 +3451,7 @@ static int stat_opt(struct diff_options *options, const char **av) width = strtoul(av[1], &end, 10); argcount = 2; } - } else if (!prefixcmp(arg, "-name-width")) { + } else if (starts_with(arg, "-name-width")) { arg += strlen("-name-width"); if (*arg == '=') name_width = strtoul(arg + 1, &end, 10); @@ -3442,7 +3461,7 @@ static int stat_opt(struct diff_options *options, const char **av) name_width = strtoul(av[1], &end, 10); argcount = 2; } - } else if (!prefixcmp(arg, "-graph-width")) { + } else if (starts_with(arg, "-graph-width")) { arg += strlen("-graph-width"); if (*arg == '=') graph_width = strtoul(arg + 1, &end, 10); @@ -3452,7 +3471,7 @@ static int stat_opt(struct diff_options *options, const char **av) graph_width = strtoul(av[1], &end, 10); argcount = 2; } - } else if (!prefixcmp(arg, "-count")) { + } else if (starts_with(arg, "-count")) { arg += strlen("-count"); if (*arg == '=') count = strtoul(arg + 1, &end, 10); @@ -3614,15 +3633,15 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->output_format |= DIFF_FORMAT_SHORTSTAT; else if (!strcmp(arg, "-X") || !strcmp(arg, "--dirstat")) return parse_dirstat_opt(options, ""); - else if (!prefixcmp(arg, "-X")) + else if (starts_with(arg, "-X")) return parse_dirstat_opt(options, arg + 2); - else if (!prefixcmp(arg, "--dirstat=")) + else if (starts_with(arg, "--dirstat=")) return parse_dirstat_opt(options, arg + 10); else if (!strcmp(arg, "--cumulative")) return parse_dirstat_opt(options, "cumulative"); else if (!strcmp(arg, "--dirstat-by-file")) return parse_dirstat_opt(options, "files"); - else if (!prefixcmp(arg, "--dirstat-by-file=")) { + else if (starts_with(arg, "--dirstat-by-file=")) { parse_dirstat_opt(options, "files"); return parse_dirstat_opt(options, arg + 18); } @@ -3639,17 +3658,17 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->output_format |= DIFF_FORMAT_NAME_STATUS; else if (!strcmp(arg, "-s") || !strcmp(arg, "--no-patch")) options->output_format |= DIFF_FORMAT_NO_OUTPUT; - else if (!prefixcmp(arg, "--stat")) + else if (starts_with(arg, "--stat")) /* --stat, --stat-width, --stat-name-width, or --stat-count */ return stat_opt(options, av); /* renames options */ - else if (!prefixcmp(arg, "-B") || !prefixcmp(arg, "--break-rewrites=") || + else if (starts_with(arg, "-B") || starts_with(arg, "--break-rewrites=") || !strcmp(arg, "--break-rewrites")) { if ((options->break_opt = diff_scoreopt_parse(arg)) == -1) return error("invalid argument to -B: %s", arg+2); } - else if (!prefixcmp(arg, "-M") || !prefixcmp(arg, "--find-renames=") || + else if (starts_with(arg, "-M") || starts_with(arg, "--find-renames=") || !strcmp(arg, "--find-renames")) { if ((options->rename_score = diff_scoreopt_parse(arg)) == -1) return error("invalid argument to -M: %s", arg+2); @@ -3658,7 +3677,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (!strcmp(arg, "-D") || !strcmp(arg, "--irreversible-delete")) { options->irreversible_delete = 1; } - else if (!prefixcmp(arg, "-C") || !prefixcmp(arg, "--find-copies=") || + else if (starts_with(arg, "-C") || starts_with(arg, "--find-copies=") || !strcmp(arg, "--find-copies")) { if (options->detect_rename == DIFF_DETECT_COPY) DIFF_OPT_SET(options, FIND_COPIES_HARDER); @@ -3674,7 +3693,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) DIFF_OPT_CLR(options, RENAME_EMPTY); else if (!strcmp(arg, "--relative")) DIFF_OPT_SET(options, RELATIVE_NAME); - else if (!prefixcmp(arg, "--relative=")) { + else if (starts_with(arg, "--relative=")) { DIFF_OPT_SET(options, RELATIVE_NAME); options->prefix = arg + 11; } @@ -3727,7 +3746,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) DIFF_OPT_CLR(options, FOLLOW_RENAMES); else if (!strcmp(arg, "--color")) options->use_color = 1; - else if (!prefixcmp(arg, "--color=")) { + else if (starts_with(arg, "--color=")) { int value = git_config_colorbool(NULL, arg+8); if (value < 0) return error("option `color' expects \"always\", \"auto\", or \"never\""); @@ -3739,7 +3758,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->use_color = 1; options->word_diff = DIFF_WORDS_COLOR; } - else if (!prefixcmp(arg, "--color-words=")) { + else if (starts_with(arg, "--color-words=")) { options->use_color = 1; options->word_diff = DIFF_WORDS_COLOR; options->word_regex = arg + 14; @@ -3748,7 +3767,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) if (options->word_diff == DIFF_WORDS_NONE) options->word_diff = DIFF_WORDS_PLAIN; } - else if (!prefixcmp(arg, "--word-diff=")) { + else if (starts_with(arg, "--word-diff=")) { const char *type = arg + 12; if (!strcmp(type, "plain")) options->word_diff = DIFF_WORDS_PLAIN; @@ -3784,12 +3803,12 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (!strcmp(arg, "--ignore-submodules")) { DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG); handle_ignore_submodules_arg(options, "all"); - } else if (!prefixcmp(arg, "--ignore-submodules=")) { + } else if (starts_with(arg, "--ignore-submodules=")) { DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG); handle_ignore_submodules_arg(options, arg + 20); } else if (!strcmp(arg, "--submodule")) DIFF_OPT_SET(options, SUBMODULE_LOG); - else if (!prefixcmp(arg, "--submodule=")) + else if (starts_with(arg, "--submodule=")) return parse_submodule_opt(options, arg + 12); /* misc options */ @@ -3825,7 +3844,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) } else if (!strcmp(arg, "--abbrev")) options->abbrev = DEFAULT_ABBREV; - else if (!prefixcmp(arg, "--abbrev=")) { + else if (starts_with(arg, "--abbrev=")) { options->abbrev = strtoul(arg + 9, NULL, 10); if (options->abbrev < MINIMUM_ABBREV) options->abbrev = MINIMUM_ABBREV; @@ -3907,15 +3926,15 @@ static int diff_scoreopt_parse(const char *opt) cmd = *opt++; if (cmd == '-') { /* convert the long-form arguments into short-form versions */ - if (!prefixcmp(opt, "break-rewrites")) { + if (starts_with(opt, "break-rewrites")) { opt += strlen("break-rewrites"); if (*opt == 0 || *opt++ == '=') cmd = 'B'; - } else if (!prefixcmp(opt, "find-copies")) { + } else if (starts_with(opt, "find-copies")) { opt += strlen("find-copies"); if (*opt == 0 || *opt++ == '=') cmd = 'C'; - } else if (!prefixcmp(opt, "find-renames")) { + } else if (starts_with(opt, "find-renames")) { opt += strlen("find-renames"); if (*opt == 0 || *opt++ == '=') cmd = 'M'; @@ -4325,7 +4344,7 @@ static void patch_id_consume(void *priv, char *line, unsigned long len) int new_len; /* Ignore line numbers when computing the SHA1 of the patch */ - if (!prefixcmp(line, "@@ -")) + if (starts_with(line, "@@ -")) return; new_len = remove_space(line, len); @@ -164,6 +164,8 @@ struct diff_options { diff_prefix_fn_t output_prefix; int output_prefix_length; void *output_prefix_data; + + int diff_path_counter; }; enum color_diff { @@ -244,7 +246,7 @@ extern struct diff_filepair *diff_unmerge(struct diff_options *, const char *pat #define DIFF_SETUP_USE_SIZE_CACHE 4 /* - * Poor man's alternative to parse-option, to allow both sticked form + * Poor man's alternative to parse-option, to allow both stuck form * (--option=value) and separate form (--option value). */ extern int parse_long_opt(const char *opt, const char **argv, @@ -330,7 +332,7 @@ extern int diff_flush_patch_id(struct diff_options *, unsigned char *); extern int diff_result_code(struct diff_options *, int); -extern void diff_no_index(struct rev_info *, int, const char **, int, const char *); +extern void diff_no_index(struct rev_info *, int, const char **, const char *); extern int index_differs_from(const char *def, int diff_flags); diff --git a/diffcore-order.c b/diffcore-order.c index 23e93852d8..fe7f1f4647 100644 --- a/diffcore-order.c +++ b/diffcore-order.c @@ -10,28 +10,21 @@ static int order_cnt; static void prepare_order(const char *orderfile) { - int fd, cnt, pass; + int cnt, pass; + struct strbuf sb = STRBUF_INIT; void *map; char *cp, *endp; - struct stat st; - size_t sz; + ssize_t sz; if (order) return; - fd = open(orderfile, O_RDONLY); - if (fd < 0) - return; - if (fstat(fd, &st)) { - close(fd); - return; - } - sz = xsize_t(st.st_size); - map = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); - close(fd); - if (map == MAP_FAILED) - return; + sz = strbuf_read_file(&sb, orderfile, 0); + if (sz < 0) + die_errno(_("failed to read orderfile '%s'"), orderfile); + map = strbuf_detach(&sb, NULL); endp = (char *) map + sz; + for (pass = 0; pass < 2; pass++) { cnt = 0; cp = map; @@ -73,15 +66,16 @@ struct pair_order { static int match_order(const char *path) { int i; - char p[PATH_MAX]; + static struct strbuf p = STRBUF_INIT; for (i = 0; i < order_cnt; i++) { - strcpy(p, path); - while (p[0]) { + strbuf_reset(&p); + strbuf_addstr(&p, path); + while (p.buf[0]) { char *cp; - if (!fnmatch(order[i], p, 0)) + if (!fnmatch(order[i], p.buf, 0)) return i; - cp = strrchr(p, '/'); + cp = strrchr(p.buf, '/'); if (!cp) break; *cp = 0; @@ -126,10 +126,13 @@ static size_t common_prefix_len(const struct pathspec *pathspec) PATHSPEC_MAXDEPTH | PATHSPEC_LITERAL | PATHSPEC_GLOB | - PATHSPEC_ICASE); + PATHSPEC_ICASE | + PATHSPEC_EXCLUDE); for (n = 0; n < pathspec->nr; n++) { size_t i = 0, len = 0, item_len; + if (pathspec->items[n].magic & PATHSPEC_EXCLUDE) + continue; if (pathspec->items[n].magic & PATHSPEC_ICASE) item_len = pathspec->items[n].prefix; else @@ -279,9 +282,10 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix, * pathspec did not match any names, which could indicate that the * user mistyped the nth pathspec. */ -int match_pathspec_depth(const struct pathspec *ps, - const char *name, int namelen, - int prefix, char *seen) +static int match_pathspec_depth_1(const struct pathspec *ps, + const char *name, int namelen, + int prefix, char *seen, + int exclude) { int i, retval = 0; @@ -290,7 +294,8 @@ int match_pathspec_depth(const struct pathspec *ps, PATHSPEC_MAXDEPTH | PATHSPEC_LITERAL | PATHSPEC_GLOB | - PATHSPEC_ICASE); + PATHSPEC_ICASE | + PATHSPEC_EXCLUDE); if (!ps->nr) { if (!ps->recursive || @@ -309,8 +314,19 @@ int match_pathspec_depth(const struct pathspec *ps, for (i = ps->nr - 1; i >= 0; i--) { int how; + + if ((!exclude && ps->items[i].magic & PATHSPEC_EXCLUDE) || + ( exclude && !(ps->items[i].magic & PATHSPEC_EXCLUDE))) + continue; + if (seen && seen[i] == MATCHED_EXACTLY) continue; + /* + * Make exclude patterns optional and never report + * "pathspec ':(exclude)foo' matches no files" + */ + if (seen && ps->items[i].magic & PATHSPEC_EXCLUDE) + seen[i] = MATCHED_FNMATCH; how = match_pathspec_item(ps->items+i, prefix, name, namelen); if (ps->recursive && (ps->magic & PATHSPEC_MAXDEPTH) && @@ -334,6 +350,18 @@ int match_pathspec_depth(const struct pathspec *ps, return retval; } +int match_pathspec_depth(const struct pathspec *ps, + const char *name, int namelen, + int prefix, char *seen) +{ + int positive, negative; + positive = match_pathspec_depth_1(ps, name, namelen, prefix, seen, 0); + if (!(ps->magic & PATHSPEC_EXCLUDE) || !positive) + return positive; + negative = match_pathspec_depth_1(ps, name, namelen, prefix, seen, 1); + return negative ? 0 : positive; +} + /* * Return the length of the "simple" part of a path match limiter. */ @@ -1375,11 +1403,18 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru PATHSPEC_MAXDEPTH | PATHSPEC_LITERAL | PATHSPEC_GLOB | - PATHSPEC_ICASE); + PATHSPEC_ICASE | + PATHSPEC_EXCLUDE); if (has_symlink_leading_path(path, len)) return dir->nr; + /* + * exclude patterns are treated like positive ones in + * create_simplify. Usually exclude patterns should be a + * subset of positive ones, which has no impacts on + * create_simplify(). + */ simplify = create_simplify(pathspec ? pathspec->_raw : NULL); if (!len || treat_leading_path(dir, path, len, simplify)) read_directory_recursive(dir, path, len, 0, simplify); diff --git a/environment.c b/environment.c index 0a15349cfe..3c76905b9f 100644 --- a/environment.c +++ b/environment.c @@ -171,7 +171,7 @@ const char *get_git_namespace(void) const char *strip_namespace(const char *namespaced_ref) { - if (prefixcmp(namespaced_ref, get_git_namespace()) != 0) + if (!starts_with(namespaced_ref, get_git_namespace())) return NULL; return namespaced_ref + namespace_len; } diff --git a/fast-import.c b/fast-import.c index f4d9969e5c..4fd18a3574 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1877,8 +1877,8 @@ static int read_next_command(void) return EOF; if (!seen_data_command - && prefixcmp(command_buf.buf, "feature ") - && prefixcmp(command_buf.buf, "option ")) { + && !starts_with(command_buf.buf, "feature ") + && !starts_with(command_buf.buf, "option ")) { parse_argv(); } @@ -1898,7 +1898,7 @@ static int read_next_command(void) rc->prev->next = rc; cmd_tail = rc; } - if (!prefixcmp(command_buf.buf, "cat-blob ")) { + if (starts_with(command_buf.buf, "cat-blob ")) { parse_cat_blob(); continue; } @@ -1917,7 +1917,7 @@ static void skip_optional_lf(void) static void parse_mark(void) { - if (!prefixcmp(command_buf.buf, "mark :")) { + if (starts_with(command_buf.buf, "mark :")) { next_mark = strtoumax(command_buf.buf + 6, NULL, 10); read_next_command(); } @@ -1929,10 +1929,10 @@ static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res) { strbuf_reset(sb); - if (prefixcmp(command_buf.buf, "data ")) + if (!starts_with(command_buf.buf, "data ")) die("Expected 'data n' command, found: %s", command_buf.buf); - if (!prefixcmp(command_buf.buf + 5, "<<")) { + if (starts_with(command_buf.buf + 5, "<<")) { char *term = xstrdup(command_buf.buf + 5 + 2); size_t term_len = command_buf.len - 5 - 2; @@ -2306,7 +2306,7 @@ static void file_change_m(struct branch *b) if (*p == ':') { oe = find_mark(parse_mark_ref_space(&p)); hashcpy(sha1, oe->idx.sha1); - } else if (!prefixcmp(p, "inline ")) { + } else if (starts_with(p, "inline ")) { inline_data = 1; oe = NULL; /* not used with inline_data, but makes gcc happy */ p += strlen("inline"); /* advance to space */ @@ -2479,7 +2479,7 @@ static void note_change_n(struct branch *b, unsigned char *old_fanout) if (*p == ':') { oe = find_mark(parse_mark_ref_space(&p)); hashcpy(sha1, oe->idx.sha1); - } else if (!prefixcmp(p, "inline ")) { + } else if (starts_with(p, "inline ")) { inline_data = 1; oe = NULL; /* not used with inline_data, but makes gcc happy */ p += strlen("inline"); /* advance to space */ @@ -2590,7 +2590,7 @@ static int parse_from(struct branch *b) const char *from; struct branch *s; - if (prefixcmp(command_buf.buf, "from ")) + if (!starts_with(command_buf.buf, "from ")) return 0; if (b->branch_tree.tree) { @@ -2636,7 +2636,7 @@ static struct hash_list *parse_merge(unsigned int *count) struct branch *s; *count = 0; - while (!prefixcmp(command_buf.buf, "merge ")) { + while (starts_with(command_buf.buf, "merge ")) { from = strchr(command_buf.buf, ' ') + 1; n = xmalloc(sizeof(*n)); s = lookup_branch(from); @@ -2687,11 +2687,11 @@ static void parse_new_commit(void) read_next_command(); parse_mark(); - if (!prefixcmp(command_buf.buf, "author ")) { + if (starts_with(command_buf.buf, "author ")) { author = parse_ident(command_buf.buf + 7); read_next_command(); } - if (!prefixcmp(command_buf.buf, "committer ")) { + if (starts_with(command_buf.buf, "committer ")) { committer = parse_ident(command_buf.buf + 10); read_next_command(); } @@ -2712,19 +2712,19 @@ static void parse_new_commit(void) /* file_change* */ while (command_buf.len > 0) { - if (!prefixcmp(command_buf.buf, "M ")) + if (starts_with(command_buf.buf, "M ")) file_change_m(b); - else if (!prefixcmp(command_buf.buf, "D ")) + else if (starts_with(command_buf.buf, "D ")) file_change_d(b); - else if (!prefixcmp(command_buf.buf, "R ")) + else if (starts_with(command_buf.buf, "R ")) file_change_cr(b, 1); - else if (!prefixcmp(command_buf.buf, "C ")) + else if (starts_with(command_buf.buf, "C ")) file_change_cr(b, 0); - else if (!prefixcmp(command_buf.buf, "N ")) + else if (starts_with(command_buf.buf, "N ")) note_change_n(b, &prev_fanout); else if (!strcmp("deleteall", command_buf.buf)) file_change_deleteall(b); - else if (!prefixcmp(command_buf.buf, "ls ")) + else if (starts_with(command_buf.buf, "ls ")) parse_ls(b); else { unread_command_buf = 1; @@ -2793,7 +2793,7 @@ static void parse_new_tag(void) read_next_command(); /* from ... */ - if (prefixcmp(command_buf.buf, "from ")) + if (!starts_with(command_buf.buf, "from ")) die("Expected from command, got %s", command_buf.buf); from = strchr(command_buf.buf, ' ') + 1; s = lookup_branch(from); @@ -2821,7 +2821,7 @@ static void parse_new_tag(void) read_next_command(); /* tagger ... */ - if (!prefixcmp(command_buf.buf, "tagger ")) { + if (starts_with(command_buf.buf, "tagger ")) { tagger = parse_ident(command_buf.buf + 7); read_next_command(); } else @@ -3209,7 +3209,7 @@ static void option_export_pack_edges(const char *edges) static int parse_one_option(const char *option) { - if (!prefixcmp(option, "max-pack-size=")) { + if (starts_with(option, "max-pack-size=")) { unsigned long v; if (!git_parse_ulong(option + 14, &v)) return 0; @@ -3221,20 +3221,20 @@ static int parse_one_option(const char *option) v = 1024 * 1024; } max_packsize = v; - } else if (!prefixcmp(option, "big-file-threshold=")) { + } else if (starts_with(option, "big-file-threshold=")) { unsigned long v; if (!git_parse_ulong(option + 19, &v)) return 0; big_file_threshold = v; - } else if (!prefixcmp(option, "depth=")) { + } else if (starts_with(option, "depth=")) { option_depth(option + 6); - } else if (!prefixcmp(option, "active-branches=")) { + } else if (starts_with(option, "active-branches=")) { option_active_branches(option + 16); - } else if (!prefixcmp(option, "export-pack-edges=")) { + } else if (starts_with(option, "export-pack-edges=")) { option_export_pack_edges(option + 18); - } else if (!prefixcmp(option, "quiet")) { + } else if (starts_with(option, "quiet")) { show_stats = 0; - } else if (!prefixcmp(option, "stats")) { + } else if (starts_with(option, "stats")) { show_stats = 1; } else { return 0; @@ -3245,14 +3245,14 @@ static int parse_one_option(const char *option) static int parse_one_feature(const char *feature, int from_stream) { - if (!prefixcmp(feature, "date-format=")) { + if (starts_with(feature, "date-format=")) { option_date_format(feature + 12); - } else if (!prefixcmp(feature, "import-marks=")) { + } else if (starts_with(feature, "import-marks=")) { option_import_marks(feature + 13, from_stream, 0); - } else if (!prefixcmp(feature, "import-marks-if-exists=")) { + } else if (starts_with(feature, "import-marks-if-exists=")) { option_import_marks(feature + strlen("import-marks-if-exists="), from_stream, 1); - } else if (!prefixcmp(feature, "export-marks=")) { + } else if (starts_with(feature, "export-marks=")) { option_export_marks(feature + 13); } else if (!strcmp(feature, "cat-blob")) { ; /* Don't die - this feature is supported */ @@ -3350,7 +3350,7 @@ static void parse_argv(void) if (parse_one_feature(a + 2, 0)) continue; - if (!prefixcmp(a + 2, "cat-blob-fd=")) { + if (starts_with(a + 2, "cat-blob-fd=")) { option_cat_blob_fd(a + 2 + strlen("cat-blob-fd=")); continue; } @@ -3404,25 +3404,25 @@ int main(int argc, char **argv) while (read_next_command() != EOF) { if (!strcmp("blob", command_buf.buf)) parse_new_blob(); - else if (!prefixcmp(command_buf.buf, "ls ")) + else if (starts_with(command_buf.buf, "ls ")) parse_ls(NULL); - else if (!prefixcmp(command_buf.buf, "commit ")) + else if (starts_with(command_buf.buf, "commit ")) parse_new_commit(); - else if (!prefixcmp(command_buf.buf, "tag ")) + else if (starts_with(command_buf.buf, "tag ")) parse_new_tag(); - else if (!prefixcmp(command_buf.buf, "reset ")) + else if (starts_with(command_buf.buf, "reset ")) parse_reset_branch(); else if (!strcmp("checkpoint", command_buf.buf)) parse_checkpoint(); else if (!strcmp("done", command_buf.buf)) break; - else if (!prefixcmp(command_buf.buf, "progress ")) + else if (starts_with(command_buf.buf, "progress ")) parse_progress(); - else if (!prefixcmp(command_buf.buf, "feature ")) + else if (starts_with(command_buf.buf, "feature ")) parse_feature(); - else if (!prefixcmp(command_buf.buf, "option git ")) + else if (starts_with(command_buf.buf, "option git ")) parse_option(); - else if (!prefixcmp(command_buf.buf, "option ")) + else if (starts_with(command_buf.buf, "option ")) /* ignore non-git options*/; else die("Unsupported command: %s", command_buf.buf); diff --git a/fetch-pack.c b/fetch-pack.c index 1042448fa0..760ed16e79 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -47,9 +47,8 @@ static void rev_list_push(struct commit *commit, int mark) if (!(commit->object.flags & mark)) { commit->object.flags |= mark; - if (!(commit->object.parsed)) - if (parse_commit(commit)) - return; + if (parse_commit(commit)) + return; prio_queue_put(&rev_list, commit); @@ -128,8 +127,7 @@ static const unsigned char *get_rev(void) return NULL; commit = prio_queue_get(&rev_list); - if (!commit->object.parsed) - parse_commit(commit); + parse_commit(commit); parents = commit->parents; commit->object.flags |= POPPED; @@ -176,9 +174,9 @@ static void consume_shallow_list(struct fetch_pack_args *args, int fd) */ char *line; while ((line = packet_read_line(fd, NULL))) { - if (!prefixcmp(line, "shallow ")) + if (starts_with(line, "shallow ")) continue; - if (!prefixcmp(line, "unshallow ")) + if (starts_with(line, "unshallow ")) continue; die("git fetch-pack: expected shallow list"); } @@ -194,7 +192,7 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1) die("git fetch-pack: expected ACK/NAK, got EOF"); if (!strcmp(line, "NAK")) return NAK; - if (!prefixcmp(line, "ACK ")) { + if (starts_with(line, "ACK ")) { if (!get_sha1_hex(line+4, result_sha1)) { if (len < 45) return ACK; @@ -323,13 +321,13 @@ static int find_common(struct fetch_pack_args *args, send_request(args, fd[1], &req_buf); while ((line = packet_read_line(fd[0], NULL))) { - if (!prefixcmp(line, "shallow ")) { + if (starts_with(line, "shallow ")) { if (get_sha1_hex(line + 8, sha1)) die("invalid shallow line: %s", line); register_shallow(sha1); continue; } - if (!prefixcmp(line, "unshallow ")) { + if (starts_with(line, "unshallow ")) { if (get_sha1_hex(line + 10, sha1)) die("invalid unshallow line: %s", line); if (!lookup_object(sha1)) @@ -523,7 +521,7 @@ static void filter_refs(struct fetch_pack_args *args, } if (!keep && args->fetch_all && - (!args->depth || prefixcmp(ref->name, "refs/tags/"))) + (!args->depth || !starts_with(ref->name, "refs/tags/"))) keep = 1; if (keep) { diff --git a/fetch-pack.h b/fetch-pack.h index 461cbf39b2..20ccc12e57 100644 --- a/fetch-pack.h +++ b/fetch-pack.h @@ -14,6 +14,7 @@ struct fetch_pack_args { use_thin_pack:1, fetch_all:1, stdin_refs:1, + diag_url:1, verbose:1, no_progress:1, include_tag:1, @@ -29,6 +29,17 @@ int use_gettext_poison(void) #endif #ifndef NO_GETTEXT +static int test_vsnprintf(const char *fmt, ...) +{ + char buf[26]; + int ret; + va_list ap; + va_start(ap, fmt); + ret = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + return ret; +} + static const char *charset; static void init_gettext_charset(const char *domain) { @@ -99,9 +110,7 @@ static void init_gettext_charset(const char *domain) $ LANGUAGE= LANG=de_DE.utf8 ./test test: Kein passendes Ger?t gefunden - In the long term we should probably see about getting that - vsnprintf bug in glibc fixed, and audit our code so it won't - fall apart under a non-C locale. + The vsnprintf bug has been fixed since glibc 2.17. Then we could simply set LC_CTYPE from the environment, which would make things like the external perror(3) messages work. @@ -115,7 +124,9 @@ static void init_gettext_charset(const char *domain) setlocale(LC_CTYPE, ""); charset = locale_charset(); bind_textdomain_codeset(domain, charset); - setlocale(LC_CTYPE, "C"); + /* the string is taken from v0.99.6~1 */ + if (test_vsnprintf("%.*s", 13, "David_K\345gedal") < 0) + setlocale(LC_CTYPE, "C"); } void git_setup_gettext(void) @@ -302,7 +302,7 @@ split_patches () { # not starting with Author, From or Date is the # subject, and the body starts with the next nonempty # line not starting with Author, From or Date - perl -ne 'BEGIN { $subject = 0 } + @@PERL@@ -ne 'BEGIN { $subject = 0 } if ($subject > 1) { print ; } elsif (/^\s+$/) { next ; } elsif (/^Author:/) { s/Author/From/ ; print ;} @@ -334,7 +334,7 @@ split_patches () { # Since we cannot guarantee that the commit message is in # git-friendly format, we put no Subject: line and just consume # all of the message as the body - LANG=C LC_ALL=C perl -M'POSIX qw(strftime)' -ne 'BEGIN { $subject = 0 } + LANG=C LC_ALL=C @@PERL@@ -M'POSIX qw(strftime)' -ne 'BEGIN { $subject = 0 } if ($subject) { print ; } elsif (/^\# User /) { s/\# User/From:/ ; print ; } elsif (/^\# Date /) { diff --git a/git-compat-util.h b/git-compat-util.h index 7776f126d3..cbd86c37f5 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -75,7 +75,7 @@ # endif #elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \ !defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__) && \ - !defined(__TANDEM) && !defined(__QNX__) + !defined(__TANDEM) && !defined(__QNX__) && !defined(__MirBSD__) #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */ #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */ #endif @@ -350,7 +350,9 @@ extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_lis extern void set_error_routine(void (*routine)(const char *err, va_list params)); extern void set_die_is_recursing_routine(int (*routine)(void)); +extern int starts_with(const char *str, const char *prefix); extern int prefixcmp(const char *str, const char *prefix); +extern int ends_with(const char *str, const char *suffix); extern int suffixcmp(const char *str, const char *suffix); static inline const char *skip_prefix(const char *str, const char *prefix) diff --git a/git-difftool--helper.sh b/git-difftool--helper.sh index b00ed95dba..7ef36b9482 100755 --- a/git-difftool--helper.sh +++ b/git-difftool--helper.sh @@ -40,7 +40,8 @@ launch_merge_tool () { # the user with the real $MERGED name before launching $merge_tool. if should_prompt then - printf "\nViewing: '%s'\n" "$MERGED" + printf "\nViewing (%s/%s): '%s'\n" "$GIT_DIFF_PATH_COUNTER" \ + "$GIT_DIFF_PATH_TOTAL" "$MERGED" if use_ext_cmd then printf "Launch '%s' [Y/n]: " \ diff --git a/git-gui/Makefile b/git-gui/Makefile index e9c2bc347d..cde8b2ea31 100644 --- a/git-gui/Makefile +++ b/git-gui/Makefile @@ -4,7 +4,7 @@ all:: # # Define NO_MSGFMT if you do not have msgfmt from the GNU gettext # package and want to use our rough pure Tcl po->msg translator. -# TCL_PATH must be vaild for this to work. +# TCL_PATH must be valid for this to work. # GIT-VERSION-FILE: FORCE diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh index 153f85da06..db46ac50bf 100755 --- a/git-gui/git-gui.sh +++ b/git-gui/git-gui.sh @@ -894,6 +894,7 @@ set default_config(gui.textconv) true set default_config(gui.pruneduringfetch) false set default_config(gui.trustmtime) false set default_config(gui.fastcopyblame) false +set default_config(gui.maxrecentrepo) 10 set default_config(gui.copyblamethreshold) 40 set default_config(gui.blamehistoryctx) 7 set default_config(gui.diffcontext) 5 @@ -912,6 +913,7 @@ set font_descs { {fontdiff font_diff {mc "Diff/Console Font"}} } set default_config(gui.stageuntracked) ask +set default_config(gui.displayuntracked) true ###################################################################### ## @@ -1282,7 +1284,11 @@ apply_config # v1.7.0 introduced --show-toplevel to return the canonical work-tree if {[package vsatisfies $_git_version 1.7.0]} { - set _gitworktree [git rev-parse --show-toplevel] + if { [is_Cygwin] } { + catch {set _gitworktree [exec cygpath --windows [git rev-parse --show-toplevel]]} + } else { + set _gitworktree [git rev-parse --show-toplevel] + } } else { # try to set work tree from environment, core.worktree or use # cdup to obtain a relative path to the top of the worktree. If @@ -1550,18 +1556,23 @@ proc rescan_stage2 {fd after} { set buf_rdf {} set buf_rlo {} - set rescan_active 3 + set rescan_active 2 ui_status [mc "Scanning for modified files ..."] set fd_di [git_read diff-index --cached -z [PARENT]] set fd_df [git_read diff-files -z] - set fd_lo [eval git_read ls-files --others -z $ls_others] fconfigure $fd_di -blocking 0 -translation binary -encoding binary fconfigure $fd_df -blocking 0 -translation binary -encoding binary - fconfigure $fd_lo -blocking 0 -translation binary -encoding binary + fileevent $fd_di readable [list read_diff_index $fd_di $after] fileevent $fd_df readable [list read_diff_files $fd_df $after] - fileevent $fd_lo readable [list read_ls_others $fd_lo $after] + + if {[is_config_true gui.displayuntracked]} { + set fd_lo [eval git_read ls-files --others -z $ls_others] + fconfigure $fd_lo -blocking 0 -translation binary -encoding binary + fileevent $fd_lo readable [list read_ls_others $fd_lo $after] + incr rescan_active + } } proc load_message {file {encoding {}}} { @@ -2654,6 +2665,16 @@ if {![is_bare]} { .mbar.repository add command \ -label [mc "Explore Working Copy"] \ -command {do_explore} +} + +if {[is_Windows]} { + .mbar.repository add command \ + -label [mc "Git Bash"] \ + -command {eval exec [auto_execok start] \ + [list "Git Bash" bash --login -l &]} +} + +if {[is_Windows] || ![is_bare]} { .mbar.repository add separator } @@ -3203,13 +3224,19 @@ unset i # -- Diff and Commit Area # -${NS}::frame .vpane.lower -height 300 -width 400 +${NS}::panedwindow .vpane.lower -orient vertical ${NS}::frame .vpane.lower.commarea -${NS}::frame .vpane.lower.diff -relief sunken -borderwidth 1 -pack .vpane.lower.diff -fill both -expand 1 -pack .vpane.lower.commarea -side bottom -fill x +${NS}::frame .vpane.lower.diff -relief sunken -borderwidth 1 -height 500 +.vpane.lower add .vpane.lower.diff +.vpane.lower add .vpane.lower.commarea .vpane add .vpane.lower -if {!$use_ttk} {.vpane paneconfigure .vpane.lower -sticky nsew} +if {$use_ttk} { + .vpane.lower pane .vpane.lower.diff -weight 1 + .vpane.lower pane .vpane.lower.commarea -weight 0 +} else { + .vpane.lower paneconfigure .vpane.lower.diff -stretch always + .vpane.lower paneconfigure .vpane.lower.commarea -stretch never +} # -- Commit Area Buttons # diff --git a/git-gui/lib/blame.tcl b/git-gui/lib/blame.tcl index 324f7744c4..b1d15f4621 100644 --- a/git-gui/lib/blame.tcl +++ b/git-gui/lib/blame.tcl @@ -5,7 +5,7 @@ class blame { image create photo ::blame::img_back_arrow -data {R0lGODlhGAAYAIUAAPwCBEzKXFTSZIz+nGzmhGzqfGTidIT+nEzGXHTqhGzmfGzifFzadETCVES+VARWDFzWbHzyjAReDGTadFTOZDSyRDyyTCymPARaFGTedFzSbDy2TCyqRCyqPARaDAyCHES6VDy6VCyiPAR6HCSeNByWLARyFARiDARqFGTifARiFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAYABgAAAajQIBwSCwaj8ikcsk0BppJwRPqHEypQwHBis0WDAdEFyBIKBaMAKLBdjQeSkFBYTBAIvgEoS6JmhUTEwIUDQ4VFhcMGEhyCgoZExoUaxsWHB0THkgfAXUGAhoBDSAVFR0XBnCbDRmgog0hpSIiDJpJIyEQhBUcJCIlwA22SSYVogknEg8eD82qSigdDSknY0IqJQXPYxIl1dZCGNvWw+Dm510GQQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7} -# Persistant data (survives loads) +# Persistent data (survives loads) # field history {}; # viewer history: {commit path} field header ; # array commit,key -> header field diff --git a/git-gui/lib/choose_repository.tcl b/git-gui/lib/choose_repository.tcl index ee58981f53..3c10bc656d 100644 --- a/git-gui/lib/choose_repository.tcl +++ b/git-gui/lib/choose_repository.tcl @@ -24,6 +24,10 @@ field sorted_recent ; # recent repositories (sorted) constructor pick {} { global M1T M1B use_ttk NS + if {[set maxrecent [get_config gui.maxrecentrepo]] eq {}} { + set maxrecent 10 + } + make_dialog top w wm title $top [mc "Git Gui"] @@ -148,7 +152,7 @@ constructor pick {} { -background [get_bg_color $w_body.recentlabel] \ -wrap none \ -width 50 \ - -height 10 + -height $maxrecent $w_recentlist tag conf link \ -foreground blue \ -underline 1 @@ -264,7 +268,11 @@ proc _append_recentrepos {path} { git config --global --add gui.recentrepo $path load_config 1 - while {[llength $recent] > 10} { + if {[set maxrecent [get_config gui.maxrecentrepo]] eq {}} { + set maxrecent 10 + } + + while {[llength $recent] > $maxrecent} { _unset_recentrepo [lindex $recent 0] set recent [lrange $recent 1 end] } diff --git a/git-gui/lib/index.tcl b/git-gui/lib/index.tcl index 8efbbdde21..74a81a7b42 100644 --- a/git-gui/lib/index.tcl +++ b/git-gui/lib/index.tcl @@ -414,7 +414,7 @@ proc revert_helper {txt paths} { # such distinction is needed in some languages. Previously, the # code used "Revert changes in" for both, but that can't work # in languages where 'in' must be combined with word from - # rest of string (in diffrent way for both cases of course). + # rest of string (in different way for both cases of course). # # FIXME: Unfortunately, even that isn't enough in some languages # as they have quite complex plural-form rules. Unfortunately, diff --git a/git-gui/lib/option.tcl b/git-gui/lib/option.tcl index 0cf1da1d7e..23c9ae72a4 100644 --- a/git-gui/lib/option.tcl +++ b/git-gui/lib/option.tcl @@ -150,6 +150,7 @@ proc do_options {} { {b gui.matchtrackingbranch {mc "Match Tracking Branches"}} {b gui.textconv {mc "Use Textconv For Diffs and Blames"}} {b gui.fastcopyblame {mc "Blame Copy Only On Changed Files"}} + {i-0..100 gui.maxrecentrepo {mc "Maximum Length of Recent Repositories List"}} {i-20..200 gui.copyblamethreshold {mc "Minimum Letters To Blame Copy On"}} {i-0..300 gui.blamehistoryctx {mc "Blame History Context Radius (days)"}} {i-1..99 gui.diffcontext {mc "Number of Diff Context Lines"}} @@ -159,6 +160,7 @@ proc do_options {} { {c gui.encoding {mc "Default File Contents Encoding"}} {b gui.warndetachedcommit {mc "Warn before committing to a detached head"}} {s gui.stageuntracked {mc "Staging of untracked files"} {list "yes" "no" "ask"}} + {b gui.displayuntracked {mc "Show untracked files"}} } { set type [lindex $option 0] set name [lindex $option 1] diff --git a/git-gui/lib/spellcheck.tcl b/git-gui/lib/spellcheck.tcl index e6120303e9..538d61c792 100644 --- a/git-gui/lib/spellcheck.tcl +++ b/git-gui/lib/spellcheck.tcl @@ -14,7 +14,7 @@ field w_menu ; # context menu for the widget field s_menuidx 0 ; # last index of insertion into $w_menu field s_i {} ; # timer registration for _run callbacks -field s_clear 0 ; # did we erase mispelled tags yet? +field s_clear 0 ; # did we erase misspelled tags yet? field s_seen [list] ; # lines last seen from $w_text in _run field s_checked [list] ; # lines already checked field s_pending [list] ; # [$line $data] sent to ispell/aspell @@ -259,7 +259,7 @@ method _run {} { if {$n == $cur_line && ![regexp {^\W$} [$w_text get $cur_pos insert]]} { - # If the current word is mispelled remove the tag + # If the current word is misspelled remove the tag # but force a spellcheck later. # set tags [$w_text tag names $cur_pos] diff --git a/git-gui/macosx/Info.plist b/git-gui/macosx/Info.plist index b3bf15fa1c..1ade121c4c 100644 --- a/git-gui/macosx/Info.plist +++ b/git-gui/macosx/Info.plist @@ -24,5 +24,7 @@ <string>GITg</string> <key>CFBundleVersion</key> <string>@@GITGUI_VERSION@@</string> + <key>NSHighResolutionCapable</key> + <true/> </dict> </plist> diff --git a/git-instaweb.sh b/git-instaweb.sh index 01a1b05e6b..e93a238675 100755 --- a/git-instaweb.sh +++ b/git-instaweb.sh @@ -581,7 +581,7 @@ EOF gitweb_conf() { cat > "$fqgitdir/gitweb/gitweb_config.perl" <<EOF -#!/usr/bin/perl +#!@@PERL@@ our \$projectroot = "$(dirname "$fqgitdir")"; our \$git_temp = "$fqgitdir/gitweb/tmp"; our \$projects_list = \$projectroot; diff --git a/git-lost-found.sh b/git-lost-found.sh deleted file mode 100755 index 0b3e8c7a86..0000000000 --- a/git-lost-found.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh - -USAGE='' -SUBDIRECTORY_OK='Yes' -OPTIONS_SPEC= -. git-sh-setup - -echo "WARNING: '$0' is deprecated in favor of 'git fsck --lost-found'" >&2 - -if [ "$#" != "0" ] -then - usage -fi - -laf="$GIT_DIR/lost-found" -rm -fr "$laf" && mkdir -p "$laf/commit" "$laf/other" || exit - -git fsck --full --no-reflogs | -while read dangling type sha1 -do - case "$dangling" in - dangling) - if git rev-parse -q --verify "$sha1^0" >/dev/null - then - dir="$laf/commit" - git show-branch "$sha1" - else - dir="$laf/other" - fi - echo "$sha1" >"$dir/$sha1" - ;; - esac -done diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh index a280f49817..c45a020301 100644 --- a/git-mergetool--lib.sh +++ b/git-mergetool--lib.sh @@ -1,5 +1,4 @@ -#!/bin/sh -# git-mergetool--lib is a library for common merge tool functions +# git-mergetool--lib is a shell library for common merge tool functions : ${MERGE_TOOLS_DIR=$(git --exec-path)/mergetools} @@ -1311,7 +1311,7 @@ class P4Submit(Command, P4UserMap): else: die("unknown modifier %s for %s" % (modifier, path)) - diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id) + diffcmd = "git diff-tree -p \"%s\"" % (id) patchcmd = diffcmd + " | git apply " tryPatchCmd = patchcmd + "--check -" applyPatchCmd = patchcmd + "--check --apply -" diff --git a/git-parse-remote.sh b/git-parse-remote.sh index 0e87e0915e..55fe8d56c9 100644 --- a/git-parse-remote.sh +++ b/git-parse-remote.sh @@ -1,4 +1,6 @@ -#!/bin/sh +# This is a shell library to calculate the remote repository and +# upstream branch that should be pulled by "git pull" from the current +# branch. # git-ls-remote could be called from outside a git managed repository; # this would fail in that case and would issue an error message. diff --git a/git-pull.sh b/git-pull.sh index b946fd975b..f210d0a147 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -172,7 +172,7 @@ error_on_no_merge_candidates () { do case "$opt" in -t|--t|--ta|--tag|--tags) - echo "Fetching tags only, you probably meant:" + echo "It doesn't make sense to pull all tags; you probably meant:" echo " git fetch --tags" exit 1 esac @@ -229,15 +229,7 @@ test true = "$rebase" && { test -n "$curr_branch" && . git-parse-remote && remoteref="$(get_remote_merge_branch "$@" 2>/dev/null)" && - oldremoteref="$(git rev-parse -q --verify "$remoteref")" && - for reflog in $(git rev-list -g $remoteref 2>/dev/null) - do - if test "$reflog" = "$(git merge-base $reflog $curr_branch)" - then - oldremoteref="$reflog" - break - fi - done + oldremoteref=$(git merge-base --fork-point "$remoteref" $curr_branch) } orig_head=$(git rev-parse -q --verify HEAD) git fetch $verbosity $progress $dry_run $recurse_submodules --update-head-ok "$@" || exit 1 diff --git a/git-rebase--am.sh b/git-rebase--am.sh index 34e3102fcb..a4f683a5d7 100644 --- a/git-rebase--am.sh +++ b/git-rebase--am.sh @@ -1,4 +1,5 @@ -#!/bin/sh +# This shell script fragment is sourced by git-rebase to implement +# its default, fast, patch-based, non-interactive mode. # # Copyright (c) 2010 Junio C Hamano. # diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 3c6bed9a28..43c19e0829 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -1,11 +1,8 @@ -#!/bin/sh +# This shell script fragment is sourced by git-rebase to implement +# its interactive mode. "git rebase --interactive" makes it easy +# to fix up commits in the middle of a series and rearrange commits. # # Copyright (c) 2006 Johannes E. Schindelin - -# SHORT DESCRIPTION -# -# This script makes it easy to fix up commits in the middle of a series, -# and rearrange commits. # # The original idea comes from Eric W. Biederman, in # http://article.gmane.org/gmane.comp.version-control.git/22407 diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh index 16d18176ec..e7d96de9ad 100644 --- a/git-rebase--merge.sh +++ b/git-rebase--merge.sh @@ -1,4 +1,6 @@ -#!/bin/sh +# This shell script fragment is sourced by git-rebase to implement +# its merge-based non-interactive mode that copes well with renamed +# files. # # Copyright (c) 2010 Junio C Hamano. # diff --git a/git-rebase.sh b/git-rebase.sh index 226752fbff..7185dc8438 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -14,6 +14,7 @@ git-rebase --continue | --abort | --skip | --edit-todo v,verbose! display a diffstat of what changed upstream q,quiet! be quiet. implies --no-stat autostash! automatically stash/stash pop before and after +fork-point use 'merge-base --fork-point' to refine upstream onto=! rebase onto given branch instead of upstream p,preserve-merges! try to recreate merges instead of ignoring them s,strategy=! use the given merge strategy @@ -66,6 +67,7 @@ verbose= diffstat= test "$(git config --bool rebase.stat)" = true && diffstat=t autostash="$(git config --bool rebase.autostash || echo false)" +fork_point=auto git_am_opt= rebase_root= force_rebase= @@ -260,6 +262,12 @@ do --no-autosquash) autosquash= ;; + --fork-point) + fork_point=t + ;; + --no-fork-point) + fork_point= + ;; -M|-m) do_merge=t ;; @@ -437,6 +445,8 @@ then error_on_missing_default_upstream "rebase" "rebase" \ "against" "git rebase <branch>" fi + + test "$fork_point" = auto && fork_point=t ;; *) upstream_name="$1" shift @@ -522,6 +532,15 @@ case "$#" in ;; esac +if test "$fork_point" = t +then + new_upstream=$(git merge-base --fork-point "$upstream_name" "$switch_to") + if test -n "$new_upstream" + then + upstream=$new_upstream + fi +fi + if test "$autostash" = true && ! (require_clean_work_tree) 2>/dev/null then stash_sha1=$(git stash create "autostash") || diff --git a/git-request-pull.sh b/git-request-pull.sh index ebf1269d29..fe21d5db63 100755 --- a/git-request-pull.sh +++ b/git-request-pull.sh @@ -106,7 +106,7 @@ find_matching_ref=' } ' -ref=$(git ls-remote "$url" | perl -e "$find_matching_ref" "$head" "$headrev" "$tag_name") +ref=$(git ls-remote "$url" | @@PERL@@ -e "$find_matching_ref" "$head" "$headrev" "$tag_name") url=$(git ls-remote --get-url "$url") diff --git a/git-send-email.perl b/git-send-email.perl index 3782c3b0cb..2016d9c619 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -291,7 +291,7 @@ my $rc = GetOptions("h" => \$help, "smtp-pass:s" => \$smtp_authpass, "smtp-ssl" => sub { $smtp_encryption = 'ssl' }, "smtp-encryption=s" => \$smtp_encryption, - "smtp-ssl-cert-path" => \$smtp_ssl_cert_path, + "smtp-ssl-cert-path=s" => \$smtp_ssl_cert_path, "smtp-debug:i" => \$debug_net_smtp, "smtp-domain:s" => \$smtp_domain, "identity=s" => \$identity, @@ -1214,10 +1214,14 @@ X-Mailer: git-send-email $gitversion $smtp_server_port ||= 465; # ssmtp require Net::SMTP::SSL; $smtp_domain ||= maildomain(); + require IO::Socket::SSL; + # Net::SMTP::SSL->new() does not forward any SSL options + IO::Socket::SSL::set_client_defaults( + ssl_verify_params()); $smtp ||= Net::SMTP::SSL->new($smtp_server, Hello => $smtp_domain, Port => $smtp_server_port, - ssl_verify_params()); + Debug => $debug_net_smtp); } else { require Net::SMTP; diff --git a/git-sh-i18n.sh b/git-sh-i18n.sh index 6a27f68136..e6c3116e18 100644 --- a/git-sh-i18n.sh +++ b/git-sh-i18n.sh @@ -1,9 +1,8 @@ -#!/bin/sh +# This shell library is Git's interface to gettext.sh. See po/README +# for usage instructions. # # Copyright (c) 2010 Ævar Arnfjörð Bjarmason # -# This is Git's interface to gettext.sh. See po/README for usage -# instructions. # Export the TEXTDOMAIN* data that we need for Git TEXTDOMAIN=git diff --git a/git-sh-setup.sh b/git-sh-setup.sh index ebfe8f7a4d..190a5394b9 100644 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -1,9 +1,6 @@ -#!/bin/sh -# -# This is included in commands that either have to be run from the toplevel -# of the repository, or with GIT_DIR environment variable properly. -# If the GIT_DIR does not look like the right correct git-repository, -# it dies. +# This shell scriplet is meant to be included by other shell scripts +# to set up some variables pointing at the normal git directories and +# a few helper shell functions. # Having this variable in your environment would break scripts because # you would cause "cd" to be taken to unexpected places. If you diff --git a/git-submodule.sh b/git-submodule.sh index 66f5f752c5..2677f2e8b3 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -156,7 +156,7 @@ module_list() git ls-files -z --error-unmatch --stage -- "$@" || echo "unmatched pathspec exists" ) | - perl -e ' + @@PERL@@ -e ' my %unmerged = (); my ($null_sha1) = ("0" x 40); my @out = (); @@ -545,7 +545,12 @@ cmd_foreach() sm_path=$(relative_path "$sm_path") && # we make $path available to scripts ... path=$sm_path && - eval "$@" && + if test $# -eq 1 + then + eval "$1" + else + "$@" + fi && if test -n "$recursive" then cmd_foreach "--recursive" "$@" @@ -716,7 +721,6 @@ cmd_deinit() cmd_update() { # parse $args after "submodule ... update". - orig_flags= while test $# -ne 0 do case "$1" in @@ -741,7 +745,6 @@ cmd_update() --reference) case "$2" in '') usage ;; esac reference="--reference=$2" - orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")" shift ;; --reference=*) @@ -775,7 +778,6 @@ cmd_update() break ;; esac - orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")" shift done @@ -919,7 +921,7 @@ Maybe you want to use 'update --init'?")" prefix="$prefix$sm_path/" clear_local_git_env cd "$sm_path" && - eval cmd_update "$orig_flags" + eval cmd_update ) res=$? if test $res -gt 0 @@ -54,7 +54,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) /* * Check remaining flags. */ - if (!prefixcmp(cmd, "--exec-path")) { + if (starts_with(cmd, "--exec-path")) { cmd += 11; if (*cmd == '=') git_set_argv_exec_path(cmd + 1); @@ -92,7 +92,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) *envchanged = 1; (*argv)++; (*argc)--; - } else if (!prefixcmp(cmd, "--git-dir=")) { + } else if (starts_with(cmd, "--git-dir=")) { setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1); if (envchanged) *envchanged = 1; @@ -106,7 +106,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) *envchanged = 1; (*argv)++; (*argc)--; - } else if (!prefixcmp(cmd, "--namespace=")) { + } else if (starts_with(cmd, "--namespace=")) { setenv(GIT_NAMESPACE_ENVIRONMENT, cmd + 12, 1); if (envchanged) *envchanged = 1; @@ -120,7 +120,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) *envchanged = 1; (*argv)++; (*argc)--; - } else if (!prefixcmp(cmd, "--work-tree=")) { + } else if (starts_with(cmd, "--work-tree=")) { setenv(GIT_WORK_TREE_ENVIRONMENT, cmd + 12, 1); if (envchanged) *envchanged = 1; @@ -408,7 +408,6 @@ static void handle_internal_command(int argc, const char **argv) { "pack-redundant", cmd_pack_redundant, RUN_SETUP }, { "pack-refs", cmd_pack_refs, RUN_SETUP }, { "patch-id", cmd_patch_id }, - { "peek-remote", cmd_ls_remote, RUN_SETUP_GENTLY }, { "pickaxe", cmd_blame, RUN_SETUP }, { "prune", cmd_prune, RUN_SETUP }, { "prune-packed", cmd_prune_packed, RUN_SETUP }, @@ -421,7 +420,6 @@ static void handle_internal_command(int argc, const char **argv) { "remote-fd", cmd_remote_fd }, { "repack", cmd_repack, RUN_SETUP }, { "replace", cmd_replace, RUN_SETUP }, - { "repo-config", cmd_repo_config, RUN_SETUP_GENTLY }, { "rerere", cmd_rerere, RUN_SETUP }, { "reset", cmd_reset, RUN_SETUP }, { "rev-list", cmd_rev_list, RUN_SETUP }, @@ -438,7 +436,6 @@ static void handle_internal_command(int argc, const char **argv) { "stripspace", cmd_stripspace }, { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP }, { "tag", cmd_tag, RUN_SETUP }, - { "tar-tree", cmd_tar_tree }, { "unpack-file", cmd_unpack_file, RUN_SETUP }, { "unpack-objects", cmd_unpack_objects, RUN_SETUP }, { "update-index", cmd_update_index, RUN_SETUP }, @@ -569,7 +566,7 @@ int main(int argc, char **av) * So we just directly call the internal command handler, and * die if that one cannot handle it. */ - if (!prefixcmp(cmd, "git-")) { + if (starts_with(cmd, "git-")) { cmd += 4; argv[0] = cmd; handle_internal_command(argc, argv); @@ -581,7 +578,7 @@ int main(int argc, char **av) argc--; handle_options(&argv, &argc, NULL); if (argc > 0) { - if (!prefixcmp(argv[0], "--")) + if (starts_with(argv[0], "--")) argv[0] += 2; } else { /* The user didn't specify a command; give them help */ diff --git a/gitk-git/gitk b/gitk-git/gitk index 5cd00d80fe..33c3a6c6b3 100755 --- a/gitk-git/gitk +++ b/gitk-git/gitk @@ -156,10 +156,12 @@ proc unmerged_files {files} { proc parseviewargs {n arglist} { global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs env + global vinlinediff global worddiff git_version set vdatemode($n) 0 set vmergeonly($n) 0 + set vinlinediff($n) 0 set glflags {} set diffargs {} set nextisval 0 @@ -227,12 +229,20 @@ proc parseviewargs {n arglist} { "--until=*" - "--before=*" - "--max-age=*" - "--min-age=*" - "--author=*" - "--committer=*" - "--grep=*" - "-[iE]" - "--remove-empty" - "--first-parent" - "--cherry-pick" - - "-S*" - "--pickaxe-all" - "--pickaxe-regex" - + "-S*" - "-G*" - "--pickaxe-all" - "--pickaxe-regex" - "--simplify-by-decoration" { # These mean that we get a subset of the commits set filtered 1 lappend glflags $arg } + "-L*" { + # Line-log with 'stuck' argument (unstuck form is + # not supported) + set filtered 1 + set vinlinediff($n) 1 + set allknown 0 + lappend glflags $arg + } "-n" { # This appears to be the only one that has a value as a # separate word following it @@ -1704,8 +1714,17 @@ proc parsecommit {id contents listed} { set comment $newcomment } set hasnote [string first "\nNotes:\n" $contents] + set diff "" + # If there is diff output shown in the git-log stream, split it + # out. But get rid of the empty line that always precedes the + # diff. + set i [string first "\n\ndiff" $comment] + if {$i >= 0} { + set diff [string range $comment $i+1 end] + set comment [string range $comment 0 $i-1] + } set commitinfo($id) [list $headline $auname $audate \ - $comname $comdate $comment $hasnote] + $comname $comdate $comment $hasnote $diff] } proc getcommit {id} { @@ -2385,6 +2404,7 @@ proc makewindow {} { $ctext tag conf found -back $foundbgcolor $ctext tag conf currentsearchhit -back $currentsearchhitbgcolor $ctext tag conf wwrap -wrap word + $ctext tag conf bold -font textfontbold .pwbottom add .bleft if {!$use_ttk} { @@ -6387,6 +6407,25 @@ proc bindline {t id} { $canv bind $t <Button-1> "lineclick %x %y $id 1" } +proc graph_pane_width {} { + global use_ttk + + if {$use_ttk} { + set g [.tf.histframe.pwclist sashpos 0] + } else { + set g [.tf.histframe.pwclist sash coord 0] + } + return [lindex $g 0] +} + +proc totalwidth {l font extra} { + set tot 0 + foreach str $l { + set tot [expr {$tot + [font measure $font $str] + $extra}] + } + return $tot +} + proc drawtags {id x xt y1} { global idtags idheads idotherrefs mainhead global linespc lthickness @@ -6398,9 +6437,27 @@ proc drawtags {id x xt y1} { set marks {} set ntags 0 set nheads 0 + set singletag 0 + set maxtags 3 + set maxtagpct 25 + set maxwidth [expr {[graph_pane_width] * $maxtagpct / 100}] + set delta [expr {int(0.5 * ($linespc - $lthickness))}] + set extra [expr {$delta + $lthickness + $linespc}] + if {[info exists idtags($id)]} { set marks $idtags($id) set ntags [llength $marks] + if {$ntags > $maxtags || + [totalwidth $marks mainfont $extra] > $maxwidth} { + # show just a single "n tags..." tag + set singletag 1 + if {$ntags == 1} { + set marks [list "tag..."] + } else { + set marks [list [format "%d tags..." $ntags]] + } + set ntags 1 + } } if {[info exists idheads($id)]} { set marks [concat $marks $idheads($id)] @@ -6413,7 +6470,6 @@ proc drawtags {id x xt y1} { return $xt } - set delta [expr {int(0.5 * ($linespc - $lthickness))}] set yt [expr {$y1 - 0.5 * $linespc}] set yb [expr {$yt + $linespc - 1}] set xvals {} @@ -6428,7 +6484,7 @@ proc drawtags {id x xt y1} { } lappend xvals $xt lappend wvals $wid - set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}] + set xt [expr {$xt + $wid + $extra}] } set t [$canv create line $x $y1 [lindex $xvals end] $y1 \ -width $lthickness -fill $reflinecolor -tags tag.$id] @@ -6444,7 +6500,12 @@ proc drawtags {id x xt y1} { $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \ -width 1 -outline $tagoutlinecolor -fill $tagbgcolor \ -tags tag.$id] - $canv bind $t <1> [list showtag $tag_quoted 1] + if {$singletag} { + set tagclick [list showtags $id 1] + } else { + set tagclick [list showtag $tag_quoted 1] + } + $canv bind $t <1> $tagclick set rowtextx([rowofcommit $id]) [expr {$xr + $linespc}] } else { # draw a head or other ref @@ -6471,7 +6532,7 @@ proc drawtags {id x xt y1} { set t [$canv create text $xl $y1 -anchor w -text $tag -fill $headfgcolor \ -font $font -tags [list tag.$id text]] if {$ntags >= 0} { - $canv bind $t <1> [list showtag $tag_quoted 1] + $canv bind $t <1> $tagclick } elseif {$nheads >= 0} { $canv bind $t $ctxbut [list headmenu %X %Y $id $tag_quoted] } @@ -7080,6 +7141,7 @@ proc selectline {l isnew {desired_loc {}}} { global cmitmode showneartags allcommits global targetrow targetid lastscrollrows global autoselect autosellen jump_to_here + global vinlinediff catch {unset pending_select} $canv delete hover @@ -7221,6 +7283,8 @@ proc selectline {l isnew {desired_loc {}}} { init_flist [mc "Comments"] if {$cmitmode eq "tree"} { gettree $id + } elseif {$vinlinediff($curview) == 1} { + showinlinediff $id } elseif {[llength $olds] <= 1} { startdiff $id } else { @@ -7557,6 +7621,39 @@ proc startdiff {ids} { } } +proc showinlinediff {ids} { + global commitinfo commitdata ctext + global treediffs + + set info $commitinfo($ids) + set diff [lindex $info 7] + set difflines [split $diff "\n"] + + initblobdiffvars + set treediff {} + + set inhdr 0 + foreach line $difflines { + if {![string compare -length 5 "diff " $line]} { + set inhdr 1 + } elseif {$inhdr && ![string compare -length 4 "+++ " $line]} { + # offset also accounts for the b/ prefix + lappend treediff [string range $line 6 end] + set inhdr 0 + } + } + + set treediffs($ids) $treediff + add_flist $treediff + + $ctext conf -state normal + foreach line $difflines { + parseblobdiffline $ids $line + } + maybe_scroll_ctext 1 + $ctext conf -state disabled +} + # If the filename (name) is under any of the passed filter paths # then return true to include the file in the listing. proc path_filter {filter name} { @@ -7710,15 +7807,25 @@ proc changeworddiff {name ix op} { reselectline } +proc initblobdiffvars {} { + global diffencoding targetline diffnparents + global diffinhdr currdiffsubmod diffseehere + set targetline {} + set diffnparents 0 + set diffinhdr 0 + set diffencoding [get_path_encoding {}] + set currdiffsubmod "" + set diffseehere -1 +} + proc getblobdiffs {ids} { global blobdifffd diffids env - global diffinhdr treediffs + global treediffs global diffcontext global ignorespace global worddiff global limitdiffs vfilelimit curview - global diffencoding targetline diffnparents - global git_version currdiffsubmod + global git_version set textconv {} if {[package vcompare $git_version "1.6.1"] >= 0} { @@ -7742,13 +7849,9 @@ proc getblobdiffs {ids} { error_popup [mc "Error getting diffs: %s" $err] return } - set targetline {} - set diffnparents 0 - set diffinhdr 0 - set diffencoding [get_path_encoding {}] fconfigure $bdf -blocking 0 -encoding binary -eofchar {} set blobdifffd($ids) $bdf - set currdiffsubmod "" + initblobdiffvars filerun $bdf [list getblobdiffline $bdf $diffids] } @@ -7814,13 +7917,17 @@ proc makediffhdr {fname ids} { set diffline 0 } +proc blobdiffmaybeseehere {ateof} { + global diffseehere + if {$diffseehere >= 0} { + mark_ctext_line [lindex [split $diffseehere .] 0] + } + maybe_scroll_ctext ateof +} + proc getblobdiffline {bdf ids} { - global diffids blobdifffd ctext curdiffstart - global diffnexthead diffnextnote difffilestart - global ctext_file_names ctext_file_lines - global diffinhdr treediffs mergemax diffnparents - global diffencoding jump_to_here targetline diffline currdiffsubmod - global worddiff + global diffids blobdifffd + global ctext set nr 0 $ctext conf -state normal @@ -7829,212 +7936,220 @@ proc getblobdiffline {bdf ids} { catch {close $bdf} return 0 } - if {![string compare -length 5 "diff " $line]} { - if {![regexp {^diff (--cc|--git) } $line m type]} { - set line [encoding convertfrom $line] - $ctext insert end "$line\n" hunksep - continue + parseblobdiffline $ids $line + } + $ctext conf -state disabled + blobdiffmaybeseehere [eof $bdf] + if {[eof $bdf]} { + catch {close $bdf} + return 0 + } + return [expr {$nr >= 1000? 2: 1}] +} + +proc parseblobdiffline {ids line} { + global ctext curdiffstart + global diffnexthead diffnextnote difffilestart + global ctext_file_names ctext_file_lines + global diffinhdr treediffs mergemax diffnparents + global diffencoding jump_to_here targetline diffline currdiffsubmod + global worddiff diffseehere + + if {![string compare -length 5 "diff " $line]} { + if {![regexp {^diff (--cc|--git) } $line m type]} { + set line [encoding convertfrom $line] + $ctext insert end "$line\n" hunksep + continue + } + # start of a new file + set diffinhdr 1 + $ctext insert end "\n" + set curdiffstart [$ctext index "end - 1c"] + lappend ctext_file_names "" + lappend ctext_file_lines [lindex [split $curdiffstart "."] 0] + $ctext insert end "\n" filesep + + if {$type eq "--cc"} { + # start of a new file in a merge diff + set fname [string range $line 10 end] + if {[lsearch -exact $treediffs($ids) $fname] < 0} { + lappend treediffs($ids) $fname + add_flist [list $fname] } - # start of a new file - set diffinhdr 1 - $ctext insert end "\n" - set curdiffstart [$ctext index "end - 1c"] - lappend ctext_file_names "" - lappend ctext_file_lines [lindex [split $curdiffstart "."] 0] - $ctext insert end "\n" filesep - - if {$type eq "--cc"} { - # start of a new file in a merge diff - set fname [string range $line 10 end] - if {[lsearch -exact $treediffs($ids) $fname] < 0} { - lappend treediffs($ids) $fname - add_flist [list $fname] - } + } else { + set line [string range $line 11 end] + # If the name hasn't changed the length will be odd, + # the middle char will be a space, and the two bits either + # side will be a/name and b/name, or "a/name" and "b/name". + # If the name has changed we'll get "rename from" and + # "rename to" or "copy from" and "copy to" lines following + # this, and we'll use them to get the filenames. + # This complexity is necessary because spaces in the + # filename(s) don't get escaped. + set l [string length $line] + set i [expr {$l / 2}] + if {!(($l & 1) && [string index $line $i] eq " " && + [string range $line 2 [expr {$i - 1}]] eq \ + [string range $line [expr {$i + 3}] end])} { + return + } + # unescape if quoted and chop off the a/ from the front + if {[string index $line 0] eq "\""} { + set fname [string range [lindex $line 0] 2 end] } else { - set line [string range $line 11 end] - # If the name hasn't changed the length will be odd, - # the middle char will be a space, and the two bits either - # side will be a/name and b/name, or "a/name" and "b/name". - # If the name has changed we'll get "rename from" and - # "rename to" or "copy from" and "copy to" lines following - # this, and we'll use them to get the filenames. - # This complexity is necessary because spaces in the - # filename(s) don't get escaped. - set l [string length $line] - set i [expr {$l / 2}] - if {!(($l & 1) && [string index $line $i] eq " " && - [string range $line 2 [expr {$i - 1}]] eq \ - [string range $line [expr {$i + 3}] end])} { - continue - } - # unescape if quoted and chop off the a/ from the front - if {[string index $line 0] eq "\""} { - set fname [string range [lindex $line 0] 2 end] - } else { - set fname [string range $line 2 [expr {$i - 1}]] - } + set fname [string range $line 2 [expr {$i - 1}]] } - makediffhdr $fname $ids + } + makediffhdr $fname $ids + + } elseif {![string compare -length 16 "* Unmerged path " $line]} { + set fname [encoding convertfrom [string range $line 16 end]] + $ctext insert end "\n" + set curdiffstart [$ctext index "end - 1c"] + lappend ctext_file_names $fname + lappend ctext_file_lines [lindex [split $curdiffstart "."] 0] + $ctext insert end "$line\n" filesep + set i [lsearch -exact $treediffs($ids) $fname] + if {$i >= 0} { + setinlist difffilestart $i $curdiffstart + } - } elseif {![string compare -length 16 "* Unmerged path " $line]} { - set fname [encoding convertfrom [string range $line 16 end]] - $ctext insert end "\n" - set curdiffstart [$ctext index "end - 1c"] - lappend ctext_file_names $fname - lappend ctext_file_lines [lindex [split $curdiffstart "."] 0] + } elseif {![string compare -length 2 "@@" $line]} { + regexp {^@@+} $line ats + set line [encoding convertfrom $diffencoding $line] + $ctext insert end "$line\n" hunksep + if {[regexp { \+(\d+),\d+ @@} $line m nl]} { + set diffline $nl + } + set diffnparents [expr {[string length $ats] - 1}] + set diffinhdr 0 + + } elseif {![string compare -length 10 "Submodule " $line]} { + # start of a new submodule + if {[regexp -indices "\[0-9a-f\]+\\.\\." $line nameend]} { + set fname [string range $line 10 [expr [lindex $nameend 0] - 2]] + } else { + set fname [string range $line 10 [expr [string first "contains " $line] - 2]] + } + if {$currdiffsubmod != $fname} { + $ctext insert end "\n"; # Add newline after commit message + } + set curdiffstart [$ctext index "end - 1c"] + lappend ctext_file_names "" + if {$currdiffsubmod != $fname} { + lappend ctext_file_lines $fname + makediffhdr $fname $ids + set currdiffsubmod $fname + $ctext insert end "\n$line\n" filesep + } else { $ctext insert end "$line\n" filesep + } + } elseif {![string compare -length 3 " >" $line]} { + set $currdiffsubmod "" + set line [encoding convertfrom $diffencoding $line] + $ctext insert end "$line\n" dresult + } elseif {![string compare -length 3 " <" $line]} { + set $currdiffsubmod "" + set line [encoding convertfrom $diffencoding $line] + $ctext insert end "$line\n" d0 + } elseif {$diffinhdr} { + if {![string compare -length 12 "rename from " $line]} { + set fname [string range $line [expr 6 + [string first " from " $line] ] end] + if {[string index $fname 0] eq "\""} { + set fname [lindex $fname 0] + } + set fname [encoding convertfrom $fname] set i [lsearch -exact $treediffs($ids) $fname] if {$i >= 0} { setinlist difffilestart $i $curdiffstart } - - } elseif {![string compare -length 2 "@@" $line]} { - regexp {^@@+} $line ats - set line [encoding convertfrom $diffencoding $line] - $ctext insert end "$line\n" hunksep - if {[regexp { \+(\d+),\d+ @@} $line m nl]} { - set diffline $nl + } elseif {![string compare -length 10 $line "rename to "] || + ![string compare -length 8 $line "copy to "]} { + set fname [string range $line [expr 4 + [string first " to " $line] ] end] + if {[string index $fname 0] eq "\""} { + set fname [lindex $fname 0] } - set diffnparents [expr {[string length $ats] - 1}] + makediffhdr $fname $ids + } elseif {[string compare -length 3 $line "---"] == 0} { + # do nothing + return + } elseif {[string compare -length 3 $line "+++"] == 0} { set diffinhdr 0 + return + } + $ctext insert end "$line\n" filesep - } elseif {![string compare -length 10 "Submodule " $line]} { - # start of a new submodule - if {[regexp -indices "\[0-9a-f\]+\\.\\." $line nameend]} { - set fname [string range $line 10 [expr [lindex $nameend 0] - 2]] - } else { - set fname [string range $line 10 [expr [string first "contains " $line] - 2]] - } - if {$currdiffsubmod != $fname} { - $ctext insert end "\n"; # Add newline after commit message - } - set curdiffstart [$ctext index "end - 1c"] - lappend ctext_file_names "" - if {$currdiffsubmod != $fname} { - lappend ctext_file_lines $fname - makediffhdr $fname $ids - set currdiffsubmod $fname - $ctext insert end "\n$line\n" filesep - } else { - $ctext insert end "$line\n" filesep - } - } elseif {![string compare -length 3 " >" $line]} { - set $currdiffsubmod "" - set line [encoding convertfrom $diffencoding $line] - $ctext insert end "$line\n" dresult - } elseif {![string compare -length 3 " <" $line]} { - set $currdiffsubmod "" - set line [encoding convertfrom $diffencoding $line] - $ctext insert end "$line\n" d0 - } elseif {$diffinhdr} { - if {![string compare -length 12 "rename from " $line]} { - set fname [string range $line [expr 6 + [string first " from " $line] ] end] - if {[string index $fname 0] eq "\""} { - set fname [lindex $fname 0] - } - set fname [encoding convertfrom $fname] - set i [lsearch -exact $treediffs($ids) $fname] - if {$i >= 0} { - setinlist difffilestart $i $curdiffstart + } else { + set line [string map {\x1A ^Z} \ + [encoding convertfrom $diffencoding $line]] + # parse the prefix - one ' ', '-' or '+' for each parent + set prefix [string range $line 0 [expr {$diffnparents - 1}]] + set tag [expr {$diffnparents > 1? "m": "d"}] + set dowords [expr {$worddiff ne [mc "Line diff"] && $diffnparents == 1}] + set words_pre_markup "" + set words_post_markup "" + if {[string trim $prefix " -+"] eq {}} { + # prefix only has " ", "-" and "+" in it: normal diff line + set num [string first "-" $prefix] + if {$dowords} { + set line [string range $line 1 end] + } + if {$num >= 0} { + # removed line, first parent with line is $num + if {$num >= $mergemax} { + set num "max" } - } elseif {![string compare -length 10 $line "rename to "] || - ![string compare -length 8 $line "copy to "]} { - set fname [string range $line [expr 4 + [string first " to " $line] ] end] - if {[string index $fname 0] eq "\""} { - set fname [lindex $fname 0] + if {$dowords && $worddiff eq [mc "Markup words"]} { + $ctext insert end "\[-$line-\]" $tag$num + } else { + $ctext insert end "$line" $tag$num } - makediffhdr $fname $ids - } elseif {[string compare -length 3 $line "---"] == 0} { - # do nothing - continue - } elseif {[string compare -length 3 $line "+++"] == 0} { - set diffinhdr 0 - continue - } - $ctext insert end "$line\n" filesep - - } else { - set line [string map {\x1A ^Z} \ - [encoding convertfrom $diffencoding $line]] - # parse the prefix - one ' ', '-' or '+' for each parent - set prefix [string range $line 0 [expr {$diffnparents - 1}]] - set tag [expr {$diffnparents > 1? "m": "d"}] - set dowords [expr {$worddiff ne [mc "Line diff"] && $diffnparents == 1}] - set words_pre_markup "" - set words_post_markup "" - if {[string trim $prefix " -+"] eq {}} { - # prefix only has " ", "-" and "+" in it: normal diff line - set num [string first "-" $prefix] - if {$dowords} { - set line [string range $line 1 end] + if {!$dowords} { + $ctext insert end "\n" $tag$num } - if {$num >= 0} { - # removed line, first parent with line is $num - if {$num >= $mergemax} { - set num "max" - } - if {$dowords && $worddiff eq [mc "Markup words"]} { - $ctext insert end "\[-$line-\]" $tag$num - } else { - $ctext insert end "$line" $tag$num - } - if {!$dowords} { - $ctext insert end "\n" $tag$num - } - } else { - set tags {} - if {[string first "+" $prefix] >= 0} { - # added line - lappend tags ${tag}result - if {$diffnparents > 1} { - set num [string first " " $prefix] - if {$num >= 0} { - if {$num >= $mergemax} { - set num "max" - } - lappend tags m$num + } else { + set tags {} + if {[string first "+" $prefix] >= 0} { + # added line + lappend tags ${tag}result + if {$diffnparents > 1} { + set num [string first " " $prefix] + if {$num >= 0} { + if {$num >= $mergemax} { + set num "max" } + lappend tags m$num } - set words_pre_markup "{+" - set words_post_markup "+}" } - if {$targetline ne {}} { - if {$diffline == $targetline} { - set seehere [$ctext index "end - 1 chars"] - set targetline {} - } else { - incr diffline - } - } - if {$dowords && $worddiff eq [mc "Markup words"]} { - $ctext insert end "$words_pre_markup$line$words_post_markup" $tags + set words_pre_markup "{+" + set words_post_markup "+}" + } + if {$targetline ne {}} { + if {$diffline == $targetline} { + set diffseehere [$ctext index "end - 1 chars"] + set targetline {} } else { - $ctext insert end "$line" $tags - } - if {!$dowords} { - $ctext insert end "\n" $tags + incr diffline } } - } elseif {$dowords && $prefix eq "~"} { - $ctext insert end "\n" {} - } else { - # "\ No newline at end of file", - # or something else we don't recognize - $ctext insert end "$line\n" hunksep + if {$dowords && $worddiff eq [mc "Markup words"]} { + $ctext insert end "$words_pre_markup$line$words_post_markup" $tags + } else { + $ctext insert end "$line" $tags + } + if {!$dowords} { + $ctext insert end "\n" $tags + } } + } elseif {$dowords && $prefix eq "~"} { + $ctext insert end "\n" {} + } else { + # "\ No newline at end of file", + # or something else we don't recognize + $ctext insert end "$line\n" hunksep } } - if {[info exists seehere]} { - mark_ctext_line [lindex [split $seehere .] 0] - } - maybe_scroll_ctext [eof $bdf] - $ctext conf -state disabled - if {[eof $bdf]} { - catch {close $bdf} - return 0 - } - return [expr {$nr >= 1000? 2: 1}] } proc changediffdisp {} { @@ -10878,6 +10993,23 @@ proc listrefs {id} { return [list $x $y $z] } +proc add_tag_ctext {tag} { + global ctext cached_tagcontent tagids + + if {![info exists cached_tagcontent($tag)]} { + catch { + set cached_tagcontent($tag) [exec git cat-file -p $tag] + } + } + $ctext insert end "[mc "Tag"]: $tag\n" bold + if {[info exists cached_tagcontent($tag)]} { + set text $cached_tagcontent($tag) + } else { + set text "[mc "Id"]: $tagids($tag)" + } + appendwithlinks $text {} +} + proc showtag {tag isnew} { global ctext cached_tagcontent tagids linknum tagobjid @@ -10888,17 +11020,28 @@ proc showtag {tag isnew} { clear_ctext settabs 0 set linknum 0 - if {![info exists cached_tagcontent($tag)]} { - catch { - set cached_tagcontent($tag) [exec git cat-file -p $tag] - } + add_tag_ctext $tag + maybe_scroll_ctext 1 + $ctext conf -state disabled + init_flist {} +} + +proc showtags {id isnew} { + global idtags ctext linknum + + if {$isnew} { + addtohistory [list showtags $id 0] savectextpos } - if {[info exists cached_tagcontent($tag)]} { - set text $cached_tagcontent($tag) - } else { - set text "[mc "Tag"]: $tag\n[mc "Id"]: $tagids($tag)" + $ctext conf -state normal + clear_ctext + settabs 0 + set linknum 0 + set sep {} + foreach tag $idtags($id) { + $ctext insert end $sep + add_tag_ctext $tag + set sep "\n\n" } - appendwithlinks $text {} maybe_scroll_ctext 1 $ctext conf -state disabled init_flist {} diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 68c77f6f8f..3bc0f0b5ca 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -548,6 +548,20 @@ our %feature = ( 'sub' => sub { feature_bool('remote_heads', @_) }, 'override' => 0, 'default' => [0]}, + + # Enable showing branches under other refs in addition to heads + + # To set system wide extra branch refs have in $GITWEB_CONFIG + # $feature{'extra-branch-refs'}{'default'} = ['dirs', 'of', 'choice']; + # To have project specific config enable override in $GITWEB_CONFIG + # $feature{'extra-branch-refs'}{'override'} = 1; + # and in project config gitweb.extrabranchrefs = dirs of choice + # Every directory is separated with whitespace. + + 'extra-branch-refs' => { + 'sub' => \&feature_extra_branch_refs, + 'override' => 0, + 'default' => []}, ); sub gitweb_get_feature { @@ -626,6 +640,21 @@ sub feature_avatar { return @val ? @val : @_; } +sub feature_extra_branch_refs { + my (@branch_refs) = @_; + my $values = git_get_project_config('extrabranchrefs'); + + if ($values) { + $values = config_to_multi ($values); + @branch_refs = (); + foreach my $value (@{$values}) { + push @branch_refs, split /\s+/, $value; + } + } + + return @branch_refs; +} + # checking HEAD file with -e is fragile if the repository was # initialized long time ago (i.e. symlink HEAD) and was pack-ref'ed # and then pruned. @@ -656,6 +685,18 @@ sub filter_snapshot_fmts { !$known_snapshot_formats{$_}{'disabled'}} @fmts; } +sub filter_and_validate_refs { + my @refs = @_; + my %unique_refs = (); + + foreach my $ref (@refs) { + die_error(500, "Invalid ref '$ref' in 'extra-branch-refs' feature") unless (is_valid_ref_format($ref)); + # 'heads' are added implicitly in get_branch_refs(). + $unique_refs{$ref} = 1 if ($ref ne 'heads'); + } + return sort keys %unique_refs; +} + # If it is set to code reference, it is code that it is to be run once per # request, allowing updating configurations that change with each request, # while running other code in config file only once. @@ -994,7 +1035,7 @@ our ($action, $project, $file_name, $file_parent, $hash, $hash_parent, $hash_bas sub evaluate_and_validate_params { our $action = $input_params{'action'}; if (defined $action) { - if (!validate_action($action)) { + if (!is_valid_action($action)) { die_error(400, "Invalid action parameter"); } } @@ -1002,7 +1043,7 @@ sub evaluate_and_validate_params { # parameters which are pathnames our $project = $input_params{'project'}; if (defined $project) { - if (!validate_project($project)) { + if (!is_valid_project($project)) { undef $project; die_error(404, "No such project"); } @@ -1010,21 +1051,21 @@ sub evaluate_and_validate_params { our $project_filter = $input_params{'project_filter'}; if (defined $project_filter) { - if (!validate_pathname($project_filter)) { + if (!is_valid_pathname($project_filter)) { die_error(404, "Invalid project_filter parameter"); } } our $file_name = $input_params{'file_name'}; if (defined $file_name) { - if (!validate_pathname($file_name)) { + if (!is_valid_pathname($file_name)) { die_error(400, "Invalid file parameter"); } } our $file_parent = $input_params{'file_parent'}; if (defined $file_parent) { - if (!validate_pathname($file_parent)) { + if (!is_valid_pathname($file_parent)) { die_error(400, "Invalid file parent parameter"); } } @@ -1032,21 +1073,21 @@ sub evaluate_and_validate_params { # parameters which are refnames our $hash = $input_params{'hash'}; if (defined $hash) { - if (!validate_refname($hash)) { + if (!is_valid_refname($hash)) { die_error(400, "Invalid hash parameter"); } } our $hash_parent = $input_params{'hash_parent'}; if (defined $hash_parent) { - if (!validate_refname($hash_parent)) { + if (!is_valid_refname($hash_parent)) { die_error(400, "Invalid hash parent parameter"); } } our $hash_base = $input_params{'hash_base'}; if (defined $hash_base) { - if (!validate_refname($hash_base)) { + if (!is_valid_refname($hash_base)) { die_error(400, "Invalid hash base parameter"); } } @@ -1066,7 +1107,7 @@ sub evaluate_and_validate_params { our $hash_parent_base = $input_params{'hash_parent_base'}; if (defined $hash_parent_base) { - if (!validate_refname($hash_parent_base)) { + if (!is_valid_refname($hash_parent_base)) { die_error(400, "Invalid hash parent base parameter"); } } @@ -1113,7 +1154,7 @@ sub evaluate_git_dir { our $git_dir = "$projectroot/$project" if $project; } -our (@snapshot_fmts, $git_avatar); +our (@snapshot_fmts, $git_avatar, @extra_branch_refs); sub configure_gitweb_features { # list of supported snapshot formats our @snapshot_fmts = gitweb_get_feature('snapshot'); @@ -1131,6 +1172,13 @@ sub configure_gitweb_features { } else { $git_avatar = ''; } + + our @extra_branch_refs = gitweb_get_feature('extra-branch-refs'); + @extra_branch_refs = filter_and_validate_refs (@extra_branch_refs); +} + +sub get_branch_refs { + return ('heads', @extra_branch_refs); } # custom error handler: 'die <message>' is Internal Server Error @@ -1418,27 +1466,30 @@ sub href { ## ====================================================================== ## validation, quoting/unquoting and escaping -sub validate_action { - my $input = shift || return undef; +sub is_valid_action { + my $input = shift; return undef unless exists $actions{$input}; - return $input; + return 1; } -sub validate_project { - my $input = shift || return undef; - if (!validate_pathname($input) || +sub is_valid_project { + my $input = shift; + + return unless defined $input; + if (!is_valid_pathname($input) || !(-d "$projectroot/$input") || !check_export_ok("$projectroot/$input") || ($strict_export && !project_in_list($input))) { return undef; } else { - return $input; + return 1; } } -sub validate_pathname { - my $input = shift || return undef; +sub is_valid_pathname { + my $input = shift; + return undef unless defined $input; # no '.' or '..' as elements of path, i.e. no '.' nor '..' # at the beginning, at the end, and between slashes. # also this catches doubled slashes @@ -1449,24 +1500,33 @@ sub validate_pathname { if ($input =~ m!\0!) { return undef; } - return $input; + return 1; } -sub validate_refname { - my $input = shift || return undef; +sub is_valid_ref_format { + my $input = shift; - # textual hashes are O.K. - if ($input =~ m/^[0-9a-fA-F]{40}$/) { - return $input; - } - # it must be correct pathname - $input = validate_pathname($input) - or return undef; + return undef unless defined $input; # restrictions on ref name according to git-check-ref-format if ($input =~ m!(/\.|\.\.|[\000-\040\177 ~^:?*\[]|/$)!) { return undef; } - return $input; + return 1; +} + +sub is_valid_refname { + my $input = shift; + + return undef unless defined $input; + # textual hashes are O.K. + if ($input =~ m/^[0-9a-fA-F]{40}$/) { + return 1; + } + # it must be correct pathname + is_valid_pathname($input) or return undef; + # check git-check-ref-format restrictions + is_valid_ref_format($input) or return undef; + return 1; } # decode sequences of octets in utf8 into Perl's internal form, @@ -2515,6 +2575,7 @@ sub format_snapshot_links { sub get_feed_info { my $format = shift || 'Atom'; my %res = (action => lc($format)); + my $matched_ref = 0; # feed links are possible only for project views return unless (defined $project); @@ -2522,12 +2583,17 @@ sub get_feed_info { # or don't have specific feed yet (so they should use generic) return if (!$action || $action =~ /^(?:tags|heads|forks|tag|search)$/x); - my $branch; - # branches refs uses 'refs/heads/' prefix (fullname) to differentiate - # from tag links; this also makes possible to detect branch links - if ((defined $hash_base && $hash_base =~ m!^refs/heads/(.*)$!) || - (defined $hash && $hash =~ m!^refs/heads/(.*)$!)) { - $branch = $1; + my $branch = undef; + # branches refs uses 'refs/' + $get_branch_refs()[x] + '/' prefix + # (fullname) to differentiate from tag links; this also makes + # possible to detect branch links + for my $ref (get_branch_refs()) { + if ((defined $hash_base && $hash_base =~ m!^refs/\Q$ref\E/(.*)$!) || + (defined $hash && $hash =~ m!^refs/\Q$ref\E/(.*)$!)) { + $branch = $1; + $matched_ref = $ref; + last; + } } # find log type for feed description (title) my $type = 'log'; @@ -2540,7 +2606,7 @@ sub get_feed_info { } $res{-title} = $type; - $res{'hash'} = (defined $branch ? "refs/heads/$branch" : undef); + $res{'hash'} = (defined $branch ? "refs/$matched_ref/$branch" : undef); $res{'file_name'} = $file_name; return %res; @@ -3193,7 +3259,7 @@ sub git_get_last_activity { '--format=%(committer)', '--sort=-committerdate', '--count=1', - 'refs/heads') or return; + map { "refs/$_" } get_branch_refs ()) or return; my $most_recent = <$fd>; close $fd or return; if (defined $most_recent && @@ -3644,7 +3710,7 @@ sub parse_from_to_diffinfo { sub git_get_heads_list { my ($limit, @classes) = @_; - @classes = ('heads') unless @classes; + @classes = get_branch_refs() unless @classes; my @patterns = map { "refs/$_" } @classes; my @headslist; @@ -3662,9 +3728,16 @@ sub git_get_heads_list { my ($committer, $epoch, $tz) = ($committerinfo =~ /^(.*) ([0-9]+) (.*)$/); $ref_item{'fullname'} = $name; - $name =~ s!^refs/(?:head|remote)s/!!; + my $strip_refs = join '|', map { quotemeta } get_branch_refs(); + $name =~ s!^refs/($strip_refs|remotes)/!!; + $ref_item{'name'} = $name; + # for refs neither in 'heads' nor 'remotes' we want to + # show their ref dir + my $ref_dir = (defined $1) ? $1 : ''; + if ($ref_dir ne '' and $ref_dir ne 'heads' and $ref_dir ne 'remotes') { + $ref_item{'name'} .= ' (' . $ref_dir . ')'; + } - $ref_item{'name'} = $name; $ref_item{'id'} = $hash; $ref_item{'title'} = $title || '(no commit message)'; $ref_item{'epoch'} = $epoch; @@ -7156,6 +7229,15 @@ sub git_tree { git_footer_html(); } +sub sanitize_for_filename { + my $name = shift; + + $name =~ s!/!-!g; + $name =~ s/[^[:alnum:]_.-]//g; + + return $name; +} + sub snapshot_name { my ($project, $hash) = @_; @@ -7163,9 +7245,7 @@ sub snapshot_name { # path/to/project/.git -> project my $name = to_utf8($project); $name =~ s,([^/])/*\.git$,$1,; - $name = basename($name); - # sanitize name - $name =~ s/[[:cntrl:]]/?/g; + $name = sanitize_for_filename(basename($name)); my $ver = $hash; if ($hash =~ /^[0-9a-fA-F]+$/) { @@ -7179,13 +7259,25 @@ sub snapshot_name { $ver = $1; } else { # branches and other need shortened SHA-1 hash - if ($hash =~ m!^refs/(?:heads|remotes)/(.*)$!) { - $ver = $1; + my $strip_refs = join '|', map { quotemeta } get_branch_refs(); + if ($hash =~ m!^refs/($strip_refs|remotes)/(.*)$!) { + my $ref_dir = (defined $1) ? $1 : ''; + $ver = $2; + + $ref_dir = sanitize_for_filename($ref_dir); + # for refs neither in heads nor remotes we want to + # add a ref dir to archive name + if ($ref_dir ne '' and $ref_dir ne 'heads' and $ref_dir ne 'remotes') { + $ver = $ref_dir . '-' . $ver; + } } $ver .= '-' . git_get_short_hash($project, $hash); } + # special case of sanitization for filename - we change + # slashes to dots instead of dashes # in case of hierarchical branch names $ver =~ s!/!.!g; + $ver =~ s/[^[:alnum:]_.-]//g; # name = project-version_string $name = "$name-$ver"; @@ -148,7 +148,7 @@ static void list_commands_in_dir(struct cmdnames *cmds, while ((de = readdir(dir)) != NULL) { int entlen; - if (prefixcmp(de->d_name, prefix)) + if (!starts_with(de->d_name, prefix)) continue; strbuf_setlen(&buf, len); @@ -255,7 +255,7 @@ static int git_unknown_cmd_config(const char *var, const char *value, void *cb) if (!strcmp(var, "help.autocorrect")) autocorrect = git_config_int(var,value); /* Also use aliases for command lookup */ - if (!prefixcmp(var, "alias.")) + if (starts_with(var, "alias.")) add_cmdname(&aliases, var + 6, strlen(var + 6)); return git_default_config(var, value, cb); @@ -329,7 +329,7 @@ const char *help_unknown_cmd(const char *cmd) if ((n < ARRAY_SIZE(common_cmds)) && !cmp) { /* Yes, this is one of the common commands */ n++; /* use the entry from common_cmds[] */ - if (!prefixcmp(candidate, cmd)) { + if (starts_with(candidate, cmd)) { /* Give prefix match a very good score */ main_cmds.names[i]->len = 0; continue; @@ -414,7 +414,7 @@ static int append_similar_ref(const char *refname, const unsigned char *sha1, struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data); char *branch = strrchr(refname, '/') + 1; /* A remote branch of the same name is deemed similar */ - if (!prefixcmp(refname, "refs/remotes/") && + if (starts_with(refname, "refs/remotes/") && !strcmp(branch, cb->base_ref)) string_list_append(cb->similar_refs, refname + strlen("refs/remotes/")); diff --git a/http-backend.c b/http-backend.c index 8c464bd805..d2c0a625ce 100644 --- a/http-backend.c +++ b/http-backend.c @@ -226,7 +226,7 @@ static int http_config(const char *var, const char *value, void *cb) return 0; } - if (!prefixcmp(var, "http.")) { + if (starts_with(var, "http.")) { int i; for (i = 0; i < ARRAY_SIZE(rpc_service); i++) { @@ -247,7 +247,7 @@ static struct rpc_service *select_service(const char *name) struct rpc_service *svc = NULL; int i; - if (prefixcmp(name, "git-")) + if (!starts_with(name, "git-")) forbidden("Unsupported service: '%s'", name); for (i = 0; i < ARRAY_SIZE(rpc_service); i++) { diff --git a/http-push.c b/http-push.c index 34cb70f90e..d4b40c9c60 100644 --- a/http-push.c +++ b/http-push.c @@ -771,7 +771,7 @@ static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed) lock->owner = xmalloc(strlen(ctx->cdata) + 1); strcpy(lock->owner, ctx->cdata); } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TIMEOUT)) { - if (!prefixcmp(ctx->cdata, "Second-")) + if (starts_with(ctx->cdata, "Second-")) lock->timeout = strtol(ctx->cdata + 7, NULL, 10); } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) { @@ -1579,7 +1579,7 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1) return; /* If it's a symref, set the refname; otherwise try for a sha1 */ - if (!prefixcmp((char *)buffer.buf, "ref: ")) { + if (starts_with((char *)buffer.buf, "ref: ")) { *symref = xmemdupz((char *)buffer.buf + 5, buffer.len - 6); } else { get_sha1_hex(buffer.buf, sha1); @@ -460,7 +460,7 @@ void http_init(struct remote *remote, const char *url, int proactive_auth) credential_from_url(&http_auth, url); if (!ssl_cert_password_required && getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") && - !prefixcmp(url, "https://")) + starts_with(url, "https://")) ssl_cert_password_required = 1; } @@ -761,6 +761,12 @@ void finish_active_slot(struct active_request_slot *slot) if (slot->results != NULL) { slot->results->curl_result = slot->curl_result; slot->results->http_code = slot->http_code; +#if LIBCURL_VERSION_NUM >= 0x070a08 + curl_easy_getinfo(slot->curl, CURLINFO_HTTPAUTH_AVAIL, + &slot->results->auth_avail); +#else + slot->results->auth_avail = 0; +#endif } /* Run callback if appropriate */ @@ -994,7 +1000,7 @@ static int update_url_from_redirect(struct strbuf *base, if (!strcmp(asked, got->buf)) return 0; - if (prefixcmp(asked, base->buf)) + if (!starts_with(asked, base->buf)) die("BUG: update_url_from_redirect: %s is not a superset of %s", asked, base->buf); @@ -1100,7 +1106,7 @@ int http_fetch_ref(const char *base, struct ref *ref) strbuf_rtrim(&buffer); if (buffer.len == 40) ret = get_sha1_hex(buffer.buf, ref->old_sha1); - else if (!prefixcmp(buffer.buf, "ref: ")) { + else if (starts_with(buffer.buf, "ref: ")) { ref->symref = xstrdup(buffer.buf + 5); ret = 0; } @@ -1201,8 +1207,8 @@ int http_get_info_packs(const char *base_url, struct packed_git **packs_head) case 'P': i++; if (i + 52 <= buf.len && - !prefixcmp(data + i, " pack-") && - !prefixcmp(data + i + 46, ".pack\n")) { + 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, base_url); @@ -54,6 +54,7 @@ struct slot_results { CURLcode curl_result; long http_code; + long auth_avail; }; struct active_request_slot { diff --git a/imap-send.c b/imap-send.c index 6f5cc4f782..0bc6f7fae1 100644 --- a/imap-send.c +++ b/imap-send.c @@ -1263,7 +1263,7 @@ static int count_messages(struct strbuf *all_msgs) char *p = all_msgs->buf; while (1) { - if (!prefixcmp(p, "From ")) { + if (starts_with(p, "From ")) { p = strstr(p+5, "\nFrom: "); if (!p) break; p = strstr(p+7, "\nDate: "); @@ -1297,7 +1297,7 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs) data = &all_msgs->buf[*ofs]; len = all_msgs->len - *ofs; - if (len < 5 || prefixcmp(data, "From ")) + if (len < 5 || !starts_with(data, "From ")) return 0; p = strchr(data, '\n'); @@ -1339,13 +1339,13 @@ static int git_imap_config(const char *key, const char *val, void *cb) if (!strcmp("folder", key)) { imap_folder = xstrdup(val); } else if (!strcmp("host", key)) { - if (!prefixcmp(val, "imap:")) + if (starts_with(val, "imap:")) val += 5; - else if (!prefixcmp(val, "imaps:")) { + else if (starts_with(val, "imaps:")) { val += 6; server.use_ssl = 1; } - if (!prefixcmp(val, "//")) + if (starts_with(val, "//")) val += 2; server.host = xstrdup(val); } else if (!strcmp("user", key)) diff --git a/log-tree.c b/log-tree.c index 8534d91826..08970bf46e 100644 --- a/log-tree.c +++ b/log-tree.c @@ -98,7 +98,7 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in struct object *obj; enum decoration_type type = DECORATION_NONE; - if (!prefixcmp(refname, "refs/replace/")) { + if (starts_with(refname, "refs/replace/")) { unsigned char original_sha1[20]; if (!read_replace_refs) return 0; @@ -116,11 +116,11 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in if (!obj) return 0; - if (!prefixcmp(refname, "refs/heads/")) + if (starts_with(refname, "refs/heads/")) type = DECORATION_REF_LOCAL; - else if (!prefixcmp(refname, "refs/remotes/")) + else if (starts_with(refname, "refs/remotes/")) type = DECORATION_REF_REMOTE; - else if (!prefixcmp(refname, "refs/tags/")) + else if (starts_with(refname, "refs/tags/")) type = DECORATION_REF_TAG; else if (!strcmp(refname, "refs/stash")) type = DECORATION_REF_STASH; @@ -134,6 +134,8 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in obj = ((struct tag *)obj)->tagged; if (!obj) break; + if (!obj->parsed) + parse_object(obj->sha1); add_name_decoration(DECORATION_REF_TAG, refname, obj); } return 0; @@ -734,7 +736,7 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log if (!opt->diff && !DIFF_OPT_TST(&opt->diffopt, EXIT_WITH_STATUS)) return 0; - parse_commit(commit); + parse_commit_or_die(commit); sha1 = commit->tree->object.sha1; /* Root commit? */ @@ -759,7 +761,7 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log * parent, showing summary diff of the others * we merged _in_. */ - parse_commit(parents->item); + parse_commit_or_die(parents->item); diff_tree_sha1(parents->item->tree->object.sha1, sha1, "", &opt->diffopt); log_tree_diff_flush(opt); @@ -774,7 +776,7 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log for (;;) { struct commit *parent = parents->item; - parse_commit(parent); + parse_commit_or_die(parent); diff_tree_sha1(parent->tree->object.sha1, sha1, "", &opt->diffopt); log_tree_diff_flush(opt); diff --git a/merge-recursive.c b/merge-recursive.c index dbb7104c04..a18bd15dd3 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -2063,13 +2063,13 @@ int parse_merge_opt(struct merge_options *o, const char *s) o->recursive_variant = MERGE_RECURSIVE_THEIRS; else if (!strcmp(s, "subtree")) o->subtree_shift = ""; - else if (!prefixcmp(s, "subtree=")) + else if (starts_with(s, "subtree=")) o->subtree_shift = s + strlen("subtree="); else if (!strcmp(s, "patience")) o->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF); else if (!strcmp(s, "histogram")) o->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF); - else if (!prefixcmp(s, "diff-algorithm=")) { + else if (starts_with(s, "diff-algorithm=")) { long value = parse_algorithm_value(s + strlen("diff-algorithm=")); if (value < 0) return -1; @@ -2088,7 +2088,7 @@ int parse_merge_opt(struct merge_options *o, const char *s) o->renormalize = 1; else if (!strcmp(s, "no-renormalize")) o->renormalize = 0; - else if (!prefixcmp(s, "rename-threshold=")) { + else if (starts_with(s, "rename-threshold=")) { const char *score = s + strlen("rename-threshold="); if ((o->rename_score = parse_rename_score(&score)) == -1 || *score != 0) return -1; diff --git a/notes-utils.c b/notes-utils.c index 9107c379d9..2975dcd581 100644 --- a/notes-utils.c +++ b/notes-utils.c @@ -18,7 +18,7 @@ void create_notes_commit(struct notes_tree *t, struct commit_list *parents, unsigned char parent_sha1[20]; if (!read_ref(t->ref, parent_sha1)) { struct commit *parent = lookup_commit(parent_sha1); - if (!parent || parse_commit(parent)) + if (parse_commit(parent)) die("Failed to find/parse commit %s", t->ref); commit_list_insert(parent, &parents); } @@ -70,7 +70,7 @@ static combine_notes_fn parse_combine_notes_fn(const char *v) static int notes_rewrite_config(const char *k, const char *v, void *cb) { struct notes_rewrite_cfg *c = cb; - if (!prefixcmp(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) { + if (starts_with(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) { c->enabled = git_config_bool(k, v); return 0; } else if (!c->mode_from_env && !strcmp(k, "notes.rewritemode")) { @@ -85,7 +85,7 @@ static int notes_rewrite_config(const char *k, const char *v, void *cb) } else if (!c->refs_from_env && !strcmp(k, "notes.rewriteref")) { /* note that a refs/ prefix is implied in the * underlying for_each_glob_ref */ - if (!prefixcmp(v, "refs/notes/")) + if (starts_with(v, "refs/notes/")) string_list_add_refs_by_glob(c->refs, v); else warning(_("Refusing to rewrite notes in %s" @@ -1243,9 +1243,9 @@ static void format_note(struct notes_tree *t, const unsigned char *object_sha1, if (!ref || !strcmp(ref, GIT_NOTES_DEFAULT_REF)) { strbuf_addstr(sb, "\nNotes:\n"); } else { - if (!prefixcmp(ref, "refs/")) + if (starts_with(ref, "refs/")) ref += 5; - if (!prefixcmp(ref, "notes/")) + if (starts_with(ref, "notes/")) ref += 6; strbuf_addf(sb, "\nNotes (%s):\n", ref); } @@ -1293,9 +1293,9 @@ int copy_note(struct notes_tree *t, void expand_notes_ref(struct strbuf *sb) { - if (!prefixcmp(sb->buf, "refs/notes/")) + if (starts_with(sb->buf, "refs/notes/")) return; /* we're happy */ - else if (!prefixcmp(sb->buf, "notes/")) + else if (starts_with(sb->buf, "notes/")) strbuf_insert(sb, 0, "refs/", 5); else strbuf_insert(sb, 0, "refs/notes/", 11); diff --git a/pack-write.c b/pack-write.c index ca9e63be18..605d01b25c 100644 --- a/pack-write.c +++ b/pack-write.c @@ -44,14 +44,13 @@ static int need_large_offset(off_t offset, const struct pack_idx_option *opts) */ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *opts, - unsigned char *sha1) + const unsigned char *sha1) { struct sha1file *f; struct pack_idx_entry **sorted_by_sha, **list, **last; off_t last_obj_offset = 0; uint32_t array[256]; int i, fd; - git_SHA_CTX ctx; uint32_t index_version; if (nr_objects) { @@ -114,9 +113,6 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec } sha1write(f, array, 256 * 4); - /* compute the SHA1 hash of sorted object names. */ - git_SHA1_Init(&ctx); - /* * Write the actual SHA1 entries.. */ @@ -128,7 +124,6 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec sha1write(f, &offset, 4); } sha1write(f, obj->sha1, 20); - git_SHA1_Update(&ctx, obj->sha1, 20); if ((opts->flags & WRITE_IDX_STRICT) && (i && !hashcmp(list[-2]->sha1, obj->sha1))) die("The same object %s appears twice in the pack", @@ -178,7 +173,6 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec sha1write(f, sha1, 20); sha1close(f, NULL, ((opts->flags & WRITE_IDX_VERIFY) ? CSUM_CLOSE : CSUM_FSYNC)); - git_SHA1_Final(sha1, &ctx); return index_name; } @@ -189,8 +183,7 @@ off_t write_pack_header(struct sha1file *f, uint32_t nr_entries) hdr.hdr_signature = htonl(PACK_SIGNATURE); hdr.hdr_version = htonl(PACK_VERSION); hdr.hdr_entries = htonl(nr_entries); - if (sha1write(f, &hdr, sizeof(hdr))) - return 0; + sha1write(f, &hdr, sizeof(hdr)); return sizeof(hdr); } @@ -76,7 +76,7 @@ struct pack_idx_entry { struct progress; typedef int (*verify_fn)(const unsigned char*, enum object_type, unsigned long, void*, int*); -extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *, unsigned char *sha1); +extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *, const unsigned char *sha1); extern int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr); extern int verify_pack_index(struct packed_git *); extern int verify_pack(struct packed_git *, verify_fn fn, struct progress *, uint32_t); @@ -151,7 +151,7 @@ int decimal_width(int number) static int pager_command_config(const char *var, const char *value, void *data) { struct pager_config *c = data; - if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd)) { + if (starts_with(var, "pager.") && !strcmp(var + 6, c->cmd)) { int b = git_config_maybe_bool(var, value); if (b >= 0) c->want = b; diff --git a/parse-options.c b/parse-options.c index 62e9b1cc68..7b8d3faa17 100644 --- a/parse-options.c +++ b/parse-options.c @@ -273,13 +273,13 @@ is_abbreviated: if (options->flags & PARSE_OPT_NONEG) continue; /* negated and abbreviated very much? */ - if (!prefixcmp("no-", arg)) { + if (starts_with("no-", arg)) { flags |= OPT_UNSET; goto is_abbreviated; } /* negated? */ - if (prefixcmp(arg, "no-")) { - if (!prefixcmp(long_name, "no-")) { + if (!starts_with(arg, "no-")) { + if (starts_with(long_name, "no-")) { long_name += 3; opt_flags |= OPT_UNSET; goto again; @@ -289,7 +289,7 @@ is_abbreviated: flags |= OPT_UNSET; rest = skip_prefix(arg + 3, long_name); /* abbreviated and negated? */ - if (!rest && !prefixcmp(long_name, arg + 3)) + if (!rest && starts_with(long_name, arg + 3)) goto is_abbreviated; if (!rest) continue; @@ -334,7 +334,7 @@ static void check_typos(const char *arg, const struct option *options) if (strlen(arg) < 3) return; - if (!prefixcmp(arg, "no-")) { + if (starts_with(arg, "no-")) { error ("did you mean `--%s` (with two dashes ?)", arg); exit(129); } @@ -342,7 +342,7 @@ static void check_typos(const char *arg, const struct option *options) for (; options->type != OPTION_END; options++) { if (!options->long_name) continue; - if (!prefixcmp(options->long_name, arg)) { + if (starts_with(options->long_name, arg)) { error ("did you mean `--%s` (with two dashes ?)", arg); exit(129); } diff --git a/parse-options.h b/parse-options.h index 8736006ed7..d670cb9669 100644 --- a/parse-options.h +++ b/parse-options.h @@ -161,9 +161,6 @@ struct option { N_("no-op (backward compatibility)"), \ PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, parse_opt_noop_cb } -/* Deprecated synonym */ -#define OPT_BOOLEAN OPT_COUNTUP - /* parse_options() will filter out the processed options and leave the * non-option arguments in argv[]. usagestr strings should be marked * for translation with N_(). @@ -232,15 +229,15 @@ extern int parse_opt_tertiary(const struct option *, const char *, int); extern int parse_opt_string_list(const struct option *, const char *, int); extern int parse_opt_noop_cb(const struct option *, const char *, int); -#define OPT__VERBOSE(var, h) OPT_BOOLEAN('v', "verbose", (var), (h)) -#define OPT__QUIET(var, h) OPT_BOOLEAN('q', "quiet", (var), (h)) +#define OPT__VERBOSE(var, h) OPT_COUNTUP('v', "verbose", (var), (h)) +#define OPT__QUIET(var, h) OPT_COUNTUP('q', "quiet", (var), (h)) #define OPT__VERBOSITY(var) \ { OPTION_CALLBACK, 'v', "verbose", (var), NULL, N_("be more verbose"), \ PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \ { OPTION_CALLBACK, 'q', "quiet", (var), NULL, N_("be more quiet"), \ PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 } -#define OPT__DRY_RUN(var, h) OPT_BOOLEAN('n', "dry-run", (var), (h)) -#define OPT__FORCE(var, h) OPT_BOOLEAN('f', "force", (var), (h)) +#define OPT__DRY_RUN(var, h) OPT_BOOL('n', "dry-run", (var), (h)) +#define OPT__FORCE(var, h) OPT_COUNTUP('f', "force", (var), (h)) #define OPT__ABBREV(var) \ { OPTION_CALLBACK, 0, "abbrev", (var), N_("n"), \ N_("use <n> digits to display SHA-1s"), \ diff --git a/pathspec.c b/pathspec.c index 87b3b82f1f..6cb9fd3014 100644 --- a/pathspec.c +++ b/pathspec.c @@ -71,8 +71,23 @@ static struct pathspec_magic { { PATHSPEC_LITERAL, 0, "literal" }, { PATHSPEC_GLOB, '\0', "glob" }, { PATHSPEC_ICASE, '\0', "icase" }, + { PATHSPEC_EXCLUDE, '!', "exclude" }, }; +static void prefix_short_magic(struct strbuf *sb, int prefixlen, + unsigned short_magic) +{ + int i; + strbuf_addstr(sb, ":("); + for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) + if (short_magic & pathspec_magic[i].bit) { + if (sb->buf[sb->len - 1] != '(') + strbuf_addch(sb, ','); + strbuf_addstr(sb, pathspec_magic[i].name); + } + strbuf_addf(sb, ",prefix:%d)", prefixlen); +} + /* * Take an element of a pathspec and check for magic signatures. * Append the result to the prefix. Return the magic bitmap. @@ -154,7 +169,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item, magic |= pathspec_magic[i].bit; break; } - if (!prefixcmp(copyfrom, "prefix:")) { + if (starts_with(copyfrom, "prefix:")) { char *endptr; pathspec_prefix = strtol(copyfrom + 7, &endptr, 10); @@ -232,22 +247,16 @@ static unsigned prefix_pathspec(struct pathspec_item *item, */ if (flags & PATHSPEC_PREFIX_ORIGIN) { struct strbuf sb = STRBUF_INIT; - const char *start = elt; if (prefixlen && !literal_global) { /* Preserve the actual prefix length of each pattern */ if (short_magic) - die("BUG: prefixing on short magic is not supported"); + prefix_short_magic(&sb, prefixlen, short_magic); else if (long_magic_end) { - strbuf_add(&sb, start, long_magic_end - start); - strbuf_addf(&sb, ",prefix:%d", prefixlen); - start = long_magic_end; - } else { - if (*start == ':') - start++; + strbuf_add(&sb, elt, long_magic_end - elt); + strbuf_addf(&sb, ",prefix:%d)", prefixlen); + } else strbuf_addf(&sb, ":(prefix:%d)", prefixlen); - } } - strbuf_add(&sb, start, copyfrom - start); strbuf_addstr(&sb, match); item->original = strbuf_detach(&sb, NULL); } else @@ -355,7 +364,7 @@ void parse_pathspec(struct pathspec *pathspec, { struct pathspec_item *item; const char *entry = argv ? *argv : NULL; - int i, n, prefixlen; + int i, n, prefixlen, nr_exclude = 0; memset(pathspec, 0, sizeof(*pathspec)); @@ -412,6 +421,8 @@ void parse_pathspec(struct pathspec *pathspec, if ((flags & PATHSPEC_LITERAL_PATH) && !(magic_mask & PATHSPEC_LITERAL)) item[i].magic |= PATHSPEC_LITERAL; + if (item[i].magic & PATHSPEC_EXCLUDE) + nr_exclude++; if (item[i].magic & magic_mask) unsupported_magic(entry, item[i].magic & magic_mask, @@ -427,6 +438,10 @@ void parse_pathspec(struct pathspec *pathspec, pathspec->magic |= item[i].magic; } + if (nr_exclude == n) + die(_("There is nothing to exclude from by :(exclude) patterns.\n" + "Perhaps you forgot to add either ':/' or '.' ?")); + if (pathspec->magic & PATHSPEC_MAXDEPTH) { if (flags & PATHSPEC_KEEP_ORDER) diff --git a/pathspec.h b/pathspec.h index a75e9242d1..0c1126264a 100644 --- a/pathspec.h +++ b/pathspec.h @@ -7,12 +7,14 @@ #define PATHSPEC_LITERAL (1<<2) #define PATHSPEC_GLOB (1<<3) #define PATHSPEC_ICASE (1<<4) +#define PATHSPEC_EXCLUDE (1<<5) #define PATHSPEC_ALL_MAGIC \ (PATHSPEC_FROMTOP | \ PATHSPEC_MAXDEPTH | \ PATHSPEC_LITERAL | \ PATHSPEC_GLOB | \ - PATHSPEC_ICASE) + PATHSPEC_ICASE | \ + PATHSPEC_EXCLUDE) #define PATHSPEC_ONESTAR 1 /* the pathspec pattern satisfies GFNM_ONESTAR */ diff --git a/perl/Git/SVN/Editor.pm b/perl/Git/SVN/Editor.pm index b3bcd476da..34e8af966c 100644 --- a/perl/Git/SVN/Editor.pm +++ b/perl/Git/SVN/Editor.pm @@ -304,8 +304,12 @@ sub C { my ($self, $m, $deletions) = @_; my ($dir, $file) = split_path($m->{file_b}); my $pbat = $self->ensure_path($dir, $deletions); + # workaround for a bug in svn serf backend (v1.8.5 and below): + # store third argument to ->add_file() in a local variable, to make it + # have the same lifetime as $fbat + my $upa = $self->url_path($m->{file_a}); my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat, - $self->url_path($m->{file_a}), $self->{r}); + $upa, $self->{r}); print "\tC\t$m->{file_a} => $m->{file_b}\n" unless $::_q; $self->chg_file($fbat, $m); $self->close_file($fbat,undef,$self->{pool}); @@ -323,8 +327,10 @@ sub R { my ($self, $m, $deletions) = @_; my ($dir, $file) = split_path($m->{file_b}); my $pbat = $self->ensure_path($dir, $deletions); + # workaround for a bug in svn serf backend, see comment in C() above + my $upa = $self->url_path($m->{file_a}); my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat, - $self->url_path($m->{file_a}), $self->{r}); + $upa, $self->{r}); print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $::_q; $self->apply_autoprops($file, $fbat); $self->chg_file($fbat, $m); diff --git a/pkt-line.c b/pkt-line.c index 70f19501d0..bc63b3b80e 100644 --- a/pkt-line.c +++ b/pkt-line.c @@ -24,8 +24,8 @@ static void packet_trace(const char *buf, unsigned int len, int write) strbuf_addf(&out, "packet: %12s%c ", packet_trace_prefix, write ? '>' : '<'); - if ((len >= 4 && !prefixcmp(buf, "PACK")) || - (len >= 5 && !prefixcmp(buf+1, "PACK"))) { + if ((len >= 4 && starts_with(buf, "PACK")) || + (len >= 5 && starts_with(buf+1, "PACK"))) { strbuf_addstr(&out, "PACK ..."); unsetenv(trace_key); } @@ -72,7 +72,7 @@ msgstr "Archivformat" #: archive.c:328 builtin/log.c:1193 msgid "prefix" -msgstr "Prefix" +msgstr "Präfix" #: archive.c:329 msgid "prepend prefix to each pathname in the archive" @@ -3716,7 +3716,7 @@ msgid "" msgstr "" "Eingabehilfe:\n" "1 - nummeriertes Element auswählen\n" -"foo - Element anhand eines eindeutigen Prefix auswählen\n" +"foo - Element anhand eines eindeutigen Präfix auswählen\n" " - (leer) nichts auswählen" #: builtin/clean.c:298 @@ -3734,7 +3734,7 @@ msgstr "" "1 - einzelnes Element auswählen\n" "3-5 - Bereich von Elementen auswählen\n" "2-3,6-9 - mehrere Bereiche auswählen\n" -"foo - Element anhand eines eindeutigen Prefix auswählen\n" +"foo - Element anhand eines eindeutigen Präfix auswählen\n" "-... - angegebenes Element abwählen\n" "* - alle Elemente auswählen\n" " - (leer) Auswahl beenden" @@ -6452,7 +6452,7 @@ msgstr "kennzeichnet die Serie als n-te Fassung" #: builtin/log.c:1194 msgid "Use [<prefix>] instead of [PATCH]" -msgstr "verwendet [<Prefix>] anstatt [PATCH]" +msgstr "verwendet [<Präfix>] anstatt [PATCH]" #: builtin/log.c:1197 msgid "store resulting files in <dir>" @@ -8182,7 +8182,7 @@ msgid "" "[-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--" "index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])" msgstr "" -"git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<Prefix>] " +"git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<Präfix>] " "[-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--" "index-output=<Datei>] (--empty | <Commit-Referenz1> [<Commit-Referenz2> " "[<Commit-Referenz3>]])" @@ -9782,15 +9782,15 @@ msgstr "gibt Tag-Inhalte aus" #: builtin/write-tree.c:13 msgid "git write-tree [--missing-ok] [--prefix=<prefix>/]" -msgstr "git write-tree [--missing-ok] [--prefix=<Prefix>/]" +msgstr "git write-tree [--missing-ok] [--prefix=<Präfix>/]" #: builtin/write-tree.c:26 msgid "<prefix>/" -msgstr "<Prefix>/" +msgstr "<Präfix>/" #: builtin/write-tree.c:27 msgid "write tree object for a subdirectory <prefix>" -msgstr "schreibt das \"Tree\"-Objekt für ein Unterverzeichnis <Prefix>" +msgstr "schreibt das \"Tree\"-Objekt für ein Unterverzeichnis <Präfix>" #: builtin/write-tree.c:30 msgid "only useful for debugging" @@ -40,7 +40,7 @@ static int git_pretty_formats_config(const char *var, const char *value, void *c const char *fmt; int i; - if (prefixcmp(var, "pretty.")) + if (!starts_with(var, "pretty.")) return 0; name = var + strlen("pretty."); @@ -67,7 +67,7 @@ static int git_pretty_formats_config(const char *var, const char *value, void *c commit_format->name = xstrdup(name); commit_format->format = CMIT_FMT_USERFORMAT; git_config_string(&fmt, var, value); - if (!prefixcmp(fmt, "format:") || !prefixcmp(fmt, "tformat:")) { + if (starts_with(fmt, "format:") || starts_with(fmt, "tformat:")) { commit_format->is_tformat = fmt[0] == 't'; fmt = strchr(fmt, ':') + 1; } else if (strchr(fmt, '%')) @@ -115,7 +115,7 @@ static struct cmt_fmt_map *find_commit_format_recursive(const char *sought, for (i = 0; i < commit_formats_len; i++) { size_t match_len; - if (prefixcmp(commit_formats[i].name, sought)) + if (!starts_with(commit_formats[i].name, sought)) continue; match_len = strlen(commit_formats[i].name); @@ -151,7 +151,7 @@ void get_commit_format(const char *arg, struct rev_info *rev) rev->commit_format = CMIT_FMT_DEFAULT; return; } - if (!prefixcmp(arg, "format:") || !prefixcmp(arg, "tformat:")) { + if (starts_with(arg, "format:") || starts_with(arg, "tformat:")) { save_user_format(rev, strchr(arg, ':') + 1, arg[0] == 't'); return; } @@ -840,10 +840,10 @@ static void parse_commit_header(struct format_commit_context *context) if (i == eol) { break; - } else if (!prefixcmp(msg + i, "author ")) { + } else if (starts_with(msg + i, "author ")) { context->author.off = i + 7; context->author.len = eol - i - 7; - } else if (!prefixcmp(msg + i, "committer ")) { + } else if (starts_with(msg + i, "committer ")) { context->committer.off = i + 10; context->committer.len = eol - i - 10; } @@ -983,7 +983,7 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */ if (!end) return 0; - if (!prefixcmp(begin, "auto,")) { + if (starts_with(begin, "auto,")) { if (!want_color(c->pretty_ctx->color)) return end - placeholder + 1; begin += 5; @@ -994,16 +994,16 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */ strbuf_addstr(sb, color); return end - placeholder + 1; } - if (!prefixcmp(placeholder + 1, "red")) { + if (starts_with(placeholder + 1, "red")) { strbuf_addstr(sb, GIT_COLOR_RED); return 4; - } else if (!prefixcmp(placeholder + 1, "green")) { + } else if (starts_with(placeholder + 1, "green")) { strbuf_addstr(sb, GIT_COLOR_GREEN); return 6; - } else if (!prefixcmp(placeholder + 1, "blue")) { + } else if (starts_with(placeholder + 1, "blue")) { strbuf_addstr(sb, GIT_COLOR_BLUE); return 5; - } else if (!prefixcmp(placeholder + 1, "reset")) { + } else if (starts_with(placeholder + 1, "reset")) { strbuf_addstr(sb, GIT_COLOR_RESET); return 6; } else @@ -1060,11 +1060,11 @@ static size_t parse_padding_placeholder(struct strbuf *sb, end = strchr(start, ')'); if (!end || end == start) return 0; - if (!prefixcmp(start, "trunc)")) + if (starts_with(start, "trunc)")) c->truncate = trunc_right; - else if (!prefixcmp(start, "ltrunc)")) + else if (starts_with(start, "ltrunc)")) c->truncate = trunc_left; - else if (!prefixcmp(start, "mtrunc)")) + else if (starts_with(start, "mtrunc)")) c->truncate = trunc_middle; else return 0; @@ -1089,7 +1089,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ /* these are independent of the commit */ switch (placeholder[0]) { case 'C': - if (!prefixcmp(placeholder + 1, "(auto)")) { + if (starts_with(placeholder + 1, "(auto)")) { c->auto_color = 1; return 7; /* consumed 7 bytes, "C(auto)" */ } else { @@ -1556,7 +1556,7 @@ static void pp_header(struct pretty_print_context *pp, continue; } - if (!prefixcmp(line, "parent ")) { + if (starts_with(line, "parent ")) { if (linelen != 48) die("bad parent line in commit"); continue; @@ -1580,11 +1580,11 @@ static void pp_header(struct pretty_print_context *pp, * FULL shows both authors but not dates. * FULLER shows both authors and dates. */ - if (!prefixcmp(line, "author ")) { + if (starts_with(line, "author ")) { strbuf_grow(sb, linelen + 80); pp_user_info(pp, "Author", sb, line + 7, encoding); } - if (!prefixcmp(line, "committer ") && + if (starts_with(line, "committer ") && (pp->fmt == CMIT_FMT_FULL || pp->fmt == CMIT_FMT_FULLER)) { strbuf_grow(sb, linelen + 80); pp_user_info(pp, "Commit", sb, line + 10, encoding); @@ -22,6 +22,7 @@ static char *do_askpass(const char *cmd, const char *prompt) if (start_command(&pass)) return NULL; + strbuf_reset(&buffer); if (strbuf_read(&buffer, pass.out, 20) < 0) err = 1; @@ -38,7 +39,7 @@ static char *do_askpass(const char *cmd, const char *prompt) strbuf_setlen(&buffer, strcspn(buffer.buf, "\r\n")); - return strbuf_detach(&buffer, NULL); + return buffer.buf; } char *git_prompt(const char *prompt, int flags) @@ -637,7 +637,7 @@ static int do_one_ref(struct ref_entry *entry, void *cb_data) struct ref_entry *old_current_ref; int retval; - if (prefixcmp(entry->name, data->base)) + if (!starts_with(entry->name, data->base)) return 0; if (!(data->flags & DO_FOR_EACH_INCLUDE_BROKEN) && @@ -1042,7 +1042,7 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir) if (refname) { last = create_ref_entry(refname, sha1, REF_ISPACKED, 1); if (peeled == PEELED_FULLY || - (peeled == PEELED_TAGS && !prefixcmp(refname, "refs/tags/"))) + (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/"))) last->flag |= REF_KNOWS_PEELED; add_ref(dir, last); continue; @@ -1376,7 +1376,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea return NULL; } buffer[len] = 0; - if (!prefixcmp(buffer, "refs/") && + if (starts_with(buffer, "refs/") && !check_refname_format(buffer, 0)) { strcpy(refname_buffer, buffer); refname = refname_buffer; @@ -1415,7 +1415,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea /* * Is it a symbolic ref? */ - if (prefixcmp(buffer, "ref:")) { + if (!starts_with(buffer, "ref:")) { /* * Please note that FETCH_HEAD has a second * line containing other data. @@ -1837,7 +1837,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, struct ref_filter filter; int ret; - if (!prefix && prefixcmp(pattern, "refs/")) + if (!prefix && !starts_with(pattern, "refs/")) strbuf_addstr(&real_pattern, "refs/"); else if (prefix) strbuf_addstr(&real_pattern, prefix); @@ -1874,9 +1874,9 @@ int for_each_rawref(each_ref_fn fn, void *cb_data) const char *prettify_refname(const char *name) { return name + ( - !prefixcmp(name, "refs/heads/") ? 11 : - !prefixcmp(name, "refs/tags/") ? 10 : - !prefixcmp(name, "refs/remotes/") ? 13 : + starts_with(name, "refs/heads/") ? 11 : + starts_with(name, "refs/tags/") ? 10 : + starts_with(name, "refs/remotes/") ? 13 : 0); } @@ -2244,7 +2244,7 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data) struct pack_refs_cb_data *cb = cb_data; enum peel_status peel_status; struct ref_entry *packed_entry; - int is_tag_ref = !prefixcmp(entry->name, "refs/tags/"); + int is_tag_ref = starts_with(entry->name, "refs/tags/"); /* ALWAYS pack tags */ if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref) @@ -2679,9 +2679,9 @@ int log_ref_setup(const char *refname, char *logfile, int bufsize) git_snpath(logfile, bufsize, "logs/%s", refname); if (log_all_ref_updates && - (!prefixcmp(refname, "refs/heads/") || - !prefixcmp(refname, "refs/remotes/") || - !prefixcmp(refname, "refs/notes/") || + (starts_with(refname, "refs/heads/") || + starts_with(refname, "refs/remotes/") || + starts_with(refname, "refs/notes/") || !strcmp(refname, "HEAD"))) { if (safe_create_leading_directories(logfile) < 0) return error("unable to create directory for %s", @@ -2751,7 +2751,7 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1, static int is_branch(const char *refname) { - return !strcmp(refname, "HEAD") || !prefixcmp(refname, "refs/heads/"); + return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/"); } int write_ref_sha1(struct ref_lock *lock, @@ -3450,7 +3450,7 @@ int parse_hide_refs_config(const char *var, const char *value, const char *secti { if (!strcmp("transfer.hiderefs", var) || /* NEEDSWORK: use parse_config_key() once both are merged */ - (!prefixcmp(var, section) && var[strlen(section)] == '.' && + (starts_with(var, section) && var[strlen(section)] == '.' && !strcmp(var + strlen(section), ".hiderefs"))) { char *ref; int len; @@ -3478,7 +3478,7 @@ int ref_is_hidden(const char *refname) return 0; for_each_string_list_item(item, hide_refs) { int len; - if (prefixcmp(refname, item->string)) + if (!starts_with(refname, item->string)) continue; len = strlen(item->string); if (!refname[len] || refname[len] == '/') diff --git a/remote-curl.c b/remote-curl.c index c9b891adbf..e38c4b026f 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -217,7 +217,7 @@ static struct discovery* discover_refs(const char *service, int for_push) free_discovery(last); strbuf_addf(&refs_url, "%sinfo/refs", url.buf); - if ((!prefixcmp(url.buf, "http://") || !prefixcmp(url.buf, "https://")) && + if ((starts_with(url.buf, "http://") || starts_with(url.buf, "https://")) && git_env_bool("GIT_SMART_HTTP", 1)) { maybe_smart = 1; if (!strchr(url.buf, '?')) @@ -394,25 +394,29 @@ static size_t rpc_in(char *ptr, size_t eltsize, return size; } -static int run_slot(struct active_request_slot *slot) +static int run_slot(struct active_request_slot *slot, + struct slot_results *results) { int err; - struct slot_results results; + struct slot_results results_buf; - slot->results = &results; + if (!results) + results = &results_buf; + + slot->results = results; slot->curl_result = curl_easy_perform(slot->curl); finish_active_slot(slot); - err = handle_curl_result(&results); + err = handle_curl_result(results); if (err != HTTP_OK && err != HTTP_REAUTH) { error("RPC failed; result=%d, HTTP code = %ld", - results.curl_result, results.http_code); + results->curl_result, results->http_code); } return err; } -static int probe_rpc(struct rpc_state *rpc) +static int probe_rpc(struct rpc_state *rpc, struct slot_results *results) { struct active_request_slot *slot; struct curl_slist *headers = NULL; @@ -434,7 +438,7 @@ static int probe_rpc(struct rpc_state *rpc) curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); curl_easy_setopt(slot->curl, CURLOPT_FILE, &buf); - err = run_slot(slot); + err = run_slot(slot, results); curl_slist_free_all(headers); strbuf_release(&buf); @@ -449,6 +453,7 @@ static int post_rpc(struct rpc_state *rpc) char *gzip_body = NULL; size_t gzip_size = 0; int err, large_request = 0; + int needs_100_continue = 0; /* Try to load the entire request, if we can fit it into the * allocated buffer space we can use HTTP/1.0 and avoid the @@ -472,18 +477,24 @@ static int post_rpc(struct rpc_state *rpc) } if (large_request) { + struct slot_results results; + do { - err = probe_rpc(rpc); + err = probe_rpc(rpc, &results); if (err == HTTP_REAUTH) credential_fill(&http_auth); } while (err == HTTP_REAUTH); if (err != HTTP_OK) return -1; + + if (results.auth_avail & CURLAUTH_GSSNEGOTIATE) + needs_100_continue = 1; } headers = curl_slist_append(headers, rpc->hdr_content_type); headers = curl_slist_append(headers, rpc->hdr_accept); - headers = curl_slist_append(headers, "Expect:"); + headers = curl_slist_append(headers, needs_100_continue ? + "Expect: 100-continue" : "Expect:"); retry: slot = get_active_slot(); @@ -574,7 +585,7 @@ retry: curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in); curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc); - err = run_slot(slot); + err = run_slot(slot, NULL); if (err == HTTP_REAUTH && !large_request) { credential_fill(&http_auth); goto retry; @@ -755,7 +766,7 @@ static void parse_fetch(struct strbuf *buf) int alloc_heads = 0, nr_heads = 0; do { - if (!prefixcmp(buf->buf, "fetch ")) { + if (starts_with(buf->buf, "fetch ")) { char *p = buf->buf + strlen("fetch "); char *name; struct ref *ref; @@ -878,7 +889,7 @@ static void parse_push(struct strbuf *buf) int alloc_spec = 0, nr_spec = 0, i, ret; do { - if (!prefixcmp(buf->buf, "push ")) { + if (starts_with(buf->buf, "push ")) { ALLOC_GROW(specs, nr_spec + 1, alloc_spec); specs[nr_spec++] = xstrdup(buf->buf + 5); } @@ -941,19 +952,19 @@ int main(int argc, const char **argv) } if (buf.len == 0) break; - if (!prefixcmp(buf.buf, "fetch ")) { + if (starts_with(buf.buf, "fetch ")) { if (nongit) die("Fetch attempted without a local repo"); parse_fetch(&buf); - } else if (!strcmp(buf.buf, "list") || !prefixcmp(buf.buf, "list ")) { + } else if (!strcmp(buf.buf, "list") || starts_with(buf.buf, "list ")) { int for_push = !!strstr(buf.buf + 4, "for-push"); output_refs(get_refs(for_push)); - } else if (!prefixcmp(buf.buf, "push ")) { + } else if (starts_with(buf.buf, "push ")) { parse_push(&buf); - } else if (!prefixcmp(buf.buf, "option ")) { + } else if (starts_with(buf.buf, "option ")) { char *name = buf.buf + strlen("option "); char *value = strchr(name, ' '); int result; diff --git a/remote-testsvn.c b/remote-testsvn.c index d7cd5d272f..078f1ffa41 100644 --- a/remote-testsvn.c +++ b/remote-testsvn.c @@ -82,7 +82,7 @@ static int parse_rev_note(const char *msg, struct rev_note *res) len = end ? end - msg : strlen(msg); key = "Revision-number: "; - if (!prefixcmp(msg, key)) { + if (starts_with(msg, key)) { long i; char *end; value = msg + strlen(key); @@ -154,7 +154,7 @@ static void check_or_regenerate_marks(int latestrev) } else { strbuf_addf(&sb, ":%d ", latestrev); while (strbuf_getline(&line, marksfile, '\n') != EOF) { - if (!prefixcmp(line.buf, sb.buf)) { + if (starts_with(line.buf, sb.buf)) { found++; break; } @@ -264,7 +264,7 @@ static int do_command(struct strbuf *line) return 1; /* end of command stream, quit */ } if (batch_cmd) { - if (prefixcmp(batch_cmd->name, line->buf)) + if (!starts_with(batch_cmd->name, line->buf)) die("Active %s batch interrupted by %s", batch_cmd->name, line->buf); /* buffer batch lines */ string_list_append(&batchlines, line->buf); @@ -272,7 +272,7 @@ static int do_command(struct strbuf *line) } for (p = input_command_list; p->name; p++) { - if (!prefixcmp(line->buf, p->name) && (strlen(p->name) == line->len || + if (starts_with(line->buf, p->name) && (strlen(p->name) == line->len || line->buf[strlen(p->name)] == ' ')) { if (p->batchable) { batch_cmd = p; @@ -304,7 +304,7 @@ int main(int argc, char **argv) remote = remote_get(argv[1]); url_in = (argc == 3) ? argv[2] : remote->url[0]; - if (!prefixcmp(url_in, "file://")) { + if (starts_with(url_in, "file://")) { dump_from_file = 1; url = url_decode(url_in + sizeof("file://")-1); } else { @@ -76,7 +76,7 @@ static const char *alias_url(const char *url, struct rewrites *r) if (!r->rewrite[i]) continue; for (j = 0; j < r->rewrite[i]->instead_of_nr; j++) { - if (!prefixcmp(url, r->rewrite[i]->instead_of[j].s) && + if (starts_with(url, r->rewrite[i]->instead_of[j].s) && (!longest || longest->len < r->rewrite[i]->instead_of[j].len)) { longest = &(r->rewrite[i]->instead_of[j]); @@ -239,13 +239,13 @@ static void read_remotes_file(struct remote *remote) int value_list; char *s, *p; - if (!prefixcmp(buffer, "URL:")) { + if (starts_with(buffer, "URL:")) { value_list = 0; s = buffer + 4; - } else if (!prefixcmp(buffer, "Push:")) { + } else if (starts_with(buffer, "Push:")) { value_list = 1; s = buffer + 5; - } else if (!prefixcmp(buffer, "Pull:")) { + } else if (starts_with(buffer, "Pull:")) { value_list = 2; s = buffer + 5; } else @@ -337,7 +337,7 @@ static int handle_config(const char *key, const char *value, void *cb) const char *subkey; struct remote *remote; struct branch *branch; - if (!prefixcmp(key, "branch.")) { + if (starts_with(key, "branch.")) { name = key + 7; subkey = strrchr(name, '.'); if (!subkey) @@ -361,7 +361,7 @@ static int handle_config(const char *key, const char *value, void *cb) } return 0; } - if (!prefixcmp(key, "url.")) { + if (starts_with(key, "url.")) { struct rewrite *rewrite; name = key + 4; subkey = strrchr(name, '.'); @@ -380,7 +380,7 @@ static int handle_config(const char *key, const char *value, void *cb) } } - if (prefixcmp(key, "remote.")) + if (!starts_with(key, "remote.")) return 0; name = key + 7; @@ -487,7 +487,7 @@ static void read_config(void) current_branch = NULL; head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag); if (head_ref && (flag & REF_ISSYMREF) && - !prefixcmp(head_ref, "refs/heads/")) { + starts_with(head_ref, "refs/heads/")) { current_branch = make_branch(head_ref + strlen("refs/heads/"), 0); } @@ -745,35 +745,66 @@ int for_each_remote(each_remote_fn fn, void *priv) return result; } -void ref_remove_duplicates(struct ref *ref_map) +static void handle_duplicate(struct ref *ref1, struct ref *ref2) +{ + if (strcmp(ref1->name, ref2->name)) { + if (ref1->fetch_head_status != FETCH_HEAD_IGNORE && + ref2->fetch_head_status != FETCH_HEAD_IGNORE) { + die(_("Cannot fetch both %s and %s to %s"), + ref1->name, ref2->name, ref2->peer_ref->name); + } else if (ref1->fetch_head_status != FETCH_HEAD_IGNORE && + ref2->fetch_head_status == FETCH_HEAD_IGNORE) { + warning(_("%s usually tracks %s, not %s"), + ref2->peer_ref->name, ref2->name, ref1->name); + } else if (ref1->fetch_head_status == FETCH_HEAD_IGNORE && + ref2->fetch_head_status == FETCH_HEAD_IGNORE) { + die(_("%s tracks both %s and %s"), + ref2->peer_ref->name, ref1->name, ref2->name); + } else { + /* + * This last possibility doesn't occur because + * FETCH_HEAD_IGNORE entries always appear at + * the end of the list. + */ + die(_("Internal error")); + } + } + free(ref2->peer_ref); + free(ref2); +} + +struct ref *ref_remove_duplicates(struct ref *ref_map) { struct string_list refs = STRING_LIST_INIT_NODUP; - struct string_list_item *item = NULL; - struct ref *prev = NULL, *next = NULL; - for (; ref_map; prev = ref_map, ref_map = next) { - next = ref_map->next; - if (!ref_map->peer_ref) - continue; + struct ref *retval = NULL; + struct ref **p = &retval; - item = string_list_lookup(&refs, ref_map->peer_ref->name); - if (item) { - if (strcmp(((struct ref *)item->util)->name, - ref_map->name)) - die("%s tracks both %s and %s", - ref_map->peer_ref->name, - ((struct ref *)item->util)->name, - ref_map->name); - prev->next = ref_map->next; - free(ref_map->peer_ref); - free(ref_map); - ref_map = prev; /* skip this; we freed it */ - continue; - } + while (ref_map) { + struct ref *ref = ref_map; + + ref_map = ref_map->next; + ref->next = NULL; - item = string_list_insert(&refs, ref_map->peer_ref->name); - item->util = ref_map; + if (!ref->peer_ref) { + *p = ref; + p = &ref->next; + } else { + struct string_list_item *item = + string_list_insert(&refs, ref->peer_ref->name); + + if (item->util) { + /* Entry already existed */ + handle_duplicate((struct ref *)item->util, ref); + } else { + *p = ref; + p = &ref->next; + item->util = ref; + } + } } + string_list_clear(&refs, 0); + return retval; } int remote_has_url(struct remote *remote, const char *url) @@ -821,10 +852,12 @@ static int match_name_with_pattern(const char *key, const char *name, return ret; } -static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query) +int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query) { int i; int find_src = !query->src; + const char *needle = find_src ? query->dst : query->src; + char **result = find_src ? &query->src : &query->dst; if (find_src && !query->dst) return error("query_refspecs: need either src or dst"); @@ -833,8 +866,6 @@ static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *q struct refspec *refspec = &refs[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; - char **result = find_src ? &query->src : &query->dst; if (!refspec->dst) continue; @@ -955,9 +986,9 @@ void sort_ref_list(struct ref **l, int (*cmp)(const void *, const void *)) *l = llist_mergesort(*l, ref_list_get_next, ref_list_set_next, cmp); } -static int count_refspec_match(const char *pattern, - struct ref *refs, - struct ref **matched_ref) +int count_refspec_match(const char *pattern, + struct ref *refs, + struct ref **matched_ref) { int patlen = strlen(pattern); struct ref *matched_weak = NULL; @@ -982,8 +1013,8 @@ static int count_refspec_match(const char *pattern, */ if (namelen != patlen && patlen != namelen - 5 && - prefixcmp(name, "refs/heads/") && - prefixcmp(name, "refs/tags/")) { + !starts_with(name, "refs/heads/") && + !starts_with(name, "refs/tags/")) { /* We want to catch the case where only weak * matches are found and there are multiple * matches, and where more than one strong @@ -1054,9 +1085,9 @@ static char *guess_ref(const char *name, struct ref *peer) if (!r) return NULL; - if (!prefixcmp(r, "refs/heads/")) + if (starts_with(r, "refs/heads/")) strbuf_addstr(&buf, "refs/heads/"); - else if (!prefixcmp(r, "refs/tags/")) + else if (starts_with(r, "refs/tags/")) strbuf_addstr(&buf, "refs/tags/"); else return NULL; @@ -1104,7 +1135,7 @@ static int match_explicit(struct ref *src, struct ref *dst, dst_value = resolve_ref_unsafe(matched_src->name, sha1, 1, &flag); if (!dst_value || ((flag & REF_ISSYMREF) && - prefixcmp(dst_value, "refs/heads/"))) + !starts_with(dst_value, "refs/heads/"))) die("%s cannot be resolved to branch.", matched_src->name); } @@ -1193,7 +1224,7 @@ static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref * including refs outside refs/heads/ hierarchy, but * that does not make much sense these days. */ - if (!send_mirror && prefixcmp(ref->name, "refs/heads/")) + if (!send_mirror && !starts_with(ref->name, "refs/heads/")) return NULL; name = xstrdup(ref->name); } @@ -1248,7 +1279,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds add_to_tips(&sent_tips, ref->peer_ref->new_sha1); else add_to_tips(&sent_tips, ref->old_sha1); - if (!prefixcmp(ref->name, "refs/tags/")) + if (starts_with(ref->name, "refs/tags/")) string_list_append(&dst_tag, ref->name); } clear_commit_marks_many(sent_tips.nr, sent_tips.tip, TMP_MARK); @@ -1257,7 +1288,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds /* Collect tags they do not have. */ for (ref = src; ref; ref = ref->next) { - if (prefixcmp(ref->name, "refs/tags/")) + if (!starts_with(ref->name, "refs/tags/")) continue; /* not a tag */ if (string_list_has_string(&dst_tag, ref->name)) continue; /* they already have it */ @@ -1481,7 +1512,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, */ else if (!ref->deletion && !is_null_sha1(ref->old_sha1)) { - if (!prefixcmp(ref->name, "refs/tags/")) + if (starts_with(ref->name, "refs/tags/")) reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS; else if (!has_sha1_file(ref->old_sha1)) reject_reason = REF_STATUS_REJECT_FETCH_FIRST; @@ -1553,6 +1584,13 @@ static int ignore_symref_update(const char *refname) return (flag & REF_ISSYMREF); } +/* + * Create and return a list of (struct ref) consisting of copies of + * each remote_ref that matches refspec. refspec must be a pattern. + * Fill in the copies' peer_ref to describe the local tracking refs to + * which they map. Omit any references that would map to an existing + * local symbolic ref. + */ static struct ref *get_expanded_map(const struct ref *remote_refs, const struct refspec *refspec) { @@ -1560,9 +1598,9 @@ static struct ref *get_expanded_map(const struct ref *remote_refs, struct ref *ret = NULL; struct ref **tail = &ret; - char *expn_name; - for (ref = remote_refs; ref; ref = ref->next) { + char *expn_name = NULL; + if (strchr(ref->name, '^')) continue; /* a dereference item */ if (match_name_with_pattern(refspec->src, ref->name, @@ -1571,12 +1609,12 @@ static struct ref *get_expanded_map(const struct ref *remote_refs, struct ref *cpy = copy_ref(ref); cpy->peer_ref = alloc_ref(expn_name); - free(expn_name); if (refspec->force) cpy->peer_ref->force = 1; *tail = cpy; tail = &cpy->next; } + free(expn_name); } return ret; @@ -1607,12 +1645,12 @@ static struct ref *get_local_ref(const char *name) if (!name || name[0] == '\0') return NULL; - if (!prefixcmp(name, "refs/")) + if (starts_with(name, "refs/")) return alloc_ref(name); - if (!prefixcmp(name, "heads/") || - !prefixcmp(name, "tags/") || - !prefixcmp(name, "remotes/")) + if (starts_with(name, "heads/") || + starts_with(name, "tags/") || + starts_with(name, "remotes/")) return alloc_ref_with_prefix("refs/", 5, name); return alloc_ref_with_prefix("refs/heads/", 11, name); @@ -1647,7 +1685,7 @@ int get_fetch_map(const struct ref *remote_refs, for (rmp = &ref_map; *rmp; ) { if ((*rmp)->peer_ref) { - if (prefixcmp((*rmp)->peer_ref->name, "refs/") || + if (!starts_with((*rmp)->peer_ref->name, "refs/") || check_refname_format((*rmp)->peer_ref->name, 0)) { struct ref *ignore = *rmp; error("* Ignoring funny ref '%s' locally", @@ -1931,7 +1969,7 @@ struct ref *guess_remote_head(const struct ref *head, /* Look for another ref that points there */ for (r = refs; r; r = r->next) { if (r != head && - !prefixcmp(r->name, "refs/heads/") && + starts_with(r->name, "refs/heads/") && !hashcmp(r->old_sha1, head->old_sha1)) { *tail = copy_ref(r); tail = &((*tail)->next); @@ -128,6 +128,7 @@ struct ref *alloc_ref(const char *name); struct ref *copy_ref(const struct ref *ref); struct ref *copy_ref_list(const struct ref *ref); void sort_ref_list(struct ref **, int (*cmp)(const void *, const void *)); +extern int count_refspec_match(const char *, struct ref *refs, struct ref **matched_ref); int ref_compare_name(const void *, const void *); int check_ref_type(const struct ref *ref, int flags); @@ -149,15 +150,20 @@ int resolve_remote_symref(struct ref *ref, struct ref *list); int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1); /* - * Removes and frees any duplicate refs in the map. + * Remove and free all but the first of any entries in the input list + * that map the same remote reference to the same local reference. If + * there are two entries that map different remote references to the + * same local reference, emit an error message and die. Return a + * pointer to the head of the resulting list. */ -void ref_remove_duplicates(struct ref *ref_map); +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); 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); diff --git a/replace_object.c b/replace_object.c index d0b1548726..cdcaf8cbe2 100644 --- a/replace_object.c +++ b/replace_object.c @@ -97,9 +97,6 @@ const unsigned char *do_lookup_replace_object(const unsigned char *sha1) int pos, depth = MAXREPLACEDEPTH; const unsigned char *cur = sha1; - if (!read_replace_refs) - return sha1; - prepare_replace_object(); /* Try to recursively replace the object */ diff --git a/revision.c b/revision.c index a8adb3fc88..a68fde6e95 100644 --- a/revision.c +++ b/revision.c @@ -1180,11 +1180,28 @@ struct all_refs_cb { const char *name_for_errormsg; }; +int ref_excluded(struct string_list *ref_excludes, const char *path) +{ + struct string_list_item *item; + + if (!ref_excludes) + return 0; + for_each_string_list_item(item, ref_excludes) { + if (!fnmatch(item->string, path, 0)) + return 1; + } + return 0; +} + static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data) { struct all_refs_cb *cb = cb_data; - struct object *object = get_reference(cb->all_revs, path, sha1, - cb->all_flags); + struct object *object; + + if (ref_excluded(cb->all_revs->ref_excludes, path)) + return 0; + + object = get_reference(cb->all_revs, path, sha1, cb->all_flags); add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags); add_pending_sha1(cb->all_revs, path, sha1, cb->all_flags); return 0; @@ -1197,6 +1214,24 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs, cb->all_flags = flags; } +void clear_ref_exclusion(struct string_list **ref_excludes_p) +{ + if (*ref_excludes_p) { + string_list_clear(*ref_excludes_p, 0); + free(*ref_excludes_p); + } + *ref_excludes_p = NULL; +} + +void add_ref_exclusion(struct string_list **ref_excludes_p, const char *exclude) +{ + if (!*ref_excludes_p) { + *ref_excludes_p = xcalloc(1, sizeof(**ref_excludes_p)); + (*ref_excludes_p)->strdup_strings = 1; + } + string_list_append(*ref_excludes_p, exclude); +} + static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags, int (*for_each)(const char *, each_ref_fn, void *)) { @@ -1592,9 +1627,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg !strcmp(arg, "--tags") || !strcmp(arg, "--remotes") || !strcmp(arg, "--reflog") || !strcmp(arg, "--not") || !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") || - !strcmp(arg, "--bisect") || !prefixcmp(arg, "--glob=") || - !prefixcmp(arg, "--branches=") || !prefixcmp(arg, "--tags=") || - !prefixcmp(arg, "--remotes=") || !prefixcmp(arg, "--no-walk=")) + !strcmp(arg, "--bisect") || starts_with(arg, "--glob=") || + starts_with(arg, "--branches=") || starts_with(arg, "--tags=") || + starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk=")) { unkv[(*unkc)++] = arg; return 1; @@ -1617,7 +1652,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->max_count = atoi(argv[1]); revs->no_walk = 0; return 2; - } else if (!prefixcmp(arg, "-n")) { + } else if (starts_with(arg, "-n")) { revs->max_count = atoi(arg + 2); revs->no_walk = 0; } else if ((argcount = parse_long_opt("max-age", argv, &optarg))) { @@ -1677,7 +1712,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } else if (!strcmp(arg, "--author-date-order")) { revs->sort_order = REV_SORT_BY_AUTHOR_DATE; revs->topo_order = 1; - } else if (!prefixcmp(arg, "--early-output")) { + } else if (starts_with(arg, "--early-output")) { int count = 100; switch (arg[14]) { case '=': @@ -1702,13 +1737,13 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->min_parents = 2; } else if (!strcmp(arg, "--no-merges")) { revs->max_parents = 1; - } else if (!prefixcmp(arg, "--min-parents=")) { + } else if (starts_with(arg, "--min-parents=")) { revs->min_parents = atoi(arg+14); - } else if (!prefixcmp(arg, "--no-min-parents")) { + } else if (starts_with(arg, "--no-min-parents")) { revs->min_parents = 0; - } else if (!prefixcmp(arg, "--max-parents=")) { + } else if (starts_with(arg, "--max-parents=")) { revs->max_parents = atoi(arg+14); - } else if (!prefixcmp(arg, "--no-max-parents")) { + } else if (starts_with(arg, "--no-max-parents")) { revs->max_parents = -1; } else if (!strcmp(arg, "--boundary")) { revs->boundary = 1; @@ -1758,7 +1793,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->verify_objects = 1; } else if (!strcmp(arg, "--unpacked")) { revs->unpacked = 1; - } else if (!prefixcmp(arg, "--unpacked=")) { + } else if (starts_with(arg, "--unpacked=")) { die("--unpacked=<packfile> no longer supported."); } else if (!strcmp(arg, "-r")) { revs->diff = 1; @@ -1783,7 +1818,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->verbose_header = 1; revs->pretty_given = 1; get_commit_format(arg+8, revs); - } else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) { + } else if (starts_with(arg, "--pretty=") || starts_with(arg, "--format=")) { /* * Detached form ("--pretty X" as opposed to "--pretty=X") * not allowed, since the argument is optional. @@ -1797,12 +1832,12 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->notes_opt.use_default_notes = 1; } else if (!strcmp(arg, "--show-signature")) { revs->show_signature = 1; - } else if (!prefixcmp(arg, "--show-notes=") || - !prefixcmp(arg, "--notes=")) { + } else if (starts_with(arg, "--show-notes=") || + starts_with(arg, "--notes=")) { struct strbuf buf = STRBUF_INIT; revs->show_notes = 1; revs->show_notes_given = 1; - if (!prefixcmp(arg, "--show-notes")) { + if (starts_with(arg, "--show-notes")) { if (revs->notes_opt.use_default_notes < 0) revs->notes_opt.use_default_notes = 1; strbuf_addstr(&buf, arg+13); @@ -1845,7 +1880,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->abbrev = 0; } else if (!strcmp(arg, "--abbrev")) { revs->abbrev = DEFAULT_ABBREV; - } else if (!prefixcmp(arg, "--abbrev=")) { + } else if (starts_with(arg, "--abbrev=")) { revs->abbrev = strtoul(arg + 9, NULL, 10); if (revs->abbrev < MINIMUM_ABBREV) revs->abbrev = MINIMUM_ABBREV; @@ -1969,40 +2004,51 @@ static int handle_revision_pseudo_opt(const char *submodule, if (!strcmp(arg, "--all")) { handle_refs(submodule, revs, *flags, for_each_ref_submodule); handle_refs(submodule, revs, *flags, head_ref_submodule); + clear_ref_exclusion(&revs->ref_excludes); } else if (!strcmp(arg, "--branches")) { handle_refs(submodule, revs, *flags, for_each_branch_ref_submodule); + clear_ref_exclusion(&revs->ref_excludes); } else if (!strcmp(arg, "--bisect")) { handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref); handle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref); revs->bisect = 1; } else if (!strcmp(arg, "--tags")) { handle_refs(submodule, revs, *flags, for_each_tag_ref_submodule); + clear_ref_exclusion(&revs->ref_excludes); } else if (!strcmp(arg, "--remotes")) { handle_refs(submodule, revs, *flags, for_each_remote_ref_submodule); + clear_ref_exclusion(&revs->ref_excludes); } else if ((argcount = parse_long_opt("glob", argv, &optarg))) { struct all_refs_cb cb; init_all_refs_cb(&cb, revs, *flags); for_each_glob_ref(handle_one_ref, optarg, &cb); + clear_ref_exclusion(&revs->ref_excludes); + return argcount; + } else if ((argcount = parse_long_opt("exclude", argv, &optarg))) { + add_ref_exclusion(&revs->ref_excludes, optarg); return argcount; - } else if (!prefixcmp(arg, "--branches=")) { + } else if (starts_with(arg, "--branches=")) { struct all_refs_cb cb; init_all_refs_cb(&cb, revs, *flags); for_each_glob_ref_in(handle_one_ref, arg + 11, "refs/heads/", &cb); - } else if (!prefixcmp(arg, "--tags=")) { + clear_ref_exclusion(&revs->ref_excludes); + } else if (starts_with(arg, "--tags=")) { struct all_refs_cb cb; init_all_refs_cb(&cb, revs, *flags); for_each_glob_ref_in(handle_one_ref, arg + 7, "refs/tags/", &cb); - } else if (!prefixcmp(arg, "--remotes=")) { + clear_ref_exclusion(&revs->ref_excludes); + } else if (starts_with(arg, "--remotes=")) { struct all_refs_cb cb; init_all_refs_cb(&cb, revs, *flags); for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb); + clear_ref_exclusion(&revs->ref_excludes); } else if (!strcmp(arg, "--reflog")) { handle_reflog(revs, *flags); } else if (!strcmp(arg, "--not")) { *flags ^= UNINTERESTING | BOTTOM; } else if (!strcmp(arg, "--no-walk")) { revs->no_walk = REVISION_WALK_NO_WALK_SORTED; - } else if (!prefixcmp(arg, "--no-walk=")) { + } else if (starts_with(arg, "--no-walk=")) { /* * Detached form ("--no-walk X" as opposed to "--no-walk=X") * not allowed, since the argument is optional. diff --git a/revision.h b/revision.h index 89132df2fa..88967d6a24 100644 --- a/revision.h +++ b/revision.h @@ -61,6 +61,9 @@ struct rev_info { /* The end-points specified by the end user */ struct rev_cmdline_info cmdline; + /* excluding from --branches, --refs, etc. expansion */ + struct string_list *ref_excludes; + /* Basic information */ const char *prefix; const char *def; @@ -194,6 +197,11 @@ struct rev_info { struct saved_parents *saved_parents_slab; }; +extern int ref_excluded(struct string_list *, const char *path); +void clear_ref_exclusion(struct string_list **); +void add_ref_exclusion(struct string_list **, const char *exclude); + + #define REV_TREE_SAME 0 #define REV_TREE_NEW 1 /* Only new files */ #define REV_TREE_OLD 2 /* Only files removed */ diff --git a/send-pack.c b/send-pack.c index fab62e3da0..ac14a4d090 100644 --- a/send-pack.c +++ b/send-pack.c @@ -109,7 +109,7 @@ static int receive_status(int in, struct ref *refs) struct ref *hint; int ret = 0; char *line = packet_read_line(in, NULL); - if (prefixcmp(line, "unpack ")) + if (!starts_with(line, "unpack ")) return error("did not receive remote status"); if (strcmp(line, "unpack ok")) { error("unpack failed: %s", line + 7); @@ -122,7 +122,7 @@ static int receive_status(int in, struct ref *refs) line = packet_read_line(in, NULL); if (!line) break; - if (prefixcmp(line, "ok ") && prefixcmp(line, "ng ")) { + if (!starts_with(line, "ok ") && !starts_with(line, "ng ")) { error("invalid ref status from remote: %s", line); ret = -1; break; @@ -206,6 +206,8 @@ int send_pack(struct send_pack_args *args, quiet_supported = 1; if (server_supports("agent")) agent_supported = 1; + if (server_supports("no-thin")) + args->use_thin_pack = 0; if (!remote_refs) { fprintf(stderr, "No refs in common and none specified; doing nothing.\n" diff --git a/sequencer.c b/sequencer.c index 06e52b4c83..90cac7b02e 100644 --- a/sequencer.c +++ b/sequencer.c @@ -41,7 +41,7 @@ static int is_cherry_picked_from_line(const char *buf, int len) * We only care that it looks roughly like (cherry picked from ...) */ return len > strlen(cherry_picked_prefix) + 1 && - !prefixcmp(buf, cherry_picked_prefix) && buf[len - 1] == ')'; + starts_with(buf, cherry_picked_prefix) && buf[len - 1] == ')'; } /* @@ -180,7 +180,7 @@ static char *get_encoding(const char *message) while (*p && *p != '\n') { for (eol = p + 1; *eol && *eol != '\n'; eol++) ; /* do nothing */ - if (!prefixcmp(p, "encoding ")) { + if (starts_with(p, "encoding ")) { char *result = xmalloc(eol - 8 - p); strlcpy(result, p + 9, eol - 8 - p); return result; @@ -705,10 +705,10 @@ static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts * char *end_of_object_name; int saved, status, padding; - if (!prefixcmp(bol, "pick")) { + if (starts_with(bol, "pick")) { action = REPLAY_PICK; bol += strlen("pick"); - } else if (!prefixcmp(bol, "revert")) { + } else if (starts_with(bol, "revert")) { action = REPLAY_REVERT; bol += strlen("revert"); } else @@ -82,7 +82,7 @@ int check_filename(const char *prefix, const char *arg) const char *name; struct stat st; - if (!prefixcmp(arg, ":/")) { + if (starts_with(arg, ":/")) { if (arg[2] == '\0') /* ":/" is root dir, always exists */ return 1; name = arg + 2; @@ -304,7 +304,7 @@ const char *read_gitfile(const char *path) if (len != st.st_size) die("Error reading %s", path); buf[len] = '\0'; - if (prefixcmp(buf, "gitdir: ")) + if (!starts_with(buf, "gitdir: ")) die("Invalid gitfile format: %s", path); while (buf[len - 1] == '\n' || buf[len - 1] == '\r') len--; diff --git a/sha1_file.c b/sha1_file.c index 760dd60031..e13bd2c3ee 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -807,15 +807,38 @@ void free_pack_by_name(const char *pack_name) static unsigned int get_max_fd_limit(void) { #ifdef RLIMIT_NOFILE - struct rlimit lim; + { + struct rlimit lim; - if (getrlimit(RLIMIT_NOFILE, &lim)) - die_errno("cannot get RLIMIT_NOFILE"); + if (!getrlimit(RLIMIT_NOFILE, &lim)) + return lim.rlim_cur; + } +#endif + +#ifdef _SC_OPEN_MAX + { + long open_max = sysconf(_SC_OPEN_MAX); + if (0 < open_max) + return open_max; + /* + * Otherwise, we got -1 for one of the two + * reasons: + * + * (1) sysconf() did not understand _SC_OPEN_MAX + * and signaled an error with -1; or + * (2) sysconf() said there is no limit. + * + * We _could_ clear errno before calling sysconf() to + * tell these two cases apart and return a huge number + * in the latter case to let the caller cap it to a + * value that is not so selfish, but letting the + * fallback OPEN_MAX codepath take care of these cases + * is a lot simpler. + */ + } +#endif - return lim.rlim_cur; -#elif defined(_SC_OPEN_MAX) - return sysconf(_SC_OPEN_MAX); -#elif defined(OPEN_MAX) +#ifdef OPEN_MAX return OPEN_MAX; #else return 1; /* see the caller ;-) */ @@ -1442,51 +1465,6 @@ void *map_sha1_file(const unsigned char *sha1, unsigned long *size) return map; } -/* - * There used to be a second loose object header format which - * was meant to mimic the in-pack format, allowing for direct - * copy of the object data. This format turned up not to be - * really worth it and we no longer write loose objects in that - * format. - */ -static int experimental_loose_object(unsigned char *map) -{ - unsigned int word; - - /* - * We must determine if the buffer contains the standard - * zlib-deflated stream or the experimental format based - * on the in-pack object format. Compare the header byte - * for each format: - * - * RFC1950 zlib w/ deflate : 0www1000 : 0 <= www <= 7 - * Experimental pack-based : Stttssss : ttt = 1,2,3,4 - * - * If bit 7 is clear and bits 0-3 equal 8, the buffer MUST be - * in standard loose-object format, UNLESS it is a Git-pack - * format object *exactly* 8 bytes in size when inflated. - * - * However, RFC1950 also specifies that the 1st 16-bit word - * must be divisible by 31 - this checksum tells us our buffer - * is in the standard format, giving a false positive only if - * the 1st word of the Git-pack format object happens to be - * divisible by 31, ie: - * ((byte0 * 256) + byte1) % 31 = 0 - * => 0ttt10000www1000 % 31 = 0 - * - * As it happens, this case can only arise for www=3 & ttt=1 - * - ie, a Commit object, which would have to be 8 bytes in - * size. As no Commit can be that small, we find that the - * combination of these two criteria (bitmask & checksum) - * can always correctly determine the buffer format. - */ - word = (map[0] << 8) + map[1]; - if ((map[0] & 0x8F) == 0x08 && !(word % 31)) - return 0; - else - return 1; -} - unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep) { @@ -1514,14 +1492,6 @@ unsigned long unpack_object_header_buffer(const unsigned char *buf, int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz) { - unsigned long size, used; - static const char valid_loose_object_type[8] = { - 0, /* OBJ_EXT */ - 1, 1, 1, 1, /* "commit", "tree", "blob", "tag" */ - 0, /* "delta" and others are invalid in a loose object */ - }; - enum object_type type; - /* Get the data stream */ memset(stream, 0, sizeof(*stream)); stream->next_in = map; @@ -1529,27 +1499,6 @@ int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long ma stream->next_out = buffer; stream->avail_out = bufsiz; - if (experimental_loose_object(map)) { - /* - * The old experimental format we no longer produce; - * we can still read it. - */ - used = unpack_object_header_buffer(map, mapsize, &type, &size); - if (!used || !valid_loose_object_type[type]) - return -1; - map += used; - mapsize -= used; - - /* Set up the stream for the rest.. */ - stream->next_in = map; - stream->avail_in = mapsize; - git_inflate_init(stream); - - /* And generate the fake traditional header */ - stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu", - typename(type), size); - return 0; - } git_inflate_init(stream); return git_inflate(stream, 0); } @@ -1741,6 +1690,38 @@ static off_t get_delta_base(struct packed_git *p, return base_offset; } +/* + * Like get_delta_base above, but we return the sha1 instead of the pack + * offset. This means it is cheaper for REF deltas (we do not have to do + * the final object lookup), but more expensive for OFS deltas (we + * have to load the revidx to convert the offset back into a sha1). + */ +static const unsigned char *get_delta_base_sha1(struct packed_git *p, + struct pack_window **w_curs, + off_t curpos, + enum object_type type, + off_t delta_obj_offset) +{ + if (type == OBJ_REF_DELTA) { + unsigned char *base = use_pack(p, w_curs, curpos, NULL); + return base; + } else if (type == OBJ_OFS_DELTA) { + struct revindex_entry *revidx; + off_t base_offset = get_delta_base(p, w_curs, &curpos, + type, delta_obj_offset); + + if (!base_offset) + return NULL; + + revidx = find_pack_revindex(p, base_offset); + if (!revidx) + return NULL; + + return nth_packed_object_sha1(p, revidx->nr); + } else + return NULL; +} + int unpack_object_header(struct packed_git *p, struct pack_window **w_curs, off_t *curpos, @@ -1898,6 +1879,22 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset, } } + if (oi->delta_base_sha1) { + if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) { + const unsigned char *base; + + base = get_delta_base_sha1(p, &w_curs, curpos, + type, obj_offset); + if (!base) { + type = OBJ_BAD; + goto out; + } + + hashcpy(oi->delta_base_sha1, base); + } else + hashclr(oi->delta_base_sha1); + } + out: unuse_pack(&w_curs); return type; @@ -2481,6 +2478,9 @@ static int sha1_loose_object_info(const unsigned char *sha1, git_zstream stream; char hdr[32]; + if (oi->delta_base_sha1) + hashclr(oi->delta_base_sha1); + /* * If we don't care about type or size, then we don't * need to look inside the object at all. Note that we @@ -2517,13 +2517,14 @@ static int sha1_loose_object_info(const unsigned char *sha1, return 0; } -int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi) +int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi, unsigned flags) { struct cached_object *co; struct pack_entry e; int rtype; + const unsigned char *real = lookup_replace_object_extended(sha1, flags); - co = find_cached_object(sha1); + co = find_cached_object(real); if (co) { if (oi->typep) *(oi->typep) = co->type; @@ -2531,27 +2532,29 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi) *(oi->sizep) = co->size; if (oi->disk_sizep) *(oi->disk_sizep) = 0; + if (oi->delta_base_sha1) + hashclr(oi->delta_base_sha1); oi->whence = OI_CACHED; return 0; } - if (!find_pack_entry(sha1, &e)) { + if (!find_pack_entry(real, &e)) { /* Most likely it's a loose object. */ - if (!sha1_loose_object_info(sha1, oi)) { + if (!sha1_loose_object_info(real, oi)) { oi->whence = OI_LOOSE; return 0; } /* Not a loose object; someone else may have just packed it. */ reprepare_packed_git(); - if (!find_pack_entry(sha1, &e)) + if (!find_pack_entry(real, &e)) return -1; } rtype = packed_object_info(e.p, e.offset, oi); if (rtype < 0) { - mark_bad_packed_object(e.p, sha1); - return sha1_object_info_extended(sha1, oi); + mark_bad_packed_object(e.p, real); + return sha1_object_info_extended(real, oi, 0); } else if (in_delta_base_cache(e.p, e.offset)) { oi->whence = OI_DBCACHED; } else { @@ -2573,7 +2576,7 @@ int sha1_object_info(const unsigned char *sha1, unsigned long *sizep) oi.typep = &type; oi.sizep = sizep; - if (sha1_object_info_extended(sha1, &oi) < 0) + if (sha1_object_info_extended(sha1, &oi, LOOKUP_REPLACE_OBJECT) < 0) return -1; return type; } @@ -2665,8 +2668,7 @@ void *read_sha1_file_extended(const unsigned char *sha1, void *data; char *path; const struct packed_git *p; - const unsigned char *repl = (flag & READ_SHA1_FILE_REPLACE) - ? lookup_replace_object(sha1) : sha1; + const unsigned char *repl = lookup_replace_object_extended(sha1, flag); errno = 0; data = read_object(repl, type, size); diff --git a/sha1_name.c b/sha1_name.c index e9c299943b..b1873d8113 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -546,7 +546,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) if (read_ref_at(real_ref, at_time, nth, sha1, NULL, &co_time, &co_tz, &co_cnt)) { if (!len) { - if (!prefixcmp(real_ref, "refs/heads/")) { + if (starts_with(real_ref, "refs/heads/")) { str = real_ref + 11; len = strlen(real_ref + 11); } else { @@ -581,8 +581,6 @@ static int get_parent(const char *name, int len, if (ret) return ret; commit = lookup_commit_reference(sha1); - if (!commit) - return -1; if (parse_commit(commit)) return -1; if (!idx) { @@ -676,15 +674,15 @@ static int peel_onion(const char *name, int len, unsigned char *sha1) return -1; sp++; /* beginning of type name, or closing brace for empty */ - if (!prefixcmp(sp, "commit}")) + if (starts_with(sp, "commit}")) expected_type = OBJ_COMMIT; - else if (!prefixcmp(sp, "tag}")) + else if (starts_with(sp, "tag}")) expected_type = OBJ_TAG; - else if (!prefixcmp(sp, "tree}")) + else if (starts_with(sp, "tree}")) expected_type = OBJ_TREE; - else if (!prefixcmp(sp, "blob}")) + else if (starts_with(sp, "blob}")) expected_type = OBJ_BLOB; - else if (!prefixcmp(sp, "object}")) + else if (starts_with(sp, "object}")) expected_type = OBJ_ANY; else if (sp[0] == '}') expected_type = OBJ_NONE; @@ -911,7 +909,7 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1, const char *match = NULL, *target = NULL; size_t len; - if (!prefixcmp(message, "checkout: moving from ")) { + if (starts_with(message, "checkout: moving from ")) { match = message + strlen("checkout: moving from "); target = strstr(match, " to "); } @@ -1304,7 +1302,7 @@ static void diagnose_invalid_index_path(int stage, static char *resolve_relative_path(const char *rel) { - if (prefixcmp(rel, "./") && prefixcmp(rel, "../")) + if (!starts_with(rel, "./") && !starts_with(rel, "../")) return NULL; if (!startup_info) @@ -90,8 +90,7 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth, cur_depth = *(int *)commit->util; } } - if (parse_commit(commit)) - die("invalid commit"); + parse_commit_or_die(commit); cur_depth++; if (cur_depth >= depth) { commit_list_insert(commit, &result); @@ -15,7 +15,7 @@ static int do_generic_cmd(const char *me, char *arg) setup_path(); if (!arg || !(arg = sq_dequote(arg))) die("bad argument"); - if (prefixcmp(me, "git-")) + if (!starts_with(me, "git-")) die("bad command"); my_argv[0] = me + 4; @@ -1,6 +1,15 @@ #include "cache.h" #include "refs.h" +int starts_with(const char *str, const char *prefix) +{ + for (; ; str++, prefix++) + if (!*prefix) + return 1; + else if (*str != *prefix) + return 0; +} + int prefixcmp(const char *str, const char *prefix) { for (; ; str++, prefix++) @@ -10,6 +19,15 @@ int prefixcmp(const char *str, const char *prefix) return (unsigned char)*prefix - (unsigned char)*str; } +int ends_with(const char *str, const char *suffix) +{ + int len = strlen(str), suflen = strlen(suffix); + if (len < suflen) + return 0; + else + return !strcmp(str + len - suflen, suffix); +} + int suffixcmp(const char *str, const char *suffix) { int len = strlen(str), suflen = strlen(suffix); diff --git a/streaming.c b/streaming.c index debe904523..9659f18be2 100644 --- a/streaming.c +++ b/streaming.c @@ -113,7 +113,7 @@ static enum input_source istream_source(const unsigned char *sha1, oi->typep = type; oi->sizep = &size; - status = sha1_object_info_extended(sha1, oi); + status = sha1_object_info_extended(sha1, oi, 0); if (status < 0) return stream_error; diff --git a/submodule.c b/submodule.c index 1905d75b2b..613857e400 100644 --- a/submodule.c +++ b/submodule.c @@ -201,7 +201,7 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt, int submodule_config(const char *var, const char *value, void *cb) { - if (!prefixcmp(var, "submodule.")) + if (starts_with(var, "submodule.")) return parse_submodule_config_option(var, value); else if (!strcmp(var, "fetch.recursesubmodules")) { config_fetch_recurse_submodules = parse_fetch_recurse_submodules_arg(var, value); diff --git a/t/Git-SVN/00compile.t b/t/Git-SVN/00compile.t index c92fee453f..c92fee453f 100644..100755 --- a/t/Git-SVN/00compile.t +++ b/t/Git-SVN/00compile.t diff --git a/t/Git-SVN/Utils/add_path_to_url.t b/t/Git-SVN/Utils/add_path_to_url.t index bfbd87845f..bfbd87845f 100644..100755 --- a/t/Git-SVN/Utils/add_path_to_url.t +++ b/t/Git-SVN/Utils/add_path_to_url.t diff --git a/t/Git-SVN/Utils/can_compress.t b/t/Git-SVN/Utils/can_compress.t index d7b49b8d54..d7b49b8d54 100644..100755 --- a/t/Git-SVN/Utils/can_compress.t +++ b/t/Git-SVN/Utils/can_compress.t diff --git a/t/Git-SVN/Utils/canonicalize_url.t b/t/Git-SVN/Utils/canonicalize_url.t index 05795ab636..05795ab636 100644..100755 --- a/t/Git-SVN/Utils/canonicalize_url.t +++ b/t/Git-SVN/Utils/canonicalize_url.t diff --git a/t/Git-SVN/Utils/collapse_dotdot.t b/t/Git-SVN/Utils/collapse_dotdot.t index 1da1cce156..1da1cce156 100644..100755 --- a/t/Git-SVN/Utils/collapse_dotdot.t +++ b/t/Git-SVN/Utils/collapse_dotdot.t diff --git a/t/Git-SVN/Utils/fatal.t b/t/Git-SVN/Utils/fatal.t index 49e1438295..49e1438295 100644..100755 --- a/t/Git-SVN/Utils/fatal.t +++ b/t/Git-SVN/Utils/fatal.t diff --git a/t/Git-SVN/Utils/join_paths.t b/t/Git-SVN/Utils/join_paths.t index d4488e7162..d4488e7162 100644..100755 --- a/t/Git-SVN/Utils/join_paths.t +++ b/t/Git-SVN/Utils/join_paths.t diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh index c9d105d707..304c7b7d87 100644 --- a/t/annotate-tests.sh +++ b/t/annotate-tests.sh @@ -116,6 +116,27 @@ test_expect_success 'blame evil merge' ' check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1 ' +test_expect_success 'blame huge graft' ' + test_when_finished "git checkout branch2" && + test_when_finished "rm -f .git/info/grafts" && + graft= && + for i in 0 1 2 + do + for j in 0 1 2 3 4 5 6 7 8 9 + do + git checkout --orphan "$i$j" && + printf "%s\n" "$i" "$j" >file && + test_tick && + GIT_AUTHOR_NAME=$i$j GIT_AUTHOR_EMAIL=$i$j@test.git \ + git commit -a -m "$i$j" && + commit=$(git rev-parse --verify HEAD) && + graft="$graft$commit " + done + done && + printf "%s " $graft >.git/info/grafts && + check_count -h 00 01 1 10 1 +' + test_expect_success 'setup incomplete line' ' echo "incomplete" | tr -d "\\012" >>file && GIT_AUTHOR_NAME="C" GIT_AUTHOR_EMAIL="C@test.git" \ diff --git a/t/gitweb-lib.sh b/t/gitweb-lib.sh index 8cf909a6c5..d5dab5a94f 100644 --- a/t/gitweb-lib.sh +++ b/t/gitweb-lib.sh @@ -1,4 +1,5 @@ -#!/bin/sh +# Initialization and helpers for Gitweb tests, which source this +# shell library instead of test-lib.sh. # # Copyright (c) 2007 Jakub Narebski # diff --git a/t/lib-bash.sh b/t/lib-bash.sh index 11397f747b..2be955fafb 100644 --- a/t/lib-bash.sh +++ b/t/lib-bash.sh @@ -1,7 +1,6 @@ -#!/bin/sh -# -# Ensures that tests are run under Bash; primarily intended for running tests -# of the completion script. +# Shell library sourced instead of ./test-lib.sh by tests that need +# to run under Bash; primarily intended for tests of the completion +# script. if test -n "$BASH" && test -z "$POSIXLY_CORRECT"; then # we are in full-on bash mode diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh index 44263ade25..5076718916 100644 --- a/t/lib-cvs.sh +++ b/t/lib-cvs.sh @@ -1,4 +1,4 @@ -#!/bin/sh +# Shell library sourced instead of ./test-lib.sh by cvsimport tests. . ./test-lib.sh diff --git a/t/lib-diff-alternative.sh b/t/lib-diff-alternative.sh index 75ffd9174f..8b4dbf22d2 100644 --- a/t/lib-diff-alternative.sh +++ b/t/lib-diff-alternative.sh @@ -1,4 +1,5 @@ -#!/bin/sh +# Helpers shared by the test scripts for diff algorithms (patience, +# histogram, etc). test_diff_frobnitz() { cat >file1 <<\EOF diff --git a/t/lib-gettext.sh b/t/lib-gettext.sh index ae8883a075..eec757f104 100644 --- a/t/lib-gettext.sh +++ b/t/lib-gettext.sh @@ -1,4 +1,5 @@ -#!/bin/sh +# Initialization and Icelandic locale for basic git i18n tests, +# which source this scriptlet instead of ./test-lib.sh. # # Copyright (c) 2010 Ævar Arnfjörð Bjarmason # diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh index 87f0ad8f41..394b06b32f 100644 --- a/t/lib-git-daemon.sh +++ b/t/lib-git-daemon.sh @@ -1,4 +1,20 @@ -#!/bin/sh +# Shell library to run git-daemon in tests. Ends the test early if +# GIT_TEST_GIT_DAEMON is not set. +# +# Usage: +# +# . ./test-lib.sh +# . "$TEST_DIRECTORY"/lib-git-daemon.sh +# start_git_daemon +# +# test_expect_success '...' ' +# ... +# ' +# +# test_expect_success ... +# +# stop_git_daemon +# test_done if test -z "$GIT_TEST_GIT_DAEMON" then diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index ad8f1ef71e..bfdff2a8c9 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -1,4 +1,31 @@ -#!/bin/sh +# Shell library to run an HTTP server for use in tests. +# Ends the test early if httpd tests should not be run, +# for example because the user has not enabled them. +# +# Usage: +# +# . ./test-lib.sh +# . "$TEST_DIRECTORY"/lib-httpd.sh +# start_httpd +# +# test_expect_success '...' ' +# ... +# ' +# +# test_expect_success ... +# +# stop_httpd +# test_done +# +# Can be configured using the following variables. +# +# GIT_TEST_HTTPD enable HTTPD tests +# LIB_HTTPD_PATH web server path +# LIB_HTTPD_MODULE_PATH web server modules path +# LIB_HTTPD_PORT listening port +# LIB_HTTPD_DAV enable DAV +# LIB_HTTPD_SVN enable SVN +# LIB_HTTPD_SSL enable SSL # # Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at> # @@ -102,7 +129,7 @@ prepare_httpd() { HTTPD_DEST=127.0.0.1:$LIB_HTTPD_PORT HTTPD_URL=$HTTPD_PROTO://$HTTPD_DEST HTTPD_URL_USER=$HTTPD_PROTO://user%40host@$HTTPD_DEST - HTTPD_URL_USER_PASS=$HTTPD_PROTO://user%40host:user%40host@$HTTPD_DEST + HTTPD_URL_USER_PASS=$HTTPD_PROTO://user%40host:pass%40host@$HTTPD_DEST if test -n "$LIB_HTTPD_DAV" -o -n "$LIB_HTTPD_SVN" then @@ -190,7 +217,15 @@ setup_askpass_helper() { test_expect_success 'setup askpass helper' ' write_script "$TRASH_DIRECTORY/askpass" <<-\EOF && echo >>"$TRASH_DIRECTORY/askpass-query" "askpass: $*" && - cat "$TRASH_DIRECTORY/askpass-response" + case "$*" in + *Username*) + what=user + ;; + *Password*) + what=pass + ;; + esac && + cat "$TRASH_DIRECTORY/askpass-$what" EOF GIT_ASKPASS="$TRASH_DIRECTORY/askpass" && export GIT_ASKPASS && @@ -200,7 +235,8 @@ setup_askpass_helper() { set_askpass() { >"$TRASH_DIRECTORY/askpass-query" && - echo "$*" >"$TRASH_DIRECTORY/askpass-response" + echo "$1" >"$TRASH_DIRECTORY/askpass-user" && + echo "$2" >"$TRASH_DIRECTORY/askpass-pass" } expect_askpass() { diff --git a/t/lib-httpd/passwd b/t/lib-httpd/passwd index f2fbcad33e..99a34d6487 100644 --- a/t/lib-httpd/passwd +++ b/t/lib-httpd/passwd @@ -1 +1 @@ -user@host:nKpa8pZUHx/ic +user@host:xb4E8pqD81KQs diff --git a/t/lib-pack.sh b/t/lib-pack.sh index b96e1254dd..7509846571 100644 --- a/t/lib-pack.sh +++ b/t/lib-pack.sh @@ -1,5 +1,3 @@ -#!/bin/sh -# # Support routines for hand-crafting weird or malicious packs. # # You can make a complete pack like: diff --git a/t/lib-pager.sh b/t/lib-pager.sh index ba03eab14f..3aa7a3ffd8 100644 --- a/t/lib-pager.sh +++ b/t/lib-pager.sh @@ -1,4 +1,4 @@ -#!/bin/sh +# Helpers for tests of git's choice of pager. test_expect_success 'determine default pager' ' test_might_fail git config --unset core.pager && diff --git a/t/lib-prereq-FILEMODE.sh b/t/lib-prereq-FILEMODE.sh deleted file mode 100644 index bce5a4c8bd..0000000000 --- a/t/lib-prereq-FILEMODE.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2010 Ævar Arnfjörð Bjarmason -# - -if test "$(git config --bool core.filemode)" = false -then - say 'filemode disabled on the filesystem' -else - test_set_prereq FILEMODE -fi diff --git a/t/lib-read-tree.sh b/t/lib-read-tree.sh index ef079afc46..b95f485606 100644 --- a/t/lib-read-tree.sh +++ b/t/lib-read-tree.sh @@ -1,5 +1,3 @@ -#!/bin/sh -# # Helper functions to check if read-tree would succeed/fail as expected with # and without the dry-run option. They also test that the dry-run does not # write the index and that together with -u it doesn't touch the work tree. diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh index 8ff87fb3f9..6bd252212a 100644 --- a/t/lib-rebase.sh +++ b/t/lib-rebase.sh @@ -1,4 +1,4 @@ -#!/bin/sh +# Helper functions used by interactive rebase tests. # After setting the fake editor with this function, you can # diff --git a/t/lib-terminal.sh b/t/lib-terminal.sh index 737df289a1..9a2dca506a 100644 --- a/t/lib-terminal.sh +++ b/t/lib-terminal.sh @@ -1,4 +1,4 @@ -#!/bin/sh +# Helpers for terminal output tests. test_expect_success PERL 'set up terminal for tests' ' # Reading from the pty master seems to get stuck _sometimes_ diff --git a/t/perf/p4001-diff-no-index.sh b/t/perf/p4001-diff-no-index.sh new file mode 100755 index 0000000000..683be6984f --- /dev/null +++ b/t/perf/p4001-diff-no-index.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +test_description="Test diff --no-index performance" + +. ./perf-lib.sh + +test_perf_large_repo +test_checkout_worktree + +file1=$(git ls-files | tail -n 2 | head -1) +file2=$(git ls-files | tail -n 1 | head -1) + +test_expect_success "empty files, so they take no time to diff" " + echo >$file1 && + echo >$file2 +" + +test_perf "diff --no-index" " + git diff --no-index $file1 $file2 >/dev/null +" + +test_done diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh index f4eecaa171..a8c9574291 100644 --- a/t/perf/perf-lib.sh +++ b/t/perf/perf-lib.sh @@ -1,4 +1,6 @@ -#!/bin/sh +# Performance testing framework. Each perf script starts much like +# a normal test script, except it sources this library instead of +# test-lib.sh. See t/perf/README for documentation. # # Copyright (c) 2011 Thomas Rast # diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 10be52beed..a2bb63ce8e 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -41,20 +41,17 @@ test_expect_success '.git/objects should have 3 subdirectories' ' test_expect_success 'success is reported like this' ' : ' -test_expect_failure 'pretend we have a known breakage' ' - false -' run_sub_test_lib_test () { name="$1" descr="$2" # stdin is the body of the test code shift 2 mkdir "$name" && ( - # Pretend we're a test harness. This prevents - # test-lib from writing the counts to a file that will - # later be summarized, showing spurious "failed" tests - HARNESS_ACTIVE=t && - export HARNESS_ACTIVE && + # Pretend we're not running under a test harness, whether we + # are or not. The test-lib output depends on the setting of + # this variable, so we need a stable setting under which to run + # the sub-test. + sane_unset HARNESS_ACTIVE && cd "$name" && cat >"$name.sh" <<-EOF && #!$SHELL_PATH @@ -71,6 +68,8 @@ run_sub_test_lib_test () { cat >>"$name.sh" && chmod +x "$name.sh" && export TEST_DIRECTORY && + TEST_OUTPUT_DIRECTORY=$(pwd) && + export TEST_OUTPUT_DIRECTORY && ./"$name.sh" "$@" >out 2>err ) } @@ -233,16 +232,13 @@ test_expect_success 'test --verbose' ' grep -v "^Initialized empty" test-verbose/out+ >test-verbose/out && check_sub_test_lib_test test-verbose <<-\EOF > expecting success: true - > Z > ok 1 - passing test > Z > expecting success: echo foo > foo - > Z > ok 2 - test with output > Z > expecting success: false - > Z > not ok 3 - failing test > # false > Z @@ -265,7 +261,6 @@ test_expect_success 'test --verbose-only' ' > Z > expecting success: echo foo > foo - > Z > ok 2 - test with output > Z > not ok 3 - failing test diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh index cb144258cc..37e9396e5d 100755 --- a/t/t0002-gitfile.sh +++ b/t/t0002-gitfile.sh @@ -7,7 +7,7 @@ Verify that plumbing commands work when .git is a file . ./test-lib.sh objpath() { - echo "$1" | sed -e 's|\(..\)|\1/|' + echo "$1" | sed -e 's|\(..\)|\1/|' } objck() { @@ -19,7 +19,6 @@ objck() { fi } - test_expect_success 'initial setup' ' REAL="$(pwd)/.real" && mv .git "$REAL" diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh index 0b98b6f8d0..b9d79476e2 100755 --- a/t/t0003-attributes.sh +++ b/t/t0003-attributes.sh @@ -13,7 +13,6 @@ attr_check () { test_line_count = 0 err } - test_expect_success 'setup' ' mkdir -p a/b/d a/c b && ( diff --git a/t/t0202/test.pl b/t/t0202/test.pl index 2c10cb4693..2c10cb4693 100644..100755 --- a/t/t0202/test.pl +++ b/t/t0202/test.pl diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh index 8a1bc5c532..a72e700ae4 100755 --- a/t/t1006-cat-file.sh +++ b/t/t1006-cat-file.sh @@ -85,6 +85,28 @@ $content" git cat-file --batch-check="%(objecttype) %(rest)" >actual && test_cmp expect actual ' + + test -z "$content" || + test_expect_success "--batch without type ($type)" ' + { + echo "$size" && + maybe_remove_timestamp "$content" $no_ts + } >expect && + echo $sha1 | git cat-file --batch="%(objectsize)" >actual.full && + maybe_remove_timestamp "$(cat actual.full)" $no_ts >actual && + test_cmp expect actual + ' + + test -z "$content" || + test_expect_success "--batch without size ($type)" ' + { + echo "$type" && + maybe_remove_timestamp "$content" $no_ts + } >expect && + echo $sha1 | git cat-file --batch="%(objecttype)" >actual.full && + maybe_remove_timestamp "$(cat actual.full)" $no_ts >actual && + test_cmp expect actual + ' } hello_content="Hello World" @@ -240,4 +262,38 @@ test_expect_success "--batch-check with multiple sha1s gives correct format" ' "$(echo_without_newline "$batch_check_input" | git cat-file --batch-check)" ' +test_expect_success 'setup blobs which are likely to delta' ' + test-genrandom foo 10240 >foo && + { cat foo; echo plus; } >foo-plus && + git add foo foo-plus && + git commit -m foo && + cat >blobs <<-\EOF + HEAD:foo + HEAD:foo-plus + EOF +' + +test_expect_success 'confirm that neither loose blob is a delta' ' + cat >expect <<-EOF + $_z40 + $_z40 + EOF + git cat-file --batch-check="%(deltabase)" <blobs >actual && + test_cmp expect actual +' + +# To avoid relying too much on the current delta heuristics, +# we will check only that one of the two objects is a delta +# against the other, but not the order. We can do so by just +# asking for the base of both, and checking whether either +# sha1 appears in the output. +test_expect_success '%(deltabase) reports packed delta bases' ' + git repack -ad && + git cat-file --batch-check="%(deltabase)" <blobs >actual && + { + grep "$(git rev-parse HEAD:foo)" actual || + grep "$(git rev-parse HEAD:foo-plus)" actual + } +' + test_done diff --git a/t/t1013-loose-object-format.sh b/t/t1013-loose-object-format.sh deleted file mode 100755 index fbf5f2fc00..0000000000 --- a/t/t1013-loose-object-format.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2011 Roberto Tyley -# - -test_description='Correctly identify and parse loose object headers - -There are two file formats for loose objects - the original standard -format, and the experimental format introduced with Git v1.4.3, later -deprecated with v1.5.3. Although Git no longer writes the -experimental format, objects in both formats must be read, with the -format for a given file being determined by the header. - -Detecting file format based on header is not entirely trivial, not -least because the first byte of a zlib-deflated stream will vary -depending on how much memory was allocated for the deflation window -buffer when the object was written out (for example 4KB on Android, -rather that 32KB on a normal PC). - -The loose objects used as test vectors have been generated with the -following Git versions: - -standard format: Git v1.7.4.1 -experimental format: Git v1.4.3 (legacyheaders=false) -standard format, deflated with 4KB window size: Agit/JGit on Android -' - -. ./test-lib.sh - -assert_blob_equals() { - printf "%s" "$2" >expected && - git cat-file -p "$1" >actual && - test_cmp expected actual -} - -test_expect_success setup ' - cp -R "$TEST_DIRECTORY/t1013/objects" .git/ && - git --version -' - -test_expect_success 'read standard-format loose objects' ' - git cat-file tag 8d4e360d6c70fbd72411991c02a09c442cf7a9fa && - git cat-file commit 6baee0540ea990d9761a3eb9ab183003a71c3696 && - git ls-tree 7a37b887a73791d12d26c0d3e39568a8fb0fa6e8 && - assert_blob_equals "257cc5642cb1a054f08cc83f2d943e56fd3ebe99" "foo$LF" -' - -test_expect_success 'read experimental-format loose objects' ' - git cat-file tag 76e7fa9941f4d5f97f64fea65a2cba436bc79cbb && - git cat-file commit 7875c6237d3fcdd0ac2f0decc7d3fa6a50b66c09 && - git ls-tree 95b1625de3ba8b2214d1e0d0591138aea733f64f && - assert_blob_equals "2e65efe2a145dda7ee51d1741299f848e5bf752e" "a" && - assert_blob_equals "9ae9e86b7bd6cb1472d9373702d8249973da0832" "ab" && - assert_blob_equals "85df50785d62d3b05ab03d9cbf7e4a0b49449730" "abcd" && - assert_blob_equals "1656f9233d999f61ef23ef390b9c71d75399f435" "abcdefgh" && - assert_blob_equals "1e72a6b2c4a577ab0338860fa9fe87f761fc9bbd" "abcdefghi" && - assert_blob_equals "70e6a83d8dcb26fc8bc0cf702e2ddeb6adca18fd" "abcdefghijklmnop" && - assert_blob_equals "bd15045f6ce8ff75747562173640456a394412c8" "abcdefghijklmnopqrstuvwx" -' - -test_expect_success 'read standard-format objects deflated with smaller window buffer' ' - git cat-file tag f816d5255855ac160652ee5253b06cd8ee14165a && - git cat-file tag 149cedb5c46929d18e0f118e9fa31927487af3b6 -' - -test_done diff --git a/t/t1013/objects/14/9cedb5c46929d18e0f118e9fa31927487af3b6 b/t/t1013/objects/14/9cedb5c46929d18e0f118e9fa31927487af3b6 Binary files differdeleted file mode 100644 index 472fd1458e..0000000000 --- a/t/t1013/objects/14/9cedb5c46929d18e0f118e9fa31927487af3b6 +++ /dev/null diff --git a/t/t1013/objects/16/56f9233d999f61ef23ef390b9c71d75399f435 b/t/t1013/objects/16/56f9233d999f61ef23ef390b9c71d75399f435 Binary files differdeleted file mode 100644 index c379d74ae2..0000000000 --- a/t/t1013/objects/16/56f9233d999f61ef23ef390b9c71d75399f435 +++ /dev/null diff --git a/t/t1013/objects/1e/72a6b2c4a577ab0338860fa9fe87f761fc9bbd b/t/t1013/objects/1e/72a6b2c4a577ab0338860fa9fe87f761fc9bbd Binary files differdeleted file mode 100644 index 93706305bc..0000000000 --- a/t/t1013/objects/1e/72a6b2c4a577ab0338860fa9fe87f761fc9bbd +++ /dev/null diff --git a/t/t1013/objects/25/7cc5642cb1a054f08cc83f2d943e56fd3ebe99 b/t/t1013/objects/25/7cc5642cb1a054f08cc83f2d943e56fd3ebe99 Binary files differdeleted file mode 100644 index bdcf704c9e..0000000000 --- a/t/t1013/objects/25/7cc5642cb1a054f08cc83f2d943e56fd3ebe99 +++ /dev/null diff --git a/t/t1013/objects/2e/65efe2a145dda7ee51d1741299f848e5bf752e b/t/t1013/objects/2e/65efe2a145dda7ee51d1741299f848e5bf752e Binary files differdeleted file mode 100644 index ad62c43e41..0000000000 --- a/t/t1013/objects/2e/65efe2a145dda7ee51d1741299f848e5bf752e +++ /dev/null diff --git a/t/t1013/objects/6b/aee0540ea990d9761a3eb9ab183003a71c3696 b/t/t1013/objects/6b/aee0540ea990d9761a3eb9ab183003a71c3696 Binary files differdeleted file mode 100644 index 3d2f0337db..0000000000 --- a/t/t1013/objects/6b/aee0540ea990d9761a3eb9ab183003a71c3696 +++ /dev/null diff --git a/t/t1013/objects/70/e6a83d8dcb26fc8bc0cf702e2ddeb6adca18fd b/t/t1013/objects/70/e6a83d8dcb26fc8bc0cf702e2ddeb6adca18fd Binary files differdeleted file mode 100644 index b3f71a6ee5..0000000000 --- a/t/t1013/objects/70/e6a83d8dcb26fc8bc0cf702e2ddeb6adca18fd +++ /dev/null diff --git a/t/t1013/objects/76/e7fa9941f4d5f97f64fea65a2cba436bc79cbb b/t/t1013/objects/76/e7fa9941f4d5f97f64fea65a2cba436bc79cbb deleted file mode 100644 index af4e9a7b0c..0000000000 --- a/t/t1013/objects/76/e7fa9941f4d5f97f64fea65a2cba436bc79cbb +++ /dev/null @@ -1,2 +0,0 @@ -Âxœ%ÌA‚0@Ñ}O1{cSZ(˜ãνáÃthª”’ZŒÜÞ Ëÿ?
¦m×6dµiÉ9…¤Gå˜h´Ø¨ÁZR'Q¶…RŒ¡ˆ‚ø³p‘ç‚ÓqL9âÏ=g¸§sIÐoopÎÿ”eÏ«_1»€³¤$×ç*Si«ëNwpP•RBôûÅÁú -³‡[(ð®d-øÁL9á
\ No newline at end of file diff --git a/t/t1013/objects/78/75c6237d3fcdd0ac2f0decc7d3fa6a50b66c09 b/t/t1013/objects/78/75c6237d3fcdd0ac2f0decc7d3fa6a50b66c09 Binary files differdeleted file mode 100644 index 3dd28be5c6..0000000000 --- a/t/t1013/objects/78/75c6237d3fcdd0ac2f0decc7d3fa6a50b66c09 +++ /dev/null diff --git a/t/t1013/objects/7a/37b887a73791d12d26c0d3e39568a8fb0fa6e8 b/t/t1013/objects/7a/37b887a73791d12d26c0d3e39568a8fb0fa6e8 Binary files differdeleted file mode 100644 index 2b97b264c3..0000000000 --- a/t/t1013/objects/7a/37b887a73791d12d26c0d3e39568a8fb0fa6e8 +++ /dev/null diff --git a/t/t1013/objects/85/df50785d62d3b05ab03d9cbf7e4a0b49449730 b/t/t1013/objects/85/df50785d62d3b05ab03d9cbf7e4a0b49449730 Binary files differdeleted file mode 100644 index 6dff746876..0000000000 --- a/t/t1013/objects/85/df50785d62d3b05ab03d9cbf7e4a0b49449730 +++ /dev/null diff --git a/t/t1013/objects/8d/4e360d6c70fbd72411991c02a09c442cf7a9fa b/t/t1013/objects/8d/4e360d6c70fbd72411991c02a09c442cf7a9fa Binary files differdeleted file mode 100644 index cb41e92d07..0000000000 --- a/t/t1013/objects/8d/4e360d6c70fbd72411991c02a09c442cf7a9fa +++ /dev/null diff --git a/t/t1013/objects/95/b1625de3ba8b2214d1e0d0591138aea733f64f b/t/t1013/objects/95/b1625de3ba8b2214d1e0d0591138aea733f64f Binary files differdeleted file mode 100644 index 7ac46b4f70..0000000000 --- a/t/t1013/objects/95/b1625de3ba8b2214d1e0d0591138aea733f64f +++ /dev/null diff --git a/t/t1013/objects/9a/e9e86b7bd6cb1472d9373702d8249973da0832 b/t/t1013/objects/9a/e9e86b7bd6cb1472d9373702d8249973da0832 Binary files differdeleted file mode 100644 index 9d8316d4e5..0000000000 --- a/t/t1013/objects/9a/e9e86b7bd6cb1472d9373702d8249973da0832 +++ /dev/null diff --git a/t/t1013/objects/bd/15045f6ce8ff75747562173640456a394412c8 b/t/t1013/objects/bd/15045f6ce8ff75747562173640456a394412c8 Binary files differdeleted file mode 100644 index eebf23956e..0000000000 --- a/t/t1013/objects/bd/15045f6ce8ff75747562173640456a394412c8 +++ /dev/null diff --git a/t/t1013/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/t/t1013/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 Binary files differdeleted file mode 100644 index 134cf19379..0000000000 --- a/t/t1013/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 +++ /dev/null diff --git a/t/t1013/objects/f8/16d5255855ac160652ee5253b06cd8ee14165a b/t/t1013/objects/f8/16d5255855ac160652ee5253b06cd8ee14165a deleted file mode 100644 index 26b75aec56..0000000000 --- a/t/t1013/objects/f8/16d5255855ac160652ee5253b06cd8ee14165a +++ /dev/null @@ -1 +0,0 @@ -H‰ÌÁ‚0€aÏ{ŠÞ
I»e&Æø*¥ˆG°ß^¸ýù¿ËDåÒ†wU‡Ò—¬S±4ªŠÆªž ,fÅ[ðßVAÛºÎüxÈÇö6[wtG§Lu¸?—¦²¼Ú×@‰"gì{†+by¾%M
\ No newline at end of file diff --git a/t/t1303-wacky-config.sh b/t/t1303-wacky-config.sh index 46103a1591..3a2c81968c 100755 --- a/t/t1303-wacky-config.sh +++ b/t/t1303-wacky-config.sh @@ -3,17 +3,28 @@ test_description='Test wacky input to git config' . ./test-lib.sh +# Leaving off the newline is intentional! setup() { (printf "[section]\n" && printf " key = foo") >.git/config } +# 'check section.key value' verifies that the entry for section.key is +# 'value' check() { echo "$2" >expected git config --get "$1" >actual 2>&1 test_cmp actual expected } +# 'check section.key regex value' verifies that the entry for +# section.key *that matches 'regex'* is 'value' +check_regex() { + echo "$3" >expected + git config --get "$1" "$2" >actual 2>&1 + test_cmp actual expected +} + test_expect_success 'modify same key' ' setup && git config section.key bar && @@ -47,4 +58,57 @@ test_expect_success 'do not crash on special long config line' ' check section.key "$LONG_VALUE" ' +setup_many() { + setup && + # This time we want the newline so that we can tack on more + # entries. + echo >>.git/config && + # Semi-efficient way of concatenating 5^5 = 3125 lines. Note + # that because 'setup' already put one line, this means 3126 + # entries for section.key in the config file. + cat >5to1 <<-\EOF && + key = foo + key = foo + key = foo + key = foo + key = foo + EOF + cat 5to1 5to1 5to1 5to1 5to1 >5to2 && # 25 + cat 5to2 5to2 5to2 5to2 5to2 >5to3 && # 125 + cat 5to3 5to3 5to3 5to3 5to3 >5to4 && # 635 + cat 5to4 5to4 5to4 5to4 5to4 >>.git/config # 3125 +} + +test_expect_success 'get many entries' ' + setup_many && + git config --get-all section.key >actual && + test_line_count = 3126 actual +' + +test_expect_success 'get many entries by regex' ' + setup_many && + git config --get-regexp "sec.*ke." >actual && + test_line_count = 3126 actual +' + +test_expect_success 'add and replace one of many entries' ' + setup_many && + git config --add section.key bar && + check_regex section.key "b.*r" bar && + git config section.key beer "b.*r" && + check_regex section.key "b.*r" beer +' + +test_expect_success 'replace many entries' ' + setup_many && + git config --replace-all section.key bar && + check section.key bar +' + +test_expect_success 'unset many entries' ' + setup_many && + git config --unset-all section.key && + test_must_fail git config section.key +' + test_done diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh index 13c88c9aae..83b1300cef 100755 --- a/t/t1502-rev-parse-parseopt.sh +++ b/t/t1502-rev-parse-parseopt.sh @@ -12,9 +12,11 @@ usage: some-command [options] <args>... -h, --help show the help --foo some nifty option --foo --bar ... some cool option --bar with an argument + -b, --baz a short and long option An option group Header -C[...] option C with an optional argument + -d, --data[=...] short and long option with an optional argument Extras --extra1 line above used to cause a segfault but no longer does @@ -31,9 +33,11 @@ h,help show the help foo some nifty option --foo bar= some cool option --bar with an argument +b,baz a short and long option An option group Header C? option C with an optional argument +d,data? short and long option with an optional argument Extras extra1 line above used to cause a segfault but no longer does @@ -45,16 +49,16 @@ test_expect_success 'test --parseopt help output' ' ' cat > expect <<EOF -set -- --foo --bar 'ham' -- 'arg' +set -- --foo --bar 'ham' -b -- 'arg' EOF test_expect_success 'test --parseopt' ' - git rev-parse --parseopt -- --foo --bar=ham arg < optionspec > output && + git rev-parse --parseopt -- --foo --bar=ham --baz arg < optionspec > output && test_cmp expect output ' test_expect_success 'test --parseopt with mixed options and arguments' ' - git rev-parse --parseopt -- --foo arg --bar=ham < optionspec > output && + git rev-parse --parseopt -- --foo arg --bar=ham --baz < optionspec > output && test_cmp expect output ' @@ -99,4 +103,36 @@ test_expect_success 'test --parseopt --keep-dashdash --stop-at-non-option withou test_cmp expect output ' +cat > expect <<EOF +set -- --foo --bar='z' --baz -C'Z' --data='A' -- 'arg' +EOF + +test_expect_success 'test --parseopt --stuck-long' ' + git rev-parse --parseopt --stuck-long -- --foo --bar=z -b arg -CZ -dA <optionspec >output && + test_cmp expect output +' + +cat > expect <<EOF +set -- --data='' -C --baz -- 'arg' +EOF + +test_expect_success 'test --parseopt --stuck-long and empty optional argument' ' + git rev-parse --parseopt --stuck-long -- --data= arg -C -b <optionspec >output && + test_cmp expect output +' + +cat > expect <<EOF +set -- --data --baz -- 'arg' +EOF + +test_expect_success 'test --parseopt --stuck-long and long option with unset optional argument' ' + git rev-parse --parseopt --stuck-long -- --data arg -b <optionspec >output && + test_cmp expect output +' + +test_expect_success 'test --parseopt --stuck-long and short option with unset optional argument' ' + git rev-parse --parseopt --stuck-long -- -d arg -b <optionspec >output && + test_cmp expect output +' + test_done diff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh index f950c10128..613d9bfe1b 100755 --- a/t/t1506-rev-parse-diagnosis.sh +++ b/t/t1506-rev-parse-diagnosis.sh @@ -196,4 +196,28 @@ test_expect_success 'dotdot is not an empty set' ' test_cmp expect actual ' +test_expect_success 'arg before dashdash must be a revision (missing)' ' + test_must_fail git rev-parse foobar -- 2>stderr && + test_i18ngrep "bad revision" stderr +' + +test_expect_success 'arg before dashdash must be a revision (file)' ' + >foobar && + test_must_fail git rev-parse foobar -- 2>stderr && + test_i18ngrep "bad revision" stderr +' + +test_expect_success 'arg before dashdash must be a revision (ambiguous)' ' + >foobar && + git update-ref refs/heads/foobar HEAD && + { + # we do not want to use rev-parse here, because + # we are testing it + cat .git/refs/heads/foobar && + printf "%s\n" -- + } >expect && + git rev-parse foobar -- >actual && + test_cmp expect actual +' + test_done diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh index ebf93b0695..998503db12 100755 --- a/t/t3400-rebase.sh +++ b/t/t3400-rebase.sh @@ -134,12 +134,14 @@ test_expect_success 'fail when upstream arg is missing and not configured' ' test_must_fail git rebase ' -test_expect_success 'default to @{upstream} when upstream arg is missing' ' +test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' ' git checkout -b default topic && git config branch.default.remote . && git config branch.default.merge refs/heads/master && git rebase && - test "$(git rev-parse default~1)" = "$(git rev-parse master)" + git rev-parse --verify master >expect && + git rev-parse default~1 >actual && + test_cmp expect actual ' test_expect_success 'rebase -q is quiet' ' diff --git a/t/t3700-add.sh b/t/t3700-add.sh index aab86e838b..fe274e2fb1 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -272,6 +272,25 @@ test_expect_success '"add non-existent" should fail' ' ! (git ls-files | grep "non-existent") ' +test_expect_success 'git add -A on empty repo does not error out' ' + rm -fr empty && + git init empty && + ( + cd empty && + git add -A . && + git add -A + ) +' + +test_expect_success '"git add ." in empty repo' ' + rm -fr empty && + git init empty && + ( + cd empty && + git add . + ) +' + test_expect_success 'git add --dry-run of existing changed file' " echo new >>track-this && git add --dry-run track-this >actual 2>&1 && diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 9dc91d09d7..24ddd8a704 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -2,7 +2,6 @@ test_description='add -i basic tests' . ./test-lib.sh -. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh if ! test_have_prereq PERL then diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh index 8a309795c9..bcae35ac1c 100755 --- a/t/t4020-diff-external.sh +++ b/t/t4020-diff-external.sh @@ -193,6 +193,19 @@ test_expect_success 'GIT_EXTERNAL_DIFF with more than one changed files' ' GIT_EXTERNAL_DIFF=echo git diff ' +test_expect_success 'GIT_EXTERNAL_DIFF path counter/total' ' + write_script external-diff.sh <<-\EOF && + echo $GIT_DIFF_PATH_COUNTER of $GIT_DIFF_PATH_TOTAL >>counter.txt + EOF + >counter.txt && + cat >expect <<-\EOF && + 1 of 2 + 2 of 2 + EOF + GIT_EXTERNAL_DIFF=./external-diff.sh git diff && + test_cmp expect counter.txt +' + test_expect_success 'GIT_EXTERNAL_DIFF generates pretty paths' ' touch file.ext && git add file.ext && diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh index 979e98398b..2ab3c48734 100755 --- a/t/t4053-diff-no-index.sh +++ b/t/t4053-diff-no-index.sh @@ -29,4 +29,30 @@ test_expect_success 'git diff --no-index relative path outside repo' ' ) ' +test_expect_success 'git diff --no-index with broken index' ' + ( + cd repo && + echo broken >.git/index && + git diff --no-index a ../non/git/a + ) +' + +test_expect_success 'git diff outside repo with broken index' ' + ( + cd repo && + git diff ../non/git/a ../non/git/b + ) +' + +test_expect_success 'git diff --no-index executed outside repo gives correct error message' ' + ( + GIT_CEILING_DIRECTORIES=$TRASH_DIRECTORY/non && + export GIT_CEILING_DIRECTORIES && + cd non/git && + test_must_fail git diff --no-index a 2>actual.err && + echo "usage: git diff --no-index <path> <path>" >expect.err && + test_cmp expect.err actual.err + ) +' + test_done diff --git a/t/t4056-diff-order.sh b/t/t4056-diff-order.sh new file mode 100755 index 0000000000..1ddd226b78 --- /dev/null +++ b/t/t4056-diff-order.sh @@ -0,0 +1,104 @@ +#!/bin/sh + +test_description='diff order' + +. ./test-lib.sh + +create_files () { + echo "$1" >a.h && + echo "$1" >b.c && + echo "$1" >c/Makefile && + echo "$1" >d.txt && + git add a.h b.c c/Makefile d.txt && + git commit -m"$1" +} + +test_expect_success 'setup' ' + mkdir c && + create_files 1 && + create_files 2 && + + cat >order_file_1 <<-\EOF && + *Makefile + *.txt + *.h + EOF + + cat >order_file_2 <<-\EOF && + *Makefile + *.h + *.c + EOF + + cat >expect_none <<-\EOF && + a.h + b.c + c/Makefile + d.txt + EOF + + cat >expect_1 <<-\EOF && + c/Makefile + d.txt + a.h + b.c + EOF + + cat >expect_2 <<-\EOF + c/Makefile + a.h + b.c + d.txt + EOF +' + +test_expect_success "no order (=tree object order)" ' + git diff --name-only HEAD^..HEAD >actual && + test_cmp expect_none actual +' + +test_expect_success 'missing orderfile' ' + rm -f bogus_file && + test_must_fail git diff -Obogus_file --name-only HEAD^..HEAD +' + +test_expect_success POSIXPERM,SANITY 'unreadable orderfile' ' + >unreadable_file && + chmod -r unreadable_file && + test_must_fail git diff -Ounreadable_file --name-only HEAD^..HEAD +' + +test_expect_success 'orderfile is a directory' ' + test_must_fail git diff -O/ --name-only HEAD^..HEAD +' + +for i in 1 2 +do + test_expect_success "orderfile using option ($i)" ' + git diff -Oorder_file_$i --name-only HEAD^..HEAD >actual && + test_cmp expect_$i actual + ' + + test_expect_success PIPE "orderfile is fifo ($i)" ' + rm -f order_fifo && + mkfifo order_fifo && + { + cat order_file_$i >order_fifo & + } && + git diff -O order_fifo --name-only HEAD^..HEAD >actual && + wait && + test_cmp expect_$i actual + ' + + test_expect_success "orderfile using config ($i)" ' + git -c diff.orderfile=order_file_$i diff --name-only HEAD^..HEAD >actual && + test_cmp expect_$i actual + ' + + test_expect_success "cancelling configured orderfile ($i)" ' + git -c diff.orderfile=order_file_$i diff -O/dev/null --name-only HEAD^..HEAD >actual && + test_cmp expect_none actual + ' +done + +test_done diff --git a/t/t4102-apply-rename.sh b/t/t4102-apply-rename.sh index e3ea3d5114..49e2d6c349 100755 --- a/t/t4102-apply-rename.sh +++ b/t/t4102-apply-rename.sh @@ -7,7 +7,6 @@ test_description='git apply handling copy/rename patch. ' . ./test-lib.sh -. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh # setup diff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh index 2298ece801..1e4d4380bf 100755 --- a/t/t4116-apply-reverse.sh +++ b/t/t4116-apply-reverse.sh @@ -48,12 +48,12 @@ test_expect_success 'apply in reverse' ' test_expect_success 'setup separate repository lacking postimage' ' - git tar-tree initial initial | $TAR xf - && + git archive --format=tar --prefix=initial/ initial | $TAR xf - && ( cd initial && git init && git add . ) && - git tar-tree second second | $TAR xf - && + git archive --format=tar --prefix=second/ second | $TAR xf - && ( cd second && git init && git add . ) diff --git a/t/t4120-apply-popt.sh b/t/t4120-apply-popt.sh index c5fecdfed4..497b62868d 100755 --- a/t/t4120-apply-popt.sh +++ b/t/t4120-apply-popt.sh @@ -6,7 +6,6 @@ test_description='git apply -p handling.' . ./test-lib.sh -. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh test_expect_success setup ' mkdir sub && diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh index 0d36ebdc86..c268298eaf 100755 --- a/t/t4129-apply-samemode.sh +++ b/t/t4129-apply-samemode.sh @@ -3,7 +3,6 @@ test_description='applying patch with mode bits' . ./test-lib.sh -. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh test_expect_success setup ' echo original >file && diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh index fb00041139..2a6278bb33 100755 --- a/t/t4205-log-pretty-formats.sh +++ b/t/t4205-log-pretty-formats.sh @@ -310,4 +310,19 @@ EOF test_cmp expected actual ' +test_expect_success 'log decoration properly follows tag chain' ' + git tag -a tag1 -m tag1 && + git tag -a tag2 -m tag2 tag1 && + git tag -d tag1 && + git commit --amend -m shorter && + git log --no-walk --tags --pretty="%H %d" --decorate=full >actual && + cat <<EOF >expected && +6a908c10688b2503073c39c9ba26322c73902bb5 (tag: refs/tags/tag2) +9f716384d92283fb915a4eee5073f030638e05f9 (tag: refs/tags/message-one) +b87e4cccdb77336ea79d89224737be7ea8e95367 (tag: refs/tags/message-two) +EOF + sort actual >actual1 && + test_cmp expected actual1 +' + test_done diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index c2023b1a3d..05f011d38e 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -3,7 +3,7 @@ # Copyright (C) 2005 Rene Scharfe # -test_description='git tar-tree and git get-tar-commit-id test +test_description='git archive and git get-tar-commit-id test This test covers the topics of file contents, commit date handling and commit id embedding: @@ -13,11 +13,11 @@ commit id embedding: binary file (/bin/sh). Only paths shorter than 99 characters are used. - git tar-tree applies the commit date to every file in the archive it + git archive applies the commit date to every file in the archive it creates. The test sets the commit date to a specific value and checks if the tar archive contains that value. - When giving git tar-tree a commit id (in contrast to a tree id) it + When giving git archive a commit id (in contrast to a tree id) it embeds this commit id into the tar archive as a comment. The test checks the ability of git get-tar-commit-id to figure it out from the tar file. @@ -25,8 +25,6 @@ commit id embedding: ' . ./test-lib.sh -GZIP=${GZIP:-gzip} -GUNZIP=${GUNZIP:-gzip -d} SUBSTFORMAT=%H%n @@ -39,6 +37,8 @@ test_lazy_prereq TAR_NEEDS_PAX_FALLBACK ' ) ' +test_lazy_prereq GZIP 'gzip --version' + get_pax_header() { file=$1 header=$2= @@ -196,16 +196,6 @@ test_expect_success \ 'git get-tar-commit-id <b.tar >b.commitid && test_cmp .git/$(git symbolic-ref HEAD) b.commitid' -test_expect_success 'git tar-tree' ' - git tar-tree HEAD >tar-tree.tar && - test_cmp b.tar tar-tree.tar -' - -test_expect_success 'git tar-tree with prefix' ' - git tar-tree HEAD prefix >tar-tree_with_prefix.tar && - test_cmp with_prefix.tar tar-tree_with_prefix.tar -' - test_expect_success 'git archive with --output, override inferred format' ' git archive --format=tar --output=d4.zip HEAD && test_cmp b.tar d4.zip @@ -275,12 +265,6 @@ test_expect_success 'only enabled filters are available remotely' ' test_cmp remote.bar config.bar ' -if $GZIP --version >/dev/null 2>&1; then - test_set_prereq GZIP -else - say "Skipping some tar.gz tests because gzip not found" -fi - test_expect_success GZIP 'git archive --format=tgz' ' git archive --format=tgz HEAD >j.tgz ' @@ -300,14 +284,8 @@ test_expect_success GZIP 'infer tgz from .tar.gz filename' ' test_cmp j.tgz j3.tar.gz ' -if $GUNZIP --version >/dev/null 2>&1; then - test_set_prereq GUNZIP -else - say "Skipping some tar.gz tests because gunzip was not found" -fi - -test_expect_success GZIP,GUNZIP 'extract tgz file' ' - $GUNZIP -c <j.tgz >j.tar && +test_expect_success GZIP 'extract tgz file' ' + gzip -d -c <j.tgz >j.tar && test_cmp b.tar j.tar ' diff --git a/t/t5001-archive-attr.sh b/t/t5001-archive-attr.sh index f47d8717fd..51dedab29b 100755 --- a/t/t5001-archive-attr.sh +++ b/t/t5001-archive-attr.sh @@ -87,14 +87,4 @@ test_expect_success 'export-subst' ' test_cmp substfile2 archive/substfile2 ' -test_expect_success 'git tar-tree vs. git archive with worktree attributes' ' - git tar-tree HEAD >tar-tree.tar && - test_cmp worktree.tar tar-tree.tar -' - -test_expect_success 'git tar-tree vs. git archive with worktree attrs, bare' ' - (cd bare && git tar-tree HEAD) >bare-tar-tree.tar && - test_cmp bare-worktree.tar bare-tar-tree.tar -' - test_done diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh index fe82025d4a..4bbb718751 100755 --- a/t/t5302-pack-index.sh +++ b/t/t5302-pack-index.sh @@ -174,11 +174,11 @@ test_expect_success \ test_expect_success \ '[index v1] 5) pack-objects happily reuses corrupted data' \ 'pack4=$(git pack-objects test-4 <obj-list) && - test -f "test-4-${pack1}.pack"' + test -f "test-4-${pack4}.pack"' test_expect_success \ '[index v1] 6) newly created pack is BAD !' \ - 'test_must_fail git verify-pack -v "test-4-${pack1}.pack"' + 'test_must_fail git verify-pack -v "test-4-${pack4}.pack"' test_expect_success \ '[index v2] 1) stream pack to repository' \ diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index d87ddf73b7..5b2b1c2c13 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -531,5 +531,92 @@ test_expect_success 'shallow fetch with tags does not break the repository' ' git fsck ) ' +check_prot_path () { + cat >expected <<-EOF && + Diag: url=$1 + Diag: protocol=$2 + Diag: path=$3 + EOF + git fetch-pack --diag-url "$1" | grep -v hostandport= >actual && + test_cmp expected actual +} + +check_prot_host_path () { + cat >expected <<-EOF && + Diag: url=$1 + Diag: protocol=$2 + Diag: hostandport=$3 + Diag: path=$4 + EOF + git fetch-pack --diag-url "$1" >actual && + test_cmp expected actual +} + +for r in repo re:po re/po +do + # git or ssh with scheme + for p in "ssh+git" "git+ssh" git ssh + do + for h in host host:12 [::1] [::1]:23 + do + case "$p" in + *ssh*) + pp=ssh + ;; + *) + pp=$p + ;; + esac + test_expect_success "fetch-pack --diag-url $p://$h/$r" ' + check_prot_host_path $p://$h/$r $pp "$h" "/$r" + ' + # "/~" -> "~" conversion + test_expect_success "fetch-pack --diag-url $p://$h/~$r" ' + check_prot_host_path $p://$h/~$r $pp "$h" "~$r" + ' + done + done + # file with scheme + for p in file + do + test_expect_success "fetch-pack --diag-url $p://$h/$r" ' + check_prot_path $p://$h/$r $p "/$r" + ' + # No "/~" -> "~" conversion for file + test_expect_success "fetch-pack --diag-url $p://$h/~$r" ' + check_prot_path $p://$h/~$r $p "/~$r" + ' + done + # file without scheme + for h in nohost nohost:12 [::1] [::1]:23 [ [:aa + do + test_expect_success "fetch-pack --diag-url ./$h:$r" ' + check_prot_path ./$h:$r $p "./$h:$r" + ' + # No "/~" -> "~" conversion for file + test_expect_success "fetch-pack --diag-url ./$p:$h/~$r" ' + check_prot_path ./$p:$h/~$r $p "./$p:$h/~$r" + ' + done + #ssh without scheme + p=ssh + for h in host [::1] + do + test_expect_success "fetch-pack --diag-url $h:$r" ' + check_prot_path $h:$r $p "$r" + ' + # Do "/~" -> "~" conversion + test_expect_success "fetch-pack --diag-url $h:/~$r" ' + check_prot_host_path $h:/~$r $p "$h" "~$r" + ' + done +done + +test_expect_success MINGW 'fetch-pack --diag-url file://c:/repo' ' + check_prot_path file://c:/repo file c:/repo +' +test_expect_success MINGW 'fetch-pack --diag-url c:repo' ' + check_prot_path c:repo file c:repo +' test_done diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 1f0f8e6827..12674ac098 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -88,7 +88,7 @@ test_expect_success 'fetch --prune on its own works as expected' ' cd "$D" && git clone . prune && cd prune && - git fetch origin refs/heads/master:refs/remotes/origin/extrabranch && + git update-ref refs/remotes/origin/extrabranch master && git fetch --prune origin && test_must_fail git rev-parse origin/extrabranch @@ -98,7 +98,7 @@ test_expect_success 'fetch --prune with a branch name keeps branches' ' cd "$D" && git clone . prune-branch && cd prune-branch && - git fetch origin refs/heads/master:refs/remotes/origin/extrabranch && + git update-ref refs/remotes/origin/extrabranch master && git fetch --prune origin master && git rev-parse origin/extrabranch @@ -113,25 +113,45 @@ test_expect_success 'fetch --prune with a namespace keeps other namespaces' ' git rev-parse origin/master ' -test_expect_success 'fetch --prune --tags does not delete the remote-tracking branches' ' +test_expect_success 'fetch --prune --tags prunes branches but not tags' ' cd "$D" && git clone . prune-tags && cd prune-tags && - git fetch origin refs/heads/master:refs/tags/sometag && + git tag sometag master && + # Create what looks like a remote-tracking branch from an earlier + # fetch that has since been deleted from the remote: + git update-ref refs/remotes/origin/fake-remote master && git fetch --prune --tags origin && git rev-parse origin/master && - test_must_fail git rev-parse somebranch + test_must_fail git rev-parse origin/fake-remote && + git rev-parse sometag ' -test_expect_success 'fetch --prune --tags with branch does not delete other remote-tracking branches' ' +test_expect_success 'fetch --prune --tags with branch does not prune other things' ' cd "$D" && git clone . prune-tags-branch && cd prune-tags-branch && - git fetch origin refs/heads/master:refs/remotes/origin/extrabranch && + git tag sometag master && + git update-ref refs/remotes/origin/extrabranch master && git fetch --prune --tags origin master && - git rev-parse origin/extrabranch + git rev-parse origin/extrabranch && + git rev-parse sometag +' + +test_expect_success 'fetch --prune --tags with refspec prunes based on refspec' ' + cd "$D" && + git clone . prune-tags-refspec && + cd prune-tags-refspec && + git tag sometag master && + git update-ref refs/remotes/origin/foo/otherbranch master && + git update-ref refs/remotes/origin/extrabranch master && + + git fetch --prune --tags origin refs/heads/foo/*:refs/remotes/origin/foo/* && + test_must_fail git rev-parse refs/remotes/origin/foo/otherbranch && + git rev-parse origin/extrabranch && + git rev-parse sometag ' test_expect_success 'fetch tags when there is no tags' ' @@ -594,4 +614,30 @@ test_expect_success 'all boundary commits are excluded' ' test_bundle_object_count .git/objects/pack/pack-${pack##pack }.pack 3 ' +test_expect_success 'fetch --prune prints the remotes url' ' + git branch goodbye && + git clone . only-prunes && + git branch -D goodbye && + ( + cd only-prunes && + git fetch --prune origin 2>&1 | head -n1 >../actual + ) && + echo "From ${D}/." >expect && + test_cmp expect actual +' + +test_expect_success 'branchname D/F conflict resolved by --prune' ' + git branch dir/file && + git clone . prune-df-conflict && + git branch -D dir/file && + git branch dir && + ( + cd prune-df-conflict && + git fetch --prune && + git rev-parse origin/dir >../actual + ) && + git rev-parse dir >expect && + test_cmp expect actual +' + test_done diff --git a/t/t5515/fetch.br-unconfig_--tags_.._.git b/t/t5515/fetch.br-unconfig_--tags_.._.git index 1669cc4af0..0f70f66c70 100644 --- a/t/t5515/fetch.br-unconfig_--tags_.._.git +++ b/t/t5515/fetch.br-unconfig_--tags_.._.git @@ -1,4 +1,5 @@ # br-unconfig --tags ../.git +0567da4d5edd2ff4bb292a465ba9e64dcad9536b ../ 6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../ 8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../ 22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../ diff --git a/t/t5515/fetch.master_--tags_.._.git b/t/t5515/fetch.master_--tags_.._.git index 8a7493537b..ab473a6e1f 100644 --- a/t/t5515/fetch.master_--tags_.._.git +++ b/t/t5515/fetch.master_--tags_.._.git @@ -1,4 +1,5 @@ # master --tags ../.git +0567da4d5edd2ff4bb292a465ba9e64dcad9536b ../ 6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 not-for-merge tag 'tag-master' of ../ 8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../ 22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../ diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 99c32d7539..926e7f6b97 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -1126,6 +1126,81 @@ test_expect_success 'fetch follows tags by default' ' test_cmp expect actual ' +test_expect_success 'pushing a specific ref applies remote.$name.push as refmap' ' + mk_test testrepo heads/master && + rm -fr src dst && + git init src && + git init --bare dst && + ( + cd src && + git pull ../testrepo master && + git branch next && + git config remote.dst.url ../dst && + git config remote.dst.push "+refs/heads/*:refs/remotes/src/*" && + git push dst master && + git show-ref refs/heads/master | + sed -e "s|refs/heads/|refs/remotes/src/|" >../dst/expect + ) && + ( + cd dst && + test_must_fail git show-ref refs/heads/next && + test_must_fail git show-ref refs/heads/master && + git show-ref refs/remotes/src/master >actual + ) && + test_cmp dst/expect dst/actual +' + +test_expect_success 'with no remote.$name.push, it is not used as refmap' ' + mk_test testrepo heads/master && + rm -fr src dst && + git init src && + git init --bare dst && + ( + cd src && + git pull ../testrepo master && + git branch next && + git config remote.dst.url ../dst && + git config push.default matching && + git push dst master && + git show-ref refs/heads/master >../dst/expect + ) && + ( + cd dst && + test_must_fail git show-ref refs/heads/next && + git show-ref refs/heads/master >actual + ) && + test_cmp dst/expect dst/actual +' + +test_expect_success 'with no remote.$name.push, upstream mapping is used' ' + mk_test testrepo heads/master && + rm -fr src dst && + git init src && + git init --bare dst && + ( + cd src && + git pull ../testrepo master && + git branch next && + git config remote.dst.url ../dst && + git config remote.dst.fetch "+refs/heads/*:refs/remotes/dst/*" && + git config push.default upstream && + + git config branch.master.merge refs/heads/trunk && + git config branch.master.remote dst && + + git push dst master && + git show-ref refs/heads/master | + sed -e "s|refs/heads/master|refs/heads/trunk|" >../dst/expect + ) && + ( + cd dst && + test_must_fail git show-ref refs/heads/master && + test_must_fail git show-ref refs/heads/next && + git show-ref refs/heads/trunk >actual + ) && + test_cmp dst/expect dst/actual +' + test_expect_success 'push does not follow tags by default' ' mk_test testrepo heads/master && rm -fr src dst && diff --git a/t/t5525-fetch-tagopt.sh b/t/t5525-fetch-tagopt.sh index 4fbf7a120f..45815f7378 100755 --- a/t/t5525-fetch-tagopt.sh +++ b/t/t5525-fetch-tagopt.sh @@ -8,7 +8,8 @@ setup_clone () { git clone --mirror . $1 && git remote add remote_$1 $1 && (cd $1 && - git tag tag_$1) + git tag tag_$1 && + git branch branch_$1) } test_expect_success setup ' @@ -21,21 +22,33 @@ test_expect_success setup ' test_expect_success "fetch with tagopt=--no-tags does not get tag" ' git fetch remote_one && - test_must_fail git show-ref tag_one + test_must_fail git show-ref tag_one && + git show-ref remote_one/branch_one ' test_expect_success "fetch --tags with tagopt=--no-tags gets tag" ' + ( + cd one && + git branch second_branch_one + ) && git fetch --tags remote_one && - git show-ref tag_one + git show-ref tag_one && + git show-ref remote_one/second_branch_one ' test_expect_success "fetch --no-tags with tagopt=--tags does not get tag" ' git fetch --no-tags remote_two && - test_must_fail git show-ref tag_two + test_must_fail git show-ref tag_two && + git show-ref remote_two/branch_two ' test_expect_success "fetch with tagopt=--tags gets tag" ' + ( + cd two && + git branch second_branch_two + ) && git fetch remote_two && - git show-ref tag_two + git show-ref tag_two && + git show-ref remote_two/second_branch_two ' test_done diff --git a/t/t5536-fetch-conflicts.sh b/t/t5536-fetch-conflicts.sh new file mode 100755 index 0000000000..6c5d3a4ce0 --- /dev/null +++ b/t/t5536-fetch-conflicts.sh @@ -0,0 +1,100 @@ +#!/bin/sh + +test_description='fetch handles conflicting refspecs correctly' + +. ./test-lib.sh + +D=$(pwd) + +setup_repository () { + git init "$1" && ( + cd "$1" && + git config remote.origin.url "$D" && + shift && + for refspec in "$@" + do + git config --add remote.origin.fetch "$refspec" + done + ) +} + +verify_stderr () { + cat >expected && + # We're not interested in the error + # "fatal: The remote end hung up unexpectedly": + grep -E '^(fatal|warning):' <error | grep -v 'hung up' >actual | sort && + test_cmp expected actual +} + +test_expect_success 'setup' ' + git commit --allow-empty -m "Initial" && + git branch branch1 && + git tag tag1 && + git commit --allow-empty -m "First" && + git branch branch2 && + git tag tag2 +' + +test_expect_success 'fetch with no conflict' ' + setup_repository ok "+refs/heads/*:refs/remotes/origin/*" && ( + cd ok && + git fetch origin + ) +' + +test_expect_success 'fetch conflict: config vs. config' ' + setup_repository ccc \ + "+refs/heads/branch1:refs/remotes/origin/branch1" \ + "+refs/heads/branch2:refs/remotes/origin/branch1" && ( + cd ccc && + test_must_fail git fetch origin 2>error && + verify_stderr <<-\EOF + fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1 + EOF + ) +' + +test_expect_success 'fetch duplicate: config vs. config' ' + setup_repository dcc \ + "+refs/heads/*:refs/remotes/origin/*" \ + "+refs/heads/branch1:refs/remotes/origin/branch1" && ( + cd dcc && + git fetch origin + ) +' + +test_expect_success 'fetch conflict: arg overrides config' ' + setup_repository aoc \ + "+refs/heads/*:refs/remotes/origin/*" && ( + cd aoc && + git fetch origin refs/heads/branch2:refs/remotes/origin/branch1 + ) +' + +test_expect_success 'fetch conflict: arg vs. arg' ' + setup_repository caa && ( + cd caa && + test_must_fail git fetch origin \ + refs/heads/*:refs/remotes/origin/* \ + refs/heads/branch2:refs/remotes/origin/branch1 2>error && + verify_stderr <<-\EOF + fatal: Cannot fetch both refs/heads/branch1 and refs/heads/branch2 to refs/remotes/origin/branch1 + EOF + ) +' + +test_expect_success 'fetch conflict: criss-cross args' ' + setup_repository xaa \ + "+refs/heads/*:refs/remotes/origin/*" && ( + cd xaa && + git fetch origin \ + refs/heads/branch1:refs/remotes/origin/branch2 \ + refs/heads/branch2:refs/remotes/origin/branch1 2>error && + verify_stderr <<-\EOF + warning: refs/remotes/origin/branch1 usually tracks refs/heads/branch1, not refs/heads/branch2 + warning: refs/remotes/origin/branch2 usually tracks refs/heads/branch2, not refs/heads/branch1 + EOF + ) +' + +test_done diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh index 01d0d95b4d..5b0198cbc8 100755 --- a/t/t5540-http-push.sh +++ b/t/t5540-http-push.sh @@ -154,7 +154,7 @@ test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \ test_expect_success 'push to password-protected repository (user in URL)' ' test_commit pw-user && - set_askpass user@host && + set_askpass user@host pass@host && git push "$HTTPD_URL_USER/auth/dumb/test_repo.git" HEAD && git rev-parse --verify HEAD >expect && git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \ @@ -168,7 +168,7 @@ test_expect_failure 'user was prompted only once for password' ' test_expect_failure 'push to password-protected repository (no user in URL)' ' test_commit pw-nouser && - set_askpass user@host && + set_askpass user@host pass@host && git push "$HTTPD_URL/auth/dumb/test_repo.git" HEAD && expect_askpass both user@host git rev-parse --verify HEAD >expect && diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh index 470ac54295..bfd241ea8a 100755 --- a/t/t5541-http-push.sh +++ b/t/t5541-http-push.sh @@ -274,7 +274,7 @@ test_expect_success 'push over smart http with auth' ' cd "$ROOT_PATH/test_repo_clone" && echo push-auth-test >expect && test_commit push-auth-test && - set_askpass user@host && + set_askpass user@host pass@host && git push "$HTTPD_URL"/auth/smart/test_repo.git && git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \ log -1 --format=%s >actual && @@ -286,7 +286,7 @@ test_expect_success 'push to auth-only-for-push repo' ' cd "$ROOT_PATH/test_repo_clone" && echo push-half-auth >expect && test_commit push-half-auth && - set_askpass user@host && + set_askpass user@host pass@host && git push "$HTTPD_URL"/auth-push/smart/test_repo.git && git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \ log -1 --format=%s >actual && @@ -316,7 +316,7 @@ test_expect_success 'push into half-auth-complete requires password' ' cd "$ROOT_PATH/half-auth-clone" && echo two >expect && test_commit two && - set_askpass user@host && + set_askpass user@host pass@host && git push "$HTTPD_URL/half-auth-complete/smart/half-auth.git" && git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git" \ log -1 --format=%s >actual && diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh index f7d0f146f0..8392624714 100755 --- a/t/t5550-http-fetch.sh +++ b/t/t5550-http-fetch.sh @@ -62,13 +62,13 @@ test_expect_success 'http auth can use user/pass in URL' ' ' test_expect_success 'http auth can use just user in URL' ' - set_askpass user@host && + set_askpass wrong pass@host && git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-pass && expect_askpass pass user@host ' test_expect_success 'http auth can request both user and pass' ' - set_askpass user@host && + set_askpass user@host pass@host && git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-both && expect_askpass both user@host ' @@ -77,7 +77,7 @@ test_expect_success 'http auth respects credential helper config' ' test_config_global credential.helper "!f() { cat >/dev/null echo username=user@host - echo password=user@host + echo password=pass@host }; f" && set_askpass wrong && git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-helper && @@ -86,14 +86,14 @@ test_expect_success 'http auth respects credential helper config' ' test_expect_success 'http auth can get username from config' ' test_config_global "credential.$HTTPD_URL.username" user@host && - set_askpass user@host && + set_askpass wrong pass@host && git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-user && expect_askpass pass user@host ' test_expect_success 'configured username does not override URL' ' test_config_global "credential.$HTTPD_URL.username" wrong && - set_askpass user@host && + set_askpass wrong pass@host && git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-user2 && expect_askpass pass user@host ' diff --git a/t/t5551-http-fetch.sh b/t/t5551-http-fetch.sh index afb439e09c..a124efe114 100755 --- a/t/t5551-http-fetch.sh +++ b/t/t5551-http-fetch.sh @@ -119,7 +119,7 @@ test_expect_success 'redirects re-root further requests' ' test_expect_success 'clone from password-protected repository' ' echo two >expect && - set_askpass user@host && + set_askpass user@host pass@host && git clone --bare "$HTTPD_URL/auth/smart/repo.git" smart-auth && expect_askpass both user@host && git --git-dir=smart-auth log -1 --format=%s >actual && @@ -137,7 +137,7 @@ test_expect_success 'clone from auth-only-for-push repository' ' test_expect_success 'clone from auth-only-for-objects repository' ' echo two >expect && - set_askpass user@host && + set_askpass user@host pass@host && git clone --bare "$HTTPD_URL/auth-fetch/smart/repo.git" half-auth && expect_askpass both user@host && git --git-dir=half-auth log -1 --format=%s >actual && @@ -151,7 +151,7 @@ test_expect_success 'no-op half-auth fetch does not require a password' ' ' test_expect_success 'redirects send auth to new location' ' - set_askpass user@host && + set_askpass user@host pass@host && git -c credential.useHttpPath=true \ clone $HTTPD_URL/smart-redir-auth/repo.git repo-redir-auth && expect_askpass both user@host auth/smart/repo.git diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index 1d1c8755ea..62fbd7e664 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -280,25 +280,26 @@ test_expect_success 'clone checking out a tag' ' test_cmp fetch.expected fetch.actual ' -test_expect_success 'setup ssh wrapper' ' - write_script "$TRASH_DIRECTORY/ssh-wrapper" <<-\EOF && - echo >>"$TRASH_DIRECTORY/ssh-output" "ssh: $*" && - # throw away all but the last argument, which should be the - # command - while test $# -gt 1; do shift; done - eval "$1" - EOF - - GIT_SSH="$TRASH_DIRECTORY/ssh-wrapper" && - export GIT_SSH && - export TRASH_DIRECTORY -' - -clear_ssh () { - >"$TRASH_DIRECTORY/ssh-output" +setup_ssh_wrapper () { + test_expect_success 'setup ssh wrapper' ' + write_script "$TRASH_DIRECTORY/ssh-wrapper" <<-\EOF && + echo >>"$TRASH_DIRECTORY/ssh-output" "ssh: $*" && + # throw away all but the last argument, which should be the + # command + while test $# -gt 1; do shift; done + eval "$1" + EOF + GIT_SSH="$TRASH_DIRECTORY/ssh-wrapper" && + export GIT_SSH && + export TRASH_DIRECTORY && + >"$TRASH_DIRECTORY"/ssh-output + ' } expect_ssh () { + test_when_finished ' + (cd "$TRASH_DIRECTORY" && rm -f ssh-expect && >ssh-output) + ' && { case "$1" in none) @@ -310,25 +311,114 @@ expect_ssh () { (cd "$TRASH_DIRECTORY" && test_cmp ssh-expect ssh-output) } -test_expect_success 'cloning myhost:src uses ssh' ' - clear_ssh && +setup_ssh_wrapper + +test_expect_success 'clone myhost:src uses ssh' ' git clone myhost:src ssh-clone && expect_ssh myhost src ' test_expect_success NOT_MINGW,NOT_CYGWIN 'clone local path foo:bar' ' - clear_ssh && cp -R src "foo:bar" && - git clone "./foo:bar" foobar && + git clone "foo:bar" foobar && expect_ssh none ' test_expect_success 'bracketed hostnames are still ssh' ' - clear_ssh && git clone "[myhost:123]:src" ssh-bracket-clone && expect_ssh myhost:123 src ' +counter=0 +# $1 url +# $2 none|host +# $3 path +test_clone_url () { + counter=$(($counter + 1)) + test_might_fail git clone "$1" tmp$counter && + expect_ssh "$2" "$3" +} + +test_expect_success NOT_MINGW 'clone c:temp is ssl' ' + test_clone_url c:temp c temp +' + +test_expect_success MINGW 'clone c:temp is dos drive' ' + test_clone_url c:temp none +' + +#ip v4 +for repo in rep rep/home/project 123 +do + test_expect_success "clone host:$repo" ' + test_clone_url host:$repo host $repo + ' +done + +#ipv6 +for repo in rep rep/home/project 123 +do + test_expect_success "clone [::1]:$repo" ' + test_clone_url [::1]:$repo ::1 $repo + ' +done +#home directory +test_expect_success "clone host:/~repo" ' + test_clone_url host:/~repo host "~repo" +' + +test_expect_success "clone [::1]:/~repo" ' + test_clone_url [::1]:/~repo ::1 "~repo" +' + +# Corner cases +for url in foo/bar:baz [foo]bar/baz:qux [foo/bar]:baz +do + test_expect_success "clone $url is not ssh" ' + test_clone_url $url none + ' +done + +#with ssh:// scheme +test_expect_success 'clone ssh://host.xz/home/user/repo' ' + test_clone_url "ssh://host.xz/home/user/repo" host.xz "/home/user/repo" +' + +# from home directory +test_expect_success 'clone ssh://host.xz/~repo' ' + test_clone_url "ssh://host.xz/~repo" host.xz "~repo" +' + +# with port number +test_expect_success 'clone ssh://host.xz:22/home/user/repo' ' + test_clone_url "ssh://host.xz:22/home/user/repo" "-p 22 host.xz" "/home/user/repo" +' + +# from home directory with port number +test_expect_success 'clone ssh://host.xz:22/~repo' ' + test_clone_url "ssh://host.xz:22/~repo" "-p 22 host.xz" "~repo" +' + +#IPv6 +test_expect_success 'clone ssh://[::1]/home/user/repo' ' + test_clone_url "ssh://[::1]/home/user/repo" "::1" "/home/user/repo" +' + +#IPv6 from home directory +test_expect_success 'clone ssh://[::1]/~repo' ' + test_clone_url "ssh://[::1]/~repo" "::1" "~repo" +' + +#IPv6 with port number +test_expect_success 'clone ssh://[::1]:22/home/user/repo' ' + test_clone_url "ssh://[::1]:22/home/user/repo" "-p 22 ::1" "/home/user/repo" +' + +#IPv6 from home directory with port number +test_expect_success 'clone ssh://[::1]:22/~repo' ' + test_clone_url "ssh://[::1]:22/~repo" "-p 22 ::1" "~repo" +' + test_expect_success 'clone from a repository with two identical branches' ' ( diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh index f80bba871c..39b3238da2 100755 --- a/t/t6010-merge-base.sh +++ b/t/t6010-merge-base.sh @@ -230,4 +230,71 @@ test_expect_success 'criss-cross merge-base for octopus-step' ' test_cmp expected.sorted actual.sorted ' +test_expect_success 'using reflog to find the fork point' ' + git reset --hard && + git checkout -b base $E && + + ( + for count in 1 2 3 + do + git commit --allow-empty -m "Base commit #$count" && + git rev-parse HEAD >expect$count && + git checkout -B derived && + git commit --allow-empty -m "Derived #$count" && + git rev-parse HEAD >derived$count && + git checkout -B base $E || exit 1 + done + + for count in 1 2 3 + do + git merge-base --fork-point base $(cat derived$count) >actual && + test_cmp expect$count actual || exit 1 + done + + ) && + # check that we correctly default to HEAD + git checkout derived && + git merge-base --fork-point base >actual && + test_cmp expect3 actual +' + +test_expect_success 'merge-base --octopus --all for complex tree' ' + # Best common ancestor for JE, JAA and JDD is JC + # JE + # / | + # / | + # / | + # JAA / | + # |\ / | + # | \ | JDD | + # | \ |/ | | + # | JC JD | + # | | /| | + # | |/ | | + # JA | | | + # |\ /| | | + # X JB | X X + # \ \ | / / + # \__\|/___/ + # J + test_commit J && + test_commit JB && + git reset --hard J && + test_commit JC && + git reset --hard J && + test_commit JTEMP1 && + test_merge JA JB && + test_merge JAA JC && + git reset --hard J && + test_commit JTEMP2 && + test_merge JD JB && + test_merge JDD JC && + git reset --hard J && + test_commit JTEMP3 && + test_merge JE JC && + git rev-parse JC >expected && + git merge-base --all --octopus JAA JDD JE >actual && + test_cmp expected actual +' + test_done diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh index f00cebff3e..d00f7db868 100755 --- a/t/t6018-rev-list-glob.sh +++ b/t/t6018-rev-list-glob.sh @@ -129,6 +129,18 @@ test_expect_success 'rev-parse --remotes=foo' ' ' +test_expect_success 'rev-parse --exclude with --branches' ' + compare rev-parse "--exclude=*/* --branches" "master someref subspace-x" +' + +test_expect_success 'rev-parse --exclude with --all' ' + compare rev-parse "--exclude=refs/remotes/* --all" "--branches --tags" +' + +test_expect_success 'rev-parse accumulates multiple --exclude' ' + compare rev-parse "--exclude=refs/remotes/* --exclude=refs/tags/* --all" --branches +' + test_expect_success 'rev-list --glob=refs/heads/subspace/*' ' compare rev-list "subspace/one subspace/two" "--glob=refs/heads/subspace/*" @@ -231,6 +243,48 @@ test_expect_success 'rev-list --remotes=foo' ' ' +test_expect_success 'rev-list --exclude with --branches' ' + compare rev-list "--exclude=*/* --branches" "master someref subspace-x" +' + +test_expect_success 'rev-list --exclude with --all' ' + compare rev-list "--exclude=refs/remotes/* --all" "--branches --tags" +' + +test_expect_success 'rev-list accumulates multiple --exclude' ' + compare rev-list "--exclude=refs/remotes/* --exclude=refs/tags/* --all" --branches +' + + +# "git rev-list<ENTER>" is likely to be a bug in the calling script and may +# deserve an error message, but do cases where set of refs programatically +# given using globbing and/or --stdin need to fail with the same error, or +# are we better off reporting a success with no output? The following few +# tests document the current behaviour to remind us that we might want to +# think about this issue. + +test_expect_failure 'rev-list may want to succeed with empty output on no input (1)' ' + >expect && + git rev-list --stdin <expect >actual && + test_cmp expect actual +' + +test_expect_failure 'rev-list may want to succeed with empty output on no input (2)' ' + >expect && + git rev-list --exclude=* --all >actual && + test_cmp expect actual +' + +test_expect_failure 'rev-list may want to succeed with empty output on no input (3)' ' + ( + test_create_repo empty && + cd empty && + >expect && + git rev-list --all >actual && + test_cmp expect actual + ) +' + test_expect_success 'shortlog accepts --glob/--tags/--remotes' ' compare shortlog "subspace/one subspace/two" --branches=subspace && diff --git a/t/t6031-merge-recursive.sh b/t/t6031-merge-recursive.sh index 1cd649e245..a953f1b55c 100755 --- a/t/t6031-merge-recursive.sh +++ b/t/t6031-merge-recursive.sh @@ -2,7 +2,6 @@ test_description='merge-recursive: handle file mode' . ./test-lib.sh -. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh test_expect_success 'mode change in one branch: keep changed version' ' : >file1 && diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh index 7d47984352..719a11673b 100755 --- a/t/t6050-replace.sh +++ b/t/t6050-replace.sh @@ -276,6 +276,48 @@ test_expect_success '-f option bypasses the type check' ' git replace -f HEAD^ $BLOB ' +test_expect_success 'git cat-file --batch works on replace objects' ' + git replace | grep $PARA3 && + echo $PARA3 | git cat-file --batch +' + +test_expect_success 'test --format bogus' ' + test_must_fail git replace --format bogus >/dev/null 2>&1 +' + +test_expect_success 'test --format short' ' + git replace --format=short >actual && + git replace >expected && + test_cmp expected actual +' + +test_expect_success 'test --format medium' ' + H1=$(git --no-replace-objects rev-parse HEAD~1) && + HT=$(git --no-replace-objects rev-parse HEAD^{tree}) && + MYTAG=$(git --no-replace-objects rev-parse mytag) && + { + echo "$H1 -> $BLOB" && + echo "$BLOB -> $REPLACED" && + echo "$HT -> $H1" && + echo "$PARA3 -> $S" && + echo "$MYTAG -> $HASH1" + } | sort >expected && + git replace -l --format medium | sort > actual && + test_cmp expected actual +' + +test_expect_success 'test --format long' ' + { + echo "$H1 (commit) -> $BLOB (blob)" && + echo "$BLOB (blob) -> $REPLACED (blob)" && + echo "$HT (tree) -> $H1 (commit)" && + echo "$PARA3 (commit) -> $S (commit)" && + echo "$MYTAG (tag) -> $HASH1 (commit)" + } | sort >expected && + git replace --format=long | sort > actual && + test_cmp expected actual +' + test_expect_success 'replace ref cleanup' ' test -n "$(git replace)" && git replace -d $(git replace) && diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh index 7ea14ced31..10b1452766 100755 --- a/t/t6101-rev-parse-parents.sh +++ b/t/t6101-rev-parse-parents.sh @@ -20,7 +20,17 @@ test_expect_success 'setup' ' test_commit start2 && git checkout master && git merge -m next start2 && - test_commit final + test_commit final && + + test_seq 40 | + while read i + do + git checkout --orphan "b$i" && + test_tick && + git commit --allow-empty -m "$i" && + commit=$(git rev-parse --verify HEAD) && + printf "$commit " >>.git/info/grafts + done ' test_expect_success 'start is valid' ' @@ -79,6 +89,10 @@ test_expect_success 'final^1^! = final^1 ^final^1^1 ^final^1^2' ' test_cmp expect actual ' +test_expect_success 'large graft octopus' ' + test_cmp_rev_output b31 "git rev-parse --verify b1^30" +' + test_expect_success 'repack for next test' ' git repack -a -d ' diff --git a/t/t6132-pathspec-exclude.sh b/t/t6132-pathspec-exclude.sh new file mode 100755 index 0000000000..62049be0c7 --- /dev/null +++ b/t/t6132-pathspec-exclude.sh @@ -0,0 +1,184 @@ +#!/bin/sh + +test_description='test case exclude pathspec' + +. ./test-lib.sh + +test_expect_success 'setup' ' + for p in file sub/file sub/sub/file sub/file2 sub/sub/sub/file sub2/file; do + if echo $p | grep /; then + mkdir -p `dirname $p` + fi && + : >$p && + git add $p && + git commit -m $p + done && + git log --oneline --format=%s >actual && + cat <<EOF >expect && +sub2/file +sub/sub/sub/file +sub/file2 +sub/sub/file +sub/file +file +EOF + test_cmp expect actual +' + +test_expect_success 'exclude only should error out' ' + test_must_fail git log --oneline --format=%s -- ":(exclude)sub" +' + +test_expect_success 't_e_i() exclude sub' ' + git log --oneline --format=%s -- . ":(exclude)sub" >actual + cat <<EOF >expect && +sub2/file +file +EOF + test_cmp expect actual +' + +test_expect_success 't_e_i() exclude sub/sub/file' ' + git log --oneline --format=%s -- . ":(exclude)sub/sub/file" >actual + cat <<EOF >expect && +sub2/file +sub/sub/sub/file +sub/file2 +sub/file +file +EOF + test_cmp expect actual +' + +test_expect_success 't_e_i() exclude sub using mnemonic' ' + git log --oneline --format=%s -- . ":!sub" >actual + cat <<EOF >expect && +sub2/file +file +EOF + test_cmp expect actual +' + +test_expect_success 't_e_i() exclude :(icase)SUB' ' + git log --oneline --format=%s -- . ":(exclude,icase)SUB" >actual + cat <<EOF >expect && +sub2/file +file +EOF + test_cmp expect actual +' + +test_expect_success 't_e_i() exclude sub2 from sub' ' + ( + cd sub && + git log --oneline --format=%s -- :/ ":/!sub2" >actual + cat <<EOF >expect && +sub/sub/sub/file +sub/file2 +sub/sub/file +sub/file +file +EOF + test_cmp expect actual + ) +' + +test_expect_success 't_e_i() exclude sub/*file' ' + git log --oneline --format=%s -- . ":(exclude)sub/*file" >actual + cat <<EOF >expect && +sub2/file +sub/file2 +file +EOF + test_cmp expect actual +' + +test_expect_success 't_e_i() exclude :(glob)sub/*/file' ' + git log --oneline --format=%s -- . ":(exclude,glob)sub/*/file" >actual + cat <<EOF >expect && +sub2/file +sub/sub/sub/file +sub/file2 +sub/file +file +EOF + test_cmp expect actual +' + +test_expect_success 'm_p_d() exclude sub' ' + git ls-files -- . ":(exclude)sub" >actual + cat <<EOF >expect && +file +sub2/file +EOF + test_cmp expect actual +' + +test_expect_success 'm_p_d() exclude sub/sub/file' ' + git ls-files -- . ":(exclude)sub/sub/file" >actual + cat <<EOF >expect && +file +sub/file +sub/file2 +sub/sub/sub/file +sub2/file +EOF + test_cmp expect actual +' + +test_expect_success 'm_p_d() exclude sub using mnemonic' ' + git ls-files -- . ":!sub" >actual + cat <<EOF >expect && +file +sub2/file +EOF + test_cmp expect actual +' + +test_expect_success 'm_p_d() exclude :(icase)SUB' ' + git ls-files -- . ":(exclude,icase)SUB" >actual + cat <<EOF >expect && +file +sub2/file +EOF + test_cmp expect actual +' + +test_expect_success 'm_p_d() exclude sub2 from sub' ' + ( + cd sub && + git ls-files -- :/ ":/!sub2" >actual + cat <<EOF >expect && +../file +file +file2 +sub/file +sub/sub/file +EOF + test_cmp expect actual + ) +' + +test_expect_success 'm_p_d() exclude sub/*file' ' + git ls-files -- . ":(exclude)sub/*file" >actual + cat <<EOF >expect && +file +sub/file2 +sub2/file +EOF + test_cmp expect actual +' + +test_expect_success 'm_p_d() exclude :(glob)sub/*/file' ' + git ls-files -- . ":(exclude,glob)sub/*/file" >actual + cat <<EOF >expect && +file +sub/file +sub/file2 +sub/sub/sub/file +sub2/file +EOF + test_cmp expect actual +' + +test_done diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index da5fb6c917..bda354c1c4 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -18,16 +18,13 @@ setdate_and_increment () { export GIT_COMMITTER_DATE GIT_AUTHOR_DATE } -test_expect_success 'Create sample commit with known timestamp' ' +test_expect_success setup ' setdate_and_increment && echo "Using $datestamp" > one && git add one && git commit -m "Initial" && setdate_and_increment && - git tag -a -m "Tagging at $datestamp" testtag -' - -test_expect_success 'Create upstream config' ' + git tag -a -m "Tagging at $datestamp" testtag && git update-ref refs/remotes/origin/master master && git remote add origin nowhere && git config branch.master.remote origin && @@ -52,8 +49,8 @@ test_atom head refname refs/heads/master test_atom head upstream refs/remotes/origin/master test_atom head objecttype commit test_atom head objectsize 171 -test_atom head objectname 67a36f10722846e891fbada1ba48ed035de75581 -test_atom head tree 0e51c00fcb93dffc755546f27593d511e1bdb46f +test_atom head objectname $(git rev-parse refs/heads/master) +test_atom head tree $(git rev-parse refs/heads/master^{tree}) test_atom head parent '' test_atom head numparent 0 test_atom head object '' @@ -82,16 +79,17 @@ test_atom head contents:body '' test_atom head contents:signature '' test_atom head contents 'Initial ' +test_atom head HEAD '*' test_atom tag refname refs/tags/testtag test_atom tag upstream '' test_atom tag objecttype tag test_atom tag objectsize 154 -test_atom tag objectname 98b46b1d36e5b07909de1b3886224e3e81e87322 +test_atom tag objectname $(git rev-parse refs/tags/testtag) test_atom tag tree '' test_atom tag parent '' test_atom tag numparent '' -test_atom tag object '67a36f10722846e891fbada1ba48ed035de75581' +test_atom tag object $(git rev-parse refs/tags/testtag^0) test_atom tag type 'commit' test_atom tag '*objectname' '67a36f10722846e891fbada1ba48ed035de75581' test_atom tag '*objecttype' 'commit' @@ -117,6 +115,7 @@ test_atom tag contents:body '' test_atom tag contents:signature '' test_atom tag contents 'Tagging at 1151939927 ' +test_atom tag HEAD ' ' test_expect_success 'Check invalid atoms names are errors' ' test_must_fail git for-each-ref --format="%(INVALID)" refs/heads @@ -308,8 +307,35 @@ test_expect_success 'Check short upstream format' ' test_cmp expected actual ' +test_expect_success 'setup for upstream:track[short]' ' + test_commit two +' + +cat >expected <<EOF +[ahead 1] +EOF + +test_expect_success 'Check upstream:track format' ' + git for-each-ref --format="%(upstream:track)" refs/heads >actual && + test_cmp expected actual +' + +cat >expected <<EOF +> +EOF + +test_expect_success 'Check upstream:trackshort format' ' + git for-each-ref --format="%(upstream:trackshort)" refs/heads >actual && + test_cmp expected actual +' + +test_expect_success 'Check that :track[short] cannot be used with other atoms' ' + test_must_fail git for-each-ref --format="%(refname:track)" 2>/dev/null && + test_must_fail git for-each-ref --format="%(refname:trackshort)" 2>/dev/null +' + cat >expected <<EOF -67a36f1 +$(git rev-parse --short HEAD) EOF test_expect_success 'Check short objectname format' ' @@ -321,6 +347,23 @@ test_expect_success 'Check for invalid refname format' ' test_must_fail git for-each-ref --format="%(refname:INVALID)" ' +get_color () +{ + git config --get-color no.such.slot "$1" +} + +cat >expected <<EOF +$(git rev-parse --short refs/heads/master) $(get_color green)master$(get_color reset) +$(git rev-parse --short refs/remotes/origin/master) $(get_color green)origin/master$(get_color reset) +$(git rev-parse --short refs/tags/testtag) $(get_color green)testtag$(get_color reset) +$(git rev-parse --short refs/tags/two) $(get_color green)two$(get_color reset) +EOF + +test_expect_success 'Check %(color:...) ' ' + git for-each-ref --format="%(objectname:short) %(color:green)%(refname:short)" >actual && + test_cmp expected actual +' + cat >expected <<\EOF heads/master tags/master @@ -460,9 +503,9 @@ test_atom refs/tags/signed-long contents "subject line body contents $sig" -cat >expected <<\EOF -408fe76d02a785a006c2e9c669b7be5589ede96d <committer@example.com> refs/tags/master -90b5ebede4899eda64893bc2a4c8f1d6fb6dfc40 <committer@example.com> refs/tags/bogo +cat >expected <<EOF +$(git rev-parse refs/tags/master) <committer@example.com> refs/tags/master +$(git rev-parse refs/tags/bogo) <committer@example.com> refs/tags/bogo EOF test_expect_success 'Verify sort with multiple keys' ' diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index b90e985a48..3bfdfed1f7 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -70,6 +70,35 @@ test_expect_success \ rm -f idontexist untracked1 untracked2 \ path0/idontexist path0/untracked1 path0/untracked2 \ .git/index.lock +rmdir path1 + +test_expect_success \ + 'moving to absent target with trailing slash' \ + 'test_must_fail git mv path0/COPYING no-such-dir/ && + test_must_fail git mv path0/COPYING no-such-dir// && + git mv path0/ no-such-dir/ && + test_path_is_dir no-such-dir' + +test_expect_success \ + 'clean up' \ + 'git reset --hard' + +test_expect_success \ + 'moving to existing untracked target with trailing slash' \ + 'mkdir path1 && + git mv path0/ path1/ && + test_path_is_dir path1/path0/' + +test_expect_success \ + 'moving to existing tracked target with trailing slash' \ + 'mkdir path2 && + >path2/file && git add path2/file && + git mv path1/path0/ path2/ && + test_path_is_dir path2/path0/' + +test_expect_success \ + 'clean up' \ + 'git reset --hard' test_expect_success \ 'adding another file' \ diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index 0825a928df..0246e80b1a 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -762,6 +762,17 @@ test_expect_success 'submodule update clone shallow submodule' ' (cd submodule && test 1 = $(git log --oneline | wc -l) ) +) +' + +test_expect_success 'submodule update --recursive drops module name before recursing' ' + (cd super2 && + (cd deeper/submodule/subsubmodule && + git checkout HEAD^ + ) && + git submodule update --recursive deeper/submodule >actual && + test_i18ngrep "Submodule path .deeper/submodule/subsubmodule.: checked out" actual ) ' + test_done diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh index b64c9ed8e7..7ca10b8606 100755 --- a/t/t7407-submodule-foreach.sh +++ b/t/t7407-submodule-foreach.sh @@ -325,4 +325,13 @@ test_expect_success 'command passed to foreach --recursive retains notion of std test_cmp expected actual ' +test_expect_success 'multi-argument command passed to foreach is not shell-evaluated twice' ' + ( + cd super && + git submodule foreach "echo \\\"quoted\\\"" > ../expected && + git submodule foreach echo \"quoted\" > ../actual + ) && + test_cmp expected actual +' + test_done diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh index da5bd3b5a5..2ddf28c984 100755 --- a/t/t7507-commit-verbose.sh +++ b/t/t7507-commit-verbose.sh @@ -65,9 +65,35 @@ test_expect_success 'diff in message is retained without -v' ' check_message diff ' -test_expect_failure 'diff in message is retained with -v' ' +test_expect_success 'diff in message is retained with -v' ' git commit --amend -F diff -v && check_message diff ' +test_expect_success 'submodule log is stripped out too with -v' ' + git config diff.submodule log && + git submodule add ./. sub && + git commit -m "sub added" && + ( + cd sub && + echo "more" >>file && + git commit -a -m "submodule commit" + ) && + ( + GIT_EDITOR=cat && + export GIT_EDITOR && + test_must_fail git commit -a -v 2>err + ) && + test_i18ngrep "Aborting commit due to empty commit message." err +' + +test_expect_success 'verbose diff is stripped out with set core.commentChar' ' + ( + GIT_EDITOR=cat && + export GIT_EDITOR && + test_must_fail git -c core.commentchar=";" commit -a -v 2>err + ) && + test_i18ngrep "Aborting commit due to empty commit message." err +' + test_done diff --git a/t/t9150/make-svk-dump b/t/t9150/make-svk-dump index 2242f14ebe..2242f14ebe 100644..100755 --- a/t/t9150/make-svk-dump +++ b/t/t9150/make-svk-dump diff --git a/t/t9151/make-svnmerge-dump b/t/t9151/make-svnmerge-dump index e1e138cb1a..e1e138cb1a 100644..100755 --- a/t/t9151/make-svnmerge-dump +++ b/t/t9151/make-svnmerge-dump diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh index 3fb3368903..812c9cd462 100755 --- a/t/t9200-git-cvsexportcommit.sh +++ b/t/t9200-git-cvsexportcommit.sh @@ -5,7 +5,6 @@ test_description='Test export of commits to CVS' . ./test-lib.sh -. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh if ! test_have_prereq PERL; then skip_all='skipping git cvsexportcommit tests, perl not available' diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index 2f79146e6c..aeae3ca769 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -1,4 +1,5 @@ -#!/bin/sh +# Library of functions shared by all tests scripts, included by +# test-lib.sh. # # Copyright (c) 2005 Junio C Hamano # diff --git a/t/test-lib.sh b/t/test-lib.sh index b25249ec4c..1531c241c0 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -1,4 +1,4 @@ -#!/bin/sh +# Test framework for git. See t/README for usage. # # Copyright (c) 2005 Junio C Hamano # @@ -26,6 +26,10 @@ then # outside of t/, e.g. for running tests on the test library # itself. TEST_DIRECTORY=$(pwd) +else + # ensure that TEST_DIRECTORY is an absolute path so that it + # is valid even if the current working directory is changed + TEST_DIRECTORY=$(cd "$TEST_DIRECTORY" && pwd) || exit 1 fi if test -z "$TEST_OUTPUT_DIRECTORY" then @@ -477,8 +481,6 @@ test_at_end_hook_ () { test_done () { GIT_EXIT_OK=t - # Note: t0000 relies on $HARNESS_ACTIVE disabling the .counts - # output file if test -z "$HARNESS_ACTIVE" then test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results" @@ -573,11 +575,9 @@ then make_valgrind_symlink () { # handle only executables, unless they are shell libraries that - # need to be in the exec-path. We will just use "#!" as a - # guess for a shell-script, since we have no idea what the user - # may have configured as the shell path. + # need to be in the exec-path. test -x "$1" || - test "#!" = "$(head -c 2 <"$1")" || + test "# " = "$(head -c 2 <"$1")" || return; base=$(basename "$1") @@ -830,6 +830,10 @@ test_lazy_prereq SYMLINKS ' ln -s x y && test -h y ' +test_lazy_prereq FILEMODE ' + test "$(git config --bool core.filemode)" = true +' + test_lazy_prereq CASE_INSENSITIVE_FS ' echo good >CamelCase && echo bad >camelcase && @@ -86,7 +86,7 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size) return -1; bufptr += 48; /* "object " + sha1 + "\n" */ - if (prefixcmp(bufptr, "type ")) + if (!starts_with(bufptr, "type ")) return -1; bufptr += 5; nl = memchr(bufptr, '\n', tail - bufptr); @@ -109,7 +109,7 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size) item->tagged = NULL; } - if (bufptr + 4 < tail && !prefixcmp(bufptr, "tag ")) + if (bufptr + 4 < tail && starts_with(bufptr, "tag ")) ; /* good */ else return -1; @@ -120,7 +120,7 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size) item->tag = xmemdupz(bufptr, nl - bufptr); bufptr = nl + 1; - if (bufptr + 7 < tail && !prefixcmp(bufptr, "tagger ")) + if (bufptr + 7 < tail && starts_with(bufptr, "tagger ")) item->date = parse_tag_date(bufptr, tail); else item->date = 0; @@ -160,8 +160,8 @@ size_t parse_signature(const char *buf, unsigned long size) { char *eol; size_t len = 0; - while (len < size && prefixcmp(buf + len, PGP_SIGNATURE) && - prefixcmp(buf + len, PGP_MESSAGE)) { + while (len < size && !starts_with(buf + len, PGP_SIGNATURE) && + !starts_with(buf + len, PGP_MESSAGE)) { eol = memchr(buf + len, '\n', size - len); len += eol ? eol - (buf + len) + 1 : size - len; } diff --git a/test-line-buffer.c b/test-line-buffer.c index ef1d7bae14..1e58f0476f 100644 --- a/test-line-buffer.c +++ b/test-line-buffer.c @@ -19,7 +19,7 @@ static void handle_command(const char *command, const char *arg, struct line_buf { switch (*command) { case 'b': - if (!prefixcmp(command, "binary ")) { + if (starts_with(command, "binary ")) { struct strbuf sb = STRBUF_INIT; strbuf_addch(&sb, '>'); buffer_read_binary(buf, &sb, strtouint32(arg)); @@ -28,12 +28,12 @@ static void handle_command(const char *command, const char *arg, struct line_buf return; } case 'c': - if (!prefixcmp(command, "copy ")) { + if (starts_with(command, "copy ")) { buffer_copy_bytes(buf, strtouint32(arg)); return; } case 's': - if (!prefixcmp(command, "skip ")) { + if (starts_with(command, "skip ")) { buffer_skip_bytes(buf, strtouint32(arg)); return; } diff --git a/test-string-list.c b/test-string-list.c index 00ce6c9a12..14bdf9d215 100644 --- a/test-string-list.c +++ b/test-string-list.c @@ -38,7 +38,7 @@ static void write_list_compact(const struct string_list *list) static int prefix_cb(struct string_list_item *item, void *cb_data) { const char *prefix = (const char *)cb_data; - return !prefixcmp(item->string, prefix); + return starts_with(item->string, prefix); } int main(int argc, char **argv) diff --git a/transport-helper.c b/transport-helper.c index 673b7c214f..2010674bb4 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -190,7 +190,7 @@ 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 && !prefixcmp(capname, "refspec ")) { + else if (!data->refspecs && starts_with(capname, "refspec ")) { ALLOC_GROW(refspecs, refspec_nr + 1, refspec_alloc); @@ -199,17 +199,17 @@ static struct child_process *get_helper(struct transport *transport) data->connect = 1; } else if (!strcmp(capname, "signed-tags")) { data->signed_tags = 1; - } else if (!prefixcmp(capname, "export-marks ")) { + } else if (starts_with(capname, "export-marks ")) { struct strbuf arg = STRBUF_INIT; strbuf_addstr(&arg, "--export-marks="); strbuf_addstr(&arg, capname + strlen("export-marks ")); data->export_marks = strbuf_detach(&arg, NULL); - } else if (!prefixcmp(capname, "import-marks")) { + } else if (starts_with(capname, "import-marks")) { struct strbuf arg = STRBUF_INIT; strbuf_addstr(&arg, "--import-marks="); strbuf_addstr(&arg, capname + strlen("import-marks ")); data->import_marks = strbuf_detach(&arg, NULL); - } else if (!prefixcmp(capname, "no-private-update")) { + } else if (starts_with(capname, "no-private-update")) { data->no_private_update = 1; } else if (mandatory) { die("Unknown mandatory capability %s. This remote " @@ -311,7 +311,7 @@ static int set_helper_option(struct transport *transport, if (!strcmp(buf.buf, "ok")) ret = 0; - else if (!prefixcmp(buf.buf, "error")) { + else if (starts_with(buf.buf, "error")) { ret = -1; } else if (!strcmp(buf.buf, "unsupported")) ret = 1; @@ -375,7 +375,7 @@ static int fetch_with_fetch(struct transport *transport, while (1) { recvline(data, &buf); - if (!prefixcmp(buf.buf, "lock ")) { + if (starts_with(buf.buf, "lock ")) { const char *name = buf.buf + 5; if (transport->pack_lockfile) warning("%s also locked %s", data->name, name); @@ -646,10 +646,10 @@ static int push_update_ref_status(struct strbuf *buf, char *refname, *msg; int status; - if (!prefixcmp(buf->buf, "ok ")) { + if (starts_with(buf->buf, "ok ")) { status = REF_STATUS_OK; refname = buf->buf + 3; - } else if (!prefixcmp(buf->buf, "error ")) { + } else if (starts_with(buf->buf, "error ")) { status = REF_STATUS_REMOTE_REJECT; refname = buf->buf + 6; } else diff --git a/transport.c b/transport.c index 7202b7777d..824c5b93f9 100644 --- a/transport.c +++ b/transport.c @@ -169,13 +169,13 @@ static void set_upstreams(struct transport *transport, struct ref *refs, remotename = ref->name; tmp = resolve_ref_unsafe(localname, sha, 1, &flag); if (tmp && flag & REF_ISSYMREF && - !prefixcmp(tmp, "refs/heads/")) + starts_with(tmp, "refs/heads/")) localname = tmp; /* Both source and destination must be local branches. */ - if (!localname || prefixcmp(localname, "refs/heads/")) + if (!localname || !starts_with(localname, "refs/heads/")) continue; - if (!remotename || prefixcmp(remotename, "refs/heads/")) + if (!remotename || !starts_with(remotename, "refs/heads/")) continue; if (!pretend) @@ -191,7 +191,7 @@ static void set_upstreams(struct transport *transport, struct ref *refs, static const char *rsync_url(const char *url) { - return prefixcmp(url, "rsync://") ? skip_prefix(url, "rsync:") : url; + return !starts_with(url, "rsync://") ? skip_prefix(url, "rsync:") : url; } static struct ref *get_refs_via_rsync(struct transport *transport, int for_push) @@ -296,8 +296,8 @@ static int write_one_ref(const char *name, const unsigned char *sha1, FILE *f; /* when called via for_each_ref(), flags is non-zero */ - if (flags && prefixcmp(name, "refs/heads/") && - prefixcmp(name, "refs/tags/")) + if (flags && !starts_with(name, "refs/heads/") && + !starts_with(name, "refs/tags/")) return 0; strbuf_addstr(buf, name); @@ -652,7 +652,7 @@ static void print_ok_ref_status(struct ref *ref, int porcelain) print_ref_status('-', "[deleted]", ref, NULL, NULL, porcelain); else if (is_null_sha1(ref->old_sha1)) print_ref_status('*', - (!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" : + (starts_with(ref->name, "refs/tags/") ? "[new tag]" : "[new branch]"), ref, ref->peer_ref, NULL, porcelain); else { @@ -885,14 +885,6 @@ void transport_take_over(struct transport *transport, transport->cannot_reuse = 1; } -static int is_local(const char *url) -{ - const char *colon = strchr(url, ':'); - const char *slash = strchr(url, '/'); - return !colon || (slash && slash < colon) || - has_dos_drive_prefix(url); -} - static int is_file(const char *url) { struct stat buf; @@ -930,18 +922,18 @@ struct transport *transport_get(struct remote *remote, const char *url) while (is_urlschemechar(p == url, *p)) p++; - if (!prefixcmp(p, "::")) + if (starts_with(p, "::")) helper = xstrndup(url, p - url); } if (helper) { transport_helper_init(ret, helper); - } else if (!prefixcmp(url, "rsync:")) { + } else if (starts_with(url, "rsync:")) { ret->get_refs_list = get_refs_via_rsync; ret->fetch = fetch_objs_via_rsync; ret->push = rsync_transport_push; ret->smart_options = NULL; - } else if (is_local(url) && is_file(url) && is_bundle(url, 1)) { + } else if (url_is_local_not_ssh(url) && is_file(url) && is_bundle(url, 1)) { struct bundle_transport_data *data = xcalloc(1, sizeof(*data)); ret->data = data; ret->get_refs_list = get_refs_from_bundle; @@ -949,11 +941,11 @@ struct transport *transport_get(struct remote *remote, const char *url) ret->disconnect = close_bundle; ret->smart_options = NULL; } else if (!is_url(url) - || !prefixcmp(url, "file://") - || !prefixcmp(url, "git://") - || !prefixcmp(url, "ssh://") - || !prefixcmp(url, "git+ssh://") - || !prefixcmp(url, "ssh+git://")) { + || starts_with(url, "file://") + || starts_with(url, "git://") + || starts_with(url, "ssh://") + || starts_with(url, "git+ssh://") + || starts_with(url, "ssh+git://")) { /* These are builtin smart transports. */ struct git_transport_data *data = xcalloc(1, sizeof(*data)); ret->data = data; @@ -1297,7 +1289,7 @@ char *transport_anonymize_url(const char *url) size_t anon_len, prefix_len = 0; anon_part = strchr(url, '@'); - if (is_local(url) || !anon_part) + if (url_is_local_not_ssh(url) || !anon_part) goto literal_copy; anon_len = strlen(++anon_part); diff --git a/tree-walk.c b/tree-walk.c index 5ece8c3477..680afda060 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -662,9 +662,10 @@ static int match_wildcard_base(const struct pathspec_item *item, * Pre-condition: either baselen == base_offset (i.e. empty path) * or base[baselen-1] == '/' (i.e. with trailing slash). */ -enum interesting tree_entry_interesting(const struct name_entry *entry, - struct strbuf *base, int base_offset, - const struct pathspec *ps) +static enum interesting do_match(const struct name_entry *entry, + struct strbuf *base, int base_offset, + const struct pathspec *ps, + int exclude) { int i; int pathlen, baselen = base->len - base_offset; @@ -676,7 +677,8 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, PATHSPEC_MAXDEPTH | PATHSPEC_LITERAL | PATHSPEC_GLOB | - PATHSPEC_ICASE); + PATHSPEC_ICASE | + PATHSPEC_EXCLUDE); if (!ps->nr) { if (!ps->recursive || @@ -697,6 +699,10 @@ enum interesting tree_entry_interesting(const struct name_entry *entry, const char *base_str = base->buf + base_offset; int matchlen = item->len, matched = 0; + if ((!exclude && item->magic & PATHSPEC_EXCLUDE) || + ( exclude && !(item->magic & PATHSPEC_EXCLUDE))) + continue; + if (baselen >= matchlen) { /* If it doesn't match, move along... */ if (!match_dir_prefix(item, base_str, match, matchlen)) @@ -782,3 +788,72 @@ match_wildcards: } return never_interesting; /* No matches */ } + +/* + * Is a tree entry interesting given the pathspec we have? + * + * Pre-condition: either baselen == base_offset (i.e. empty path) + * or base[baselen-1] == '/' (i.e. with trailing slash). + */ +enum interesting tree_entry_interesting(const struct name_entry *entry, + struct strbuf *base, int base_offset, + const struct pathspec *ps) +{ + enum interesting positive, negative; + positive = do_match(entry, base, base_offset, ps, 0); + + /* + * case | entry | positive | negative | result + * -----+-------+----------+----------+------- + * 1 | file | -1 | -1..2 | -1 + * 2 | file | 0 | -1..2 | 0 + * 3 | file | 1 | -1 | 1 + * 4 | file | 1 | 0 | 1 + * 5 | file | 1 | 1 | 0 + * 6 | file | 1 | 2 | 0 + * 7 | file | 2 | -1 | 2 + * 8 | file | 2 | 0 | 2 + * 9 | file | 2 | 1 | 0 + * 10 | file | 2 | 2 | -1 + * -----+-------+----------+----------+------- + * 11 | dir | -1 | -1..2 | -1 + * 12 | dir | 0 | -1..2 | 0 + * 13 | dir | 1 | -1 | 1 + * 14 | dir | 1 | 0 | 1 + * 15 | dir | 1 | 1 | 1 (*) + * 16 | dir | 1 | 2 | 0 + * 17 | dir | 2 | -1 | 2 + * 18 | dir | 2 | 0 | 2 + * 19 | dir | 2 | 1 | 1 (*) + * 20 | dir | 2 | 2 | -1 + * + * (*) An exclude pattern interested in a directory does not + * necessarily mean it will exclude all of the directory. In + * wildcard case, it can't decide until looking at individual + * files inside. So don't write such directories off yet. + */ + + if (!(ps->magic & PATHSPEC_EXCLUDE) || + positive <= entry_not_interesting) /* #1, #2, #11, #12 */ + return positive; + + negative = do_match(entry, base, base_offset, ps, 1); + + /* #3, #4, #7, #8, #13, #14, #17, #18 */ + if (negative <= entry_not_interesting) + return positive; + + /* #15, #19 */ + if (S_ISDIR(entry->mode) && + positive >= entry_interesting && + negative == entry_interesting) + return entry_interesting; + + if ((positive == entry_interesting && + negative >= entry_interesting) || /* #5, #6, #16 */ + (positive == all_entries_interesting && + negative == entry_interesting)) /* #9 */ + return entry_not_interesting; + + return all_entries_not_interesting; /* #10, #20 */ +} diff --git a/unpack-trees.c b/unpack-trees.c index ad3e9a04fe..164354dad7 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -830,23 +830,24 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str } static int clear_ce_flags_1(struct cache_entry **cache, int nr, - char *prefix, int prefix_len, + struct strbuf *prefix, int select_mask, int clear_mask, struct exclude_list *el, int defval); /* Whole directory matching */ static int clear_ce_flags_dir(struct cache_entry **cache, int nr, - char *prefix, int prefix_len, + struct strbuf *prefix, char *basename, int select_mask, int clear_mask, struct exclude_list *el, int defval) { struct cache_entry **cache_end; int dtype = DT_DIR; - int ret = is_excluded_from_list(prefix, prefix_len, + int ret = is_excluded_from_list(prefix->buf, prefix->len, basename, &dtype, el); + int rc; - prefix[prefix_len++] = '/'; + strbuf_addch(prefix, '/'); /* If undecided, use matching result of parent dir in defval */ if (ret < 0) @@ -854,7 +855,7 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr, for (cache_end = cache; cache_end != cache + nr; cache_end++) { struct cache_entry *ce = *cache_end; - if (strncmp(ce->name, prefix, prefix_len)) + if (strncmp(ce->name, prefix->buf, prefix->len)) break; } @@ -865,10 +866,12 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr, * calling clear_ce_flags_1(). That function will call * the expensive is_excluded_from_list() on every entry. */ - return clear_ce_flags_1(cache, cache_end - cache, - prefix, prefix_len, - select_mask, clear_mask, - el, ret); + rc = clear_ce_flags_1(cache, cache_end - cache, + prefix, + select_mask, clear_mask, + el, ret); + strbuf_setlen(prefix, prefix->len - 1); + return rc; } /* @@ -887,7 +890,7 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr, * Top level path has prefix_len zero. */ static int clear_ce_flags_1(struct cache_entry **cache, int nr, - char *prefix, int prefix_len, + struct strbuf *prefix, int select_mask, int clear_mask, struct exclude_list *el, int defval) { @@ -907,10 +910,10 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr, continue; } - if (prefix_len && strncmp(ce->name, prefix, prefix_len)) + if (prefix->len && strncmp(ce->name, prefix->buf, prefix->len)) break; - name = ce->name + prefix_len; + name = ce->name + prefix->len; slash = strchr(name, '/'); /* If it's a directory, try whole directory match first */ @@ -918,29 +921,26 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr, int processed; len = slash - name; - memcpy(prefix + prefix_len, name, len); + strbuf_add(prefix, name, len); - /* - * terminate the string (no trailing slash), - * clear_c_f_dir needs it - */ - prefix[prefix_len + len] = '\0'; processed = clear_ce_flags_dir(cache, cache_end - cache, - prefix, prefix_len + len, - prefix + prefix_len, + prefix, + prefix->buf + prefix->len - len, select_mask, clear_mask, el, defval); /* clear_c_f_dir eats a whole dir already? */ if (processed) { cache += processed; + strbuf_setlen(prefix, prefix->len - len); continue; } - prefix[prefix_len + len++] = '/'; + strbuf_addch(prefix, '/'); cache += clear_ce_flags_1(cache, cache_end - cache, - prefix, prefix_len + len, + prefix, select_mask, clear_mask, el, defval); + strbuf_setlen(prefix, prefix->len - len - 1); continue; } @@ -961,9 +961,12 @@ static int clear_ce_flags(struct cache_entry **cache, int nr, int select_mask, int clear_mask, struct exclude_list *el) { - char prefix[PATH_MAX]; + static struct strbuf prefix = STRBUF_INIT; + + strbuf_reset(&prefix); + return clear_ce_flags_1(cache, nr, - prefix, 0, + &prefix, select_mask, clear_mask, el, 0); } diff --git a/upload-pack.c b/upload-pack.c index c989a737f9..ec56cdbce9 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -394,7 +394,7 @@ static int get_common_commits(void) got_other = 0; continue; } - if (!prefixcmp(line, "have ")) { + if (starts_with(line, "have ")) { switch (got_sha1(line+5, sha1)) { case -1: /* they have what we do not */ got_other = 1; @@ -540,7 +540,7 @@ static void receive_needs(void) if (!line) break; - if (!prefixcmp(line, "shallow ")) { + if (starts_with(line, "shallow ")) { unsigned char sha1[20]; struct object *object; if (get_sha1_hex(line + 8, sha1)) @@ -556,14 +556,14 @@ static void receive_needs(void) } continue; } - if (!prefixcmp(line, "deepen ")) { + if (starts_with(line, "deepen ")) { char *end; depth = strtol(line + 7, &end, 0); if (end == line + 7 || depth <= 0) die("Invalid deepen: %s", line); continue; } - if (prefixcmp(line, "want ") || + if (!starts_with(line, "want ") || get_sha1_hex(line+5, sha1_buf)) die("git upload-pack: protocol error, " "expected to get sha, not '%s'", line); @@ -649,8 +649,7 @@ static void receive_needs(void) /* make sure the real parents are parsed */ unregister_shallow(object->sha1); object->parsed = 0; - if (parse_commit((struct commit *)object)) - die("invalid commit"); + parse_commit_or_die((struct commit *)object); parents = ((struct commit *)object)->parents; while (parents) { add_object_array(&parents->item->object, @@ -815,7 +814,7 @@ int main(int argc, char **argv) strict = 1; continue; } - if (!prefixcmp(arg, "--timeout=")) { + if (starts_with(arg, "--timeout=")) { timeout = atoi(arg+10); daemon_mode = 1; continue; diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c index f2b23c81de..bd0f2c2b86 100644 --- a/vcs-svn/fast_export.c +++ b/vcs-svn/fast_export.c @@ -162,22 +162,13 @@ static void die_short_read(struct line_buffer *input) die("invalid dump: unexpected end of file"); } -static int ends_with(const char *s, size_t len, const char *suffix) -{ - const size_t suffixlen = strlen(suffix); - if (len < suffixlen) - return 0; - return !memcmp(s + len - suffixlen, suffix, suffixlen); -} - static int parse_cat_response_line(const char *header, off_t *len) { - size_t headerlen = strlen(header); uintmax_t n; const char *type; const char *end; - if (ends_with(header, headerlen, " missing")) + if (ends_with(header, " missing")) return error("cat-blob reports missing blob: %s", header); type = strstr(header, " blob "); if (!type) diff --git a/wt-status.c b/wt-status.c index b4e44baa29..4e55810059 100644 --- a/wt-status.c +++ b/wt-status.c @@ -15,6 +15,10 @@ #include "submodule.h" #include "column.h" #include "strbuf.h" +#include "utf8.h" + +static char cut_line[] = +"------------------------ >8 ------------------------\n"; static char default_wt_status_colors[][COLOR_MAXLEN] = { GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */ @@ -264,6 +268,30 @@ static void wt_status_print_unmerged_data(struct wt_status *s, strbuf_release(&onebuf); } +static const char *wt_status_diff_status_string(int status) +{ + switch (status) { + case DIFF_STATUS_ADDED: + return _("new file"); + case DIFF_STATUS_COPIED: + return _("copied"); + case DIFF_STATUS_DELETED: + return _("deleted"); + case DIFF_STATUS_MODIFIED: + return _("modified"); + case DIFF_STATUS_RENAMED: + return _("renamed"); + case DIFF_STATUS_TYPE_CHANGED: + return _("typechange"); + case DIFF_STATUS_UNKNOWN: + return _("unknown"); + case DIFF_STATUS_UNMERGED: + return _("unmerged"); + default: + return NULL; + } +} + static void wt_status_print_change_data(struct wt_status *s, int change_type, struct string_list_item *it) @@ -276,6 +304,23 @@ static void wt_status_print_change_data(struct wt_status *s, const char *one, *two; struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT; struct strbuf extra = STRBUF_INIT; + static char *padding; + const char *what; + int len; + + if (!padding) { + int width = 0; + /* If DIFF_STATUS_* uses outside this range, we're in trouble */ + for (status = 'A'; status <= 'Z'; status++) { + what = wt_status_diff_status_string(status); + len = what ? strlen(what) : 0; + if (len > width) + width = len; + } + width += 2; /* colon and a space */ + padding = xmallocz(width); + memset(padding, ' ', width); + } one_name = two_name = it->string; switch (change_type) { @@ -307,34 +352,18 @@ static void wt_status_print_change_data(struct wt_status *s, two = quote_path(two_name, s->prefix, &twobuf); status_printf(s, color(WT_STATUS_HEADER, s), "\t"); - switch (status) { - case DIFF_STATUS_ADDED: - status_printf_more(s, c, _("new file: %s"), one); - break; - case DIFF_STATUS_COPIED: - status_printf_more(s, c, _("copied: %s -> %s"), one, two); - break; - case DIFF_STATUS_DELETED: - status_printf_more(s, c, _("deleted: %s"), one); - break; - case DIFF_STATUS_MODIFIED: - status_printf_more(s, c, _("modified: %s"), one); - break; - case DIFF_STATUS_RENAMED: - status_printf_more(s, c, _("renamed: %s -> %s"), one, two); - break; - case DIFF_STATUS_TYPE_CHANGED: - status_printf_more(s, c, _("typechange: %s"), one); - break; - case DIFF_STATUS_UNKNOWN: - status_printf_more(s, c, _("unknown: %s"), one); - break; - case DIFF_STATUS_UNMERGED: - status_printf_more(s, c, _("unmerged: %s"), one); - break; - default: + what = wt_status_diff_status_string(status); + if (!what) die(_("bug: unhandled diff status %c"), status); - } + /* 1 for colon, which is not part of "what" */ + len = strlen(padding) - (utf8_strwidth(what) + 1); + assert(len >= 0); + if (status == DIFF_STATUS_COPIED || status == DIFF_STATUS_RENAMED) + status_printf_more(s, c, "%s:%.*s%s -> %s", + what, len, padding, one, two); + else + status_printf_more(s, c, "%s:%.*s%s", + what, len, padding, one); if (extra.len) { status_printf_more(s, color(WT_STATUS_HEADER, s), "%s", extra.buf); strbuf_release(&extra); @@ -767,6 +796,18 @@ conclude: status_printf_ln(s, GIT_COLOR_NORMAL, ""); } +void wt_status_truncate_message_at_cut_line(struct strbuf *buf) +{ + const char *p; + struct strbuf pattern = STRBUF_INIT; + + strbuf_addf(&pattern, "%c %s", comment_line_char, cut_line); + p = strstr(buf->buf, pattern.buf); + if (p && (p == buf->buf || p[-1] == '\n')) + strbuf_setlen(buf, p - buf->buf); + strbuf_release(&pattern); +} + static void wt_status_print_verbose(struct wt_status *s) { struct rev_info rev; @@ -787,10 +828,20 @@ static void wt_status_print_verbose(struct wt_status *s) * If we're not going to stdout, then we definitely don't * want color, since we are going to the commit message * file (and even the "auto" setting won't work, since it - * will have checked isatty on stdout). + * will have checked isatty on stdout). But we then do want + * to insert the scissor line here to reliably remove the + * diff before committing. */ - if (s->fp != stdout) + if (s->fp != stdout) { + const char *explanation = _("Do not touch the line above.\nEverything below will be removed."); + struct strbuf buf = STRBUF_INIT; + rev.diffopt.use_color = 0; + fprintf(s->fp, "%c %s", comment_line_char, cut_line); + strbuf_add_commented_lines(&buf, explanation, strlen(explanation)); + fputs(buf.buf, s->fp); + strbuf_release(&buf); + } run_diff_index(&rev, 1); } @@ -803,7 +854,7 @@ static void wt_status_print_tracking(struct wt_status *s) int i; assert(s->branch && !s->is_initial); - if (prefixcmp(s->branch, "refs/heads/")) + if (!starts_with(s->branch, "refs/heads/")) return; branch = branch_get(s->branch + 11); if (!format_tracking_info(branch, &sb)) @@ -1062,9 +1113,9 @@ static char *read_and_strip_branch(const char *path) strbuf_setlen(&sb, sb.len - 1); if (!sb.len) goto got_nothing; - if (!prefixcmp(sb.buf, "refs/heads/")) + if (starts_with(sb.buf, "refs/heads/")) strbuf_remove(&sb,0, strlen("refs/heads/")); - else if (!prefixcmp(sb.buf, "refs/")) + else if (starts_with(sb.buf, "refs/")) ; else if (!get_sha1_hex(sb.buf, sha1)) { const char *abbrev; @@ -1094,7 +1145,7 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1, struct grab_1st_switch_cbdata *cb = cb_data; const char *target = NULL, *end; - if (prefixcmp(message, "checkout: moving from ")) + if (!starts_with(message, "checkout: moving from ")) return 0; message += strlen("checkout: moving from "); target = strstr(message, " to "); @@ -1129,9 +1180,9 @@ static void wt_status_get_detached_from(struct wt_status_state *state) ((commit = lookup_commit_reference_gently(sha1, 1)) != NULL && !hashcmp(cb.nsha1, commit->object.sha1)))) { int ofs; - if (!prefixcmp(ref, "refs/tags/")) + if (starts_with(ref, "refs/tags/")) ofs = strlen("refs/tags/"); - else if (!prefixcmp(ref, "refs/remotes/")) + else if (starts_with(ref, "refs/remotes/")) ofs = strlen("refs/remotes/"); else ofs = 0; @@ -1220,7 +1271,7 @@ void wt_status_print(struct wt_status *s) if (s->branch) { const char *on_what = _("On branch "); const char *branch_name = s->branch; - if (!prefixcmp(branch_name, "refs/heads/")) + if (starts_with(branch_name, "refs/heads/")) branch_name += 11; else if (!strcmp(branch_name, "HEAD")) { branch_status_color = color(WT_STATUS_NOBRANCH, s); @@ -1421,7 +1472,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s) return; branch_name = s->branch; - if (!prefixcmp(branch_name, "refs/heads/")) + if (starts_with(branch_name, "refs/heads/")) branch_name += 11; else if (!strcmp(branch_name, "HEAD")) { branch_name = _("HEAD (no branch)"); diff --git a/wt-status.h b/wt-status.h index 6c29e6f5e5..30a4812583 100644 --- a/wt-status.h +++ b/wt-status.h @@ -91,6 +91,7 @@ struct wt_status_state { unsigned char cherry_pick_head_sha1[20]; }; +void wt_status_truncate_message_at_cut_line(struct strbuf *); void wt_status_prepare(struct wt_status *s); void wt_status_print(struct wt_status *s); void wt_status_collect(struct wt_status *s); |