diff options
320 files changed, 5411 insertions, 3370 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/CodingGuidelines b/Documentation/CodingGuidelines index a600e35c81..ef67b53f72 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -260,9 +260,11 @@ Writing Documentation: Every user-visible change should be reflected in the documentation. The same general rule as for code applies -- imitate the existing - conventions. A few commented examples follow to provide reference - when writing or modifying command usage strings and synopsis sections - in the manual pages: + conventions. + + A few commented examples follow to provide reference when writing or + modifying command usage strings and synopsis sections in the manual + pages: Placeholders are spelled in lowercase and enclosed in angle brackets: <file> @@ -312,3 +314,29 @@ Writing Documentation: Use 'git' (all lowercase) when talking about commands i.e. something the user would type into a shell and use 'Git' (uppercase first letter) when talking about the version control system and its properties. + + A few commented examples follow to provide reference when writing or + modifying paragraphs or option/command explanations that contain options + or commands: + + Literal examples (e.g. use of command-line options, command names, and + configuration variables) are typeset in monospace, and if you can use + `backticks around word phrases`, do so. + `--pretty=oneline` + `git rev-list` + `remote.pushdefault` + + Word phrases enclosed in `backtick characters` are rendered literally + and will not be further expanded. The use of `backticks` to achieve the + previous rule means that literal examples should not use AsciiDoc + escapes. + Correct: + `--pretty=oneline` + Incorrect: + `\--pretty=oneline` + + If some place in the documentation needs to typeset a command usage + example with inline substitutions, it is fine to use +monospaced and + inline substituted text+ instead of `monospaced literal text`, and with + the former, the part that should not get substituted must be + quoted/escaped. diff --git a/Documentation/RelNotes/1.8.4.5.txt b/Documentation/RelNotes/1.8.4.5.txt new file mode 100644 index 0000000000..215bd1a7a2 --- /dev/null +++ b/Documentation/RelNotes/1.8.4.5.txt @@ -0,0 +1,13 @@ +Git v1.8.4.5 Release Notes +========================== + +Fixes since v1.8.4.4 +-------------------- + + * Recent update to remote-hg that attempted to make it work better + with non ASCII pathnames fed Unicode strings to the underlying Hg + API, which was wrong. + + * "git submodule init" copied "submodule.$name.update" settings from + .gitmodules to .git/config without making sure if the suggested + value was sensible. diff --git a/Documentation/RelNotes/1.8.5.1.txt b/Documentation/RelNotes/1.8.5.1.txt new file mode 100644 index 0000000000..7236aaf232 --- /dev/null +++ b/Documentation/RelNotes/1.8.5.1.txt @@ -0,0 +1,9 @@ +Git v1.8.5.1 Release Notes +========================== + +Fixes since v1.8.5 +------------------ + + * "git submodule init" copied "submodule.$name.update" settings from + .gitmodules to .git/config without making sure if the suggested + value was sensible. diff --git a/Documentation/RelNotes/1.8.5.2.txt b/Documentation/RelNotes/1.8.5.2.txt new file mode 100644 index 0000000000..3ac4984f10 --- /dev/null +++ b/Documentation/RelNotes/1.8.5.2.txt @@ -0,0 +1,20 @@ +Git v1.8.5.2 Release Notes +========================== + +Fixes since v1.8.5.1 +-------------------- + + * "git diff -- ':(icase)makefile'" was unnecessarily rejected at the + command line parser. + + * "git cat-file --batch-check=ok" did not check the existence of + the named object. + + * "git am --abort" sometimes complained about not being able to write + a tree with an 0{40} object in it. + + * 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. + +Also contains typofixes, documentation updates and trivial code clean-ups. 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/SubmittingPatches b/Documentation/SubmittingPatches index 705557689d..e6d46edbe7 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -139,8 +139,15 @@ People on the Git mailing list need to be able to read and comment on the changes you are submitting. It is important for a developer to be able to "quote" your changes, using standard e-mail tools, so that they may comment on specific portions of -your code. For this reason, all patches should be submitted -"inline". If your log message (including your name on the +your code. For this reason, each patch should be submitted +"inline" in a separate message. + +Multiple related patches should be grouped into their own e-mail +thread to help readers find all parts of the series. To that end, +send them as replies to either an additional "cover letter" message +(see below), the first patch, or the respective preceding patch. + +If your log message (including your name on the Signed-off-by line) is not writable in ASCII, make sure that you send off a message in the correct encoding. 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-cherry.txt b/Documentation/git-cherry.txt index 2d0daae626..0ea921a593 100644 --- a/Documentation/git-cherry.txt +++ b/Documentation/git-cherry.txt @@ -3,7 +3,7 @@ git-cherry(1) NAME ---- -git-cherry - Find commits not merged upstream +git-cherry - Find commits yet to be applied to upstream SYNOPSIS -------- @@ -12,46 +12,26 @@ SYNOPSIS DESCRIPTION ----------- -The changeset (or "diff") of each commit between the fork-point and <head> -is compared against each commit between the fork-point and <upstream>. -The diffs are compared after removing any whitespace and line numbers. +Determine whether there are commits in `<head>..<upstream>` that are +equivalent to those in the range `<limit>..<head>`. -Every commit that doesn't exist in the <upstream> branch -has its id (sha1) reported, prefixed by a symbol. The ones that have -equivalent change already -in the <upstream> branch are prefixed with a minus (-) sign, and those -that only exist in the <head> branch are prefixed with a plus (+) symbol: - - __*__*__*__*__> <upstream> - / - fork-point - \__+__+__-__+__+__-__+__> <head> - - -If a <limit> has been given then the commits along the <head> branch up -to and including <limit> are not reported: - - __*__*__*__*__> <upstream> - / - fork-point - \__*__*__<limit>__-__+__> <head> - - -Because 'git cherry' compares the changeset rather than the commit id -(sha1), you can use 'git cherry' to find out if a commit you made locally -has been applied <upstream> under a different commit id. For example, -this will happen if you're feeding patches <upstream> via email rather -than pushing or pulling commits directly. +The equivalence test is based on the diff, after removing whitespace +and line numbers. git-cherry therefore detects when commits have been +"copied" by means of linkgit:git-cherry-pick[1], linkgit:git-am[1] or +linkgit:git-rebase[1]. +Outputs the SHA1 of every commit in `<limit>..<head>`, prefixed with +`-` for commits that have an equivalent in <upstream>, and `+` for +commits that do not. OPTIONS ------- -v:: - Verbose. + Show the commit subjects next to the SHA1s. <upstream>:: - Upstream branch to compare against. - Defaults to the first tracked remote branch, if available. + Upstream branch to search for equivalent commits. + Defaults to the upstream branch of HEAD. <head>:: Working branch; defaults to HEAD. @@ -59,6 +39,103 @@ OPTIONS <limit>:: Do not report commits up to (and including) limit. +EXAMPLES +-------- + +Patch workflows +~~~~~~~~~~~~~~~ + +git-cherry is frequently used in patch-based workflows (see +linkgit:gitworkflows[7]) to determine if a series of patches has been +applied by the upstream maintainer. In such a workflow you might +create and send a topic branch like this: + +------------ +$ git checkout -b topic origin/master +# work and create some commits +$ git format-patch origin/master +$ git send-email ... 00* +------------ + +Later, you can see whether your changes have been applied by saying +(still on `topic`): + +------------ +$ git fetch # update your notion of origin/master +$ git cherry -v +------------ + +Concrete example +~~~~~~~~~~~~~~~~ + +In a situation where topic consisted of three commits, and the +maintainer applied two of them, the situation might look like: + +------------ +$ git log --graph --oneline --decorate --boundary origin/master...topic +* 7654321 (origin/master) upstream tip commit +[... snip some other commits ...] +* cccc111 cherry-pick of C +* aaaa111 cherry-pick of A +[... snip a lot more that has happened ...] +| * cccc000 (topic) commit C +| * bbbb000 commit B +| * aaaa000 commit A +|/ +o 1234567 branch point +------------ + +In such cases, git-cherry shows a concise summary of what has yet to +be applied: + +------------ +$ git cherry origin/master topic +- cccc000... commit C ++ bbbb000... commit B +- aaaa000... commit A +------------ + +Here, we see that the commits A and C (marked with `-`) can be +dropped from your `topic` branch when you rebase it on top of +`origin/master`, while the commit B (marked with `+`) still needs to +be kept so that it will be sent to be applied to `origin/master`. + + +Using a limit +~~~~~~~~~~~~~ + +The optional <limit> is useful in cases where your topic is based on +other work that is not in upstream. Expanding on the previous +example, this might look like: + +------------ +$ git log --graph --oneline --decorate --boundary origin/master...topic +* 7654321 (origin/master) upstream tip commit +[... snip some other commits ...] +* cccc111 cherry-pick of C +* aaaa111 cherry-pick of A +[... snip a lot more that has happened ...] +| * cccc000 (topic) commit C +| * bbbb000 commit B +| * aaaa000 commit A +| * 0000fff (base) unpublished stuff F +[... snip ...] +| * 0000aaa unpublished stuff A +|/ +o 1234567 merge-base between upstream and topic +------------ + +By specifying `base` as the limit, you can avoid listing commits +between `base` and `topic`: + +------------ +$ git cherry origin/master topic base +- cccc000... commit C ++ bbbb000... commit B +- aaaa000... commit A +------------ + + SEE ALSO -------- linkgit:git-patch-id[1] diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt index 444b805d35..93b5067946 100644 --- a/Documentation/git-fetch-pack.txt +++ b/Documentation/git-fetch-pack.txt @@ -12,7 +12,7 @@ SYNOPSIS 'git fetch-pack' [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] - [-v] [<host>:]<directory> [<refs>...] + [-v] <repository> [<refs>...] DESCRIPTION ----------- @@ -97,19 +97,18 @@ be in a separate packet, and the list must end with a flush packet. -v:: Run verbosely. -<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. +<repository>:: + The URL to the remote repository. <refs>...:: The remote heads to update from. This is relative to $GIT_DIR (e.g. "HEAD", "refs/heads/master"). When unspecified, update from all heads the remote side has. +SEE ALSO +-------- +linkgit:git-fetch[1] + GIT --- Part of the linkgit:git[1] suite 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-log.txt b/Documentation/git-log.txt index 34097efea7..1f7bc67d6c 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -15,9 +15,9 @@ DESCRIPTION ----------- Shows the commit logs. -The command takes options applicable to the 'git rev-list' +The command takes options applicable to the `git rev-list` command to control what is shown and how, and options applicable to -the 'git diff-*' commands to control how the changes +the `git diff-*` commands to control how the changes each commit introduces are shown. @@ -42,29 +42,27 @@ OPTIONS --use-mailmap:: Use mailmap file to map author and committer names and email - to canonical real names and email addresses. See + addresses to canonical real names and email addresses. See linkgit:git-shortlog[1]. --full-diff:: - Without this flag, "git log -p <path>..." shows commits that + Without this flag, `git log -p <path>...` shows commits that touch the specified paths, and diffs about the same specified paths. With this, the full diff is shown for commits that touch the specified paths; this means that "<path>..." limits only commits, and doesn't limit diff for those commits. + Note that this affects all diff-based output types, e.g. those -produced by --stat etc. +produced by `--stat`, etc. --log-size:: - Before the log message print out its size in bytes. Intended - mainly for porcelain tools consumption. If Git is unable to - produce a valid value size is set to zero. - Note that only message is considered, if also a diff is shown - its size is not included. + Include a line ``log size <number>'' in the output for each commit, + where <number> is the length of that commit's message in bytes. + Intended to speed up tools that read log messages from `git log` + output by allowing them to allocate space in advance. -L <start>,<end>:<file>:: -L :<regex>:<file>:: - Trace the evolution of the line range given by "<start>,<end>" (or the funcname regex <regex>) within the <file>. You may not give any pathspec limiters. This is currently limited to @@ -80,16 +78,16 @@ include::line-range-format.txt[] whole history leading to the current commit). `origin..HEAD` specifies all the commits reachable from the current commit (i.e. `HEAD`), but not from `origin`. For a complete list of - ways to spell <revision range>, see the "Specifying Ranges" + ways to spell <revision range>, see the 'Specifying Ranges' section of linkgit:gitrevisions[7]. [\--] <path>...:: Show only commits that are enough to explain how the files - that match the specified paths came to be. See "History - Simplification" below for details and other simplification + that match the specified paths came to be. See 'History + Simplification' below for details and other simplification modes. + -Paths may need to be prefixed with "\-- " to separate them from +Paths may need to be prefixed with ``\-- '' to separate them from options or the revision range, when confusion arises. include::rev-list-options.txt[] @@ -113,12 +111,12 @@ EXAMPLES `git log v2.6.12.. include/scsi drivers/scsi`:: Show all commits since version 'v2.6.12' that changed any file - in the include/scsi or drivers/scsi subdirectories + in the `include/scsi` or `drivers/scsi` subdirectories `git log --since="2 weeks ago" -- gitk`:: Show the changes during the last two weeks to the file 'gitk'. - The "--" is necessary to avoid confusion with the *branch* named + The ``--'' is necessary to avoid confusion with the *branch* named 'gitk' `git log --name-status release..test`:: @@ -129,7 +127,7 @@ EXAMPLES `git log --follow builtin/rev-list.c`:: - Shows the commits that changed builtin/rev-list.c, including + Shows the commits that changed `builtin/rev-list.c`, including those commits that occurred before the file was given its present name. @@ -147,17 +145,18 @@ EXAMPLES `git log -p -m --first-parent`:: Shows the history including change diffs, but only from the - "main branch" perspective, skipping commits that come from merged + ``main branch'' perspective, skipping commits that come from merged branches, and showing full diffs of changes introduced by the merges. This makes sense only when following a strict policy of merging all topic branches when staying on a single integration branch. `git log -L '/int main/',/^}/:main.c`:: - Shows how the function `main()` in the file 'main.c' evolved + Shows how the function `main()` in the file `main.c` evolved over time. `git log -3`:: + Limits the number of commits to show to 3. DISCUSSION @@ -172,12 +171,12 @@ See linkgit:git-config[1] for core variables and linkgit:git-diff[1] for settings related to diff generation. format.pretty:: - Default for the `--format` option. (See "PRETTY FORMATS" above.) - Defaults to "medium". + Default for the `--format` option. (See 'Pretty Formats' above.) + Defaults to `medium`. i18n.logOutputEncoding:: - Encoding to use when displaying logs. (See "Discussion", above.) - Defaults to the value of `i18n.commitEncoding` if set, UTF-8 + Encoding to use when displaying logs. (See 'Discussion' above.) + Defaults to the value of `i18n.commitEncoding` if set, and UTF-8 otherwise. log.date:: @@ -186,7 +185,7 @@ log.date:: dates like `Sat May 8 19:35:34 2010 -0500`. log.showroot:: - If `false`, 'git log' and related commands will not treat the + If `false`, `git log` and related commands will not treat the initial commit as a big creation event. Any root commits in `git log -p` output would be shown without a diff attached. The default is `true`. @@ -197,7 +196,7 @@ mailmap.*:: notes.displayRef:: Which refs, in addition to the default set by `core.notesRef` or 'GIT_NOTES_REF', to read notes from when showing commit - messages with the 'log' family of commands. See + messages with the `log` family of commands. See linkgit:git-notes[1]. + May be an unabbreviated ref name or a glob and may be specified 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 b73a24a3c7..aec372646f 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,14 +43,17 @@ unreleased) version of Git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v1.8.5/git.html[documentation for release 1.8.5] +* link:v1.8.5.2/git.html[documentation for release 1.8.5.2] * release notes for + link:RelNotes/1.8.5.2.txt[1.8.5.2], + link:RelNotes/1.8.5.1.txt[1.8.5.1], link:RelNotes/1.8.5.txt[1.8.5]. -* link:v1.8.4.4/git.html[documentation for release 1.8.4.4] +* link:v1.8.4.5/git.html[documentation for release 1.8.4.5] * release notes for + link:RelNotes/1.8.4.5.txt[1.8.4.5], link:RelNotes/1.8.4.4.txt[1.8.4.4], link:RelNotes/1.8.4.3.txt[1.8.4.3], link:RelNotes/1.8.4.2.txt[1.8.4.2], @@ -804,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 3146413cce..3f33ca5507 100644 --- a/Documentation/gitcli.txt +++ b/Documentation/gitcli.txt @@ -72,23 +72,23 @@ 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 `git log -1 HEAD` but write `git log -1 HEAD --`; the former will not work if you happen to have a file called `HEAD` in the work tree. - * many commands allow a long option "--option" to be abbreviated + * many commands allow a long option `--option` to be abbreviated only to their unique prefix (e.g. if there is no other option - whose name begins with "opt", you may be able to spell "--opt" to - invoke the "--option" flag), but you should fully spell them out + whose name begins with `opt`, you may be able to spell `--opt` to + invoke the `--option` flag), but you should fully spell them out when writing your scripts; later versions of Git may introduce a - new option whose name shares the same prefix, e.g. "--optimize", + new option whose name shares the same prefix, e.g. `--optimize`, to make a short prefix that used to be unique no longer unique. @@ -149,7 +149,7 @@ prefix of a long option as if it is fully spelled out, but use this with a caution. For example, `git commit --amen` behaves as if you typed `git commit --amend`, but that is true only until a later version of Git introduces another option that shares the same prefix, -e.g `git commit --amenity" option. +e.g. `git commit --amenity` option. Separating argument from the option @@ -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 e4706615be..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 @@ -362,12 +364,12 @@ full pathname may have special meaning: - A leading "`**`" followed by a slash means match in all directories. For example, "`**/foo`" matches file or directory - "`foo`" anywhere, the same as pattern "`foo`". "**/foo/bar" + "`foo`" anywhere, the same as pattern "`foo`". "`**/foo/bar`" matches file or directory "`bar`" anywhere that is directly under directory "`foo`". - - A trailing "/**" matches everything inside. For example, - "abc/**" matches all files inside directory "abc", relative + - A trailing "`/**`" matches everything inside. For example, + "`abc/**`" matches all files inside directory "abc", relative to the location of the `.gitignore` file, with infinite depth. - A slash followed by two consecutive asterisks then a slash @@ -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 ec86d09199..03533af715 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -18,33 +18,27 @@ ordering and formatting options, such as `--reverse`. -<number>:: -n <number>:: --max-count=<number>:: - Limit the number of commits to output. --skip=<number>:: - Skip 'number' commits before starting to show the commit output. --since=<date>:: --after=<date>:: - Show commits more recent than a specific date. --until=<date>:: --before=<date>:: - Show commits older than a specific date. ifdef::git-rev-list[] --max-age=<timestamp>:: --min-age=<timestamp>:: - Limit the commits output to specified time range. endif::git-rev-list[] --author=<pattern>:: --committer=<pattern>:: - Limit the commits output to ones with author/committer header lines that match the specified pattern (regular expression). With more than one `--author=<pattern>`, @@ -52,7 +46,6 @@ endif::git-rev-list[] chosen (similarly for multiple `--committer=<pattern>`). --grep-reflog=<pattern>:: - Limit the commits output to ones with reflog entries that match the specified pattern (regular expression). With more than one `--grep-reflog`, commits whose reflog message @@ -60,7 +53,6 @@ endif::git-rev-list[] error to use this option unless `--walk-reflogs` is in use. --grep=<pattern>:: - Limit the commits output to ones with log message that matches the specified pattern (regular expression). With more than one `--grep=<pattern>`, commits whose message @@ -71,46 +63,39 @@ When `--show-notes` is in effect, the message from the notes as if it is part of the log message. --all-match:: - Limit the commits output to ones that match all given --grep, + Limit the commits output to ones that match all given `--grep`, instead of ones that match at least one. -i:: --regexp-ignore-case:: - - Match the regexp limiting patterns without regard to letters case. + Match the regular expression limiting patterns without regard to letter + case. --basic-regexp:: - Consider the limiting patterns to be basic regular expressions; this is the default. -E:: --extended-regexp:: - Consider the limiting patterns to be extended regular expressions instead of the default basic regular expressions. -F:: --fixed-strings:: - Consider the limiting patterns to be fixed strings (don't interpret pattern as a regular expression). --perl-regexp:: - - Consider the limiting patterns to be Perl-compatible regexp. + Consider the limiting patterns to be Perl-compatible regular expressions. Requires libpcre to be compiled in. --remove-empty:: - Stop when a given path disappears from the tree. --merges:: - Print only merge commits. This is exactly the same as `--min-parents=2`. --no-merges:: - Do not print commits with more than one parent. This is exactly the same as `--max-parents=1`. @@ -118,7 +103,6 @@ if it is part of the log message. --max-parents=<number>:: --no-min-parents:: --no-max-parents:: - Show only commits which have at least (or at most) that many parent commits. In particular, `--max-parents=1` is the same as `--no-merges`, `--min-parents=2` is the same as `--merges`. `--max-parents=0` @@ -138,31 +122,26 @@ parents) and `--max-parents=-1` (negative numbers denote no upper limit). brought in to your history by such a merge. --not:: - Reverses the meaning of the '{caret}' prefix (or lack thereof) - for all following revision specifiers, up to the next '--not'. + for all following revision specifiers, up to the next `--not`. --all:: - Pretend as if all the refs in `refs/` are listed on the command line as '<commit>'. --branches[=<pattern>]:: - Pretend as if all the refs in `refs/heads` are listed on the command line as '<commit>'. If '<pattern>' is given, limit branches to ones matching given shell glob. If pattern lacks '?', '{asterisk}', or '[', '/{asterisk}' at the end is implied. --tags[=<pattern>]:: - Pretend as if all the refs in `refs/tags` are listed on the command line as '<commit>'. If '<pattern>' is given, limit tags to ones matching given shell glob. If pattern lacks '?', '{asterisk}', or '[', '/{asterisk}' at the end is implied. --remotes[=<pattern>]:: - Pretend as if all the refs in `refs/remotes` are listed on the command line as '<commit>'. If '<pattern>' is given, limit remote-tracking branches to ones matching given shell glob. @@ -174,14 +153,27 @@ 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. ---ignore-missing:: +--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. ifndef::git-rev-list[] --bisect:: - Pretend as if the bad bisection ref `refs/bisect/bad` was listed and as if it was followed by `--not` and the good bisection refs `refs/bisect/good-*` on the command @@ -189,7 +181,6 @@ ifndef::git-rev-list[] endif::git-rev-list[] --stdin:: - In addition to the '<commit>' listed on the command line, read them from the standard input. If a '--' separator is seen, stop reading commits and start reading paths to limit the @@ -197,36 +188,32 @@ endif::git-rev-list[] ifdef::git-rev-list[] --quiet:: - Don't print anything to standard output. This form is primarily meant to allow the caller to test the exit status to see if a range of objects is fully connected (or not). It is faster than redirecting stdout - to /dev/null as the output does not have to be formatted. + to `/dev/null` as the output does not have to be formatted. endif::git-rev-list[] --cherry-mark:: - Like `--cherry-pick` (see below) but mark equivalent commits with `=` rather than omitting them, and inequivalent ones with `+`. --cherry-pick:: - Omit any commit that introduces the same change as - another commit on the "other side" when the set of + another commit on the ``other side'' when the set of commits are limited with symmetric difference. + For example, if you have two branches, `A` and `B`, a usual way to list all commits on only one side of them is with `--left-right` (see the example below in the description of -the `--left-right` option). It however shows the commits that were cherry-picked -from the other branch (for example, "3rd on b" may be cherry-picked -from branch A). With this option, such pairs of commits are +the `--left-right` option). However, it shows the commits that were +cherry-picked from the other branch (for example, ``3rd on b'' may be +cherry-picked from branch A). With this option, such pairs of commits are excluded from the output. --left-only:: --right-only:: - List only commits on the respective side of a symmetric range, i.e. only those which would be marked `<` resp. `>` by `--left-right`. @@ -238,7 +225,6 @@ More precisely, `--cherry-pick --right-only --no-merges` gives the exact list. --cherry:: - A synonym for `--right-only --cherry-mark --no-merges`; useful to limit the output to the commits on our side and mark those that have been applied to the other side of a forked history with @@ -247,30 +233,27 @@ list. -g:: --walk-reflogs:: - Instead of walking the commit ancestry chain, walk reflog entries from the most recent one to older ones. When this option is used you cannot specify commits to exclude (that is, '{caret}commit', 'commit1..commit2', nor 'commit1\...commit2' notations cannot be used). + -With '\--pretty' format other than oneline (for obvious reasons), +With `--pretty` format other than `oneline` (for obvious reasons), this causes the output to have two extra lines of information taken from the reflog. By default, 'commit@\{Nth}' notation is used in the output. When the starting commit is specified as 'commit@\{now}', output also uses 'commit@\{timestamp}' notation -instead. Under '\--pretty=oneline', the commit message is +instead. Under `--pretty=oneline`, the commit message is prefixed with this information on the same line. -This option cannot be combined with '\--reverse'. +This option cannot be combined with `--reverse`. See also linkgit:git-reflog[1]. --merge:: - After a failed merge, show refs that touch files having a conflict and don't exist on all heads to merge. --boundary:: - Output excluded boundary commits. Boundary commits are prefixed with `-`. @@ -287,11 +270,9 @@ is how to do it, as there are various strategies to simplify the history. The following options select the commits to be shown: <paths>:: - Commits modifying the given <paths> are selected. --simplify-by-decoration:: - Commits that are referred by some branch or tag are selected. Note that extra commits can be shown to give a meaningful history. @@ -299,33 +280,27 @@ Note that extra commits can be shown to give a meaningful history. The following options affect the way the simplification is performed: Default mode:: - Simplifies the history to the simplest history explaining the final state of the tree. Simplest because it prunes some side branches if the end result is the same (i.e. merging branches with the same content) --full-history:: - Same as the default mode, but does not prune some history. --dense:: - Only the selected commits are shown, plus some to have a meaningful history. --sparse:: - All commits in the simplified history are shown. --simplify-merges:: - - Additional option to '--full-history' to remove some needless + Additional option to `--full-history` to remove some needless merges from the resulting history, as there are no selected commits contributing to this merge. --ancestry-path:: - When given a range of commits to display (e.g. 'commit1..commit2' or 'commit2 {caret}commit1'), only display commits that exist directly on the ancestry chain between the 'commit1' and @@ -352,36 +327,35 @@ The horizontal line of history A---Q is taken to be the first parent of each merge. The commits are: * `I` is the initial commit, in which `foo` exists with contents - "asdf", and a file `quux` exists with contents "quux". Initial + ``asdf'', and a file `quux` exists with contents ``quux''. Initial commits are compared to an empty tree, so `I` is !TREESAME. -* In `A`, `foo` contains just "foo". +* In `A`, `foo` contains just ``foo''. * `B` contains the same change as `A`. Its merge `M` is trivial and hence TREESAME to all parents. -* `C` does not change `foo`, but its merge `N` changes it to "foobar", +* `C` does not change `foo`, but its merge `N` changes it to ``foobar'', so it is not TREESAME to any parent. -* `D` sets `foo` to "baz". Its merge `O` combines the strings from - `N` and `D` to "foobarbaz"; i.e., it is not TREESAME to any parent. +* `D` sets `foo` to ``baz''. Its merge `O` combines the strings from + `N` and `D` to ``foobarbaz''; i.e., it is not TREESAME to any parent. -* `E` changes `quux` to "xyzzy", and its merge `P` combines the - strings to "quux xyzzy". `P` is TREESAME to `O`, but not to `E`. +* `E` changes `quux` to ``xyzzy'', and its merge `P` combines the + strings to ``quux xyzzy''. `P` is TREESAME to `O`, but not to `E`. * `X` is an independent root commit that added a new file `side`, and `Y` modified it. `Y` is TREESAME to `X`. Its merge `Q` added `side` to `P`, and `Q` is TREESAME to `P`, but not to `Y`. -'rev-list' walks backwards through history, including or excluding -commits based on whether '\--full-history' and/or parent rewriting -(via '\--parents' or '\--children') are used. The following settings +`rev-list` walks backwards through history, including or excluding +commits based on whether `--full-history` and/or parent rewriting +(via `--parents` or `--children`) are used. The following settings are available. Default mode:: - Commits are included if they are not TREESAME to any parent - (though this can be changed, see '\--sparse' below). If the + (though this can be changed, see `--sparse` below). If the commit was a merge, and it was TREESAME to one parent, follow only that parent. (Even if there are several TREESAME parents, follow only one of them.) Otherwise, follow all @@ -400,12 +374,11 @@ available, removed `B` from consideration entirely. `C` was considered via `N`, but is TREESAME. Root commits are compared to an empty tree, so `I` is !TREESAME. + -Parent/child relations are only visible with --parents, but that does +Parent/child relations are only visible with `--parents`, but that does not affect the commits selected in default mode, so we have shown the parent lines. --full-history without parent rewriting:: - This mode differs from the default in one point: always follow all parents of a merge, even if it is TREESAME to one of them. Even if more than one side of the merge has commits that are @@ -425,9 +398,8 @@ about the parent/child relationships between the commits, so we show them disconnected. --full-history with parent rewriting:: - Ordinary commits are only included if they are !TREESAME - (though this can be changed, see '\--sparse' below). + (though this can be changed, see `--sparse` below). + Merges are always included. However, their parent list is rewritten: Along each parent, prune away commits that are not included @@ -441,7 +413,7 @@ themselves. This results in `-------------' ----------------------------------------------------------------------- + -Compare to '\--full-history' without rewriting above. Note that `E` +Compare to `--full-history` without rewriting above. Note that `E` was pruned away because it is TREESAME, but the parent list of P was rewritten to contain `E`'s parent `I`. The same happened for `C` and `N`, and `X`, `Y` and `Q`. @@ -450,22 +422,19 @@ In addition to the above settings, you can change whether TREESAME affects inclusion: --dense:: - Commits that are walked are included if they are not TREESAME to any parent. --sparse:: - All commits that are walked are included. + -Note that without '\--full-history', this still simplifies merges: if +Note that without `--full-history`, this still simplifies merges: if one of the parents is TREESAME, we follow only that one, so the other sides of the merge are never walked. --simplify-merges:: - First, build a history graph in the same way that - '\--full-history' with parent rewriting does (see above). + `--full-history` with parent rewriting does (see above). + Then simplify each commit `C` to its replacement `C'` in the final history according to the following rules: @@ -484,7 +453,7 @@ history according to the following rules: -- + The effect of this is best shown by way of comparing to -'\--full-history' with parent rewriting. The example turns into: +`--full-history` with parent rewriting. The example turns into: + ----------------------------------------------------------------------- .-A---M---N---O @@ -494,7 +463,7 @@ The effect of this is best shown by way of comparing to `---------' ----------------------------------------------------------------------- + -Note the major differences in `N`, `P` and `Q` over '--full-history': +Note the major differences in `N`, `P`, and `Q` over `--full-history`: + -- * `N`'s parent list had `I` removed, because it is an ancestor of the @@ -511,11 +480,10 @@ Note the major differences in `N`, `P` and `Q` over '--full-history': Finally, there is a fifth simplification mode available: --ancestry-path:: - Limit the displayed commits to those directly on the ancestry - chain between the "from" and "to" commits in the given commit - range. I.e. only display commits that are ancestor of the "to" - commit, and descendants of the "from" commit. + chain between the ``from'' and ``to'' commits in the given commit + range. I.e. only display commits that are ancestor of the ``to'' + commit and descendants of the ``from'' commit. + As an example use case, consider the following commit history: + @@ -530,14 +498,14 @@ As an example use case, consider the following commit history: A regular 'D..M' computes the set of commits that are ancestors of `M`, but excludes the ones that are ancestors of `D`. This is useful to see what happened to the history leading to `M` since `D`, in the sense -that "what does `M` have that did not exist in `D`". The result in this +that ``what does `M` have that did not exist in `D`''. The result in this example would be all the commits, except `A` and `B` (and `D` itself, of course). + When we want to find out what commits in `M` are contaminated with the bug introduced by `D` and need fixing, however, we might want to view only the subset of 'D..M' that are actually descendants of `D`, i.e. -excluding `C` and `K`. This is exactly what the '--ancestry-path' +excluding `C` and `K`. This is exactly what the `--ancestry-path` option does. Applied to the 'D..M' range, it results in: + ----------------------------------------------------------------------- @@ -548,7 +516,7 @@ option does. Applied to the 'D..M' range, it results in: L--M ----------------------------------------------------------------------- -The '\--simplify-by-decoration' option allows you to view only the +The `--simplify-by-decoration` option allows you to view only the big picture of the topology of the history, by omitting commits that are not referenced by tags. Commits are marked as !TREESAME (in other words, kept after history simplification rules described @@ -561,50 +529,47 @@ Bisection Helpers ~~~~~~~~~~~~~~~~~ --bisect:: - -Limit output to the one commit object which is roughly halfway between -included and excluded commits. Note that the bad bisection ref -`refs/bisect/bad` is added to the included commits (if it -exists) and the good bisection refs `refs/bisect/good-*` are -added to the excluded commits (if they exist). Thus, supposing there -are no refs in `refs/bisect/`, if - + Limit output to the one commit object which is roughly halfway between + included and excluded commits. Note that the bad bisection ref + `refs/bisect/bad` is added to the included commits (if it + exists) and the good bisection refs `refs/bisect/good-*` are + added to the excluded commits (if they exist). Thus, supposing there + are no refs in `refs/bisect/`, if ++ ----------------------------------------------------------------------- $ git rev-list --bisect foo ^bar ^baz ----------------------------------------------------------------------- - ++ outputs 'midpoint', the output of the two commands - ++ ----------------------------------------------------------------------- $ git rev-list foo ^midpoint $ git rev-list midpoint ^bar ^baz ----------------------------------------------------------------------- - ++ would be of roughly the same length. Finding the change which introduces a regression is thus reduced to a binary search: repeatedly generate and test new 'midpoint's until the commit chain is of length one. --bisect-vars:: - -This calculates the same as `--bisect`, except that refs in -`refs/bisect/` are not used, and except that this outputs -text ready to be eval'ed by the shell. These lines will assign the -name of the midpoint revision to the variable `bisect_rev`, and the -expected number of commits to be tested after `bisect_rev` is tested -to `bisect_nr`, the expected number of commits to be tested if -`bisect_rev` turns out to be good to `bisect_good`, the expected -number of commits to be tested if `bisect_rev` turns out to be bad to -`bisect_bad`, and the number of commits we are bisecting right now to -`bisect_all`. + This calculates the same as `--bisect`, except that refs in + `refs/bisect/` are not used, and except that this outputs + text ready to be eval'ed by the shell. These lines will assign the + name of the midpoint revision to the variable `bisect_rev`, and the + expected number of commits to be tested after `bisect_rev` is tested + to `bisect_nr`, the expected number of commits to be tested if + `bisect_rev` turns out to be good to `bisect_good`, the expected + number of commits to be tested if `bisect_rev` turns out to be bad to + `bisect_bad`, and the number of commits we are bisecting right now to + `bisect_all`. --bisect-all:: - -This outputs all the commit objects between the included and excluded -commits, ordered by their distance to the included and excluded -commits. Refs in `refs/bisect/` are not used. The farthest -from them is displayed first. (This is the only one displayed by -`--bisect`.) + This outputs all the commit objects between the included and excluded + commits, ordered by their distance to the included and excluded + commits. Refs in `refs/bisect/` are not used. The farthest + from them is displayed first. (This is the only one displayed by + `--bisect`.) + This is useful because it makes it easy to choose a good commit to test when you want to avoid to test some of them for some reason (they @@ -654,9 +619,8 @@ avoid showing the commits from two parallel development track mixed together. --reverse:: - Output the commits in reverse order. - Cannot be combined with '\--walk-reflogs'. + Cannot be combined with `--walk-reflogs`. Object Traversal ~~~~~~~~~~~~~~~~ @@ -664,37 +628,32 @@ Object Traversal These options are mostly targeted for packing of Git repositories. --objects:: - Print the object IDs of any object referenced by the listed - commits. '--objects foo ^bar' thus means "send me + commits. `--objects foo ^bar` thus means ``send me all object IDs which I need to download if I have the commit - object 'bar', but not 'foo'". + object _bar_ but not _foo_''. --objects-edge:: - - Similar to '--objects', but also print the IDs of excluded - commits prefixed with a "-" character. This is used by - linkgit:git-pack-objects[1] to build "thin" pack, which records + Similar to `--objects`, but also print the IDs of excluded + commits prefixed with a ``-'' character. This is used by + linkgit:git-pack-objects[1] to build ``thin'' pack, which records objects in deltified form based on objects contained in these excluded commits to reduce network traffic. --unpacked:: - - Only useful with '--objects'; print the object IDs that are not + Only useful with `--objects`; print the object IDs that are not in packs. --no-walk[=(sorted|unsorted)]:: - Only show the given commits, but do not traverse their ancestors. This has no effect if a range is specified. If the argument - "unsorted" is given, the commits are show in the order they were - given on the command line. Otherwise (if "sorted" or no argument - was given), the commits are show in reverse chronological order + `unsorted` is given, the commits are shown in the order they were + given on the command line. Otherwise (if `sorted` or no argument + was given), the commits are shown in reverse chronological order by commit time. --do-walk:: - - Overrides a previous --no-walk. + Overrides a previous `--no-walk`. Commit Formatting ~~~~~~~~~~~~~~~~~ @@ -708,26 +667,24 @@ endif::git-rev-list[] include::pretty-options.txt[] --relative-date:: - Synonym for `--date=relative`. --date=(relative|local|default|iso|rfc|short|raw):: - Only takes effect for dates shown in human-readable format, such - as when using "--pretty". `log.date` config variable sets a default - value for log command's --date option. + as when using `--pretty`. `log.date` config variable sets a default + value for the log command's `--date` option. + `--date=relative` shows dates relative to the current time, -e.g. "2 hours ago". +e.g. ``2 hours ago''. + `--date=local` shows timestamps in user's local time zone. + `--date=iso` (or `--date=iso8601`) shows timestamps in ISO 8601 format. + `--date=rfc` (or `--date=rfc2822`) shows timestamps in RFC 2822 -format, often found in E-mail messages. +format, often found in email messages. + -`--date=short` shows only date but not time, in `YYYY-MM-DD` format. +`--date=short` shows only the date, but not the time, in `YYYY-MM-DD` format. + `--date=raw` shows the date in the internal raw Git format `%s %z` format. + @@ -736,18 +693,15 @@ format, often found in E-mail messages. ifdef::git-rev-list[] --header:: - Print the contents of the commit in raw-format; each record is separated with a NUL character. endif::git-rev-list[] --parents:: - Print also the parents of the commit (in the form "commit parent..."). Also enables parent rewriting, see 'History Simplification' below. --children:: - Print also the children of the commit (in the form "commit child..."). Also enables parent rewriting, see 'History Simplification' below. @@ -757,7 +711,6 @@ ifdef::git-rev-list[] endif::git-rev-list[] --left-right:: - Mark which side of a symmetric diff a commit is reachable from. Commits from the left side are prefixed with `<` and those from the right with `>`. If combined with `--boundary`, those @@ -787,7 +740,6 @@ you would get an output like this: ----------------------------------------------------------------------- --graph:: - Draw a text-based graphical representation of the commit history on the left hand side of the output. This may cause extra lines to be printed in between commits, in order for the graph history @@ -795,31 +747,29 @@ you would get an output like this: + This enables parent rewriting, see 'History Simplification' below. + -This implies the '--topo-order' option by default, but the -'--date-order' option may also be specified. +This implies the `--topo-order` option by default, but the +`--date-order` option may also be specified. ifdef::git-rev-list[] --count:: Print a number stating how many commits would have been listed, and suppress all other output. When used together - with '--left-right', instead print the counts for left and + with `--left-right`, instead print the counts for left and right commits, separated by a tab. When used together with - '--cherry-mark', omit patch equivalent commits from these + `--cherry-mark`, omit patch equivalent commits from these counts and print the count for equivalent commits separated by a tab. endif::git-rev-list[] - ifndef::git-rev-list[] Diff Formatting ~~~~~~~~~~~~~~~ -Below are listed options that control the formatting of diff output. +Listed below are options that control the formatting of diff output. Some of them are specific to linkgit:git-rev-list[1], however other diff options may be given. See linkgit:git-diff-files[1] for more options. -c:: - With this option, diff output for a merge commit shows the differences from each of the parents to the merge result simultaneously instead of showing pairwise diff between a parent @@ -827,26 +777,22 @@ options may be given. See linkgit:git-diff-files[1] for more options. which were modified from all parents. --cc:: - - This flag implies the '-c' option and further compresses the + This flag implies the `-c` option and further compresses the patch output by omitting uninteresting hunks whose contents in the parents have only two variants and the merge result picks one of them without modification. -m:: - This flag makes the merge commits show the full diff like regular commits; for each merge parent, a separate log entry and diff is generated. An exception is that only diff against - the first parent is shown when '--first-parent' option is given; + the first parent is shown when `--first-parent` option is given; in that case, the output represents the changes the merge brought _into_ the then-current branch. -r:: - Show recursive diffs. -t:: - - Show the tree objects in the diff output. This implies '-r'. + Show the tree objects in the diff output. This implies `-r`. endif::git-rev-list[] 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-ref-iteration.txt b/Documentation/technical/api-ref-iteration.txt index aa1c50f181..02adfd45d3 100644 --- a/Documentation/technical/api-ref-iteration.txt +++ b/Documentation/technical/api-ref-iteration.txt @@ -50,10 +50,10 @@ submodules object database. You can do this by a code-snippet like this: const char *path = "path/to/submodule" - if (!add_submodule_odb(path)) + if (add_submodule_odb(path)) die("Error submodule '%s' not populated.", path); -`add_submodule_odb()` will return an non-zero value on success. If you +`add_submodule_odb()` will return zero on success. If you do not do this you will get an error for each ref that it does not point to a valid object. 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/http-protocol.txt b/Documentation/technical/http-protocol.txt index caf941a1c5..d21d77d1de 100644 --- a/Documentation/technical/http-protocol.txt +++ b/Documentation/technical/http-protocol.txt @@ -307,7 +307,7 @@ Clients MUST first perform ref discovery with S: ....ACK %s, continue S: ....NAK -Clients MUST NOT reuse or revalidate a cached reponse. +Clients MUST NOT reuse or revalidate a cached response. Servers MUST include sufficient Cache-Control headers to prevent caching of the response. @@ -468,7 +468,7 @@ Clients MUST first perform ref discovery with S: S: .... -Clients MUST NOT reuse or revalidate a cached reponse. +Clients MUST NOT reuse or revalidate a cached response. Servers MUST include sufficient Cache-Control headers to prevent caching of the response. 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 99a62f3baf..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 +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.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..4916eb2bd2 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))) 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 adb93a9efa..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; @@ -64,15 +67,18 @@ static void stuff_change(struct diff_options *opt, static int builtin_diff_b_f(struct rev_info *revs, int argc, const char **argv, - struct blobinfo *blob, - const char *path) + struct blobinfo *blob) { /* Blob vs file in the working tree*/ struct stat st; + const char *path; if (argc > 1) usage(builtin_diff_usage); + GUARD_PATHSPEC(&revs->prune_data, PATHSPEC_FROMTOP | PATHSPEC_LITERAL); + path = revs->prune_data.items[0].match; + if (lstat(path, &st)) die_errno(_("failed to stat '%s'"), path); if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) @@ -255,9 +261,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix) struct rev_info rev; struct object_array ent = OBJECT_ARRAY_INIT; int blobs = 0, paths = 0; - const char *path = NULL; struct blobinfo blob[2]; - int nongit; + int nongit = 0, no_index = 0; int result = 0; /* @@ -283,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; @@ -366,13 +416,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix) die(_("unhandled object '%s' given."), name); } } - if (rev.prune_data.nr) { - /* builtin_diff_b_f() */ - GUARD_PATHSPEC(&rev.prune_data, PATHSPEC_FROMTOP | PATHSPEC_LITERAL); - if (!path) - path = rev.prune_data.items[0].match; + if (rev.prune_data.nr) paths += rev.prune_data.nr; - } /* * Now, do the arguments look reasonable? @@ -385,7 +430,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) case 1: if (paths != 1) usage(builtin_diff_usage); - result = builtin_diff_b_f(&rev, argc, argv, blob, path); + result = builtin_diff_b_f(&rev, argc, argv, blob); break; case 2: if (paths) 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 d096051b15..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" }, }; /* @@ -89,7 +92,8 @@ static struct { */ static const char **used_atom; static cmp_type *used_atom_type; -static int used_atom_cnt, sort_atom_limit, need_tagged, need_symref; +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'); } @@ -1037,7 +1105,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) if (!sort) sort = default_sort(); - sort_atom_limit = used_atom_cnt; /* for warn_ambiguous_refs */ git_config(git_default_config, NULL); 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/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..a2923235e1 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 }; @@ -85,37 +89,148 @@ 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 (cmdmode == 'r' && show_all) + die("--independent cannot be used with --all"); - if (octopus || reduce) - return handle_octopus(argc, argv, reduce, show_all); + if (cmdmode == 'r' || cmdmode == 'o') + return handle_octopus(argc, argv, cmdmode == 'r', show_all); + + 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 a0ff5c704f..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"); @@ -123,7 +123,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) struct string_list rollback = STRING_LIST_INIT_NODUP; struct string_list existing_packs = STRING_LIST_INIT_DUP; struct strbuf line = STRBUF_INIT; - int nr_packs, ext, ret, failed; + int ext, ret, failed; FILE *out; /* variables to be filled by option parsing */ @@ -233,13 +233,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (ret) return ret; - nr_packs = 0; out = xfdopen(cmd.out, "r"); while (strbuf_getline(&line, out, '\n') != EOF) { if (line.len != 40) die("repack: Expecting 40 character sha1 lines only from pack-objects."); string_list_append(&names, line.buf); - nr_packs++; } fclose(out); ret = finish_command(&cmd); @@ -247,7 +245,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) return ret; argv_array_clear(&cmd_args); - if (!nr_packs && !quiet) + if (!names.nr && !quiet) printf("Nothing new to pack.\n"); /* 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; } @@ -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..51c2dd4dec 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> @@ -673,7 +671,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 +684,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 +702,6 @@ __git_list_porcelain_commands () ssh-*) : transport;; stripspace) : plumbing;; symbolic-ref) : plumbing;; - tar-tree) : deprecated;; unpack-file) : plumbing;; unpack-objects) : plumbing;; update-index) : plumbing;; 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/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/gitview/gitview b/contrib/gitview/gitview index 4c99dfb903..4e23c650fe 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -1205,7 +1205,7 @@ class GitView(object): #The first parent always continue on the same line try: - # check we alreay have the value + # check we already have the value tmp_node_pos = self.nodepos[commit.parent_sha1[0]] except KeyError: self.colours[commit.parent_sha1[0]] = colour 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/mw-to-git/git-remote-mediawiki.perl b/contrib/mw-to-git/git-remote-mediawiki.perl index 476e0a2bc0..3f8d993afa 100755 --- a/contrib/mw-to-git/git-remote-mediawiki.perl +++ b/contrib/mw-to-git/git-remote-mediawiki.perl @@ -1315,7 +1315,7 @@ sub get_mw_namespace_id { # Store "notANameSpace" as special value for inexisting namespaces my $store_id = ($id || 'notANameSpace'); - # Store explicitely requested namespaces on disk + # Store explicitly requested namespaces on disk if (!exists $cached_mw_namespace_id{$name}) { run_git(qq(config --add remote.${remotename}.namespaceCache "${name}:${store_id}")); $cached_mw_namespace_id{$name} = 1; diff --git a/contrib/mw-to-git/t/test-gitmw-lib.sh b/contrib/mw-to-git/t/test-gitmw-lib.sh index ca6860ff30..3372b2af34 100755 --- a/contrib/mw-to-git/t/test-gitmw-lib.sh +++ b/contrib/mw-to-git/t/test-gitmw-lib.sh @@ -91,7 +91,7 @@ test_diff_directories () { # Check that <dir> contains exactly <N> files test_contains_N_files () { if test `ls -- "$1" | wc -l` -ne "$2"; then - echo "directory $1 sould contain $2 files" + echo "directory $1 should contain $2 files" echo "it contains these files:" ls "$1" false diff --git a/contrib/mw-to-git/t/test.config b/contrib/mw-to-git/t/test.config index 4cfebe9c69..5ba0684162 100644 --- a/contrib/mw-to-git/t/test.config +++ b/contrib/mw-to-git/t/test.config @@ -12,7 +12,7 @@ SERVER_ADDR=localhost TMP=/tmp DB_FILE=wikidb.sqlite -# If LIGHTTPD is not set to true, the script will use the defaut +# If LIGHTTPD is not set to true, the script will use the default # web server running in WIKI_DIR_INST. WIKI_DIR_INST=/var/www 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-bisect.sh b/git-bisect.sh index 9f064b6f4f..73b4c14d4f 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -137,7 +137,7 @@ bisect_start() { # cogito usage, and cogito users should understand # it relates to cg-seek. [ -s "$GIT_DIR/head-name" ] && - die "$(gettext "won't bisect on seeked tree")" + die "$(gettext "won't bisect on cg-seek'ed tree")" start_head="${head#refs/heads/}" ;; *) 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 896f1c9b82..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" "$@" @@ -612,11 +617,21 @@ cmd_init() fi # Copy "update" setting when it is not set yet - upd="$(git config -f .gitmodules submodule."$name".update)" - test -z "$upd" || - test -n "$(git config submodule."$name".update)" || - git config submodule."$name".update "$upd" || - die "$(eval_gettext "Failed to register update mode for submodule path '\$displaypath'")" + if upd="$(git config -f .gitmodules submodule."$name".update)" && + test -n "$upd" && + test -z "$(git config submodule."$name".update)" + then + case "$upd" in + rebase | merge | none) + ;; # known modes of updating + *) + echo >&2 "warning: unknown update mode '$upd' suggested for submodule '$name'" + upd=none + ;; + esac + git config submodule."$name".update "$upd" || + die "$(eval_gettext "Failed to register update mode for submodule path '\$displaypath'")" + fi done } @@ -706,7 +721,6 @@ cmd_deinit() cmd_update() { # parse $args after "submodule ... update". - orig_flags= while test $# -ne 0 do case "$1" in @@ -731,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=*) @@ -765,7 +778,6 @@ cmd_update() break ;; esac - orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")" shift done @@ -909,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 4cf2bd365a..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); @@ -197,11 +212,11 @@ static unsigned prefix_pathspec(struct pathspec_item *item, magic |= short_magic; *p_short_magic = short_magic; - /* --noglob-pathspec adds :(literal) _unless_ :(glob) is specifed */ + /* --noglob-pathspec adds :(literal) _unless_ :(glob) is specified */ if (noglob_global && !(magic & PATHSPEC_GLOB)) global_magic |= PATHSPEC_LITERAL; - /* --glob-pathspec is overriden by :(literal) */ + /* --glob-pathspec is overridden by :(literal) */ if ((global_magic & PATHSPEC_GLOB) && (magic & PATHSPEC_LITERAL)) global_magic &= ~PATHSPEC_GLOB; @@ -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); @@ -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 7dadd04cb7..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,17 +2478,23 @@ 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. + * need to look inside the object at all. Note that we + * do not optimize out the stat call, even if the + * caller doesn't care about the disk-size, since our + * return value implicitly indicates whether the + * object even exists. */ if (!oi->typep && !oi->sizep) { - if (oi->disk_sizep) { - struct stat st; - if (stat_sha1_file(sha1, &st) < 0) - return -1; + struct stat st; + if (stat_sha1_file(sha1, &st) < 0) + return -1; + if (oi->disk_sizep) *oi->disk_sizep = st.st_size; - } return 0; } @@ -2514,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; @@ -2528,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 { @@ -2570,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; } @@ -2662,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); @@ -2857,7 +2862,9 @@ static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename) /* Make sure the directory exists */ memcpy(buffer, filename, dirlen); buffer[dirlen-1] = 0; - if (mkdir(buffer, 0777) || adjust_shared_perm(buffer)) + if (mkdir(buffer, 0777) && errno != EEXIST) + return -1; + if (adjust_shared_perm(buffer)) return -1; /* Try again */ 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/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..c4707843c4 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> # 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 abc2c6f57f..b95f485606 100644 --- a/t/lib-read-tree.sh +++ b/t/lib-read-tree.sh @@ -1,43 +1,41 @@ -#!/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. # read_tree_must_succeed () { - git ls-files -s >pre-dry-run && - git read-tree -n "$@" && - git ls-files -s >post-dry-run && - test_cmp pre-dry-run post-dry-run && - git read-tree "$@" + git ls-files -s >pre-dry-run && + git read-tree -n "$@" && + git ls-files -s >post-dry-run && + test_cmp pre-dry-run post-dry-run && + git read-tree "$@" } read_tree_must_fail () { - git ls-files -s >pre-dry-run && - test_must_fail git read-tree -n "$@" && - git ls-files -s >post-dry-run && - test_cmp pre-dry-run post-dry-run && - test_must_fail git read-tree "$@" + git ls-files -s >pre-dry-run && + test_must_fail git read-tree -n "$@" && + git ls-files -s >post-dry-run && + test_cmp pre-dry-run post-dry-run && + test_must_fail git read-tree "$@" } read_tree_u_must_succeed () { - git ls-files -s >pre-dry-run && - git diff-files -p >pre-dry-run-wt && - git read-tree -n "$@" && - git ls-files -s >post-dry-run && - git diff-files -p >post-dry-run-wt && - test_cmp pre-dry-run post-dry-run && - test_cmp pre-dry-run-wt post-dry-run-wt && - git read-tree "$@" + git ls-files -s >pre-dry-run && + git diff-files -p >pre-dry-run-wt && + git read-tree -n "$@" && + git ls-files -s >post-dry-run && + git diff-files -p >post-dry-run-wt && + test_cmp pre-dry-run post-dry-run && + test_cmp pre-dry-run-wt post-dry-run-wt && + git read-tree "$@" } read_tree_u_must_fail () { - git ls-files -s >pre-dry-run && - git diff-files -p >pre-dry-run-wt && - test_must_fail git read-tree -n "$@" && - git ls-files -s >post-dry-run && - git diff-files -p >post-dry-run-wt && - test_cmp pre-dry-run post-dry-run && - test_cmp pre-dry-run-wt post-dry-run-wt && - test_must_fail git read-tree "$@" + git ls-files -s >pre-dry-run && + git diff-files -p >pre-dry-run-wt && + test_must_fail git read-tree -n "$@" && + git ls-files -s >post-dry-run && + git diff-files -p >post-dry-run-wt && + test_cmp pre-dry-run post-dry-run && + test_cmp pre-dry-run-wt post-dry-run-wt && + test_must_fail git read-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/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/t1005-read-tree-reset.sh b/t/t1005-read-tree-reset.sh index f53de79e56..074568500a 100755 --- a/t/t1005-read-tree-reset.sh +++ b/t/t1005-read-tree-reset.sh @@ -8,84 +8,99 @@ test_description='read-tree -u --reset' # two-tree test test_expect_success 'setup' ' - git init && - mkdir df && - echo content >df/file && - git add df/file && - git commit -m one && - git ls-files >expect && - rm -rf df && - echo content >df && - git add df && - echo content >new && - git add new && - git commit -m two + git init && + mkdir df && + echo content >df/file && + git add df/file && + git commit -m one && + git ls-files >expect && + rm -rf df && + echo content >df && + git add df && + echo content >new && + git add new && + git commit -m two ' test_expect_success 'reset should work' ' - read_tree_u_must_succeed -u --reset HEAD^ && - git ls-files >actual && - test_cmp expect actual + read_tree_u_must_succeed -u --reset HEAD^ && + git ls-files >actual && + test_cmp expect actual ' test_expect_success 'reset should remove remnants from a failed merge' ' - read_tree_u_must_succeed --reset -u HEAD && - git ls-files -s >expect && - sha1=$(git rev-parse :new) && - ( - echo "100644 $sha1 1 old" - echo "100644 $sha1 3 old" - ) | git update-index --index-info && - >old && - git ls-files -s && - read_tree_u_must_succeed --reset -u HEAD && - git ls-files -s >actual && - ! test -f old + read_tree_u_must_succeed --reset -u HEAD && + git ls-files -s >expect && + sha1=$(git rev-parse :new) && + ( + echo "100644 $sha1 1 old" + echo "100644 $sha1 3 old" + ) | git update-index --index-info && + >old && + git ls-files -s && + read_tree_u_must_succeed --reset -u HEAD && + git ls-files -s >actual && + ! test -f old +' + +test_expect_success 'two-way reset should remove remnants too' ' + read_tree_u_must_succeed --reset -u HEAD && + git ls-files -s >expect && + sha1=$(git rev-parse :new) && + ( + echo "100644 $sha1 1 old" + echo "100644 $sha1 3 old" + ) | git update-index --index-info && + >old && + git ls-files -s && + read_tree_u_must_succeed --reset -u HEAD HEAD && + git ls-files -s >actual && + ! test -f old ' test_expect_success 'Porcelain reset should remove remnants too' ' - read_tree_u_must_succeed --reset -u HEAD && - git ls-files -s >expect && - sha1=$(git rev-parse :new) && - ( - echo "100644 $sha1 1 old" - echo "100644 $sha1 3 old" - ) | git update-index --index-info && - >old && - git ls-files -s && - git reset --hard && - git ls-files -s >actual && - ! test -f old + read_tree_u_must_succeed --reset -u HEAD && + git ls-files -s >expect && + sha1=$(git rev-parse :new) && + ( + echo "100644 $sha1 1 old" + echo "100644 $sha1 3 old" + ) | git update-index --index-info && + >old && + git ls-files -s && + git reset --hard && + git ls-files -s >actual && + ! test -f old ' test_expect_success 'Porcelain checkout -f should remove remnants too' ' - read_tree_u_must_succeed --reset -u HEAD && - git ls-files -s >expect && - sha1=$(git rev-parse :new) && - ( - echo "100644 $sha1 1 old" - echo "100644 $sha1 3 old" - ) | git update-index --index-info && - >old && - git ls-files -s && - git checkout -f && - git ls-files -s >actual && - ! test -f old + read_tree_u_must_succeed --reset -u HEAD && + git ls-files -s >expect && + sha1=$(git rev-parse :new) && + ( + echo "100644 $sha1 1 old" + echo "100644 $sha1 3 old" + ) | git update-index --index-info && + >old && + git ls-files -s && + git checkout -f && + git ls-files -s >actual && + ! test -f old ' test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' ' - read_tree_u_must_succeed --reset -u HEAD && - git ls-files -s >expect && - sha1=$(git rev-parse :new) && - ( - echo "100644 $sha1 1 old" - echo "100644 $sha1 3 old" - ) | git update-index --index-info && - >old && - git ls-files -s && - git checkout -f HEAD && - git ls-files -s >actual && - ! test -f old + read_tree_u_must_succeed --reset -u HEAD && + git ls-files -s >expect && + sha1=$(git rev-parse :new) && + ( + echo "100644 $sha1 1 old" + echo "100644 $sha1 3 old" + ) | git update-index --index-info && + >old && + git ls-files -s && + git checkout -f HEAD && + git ls-files -s >actual && + ! test -f old ' test_done diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh index a420742494..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" @@ -194,6 +216,12 @@ test_expect_success "--batch-check for an emtpy line" ' test " missing" = "$(echo | git cat-file --batch-check)" ' +test_expect_success 'empty --batch-check notices missing object' ' + echo "$_z40 missing" >expect && + echo "$_z40" | git cat-file --batch-check="" >actual && + test_cmp expect actual +' + batch_input="$hello_sha1 $commit_sha1 $tag_sha1 @@ -234,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/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..30a68335b3 100755 --- a/t/t6010-merge-base.sh +++ b/t/t6010-merge-base.sh @@ -230,4 +230,32 @@ 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_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/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh index 8d4a7fcb91..a7c7ff5f49 100755 --- a/t/t6131-pathspec-icase.sh +++ b/t/t6131-pathspec-icase.sh @@ -100,4 +100,10 @@ test_expect_success 'match_pathspec_depth matches :(icase)bar with empty prefix' test_cmp expect actual ' +test_expect_success '"git diff" can take magic :(icase) pathspec' ' + echo FOO/BAR >expect && + git diff --name-only HEAD^ HEAD -- ":(icase)foo/bar" >actual && + test_cmp expect actual +' + test_done 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 f0b33053ab..0246e80b1a 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -323,6 +323,21 @@ test_expect_success 'submodule update - command in .git/config catches failure' ) ' +test_expect_success 'submodule init does not copy command into .git/config' ' + (cd super && + H=$(git ls-files -s submodule | cut -d" " -f2) && + mkdir submodule1 && + git update-index --add --cacheinfo 160000 $H submodule1 && + git config -f .gitmodules submodule.submodule1.path submodule1 && + git config -f .gitmodules submodule.submodule1.url ../submodule && + git config -f .gitmodules submodule.submodule1.update !false && + git submodule init submodule1 && + echo "none" >expect && + git config submodule.submodule1.update >actual && + test_cmp expect actual + ) +' + test_expect_success 'submodule init picks up rebase' ' (cd super && git config -f .gitmodules submodule.rebasing.update rebase && @@ -747,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..1cf78d5b11 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 @@ -573,11 +577,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 +832,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 35cb05e92b..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); } @@ -1763,14 +1766,23 @@ int twoway_merge(const struct cache_entry * const *src, newtree = NULL; if (current) { - if ((!oldtree && !newtree) || /* 4 and 5 */ - (!oldtree && newtree && - same(current, newtree)) || /* 6 and 7 */ - (oldtree && newtree && - same(oldtree, newtree)) || /* 14 and 15 */ - (oldtree && newtree && - !same(oldtree, newtree) && /* 18 and 19 */ - same(current, newtree))) { + if (current->ce_flags & CE_CONFLICTED) { + if (same(oldtree, newtree) || o->reset) { + if (!newtree) + return deleted_entry(current, current, o); + else + return merged_entry(newtree, current, o); + } + return o->gently ? -1 : reject_merge(current, o); + } + else if ((!oldtree && !newtree) || /* 4 and 5 */ + (!oldtree && newtree && + same(current, newtree)) || /* 6 and 7 */ + (oldtree && newtree && + same(oldtree, newtree)) || /* 14 and 15 */ + (oldtree && newtree && + !same(oldtree, newtree) && /* 18 and 19 */ + same(current, newtree))) { return keep_entry(current, o); } else if (oldtree && !newtree && same(current, oldtree)) { 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) @@ -133,7 +133,7 @@ void *xcalloc(size_t nmemb, size_t size) /* * Limit size of IO chunks, because huge chunks only cause pain. OS X * 64-bit is buggy, returning EINVAL if len >= INT_MAX; and even in - * the absense of bugs, large chunks can result in bad latencies when + * the absence of bugs, large chunks can result in bad latencies when * you decide to kill the process. */ #define MAX_IO_SIZE (8*1024*1024) 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); |