diff options
171 files changed, 19503 insertions, 12949 deletions
@@ -171,6 +171,7 @@ /git-verify-tag /git-web--browse /git-whatchanged +/git-worktree /git-write-tree /git-core-*/?* /gitweb/GITWEB-BUILD-OPTIONS diff --git a/Documentation/RelNotes/2.4.4.txt b/Documentation/RelNotes/2.4.4.txt new file mode 100644 index 0000000..f1ccd00 --- /dev/null +++ b/Documentation/RelNotes/2.4.4.txt @@ -0,0 +1,35 @@ +Git v2.4.4 Release Notes +======================== + +Fixes since v2.4.3 +------------------ + + * l10n updates for German. + + * An earlier leakfix to bitmap testing code was incomplete. + + * "git clean pathspec..." tried to lstat(2) and complain even for + paths outside the given pathspec. + + * Communication between the HTTP server and http_backend process can + lead to a dead-lock when relaying a large ref negotiation request. + Diagnose the situation better, and mitigate it by reading such a + request first into core (to a reasonable limit). + + * The clean/smudge interface did not work well when filtering an + empty contents (failed and then passed the empty input through). + It can be argued that a filter that produces anything but empty for + an empty input is nonsense, but if the user wants to do strange + things, then why not? + + * Make "git stash something --help" error out, so that users can + safely say "git stash drop --help". + + * Clarify that "log --raw" and "log --format=raw" are unrelated + concepts. + + * Catch a programmer mistake to feed a pointer not an array to + ARRAY_SIZE() macro, by using a couple of GCC extensions. + +Also contains typofixes, documentation updates and trivial code +clean-ups. diff --git a/Documentation/RelNotes/2.4.5.txt b/Documentation/RelNotes/2.4.5.txt new file mode 100644 index 0000000..568297c --- /dev/null +++ b/Documentation/RelNotes/2.4.5.txt @@ -0,0 +1,28 @@ +Git v2.4.5 Release Notes +======================== + +Fixes since v2.4.4 +------------------ + + * The setup code used to die when core.bare and core.worktree are set + inconsistently, even for commands that do not need working tree. + + * There was a dead code that used to handle "git pull --tags" and + show special-cased error message, which was made irrelevant when + the semantics of the option changed back in Git 1.9 days. + + * "color.diff.plain" was a misnomer; give it 'color.diff.context' as + a more logical synonym. + + * The configuration reader/writer uses mmap(2) interface to access + the files; when we find a directory, it barfed with "Out of memory?". + + * Recent "git prune" traverses young unreachable objects to safekeep + old objects in the reachability chain from them, which sometimes + showed unnecessary error messages that are alarming. + + * "git rebase -i" fired post-rewrite hook when it shouldn't (namely, + when it was told to stop sequencing with 'exec' insn). + +Also contains typofixes, documentation updates and trivial code +clean-ups. diff --git a/Documentation/RelNotes/2.4.6.txt b/Documentation/RelNotes/2.4.6.txt new file mode 100644 index 0000000..b53f353 --- /dev/null +++ b/Documentation/RelNotes/2.4.6.txt @@ -0,0 +1,23 @@ +Git v2.4.6 Release Notes +======================== + +Fixes since v2.4.5 +------------------ + + * "git fetch --depth=<depth>" and "git clone --depth=<depth>" issued + a shallow transfer request even to an upload-pack that does not + support the capability. + + * "git fsck" used to ignore missing or invalid objects recorded in reflog. + + * The tcsh completion writes a bash scriptlet but that would have + failed for users with noclobber set. + + * Recent Mac OS X updates breaks the logic to detect that the machine + is on the AC power in the sample pre-auto-gc script. + + * "git format-patch --ignore-if-upstream A..B" did not like to be fed + tags as boundary commits. + +Also contains typofixes, documentation updates and trivial code +clean-ups. diff --git a/Documentation/RelNotes/2.4.7.txt b/Documentation/RelNotes/2.4.7.txt new file mode 100644 index 0000000..b3ac412 --- /dev/null +++ b/Documentation/RelNotes/2.4.7.txt @@ -0,0 +1,53 @@ +Git v2.4.7 Release Notes +======================== + +Fixes since v2.4.6 +------------------ + + * A minor regression to "git fsck" in v2.2 era was fixed; it + complained about a body-less tag object when it lacked a + separator empty line after its header to separate it with a + non-existent body. + + * We used to ask libCURL to use the most secure authentication method + available when talking to an HTTP proxy only when we were told to + talk to one via configuration variables. We now ask libCURL to + always use the most secure authentication method, because the user + can tell libCURL to use an HTTP proxy via an environment variable + without using configuration variables. + + * When you say "!<ENTER>" while running say "git log", you'd confuse + yourself in the resulting shell, that may look as if you took + control back to the original shell you spawned "git log" from but + that isn't what is happening. To that new shell, we leaked + GIT_PAGER_IN_USE environment variable that was meant as a local + communication between the original "Git" and subprocesses that was + spawned by it after we launched the pager, which caused many + "interesting" things to happen, e.g. "git diff | cat" still paints + its output in color by default. + + Stop leaking that environment variable to the pager's half of the + fork; we only need it on "Git" side when we spawn the pager. + + * Avoid possible ssize_t to int truncation. + + * "git config" failed to update the configuration file when the + underlying filesystem is incapable of renaming a file that is still + open. + + * A minor bugfix when pack bitmap is used with "rev-list --count". + + * An ancient test framework enhancement to allow color was not + entirely correct; this makes it work even when tput needs to read + from the ~/.terminfo under the user's real HOME directory. + + * Fix a small bug in our use of umask() return value. + + * "git rebase" did not exit with failure when format-patch it invoked + failed for whatever reason. + + * Disable "have we lost a race with competing repack?" check while + receiving a huge object transfer that runs index-pack. + +Also contains typofixes, documentation updates and trivial code +clean-ups. diff --git a/Documentation/RelNotes/2.5.0.txt b/Documentation/RelNotes/2.5.0.txt index e33b0ac..8704450 100644 --- a/Documentation/RelNotes/2.5.0.txt +++ b/Documentation/RelNotes/2.5.0.txt @@ -4,9 +4,6 @@ Git 2.5 Release Notes Updates since v2.4 ------------------ -Ports - - UI, Workflows & Features * The bash completion script (in contrib/) learned a few options that @@ -28,6 +25,8 @@ UI, Workflows & Features chunks from Perforce, instead of making one call to "p4 changes" that may trigger "too many rows scanned" error from Perforce. + * More workaround for Perforce's row number limit in "git p4". + * Unlike "$EDITOR" and "$GIT_EDITOR" that can hold the path to the command and initial options (e.g. "/path/to/emacs -nw"), 'git p4' did not let the shell interpolate the contents of the environment @@ -47,11 +46,14 @@ UI, Workflows & Features rely on symbolic links and make sharing of objects and refs safer by making the borrowee and borrowers aware of each other. + Consider this as still an experimental feature; its UI is still + likely to change. + * Tweak the sample "store" backend of the credential helper to honor XDG configuration file locations when specified. - * A heuristic to help the "git <cmd> <revs> <pathspec>" command line - convention to catch mistyped paths is to make sure all the non-rev + * A heuristic we use to catch mistyped paths on the command line + "git <cmd> <revs> <pathspec>" is to make sure that all the non-rev parameters in the later part of the command line are names of the files in the working tree, but that means "git grep $str -- \*.c" must always be disambiguated with "--", because nobody sane will @@ -64,25 +66,18 @@ UI, Workflows & Features that are not marked as "not-for-merge"; this allows us to lose an old style invocation "git merge <msg> HEAD $commits..." in the implementation of "git pull" script; the old style syntax can now - be deprecated. - - * Help us to find broken test script that splits the body part of the - test by mistaken use of wrong kind of quotes. - (merge d93d5d5 jc/test-prereq-validate later to maint). - - * Developer support to automatically detect broken &&-chain in the - test scripts is now turned on by default. - (merge 92b269f jk/test-chain-lint later to maint). + be deprecated (but not removed yet). * Filter scripts were run with SIGPIPE disabled on the Git side, expecting that they may not read what Git feeds them to filter. We however treated a filter that does not read its input fully - before exiting as an error. + before exiting as an error. We no longer do and ignore EPIPE + when writing to feed the filter scripts. This changes semantics, but arguably in a good way. If a filter - can produce its output without consuming its input using whatever - magic, we now let it do so, instead of diagnosing it as a - programming error. + can produce its output without fully consuming its input using + whatever magic, we now let it do so, instead of diagnosing it + as a programming error. * Instead of dying immediately upon failing to obtain a lock, the locking (of refs etc) retries after a short while with backoff. @@ -98,8 +93,8 @@ UI, Workflows & Features when pushing, but the documentation and help text pretended as if it did. - * The Git subcommand completion (in contrib/) listed credential - helpers among candidates, which is not something the end user would + * The Git subcommand completion (in contrib/) no longer lists credential + helpers among candidates; they are not something the end user would invoke interactively. * The index file can be taught with "update-index --untracked-cache" @@ -120,6 +115,40 @@ UI, Workflows & Features behaves as if HEAD:Documentation/RelNotes/2.5.0.txt was given as input instead. + Consider this as still an experimental and incomplete feature: + + - We may want to do the same for in-index objects, e.g. + asking for :RelNotes with this option should give + :Documentation/RelNotes/2.5.0.txt, too + + - "git cat-file --follow-symlinks blob HEAD:RelNotes" + may also be something we want to allow in the future. + + * "git send-email" learned the alias file format used by the sendmail + program (in a simplified form; we obviously do not feed pipes). + + * Traditionally, external low-level 3-way merge drivers are expected + to produce their results based solely on the contents of the three + variants given in temporary files named by %O, %A and %B on their + command line. Additionally allow them to look at the final path + (given by %P). + + * "git blame" learned blame.showEmail configuration variable. + + * "git apply" cannot diagnose a patch corruption when the breakage is + to mark the length of the hunk shorter than it really is on the + hunk header line "@@ -l,k +m,n @@"; one special case it could is + when the hunk becomes no-op (e.g. k == n == 2 for two-line context + patch output), and it learned to do so in this special case. + + * Add the "--allow-unknown-type" option to "cat-file" to allow + inspecting loose objects of an experimental or a broken type. + + * Many long-running operations show progress eye-candy, even when + they are later backgrounded. Hide the eye-candy when the process + is sent to the background instead. + (merge a4fb76c lm/squelch-bg-progress later to maint). + Performance, Internal Implementation, Development Support etc. @@ -129,9 +158,11 @@ Performance, Internal Implementation, Development Support etc. but hopefully will give us one extra level of abstraction in the end, when completed. + * for_each_ref() callback functions were taught to name the objects + not with "unsigned char sha1[20]" but with "struct object_id". + * Catch a programmer mistake to feed a pointer not an array to ARRAY_SIZE() macro, by using a couple of GCC extensions. - (merge 89c855e ep/do-not-feed-a-pointer-to-array-size later to maint). * Some error messages in "git config" were emitted without calling the usual error() facility. @@ -149,33 +180,36 @@ Performance, Internal Implementation, Development Support etc. * An earlier rewrite to use strbuf_getwholeline() instead of fgets(3) to read packed-refs file revealed that the former is unacceptably - inefficient. + inefficient. It has been optimized by using getdelim(3) when + available. * The refs API uses ref_lock struct which had its own "int fd", even though the same file descriptor was in the lock struct it contains. Clean-up the code to lose this redundant field. - * Add the "--allow-unknown-type" option to "cat-file" to allow - inspecting loose objects of an experimental or a broken type. - - * Many long-running operations show progress eye-candy, even when - they are later backgrounded. Hide the eye-candy when the process - is sent to the background instead. - (merge 9a9a41d lm/squelch-bg-progress later to maint). - * There was a dead code that used to handle "git pull --tags" and show special-cased error message, which was made irrelevant when the semantics of the option changed back in Git 1.9 days. (merge 19d122b pt/pull-tags-error-diag later to maint). - * for_each_ref() callback functions were taught to name the objects - not with "unsigned char sha1[20]" but with "struct object_id". + * Help us to find broken test script that splits the body part of the + test by mistaken use of wrong kind of quotes. + (merge d93d5d5 jc/test-prereq-validate later to maint). + + * Developer support to automatically detect broken &&-chain in the + test scripts is now turned on by default. + (merge 92b269f jk/test-chain-lint later to maint). * Error reporting mechanism used in "refs" API has been made more consistent. * "git pull" has more test coverage now. + * "git pull" has become more aware of the options meant for + underlying "git fetch" and then learned to use parse-options + parser. + + * Clarify in the Makefile a guideline to decide use of USE_NSEC. Also contains various documentation updates and code clean-ups. @@ -197,7 +231,7 @@ notes for details). * Memory usage of "git index-pack" has been trimmed by tens of per-cent. - (merge c6458e6 nd/slim-index-pack-memory-usage later to maint). + (merge f0e7f11 nd/slim-index-pack-memory-usage later to maint). * "git rev-list --objects $old --not --all" to see if everything that is reachable from $old is already connected to the existing refs @@ -286,12 +320,6 @@ notes for details). anywhere in the path (e.g. "/home/me/bin/uplink/ssh"). (merge baaf233 bc/connect-plink later to maint). - * "git stash pop/apply" forgot to make sure that not just the working - tree is clean but also the index is clean. The latter is important - as a stash application can conflict and the index will be used for - conflict resolution. - (merge ed178ef jk/stash-require-clean-index later to maint). - * We have prepended $GIT_EXEC_PATH and the path "git" is installed in (typically "/usr/bin") to $PATH when invoking subprograms and hooks for almost eternity, but the original use case the latter tried to @@ -311,19 +339,6 @@ notes for details). the order was swapped from the beginning. This belatedly fixes it. (merge 099d2d8 jc/gitignore-precedence later to maint). - * After "git add -N", the path appeared in output of "git diff HEAD" - and "git diff --cached HEAD", leading "git status" to classify it - as "Changes to be committed". Such a path, however, is not yet to - be scheduled to be committed. "git diff" showed the change to the - path as modification, not as a "new file", in the header of its - output. - - Treat such paths as "yet to be added to the index but Git already - know about them"; "git diff HEAD" and "git diff --cached HEAD" - should not talk about them, and "git diff" should show them as new - files yet to be added to the index. - (merge d95d728 nd/diff-i-t-a later to maint). - * There was a commented-out (instead of being marked to expect failure) test that documented a breakage that was fixed since the test was written; turn it into a proper test. @@ -415,6 +430,102 @@ notes for details). a more logical synonym. (merge 8dbf3eb jk/color-diff-plain-is-context later to maint). + * The setup code used to die when core.bare and core.worktree are set + inconsistently, even for commands that do not need working tree. + (merge fada767 jk/die-on-bogus-worktree-late later to maint). + + * Recent Mac OS X updates breaks the logic to detect that the machine + is on the AC power in the sample pre-auto-gc script. + (merge c54c7b3 pa/auto-gc-mac-osx later to maint). + + * "git commit --cleanup=scissors" was not careful enough to protect + against getting fooled by a line that looked like scissors. + (merge fbfa097 sg/commit-cleanup-scissors later to maint). + + * "Have we lost a race with competing repack?" check was too + expensive, especially while receiving a huge object transfer + that runs index-pack (e.g. "clone" or "fetch"). + (merge 0eeb077 jk/index-pack-reduce-recheck later to maint). + + * The tcsh completion writes a bash scriptlet but that would have + failed for users with noclobber set. + (merge 0b1f688 af/tcsh-completion-noclobber later to maint). + + * "git for-each-ref" reported "missing object" for 0{40} when it + encounters a broken ref. The lack of object whose name is 0{40} is + not the problem; the ref being broken is. + (merge 501cf47 mh/reporting-broken-refs-from-for-each-ref later to maint). + + * Various fixes around "git am" that applies a patch to a history + that is not there yet. + (merge 6ea3b67 pt/am-abort-fix later to maint). + + * "git fsck" used to ignore missing or invalid objects recorded in reflog. + (merge 19bf6c9 mh/fsck-reflog-entries later to maint). + + * "git format-patch --ignore-if-upstream A..B" did not like to be fed + tags as boundary commits. + (merge 9b7a61d jc/do-not-feed-tags-to-clear-commit-marks later to maint). + + * "git fetch --depth=<depth>" and "git clone --depth=<depth>" issued + a shallow transfer request even to an upload-pack that does not + support the capability. + (merge eb86a50 me/fetch-into-shallow-safety later to maint). + + * "git rebase" did not exit with failure when format-patch it invoked + failed for whatever reason. + (merge 60d708b cb/rebase-am-exit-code later to maint). + + * Fix a small bug in our use of umask() return value. + (merge 3096b2e jk/fix-refresh-utime later to maint). + + * An ancient test framework enhancement to allow color was not + entirely correct; this makes it work even when tput needs to read + from the ~/.terminfo under the user's real HOME directory. + (merge d5c1b7c rh/test-color-avoid-terminfo-in-original-home later to maint). + + * A minor bugfix when pack bitmap is used with "rev-list --count". + (merge c8a70d3 jk/rev-list-no-bitmap-while-pruning later to maint). + + * "git config" failed to update the configuration file when the + underlying filesystem is incapable of renaming a file that is still + open. + (merge 7a64592 kb/config-unmap-before-renaming later to maint). + + * Avoid possible ssize_t to int truncation. + (merge 6c8afe4 mh/strbuf-read-file-returns-ssize-t later to maint). + + * When you say "!<ENTER>" while running say "git log", you'd confuse + yourself in the resulting shell, that may look as if you took + control back to the original shell you spawned "git log" from but + that isn't what is happening. To that new shell, we leaked + GIT_PAGER_IN_USE environment variable that was meant as a local + communication between the original "Git" and subprocesses that was + spawned by it after we launched the pager, which caused many + "interesting" things to happen, e.g. "git diff | cat" still paints + its output in color by default. + + Stop leaking that environment variable to the pager's half of the + fork; we only need it on "Git" side when we spawn the pager. + (merge 124b519 jc/unexport-git-pager-in-use-in-pager later to maint). + + * Abandoning an already applied change in "git rebase -i" with + "--continue" left CHERRY_PICK_HEAD and confused later steps. + (merge 0e0aff4 js/rebase-i-clean-up-upon-continue-to-skip later to maint). + + * We used to ask libCURL to use the most secure authentication method + available when talking to an HTTP proxy only when we were told to + talk to one via configuration variables. We now ask libCURL to + always use the most secure authentication method, because the user + can tell libCURL to use an HTTP proxy via an environment variable + without using configuration variables. + (merge 5841520 et/http-proxyauth later to maint). + + * A fix to a minor regression to "git fsck" in v2.2 era that started + complaining about a body-less tag object when it lacks a separator + empty line after its header to separate it with a non-existent body. + (merge 84d18c0 jc/fsck-retire-require-eoh later to maint). + * Code cleanups and documentation updates. (merge 0269f96 mm/usage-log-l-can-take-regex later to maint). (merge 64f2589 nd/t1509-chroot-test later to maint). @@ -433,3 +544,20 @@ notes for details). (merge e6a268c sb/glossary-submodule later to maint). (merge ec48a76 sb/submodule-doc-intro later to maint). (merge 14f8b9b jk/clone-dissociate later to maint). + (merge 055c7e9 sb/pack-protocol-mention-smart-http later to maint). + (merge 7c37a5d jk/make-fix-dependencies later to maint). + (merge fc0aa39 sg/merge-summary-config later to maint). + (merge 329af6c pt/t0302-needs-sanity later to maint). + (merge d614f07 fk/doc-format-patch-vn later to maint). + (merge 72dbb36 sg/completion-commit-cleanup later to maint). + (merge e654eb2 es/utf8-stupid-compiler-workaround later to maint). + (merge 34b935c es/osx-header-pollutes-mask-macro later to maint). + (merge ab7fade jc/prompt-document-ps1-state-separator later to maint). + (merge 25f600e mm/describe-doc later to maint). + (merge 83fe167 mm/branch-doc-updates later to maint). + (merge 75d2e5a ls/hint-rev-list-count later to maint). + (merge edc8f71 cb/subtree-tests-update later to maint). + (merge 5330e6e sb/p5310-and-chain later to maint). + (merge c4ac525 tb/checkout-doc later to maint). + (merge e479c5f jk/pretty-encoding-doc later to maint). + (merge 7e837c6 ss/clone-guess-dir-name-simplify later to maint). diff --git a/Documentation/config.txt b/Documentation/config.txt index 43bb53c..75c3722 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -2161,6 +2161,11 @@ rebase.autoStash:: successful rebase might result in non-trivial conflicts. Defaults to false. +rebase.instructionFormat + A format string, as specified in linkgit:git-log[1], to be used for + the instruction list during an interactive rebase. The format will automatically + have the long commit hash prepended to the format. + receive.advertiseAtomic:: By default, git-receive-pack will advertise the atomic push capability to its clients. If you don't want to this capability diff --git a/Documentation/fmt-merge-msg-config.txt b/Documentation/fmt-merge-msg-config.txt new file mode 100644 index 0000000..c73cfa9 --- /dev/null +++ b/Documentation/fmt-merge-msg-config.txt @@ -0,0 +1,10 @@ +merge.branchdesc:: + In addition to branch names, populate the log message with + the branch description text associated with them. Defaults + to false. + +merge.log:: + In addition to branch names, populate the log message with at + most the specified number of one-line descriptions from the + actual commits that are being merged. Defaults to false, and + true is a synonym for 20. diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt index 9f23a86..e6e947c 100644 --- a/Documentation/git-blame.txt +++ b/Documentation/git-blame.txt @@ -76,6 +76,8 @@ include::blame-options.txt[] -e:: --show-email:: Show the author email instead of author name (Default: off). + This can also be controlled via the `blame.showEmail` config + option. -w:: Ignore whitespace when comparing the parent's version and diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 359619b..a67138a 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -81,7 +81,7 @@ OPTIONS `--track` or `--set-upstream`. -D:: - Delete a branch irrespective of its merged status. + Shortcut for `--delete --force`. -l:: --create-reflog:: @@ -95,13 +95,17 @@ OPTIONS --force:: Reset <branchname> to <startpoint> if <branchname> exists already. Without `-f` 'git branch' refuses to change an existing branch. + In combination with `-d` (or `--delete`), allow deleting the + branch irrespective of its merged status. In combination with + `-m` (or `--move`), allow renaming the branch even if the new + branch name already exists. -m:: --move:: Move/rename a branch and the corresponding reflog. -M:: - Move/rename a branch even if the new branch name already exists. + Shortcut for `--move --force`. --color[=<when>]:: Color branches to highlight current, local, and diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index d263a56..63b739c 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -3,7 +3,7 @@ git-checkout(1) NAME ---- -git-checkout - Checkout a branch or paths to the working tree +git-checkout - Switch branches or restore working tree files SYNOPSIS -------- @@ -89,6 +89,10 @@ Omitting <branch> detaches HEAD at the tip of the current branch. (i.e. commit, tag or tree) to update the index for the given paths before updating the working tree. + +'git checkout' with <paths> or `--patch` is used to restore modified or +deleted paths to their original contents from the index or replace paths +with the contents from a named <tree-ish> (most often a commit-ish). ++ The index may contain unmerged entries because of a previous failed merge. By default, if you try to check out such an entry from the index, the checkout operation will fail and nothing will be checked out. @@ -225,13 +229,6 @@ This means that you can use `git checkout -p` to selectively discard edits from your current working tree. See the ``Interactive Mode'' section of linkgit:git-add[1] to learn how to operate the `--patch` mode. ---to=<path>:: - Check out a branch in a separate working directory at - `<path>`. A new working directory is linked to the current - repository, sharing everything except working directory - specific files such as HEAD, index... See "MULTIPLE WORKING - TREES" section for more information. - --ignore-other-worktrees:: `git checkout` refuses when the wanted ref is already checked out by another worktree. This option makes it check the ref @@ -401,71 +398,6 @@ $ git reflog -2 HEAD # or $ git log -g -2 HEAD ------------ -MULTIPLE WORKING TREES ----------------------- - -A git repository can support multiple working trees, allowing you to check -out more than one branch at a time. With `git checkout --to` a new working -tree is associated with the repository. This new working tree is called a -"linked working tree" as opposed to the "main working tree" prepared by "git -init" or "git clone". A repository has one main working tree (if it's not a -bare repository) and zero or more linked working trees. - -Each linked working tree has a private sub-directory in the repository's -$GIT_DIR/worktrees directory. The private sub-directory's name is usually -the base name of the linked working tree's path, possibly appended with a -number to make it unique. For example, when `$GIT_DIR=/path/main/.git` the -command `git checkout --to /path/other/test-next next` creates the linked -working tree in `/path/other/test-next` and also creates a -`$GIT_DIR/worktrees/test-next` directory (or `$GIT_DIR/worktrees/test-next1` -if `test-next` is already taken). - -Within a linked working tree, $GIT_DIR is set to point to this private -directory (e.g. `/path/main/.git/worktrees/test-next` in the example) and -$GIT_COMMON_DIR is set to point back to the main working tree's $GIT_DIR -(e.g. `/path/main/.git`). These settings are made in a `.git` file located at -the top directory of the linked working tree. - -Path resolution via `git rev-parse --git-path` uses either -$GIT_DIR or $GIT_COMMON_DIR depending on the path. For example, in the -linked working tree `git rev-parse --git-path HEAD` returns -`/path/main/.git/worktrees/test-next/HEAD` (not -`/path/other/test-next/.git/HEAD` or `/path/main/.git/HEAD`) while `git -rev-parse --git-path refs/heads/master` uses -$GIT_COMMON_DIR and returns `/path/main/.git/refs/heads/master`, -since refs are shared across all working trees. - -See linkgit:gitrepository-layout[5] for more information. The rule of -thumb is do not make any assumption about whether a path belongs to -$GIT_DIR or $GIT_COMMON_DIR when you need to directly access something -inside $GIT_DIR. Use `git rev-parse --git-path` to get the final path. - -When you are done with a linked working tree you can simply delete it. -The working tree's entry in the repository's $GIT_DIR/worktrees -directory will eventually be removed automatically (see -`gc.pruneworktreesexpire` in linkgit::git-config[1]), or you can run -`git prune --worktrees` in the main or any linked working tree to -clean up any stale entries in $GIT_DIR/worktrees. - -If you move a linked working directory to another file system, or -within a file system that does not support hard links, you need to run -at least one git command inside the linked working directory -(e.g. `git status`) in order to update its entry in $GIT_DIR/worktrees -so that it does not get automatically removed. - -To prevent a $GIT_DIR/worktrees entry from from being pruned (which -can be useful in some situations, such as when the -entry's working tree is stored on a portable device), add a file named -'locked' to the entry's directory. The file contains the reason in -plain text. For example, if a linked working tree's `.git` file points -to `/path/main/.git/worktrees/test-next` then a file named -`/path/main/.git/worktrees/test-next/locked` will prevent the -`test-next` entry from being pruned. See -linkgit:gitrepository-layout[5] for details. - -Multiple checkout support for submodules is incomplete. It is NOT -recommended to make multiple checkouts of a superproject. - EXAMPLES -------- diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt index d20ca40..e045fc7 100644 --- a/Documentation/git-describe.txt +++ b/Documentation/git-describe.txt @@ -3,7 +3,7 @@ git-describe(1) NAME ---- -git-describe - Show the most recent tag that is reachable from a commit +git-describe - Describe a commit using the most recent tag reachable from it SYNOPSIS diff --git a/Documentation/git-fmt-merge-msg.txt b/Documentation/git-fmt-merge-msg.txt index bb1232a..55a9a4b 100644 --- a/Documentation/git-fmt-merge-msg.txt +++ b/Documentation/git-fmt-merge-msg.txt @@ -51,17 +51,7 @@ OPTIONS CONFIGURATION ------------- - -merge.branchdesc:: - In addition to branch names, populate the log message with - the branch description text associated with them. Defaults - to false. - -merge.log:: - In addition to branch names, populate the log message with at - most the specified number of one-line descriptions from the - actual commits that are being merged. Defaults to false, and - true is a synonym for 20. +include::fmt-merge-msg-config.txt[] merge.summary:: Synonym to `merge.log`; this is deprecated and will be removed in diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index bb3ea93..0dac4e9 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -170,7 +170,7 @@ will want to ensure that threading is disabled for `git send-email`. -v <n>:: --reroll-count=<n>:: Mark the series as the <n>-th iteration of the topic. The - output filenames have `v<n>` pretended to them, and the + output filenames have `v<n>` prepended to them, and the subject prefix ("PATCH" by default, but configurable via the `--subject-prefix` option) has ` v<n>` appended to it. E.g. `--reroll-count=4` may produce `v4-0001-add-makefile.patch` diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt index 1cf3bed..7a493c8 100644 --- a/Documentation/git-prune.txt +++ b/Documentation/git-prune.txt @@ -48,9 +48,6 @@ OPTIONS --expire <time>:: Only expire loose objects older than <time>. ---worktrees:: - Prune dead working tree information in $GIT_DIR/worktrees. - <head>...:: In addition to objects reachable from any of our references, keep objects diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt index 712ab4b..93c72a2 100644 --- a/Documentation/git-pull.txt +++ b/Documentation/git-pull.txt @@ -74,9 +74,6 @@ pulling or stash them away with linkgit:git-stash[1]. OPTIONS ------- -Options meant for 'git pull' itself and the underlying 'git merge' -must be given before the options meant for 'git fetch'. - -q:: --quiet:: This is passed to both underlying git-fetch to squelch reporting of diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 1d01baa..7dc613c 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -213,6 +213,9 @@ rebase.autoSquash:: rebase.autoStash:: If set to true enable '--autostash' option by default. +rebase.instructionFormat:: + Custom commit list format to use during an '--interactive' rebase. + OPTIONS ------- --onto <newbase>:: @@ -359,6 +362,10 @@ default is `--no-fork-point`, otherwise the default is `--fork-point`. Make a list of the commits which are about to be rebased. Let the user edit that list before rebasing. This mode can also be used to split commits (see SPLITTING COMMITS below). ++ +The commit list format can be changed by setting the configuration option +rebase.instructionFormat. A customized instruction format will automatically +have the long commit hash prepended to the format. -p:: --preserve-merges:: diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index b10ea60..7b49c85 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -56,6 +56,7 @@ SYNOPSIS [ --reverse ] [ --walk-reflogs ] [ --no-walk ] [ --do-walk ] + [ --count ] [ --use-bitmap-index ] <commit>... [ \-- <paths>... ] diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 8045546..f14705e 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -49,17 +49,17 @@ Composing of 'sendemail.annotate'. See the CONFIGURATION section for 'sendemail.multiEdit'. ---bcc=<address>:: +--bcc=<address>,...:: Specify a "Bcc:" value for each email. Default is the value of 'sendemail.bcc'. + -The --bcc option must be repeated for each user you want on the bcc list. +This option may be specified multiple times. ---cc=<address>:: +--cc=<address>,...:: Specify a starting "Cc:" value for each email. Default is the value of 'sendemail.cc'. + -The --cc option must be repeated for each user you want on the cc list. +This option may be specified multiple times. --compose:: Invoke a text editor (see GIT_EDITOR in linkgit:git-var[1]) @@ -110,13 +110,13 @@ is not set, this will be prompted for. Only necessary if --compose is also set. If --compose is not set, this will be prompted for. ---to=<address>:: +--to=<address>,...:: Specify the primary recipient of the emails generated. Generally, this will be the upstream maintainer of the project involved. Default is the value of the 'sendemail.to' configuration value; if that is unspecified, and --to-cmd is not specified, this will be prompted for. + -The --to option must be repeated for each user you want on the to list. +This option may be specified multiple times. --8bit-encoding=<encoding>:: When encountering a non-ASCII message or subject that does not @@ -383,7 +383,24 @@ sendemail.aliasesFile:: sendemail.aliasFileType:: Format of the file(s) specified in sendemail.aliasesFile. Must be - one of 'mutt', 'mailrc', 'pine', 'elm', or 'gnus'. + one of 'mutt', 'mailrc', 'pine', 'elm', or 'gnus', or 'sendmail'. ++ +What an alias file in each format looks like can be found in +the documentation of the email program of the same name. The +differences and limitations from the standard formats are +described below: ++ +-- +sendmail;; +* Quoted aliases and quoted addresses are not supported: lines that + contain a `"` symbol are ignored. +* Redirection to a file (`/path/name`) or pipe (`|command`) is not + supported. +* File inclusion (`:include: /path/name`) is not supported. +* Warnings are printed on the standard error output for any + explicitly unsupported constructs, and any other lines that are not + recognized by the parser. +-- sendemail.multiEdit:: If true (default), a single editor instance will be spawned to edit diff --git a/Documentation/git-verify-commit.txt b/Documentation/git-verify-commit.txt index 9413e28..ecf4da1 100644 --- a/Documentation/git-verify-commit.txt +++ b/Documentation/git-verify-commit.txt @@ -16,6 +16,10 @@ Validates the gpg signature created by 'git commit -S'. OPTIONS ------- +--raw:: + Print the raw gpg status output to standard error instead of the normal + human-readable output. + -v:: --verbose:: Print the contents of the commit object before validating it. diff --git a/Documentation/git-verify-tag.txt b/Documentation/git-verify-tag.txt index f88ba96..d590edc 100644 --- a/Documentation/git-verify-tag.txt +++ b/Documentation/git-verify-tag.txt @@ -16,6 +16,10 @@ Validates the gpg signature created by 'git tag'. OPTIONS ------- +--raw:: + Print the raw gpg status output to standard error instead of the normal + human-readable output. + -v:: --verbose:: Print the contents of the tag object before validating it. diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt new file mode 100644 index 0000000..3387e2f --- /dev/null +++ b/Documentation/git-worktree.txt @@ -0,0 +1,176 @@ +git-worktree(1) +=============== + +NAME +---- +git-worktree - Manage multiple worktrees + + +SYNOPSIS +-------- +[verse] +'git worktree add' [-f] [--detach] [-b <new-branch>] <path> [<branch>] +'git worktree prune' [-n] [-v] [--expire <expire>] + +DESCRIPTION +----------- + +Manage multiple worktrees attached to the same repository. + +A git repository can support multiple working trees, allowing you to check +out more than one branch at a time. With `git worktree add` a new working +tree is associated with the repository. This new working tree is called a +"linked working tree" as opposed to the "main working tree" prepared by "git +init" or "git clone". A repository has one main working tree (if it's not a +bare repository) and zero or more linked working trees. + +When you are done with a linked working tree you can simply delete it. +The working tree's administrative files in the repository (see +"DETAILS" below) will eventually be removed automatically (see +`gc.pruneworktreesexpire` in linkgit::git-config[1]), or you can run +`git worktree prune` in the main or any linked working tree to +clean up any stale administrative files. + +If you move a linked working directory to another file system, or +within a file system that does not support hard links, you need to run +at least one git command inside the linked working directory +(e.g. `git status`) in order to update its administrative files in the +repository so that they do not get automatically pruned. + +If a linked working tree is stored on a portable device or network share +which is not always mounted, you can prevent its administrative files from +being pruned by creating a file named 'lock' alongside the other +administrative files, optionally containing a plain text reason that +pruning should be suppressed. See section "DETAILS" for more information. + +COMMANDS +-------- +add <path> [<branch>]:: + +Create `<path>` and checkout `<branch>` into it. The new working directory +is linked to the current repository, sharing everything except working +directory specific files such as HEAD, index, etc. ++ +If `<branch>` is omitted and neither `-b` nor `-B` is used, then, as a +convenience, a new branch based at HEAD is created automatically, as if +`-b $(basename <path>)` was specified. + +prune:: + +Prune working tree information in $GIT_DIR/worktrees. + +OPTIONS +------- + +-f:: +--force:: + By default, `add` refuses to create a new worktree when `<branch>` + is already checked out by another worktree. This option overrides + that safeguard. + +-b <new-branch>:: +-B <new-branch>:: + With `add`, create a new branch named `<new-branch>` starting at + `<branch>`, and check out `<new-branch>` into the new worktree. + If `<branch>` is omitted, it defaults to HEAD. + By default, `-b` refuses to create a new branch if it already + exists. `-B` overrides this safeguard, resetting `<new-branch>` to + `<branch>`. + +--detach:: + With `add`, detach HEAD in the new worktree. See "DETACHED HEAD" in + linkgit:git-checkout[1]. + +-n:: +--dry-run:: + With `prune`, do not remove anything; just report what it would + remove. + +-v:: +--verbose:: + With `prune`, report all removals. + +--expire <time>:: + With `prune`, only expire unused worktrees older than <time>. + +DETAILS +------- +Each linked working tree has a private sub-directory in the repository's +$GIT_DIR/worktrees directory. The private sub-directory's name is usually +the base name of the linked working tree's path, possibly appended with a +number to make it unique. For example, when `$GIT_DIR=/path/main/.git` the +command `git worktree add /path/other/test-next next` creates the linked +working tree in `/path/other/test-next` and also creates a +`$GIT_DIR/worktrees/test-next` directory (or `$GIT_DIR/worktrees/test-next1` +if `test-next` is already taken). + +Within a linked working tree, $GIT_DIR is set to point to this private +directory (e.g. `/path/main/.git/worktrees/test-next` in the example) and +$GIT_COMMON_DIR is set to point back to the main working tree's $GIT_DIR +(e.g. `/path/main/.git`). These settings are made in a `.git` file located at +the top directory of the linked working tree. + +Path resolution via `git rev-parse --git-path` uses either +$GIT_DIR or $GIT_COMMON_DIR depending on the path. For example, in the +linked working tree `git rev-parse --git-path HEAD` returns +`/path/main/.git/worktrees/test-next/HEAD` (not +`/path/other/test-next/.git/HEAD` or `/path/main/.git/HEAD`) while `git +rev-parse --git-path refs/heads/master` uses +$GIT_COMMON_DIR and returns `/path/main/.git/refs/heads/master`, +since refs are shared across all working trees. + +See linkgit:gitrepository-layout[5] for more information. The rule of +thumb is do not make any assumption about whether a path belongs to +$GIT_DIR or $GIT_COMMON_DIR when you need to directly access something +inside $GIT_DIR. Use `git rev-parse --git-path` to get the final path. + +To prevent a $GIT_DIR/worktrees entry from from being pruned (which +can be useful in some situations, such as when the +entry's working tree is stored on a portable device), add a file named +'locked' to the entry's directory. The file contains the reason in +plain text. For example, if a linked working tree's `.git` file points +to `/path/main/.git/worktrees/test-next` then a file named +`/path/main/.git/worktrees/test-next/locked` will prevent the +`test-next` entry from being pruned. See +linkgit:gitrepository-layout[5] for details. + +EXAMPLES +-------- +You are in the middle of a refactoring session and your boss comes in and +demands that you fix something immediately. You might typically use +linkgit:git-stash[1] to store your changes away temporarily, however, your +worktree is in such a state of disarray (with new, moved, and removed files, +and other bits and pieces strewn around) that you don't want to risk +disturbing any of it. Instead, you create a temporary linked worktree to +make the emergency fix, remove it when done, and then resume your earlier +refactoring session. + +------------ +$ git worktree add -b emergency-fix ../temp master +$ pushd ../temp +# ... hack hack hack ... +$ git commit -a -m 'emergency fix for boss' +$ popd +$ rm -rf ../temp +$ git worktree prune +------------ + +BUGS +---- +Multiple checkout in general is still experimental, and the support +for submodules is incomplete. It is NOT recommended to make multiple +checkouts of a superproject. + +git-worktree could provide more automation for tasks currently +performed manually, such as: + +- `remove` to remove a linked worktree and its administrative files (and + warn if the worktree is dirty) +- `mv` to move or rename a worktree and update its administrative files +- `list` to list linked worktrees +- `lock` to prevent automatic pruning of administrative files (for instance, + for a worktree on a portable device) + +GIT +--- +Part of the linkgit:git[1] suite diff --git a/Documentation/git.txt b/Documentation/git.txt index 45b64a7..f87d332 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,9 +43,18 @@ unreleased) version of Git, that is available from the 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v2.4.3/git.html[documentation for release 2.4.3] +* link:v2.5.0/git.html[documentation for release 2.5] * release notes for + link:RelNotes/2.5.0.txt[2.5], + +* link:v2.4.7/git.html[documentation for release 2.4.7] + +* release notes for + link:RelNotes/2.4.7.txt[2.4.7], + link:RelNotes/2.4.6.txt[2.4.6], + link:RelNotes/2.4.5.txt[2.4.5], + link:RelNotes/2.4.4.txt[2.4.4], link:RelNotes/2.4.3.txt[2.4.3], link:RelNotes/2.4.2.txt[2.4.2], link:RelNotes/2.4.1.txt[2.4.1], @@ -842,7 +851,7 @@ Git so take care if using Cogito etc. normally in $GIT_DIR will be taken from this path instead. Worktree-specific files such as HEAD or index are taken from $GIT_DIR. See linkgit:gitrepository-layout[5] and - the section 'MULTIPLE CHECKOUT MODE' in linkgit:checkout[1] + linkgit:git-worktree[1] for details. This variable has lower precedence than other path variables such as GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY... @@ -1000,9 +1009,20 @@ Unsetting the variable, or setting it to empty, "0" or Enables trace messages for all packets coming in or out of a given program. This can help with debugging object negotiation or other protocol issues. Tracing is turned off at a packet - starting with "PACK". + starting with "PACK" (but see 'GIT_TRACE_PACKFILE' below). See 'GIT_TRACE' for available trace output options. +'GIT_TRACE_PACKFILE':: + Enables tracing of packfiles sent or received by a + given program. Unlike other trace output, this trace is + verbatim: no headers, and no quoting of binary data. You almost + certainly want to direct into a file (e.g., + `GIT_TRACE_PACKFILE=/tmp/my.pack`) rather than displaying it on + the terminal or mixing it with other trace output. ++ +Note that this is currently only implemented for the client side +of clones and fetches. + 'GIT_TRACE_PERFORMANCE':: Enables performance related trace messages, e.g. total execution time of each Git command. diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 70899b3..81fe586 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -774,7 +774,7 @@ To define a custom merge driver `filfre`, add a section to your ---------------------------------------------------------------- [merge "filfre"] name = feel-free merge driver - driver = filfre %O %A %B + driver = filfre %O %A %B %L %P recursive = binary ---------------------------------------------------------------- @@ -800,6 +800,9 @@ merge between common ancestors, when there are more than one. When left unspecified, the driver itself is used for both internal merge and the final merge. +The merge driver can learn the pathname in which the merged result +will be stored via placeholder `%P`. + `conflict-marker-size` ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/Documentation/gitweb.conf.txt b/Documentation/gitweb.conf.txt index a096e7d..8a42270 100644 --- a/Documentation/gitweb.conf.txt +++ b/Documentation/gitweb.conf.txt @@ -487,7 +487,7 @@ By default it is set to (), i.e. an empty list. This means that gitweb would not try to create project URL (to fetch) from project name. $projects_list_group_categories:: - Whether to enables the grouping of projects by category on the project + Whether to enable the grouping of projects by category on the project list page. The category of a project is determined by the `$GIT_DIR/category` file or the `gitweb.category` variable in each repository's configuration. Disabled by default (set to 0). diff --git a/Documentation/i18n.txt b/Documentation/i18n.txt index e9a1d5d..2dd79db 100644 --- a/Documentation/i18n.txt +++ b/Documentation/i18n.txt @@ -1,18 +1,31 @@ -At the core level, Git is character encoding agnostic. - - - The pathnames recorded in the index and in the tree objects - are treated as uninterpreted sequences of non-NUL bytes. - What readdir(2) returns are what are recorded and compared - with the data Git keeps track of, which in turn are expected - to be what lstat(2) and creat(2) accepts. There is no such - thing as pathname encoding translation. +Git is to some extent character encoding agnostic. - The contents of the blob objects are uninterpreted sequences of bytes. There is no encoding translation at the core level. - - The commit log messages are uninterpreted sequences of non-NUL - bytes. + - Path names are encoded in UTF-8 normalization form C. This + applies to tree objects, the index file, ref names, as well as + path names in command line arguments, environment variables + and config files (`.git/config` (see linkgit:git-config[1]), + linkgit:gitignore[5], linkgit:gitattributes[5] and + linkgit:gitmodules[5]). ++ +Note that Git at the core level treats path names simply as +sequences of non-NUL bytes, there are no path name encoding +conversions (except on Mac and Windows). Therefore, using +non-ASCII path names will mostly work even on platforms and file +systems that use legacy extended ASCII encodings. However, +repositories created on such systems will not work properly on +UTF-8-based systems (e.g. Linux, Mac, Windows) and vice versa. +Additionally, many Git-based tools simply assume path names to +be UTF-8 and will fail to display other encodings correctly. + + - Commit log messages are typically encoded in UTF-8, but other + extended ASCII encodings are also supported. This includes + ISO-8859-x, CP125x and many others, but _not_ UTF-16/32, + EBCDIC and CJK multi-byte encodings (GBK, Shift-JIS, Big5, + EUC-x, CP9xx etc.). Although we encourage that the commit log messages are encoded in UTF-8, both the core and Git Porcelain are designed not to diff --git a/Documentation/merge-config.txt b/Documentation/merge-config.txt index 8a0e52f..002ca58 100644 --- a/Documentation/merge-config.txt +++ b/Documentation/merge-config.txt @@ -26,11 +26,7 @@ merge.ff:: allowed (equivalent to giving the `--ff-only` option from the command line). -merge.log:: - In addition to branch names, populate the log message with at - most the specified number of one-line descriptions from the - actual commits that are being merged. Defaults to false, and - true is a synonym for 20. +include::fmt-merge-msg-config.txt[] merge.renameLimit:: The number of files to consider when performing rename detection diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt index 74aa01a..642af6e 100644 --- a/Documentation/pretty-options.txt +++ b/Documentation/pretty-options.txt @@ -37,7 +37,10 @@ people using 80-column terminals. in their encoding header; this option can be used to tell the command to re-code the commit log message in the encoding preferred by the user. For non plumbing commands this - defaults to UTF-8. + defaults to UTF-8. Note that if an object claims to be encoded + in `X` and we are outputting in `X`, we will output the object + verbatim; this means that invalid sequences in the original + commit may be copied to the output. --notes[=<ref>]:: Show the notes (see linkgit:git-notes[1]) that annotate the diff --git a/Documentation/technical/api-argv-array.txt b/Documentation/technical/api-argv-array.txt index 1a79781..8076172 100644 --- a/Documentation/technical/api-argv-array.txt +++ b/Documentation/technical/api-argv-array.txt @@ -46,6 +46,9 @@ Functions Format a string and push it onto the end of the array. This is a convenience wrapper combining `strbuf_addf` and `argv_array_push`. +`argv_array_pushv`:: + Push a null-terminated array of strings onto the end of the array. + `argv_array_pop`:: Remove the final element from the array. If there are no elements in the array, do nothing. diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt index 1f2db31..5f0757d 100644 --- a/Documentation/technical/api-parse-options.txt +++ b/Documentation/technical/api-parse-options.txt @@ -168,6 +168,12 @@ There are some macros to easily define options: Introduce an option with integer argument. The integer is put into `int_var`. +`OPT_MAGNITUDE(short, long, &unsigned_long_var, description)`:: + Introduce an option with a size argument. The argument must be a + non-negative integer and may include a suffix of 'k', 'm' or 'g' to + scale the provided value by 1024, 1024^2 or 1024^3 respectively. + The scaled value is put into `unsigned_long_var`. + `OPT_DATE(short, long, &int_var, description)`:: Introduce an option with date argument, see `approxidate()`. The timestamp is put into `int_var`. @@ -212,6 +218,19 @@ There are some macros to easily define options: Use it to hide deprecated options that are still to be recognized and ignored silently. +`OPT_PASSTHRU(short, long, &char_var, arg_str, description, flags)`:: + Introduce an option that will be reconstructed into a char* string, + which must be initialized to NULL. This is useful when you need to + pass the command-line option to another command. Any previous value + will be overwritten, so this should only be used for options where + the last one specified on the command line wins. + +`OPT_PASSTHRU_ARGV(short, long, &argv_array_var, arg_str, description, flags)`:: + Introduce an option where all instances of it on the command-line will + be reconstructed into an argv_array. This is useful when you need to + pass the command-line option, which can be specified multiple times, + to another command. + The last element of the array must be `OPT_END()`. diff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt index fc09c63..4064fc7 100644 --- a/Documentation/technical/pack-protocol.txt +++ b/Documentation/technical/pack-protocol.txt @@ -1,11 +1,11 @@ Packfile transfer protocols =========================== -Git supports transferring data in packfiles over the ssh://, git:// and +Git supports transferring data in packfiles over the ssh://, git://, http:// and file:// transports. There exist two sets of protocols, one for pushing data from a client to a server and another for fetching data from a -server to a client. All three transports (ssh, git, file) use the same -protocol to transfer data. +server to a client. The three transports (ssh, git, file) use the same +protocol to transfer data. http is documented in http-protocol.txt. The processes invoked in the canonical Git implementation are 'upload-pack' on the server side and 'fetch-pack' on the client side for fetching data; diff --git a/Documentation/technical/racy-git.txt b/Documentation/technical/racy-git.txt index 242a044..4a8be4d 100644 --- a/Documentation/technical/racy-git.txt +++ b/Documentation/technical/racy-git.txt @@ -41,13 +41,17 @@ With a `USE_STDEV` compile-time option, `st_dev` is also compared, but this is not enabled by default because this member is not stable on network filesystems. With `USE_NSEC` compile-time option, `st_mtim.tv_nsec` and `st_ctim.tv_nsec` -members are also compared, but this is not enabled by default +members are also compared. On Linux, this is not enabled by default because in-core timestamps can have finer granularity than on-disk timestamps, resulting in meaningless changes when an inode is evicted from the inode cache. See commit 8ce13b0 of git://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git ([PATCH] Sync in core time granularity with filesystems, -2005-01-04). +2005-01-04). This patch is included in kernel 2.6.11 and newer, but +only fixes the issue for file systems with exactly 1 ns or 1 s +resolution. Other file systems are still broken in current Linux +kernels (e.g. CEPH, CIFS, NTFS, UDF), see +https://lkml.org/lkml/2015/6/9/714 Racy Git -------- diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index bfb715d..cd96820 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.4.0.GIT +DEF_VER=v2.5.0 LF=' ' @@ -217,10 +217,11 @@ all:: # as the compiler can crash (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299) # # Define USE_NSEC below if you want git to care about sub-second file mtimes -# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and -# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely -# randomly break unless your underlying filesystem supports those sub-second -# times (my ext3 doesn't). +# and ctimes. Note that you need recent glibc (at least 2.2.4) for this. On +# Linux, kernel 2.6.11 or newer is required for reliable sub-second file times +# on file systems with exactly 1 ns or 1 s resolution. If you intend to use Git +# on other file systems (e.g. CEPH, CIFS, NTFS, UDF), don't enable USE_NSEC. See +# Documentation/technical/racy-git.txt for details. # # Define USE_ST_TIMESPEC if your "struct stat" uses "st_ctimespec" instead of # "st_ctim" @@ -474,7 +475,6 @@ SCRIPT_SH += git-merge-octopus.sh SCRIPT_SH += git-merge-one-file.sh SCRIPT_SH += git-merge-resolve.sh SCRIPT_SH += git-mergetool.sh -SCRIPT_SH += git-pull.sh SCRIPT_SH += git-quiltimport.sh SCRIPT_SH += git-rebase.sh SCRIPT_SH += git-remote-testgit.sh @@ -762,6 +762,7 @@ LIB_OBJS += reachable.o LIB_OBJS += read-cache.o LIB_OBJS += reflog-walk.o LIB_OBJS += refs.o +LIB_OBJS += ref-filter.o LIB_OBJS += remote.o LIB_OBJS += replace_object.o LIB_OBJS += rerere.o @@ -877,6 +878,7 @@ BUILTIN_OBJS += builtin/pack-refs.o BUILTIN_OBJS += builtin/patch-id.o BUILTIN_OBJS += builtin/prune-packed.o BUILTIN_OBJS += builtin/prune.o +BUILTIN_OBJS += builtin/pull.o BUILTIN_OBJS += builtin/push.o BUILTIN_OBJS += builtin/read-tree.o BUILTIN_OBJS += builtin/receive-pack.o @@ -909,6 +911,7 @@ BUILTIN_OBJS += builtin/var.o BUILTIN_OBJS += builtin/verify-commit.o BUILTIN_OBJS += builtin/verify-pack.o BUILTIN_OBJS += builtin/verify-tag.o +BUILTIN_OBJS += builtin/worktree.o BUILTIN_OBJS += builtin/write-tree.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) @@ -1747,7 +1750,7 @@ $(SCRIPT_PERL_GEN): perl/perl.mak perl/perl.mak: perl/PM.stamp perl/PM.stamp: FORCE - $(QUIET_GEN)$(FIND) perl -type f -name '*.pm' | sort >$@+ && \ + @$(FIND) perl -type f -name '*.pm' | sort >$@+ && \ { cmp $@+ $@ >/dev/null 2>/dev/null || mv $@+ $@; } && \ $(RM) $@+ @@ -1784,7 +1787,7 @@ GIT-PERL-DEFINES: FORCE gitweb: $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) all -git-instaweb: git-instaweb.sh gitweb GIT-SCRIPT-DEFINES +git-instaweb: git-instaweb.sh GIT-SCRIPT-DEFINES $(QUIET_GEN)$(cmd_munge_script) && \ chmod +x $@+ && \ mv $@+ $@ @@ -2103,46 +2106,47 @@ GIT-LDFLAGS: FORCE # that runs GIT-BUILD-OPTIONS, and then again to protect it # and the first level quoting from the shell that runs "echo". GIT-BUILD-OPTIONS: FORCE - @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@ - @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@ - @echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@ - @echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@ - @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@ - @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@ - @echo NO_EXPAT=\''$(subst ','\'',$(subst ','\'',$(NO_EXPAT)))'\' >>$@ - @echo USE_LIBPCRE=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@ - @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@ - @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@ - @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@ + @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@+ + @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@+ + @echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@+ + @echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@+ + @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@+ + @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@+ + @echo NO_EXPAT=\''$(subst ','\'',$(subst ','\'',$(NO_EXPAT)))'\' >>$@+ + @echo USE_LIBPCRE=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@+ + @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+ + @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+ + @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+ ifdef TEST_OUTPUT_DIRECTORY - @echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@ + @echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+ endif ifdef GIT_TEST_OPTS - @echo GIT_TEST_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_OPTS)))'\' >>$@ + @echo GIT_TEST_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_OPTS)))'\' >>$@+ endif ifdef GIT_TEST_CMP - @echo GIT_TEST_CMP=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_CMP)))'\' >>$@ + @echo GIT_TEST_CMP=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_CMP)))'\' >>$@+ endif ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT - @echo GIT_TEST_CMP_USE_COPIED_CONTEXT=YesPlease >>$@ + @echo GIT_TEST_CMP_USE_COPIED_CONTEXT=YesPlease >>$@+ endif - @echo NO_GETTEXT=\''$(subst ','\'',$(subst ','\'',$(NO_GETTEXT)))'\' >>$@ - @echo GETTEXT_POISON=\''$(subst ','\'',$(subst ','\'',$(GETTEXT_POISON)))'\' >>$@ + @echo NO_GETTEXT=\''$(subst ','\'',$(subst ','\'',$(NO_GETTEXT)))'\' >>$@+ + @echo GETTEXT_POISON=\''$(subst ','\'',$(subst ','\'',$(GETTEXT_POISON)))'\' >>$@+ ifdef GIT_PERF_REPEAT_COUNT - @echo GIT_PERF_REPEAT_COUNT=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPEAT_COUNT)))'\' >>$@ + @echo GIT_PERF_REPEAT_COUNT=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPEAT_COUNT)))'\' >>$@+ endif ifdef GIT_PERF_REPO - @echo GIT_PERF_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPO)))'\' >>$@ + @echo GIT_PERF_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPO)))'\' >>$@+ endif ifdef GIT_PERF_LARGE_REPO - @echo GIT_PERF_LARGE_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_LARGE_REPO)))'\' >>$@ + @echo GIT_PERF_LARGE_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_LARGE_REPO)))'\' >>$@+ endif ifdef GIT_PERF_MAKE_OPTS - @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@ + @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@+ endif ifdef TEST_GIT_INDEX_VERSION - @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@ + @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@+ endif + @if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi ### Detect Python interpreter path changes ifndef NO_PYTHON @@ -96,6 +96,14 @@ void NORETURN die_resolve_conflict(const char *me) die("Exiting because of an unresolved conflict."); } +void NORETURN die_conclude_merge(void) +{ + error(_("You have not concluded your merge (MERGE_HEAD exists).")); + if (advice_resolve_conflict) + advise(_("Please, commit your changes before you can merge.")); + die(_("Exiting because of unfinished merge.")); +} + void detach_advice(const char *new_name) { const char fmt[] = @@ -24,6 +24,7 @@ __attribute__((format (printf, 1, 2))) void advise(const char *advice, ...); int error_resolve_conflict(const char *me); extern void NORETURN die_resolve_conflict(const char *me); +void NORETURN die_conclude_merge(void); void detach_advice(const char *new_name); #endif /* ADVICE_H */ diff --git a/argv-array.c b/argv-array.c index 256741d..eaed477 100644 --- a/argv-array.c +++ b/argv-array.c @@ -49,6 +49,12 @@ void argv_array_pushl(struct argv_array *array, ...) va_end(ap); } +void argv_array_pushv(struct argv_array *array, const char **argv) +{ + for (; *argv; argv++) + argv_array_push(array, *argv); +} + void argv_array_pop(struct argv_array *array) { if (!array->argc) diff --git a/argv-array.h b/argv-array.h index c65e6e8..a2fa0aa 100644 --- a/argv-array.h +++ b/argv-array.h @@ -17,6 +17,7 @@ __attribute__((format (printf,2,3))) void argv_array_pushf(struct argv_array *, const char *fmt, ...); LAST_ARG_MUST_BE_NULL void argv_array_pushl(struct argv_array *, ...); +void argv_array_pushv(struct argv_array *, const char **); void argv_array_pop(struct argv_array *); void argv_array_clear(struct argv_array *); @@ -98,6 +98,7 @@ extern int cmd_pack_redundant(int argc, const char **argv, const char *prefix); extern int cmd_patch_id(int argc, const char **argv, const char *prefix); extern int cmd_prune(int argc, const char **argv, const char *prefix); extern int cmd_prune_packed(int argc, const char **argv, const char *prefix); +extern int cmd_pull(int argc, const char **argv, const char *prefix); extern int cmd_push(int argc, const char **argv, const char *prefix); extern int cmd_read_tree(int argc, const char **argv, const char *prefix); extern int cmd_receive_pack(int argc, const char **argv, const char *prefix); @@ -133,6 +134,7 @@ extern int cmd_verify_commit(int argc, const char **argv, const char *prefix); extern int cmd_verify_tag(int argc, const char **argv, const char *prefix); extern int cmd_version(int argc, const char **argv, const char *prefix); extern int cmd_whatchanged(int argc, const char **argv, const char *prefix); +extern int cmd_worktree(int argc, const char **argv, const char *prefix); extern int cmd_write_tree(int argc, const char **argv, const char *prefix); extern int cmd_verify_pack(int argc, const char **argv, const char *prefix); extern int cmd_show_ref(int argc, const char **argv, const char *prefix); diff --git a/builtin/add.c b/builtin/add.c index df5135b..4bd98b7 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -63,7 +63,6 @@ static void update_callback(struct diff_queue_struct *q, switch (fix_unmerged_status(p, data)) { default: die(_("unexpected diff status %c"), p->status); - case DIFF_STATUS_ADDED: case DIFF_STATUS_MODIFIED: case DIFF_STATUS_TYPE_CHANGED: if (add_file_to_index(&the_index, path, data->flags)) { diff --git a/builtin/apply.c b/builtin/apply.c index 146be97..54aba4e 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -1638,6 +1638,9 @@ static int parse_fragment(const char *line, unsigned long size, } if (oldlines || newlines) return -1; + if (!deleted && !added) + return -1; + fragment->leading = leading; fragment->trailing = trailing; diff --git a/builtin/blame.c b/builtin/blame.c index 1c998cb..272a222 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -2177,6 +2177,14 @@ static int git_blame_config(const char *var, const char *value, void *cb) blank_boundary = git_config_bool(var, value); return 0; } + if (!strcmp(var, "blame.showemail")) { + int *output_option = cb; + if (git_config_bool(var, value)) + *output_option |= OUTPUT_SHOW_EMAIL; + else + *output_option &= ~OUTPUT_SHOW_EMAIL; + return 0; + } if (!strcmp(var, "blame.date")) { if (!value) return config_error_nonbool(var); @@ -2521,7 +2529,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) unsigned int range_i; long anchor; - git_config(git_blame_config, NULL); + git_config(git_blame_config, &output_option); init_revisions(&revs, NULL); revs.date_mode = blame_date_mode; DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV); diff --git a/builtin/checkout.c b/builtin/checkout.c index 9b49f0e..f71844a 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -19,8 +19,6 @@ #include "ll-merge.h" #include "resolve-undo.h" #include "submodule.h" -#include "argv-array.h" -#include "sigchain.h" static const char * const checkout_usage[] = { N_("git checkout [<options>] <branch>"), @@ -51,8 +49,6 @@ struct checkout_opts { struct pathspec pathspec; struct tree *source_tree; - const char *new_worktree; - const char **saved_argv; int new_worktree_mode; }; @@ -273,9 +269,6 @@ static int checkout_paths(const struct checkout_opts *opts, die(_("Cannot update paths and switch to branch '%s' at the same time."), opts->new_branch); - if (opts->new_worktree) - die(_("'%s' cannot be used with updating paths"), "--to"); - if (opts->patch_mode) return run_add_interactive(revision, "--patch=checkout", &opts->pathspec); @@ -850,138 +843,6 @@ static int switch_branches(const struct checkout_opts *opts, return ret || writeout_error; } -static char *junk_work_tree; -static char *junk_git_dir; -static int is_junk; -static pid_t junk_pid; - -static void remove_junk(void) -{ - struct strbuf sb = STRBUF_INIT; - if (!is_junk || getpid() != junk_pid) - return; - if (junk_git_dir) { - strbuf_addstr(&sb, junk_git_dir); - remove_dir_recursively(&sb, 0); - strbuf_reset(&sb); - } - if (junk_work_tree) { - strbuf_addstr(&sb, junk_work_tree); - remove_dir_recursively(&sb, 0); - } - strbuf_release(&sb); -} - -static void remove_junk_on_signal(int signo) -{ - remove_junk(); - sigchain_pop(signo); - raise(signo); -} - -static int prepare_linked_checkout(const struct checkout_opts *opts, - struct branch_info *new) -{ - struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT; - struct strbuf sb = STRBUF_INIT; - const char *path = opts->new_worktree, *name; - struct stat st; - struct child_process cp; - int counter = 0, len, ret; - - if (!new->commit) - die(_("no branch specified")); - if (file_exists(path) && !is_empty_dir(path)) - die(_("'%s' already exists"), path); - - len = strlen(path); - while (len && is_dir_sep(path[len - 1])) - len--; - - for (name = path + len - 1; name > path; name--) - if (is_dir_sep(*name)) { - name++; - break; - } - strbuf_addstr(&sb_repo, - git_path("worktrees/%.*s", (int)(path + len - name), name)); - len = sb_repo.len; - if (safe_create_leading_directories_const(sb_repo.buf)) - die_errno(_("could not create leading directories of '%s'"), - sb_repo.buf); - while (!stat(sb_repo.buf, &st)) { - counter++; - strbuf_setlen(&sb_repo, len); - strbuf_addf(&sb_repo, "%d", counter); - } - name = strrchr(sb_repo.buf, '/') + 1; - - junk_pid = getpid(); - atexit(remove_junk); - sigchain_push_common(remove_junk_on_signal); - - if (mkdir(sb_repo.buf, 0777)) - die_errno(_("could not create directory of '%s'"), sb_repo.buf); - junk_git_dir = xstrdup(sb_repo.buf); - is_junk = 1; - - /* - * lock the incomplete repo so prune won't delete it, unlock - * after the preparation is over. - */ - strbuf_addf(&sb, "%s/locked", sb_repo.buf); - write_file(sb.buf, 1, "initializing\n"); - - strbuf_addf(&sb_git, "%s/.git", path); - if (safe_create_leading_directories_const(sb_git.buf)) - die_errno(_("could not create leading directories of '%s'"), - sb_git.buf); - junk_work_tree = xstrdup(path); - - strbuf_reset(&sb); - strbuf_addf(&sb, "%s/gitdir", sb_repo.buf); - write_file(sb.buf, 1, "%s\n", real_path(sb_git.buf)); - write_file(sb_git.buf, 1, "gitdir: %s/worktrees/%s\n", - real_path(get_git_common_dir()), name); - /* - * This is to keep resolve_ref() happy. We need a valid HEAD - * or is_git_directory() will reject the directory. Any valid - * value would do because this value will be ignored and - * replaced at the next (real) checkout. - */ - strbuf_reset(&sb); - strbuf_addf(&sb, "%s/HEAD", sb_repo.buf); - write_file(sb.buf, 1, "%s\n", sha1_to_hex(new->commit->object.sha1)); - strbuf_reset(&sb); - strbuf_addf(&sb, "%s/commondir", sb_repo.buf); - write_file(sb.buf, 1, "../..\n"); - - if (!opts->quiet) - fprintf_ln(stderr, _("Enter %s (identifier %s)"), path, name); - - setenv("GIT_CHECKOUT_NEW_WORKTREE", "1", 1); - setenv(GIT_DIR_ENVIRONMENT, sb_git.buf, 1); - setenv(GIT_WORK_TREE_ENVIRONMENT, path, 1); - memset(&cp, 0, sizeof(cp)); - cp.git_cmd = 1; - cp.argv = opts->saved_argv; - ret = run_command(&cp); - if (!ret) { - is_junk = 0; - free(junk_work_tree); - free(junk_git_dir); - junk_work_tree = NULL; - junk_git_dir = NULL; - } - strbuf_reset(&sb); - strbuf_addf(&sb, "%s/locked", sb_repo.buf); - unlink_or_warn(sb.buf); - strbuf_release(&sb); - strbuf_release(&sb_repo); - strbuf_release(&sb_git); - return ret; -} - static int git_checkout_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "diff.ignoresubmodules")) { @@ -1110,7 +971,6 @@ static int parse_branchname_arg(int argc, const char **argv, { struct tree **source_tree = &opts->source_tree; const char **new_branch = &opts->new_branch; - int force_detach = opts->force_detach; int argcount = 0; unsigned char branch_rev[20]; const char *arg; @@ -1231,17 +1091,6 @@ static int parse_branchname_arg(int argc, const char **argv, else new->path = NULL; /* not an existing branch */ - if (new->path && !force_detach && !*new_branch) { - unsigned char sha1[20]; - int flag; - char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag); - if (head_ref && - (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)) && - !opts->ignore_other_worktrees) - check_linked_checkouts(new); - free(head_ref); - } - new->commit = lookup_commit_reference_gently(rev, 1); if (!new->commit) { /* not a commit */ @@ -1321,8 +1170,16 @@ static int checkout_branch(struct checkout_opts *opts, die(_("Cannot switch branch to a non-commit '%s'"), new->name); - if (opts->new_worktree) - return prepare_linked_checkout(opts, new); + if (new->path && !opts->force_detach && !opts->new_branch) { + unsigned char sha1[20]; + int flag; + char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag); + if (head_ref && + (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)) && + !opts->ignore_other_worktrees) + check_linked_checkouts(new); + free(head_ref); + } if (!new->commit && opts->new_branch) { unsigned char rev[20]; @@ -1366,8 +1223,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) N_("do not limit pathspecs to sparse entries only")), OPT_HIDDEN_BOOL(0, "guess", &dwim_new_local_branch, N_("second guess 'git checkout <no-such-branch>'")), - OPT_FILENAME(0, "to", &opts.new_worktree, - N_("check a branch out in a separate working directory")), OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees, N_("do not check if another worktree is holding the given ref")), OPT_END(), @@ -1378,9 +1233,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) opts.overwrite_ignore = 1; opts.prefix = prefix; - opts.saved_argv = xmalloc(sizeof(const char *) * (argc + 2)); - memcpy(opts.saved_argv, argv, sizeof(const char *) * (argc + 1)); - gitmodules_config(); git_config(git_checkout_config, &opts); @@ -1389,13 +1241,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, checkout_usage, PARSE_OPT_KEEP_DASHDASH); - /* recursive execution from checkout_new_worktree() */ opts.new_worktree_mode = getenv("GIT_CHECKOUT_NEW_WORKTREE") != NULL; - if (opts.new_worktree_mode) - opts.new_worktree = NULL; - - if (!opts.new_worktree) - setup_work_tree(); if (conflict_style) { opts.merge = 1; /* implied */ diff --git a/builtin/clean.c b/builtin/clean.c index 6dcb72e..df53def 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -10,7 +10,6 @@ #include "cache.h" #include "dir.h" #include "parse-options.h" -#include "refs.h" #include "string-list.h" #include "quote.h" #include "column.h" @@ -148,6 +147,31 @@ static int exclude_cb(const struct option *opt, const char *arg, int unset) return 0; } +/* + * Return 1 if the given path is the root of a git repository or + * submodule else 0. Will not return 1 for bare repositories with the + * exception of creating a bare repository in "foo/.git" and calling + * is_git_repository("foo"). + */ +static int is_git_repository(struct strbuf *path) +{ + int ret = 0; + int gitfile_error; + size_t orig_path_len = path->len; + assert(orig_path_len != 0); + if (path->buf[orig_path_len - 1] != '/') + strbuf_addch(path, '/'); + strbuf_addstr(path, ".git"); + if (read_gitfile_gently(path->buf, &gitfile_error) || is_git_directory(path->buf)) + ret = 1; + if (gitfile_error == READ_GITFILE_ERR_OPEN_FAILED || + gitfile_error == READ_GITFILE_ERR_READ_FAILED) + ret = 1; /* This could be a real .git file, take the + * safe option and avoid cleaning */ + strbuf_setlen(path, orig_path_len); + return ret; +} + static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, int dry_run, int quiet, int *dir_gone) { @@ -155,13 +179,11 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, struct strbuf quoted = STRBUF_INIT; struct dirent *e; int res = 0, ret = 0, gone = 1, original_len = path->len, len; - unsigned char submodule_head[20]; struct string_list dels = STRING_LIST_INIT_DUP; *dir_gone = 1; - if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && - !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) { + if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && is_git_repository(path)) { if (!quiet) { quote_path_relative(path->buf, prefix, "ed); printf(dry_run ? _(msg_would_skip_git_dir) : _(msg_skip_git_dir), diff --git a/builtin/clone.c b/builtin/clone.c index 8539b8d..303a3a7 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -147,6 +147,7 @@ static char *get_repo_path(const char *repo, int *is_bundle) static char *guess_dir_name(const char *repo, int is_bundle, int is_bare) { const char *end = repo + strlen(repo), *start; + size_t len; char *dir; /* @@ -173,20 +174,12 @@ static char *guess_dir_name(const char *repo, int is_bundle, int is_bare) /* * Strip .{bundle,git}. */ - if (is_bundle) { - if (end - start > 7 && !strncmp(end - 7, ".bundle", 7)) - end -= 7; - } else { - if (end - start > 4 && !strncmp(end - 4, ".git", 4)) - end -= 4; - } + strip_suffix(start, is_bundle ? ".bundle" : ".git" , &len); - if (is_bare) { - struct strbuf result = STRBUF_INIT; - strbuf_addf(&result, "%.*s.git", (int)(end - start), start); - dir = strbuf_detach(&result, NULL); - } else - dir = xstrndup(start, end - start); + if (is_bare) + dir = xstrfmt("%.*s.git", (int)len, start); + else + dir = xstrndup(start, len); /* * Replace sequences of 'control' characters and whitespace * with one ascii space, remove leading and trailing spaces. diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index f7e51a7..7919206 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -2,1077 +2,8 @@ #include "cache.h" #include "refs.h" #include "object.h" -#include "tag.h" -#include "commit.h" -#include "tree.h" -#include "blob.h" -#include "quote.h" #include "parse-options.h" -#include "remote.h" -#include "color.h" - -/* Quoting styles */ -#define QUOTE_NONE 0 -#define QUOTE_SHELL 1 -#define QUOTE_PERL 2 -#define QUOTE_PYTHON 4 -#define QUOTE_TCL 8 - -typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type; - -struct atom_value { - const char *s; - unsigned long ul; /* used for sorting when not FIELD_STR */ -}; - -struct ref_sort { - struct ref_sort *next; - int atom; /* index into used_atom array */ - unsigned reverse : 1; -}; - -struct refinfo { - char *refname; - unsigned char objectname[20]; - int flag; - const char *symref; - struct atom_value *value; -}; - -static struct { - const char *name; - cmp_type cmp_type; -} valid_atom[] = { - { "refname" }, - { "objecttype" }, - { "objectsize", FIELD_ULONG }, - { "objectname" }, - { "tree" }, - { "parent" }, - { "numparent", FIELD_ULONG }, - { "object" }, - { "type" }, - { "tag" }, - { "author" }, - { "authorname" }, - { "authoremail" }, - { "authordate", FIELD_TIME }, - { "committer" }, - { "committername" }, - { "committeremail" }, - { "committerdate", FIELD_TIME }, - { "tagger" }, - { "taggername" }, - { "taggeremail" }, - { "taggerdate", FIELD_TIME }, - { "creator" }, - { "creatordate", FIELD_TIME }, - { "subject" }, - { "body" }, - { "contents" }, - { "contents:subject" }, - { "contents:body" }, - { "contents:signature" }, - { "upstream" }, - { "push" }, - { "symref" }, - { "flag" }, - { "HEAD" }, - { "color" }, -}; - -/* - * An atom is a valid field atom listed above, possibly prefixed with - * a "*" to denote deref_tag(). - * - * We parse given format string and sort specifiers, and make a list - * of properties that we need to extract out of objects. refinfo - * structure will hold an array of values extracted that can be - * indexed with the "atom number", which is an index into this - * array. - */ -static const char **used_atom; -static cmp_type *used_atom_type; -static int used_atom_cnt, need_tagged, need_symref; -static int need_color_reset_at_eol; - -/* - * Used to parse format string and sort specifiers - */ -static int parse_atom(const char *atom, const char *ep) -{ - const char *sp; - int i, at; - - sp = atom; - if (*sp == '*' && sp < ep) - sp++; /* deref */ - if (ep <= sp) - die("malformed field name: %.*s", (int)(ep-atom), atom); - - /* Do we have the atom already used elsewhere? */ - for (i = 0; i < used_atom_cnt; i++) { - int len = strlen(used_atom[i]); - if (len == ep - atom && !memcmp(used_atom[i], atom, len)) - return i; - } - - /* Is the atom a valid one? */ - for (i = 0; i < ARRAY_SIZE(valid_atom); i++) { - int len = strlen(valid_atom[i].name); - /* - * If the atom name has a colon, strip it and everything after - * it off - it specifies the format for this entry, and - * shouldn't be used for checking against the valid_atom - * table. - */ - const char *formatp = strchr(sp, ':'); - if (!formatp || ep < formatp) - formatp = ep; - if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len)) - break; - } - - if (ARRAY_SIZE(valid_atom) <= i) - die("unknown field name: %.*s", (int)(ep-atom), atom); - - /* Add it in, including the deref prefix */ - at = used_atom_cnt; - used_atom_cnt++; - REALLOC_ARRAY(used_atom, used_atom_cnt); - REALLOC_ARRAY(used_atom_type, used_atom_cnt); - used_atom[at] = xmemdupz(atom, ep - atom); - used_atom_type[at] = valid_atom[i].cmp_type; - if (*atom == '*') - need_tagged = 1; - if (!strcmp(used_atom[at], "symref")) - need_symref = 1; - return at; -} - -/* - * In a format string, find the next occurrence of %(atom). - */ -static const char *find_next(const char *cp) -{ - while (*cp) { - if (*cp == '%') { - /* - * %( is the start of an atom; - * %% is a quoted per-cent. - */ - if (cp[1] == '(') - return cp; - else if (cp[1] == '%') - cp++; /* skip over two % */ - /* otherwise this is a singleton, literal % */ - } - cp++; - } - return NULL; -} - -/* - * Make sure the format string is well formed, and parse out - * the used atoms. - */ -static int verify_format(const char *format) -{ - const char *cp, *sp; - - need_color_reset_at_eol = 0; - for (cp = format; *cp && (sp = find_next(cp)); ) { - const char *color, *ep = strchr(sp, ')'); - int at; - - if (!ep) - return error("malformed format string %s", sp); - /* sp points at "%(" and ep points at the closing ")" */ - at = parse_atom(sp + 2, ep); - cp = ep + 1; - - if (skip_prefix(used_atom[at], "color:", &color)) - need_color_reset_at_eol = !!strcmp(color, "reset"); - } - return 0; -} - -/* - * Given an object name, read the object data and size, and return a - * "struct object". If the object data we are returning is also borrowed - * by the "struct object" representation, set *eaten as well---it is a - * signal from parse_object_buffer to us not to free the buffer. - */ -static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned long *sz, int *eaten) -{ - enum object_type type; - void *buf = read_sha1_file(sha1, &type, sz); - - if (buf) - *obj = parse_object_buffer(sha1, type, *sz, buf, eaten); - else - *obj = NULL; - return buf; -} - -static int grab_objectname(const char *name, const unsigned char *sha1, - struct atom_value *v) -{ - if (!strcmp(name, "objectname")) { - char *s = xmalloc(41); - strcpy(s, sha1_to_hex(sha1)); - v->s = s; - return 1; - } - if (!strcmp(name, "objectname:short")) { - v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); - return 1; - } - return 0; -} - -/* See grab_values */ -static void grab_common_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) -{ - int i; - - for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; - struct atom_value *v = &val[i]; - if (!!deref != (*name == '*')) - continue; - if (deref) - name++; - if (!strcmp(name, "objecttype")) - v->s = typename(obj->type); - else if (!strcmp(name, "objectsize")) { - char *s = xmalloc(40); - sprintf(s, "%lu", sz); - v->ul = sz; - v->s = s; - } - else if (deref) - grab_objectname(name, obj->sha1, v); - } -} - -/* See grab_values */ -static void grab_tag_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) -{ - int i; - struct tag *tag = (struct tag *) obj; - - for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; - struct atom_value *v = &val[i]; - if (!!deref != (*name == '*')) - continue; - if (deref) - name++; - if (!strcmp(name, "tag")) - v->s = tag->tag; - else if (!strcmp(name, "type") && tag->tagged) - v->s = typename(tag->tagged->type); - else if (!strcmp(name, "object") && tag->tagged) { - char *s = xmalloc(41); - strcpy(s, sha1_to_hex(tag->tagged->sha1)); - v->s = s; - } - } -} - -/* See grab_values */ -static void grab_commit_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) -{ - int i; - struct commit *commit = (struct commit *) obj; - - for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; - struct atom_value *v = &val[i]; - if (!!deref != (*name == '*')) - continue; - if (deref) - name++; - if (!strcmp(name, "tree")) { - char *s = xmalloc(41); - strcpy(s, sha1_to_hex(commit->tree->object.sha1)); - v->s = s; - } - if (!strcmp(name, "numparent")) { - char *s = xmalloc(40); - v->ul = commit_list_count(commit->parents); - sprintf(s, "%lu", v->ul); - v->s = s; - } - else if (!strcmp(name, "parent")) { - int num = commit_list_count(commit->parents); - int i; - struct commit_list *parents; - char *s = xmalloc(41 * num + 1); - v->s = s; - for (i = 0, parents = commit->parents; - parents; - parents = parents->next, i = i + 41) { - struct commit *parent = parents->item; - strcpy(s+i, sha1_to_hex(parent->object.sha1)); - if (parents->next) - s[i+40] = ' '; - } - if (!i) - *s = '\0'; - } - } -} - -static const char *find_wholine(const char *who, int wholen, const char *buf, unsigned long sz) -{ - const char *eol; - while (*buf) { - if (!strncmp(buf, who, wholen) && - buf[wholen] == ' ') - return buf + wholen + 1; - eol = strchr(buf, '\n'); - if (!eol) - return ""; - eol++; - if (*eol == '\n') - return ""; /* end of header */ - buf = eol; - } - return ""; -} - -static const char *copy_line(const char *buf) -{ - const char *eol = strchrnul(buf, '\n'); - return xmemdupz(buf, eol - buf); -} - -static const char *copy_name(const char *buf) -{ - const char *cp; - for (cp = buf; *cp && *cp != '\n'; cp++) { - if (!strncmp(cp, " <", 2)) - return xmemdupz(buf, cp - buf); - } - return ""; -} - -static const char *copy_email(const char *buf) -{ - const char *email = strchr(buf, '<'); - const char *eoemail; - if (!email) - return ""; - eoemail = strchr(email, '>'); - if (!eoemail) - return ""; - return xmemdupz(email, eoemail + 1 - email); -} - -static char *copy_subject(const char *buf, unsigned long len) -{ - char *r = xmemdupz(buf, len); - int i; - - for (i = 0; i < len; i++) - if (r[i] == '\n') - r[i] = ' '; - - return r; -} - -static void grab_date(const char *buf, struct atom_value *v, const char *atomname) -{ - const char *eoemail = strstr(buf, "> "); - char *zone; - unsigned long timestamp; - long tz; - enum date_mode date_mode = DATE_NORMAL; - const char *formatp; - - /* - * We got here because atomname ends in "date" or "date<something>"; - * it's not possible that <something> is not ":<format>" because - * parse_atom() wouldn't have allowed it, so we can assume that no - * ":" means no format is specified, and use the default. - */ - formatp = strchr(atomname, ':'); - if (formatp != NULL) { - formatp++; - date_mode = parse_date_format(formatp); - } - - if (!eoemail) - goto bad; - timestamp = strtoul(eoemail + 2, &zone, 10); - if (timestamp == ULONG_MAX) - goto bad; - tz = strtol(zone, NULL, 10); - if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE) - goto bad; - v->s = xstrdup(show_date(timestamp, tz, date_mode)); - v->ul = timestamp; - return; - bad: - v->s = ""; - v->ul = 0; -} - -/* See grab_values */ -static void grab_person(const char *who, struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) -{ - int i; - int wholen = strlen(who); - const char *wholine = NULL; - - for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; - struct atom_value *v = &val[i]; - if (!!deref != (*name == '*')) - continue; - if (deref) - name++; - if (strncmp(who, name, wholen)) - continue; - if (name[wholen] != 0 && - strcmp(name + wholen, "name") && - strcmp(name + wholen, "email") && - !starts_with(name + wholen, "date")) - continue; - if (!wholine) - wholine = find_wholine(who, wholen, buf, sz); - if (!wholine) - return; /* no point looking for it */ - if (name[wholen] == 0) - v->s = copy_line(wholine); - else if (!strcmp(name + wholen, "name")) - v->s = copy_name(wholine); - else if (!strcmp(name + wholen, "email")) - v->s = copy_email(wholine); - else if (starts_with(name + wholen, "date")) - grab_date(wholine, v, name); - } - - /* - * For a tag or a commit object, if "creator" or "creatordate" is - * requested, do something special. - */ - if (strcmp(who, "tagger") && strcmp(who, "committer")) - return; /* "author" for commit object is not wanted */ - if (!wholine) - wholine = find_wholine(who, wholen, buf, sz); - if (!wholine) - return; - for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; - struct atom_value *v = &val[i]; - if (!!deref != (*name == '*')) - continue; - if (deref) - name++; - - if (starts_with(name, "creatordate")) - grab_date(wholine, v, name); - else if (!strcmp(name, "creator")) - v->s = copy_line(wholine); - } -} - -static void find_subpos(const char *buf, unsigned long sz, - const char **sub, unsigned long *sublen, - const char **body, unsigned long *bodylen, - unsigned long *nonsiglen, - const char **sig, unsigned long *siglen) -{ - const char *eol; - /* skip past header until we hit empty line */ - while (*buf && *buf != '\n') { - eol = strchrnul(buf, '\n'); - if (*eol) - eol++; - buf = eol; - } - /* skip any empty lines */ - while (*buf == '\n') - buf++; - - /* parse signature first; we might not even have a subject line */ - *sig = buf + parse_signature(buf, strlen(buf)); - *siglen = strlen(*sig); - - /* subject is first non-empty line */ - *sub = buf; - /* subject goes to first empty line */ - while (buf < *sig && *buf && *buf != '\n') { - eol = strchrnul(buf, '\n'); - if (*eol) - eol++; - buf = eol; - } - *sublen = buf - *sub; - /* drop trailing newline, if present */ - if (*sublen && (*sub)[*sublen - 1] == '\n') - *sublen -= 1; - - /* skip any empty lines */ - while (*buf == '\n') - buf++; - *body = buf; - *bodylen = strlen(buf); - *nonsiglen = *sig - buf; -} - -/* See grab_values */ -static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) -{ - int i; - const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL; - unsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0; - - for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; - struct atom_value *v = &val[i]; - if (!!deref != (*name == '*')) - continue; - if (deref) - name++; - if (strcmp(name, "subject") && - strcmp(name, "body") && - strcmp(name, "contents") && - strcmp(name, "contents:subject") && - strcmp(name, "contents:body") && - strcmp(name, "contents:signature")) - continue; - if (!subpos) - find_subpos(buf, sz, - &subpos, &sublen, - &bodypos, &bodylen, &nonsiglen, - &sigpos, &siglen); - - if (!strcmp(name, "subject")) - v->s = copy_subject(subpos, sublen); - else if (!strcmp(name, "contents:subject")) - v->s = copy_subject(subpos, sublen); - else if (!strcmp(name, "body")) - v->s = xmemdupz(bodypos, bodylen); - else if (!strcmp(name, "contents:body")) - v->s = xmemdupz(bodypos, nonsiglen); - else if (!strcmp(name, "contents:signature")) - v->s = xmemdupz(sigpos, siglen); - else if (!strcmp(name, "contents")) - v->s = xstrdup(subpos); - } -} - -/* - * We want to have empty print-string for field requests - * that do not apply (e.g. "authordate" for a tag object) - */ -static void fill_missing_values(struct atom_value *val) -{ - int i; - for (i = 0; i < used_atom_cnt; i++) { - struct atom_value *v = &val[i]; - if (v->s == NULL) - v->s = ""; - } -} - -/* - * val is a list of atom_value to hold returned values. Extract - * the values for atoms in used_atom array out of (obj, buf, sz). - * when deref is false, (obj, buf, sz) is the object that is - * pointed at by the ref itself; otherwise it is the object the - * ref (which is a tag) refers to. - */ -static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) -{ - grab_common_values(val, deref, obj, buf, sz); - switch (obj->type) { - case OBJ_TAG: - grab_tag_values(val, deref, obj, buf, sz); - grab_sub_body_contents(val, deref, obj, buf, sz); - grab_person("tagger", val, deref, obj, buf, sz); - break; - case OBJ_COMMIT: - grab_commit_values(val, deref, obj, buf, sz); - grab_sub_body_contents(val, deref, obj, buf, sz); - grab_person("author", val, deref, obj, buf, sz); - grab_person("committer", val, deref, obj, buf, sz); - break; - case OBJ_TREE: - /* grab_tree_values(val, deref, obj, buf, sz); */ - break; - case OBJ_BLOB: - /* grab_blob_values(val, deref, obj, buf, sz); */ - break; - default: - die("Eh? Object of type %d?", obj->type); - } -} - -static inline char *copy_advance(char *dst, const char *src) -{ - while (*src) - *dst++ = *src++; - return dst; -} - -/* - * Parse the object referred by ref, and grab needed value. - */ -static void populate_value(struct refinfo *ref) -{ - void *buf; - struct object *obj; - int eaten, i; - unsigned long size; - const unsigned char *tagged; - - ref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value)); - - if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) { - unsigned char unused1[20]; - ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING, - unused1, NULL); - if (!ref->symref) - ref->symref = ""; - } - - /* Fill in specials first */ - for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i]; - struct atom_value *v = &ref->value[i]; - int deref = 0; - const char *refname; - const char *formatp; - struct branch *branch = NULL; - - if (*name == '*') { - deref = 1; - name++; - } - - if (starts_with(name, "refname")) - refname = ref->refname; - else if (starts_with(name, "symref")) - refname = ref->symref ? ref->symref : ""; - else if (starts_with(name, "upstream")) { - const char *branch_name; - /* only local branches may have an upstream */ - if (!skip_prefix(ref->refname, "refs/heads/", - &branch_name)) - continue; - branch = branch_get(branch_name); - - refname = branch_get_upstream(branch, NULL); - if (!refname) - continue; - } else if (starts_with(name, "push")) { - const char *branch_name; - if (!skip_prefix(ref->refname, "refs/heads/", - &branch_name)) - continue; - branch = branch_get(branch_name); - - refname = branch_get_push(branch, NULL); - if (!refname) - continue; - } else if (starts_with(name, "color:")) { - char color[COLOR_MAXLEN] = ""; - - if (color_parse(name + 6, color) < 0) - die(_("unable to parse format")); - 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"); - if (ref->flag & REF_ISPACKED) - cp = copy_advance(cp, ",packed"); - if (cp == buf) - v->s = ""; - else { - *cp = '\0'; - v->s = xstrdup(buf + 1); - } - continue; - } else if (!deref && grab_objectname(name, ref->objectname, v)) { - continue; - } else if (!strcmp(name, "HEAD")) { - const char *head; - unsigned char sha1[20]; - - head = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, - sha1, NULL); - if (!strcmp(ref->refname, head)) - v->s = "*"; - else - v->s = " "; - continue; - } else - continue; - - formatp = strchr(name, ':'); - if (formatp) { - int num_ours, num_theirs; - - formatp++; - if (!strcmp(formatp, "short")) - refname = shorten_unambiguous_ref(refname, - warn_ambiguous_refs); - else if (!strcmp(formatp, "track") && - (starts_with(name, "upstream") || - starts_with(name, "push"))) { - char buf[40]; - - if (stat_tracking_info(branch, &num_ours, - &num_theirs, NULL)) - continue; - - 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") || - starts_with(name, "push"))) { - assert(branch); - - if (stat_tracking_info(branch, &num_ours, - &num_theirs, NULL)) - continue; - - 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); - } - - if (!deref) - v->s = refname; - else { - int len = strlen(refname); - char *s = xmalloc(len + 4); - sprintf(s, "%s^{}", refname); - v->s = s; - } - } - - for (i = 0; i < used_atom_cnt; i++) { - struct atom_value *v = &ref->value[i]; - if (v->s == NULL) - goto need_obj; - } - return; - - need_obj: - buf = get_obj(ref->objectname, &obj, &size, &eaten); - if (!buf) - die("missing object %s for %s", - sha1_to_hex(ref->objectname), ref->refname); - if (!obj) - die("parse_object_buffer failed on %s for %s", - sha1_to_hex(ref->objectname), ref->refname); - - grab_values(ref->value, 0, obj, buf, size); - if (!eaten) - free(buf); - - /* - * If there is no atom that wants to know about tagged - * object, we are done. - */ - if (!need_tagged || (obj->type != OBJ_TAG)) - return; - - /* - * If it is a tag object, see if we use a value that derefs - * the object, and if we do grab the object it refers to. - */ - tagged = ((struct tag *)obj)->tagged->sha1; - - /* - * NEEDSWORK: This derefs tag only once, which - * is good to deal with chains of trust, but - * is not consistent with what deref_tag() does - * which peels the onion to the core. - */ - buf = get_obj(tagged, &obj, &size, &eaten); - if (!buf) - die("missing object %s for %s", - sha1_to_hex(tagged), ref->refname); - if (!obj) - die("parse_object_buffer failed on %s for %s", - sha1_to_hex(tagged), ref->refname); - grab_values(ref->value, 1, obj, buf, size); - if (!eaten) - free(buf); -} - -/* - * Given a ref, return the value for the atom. This lazily gets value - * out of the object by calling populate value. - */ -static void get_value(struct refinfo *ref, int atom, struct atom_value **v) -{ - if (!ref->value) { - populate_value(ref); - fill_missing_values(ref->value); - } - *v = &ref->value[atom]; -} - -struct grab_ref_cbdata { - struct refinfo **grab_array; - const char **grab_pattern; - int grab_cnt; -}; - -/* - * A call-back given to for_each_ref(). Filter refs and keep them for - * later object processing. - */ -static int grab_single_ref(const char *refname, const struct object_id *oid, - int flag, void *cb_data) -{ - struct grab_ref_cbdata *cb = cb_data; - struct refinfo *ref; - int cnt; - - if (flag & REF_BAD_NAME) { - warning("ignoring ref with broken name %s", refname); - return 0; - } - - if (*cb->grab_pattern) { - const char **pattern; - int namelen = strlen(refname); - for (pattern = cb->grab_pattern; *pattern; pattern++) { - const char *p = *pattern; - int plen = strlen(p); - - if ((plen <= namelen) && - !strncmp(refname, p, plen) && - (refname[plen] == '\0' || - refname[plen] == '/' || - p[plen-1] == '/')) - break; - if (!wildmatch(p, refname, WM_PATHNAME, NULL)) - break; - } - if (!*pattern) - return 0; - } - - /* - * We do not open the object yet; sort may only need refname - * to do its job and the resulting list may yet to be pruned - * by maxcount logic. - */ - ref = xcalloc(1, sizeof(*ref)); - ref->refname = xstrdup(refname); - hashcpy(ref->objectname, oid->hash); - ref->flag = flag; - - cnt = cb->grab_cnt; - REALLOC_ARRAY(cb->grab_array, cnt + 1); - cb->grab_array[cnt++] = ref; - cb->grab_cnt = cnt; - return 0; -} - -static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b) -{ - struct atom_value *va, *vb; - int cmp; - cmp_type cmp_type = used_atom_type[s->atom]; - - get_value(a, s->atom, &va); - get_value(b, s->atom, &vb); - switch (cmp_type) { - case FIELD_STR: - cmp = strcmp(va->s, vb->s); - break; - default: - if (va->ul < vb->ul) - cmp = -1; - else if (va->ul == vb->ul) - cmp = 0; - else - cmp = 1; - break; - } - return (s->reverse) ? -cmp : cmp; -} - -static struct ref_sort *ref_sort; -static int compare_refs(const void *a_, const void *b_) -{ - struct refinfo *a = *((struct refinfo **)a_); - struct refinfo *b = *((struct refinfo **)b_); - struct ref_sort *s; - - for (s = ref_sort; s; s = s->next) { - int cmp = cmp_ref_sort(s, a, b); - if (cmp) - return cmp; - } - return 0; -} - -static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs) -{ - ref_sort = sort; - qsort(refs, num_refs, sizeof(struct refinfo *), compare_refs); -} - -static void print_value(struct atom_value *v, int quote_style) -{ - struct strbuf sb = STRBUF_INIT; - switch (quote_style) { - case QUOTE_NONE: - fputs(v->s, stdout); - break; - case QUOTE_SHELL: - sq_quote_buf(&sb, v->s); - break; - case QUOTE_PERL: - perl_quote_buf(&sb, v->s); - break; - case QUOTE_PYTHON: - python_quote_buf(&sb, v->s); - break; - case QUOTE_TCL: - tcl_quote_buf(&sb, v->s); - break; - } - if (quote_style != QUOTE_NONE) { - fputs(sb.buf, stdout); - strbuf_release(&sb); - } -} - -static int hex1(char ch) -{ - if ('0' <= ch && ch <= '9') - return ch - '0'; - else if ('a' <= ch && ch <= 'f') - return ch - 'a' + 10; - else if ('A' <= ch && ch <= 'F') - return ch - 'A' + 10; - return -1; -} -static int hex2(const char *cp) -{ - if (cp[0] && cp[1]) - return (hex1(cp[0]) << 4) | hex1(cp[1]); - else - return -1; -} - -static void emit(const char *cp, const char *ep) -{ - while (*cp && (!ep || cp < ep)) { - if (*cp == '%') { - if (cp[1] == '%') - cp++; - else { - int ch = hex2(cp + 1); - if (0 <= ch) { - putchar(ch); - cp += 3; - continue; - } - } - } - putchar(*cp); - cp++; - } -} - -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); - 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] = ""; - - if (color_parse("reset", color) < 0) - die("BUG: couldn't parse 'reset' as a color"); - resetv.s = color; - print_value(&resetv, quote_style); - } - putchar('\n'); -} - -static struct ref_sort *default_sort(void) -{ - static const char cstr_name[] = "refname"; - - struct ref_sort *sort = xcalloc(1, sizeof(*sort)); - - sort->next = NULL; - sort->atom = parse_atom(cstr_name, cstr_name + strlen(cstr_name)); - return sort; -} - -static int opt_parse_sort(const struct option *opt, const char *arg, int unset) -{ - struct ref_sort **sort_tail = opt->value; - struct ref_sort *s; - int len; - - if (!arg) /* should --no-sort void the list ? */ - return -1; - - s = xcalloc(1, sizeof(*s)); - s->next = *sort_tail; - *sort_tail = s; - - if (*arg == '-') { - s->reverse = 1; - arg++; - } - len = strlen(arg); - s->atom = parse_atom(arg, arg+len); - return 0; -} +#include "ref-filter.h" static char const * const for_each_ref_usage[] = { N_("git for-each-ref [<options>] [<pattern>]"), @@ -1081,12 +12,12 @@ static char const * const for_each_ref_usage[] = { int cmd_for_each_ref(int argc, const char **argv, const char *prefix) { - int i, num_refs; + int i; const char *format = "%(objectname) %(objecttype)\t%(refname)"; - struct ref_sort *sort = NULL, **sort_tail = &sort; + struct ref_sorting *sorting = NULL, **sorting_tail = &sorting; int maxcount = 0, quote_style = 0; - struct refinfo **refs; - struct grab_ref_cbdata cbdata; + struct ref_array array; + struct ref_filter filter; struct option opts[] = { OPT_BIT('s', "shell", "e_style, @@ -1101,8 +32,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) OPT_GROUP(""), OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")), OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")), - OPT_CALLBACK(0 , "sort", sort_tail, N_("key"), - N_("field name to sort on"), &opt_parse_sort), + OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"), + N_("field name to sort on"), &parse_opt_ref_sorting), OPT_END(), }; @@ -1115,26 +46,25 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) error("more than one quoting style?"); usage_with_options(for_each_ref_usage, opts); } - if (verify_format(format)) + if (verify_ref_format(format)) usage_with_options(for_each_ref_usage, opts); - if (!sort) - sort = default_sort(); + if (!sorting) + sorting = ref_default_sorting(); /* for warn_ambiguous_refs */ git_config(git_default_config, NULL); - memset(&cbdata, 0, sizeof(cbdata)); - cbdata.grab_pattern = argv; - for_each_rawref(grab_single_ref, &cbdata); - refs = cbdata.grab_array; - num_refs = cbdata.grab_cnt; - - sort_refs(sort, refs, num_refs); + memset(&array, 0, sizeof(array)); + memset(&filter, 0, sizeof(filter)); + filter.name_patterns = argv; + filter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN); + ref_array_sort(sorting, &array); - if (!maxcount || num_refs < maxcount) - maxcount = num_refs; + if (!maxcount || array.nr < maxcount) + maxcount = array.nr; for (i = 0; i < maxcount; i++) - show_ref(refs[i], format, quote_style); + show_ref_array_item(array.items[i], format, quote_style); + ref_array_clear(&array); return 0; } diff --git a/builtin/fsck.c b/builtin/fsck.c index 4e8e2ee..2679793 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -451,35 +451,41 @@ static void fsck_dir(int i, char *path) static int default_refs; +static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1) +{ + struct object *obj; + + if (!is_null_sha1(sha1)) { + obj = lookup_object(sha1); + if (obj) { + obj->used = 1; + mark_object_reachable(obj); + } else { + error("%s: invalid reflog entry %s", refname, sha1_to_hex(sha1)); + errors_found |= ERROR_REACHABLE; + } + } +} + static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1, const char *email, unsigned long timestamp, int tz, const char *message, void *cb_data) { - struct object *obj; + const char *refname = cb_data; if (verbose) fprintf(stderr, "Checking reflog %s->%s\n", sha1_to_hex(osha1), sha1_to_hex(nsha1)); - if (!is_null_sha1(osha1)) { - obj = lookup_object(osha1); - if (obj) { - obj->used = 1; - mark_object_reachable(obj); - } - } - obj = lookup_object(nsha1); - if (obj) { - obj->used = 1; - mark_object_reachable(obj); - } + fsck_handle_reflog_sha1(refname, osha1); + fsck_handle_reflog_sha1(refname, nsha1); return 0; } static int fsck_handle_reflog(const char *logname, const struct object_id *oid, int flag, void *cb_data) { - for_each_reflog_ent(logname, fsck_handle_reflog_ent, NULL); + for_each_reflog_ent(logname, fsck_handle_reflog_ent, (void *)logname); return 0; } diff --git a/builtin/gc.c b/builtin/gc.c index 36fe333..4957c39 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -293,7 +293,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL); argv_array_pushl(&repack, "repack", "-d", "-l", NULL); argv_array_pushl(&prune, "prune", "--expire", NULL); - argv_array_pushl(&prune_worktrees, "prune", "--worktrees", "--expire", NULL); + argv_array_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL); argv_array_pushl(&rerere, "rerere", "gc", NULL); gc_config(); diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 7ea2020..f07bc66 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -616,7 +616,9 @@ static int compare_ofs_delta_bases(off_t offset1, off_t offset2, int cmp = type1 - type2; if (cmp) return cmp; - return offset1 - offset2; + return offset1 < offset2 ? -1 : + offset1 > offset2 ? 1 : + 0; } static int find_ofs_delta(const off_t offset, enum object_type type) @@ -784,7 +786,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, assert(data || obj_entry); read_lock(); - collision_test_needed = has_sha1_file(sha1); + collision_test_needed = has_sha1_file_with_flags(sha1, HAS_SHA1_QUICK); read_unlock(); if (collision_test_needed && !data) { @@ -1051,7 +1053,9 @@ static int compare_ofs_delta_entry(const void *a, const void *b) const struct ofs_delta_entry *delta_a = a; const struct ofs_delta_entry *delta_b = b; - return delta_a->offset - delta_b->offset; + return delta_a->offset < delta_b->offset ? -1 : + delta_a->offset > delta_b->offset ? 1 : + 0; } static int compare_ref_delta_entry(const void *a, const void *b) @@ -1223,7 +1227,7 @@ static void resolve_deltas(void) * - append objects to convert thin pack to full pack if required * - write the final 20-byte SHA-1 */ -static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved); +static void fix_unresolved_deltas(struct sha1file *f); static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned char *pack_sha1) { if (nr_ref_deltas + nr_ofs_deltas == nr_resolved_deltas) { @@ -1245,7 +1249,7 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha memset(objects + nr_objects + 1, 0, nr_unresolved * sizeof(*objects)); f = sha1fd(output_fd, curr_pack); - fix_unresolved_deltas(f, nr_unresolved); + fix_unresolved_deltas(f); strbuf_addf(&msg, _("completed with %d local objects"), nr_objects - nr_objects_initial); stop_progress_msg(&progress, msg.buf); @@ -1327,10 +1331,10 @@ static int delta_pos_compare(const void *_a, const void *_b) return a->obj_no - b->obj_no; } -static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved) +static void fix_unresolved_deltas(struct sha1file *f) { struct ref_delta_entry **sorted_by_pos; - int i, n = 0; + int i; /* * Since many unresolved deltas may well be themselves base objects @@ -1342,12 +1346,12 @@ static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved) * before deltas depending on them, a good heuristic is to start * resolving deltas in the same order as their position in the pack. */ - sorted_by_pos = xmalloc(nr_unresolved * sizeof(*sorted_by_pos)); + sorted_by_pos = xmalloc(nr_ref_deltas * sizeof(*sorted_by_pos)); for (i = 0; i < nr_ref_deltas; i++) - sorted_by_pos[n++] = &ref_deltas[i]; - qsort(sorted_by_pos, n, sizeof(*sorted_by_pos), delta_pos_compare); + sorted_by_pos[i] = &ref_deltas[i]; + qsort(sorted_by_pos, nr_ref_deltas, sizeof(*sorted_by_pos), delta_pos_compare); - for (i = 0; i < n; i++) { + for (i = 0; i < nr_ref_deltas; i++) { struct ref_delta_entry *d = sorted_by_pos[i]; enum object_type type; struct base_data *base_obj = alloc_base_data(); diff --git a/builtin/log.c b/builtin/log.c index 3caa917..93025d0 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -796,7 +796,7 @@ static int reopen_stdout(struct commit *commit, const char *subject, static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids) { struct rev_info check_rev; - struct commit *commit; + struct commit *commit, *c1, *c2; struct object *o1, *o2; unsigned flags1, flags2; @@ -804,9 +804,11 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids) die(_("Need exactly one range.")); o1 = rev->pending.objects[0].item; - flags1 = o1->flags; o2 = rev->pending.objects[1].item; + flags1 = o1->flags; flags2 = o2->flags; + c1 = lookup_commit_reference(o1->sha1); + c2 = lookup_commit_reference(o2->sha1); if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING)) die(_("Not a range.")); @@ -828,10 +830,8 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids) } /* reset for next revision walk */ - clear_commit_marks((struct commit *)o1, - SEEN | UNINTERESTING | SHOWN | ADDED); - clear_commit_marks((struct commit *)o2, - SEEN | UNINTERESTING | SHOWN | ADDED); + clear_commit_marks(c1, SEEN | UNINTERESTING | SHOWN | ADDED); + clear_commit_marks(c2, SEEN | UNINTERESTING | SHOWN | ADDED); o1->flags = flags1; o2->flags = flags2; } diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 80fe8c7..62cc16d 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -2588,23 +2588,6 @@ static int option_parse_unpack_unreachable(const struct option *opt, return 0; } -static int option_parse_ulong(const struct option *opt, - const char *arg, int unset) -{ - if (unset) - die(_("option %s does not accept negative form"), - opt->long_name); - - if (!git_parse_ulong(arg, opt->value)) - die(_("unable to parse value '%s' for option %s"), - arg, opt->long_name); - return 0; -} - -#define OPT_ULONG(s, l, v, h) \ - { OPTION_CALLBACK, (s), (l), (v), "n", (h), \ - PARSE_OPT_NONEG, option_parse_ulong } - int cmd_pack_objects(int argc, const char **argv, const char *prefix) { int use_internal_rev_list = 0; @@ -2627,16 +2610,16 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) { OPTION_CALLBACK, 0, "index-version", NULL, N_("version[,offset]"), N_("write the pack index file in the specified idx format version"), 0, option_parse_index_version }, - OPT_ULONG(0, "max-pack-size", &pack_size_limit, - N_("maximum size of each output pack file")), + OPT_MAGNITUDE(0, "max-pack-size", &pack_size_limit, + N_("maximum size of each output pack file")), OPT_BOOL(0, "local", &local, N_("ignore borrowed objects from alternate object store")), OPT_BOOL(0, "incremental", &incremental, N_("ignore packed objects")), OPT_INTEGER(0, "window", &window, N_("limit pack window by objects")), - OPT_ULONG(0, "window-memory", &window_memory_limit, - N_("limit pack window by memory in addition to object limit")), + OPT_MAGNITUDE(0, "window-memory", &window_memory_limit, + N_("limit pack window by memory in addition to object limit")), OPT_INTEGER(0, "depth", &depth, N_("maximum length of delta chain allowed in the resulting pack")), OPT_BOOL(0, "reuse-delta", &reuse_delta, diff --git a/builtin/prune.c b/builtin/prune.c index 0c73246..10b03d3 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -6,7 +6,6 @@ #include "reachable.h" #include "parse-options.h" #include "progress.h" -#include "dir.h" static const char * const prune_usage[] = { N_("git prune [-n] [-v] [--expire <time>] [--] [<head>...]"), @@ -76,95 +75,6 @@ static int prune_subdir(int nr, const char *path, void *data) return 0; } -static int prune_worktree(const char *id, struct strbuf *reason) -{ - struct stat st; - char *path; - int fd, len; - - if (!is_directory(git_path("worktrees/%s", id))) { - strbuf_addf(reason, _("Removing worktrees/%s: not a valid directory"), id); - return 1; - } - if (file_exists(git_path("worktrees/%s/locked", id))) - return 0; - if (stat(git_path("worktrees/%s/gitdir", id), &st)) { - strbuf_addf(reason, _("Removing worktrees/%s: gitdir file does not exist"), id); - return 1; - } - fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY); - if (fd < 0) { - strbuf_addf(reason, _("Removing worktrees/%s: unable to read gitdir file (%s)"), - id, strerror(errno)); - return 1; - } - len = st.st_size; - path = xmalloc(len + 1); - read_in_full(fd, path, len); - close(fd); - while (len && (path[len - 1] == '\n' || path[len - 1] == '\r')) - len--; - if (!len) { - strbuf_addf(reason, _("Removing worktrees/%s: invalid gitdir file"), id); - free(path); - return 1; - } - path[len] = '\0'; - if (!file_exists(path)) { - struct stat st_link; - free(path); - /* - * the repo is moved manually and has not been - * accessed since? - */ - if (!stat(git_path("worktrees/%s/link", id), &st_link) && - st_link.st_nlink > 1) - return 0; - if (st.st_mtime <= expire) { - strbuf_addf(reason, _("Removing worktrees/%s: gitdir file points to non-existent location"), id); - return 1; - } else { - return 0; - } - } - free(path); - return 0; -} - -static void prune_worktrees(void) -{ - struct strbuf reason = STRBUF_INIT; - struct strbuf path = STRBUF_INIT; - DIR *dir = opendir(git_path("worktrees")); - struct dirent *d; - int ret; - if (!dir) - return; - while ((d = readdir(dir)) != NULL) { - if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) - continue; - strbuf_reset(&reason); - if (!prune_worktree(d->d_name, &reason)) - continue; - if (show_only || verbose) - printf("%s\n", reason.buf); - if (show_only) - continue; - strbuf_reset(&path); - strbuf_addstr(&path, git_path("worktrees/%s", d->d_name)); - ret = remove_dir_recursively(&path, 0); - if (ret < 0 && errno == ENOTDIR) - ret = unlink(path.buf); - if (ret) - error(_("failed to remove: %s"), strerror(errno)); - } - closedir(dir); - if (!show_only) - rmdir(git_path("worktrees")); - strbuf_release(&reason); - strbuf_release(&path); -} - /* * Write errors (particularly out of space) can result in * failed temporary packs (and more rarely indexes and other @@ -191,12 +101,10 @@ int cmd_prune(int argc, const char **argv, const char *prefix) { struct rev_info revs; struct progress *progress = NULL; - int do_prune_worktrees = 0; const struct option options[] = { OPT__DRY_RUN(&show_only, N_("do not remove, show only")), OPT__VERBOSE(&verbose, N_("report pruned objects")), OPT_BOOL(0, "progress", &show_progress, N_("show progress")), - OPT_BOOL(0, "worktrees", &do_prune_worktrees, N_("prune .git/worktrees")), OPT_EXPIRY_DATE(0, "expire", &expire, N_("expire objects older than <time>")), OPT_END() @@ -211,13 +119,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, prune_usage, 0); - if (do_prune_worktrees) { - if (argc) - die(_("--worktrees does not take extra arguments")); - prune_worktrees(); - return 0; - } - while (argc--) { unsigned char sha1[20]; const char *name = *argv++; diff --git a/builtin/pull.c b/builtin/pull.c new file mode 100644 index 0000000..722a83c --- /dev/null +++ b/builtin/pull.c @@ -0,0 +1,882 @@ +/* + * Builtin "git pull" + * + * Based on git-pull.sh by Junio C Hamano + * + * Fetch one or more remote refs and merge it/them into the current HEAD. + */ +#include "cache.h" +#include "builtin.h" +#include "parse-options.h" +#include "exec_cmd.h" +#include "run-command.h" +#include "sha1-array.h" +#include "remote.h" +#include "dir.h" +#include "refs.h" +#include "revision.h" +#include "lockfile.h" + +enum rebase_type { + REBASE_INVALID = -1, + REBASE_FALSE = 0, + REBASE_TRUE, + REBASE_PRESERVE +}; + +/** + * Parses the value of --rebase. If value is a false value, returns + * REBASE_FALSE. If value is a true value, returns REBASE_TRUE. If value is + * "preserve", returns REBASE_PRESERVE. If value is a invalid value, dies with + * a fatal error if fatal is true, otherwise returns REBASE_INVALID. + */ +static enum rebase_type parse_config_rebase(const char *key, const char *value, + int fatal) +{ + int v = git_config_maybe_bool("pull.rebase", value); + + if (!v) + return REBASE_FALSE; + else if (v > 0) + return REBASE_TRUE; + else if (!strcmp(value, "preserve")) + return REBASE_PRESERVE; + + if (fatal) + die(_("Invalid value for %s: %s"), key, value); + else + error(_("Invalid value for %s: %s"), key, value); + + return REBASE_INVALID; +} + +/** + * Callback for --rebase, which parses arg with parse_config_rebase(). + */ +static int parse_opt_rebase(const struct option *opt, const char *arg, int unset) +{ + enum rebase_type *value = opt->value; + + if (arg) + *value = parse_config_rebase("--rebase", arg, 0); + else + *value = unset ? REBASE_FALSE : REBASE_TRUE; + return *value == REBASE_INVALID ? -1 : 0; +} + +static const char * const pull_usage[] = { + N_("git pull [options] [<repository> [<refspec>...]]"), + NULL +}; + +/* Shared options */ +static int opt_verbosity; +static char *opt_progress; + +/* Options passed to git-merge or git-rebase */ +static enum rebase_type opt_rebase = -1; +static char *opt_diffstat; +static char *opt_log; +static char *opt_squash; +static char *opt_commit; +static char *opt_edit; +static char *opt_ff; +static char *opt_verify_signatures; +static struct argv_array opt_strategies = ARGV_ARRAY_INIT; +static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT; +static char *opt_gpg_sign; + +/* Options passed to git-fetch */ +static char *opt_all; +static char *opt_append; +static char *opt_upload_pack; +static int opt_force; +static char *opt_tags; +static char *opt_prune; +static char *opt_recurse_submodules; +static int opt_dry_run; +static char *opt_keep; +static char *opt_depth; +static char *opt_unshallow; +static char *opt_update_shallow; +static char *opt_refmap; + +static struct option pull_options[] = { + /* Shared options */ + OPT__VERBOSITY(&opt_verbosity), + OPT_PASSTHRU(0, "progress", &opt_progress, NULL, + N_("force progress reporting"), + PARSE_OPT_NOARG), + + /* Options passed to git-merge or git-rebase */ + OPT_GROUP(N_("Options related to merging")), + { OPTION_CALLBACK, 'r', "rebase", &opt_rebase, + N_("false|true|preserve"), + N_("incorporate changes by rebasing rather than merging"), + PARSE_OPT_OPTARG, parse_opt_rebase }, + OPT_PASSTHRU('n', NULL, &opt_diffstat, NULL, + N_("do not show a diffstat at the end of the merge"), + PARSE_OPT_NOARG | PARSE_OPT_NONEG), + OPT_PASSTHRU(0, "stat", &opt_diffstat, NULL, + N_("show a diffstat at the end of the merge"), + PARSE_OPT_NOARG), + OPT_PASSTHRU(0, "summary", &opt_diffstat, NULL, + N_("(synonym to --stat)"), + PARSE_OPT_NOARG | PARSE_OPT_HIDDEN), + OPT_PASSTHRU(0, "log", &opt_log, N_("n"), + N_("add (at most <n>) entries from shortlog to merge commit message"), + PARSE_OPT_OPTARG), + OPT_PASSTHRU(0, "squash", &opt_squash, NULL, + N_("create a single commit instead of doing a merge"), + PARSE_OPT_NOARG), + OPT_PASSTHRU(0, "commit", &opt_commit, NULL, + N_("perform a commit if the merge succeeds (default)"), + PARSE_OPT_NOARG), + OPT_PASSTHRU(0, "edit", &opt_edit, NULL, + N_("edit message before committing"), + PARSE_OPT_NOARG), + OPT_PASSTHRU(0, "ff", &opt_ff, NULL, + N_("allow fast-forward"), + PARSE_OPT_NOARG), + OPT_PASSTHRU(0, "ff-only", &opt_ff, NULL, + N_("abort if fast-forward is not possible"), + PARSE_OPT_NOARG | PARSE_OPT_NONEG), + OPT_PASSTHRU(0, "verify-signatures", &opt_verify_signatures, NULL, + N_("verify that the named commit has a valid GPG signature"), + PARSE_OPT_NOARG), + OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"), + N_("merge strategy to use"), + 0), + OPT_PASSTHRU_ARGV('X', "strategy-option", &opt_strategy_opts, + N_("option=value"), + N_("option for selected merge strategy"), + 0), + OPT_PASSTHRU('S', "gpg-sign", &opt_gpg_sign, N_("key-id"), + N_("GPG sign commit"), + PARSE_OPT_OPTARG), + + /* Options passed to git-fetch */ + OPT_GROUP(N_("Options related to fetching")), + OPT_PASSTHRU(0, "all", &opt_all, NULL, + N_("fetch from all remotes"), + PARSE_OPT_NOARG), + OPT_PASSTHRU('a', "append", &opt_append, NULL, + N_("append to .git/FETCH_HEAD instead of overwriting"), + PARSE_OPT_NOARG), + OPT_PASSTHRU(0, "upload-pack", &opt_upload_pack, N_("path"), + N_("path to upload pack on remote end"), + 0), + OPT__FORCE(&opt_force, N_("force overwrite of local branch")), + OPT_PASSTHRU('t', "tags", &opt_tags, NULL, + N_("fetch all tags and associated objects"), + PARSE_OPT_NOARG), + OPT_PASSTHRU('p', "prune", &opt_prune, NULL, + N_("prune remote-tracking branches no longer on remote"), + PARSE_OPT_NOARG), + OPT_PASSTHRU(0, "recurse-submodules", &opt_recurse_submodules, + N_("on-demand"), + N_("control recursive fetching of submodules"), + PARSE_OPT_OPTARG), + OPT_BOOL(0, "dry-run", &opt_dry_run, + N_("dry run")), + OPT_PASSTHRU('k', "keep", &opt_keep, NULL, + N_("keep downloaded pack"), + PARSE_OPT_NOARG), + OPT_PASSTHRU(0, "depth", &opt_depth, N_("depth"), + N_("deepen history of shallow clone"), + 0), + OPT_PASSTHRU(0, "unshallow", &opt_unshallow, NULL, + N_("convert to a complete repository"), + PARSE_OPT_NONEG | PARSE_OPT_NOARG), + OPT_PASSTHRU(0, "update-shallow", &opt_update_shallow, NULL, + N_("accept refs that update .git/shallow"), + PARSE_OPT_NOARG), + OPT_PASSTHRU(0, "refmap", &opt_refmap, N_("refmap"), + N_("specify fetch refmap"), + PARSE_OPT_NONEG), + + OPT_END() +}; + +/** + * Pushes "-q" or "-v" switches into arr to match the opt_verbosity level. + */ +static void argv_push_verbosity(struct argv_array *arr) +{ + int verbosity; + + for (verbosity = opt_verbosity; verbosity > 0; verbosity--) + argv_array_push(arr, "-v"); + + for (verbosity = opt_verbosity; verbosity < 0; verbosity++) + argv_array_push(arr, "-q"); +} + +/** + * Pushes "-f" switches into arr to match the opt_force level. + */ +static void argv_push_force(struct argv_array *arr) +{ + int force = opt_force; + while (force-- > 0) + argv_array_push(arr, "-f"); +} + +/** + * Sets the GIT_REFLOG_ACTION environment variable to the concatenation of argv + */ +static void set_reflog_message(int argc, const char **argv) +{ + int i; + struct strbuf msg = STRBUF_INIT; + + for (i = 0; i < argc; i++) { + if (i) + strbuf_addch(&msg, ' '); + strbuf_addstr(&msg, argv[i]); + } + + setenv("GIT_REFLOG_ACTION", msg.buf, 0); + + strbuf_release(&msg); +} + +/** + * If pull.ff is unset, returns NULL. If pull.ff is "true", returns "--ff". If + * pull.ff is "false", returns "--no-ff". If pull.ff is "only", returns + * "--ff-only". Otherwise, if pull.ff is set to an invalid value, die with an + * error. + */ +static const char *config_get_ff(void) +{ + const char *value; + + if (git_config_get_value("pull.ff", &value)) + return NULL; + + switch (git_config_maybe_bool("pull.ff", value)) { + case 0: + return "--no-ff"; + case 1: + return "--ff"; + } + + if (!strcmp(value, "only")) + return "--ff-only"; + + die(_("Invalid value for pull.ff: %s"), value); +} + +/** + * Returns the default configured value for --rebase. It first looks for the + * value of "branch.$curr_branch.rebase", where $curr_branch is the current + * branch, and if HEAD is detached or the configuration key does not exist, + * looks for the value of "pull.rebase". If both configuration keys do not + * exist, returns REBASE_FALSE. + */ +static enum rebase_type config_get_rebase(void) +{ + struct branch *curr_branch = branch_get("HEAD"); + const char *value; + + if (curr_branch) { + char *key = xstrfmt("branch.%s.rebase", curr_branch->name); + + if (!git_config_get_value(key, &value)) { + enum rebase_type ret = parse_config_rebase(key, value, 1); + free(key); + return ret; + } + + free(key); + } + + if (!git_config_get_value("pull.rebase", &value)) + return parse_config_rebase("pull.rebase", value, 1); + + return REBASE_FALSE; +} + +/** + * Returns 1 if there are unstaged changes, 0 otherwise. + */ +static int has_unstaged_changes(const char *prefix) +{ + struct rev_info rev_info; + int result; + + init_revisions(&rev_info, prefix); + DIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES); + DIFF_OPT_SET(&rev_info.diffopt, QUICK); + diff_setup_done(&rev_info.diffopt); + result = run_diff_files(&rev_info, 0); + return diff_result_code(&rev_info.diffopt, result); +} + +/** + * Returns 1 if there are uncommitted changes, 0 otherwise. + */ +static int has_uncommitted_changes(const char *prefix) +{ + struct rev_info rev_info; + int result; + + if (is_cache_unborn()) + return 0; + + init_revisions(&rev_info, prefix); + DIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES); + DIFF_OPT_SET(&rev_info.diffopt, QUICK); + add_head_to_pending(&rev_info); + diff_setup_done(&rev_info.diffopt); + result = run_diff_index(&rev_info, 1); + return diff_result_code(&rev_info.diffopt, result); +} + +/** + * If the work tree has unstaged or uncommitted changes, dies with the + * appropriate message. + */ +static void die_on_unclean_work_tree(const char *prefix) +{ + struct lock_file *lock_file = xcalloc(1, sizeof(*lock_file)); + int do_die = 0; + + hold_locked_index(lock_file, 0); + refresh_cache(REFRESH_QUIET); + update_index_if_able(&the_index, lock_file); + rollback_lock_file(lock_file); + + if (has_unstaged_changes(prefix)) { + error(_("Cannot pull with rebase: You have unstaged changes.")); + do_die = 1; + } + + if (has_uncommitted_changes(prefix)) { + if (do_die) + error(_("Additionally, your index contains uncommitted changes.")); + else + error(_("Cannot pull with rebase: Your index contains uncommitted changes.")); + do_die = 1; + } + + if (do_die) + exit(1); +} + +/** + * Appends merge candidates from FETCH_HEAD that are not marked not-for-merge + * into merge_heads. + */ +static void get_merge_heads(struct sha1_array *merge_heads) +{ + const char *filename = git_path("FETCH_HEAD"); + FILE *fp; + struct strbuf sb = STRBUF_INIT; + unsigned char sha1[GIT_SHA1_RAWSZ]; + + if (!(fp = fopen(filename, "r"))) + die_errno(_("could not open '%s' for reading"), filename); + while (strbuf_getline(&sb, fp, '\n') != EOF) { + if (get_sha1_hex(sb.buf, sha1)) + continue; /* invalid line: does not start with SHA1 */ + if (starts_with(sb.buf + GIT_SHA1_HEXSZ, "\tnot-for-merge\t")) + continue; /* ref is not-for-merge */ + sha1_array_append(merge_heads, sha1); + } + fclose(fp); + strbuf_release(&sb); +} + +/** + * Used by die_no_merge_candidates() as a for_each_remote() callback to + * retrieve the name of the remote if the repository only has one remote. + */ +static int get_only_remote(struct remote *remote, void *cb_data) +{ + const char **remote_name = cb_data; + + if (*remote_name) + return -1; + + *remote_name = remote->name; + return 0; +} + +/** + * Dies with the appropriate reason for why there are no merge candidates: + * + * 1. We fetched from a specific remote, and a refspec was given, but it ended + * up not fetching anything. This is usually because the user provided a + * wildcard refspec which had no matches on the remote end. + * + * 2. We fetched from a non-default remote, but didn't specify a branch to + * merge. We can't use the configured one because it applies to the default + * remote, thus the user must specify the branches to merge. + * + * 3. We fetched from the branch's or repo's default remote, but: + * + * a. We are not on a branch, so there will never be a configured branch to + * merge with. + * + * b. We are on a branch, but there is no configured branch to merge with. + * + * 4. We fetched from the branch's or repo's default remote, but the configured + * branch to merge didn't get fetched. (Either it doesn't exist, or wasn't + * part of the configured fetch refspec.) + */ +static void NORETURN die_no_merge_candidates(const char *repo, const char **refspecs) +{ + struct branch *curr_branch = branch_get("HEAD"); + const char *remote = curr_branch ? curr_branch->remote_name : NULL; + + if (*refspecs) { + if (opt_rebase) + fprintf_ln(stderr, _("There is no candidate for rebasing against among the refs that you just fetched.")); + else + fprintf_ln(stderr, _("There are no candidates for merging among the refs that you just fetched.")); + fprintf_ln(stderr, _("Generally this means that you provided a wildcard refspec which had no\n" + "matches on the remote end.")); + } else if (repo && curr_branch && (!remote || strcmp(repo, remote))) { + fprintf_ln(stderr, _("You asked to pull from the remote '%s', but did not specify\n" + "a branch. Because this is not the default configured remote\n" + "for your current branch, you must specify a branch on the command line."), + repo); + } else if (!curr_branch) { + fprintf_ln(stderr, _("You are not currently on a branch.")); + if (opt_rebase) + fprintf_ln(stderr, _("Please specify which branch you want to rebase against.")); + else + fprintf_ln(stderr, _("Please specify which branch you want to merge with.")); + fprintf_ln(stderr, _("See git-pull(1) for details.")); + fprintf(stderr, "\n"); + fprintf_ln(stderr, " git pull <remote> <branch>"); + fprintf(stderr, "\n"); + } else if (!curr_branch->merge_nr) { + const char *remote_name = NULL; + + if (for_each_remote(get_only_remote, &remote_name) || !remote_name) + remote_name = "<remote>"; + + fprintf_ln(stderr, _("There is no tracking information for the current branch.")); + if (opt_rebase) + fprintf_ln(stderr, _("Please specify which branch you want to rebase against.")); + else + fprintf_ln(stderr, _("Please specify which branch you want to merge with.")); + fprintf_ln(stderr, _("See git-pull(1) for details.")); + fprintf(stderr, "\n"); + fprintf_ln(stderr, " git pull <remote> <branch>"); + fprintf(stderr, "\n"); + fprintf_ln(stderr, _("If you wish to set tracking information for this branch you can do so with:\n" + "\n" + " git branch --set-upstream-to=%s/<branch> %s\n"), + remote_name, curr_branch->name); + } else + fprintf_ln(stderr, _("Your configuration specifies to merge with the ref '%s'\n" + "from the remote, but no such ref was fetched."), + *curr_branch->merge_name); + exit(1); +} + +/** + * Parses argv into [<repo> [<refspecs>...]], returning their values in `repo` + * as a string and `refspecs` as a null-terminated array of strings. If `repo` + * is not provided in argv, it is set to NULL. + */ +static void parse_repo_refspecs(int argc, const char **argv, const char **repo, + const char ***refspecs) +{ + if (argc > 0) { + *repo = *argv++; + argc--; + } else + *repo = NULL; + *refspecs = argv; +} + +/** + * Runs git-fetch, returning its exit status. `repo` and `refspecs` are the + * repository and refspecs to fetch, or NULL if they are not provided. + */ +static int run_fetch(const char *repo, const char **refspecs) +{ + struct argv_array args = ARGV_ARRAY_INIT; + int ret; + + argv_array_pushl(&args, "fetch", "--update-head-ok", NULL); + + /* Shared options */ + argv_push_verbosity(&args); + if (opt_progress) + argv_array_push(&args, opt_progress); + + /* Options passed to git-fetch */ + if (opt_all) + argv_array_push(&args, opt_all); + if (opt_append) + argv_array_push(&args, opt_append); + if (opt_upload_pack) + argv_array_push(&args, opt_upload_pack); + argv_push_force(&args); + if (opt_tags) + argv_array_push(&args, opt_tags); + if (opt_prune) + argv_array_push(&args, opt_prune); + if (opt_recurse_submodules) + argv_array_push(&args, opt_recurse_submodules); + if (opt_dry_run) + argv_array_push(&args, "--dry-run"); + if (opt_keep) + argv_array_push(&args, opt_keep); + if (opt_depth) + argv_array_push(&args, opt_depth); + if (opt_unshallow) + argv_array_push(&args, opt_unshallow); + if (opt_update_shallow) + argv_array_push(&args, opt_update_shallow); + if (opt_refmap) + argv_array_push(&args, opt_refmap); + + if (repo) { + argv_array_push(&args, repo); + argv_array_pushv(&args, refspecs); + } else if (*refspecs) + die("BUG: refspecs without repo?"); + ret = run_command_v_opt(args.argv, RUN_GIT_CMD); + argv_array_clear(&args); + return ret; +} + +/** + * "Pulls into void" by branching off merge_head. + */ +static int pull_into_void(const unsigned char *merge_head, + const unsigned char *curr_head) +{ + /* + * Two-way merge: we treat the index as based on an empty tree, + * and try to fast-forward to HEAD. This ensures we will not lose + * index/worktree changes that the user already made on the unborn + * branch. + */ + if (checkout_fast_forward(EMPTY_TREE_SHA1_BIN, merge_head, 0)) + return 1; + + if (update_ref("initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR)) + return 1; + + return 0; +} + +/** + * Runs git-merge, returning its exit status. + */ +static int run_merge(void) +{ + int ret; + struct argv_array args = ARGV_ARRAY_INIT; + + argv_array_pushl(&args, "merge", NULL); + + /* Shared options */ + argv_push_verbosity(&args); + if (opt_progress) + argv_array_push(&args, opt_progress); + + /* Options passed to git-merge */ + if (opt_diffstat) + argv_array_push(&args, opt_diffstat); + if (opt_log) + argv_array_push(&args, opt_log); + if (opt_squash) + argv_array_push(&args, opt_squash); + if (opt_commit) + argv_array_push(&args, opt_commit); + if (opt_edit) + argv_array_push(&args, opt_edit); + if (opt_ff) + argv_array_push(&args, opt_ff); + if (opt_verify_signatures) + argv_array_push(&args, opt_verify_signatures); + argv_array_pushv(&args, opt_strategies.argv); + argv_array_pushv(&args, opt_strategy_opts.argv); + if (opt_gpg_sign) + argv_array_push(&args, opt_gpg_sign); + + argv_array_push(&args, "FETCH_HEAD"); + ret = run_command_v_opt(args.argv, RUN_GIT_CMD); + argv_array_clear(&args); + return ret; +} + +/** + * Returns remote's upstream branch for the current branch. If remote is NULL, + * the current branch's configured default remote is used. Returns NULL if + * `remote` does not name a valid remote, HEAD does not point to a branch, + * remote is not the branch's configured remote or the branch does not have any + * configured upstream branch. + */ +static const char *get_upstream_branch(const char *remote) +{ + struct remote *rm; + struct branch *curr_branch; + const char *curr_branch_remote; + + rm = remote_get(remote); + if (!rm) + return NULL; + + curr_branch = branch_get("HEAD"); + if (!curr_branch) + return NULL; + + curr_branch_remote = remote_for_branch(curr_branch, NULL); + assert(curr_branch_remote); + + if (strcmp(curr_branch_remote, rm->name)) + return NULL; + + return branch_get_upstream(curr_branch, NULL); +} + +/** + * Derives the remote tracking branch from the remote and refspec. + * + * FIXME: The current implementation assumes the default mapping of + * refs/heads/<branch_name> to refs/remotes/<remote_name>/<branch_name>. + */ +static const char *get_tracking_branch(const char *remote, const char *refspec) +{ + struct refspec *spec; + const char *spec_src; + const char *merge_branch; + + spec = parse_fetch_refspec(1, &refspec); + spec_src = spec->src; + if (!*spec_src || !strcmp(spec_src, "HEAD")) + spec_src = "HEAD"; + else if (skip_prefix(spec_src, "heads/", &spec_src)) + ; + else if (skip_prefix(spec_src, "refs/heads/", &spec_src)) + ; + else if (starts_with(spec_src, "refs/") || + starts_with(spec_src, "tags/") || + starts_with(spec_src, "remotes/")) + spec_src = ""; + + if (*spec_src) { + if (!strcmp(remote, ".")) + merge_branch = mkpath("refs/heads/%s", spec_src); + else + merge_branch = mkpath("refs/remotes/%s/%s", remote, spec_src); + } else + merge_branch = NULL; + + free_refspec(1, spec); + return merge_branch; +} + +/** + * Given the repo and refspecs, sets fork_point to the point at which the + * current branch forked from its remote tracking branch. Returns 0 on success, + * -1 on failure. + */ +static int get_rebase_fork_point(unsigned char *fork_point, const char *repo, + const char *refspec) +{ + int ret; + struct branch *curr_branch; + const char *remote_branch; + struct child_process cp = CHILD_PROCESS_INIT; + struct strbuf sb = STRBUF_INIT; + + curr_branch = branch_get("HEAD"); + if (!curr_branch) + return -1; + + if (refspec) + remote_branch = get_tracking_branch(repo, refspec); + else + remote_branch = get_upstream_branch(repo); + + if (!remote_branch) + return -1; + + argv_array_pushl(&cp.args, "merge-base", "--fork-point", + remote_branch, curr_branch->name, NULL); + cp.no_stdin = 1; + cp.no_stderr = 1; + cp.git_cmd = 1; + + ret = capture_command(&cp, &sb, GIT_SHA1_HEXSZ); + if (ret) + goto cleanup; + + ret = get_sha1_hex(sb.buf, fork_point); + if (ret) + goto cleanup; + +cleanup: + strbuf_release(&sb); + return ret ? -1 : 0; +} + +/** + * Sets merge_base to the octopus merge base of curr_head, merge_head and + * fork_point. Returns 0 if a merge base is found, 1 otherwise. + */ +static int get_octopus_merge_base(unsigned char *merge_base, + const unsigned char *curr_head, + const unsigned char *merge_head, + const unsigned char *fork_point) +{ + struct commit_list *revs = NULL, *result; + + commit_list_insert(lookup_commit_reference(curr_head), &revs); + commit_list_insert(lookup_commit_reference(merge_head), &revs); + if (!is_null_sha1(fork_point)) + commit_list_insert(lookup_commit_reference(fork_point), &revs); + + result = reduce_heads(get_octopus_merge_bases(revs)); + free_commit_list(revs); + if (!result) + return 1; + + hashcpy(merge_base, result->item->object.sha1); + return 0; +} + +/** + * Given the current HEAD SHA1, the merge head returned from git-fetch and the + * fork point calculated by get_rebase_fork_point(), runs git-rebase with the + * appropriate arguments and returns its exit status. + */ +static int run_rebase(const unsigned char *curr_head, + const unsigned char *merge_head, + const unsigned char *fork_point) +{ + int ret; + unsigned char oct_merge_base[GIT_SHA1_RAWSZ]; + struct argv_array args = ARGV_ARRAY_INIT; + + if (!get_octopus_merge_base(oct_merge_base, curr_head, merge_head, fork_point)) + if (!is_null_sha1(fork_point) && !hashcmp(oct_merge_base, fork_point)) + fork_point = NULL; + + argv_array_push(&args, "rebase"); + + /* Shared options */ + argv_push_verbosity(&args); + + /* Options passed to git-rebase */ + if (opt_rebase == REBASE_PRESERVE) + argv_array_push(&args, "--preserve-merges"); + if (opt_diffstat) + argv_array_push(&args, opt_diffstat); + argv_array_pushv(&args, opt_strategies.argv); + argv_array_pushv(&args, opt_strategy_opts.argv); + if (opt_gpg_sign) + argv_array_push(&args, opt_gpg_sign); + + argv_array_push(&args, "--onto"); + argv_array_push(&args, sha1_to_hex(merge_head)); + + if (fork_point && !is_null_sha1(fork_point)) + argv_array_push(&args, sha1_to_hex(fork_point)); + else + argv_array_push(&args, sha1_to_hex(merge_head)); + + ret = run_command_v_opt(args.argv, RUN_GIT_CMD); + argv_array_clear(&args); + return ret; +} + +int cmd_pull(int argc, const char **argv, const char *prefix) +{ + const char *repo, **refspecs; + struct sha1_array merge_heads = SHA1_ARRAY_INIT; + unsigned char orig_head[GIT_SHA1_RAWSZ], curr_head[GIT_SHA1_RAWSZ]; + unsigned char rebase_fork_point[GIT_SHA1_RAWSZ]; + + if (!getenv("GIT_REFLOG_ACTION")) + set_reflog_message(argc, argv); + + argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0); + + parse_repo_refspecs(argc, argv, &repo, &refspecs); + + if (!opt_ff) + opt_ff = xstrdup_or_null(config_get_ff()); + + if (opt_rebase < 0) + opt_rebase = config_get_rebase(); + + git_config(git_default_config, NULL); + + if (read_cache_unmerged()) + die_resolve_conflict("Pull"); + + if (file_exists(git_path("MERGE_HEAD"))) + die_conclude_merge(); + + if (get_sha1("HEAD", orig_head)) + hashclr(orig_head); + + if (opt_rebase) { + if (is_null_sha1(orig_head) && !is_cache_unborn()) + die(_("Updating an unborn branch with changes added to the index.")); + + die_on_unclean_work_tree(prefix); + + if (get_rebase_fork_point(rebase_fork_point, repo, *refspecs)) + hashclr(rebase_fork_point); + } + + if (run_fetch(repo, refspecs)) + return 1; + + if (opt_dry_run) + return 0; + + if (get_sha1("HEAD", curr_head)) + hashclr(curr_head); + + if (!is_null_sha1(orig_head) && !is_null_sha1(curr_head) && + hashcmp(orig_head, curr_head)) { + /* + * The fetch involved updating the current branch. + * + * The working tree and the index file are still based on + * orig_head commit, but we are merging into curr_head. + * Update the working tree to match curr_head. + */ + + warning(_("fetch updated the current branch head.\n" + "fast-forwarding your working tree from\n" + "commit %s."), sha1_to_hex(orig_head)); + + if (checkout_fast_forward(orig_head, curr_head, 0)) + die(_("Cannot fast-forward your working tree.\n" + "After making sure that you saved anything precious from\n" + "$ git diff %s\n" + "output, run\n" + "$ git reset --hard\n" + "to recover."), sha1_to_hex(orig_head)); + } + + get_merge_heads(&merge_heads); + + if (!merge_heads.nr) + die_no_merge_candidates(repo, refspecs); + + if (is_null_sha1(orig_head)) { + if (merge_heads.nr > 1) + die(_("Cannot merge multiple branches into empty head.")); + return pull_into_void(*merge_heads.sha1, curr_head); + } else if (opt_rebase) { + if (merge_heads.nr > 1) + die(_("Cannot rebase onto multiple branches.")); + return run_rebase(curr_head, *merge_heads.sha1, rebase_fork_point); + } else + return run_merge(); +} diff --git a/builtin/replace.c b/builtin/replace.c index 0d52e7f..6b3c469 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -104,9 +104,9 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn) continue; } full_hex = sha1_to_hex(sha1); - snprintf(ref, sizeof(ref), "refs/replace/%s", full_hex); + snprintf(ref, sizeof(ref), "%s%s", git_replace_ref_base, full_hex); /* read_ref() may reuse the buffer */ - full_hex = ref + strlen("refs/replace/"); + full_hex = ref + strlen(git_replace_ref_base); if (read_ref(ref, sha1)) { error("replace ref '%s' not found.", full_hex); had_error = 1; @@ -134,7 +134,7 @@ static void check_ref_valid(unsigned char object[20], int force) { if (snprintf(ref, ref_size, - "refs/replace/%s", + "%s%s", git_replace_ref_base, sha1_to_hex(object)) > ref_size - 1) die("replace ref name too long: %.*s...", 50, ref); if (check_refname_format(ref, 0)) diff --git a/builtin/rev-list.c b/builtin/rev-list.c index ff84a82..c0b4b53 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -42,6 +42,7 @@ static const char rev_list_usage[] = " --abbrev=<n> | --no-abbrev\n" " --abbrev-commit\n" " --left-right\n" +" --count\n" " special purpose:\n" " --bisect\n" " --bisect-vars\n" @@ -355,7 +356,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (bisect_list) revs.limited = 1; - if (use_bitmap_index) { + if (use_bitmap_index && !revs.prune) { if (revs.count && !revs.left_right && !revs.cherry_mark) { uint32_t commit_count; if (!prepare_bitmap_walk(&revs)) { diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c index ec0c4e3..38bedf8 100644 --- a/builtin/verify-commit.c +++ b/builtin/verify-commit.c @@ -18,25 +18,21 @@ static const char * const verify_commit_usage[] = { NULL }; -static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned long size, int verbose) +static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned long size, unsigned flags) { struct signature_check signature_check; + int ret; memset(&signature_check, 0, sizeof(signature_check)); - check_commit_signature(lookup_commit(sha1), &signature_check); - - if (verbose && signature_check.payload) - fputs(signature_check.payload, stdout); - - if (signature_check.gpg_output) - fputs(signature_check.gpg_output, stderr); + ret = check_commit_signature(lookup_commit(sha1), &signature_check); + print_signature_buffer(&signature_check, flags); signature_check_clear(&signature_check); - return signature_check.result != 'G'; + return ret; } -static int verify_commit(const char *name, int verbose) +static int verify_commit(const char *name, unsigned flags) { enum object_type type; unsigned char sha1[20]; @@ -54,7 +50,7 @@ static int verify_commit(const char *name, int verbose) return error("%s: cannot verify a non-commit object of type %s.", name, typename(type)); - ret = run_gpg_verify(sha1, buf, size, verbose); + ret = run_gpg_verify(sha1, buf, size, flags); free(buf); return ret; @@ -71,8 +67,10 @@ static int git_verify_commit_config(const char *var, const char *value, void *cb int cmd_verify_commit(int argc, const char **argv, const char *prefix) { int i = 1, verbose = 0, had_error = 0; + unsigned flags = 0; const struct option verify_commit_options[] = { OPT__VERBOSE(&verbose, N_("print commit contents")), + OPT_BIT(0, "raw", &flags, N_("print raw gpg status output"), GPG_VERIFY_RAW), OPT_END() }; @@ -83,11 +81,14 @@ int cmd_verify_commit(int argc, const char **argv, const char *prefix) if (argc <= i) usage_with_options(verify_commit_usage, verify_commit_options); + if (verbose) + flags |= GPG_VERIFY_VERBOSE; + /* sometimes the program was terminated because this signal * was received in the process of writing the gpg input: */ signal(SIGPIPE, SIG_IGN); while (i < argc) - if (verify_commit(argv[i++], verbose)) + if (verify_commit(argv[i++], flags)) had_error = 1; return had_error; } diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c index 53c68fc..00663f6 100644 --- a/builtin/verify-tag.c +++ b/builtin/verify-tag.c @@ -18,21 +18,30 @@ static const char * const verify_tag_usage[] = { NULL }; -static int run_gpg_verify(const char *buf, unsigned long size, int verbose) +static int run_gpg_verify(const char *buf, unsigned long size, unsigned flags) { + struct signature_check sigc; int len; + int ret; + + memset(&sigc, 0, sizeof(sigc)); len = parse_signature(buf, size); - if (verbose) - write_in_full(1, buf, len); - if (size == len) + if (size == len) { + if (flags & GPG_VERIFY_VERBOSE) + write_in_full(1, buf, len); return error("no signature found"); + } + + ret = check_signature(buf, len, buf + len, size - len, &sigc); + print_signature_buffer(&sigc, flags); - return verify_signed_buffer(buf, len, buf + len, size - len, NULL, NULL); + signature_check_clear(&sigc); + return ret; } -static int verify_tag(const char *name, int verbose) +static int verify_tag(const char *name, unsigned flags) { enum object_type type; unsigned char sha1[20]; @@ -52,7 +61,7 @@ static int verify_tag(const char *name, int verbose) if (!buf) return error("%s: unable to read file.", name); - ret = run_gpg_verify(buf, size, verbose); + ret = run_gpg_verify(buf, size, flags); free(buf); return ret; @@ -69,8 +78,10 @@ static int git_verify_tag_config(const char *var, const char *value, void *cb) int cmd_verify_tag(int argc, const char **argv, const char *prefix) { int i = 1, verbose = 0, had_error = 0; + unsigned flags = 0; const struct option verify_tag_options[] = { OPT__VERBOSE(&verbose, N_("print tag contents")), + OPT_BIT(0, "raw", &flags, N_("print raw gpg status output"), GPG_VERIFY_RAW), OPT_END() }; @@ -81,11 +92,14 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix) if (argc <= i) usage_with_options(verify_tag_usage, verify_tag_options); + if (verbose) + flags |= GPG_VERIFY_VERBOSE; + /* sometimes the program was terminated because this signal * was received in the process of writing the gpg input: */ signal(SIGPIPE, SIG_IGN); while (i < argc) - if (verify_tag(argv[i++], verbose)) + if (verify_tag(argv[i++], flags)) had_error = 1; return had_error; } diff --git a/builtin/worktree.c b/builtin/worktree.c new file mode 100644 index 0000000..6a264ee --- /dev/null +++ b/builtin/worktree.c @@ -0,0 +1,332 @@ +#include "cache.h" +#include "builtin.h" +#include "dir.h" +#include "parse-options.h" +#include "argv-array.h" +#include "run-command.h" +#include "sigchain.h" +#include "refs.h" + +static const char * const worktree_usage[] = { + N_("git worktree add [<options>] <path> <branch>"), + N_("git worktree prune [<options>]"), + NULL +}; + +static int show_only; +static int verbose; +static unsigned long expire; + +static int prune_worktree(const char *id, struct strbuf *reason) +{ + struct stat st; + char *path; + int fd, len; + + if (!is_directory(git_path("worktrees/%s", id))) { + strbuf_addf(reason, _("Removing worktrees/%s: not a valid directory"), id); + return 1; + } + if (file_exists(git_path("worktrees/%s/locked", id))) + return 0; + if (stat(git_path("worktrees/%s/gitdir", id), &st)) { + strbuf_addf(reason, _("Removing worktrees/%s: gitdir file does not exist"), id); + return 1; + } + fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY); + if (fd < 0) { + strbuf_addf(reason, _("Removing worktrees/%s: unable to read gitdir file (%s)"), + id, strerror(errno)); + return 1; + } + len = st.st_size; + path = xmalloc(len + 1); + read_in_full(fd, path, len); + close(fd); + while (len && (path[len - 1] == '\n' || path[len - 1] == '\r')) + len--; + if (!len) { + strbuf_addf(reason, _("Removing worktrees/%s: invalid gitdir file"), id); + free(path); + return 1; + } + path[len] = '\0'; + if (!file_exists(path)) { + struct stat st_link; + free(path); + /* + * the repo is moved manually and has not been + * accessed since? + */ + if (!stat(git_path("worktrees/%s/link", id), &st_link) && + st_link.st_nlink > 1) + return 0; + if (st.st_mtime <= expire) { + strbuf_addf(reason, _("Removing worktrees/%s: gitdir file points to non-existent location"), id); + return 1; + } else { + return 0; + } + } + free(path); + return 0; +} + +static void prune_worktrees(void) +{ + struct strbuf reason = STRBUF_INIT; + struct strbuf path = STRBUF_INIT; + DIR *dir = opendir(git_path("worktrees")); + struct dirent *d; + int ret; + if (!dir) + return; + while ((d = readdir(dir)) != NULL) { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + strbuf_reset(&reason); + if (!prune_worktree(d->d_name, &reason)) + continue; + if (show_only || verbose) + printf("%s\n", reason.buf); + if (show_only) + continue; + strbuf_reset(&path); + strbuf_addstr(&path, git_path("worktrees/%s", d->d_name)); + ret = remove_dir_recursively(&path, 0); + if (ret < 0 && errno == ENOTDIR) + ret = unlink(path.buf); + if (ret) + error(_("failed to remove: %s"), strerror(errno)); + } + closedir(dir); + if (!show_only) + rmdir(git_path("worktrees")); + strbuf_release(&reason); + strbuf_release(&path); +} + +static int prune(int ac, const char **av, const char *prefix) +{ + struct option options[] = { + OPT__DRY_RUN(&show_only, N_("do not remove, show only")), + OPT__VERBOSE(&verbose, N_("report pruned objects")), + OPT_EXPIRY_DATE(0, "expire", &expire, + N_("expire objects older than <time>")), + OPT_END() + }; + + expire = ULONG_MAX; + ac = parse_options(ac, av, prefix, options, worktree_usage, 0); + if (ac) + usage_with_options(worktree_usage, options); + prune_worktrees(); + return 0; +} + +static char *junk_work_tree; +static char *junk_git_dir; +static int is_junk; +static pid_t junk_pid; + +static void remove_junk(void) +{ + struct strbuf sb = STRBUF_INIT; + if (!is_junk || getpid() != junk_pid) + return; + if (junk_git_dir) { + strbuf_addstr(&sb, junk_git_dir); + remove_dir_recursively(&sb, 0); + strbuf_reset(&sb); + } + if (junk_work_tree) { + strbuf_addstr(&sb, junk_work_tree); + remove_dir_recursively(&sb, 0); + } + strbuf_release(&sb); +} + +static void remove_junk_on_signal(int signo) +{ + remove_junk(); + sigchain_pop(signo); + raise(signo); +} + +static const char *worktree_basename(const char *path, int *olen) +{ + const char *name; + int len; + + len = strlen(path); + while (len && is_dir_sep(path[len - 1])) + len--; + + for (name = path + len - 1; name > path; name--) + if (is_dir_sep(*name)) { + name++; + break; + } + + *olen = len; + return name; +} + +static int add_worktree(const char *path, const char **child_argv) +{ + struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT; + struct strbuf sb = STRBUF_INIT; + const char *name; + struct stat st; + struct child_process cp; + int counter = 0, len, ret; + unsigned char rev[20]; + + if (file_exists(path) && !is_empty_dir(path)) + die(_("'%s' already exists"), path); + + name = worktree_basename(path, &len); + strbuf_addstr(&sb_repo, + git_path("worktrees/%.*s", (int)(path + len - name), name)); + len = sb_repo.len; + if (safe_create_leading_directories_const(sb_repo.buf)) + die_errno(_("could not create leading directories of '%s'"), + sb_repo.buf); + while (!stat(sb_repo.buf, &st)) { + counter++; + strbuf_setlen(&sb_repo, len); + strbuf_addf(&sb_repo, "%d", counter); + } + name = strrchr(sb_repo.buf, '/') + 1; + + junk_pid = getpid(); + atexit(remove_junk); + sigchain_push_common(remove_junk_on_signal); + + if (mkdir(sb_repo.buf, 0777)) + die_errno(_("could not create directory of '%s'"), sb_repo.buf); + junk_git_dir = xstrdup(sb_repo.buf); + is_junk = 1; + + /* + * lock the incomplete repo so prune won't delete it, unlock + * after the preparation is over. + */ + strbuf_addf(&sb, "%s/locked", sb_repo.buf); + write_file(sb.buf, 1, "initializing\n"); + + strbuf_addf(&sb_git, "%s/.git", path); + if (safe_create_leading_directories_const(sb_git.buf)) + die_errno(_("could not create leading directories of '%s'"), + sb_git.buf); + junk_work_tree = xstrdup(path); + + strbuf_reset(&sb); + strbuf_addf(&sb, "%s/gitdir", sb_repo.buf); + write_file(sb.buf, 1, "%s\n", real_path(sb_git.buf)); + write_file(sb_git.buf, 1, "gitdir: %s/worktrees/%s\n", + real_path(get_git_common_dir()), name); + /* + * This is to keep resolve_ref() happy. We need a valid HEAD + * or is_git_directory() will reject the directory. Moreover, HEAD + * in the new worktree must resolve to the same value as HEAD in + * the current tree since the command invoked to populate the new + * worktree will be handed the branch/ref specified by the user. + * For instance, if the user asks for the new worktree to be based + * at HEAD~5, then the resolved HEAD~5 in the new worktree must + * match the resolved HEAD~5 in the current tree in order to match + * the user's expectation. + */ + if (!resolve_ref_unsafe("HEAD", 0, rev, NULL)) + die(_("unable to resolve HEAD")); + strbuf_reset(&sb); + strbuf_addf(&sb, "%s/HEAD", sb_repo.buf); + write_file(sb.buf, 1, "%s\n", sha1_to_hex(rev)); + strbuf_reset(&sb); + strbuf_addf(&sb, "%s/commondir", sb_repo.buf); + write_file(sb.buf, 1, "../..\n"); + + fprintf_ln(stderr, _("Enter %s (identifier %s)"), path, name); + + setenv("GIT_CHECKOUT_NEW_WORKTREE", "1", 1); + setenv(GIT_DIR_ENVIRONMENT, sb_git.buf, 1); + setenv(GIT_WORK_TREE_ENVIRONMENT, path, 1); + memset(&cp, 0, sizeof(cp)); + cp.git_cmd = 1; + cp.argv = child_argv; + ret = run_command(&cp); + if (!ret) { + is_junk = 0; + free(junk_work_tree); + free(junk_git_dir); + junk_work_tree = NULL; + junk_git_dir = NULL; + } + strbuf_reset(&sb); + strbuf_addf(&sb, "%s/locked", sb_repo.buf); + unlink_or_warn(sb.buf); + strbuf_release(&sb); + strbuf_release(&sb_repo); + strbuf_release(&sb_git); + return ret; +} + +static int add(int ac, const char **av, const char *prefix) +{ + int force = 0, detach = 0; + const char *new_branch = NULL, *new_branch_force = NULL; + const char *path, *branch; + struct argv_array cmd = ARGV_ARRAY_INIT; + struct option options[] = { + OPT__FORCE(&force, N_("checkout <branch> even if already checked out in other worktree")), + OPT_STRING('b', NULL, &new_branch, N_("branch"), + N_("create a new branch")), + OPT_STRING('B', NULL, &new_branch_force, N_("branch"), + N_("create or reset a branch")), + OPT_BOOL(0, "detach", &detach, N_("detach HEAD at named commit")), + OPT_END() + }; + + ac = parse_options(ac, av, prefix, options, worktree_usage, 0); + if (new_branch && new_branch_force) + die(_("-b and -B are mutually exclusive")); + if (ac < 1 || ac > 2) + usage_with_options(worktree_usage, options); + + path = prefix ? prefix_filename(prefix, strlen(prefix), av[0]) : av[0]; + branch = ac < 2 ? "HEAD" : av[1]; + + if (ac < 2 && !new_branch && !new_branch_force) { + int n; + const char *s = worktree_basename(path, &n); + new_branch = xstrndup(s, n); + } + + argv_array_push(&cmd, "checkout"); + if (force) + argv_array_push(&cmd, "--ignore-other-worktrees"); + if (new_branch) + argv_array_pushl(&cmd, "-b", new_branch, NULL); + if (new_branch_force) + argv_array_pushl(&cmd, "-B", new_branch_force, NULL); + if (detach) + argv_array_push(&cmd, "--detach"); + argv_array_push(&cmd, branch); + + return add_worktree(path, cmd.argv); +} + +int cmd_worktree(int ac, const char **av, const char *prefix) +{ + struct option options[] = { + OPT_END() + }; + + if (ac < 2) + usage_with_options(worktree_usage, options); + if (!strcmp(av[1], "add")) + return add(ac - 1, av + 1, prefix); + if (!strcmp(av[1], "prune")) + return prune(ac - 1, av + 1, prefix); + usage_with_options(worktree_usage, options); +} @@ -397,6 +397,7 @@ static inline enum object_type object_type(unsigned int mode) #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH" #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES" #define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS" +#define GIT_REPLACE_REF_BASE_ENVIRONMENT "GIT_REPLACE_REF_BASE" #define GITATTRIBUTES_FILE ".gitattributes" #define INFOATTRIBUTES_FILE "info/attributes" #define ATTRIBUTE_MACRO_PREFIX "[attr]" @@ -446,7 +447,17 @@ extern int get_common_dir(struct strbuf *sb, const char *gitdir); extern const char *get_git_namespace(void); extern const char *strip_namespace(const char *namespaced_ref); extern const char *get_git_work_tree(void); -extern const char *read_gitfile(const char *path); + +#define READ_GITFILE_ERR_STAT_FAILED 1 +#define READ_GITFILE_ERR_NOT_A_FILE 2 +#define READ_GITFILE_ERR_OPEN_FAILED 3 +#define READ_GITFILE_ERR_READ_FAILED 4 +#define READ_GITFILE_ERR_INVALID_FORMAT 5 +#define READ_GITFILE_ERR_NO_PATH 6 +#define READ_GITFILE_ERR_NOT_A_REPO 7 +#define READ_GITFILE_ERR_TOO_LARGE 8 +extern const char *read_gitfile_gently(const char *path, int *return_error_code); +#define read_gitfile(path) read_gitfile_gently((path), NULL) extern const char *resolve_gitdir(const char *suspect); extern void set_git_work_tree(const char *tree); @@ -620,6 +631,7 @@ extern unsigned long pack_size_limit_cfg; * been sought but there were none. */ extern int check_replace_refs; +extern char *git_replace_ref_base; extern int fsync_object_files; extern int core_preload_index; @@ -941,8 +953,17 @@ extern int has_sha1_pack(const unsigned char *sha1); * Return true iff we have an object named sha1, whether local or in * an alternate object database, and whether packed or loose. This * function does not respect replace references. + * + * If the QUICK flag is set, do not re-check the pack directory + * when we cannot find the object (this means we may give a false + * negative answer if another process is simultaneously repacking). */ -extern int has_sha1_file(const unsigned char *sha1); +#define HAS_SHA1_QUICK 0x1 +extern int has_sha1_file_with_flags(const unsigned char *sha1, int flags); +static inline int has_sha1_file(const unsigned char *sha1) +{ + return has_sha1_file_with_flags(sha1, 0); +} /* * Return true iff an alternate object database has a loose object @@ -1630,5 +1651,6 @@ int stat_validity_check(struct stat_validity *sv, const char *path); void stat_validity_update(struct stat_validity *sv, int fd); int versioncmp(const char *s1, const char *s2); +void sleep_millisec(int millisec); #endif /* CACHE_H */ diff --git a/command-list.txt b/command-list.txt index b17c011..2a94137 100644 --- a/command-list.txt +++ b/command-list.txt @@ -148,4 +148,5 @@ git-verify-pack plumbinginterrogators git-verify-tag ancillaryinterrogators gitweb ancillaryinterrogators git-whatchanged ancillaryinterrogators +git-worktree mainporcelain git-write-tree plumbingmanipulators @@ -1232,33 +1232,24 @@ free_return: free(buf); } -void check_commit_signature(const struct commit *commit, struct signature_check *sigc) +int check_commit_signature(const struct commit *commit, struct signature_check *sigc) { struct strbuf payload = STRBUF_INIT; struct strbuf signature = STRBUF_INIT; - struct strbuf gpg_output = STRBUF_INIT; - struct strbuf gpg_status = STRBUF_INIT; - int status; + int ret = 1; sigc->result = 'N'; if (parse_signed_commit(commit, &payload, &signature) <= 0) goto out; - status = verify_signed_buffer(payload.buf, payload.len, - signature.buf, signature.len, - &gpg_output, &gpg_status); - if (status && !gpg_output.len) - goto out; - sigc->payload = strbuf_detach(&payload, NULL); - sigc->gpg_output = strbuf_detach(&gpg_output, NULL); - sigc->gpg_status = strbuf_detach(&gpg_status, NULL); - parse_gpg_output(sigc); + ret = check_signature(payload.buf, payload.len, signature.buf, + signature.len, sigc); out: - strbuf_release(&gpg_status); - strbuf_release(&gpg_output); strbuf_release(&payload); strbuf_release(&signature); + + return ret; } @@ -379,7 +379,7 @@ extern void print_commit_list(struct commit_list *list, * at all. This may allocate memory for sig->gpg_output, sig->gpg_status, * sig->signer and sig->key. */ -extern void check_commit_signature(const struct commit *commit, struct signature_check *sigc); +extern int check_commit_signature(const struct commit *commit, struct signature_check *sigc); int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused); @@ -2118,6 +2118,9 @@ int git_config_set_multivar_in_file(const char *config_filename, contents_sz - copy_begin) < contents_sz - copy_begin) goto write_err_out; + + munmap(contents, contents_sz); + contents = NULL; } if (commit_lock_file(lock) < 0) { diff --git a/config.mak.uname b/config.mak.uname index d26665f..943c439 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -102,6 +102,9 @@ ifeq ($(uname_S),Darwin) ifeq ($(shell expr "$(uname_R)" : '[15]\.'),2) NO_STRLCPY = YesPlease endif + ifeq ($(shell test "`expr "$(uname_R)" : '\([0-9][0-9]*\)\.'`" -ge 11 && echo 1),1) + HAVE_GETDELIM = YesPlease + endif NO_MEMMEM = YesPlease USE_ST_TIMESPEC = YesPlease HAVE_DEV_TTY = YesPlease diff --git a/configure.ac b/configure.ac index bbdde85..14012fa 100644 --- a/configure.ac +++ b/configure.ac @@ -1041,6 +1041,12 @@ GIT_CHECK_FUNC(initgroups, [NO_INITGROUPS=YesPlease]) GIT_CONF_SUBST([NO_INITGROUPS]) # +# Define HAVE_GETDELIM if you have getdelim in the C library. +GIT_CHECK_FUNC(getdelim, +[HAVE_GETDELIM=YesPlease], +[HAVE_GETDELIM=]) +GIT_CONF_SUBST([HAVE_GETDELIM]) +# # # Define NO_MMAP if you want to avoid mmap. # diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3c00acd..c97c648 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1108,7 +1108,7 @@ _git_commit () case "$cur" in --cleanup=*) - __gitcomp "default strip verbatim whitespace + __gitcomp "default scissors strip verbatim whitespace " "" "${cur##--cleanup=}" return ;; diff --git a/contrib/completion/git-completion.tcsh b/contrib/completion/git-completion.tcsh index 6104a42..4a790d8 100644 --- a/contrib/completion/git-completion.tcsh +++ b/contrib/completion/git-completion.tcsh @@ -41,7 +41,7 @@ if ( ! -e ${__git_tcsh_completion_original_script} ) then exit endif -cat << EOF > ${__git_tcsh_completion_script} +cat << EOF >! ${__git_tcsh_completion_script} #!bash # # This script is GENERATED and will be overwritten automatically. diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index f18aedc..366f0bc 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -66,6 +66,10 @@ # git always compare HEAD to @{upstream} # svn always compare HEAD to your SVN upstream # +# You can change the separator between the branch name and the above +# state symbols by setting GIT_PS1_STATESEPARATOR. The default separator +# is SP. +# # By default, __git_ps1 will compare HEAD to your SVN upstream if it can # find one, or @{upstream} otherwise. Once you have set # GIT_PS1_SHOWUPSTREAM, you can override it on a per-repository basis by diff --git a/git-pull.sh b/contrib/examples/git-pull.sh index 0917d0d..a814bf6 100755 --- a/git-pull.sh +++ b/contrib/examples/git-pull.sh @@ -4,13 +4,53 @@ # # Fetch one or more remote refs and merge it/them into the current HEAD. -USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff|--ff-only] [--[no-]rebase|--rebase=preserve] [-s strategy]... [<fetch-options>] <repo> <head>...' -LONG_USAGE='Fetch one or more remote refs and integrate it/them with the current HEAD.' SUBDIRECTORY_OK=Yes -OPTIONS_SPEC= +OPTIONS_KEEPDASHDASH= +OPTIONS_STUCKLONG=Yes +OPTIONS_SPEC="\ +git pull [options] [<repository> [<refspec>...]] + +Fetch one or more remote refs and integrate it/them with the current HEAD. +-- +v,verbose be more verbose +q,quiet be more quiet +progress force progress reporting + + Options related to merging +r,rebase?false|true|preserve incorporate changes by rebasing rather than merging +n! do not show a diffstat at the end of the merge +stat show a diffstat at the end of the merge +summary (synonym to --stat) +log?n add (at most <n>) entries from shortlog to merge commit message +squash create a single commit instead of doing a merge +commit perform a commit if the merge succeeds (default) +e,edit edit message before committing +ff allow fast-forward +ff-only! abort if fast-forward is not possible +verify-signatures verify that the named commit has a valid GPG signature +s,strategy=strategy merge strategy to use +X,strategy-option=option option for selected merge strategy +S,gpg-sign?key-id GPG sign commit + + Options related to fetching +all fetch from all remotes +a,append append to .git/FETCH_HEAD instead of overwriting +upload-pack=path path to upload pack on remote end +f,force force overwrite of local branch +t,tags fetch all tags and associated objects +p,prune prune remote-tracking branches no longer on remote +recurse-submodules?on-demand control recursive fetching of submodules +dry-run dry run +k,keep keep downloaded pack +depth=depth deepen history of shallow clone +unshallow convert to a complete repository +update-shallow accept refs that update .git/shallow +refmap=refmap specify fetch refmap +" +test $# -gt 0 && args="$*" . git-sh-setup . git-sh-i18n -set_reflog_action "pull${1+ $*}" +set_reflog_action "pull${args+ $args}" require_work_tree_exists cd_to_toplevel @@ -44,7 +84,8 @@ bool_or_string_config () { strategy_args= diffstat= no_commit= squash= no_ff= ff_only= log_arg= verbosity= progress= recurse_submodules= verify_signatures= -merge_args= edit= rebase_args= +merge_args= edit= rebase_args= all= append= upload_pack= force= tags= prune= +keep= depth= unshallow= update_shallow= refmap= curr_branch=$(git symbolic-ref -q HEAD) curr_branch_short="${curr_branch#refs/heads/}" rebase=$(bool_or_string_config branch.$curr_branch_short.rebase) @@ -86,17 +127,17 @@ do diffstat=--stat ;; --log|--log=*|--no-log) log_arg="$1" ;; - --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit) + --no-commit) no_commit=--no-commit ;; - --c|--co|--com|--comm|--commi|--commit) + --commit) no_commit=--commit ;; -e|--edit) edit=--edit ;; --no-edit) edit=--no-edit ;; - --sq|--squ|--squa|--squas|--squash) + --squash) squash=--squash ;; - --no-sq|--no-squ|--no-squa|--no-squas|--no-squash) + --no-squash) squash=--no-squash ;; --ff) no_ff=--ff ;; @@ -104,39 +145,19 @@ do no_ff=--no-ff ;; --ff-only) ff_only=--ff-only ;; - -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\ - --strateg=*|--strategy=*|\ - -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy) - case "$#,$1" in - *,*=*) - strategy=$(expr "z$1" : 'z-[^=]*=\(.*\)') ;; - 1,*) - usage ;; - *) - strategy="$2" - shift ;; - esac - strategy_args="${strategy_args}-s $strategy " + -s*|--strategy=*) + strategy_args="$strategy_args $1" ;; - -X*) - case "$#,$1" in - 1,-X) - usage ;; - *,-X) - xx="-X $(git rev-parse --sq-quote "$2")" - shift ;; - *,*) - xx=$(git rev-parse --sq-quote "$1") ;; - esac - merge_args="$merge_args$xx " + -X*|--strategy-option=*) + merge_args="$merge_args $(git rev-parse --sq-quote "$1")" ;; - -r=*|--r=*|--re=*|--reb=*|--reba=*|--rebas=*|--rebase=*) + -r*|--rebase=*) rebase="${1#*=}" ;; - -r|--r|--re|--reb|--reba|--rebas|--rebase) + --rebase) rebase=true ;; - --no-r|--no-re|--no-reb|--no-reba|--no-rebas|--no-rebase) + --no-rebase) rebase=false ;; --recurse-submodules) @@ -163,16 +184,41 @@ do -S*) gpg_sign_args=$(git rev-parse --sq-quote "$1") ;; - --d|--dr|--dry|--dry-|--dry-r|--dry-ru|--dry-run) + --dry-run) dry_run=--dry-run ;; + --all|--no-all) + all=$1 ;; + -a|--append|--no-append) + append=$1 ;; + --upload-pack=*|--no-upload-pack) + upload_pack=$1 ;; + -f|--force|--no-force) + force="$force $1" ;; + -t|--tags|--no-tags) + tags=$1 ;; + -p|--prune|--no-prune) + prune=$1 ;; + -k|--keep|--no-keep) + keep=$1 ;; + --depth=*|--no-depth) + depth=$1 ;; + --unshallow|--no-unshallow) + unshallow=$1 ;; + --update-shallow|--no-update-shallow) + update_shallow=$1 ;; + --refmap=*|--no-refmap) + refmap=$1 ;; -h|--help-all) usage ;; - *) - # Pass thru anything that may be meant for fetch. + --) + shift break ;; + *) + usage + ;; esac shift done @@ -248,7 +294,9 @@ test true = "$rebase" && { oldremoteref=$(git merge-base --fork-point "$remoteref" $curr_branch 2>/dev/null) } orig_head=$(git rev-parse -q --verify HEAD) -git fetch $verbosity $progress $dry_run $recurse_submodules --update-head-ok "$@" || exit 1 +git fetch $verbosity $progress $dry_run $recurse_submodules $all $append \ +$upload_pack $force $tags $prune $keep $depth $unshallow $update_shallow \ +$refmap --update-head-ok "$@" || exit 1 test -z "$dry_run" || exit 0 curr_head=$(git rev-parse -q --verify HEAD) diff --git a/contrib/hooks/multimail/CHANGES b/contrib/hooks/multimail/CHANGES index 3603d56..6bb1230 100644 --- a/contrib/hooks/multimail/CHANGES +++ b/contrib/hooks/multimail/CHANGES @@ -1,3 +1,56 @@ +Release 1.1.1 (bugfix-only release) +=================================== + +* The SMTP mailer was not working with Python 2.4. + +Release 1.1.0 +============= + +* When a single commit is pushed, omit the reference changed email. + Set multimailhook.combineWhenSingleCommit to false to disable this + new feature. + +* In gitolite environments, the pusher's email address can be used as + the From address by creating a specially formatted comment block in + gitolite.conf (see multimailhook.from in README). + +* Support for SMTP authentication and SSL/TLS encryption was added, + see smtpUser, smtpPass, smtpEncryption in README. + +* A new option scanCommitForCc was added to allow git-multimail to + search the commit message for 'Cc: ...' lines, and add the + corresponding emails in Cc. + +* If $USER is not set, use the variable $USERNAME. This is needed on + Windows platform to recognize the pusher. + +* The emailPrefix variable can now be set to an empty string to remove + the prefix. + +* A short tutorial was added in doc/gitolite.rst to set up + git-multimail with gitolite. + +* The post-receive file was renamed to post-receive.example. It has + always been an example (the standard way to call git-multimail is to + call git_multimail.py), but it was unclear to many users. + +* A new refchangeShowGraph option was added to make it possible to + include both a graph and a log in the summary emails. The options + to control the graph formatting can be set via the new graphOpts + option. + +* New option --force-send was added to disable new commit detection + for update hook. One use-case is to run git_multimail.py after + running "git fetch" to send emails about commits that have just been + fetched (the detection of new commits was unreliable in this mode). + +* The testing infrastructure was considerably improved (continuous + integration with travis-ci, automatic check of PEP8 and RST syntax, + many improvements to the test scripts). + +This version has been tested with Python 2.4 to 2.7, and Git 1.7.1 to +2.4. + Release 1.0.0 ============= diff --git a/contrib/hooks/multimail/README b/contrib/hooks/multimail/README index 6efa4ff..e552c90 100644 --- a/contrib/hooks/multimail/README +++ b/contrib/hooks/multimail/README @@ -1,5 +1,8 @@ - git-multimail - ============= +git-multimail Version 1.1.1 +=========================== + +.. image:: https://travis-ci.org/git-multimail/git-multimail.svg?branch=master + :target: https://travis-ci.org/git-multimail/git-multimail git-multimail is a tool for sending notification emails on pushes to a Git repository. It includes a Python module called git_multimail.py, @@ -38,17 +41,17 @@ By default, for each push received by the repository, git-multimail: list) makes it easy to scan through the emails, jump to patches that need further attention, and write comments about specific commits. Commits are handled in reverse topological order (i.e., - parents shown before children). For example, - - [git] branch master updated - + [git] 01/08: doc: fix xref link from api docs to manual pages - + [git] 02/08: api-credentials.txt: show the big picture first - + [git] 03/08: api-credentials.txt: mention credential.helper explicitly - + [git] 04/08: api-credentials.txt: add "see also" section - + [git] 05/08: t3510 (cherry-pick-sequence): add missing '&&' - + [git] 06/08: Merge branch 'rr/maint-t3510-cascade-fix' - + [git] 07/08: Merge branch 'mm/api-credentials-doc' - + [git] 08/08: Git 1.7.11-rc2 + parents shown before children). For example:: + + [git] branch master updated + + [git] 01/08: doc: fix xref link from api docs to manual pages + + [git] 02/08: api-credentials.txt: show the big picture first + + [git] 03/08: api-credentials.txt: mention credential.helper explicitly + + [git] 04/08: api-credentials.txt: add "see also" section + + [git] 05/08: t3510 (cherry-pick-sequence): add missing '&&' + + [git] 06/08: Merge branch 'rr/maint-t3510-cascade-fix' + + [git] 07/08: Merge branch 'mm/api-credentials-doc' + + [git] 08/08: Git 1.7.11-rc2 Each commit appears in exactly one commit email, the first time that it is pushed to the repository. If a commit is later merged @@ -74,19 +77,19 @@ Requirements 3.x. The example scripts invoke Python using the following shebang line - (following PEP 394 [1]): + (following PEP 394 [1]_):: #! /usr/bin/env python2 If your system's Python2 interpreter is not in your PATH or is not - called "python2", you can change the lines accordingly. Or you can + called ``python2``, you can change the lines accordingly. Or you can invoke the Python interpreter explicitly, for example via a tiny - shell script like + shell script like:: #! /bin/sh /usr/local/bin/python /path/to/git_multimail.py "$@" -* The "git" command must be in your PATH. git-multimail is known to +* The ``git`` command must be in your PATH. git-multimail is known to work with Git versions back to 1.7.1. (Earlier versions have not been tested; if you do so, please report your results.) @@ -101,7 +104,7 @@ Requirements Invocation ---------- -git_multimail.py is designed to be used as a "post-receive" hook in a +git_multimail.py is designed to be used as a ``post-receive`` hook in a Git repository (see githooks(5)). Link or copy it to $GIT_DIR/hooks/post-receive within the repository for which email notifications are desired. Usually it should be installed on the @@ -109,10 +112,10 @@ central repository for a project, to which all commits are eventually pushed. For use on pre-v1.5.1 Git servers, git_multimail.py can also work as -an "update" hook, taking its arguments on the command line. To use +an ``update`` hook, taking its arguments on the command line. To use this script in this manner, link or copy it to $GIT_DIR/hooks/update. Please note that the script is not completely reliable in this mode -[2]. +[2]_. Alternatively, git_multimail.py can be imported as a Python module into your own Python post-receive script. This method is a bit more @@ -129,7 +132,7 @@ arbitrary Python code. For example, you can use a custom environment only about changes affecting particular files or subdirectories) Or you can change how emails are sent by writing your own Mailer -class. The "post-receive" script in this directory demonstrates how +class. The ``post-receive`` script in this directory demonstrates how to use git_multimail.py as a Python module. (If you make interesting changes of this type, please consider sharing them with the community.) @@ -139,18 +142,26 @@ Configuration ------------- By default, git-multimail mostly takes its configuration from the -following "git config" settings: +following ``git config`` settings: multimailhook.environment This describes the general environment of the repository. Currently supported values: - "generic" -- the username of the pusher is read from $USER and the - repository name is derived from the repository's path. + * generic + + the username of the pusher is read from $USER or $USERNAME and + the repository name is derived from the repository's path. + + * gitolite - "gitolite" -- the username of the pusher is read from $GL_USER and - the repository name from $GL_REPO. + the username of the pusher is read from $GL_USER, the repository + name is read from $GL_REPO, and the From: header value is + optionally read from gitolite.conf (see multimailhook.from). + + For more information about gitolite and git-multimail, read + doc/gitolite.rst If neither of these environments is suitable for your setup, then you can implement a Python class that inherits from Environment @@ -160,8 +171,8 @@ multimailhook.environment The environment value can be specified on the command line using the --environment option. If it is not specified on the command line or by multimailhook.environment, then it defaults to - "gitolite" if the environment contains variables $GL_USER and - $GL_REPO; otherwise "generic". + ``gitolite`` if the environment contains variables $GL_USER and + $GL_REPO; otherwise ``generic``. multimailhook.repoName @@ -219,61 +230,109 @@ multimailhook.announceShortlog not so straightforward, then the shortlog might be confusing rather than useful. Default is false. +multimailhook.refchangeShowGraph + + If this option is set to true, then summary emails about reference + changes will additionally include: + + * a graph of the added commits (if any) + + * a graph of the discarded commits (if any) + + The log is generated by running ``git log --graph`` with the options + specified in graphOpts. The default is false. + multimailhook.refchangeShowLog If this option is set to true, then summary emails about reference changes will include a detailed log of the added commits in addition to the one line summary. The log is generated by running - "git log" with the options specified in multimailhook.logOpts. + ``git log`` with the options specified in multimailhook.logOpts. Default is false. multimailhook.mailer This option changes the way emails are sent. Accepted values are: - - sendmail (the default): use the command /usr/sbin/sendmail or - /usr/lib/sendmail (or sendmailCommand, if configured). This + - sendmail (the default): use the command ``/usr/sbin/sendmail`` or + ``/usr/lib/sendmail`` (or sendmailCommand, if configured). This mode can be further customized via the following options: - multimailhook.sendmailCommand + * multimailhook.sendmailCommand - The command used by mailer "sendmail" to send emails. Shell - quoting is allowed in the value of this setting, but remember that - Git requires double-quotes to be escaped; e.g., + The command used by mailer ``sendmail`` to send emails. Shell + quoting is allowed in the value of this setting, but remember that + Git requires double-quotes to be escaped; e.g.:: git config multimailhook.sendmailcommand '/usr/sbin/sendmail -oi -t -F \"Git Repo\"' - Default is '/usr/sbin/sendmail -oi -t' or - '/usr/lib/sendmail -oi -t' (depending on which file is - present and executable). + Default is '/usr/sbin/sendmail -oi -t' or + '/usr/lib/sendmail -oi -t' (depending on which file is + present and executable). - multimailhook.envelopeSender + * multimailhook.envelopeSender - If set then pass this value to sendmail via the -f option to set - the envelope sender address. + If set then pass this value to sendmail via the -f option to set + the envelope sender address. - smtp: use Python's smtplib. This is useful when the sendmail command is not available on the system. This mode can be further customized via the following options: - multimailhook.smtpServer + * multimailhook.smtpServer + + The name of the SMTP server to connect to. The value can + also include a colon and a port number; e.g., + ``mail.example.com:25``. Default is 'localhost' using port 25. + + * multimailhook.smtpUser + * multimailhook.smtpPass + + Server username and password. Required if smtpEncryption is 'ssl'. + Note that the username and password currently need to be + set cleartext in the configuration file, which is not + recommended. If you need to use this option, be sure your + configuration file is read-only. + + * multimailhook.envelopeSender + + The sender address to be passed to the SMTP server. If + unset, then the value of multimailhook.from is used. + + * multimailhook.smtpServerTimeout + + Timeout in seconds. - The name of the SMTP server to connect to. The value can - also include a colon and a port number; e.g., - "mail.example.com:25". Default is 'localhost' using port - 25. + * multimailhook.smtpEncryption - multimailhook.envelopeSender + Set the security type. Allowed values: none, ssl. + Default=none. - The sender address to be passed to the SMTP server. If - unset, then the value of multimailhook.from is used. + * multimailhook.smtpServerDebugLevel + + Integer number. Set to greater than 0 to activate debugging. multimailhook.from - If set then use this value in the From: field of generated emails. - If unset, then use the repository's user configuration (user.name - and user.email). If user.email is also unset, then use - multimailhook.envelopeSender. + If set, use this value in the From: field of generated emails. If + unset, the value of the From: header is determined as follows: + + 1. (gitolite environment only) Parse gitolite.conf, looking for a + block of comments that looks like this:: + + # BEGIN USER EMAILS + # username Firstname Lastname <email@example.com> + # END USER EMAILS + + If that block exists, and there is a line between the BEGIN + USER EMAILS and END USER EMAILS lines where the first field + matches the gitolite username ($GL_USER), use the rest of the + line for the From: header. + + 2. If the user.email configuration setting is set, use its value + (and the value of user.name, if set). + + 3. Use the value of multimailhook.envelopeSender. multimailhook.administrator @@ -287,7 +346,8 @@ multimailhook.emailPrefix All emails have this string prepended to their subjects, to aid email filtering (though filtering based on the X-Git-* email headers is probably more robust). Default is the short name of - the repository in square brackets; e.g., "[myrepo]". + the repository in square brackets; e.g., ``[myrepo]``. Set this + value to the empty string to suppress the email prefix. multimailhook.emailMaxLines @@ -299,7 +359,7 @@ multimailhook.emailMaxLines multimailhook.emailMaxLineLength The maximum length of a line in the email body. Lines longer than - this limit are truncated to this length with a trailing " [...]" + this limit are truncated to this length with a trailing `` [...]`` added to indicate the missing text. The default is 500, because (a) diffs with longer lines are probably from binary files, for which a diff is useless, and (b) even if a text file has such long @@ -316,54 +376,62 @@ multimailhook.maxCommitEmails multimailhook.emailStrictUTF8 - If this boolean option is set to "true", then the main part of the + If this boolean option is set to `true`, then the main part of the email body is forced to be valid UTF-8. Any characters that are not valid UTF-8 are converted to the Unicode replacement - character, U+FFFD. The default is "true". + character, U+FFFD. The default is `true`. multimailhook.diffOpts - Options passed to "git diff-tree" when generating the summary - information for ReferenceChange emails. Default is "--stat - --summary --find-copies-harder". Add -p to those options to + Options passed to ``git diff-tree`` when generating the summary + information for ReferenceChange emails. Default is ``--stat + --summary --find-copies-harder``. Add -p to those options to include a unified diff of changes in addition to the usual summary output. Shell quoting is allowed; see multimailhook.logOpts for details. +multimailhook.graphOpts + + Options passed to ``git log --graph`` when generating graphs for the + reference change summary emails (used only if refchangeShowGraph + is true). The default is '--oneline --decorate'. + + Shell quoting is allowed; see logOpts for details. + multimailhook.logOpts - Options passed to "git log" to generate additional info for + Options passed to ``git log`` to generate additional info for reference change emails (used only if refchangeShowLog is set). - For example, adding --graph will show the graph of revisions, -p - will show the complete diff, etc. The default is empty. + For example, adding -p will show each commit's complete diff. The + default is empty. Shell quoting is allowed; for example, a log format that contains - spaces can be specified using something like: + spaces can be specified using something like:: git config multimailhook.logopts '--pretty=format:"%h %aN <%aE>%n%s%n%n%b%n"' If you want to set this by editing your configuration file directly, remember that Git requires double-quotes to be escaped - (see git-config(1) for more information): + (see git-config(1) for more information):: [multimailhook] logopts = --pretty=format:\"%h %aN <%aE>%n%s%n%n%b%n\" multimailhook.commitLogOpts - Options passed to "git log" to generate additional info for + Options passed to ``git log`` to generate additional info for revision change emails. For example, adding --ignore-all-spaces - will suppress whitespace changes. The default options are "-C - --stat -p --cc". Shell quoting is allowed; see + will suppress whitespace changes. The default options are ``-C + --stat -p --cc``. Shell quoting is allowed; see multimailhook.logOpts for details. multimailhook.emailDomain Domain name appended to the username of the person doing the push - to convert it into an email address (via "%s@%s" % (username, - emaildomain)). More complicated schemes can be implemented by - overriding Environment and overriding its get_pusher_email() - method. + to convert it into an email address + (via ``"%s@%s" % (username, emaildomain)``). More complicated + schemes can be implemented by overriding Environment and + overriding its get_pusher_email() method. multimailhook.replyTo multimailhook.replyToCommit @@ -377,26 +445,48 @@ multimailhook.replyToRefchange - An email address, which will be used directly. - - The value "pusher", in which case the pusher's address (if + - The value `pusher`, in which case the pusher's address (if available) will be used. This is the default for refchange emails. - - The value "author" (meaningful only for replyToCommit), in which + - The value `author` (meaningful only for replyToCommit), in which case the commit author's address will be used. This is the default for commit emails. - - The value "none", in which case the Reply-To: field will be + - The value `none`, in which case the Reply-To: field will be omitted. +multimailhook.quiet + + Do not output the list of email recipients from the hook + +multimailhook.stdout + + For debugging, send emails to stdout rather than to the + mailer. Equivalent to the --stdout command line option + +multimailhook.scanCommitForCc + + If this option is set to true, than recipients from lines in commit body + that starts with ``CC:`` will be added to CC list. + Default: false + +multimailhook.combineWhenSingleCommit + + If this option is set to true and a single new commit is pushed to + a branch, combine the summary and commit email messages into a + single email. + Default: true + Email filtering aids -------------------- All emails include extra headers to enable fine tuned filtering and give information for debugging. All emails include the headers -"X-Git-Host", "X-Git-Repo", "X-Git-Refname", and "X-Git-Reftype". -ReferenceChange emails also include headers "X-Git-Oldrev" and "X-Git-Newrev"; -Revision emails also include header "X-Git-Rev". +``X-Git-Host``, ``X-Git-Repo``, ``X-Git-Refname``, and ``X-Git-Reftype``. +ReferenceChange emails also include headers ``X-Git-Oldrev`` and ``X-Git-Newrev``; +Revision emails also include header ``X-Git-Rev``. Customizing email contents @@ -420,16 +510,17 @@ environment are built in: * GenericEnvironment: a stand-alone Git repository. * GitoliteEnvironment: a Git repository that is managed by gitolite - [3]. For such repositories, the identity of the pusher is read from - environment variable $GL_USER, and the name of the repository is - read from $GL_REPO (if it is not overridden by - multimailhook.reponame). + [3]_. For such repositories, the identity of the pusher is read from + environment variable $GL_USER, the name of the repository is read + from $GL_REPO (if it is not overridden by multimailhook.reponame), + and the From: header value is optionally read from gitolite.conf + (see multimailhook.from). By default, git-multimail assumes GitoliteEnvironment if $GL_USER and $GL_REPO are set, and otherwise assumes GenericEnvironment. Alternatively, you can choose one of these two environments explicitly -by setting a "multimailhook.environment" config setting (which can -have the value "generic" or "gitolite") or by passing an --environment +by setting a ``multimailhook.environment`` config setting (which can +have the value `generic` or `gitolite`) or by passing an --environment option to the script. If you need to customize the script in ways that are not supported by @@ -439,8 +530,8 @@ git_multimail.py as a Python module, as demonstrated by the example post-receive script. Then implement your environment class; it should usually inherit from one of the existing Environment classes and possibly one or more of the EnvironmentMixin classes. Then set the -"environment" variable to an instance of your own environment class -and pass it to run_as_post_receive_hook(). +``environment`` variable to an instance of your own environment class +and pass it to ``run_as_post_receive_hook()``. The standard environment classes, GenericEnvironment and GitoliteEnvironment, are in fact themselves put together out of a @@ -490,12 +581,14 @@ don't overlook them. Footnotes --------- -[1] http://www.python.org/dev/peps/pep-0394/ +.. [1] http://www.python.org/dev/peps/pep-0394/ -[2] Because of the way information is passed to update hooks, the - script's method of determining whether a commit has already been - seen does not work when it is used as an "update" script. In - particular, no notification email will be generated for a new - commit that is added to multiple references in the same push. +.. [2] Because of the way information is passed to update hooks, the + script's method of determining whether a commit has already + been seen does not work when it is used as an ``update`` script. + In particular, no notification email will be generated for a + new commit that is added to multiple references in the same + push. A workaround is to use --force-send to force sending the + emails. -[3] https://github.com/sitaramc/gitolite +.. [3] https://github.com/sitaramc/gitolite diff --git a/contrib/hooks/multimail/README.Git b/contrib/hooks/multimail/README.Git index ab3ece5..f5d59a8 100644 --- a/contrib/hooks/multimail/README.Git +++ b/contrib/hooks/multimail/README.Git @@ -6,10 +6,10 @@ website: https://github.com/git-multimail/git-multimail The version in this directory was obtained from the upstream project -on 2015-04-27 and consists of the "git-multimail" subdirectory from +on July 03 2015 and consists of the "git-multimail" subdirectory from revision - 8c3aaafa873bf10de8dddf1d202c449b3eff3b42 refs/tags/1.0.2 + 6d6c9eb62a054143322cfaecde3949189c065b46 refs/tags/1.1.1 Please see the README file in this directory for information about how to report bugs or contribute to git-multimail. diff --git a/contrib/hooks/multimail/git_multimail.py b/contrib/hooks/multimail/git_multimail.py index 8b58ed6..c06ce7a 100755 --- a/contrib/hooks/multimail/git_multimail.py +++ b/contrib/hooks/multimail/git_multimail.py @@ -1,5 +1,6 @@ #! /usr/bin/env python2 +# Copyright (c) 2015 Matthieu Moy and others # Copyright (c) 2012-2014 Michael Haggerty and others # Derived from contrib/hooks/post-receive-email, which is # Copyright (c) 2007 Andy Parkins @@ -99,6 +100,10 @@ REF_DELETED_SUBJECT_TEMPLATE = ( ' (was %(oldrev_short)s)' ) +COMBINED_REFCHANGE_REVISION_SUBJECT_TEMPLATE = ( + '%(emailprefix)s%(refname_type)s %(short_refname)s updated: %(oneline)s' + ) + REFCHANGE_HEADER_TEMPLATE = """\ Date: %(send_date)s To: %(recipients)s @@ -230,6 +235,7 @@ how to provide full information about this reference change. REVISION_HEADER_TEMPLATE = """\ Date: %(send_date)s To: %(recipients)s +Cc: %(cc_recipients)s Subject: %(emailprefix)s%(num)02d/%(tot)02d: %(oneline)s MIME-Version: 1.0 Content-Type: text/plain; charset=%(charset)s @@ -258,6 +264,38 @@ in repository %(repo_shortname)s. REVISION_FOOTER_TEMPLATE = FOOTER_TEMPLATE +# Combined, meaning refchange+revision email (for single-commit additions) +COMBINED_HEADER_TEMPLATE = """\ +Date: %(send_date)s +To: %(recipients)s +Subject: %(subject)s +MIME-Version: 1.0 +Content-Type: text/plain; charset=%(charset)s +Content-Transfer-Encoding: 8bit +Message-ID: %(msgid)s +From: %(fromaddr)s +Reply-To: %(reply_to)s +X-Git-Host: %(fqdn)s +X-Git-Repo: %(repo_shortname)s +X-Git-Refname: %(refname)s +X-Git-Reftype: %(refname_type)s +X-Git-Oldrev: %(oldrev)s +X-Git-Newrev: %(newrev)s +X-Git-Rev: %(rev)s +Auto-Submitted: auto-generated +""" + +COMBINED_INTRO_TEMPLATE = """\ +This is an automated email from the git hooks/post-receive script. + +%(pusher)s pushed a commit to %(refname_type)s %(short_refname)s +in repository %(repo_shortname)s. + +""" + +COMBINED_FOOTER_TEMPLATE = FOOTER_TEMPLATE + + class CommandError(Exception): def __init__(self, cmd, retcode): self.cmd = cmd @@ -336,6 +374,47 @@ def read_git_lines(args, keepends=False, **kw): return read_git_output(args, keepends=True, **kw).splitlines(keepends) +def git_rev_list_ish(cmd, spec, args=None, **kw): + """Common functionality for invoking a 'git rev-list'-like command. + + Parameters: + * cmd is the Git command to run, e.g., 'rev-list' or 'log'. + * spec is a list of revision arguments to pass to the named + command. If None, this function returns an empty list. + * args is a list of extra arguments passed to the named command. + * All other keyword arguments (if any) are passed to the + underlying read_git_lines() function. + + Return the output of the Git command in the form of a list, one + entry per output line. + """ + if spec is None: + return [] + if args is None: + args = [] + args = [cmd, '--stdin'] + args + spec_stdin = ''.join(s + '\n' for s in spec) + return read_git_lines(args, input=spec_stdin, **kw) + + +def git_rev_list(spec, **kw): + """Run 'git rev-list' with the given list of revision arguments. + + See git_rev_list_ish() for parameter and return value + documentation. + """ + return git_rev_list_ish('rev-list', spec, **kw) + + +def git_log(spec, **kw): + """Run 'git log' with the given list of revision arguments. + + See git_rev_list_ish() for parameter and return value + documentation. + """ + return git_rev_list_ish('log', spec, **kw) + + def header_encode(text, header_name=None): """Encode and line-wrap the value of an email header field.""" @@ -388,9 +467,9 @@ class Config(object): def get(self, name, default=None): try: values = self._split(read_git_output( - ['config', '--get', '--null', '%s.%s' % (self.section, name)], - env=self.env, keepends=True, - )) + ['config', '--get', '--null', '%s.%s' % (self.section, name)], + env=self.env, keepends=True, + )) assert len(values) == 1 return values[0] except CommandError: @@ -449,9 +528,14 @@ class Config(object): env=self.env, ) - def has_key(self, name): + def __contains__(self, name): return self.get_all(name, default=None) is not None + # We don't use this method anymore internally, but keep it here in + # case somebody is calling it from their own code: + def has_key(self, name): + return name in self + def unset_all(self, name): try: read_git_output( @@ -579,7 +663,7 @@ class Change(object): self._values = None def _compute_values(self): - """Return a dictionary {keyword : expansion} for this Change. + """Return a dictionary {keyword: expansion} for this Change. Derived classes overload this method to add more entries to the return value. This method is used internally by @@ -589,7 +673,7 @@ class Change(object): return self.environment.get_values() def get_values(self, **extra_values): - """Return a dictionary {keyword : expansion} for this Change. + """Return a dictionary {keyword: expansion} for this Change. Return a dictionary mapping keywords to the values that they should be expanded to for this Change (used when interpolating @@ -636,7 +720,7 @@ class Change(object): value = value % values except KeyError, e: if DEBUG: - sys.stderr.write( + self.environment.log_warning( 'Warning: unknown variable %r in the following line; line skipped:\n' ' %s\n' % (e.args[0], line,) @@ -711,6 +795,8 @@ class Change(object): class Revision(Change): """A Change consisting of a single git commit.""" + CC_RE = re.compile(r'^\s*C[Cc]:\s*(?P<to>[^#]+@[^\s#]*)\s*(#.*)?$') + def __init__(self, reference_change, rev, num, tot): Change.__init__(self, reference_change.environment) self.reference_change = reference_change @@ -722,6 +808,24 @@ class Revision(Change): self.author = read_git_output(['log', '--no-walk', '--format=%aN <%aE>', self.rev.sha1]) self.recipients = self.environment.get_revision_recipients(self) + self.cc_recipients = '' + if self.environment.get_scancommitforcc(): + self.cc_recipients = ', '.join(to.strip() for to in self._cc_recipients()) + if self.cc_recipients: + self.environment.log_msg( + 'Add %s to CC for %s\n' % (self.cc_recipients, self.rev.sha1)) + + def _cc_recipients(self): + cc_recipients = [] + message = read_git_output(['log', '--no-walk', '--format=%b', self.rev.sha1]) + lines = message.strip().split('\n') + for line in lines: + m = re.match(self.CC_RE, line) + if m: + cc_recipients.append(m.group('to')) + + return cc_recipients + def _compute_values(self): values = Change._compute_values(self) @@ -739,6 +843,8 @@ class Revision(Change): values['num'] = self.num values['tot'] = self.tot values['recipients'] = self.recipients + if self.cc_recipients: + values['cc_recipients'] = self.cc_recipients values['oneline'] = oneline values['author'] = self.author @@ -750,8 +856,8 @@ class Revision(Change): def generate_email_header(self, **extra_values): for line in self.expand_header_lines( - REVISION_HEADER_TEMPLATE, **extra_values - ): + REVISION_HEADER_TEMPLATE, **extra_values + ): yield line def generate_email_intro(self): @@ -822,26 +928,26 @@ class ReferenceChange(Change): klass = BranchChange elif area == 'remotes': # Tracking branch: - sys.stderr.write( + environment.log_warning( '*** Push-update of tracking branch %r\n' '*** - incomplete email generated.\n' - % (refname,) + % (refname,) ) klass = OtherReferenceChange else: # Some other reference namespace: - sys.stderr.write( + environment.log_warning( '*** Push-update of strange reference %r\n' '*** - incomplete email generated.\n' - % (refname,) + % (refname,) ) klass = OtherReferenceChange else: # Anything else (is there anything else?) - sys.stderr.write( + environment.log_warning( '*** Unknown type of update to %r (%s)\n' '*** - incomplete email generated.\n' - % (refname, rev.type,) + % (refname, rev.type,) ) klass = OtherReferenceChange @@ -854,9 +960,9 @@ class ReferenceChange(Change): def __init__(self, environment, refname, short_refname, old, new, rev): Change.__init__(self, environment) self.change_type = { - (False, True) : 'create', - (True, True) : 'update', - (True, False) : 'delete', + (False, True): 'create', + (True, True): 'update', + (True, False): 'delete', }[bool(old), bool(new)] self.refname = refname self.short_refname = short_refname @@ -865,10 +971,16 @@ class ReferenceChange(Change): self.rev = rev self.msgid = make_msgid() self.diffopts = environment.diffopts + self.graphopts = environment.graphopts self.logopts = environment.logopts self.commitlogopts = environment.commitlogopts + self.showgraph = environment.refchange_showgraph self.showlog = environment.refchange_showlog + self.header_template = REFCHANGE_HEADER_TEMPLATE + self.intro_template = REFCHANGE_INTRO_TEMPLATE + self.footer_template = FOOTER_TEMPLATE + def _compute_values(self): values = Change._compute_values(self) @@ -894,11 +1006,39 @@ class ReferenceChange(Change): return values + def send_single_combined_email(self, known_added_sha1s): + """Determine if a combined refchange/revision email should be sent + + If there is only a single new (non-merge) commit added by a + change, it is useful to combine the ReferenceChange and + Revision emails into one. In such a case, return the single + revision; otherwise, return None. + + This method is overridden in BranchChange.""" + + return None + + def generate_combined_email(self, push, revision, body_filter=None, extra_header_values={}): + """Generate an email describing this change AND specified revision. + + Iterate over the lines (including the header lines) of an + email describing this change. If body_filter is not None, + then use it to filter the lines that are intended for the + email body. + + The extra_header_values field is received as a dict and not as + **kwargs, to allow passing other keyword arguments in the + future (e.g. passing extra values to generate_email_intro() + + This method is overridden in BranchChange.""" + + raise NotImplementedError + def get_subject(self): template = { - 'create' : REF_CREATED_SUBJECT_TEMPLATE, - 'update' : REF_UPDATED_SUBJECT_TEMPLATE, - 'delete' : REF_DELETED_SUBJECT_TEMPLATE, + 'create': REF_CREATED_SUBJECT_TEMPLATE, + 'update': REF_UPDATED_SUBJECT_TEMPLATE, + 'delete': REF_DELETED_SUBJECT_TEMPLATE, }[self.change_type] return self.expand(template) @@ -907,12 +1047,12 @@ class ReferenceChange(Change): extra_values['subject'] = self.get_subject() for line in self.expand_header_lines( - REFCHANGE_HEADER_TEMPLATE, **extra_values - ): + self.header_template, **extra_values + ): yield line def generate_email_intro(self): - for line in self.expand_lines(REFCHANGE_INTRO_TEMPLATE): + for line in self.expand_lines(self.intro_template): yield line def generate_email_body(self, push): @@ -922,9 +1062,9 @@ class ReferenceChange(Change): generate_update_summary() / generate_delete_summary().""" change_summary = { - 'create' : self.generate_create_summary, - 'delete' : self.generate_delete_summary, - 'update' : self.generate_update_summary, + 'create': self.generate_create_summary, + 'delete': self.generate_delete_summary, + 'update': self.generate_update_summary, }[self.change_type](push) for line in change_summary: yield line @@ -933,7 +1073,23 @@ class ReferenceChange(Change): yield line def generate_email_footer(self): - return self.expand_lines(FOOTER_TEMPLATE) + return self.expand_lines(self.footer_template) + + def generate_revision_change_graph(self, push): + if self.showgraph: + args = ['--graph'] + self.graphopts + for newold in ('new', 'old'): + has_newold = False + spec = push.get_commits_spec(newold, self) + for line in git_log(spec, args=args, keepends=True): + if not has_newold: + has_newold = True + yield '\n' + yield 'Graph of %s commits:\n\n' % ( + {'new': 'new', 'old': 'discarded'}[newold],) + yield ' ' + line + if has_newold: + yield '\n' def generate_revision_change_log(self, new_commits_list): if self.showlog: @@ -945,9 +1101,17 @@ class ReferenceChange(Change): + new_commits_list + ['--'], keepends=True, - ): + ): yield line + def generate_new_revision_summary(self, tot, new_commits_list, push): + for line in self.expand_lines(NEW_REVISIONS_TEMPLATE, tot=tot): + yield line + for line in self.generate_revision_change_graph(push): + yield line + for line in self.generate_revision_change_log(new_commits_list): + yield line + def generate_revision_change_summary(self, push): """Generate a summary of the revisions added/removed by this change.""" @@ -960,7 +1124,7 @@ class ReferenceChange(Change): sha1s.reverse() tot = len(sha1s) new_revisions = [ - Revision(self, GitObject(sha1), num=i+1, tot=tot) + Revision(self, GitObject(sha1), num=i + 1, tot=tot) for (i, sha1) in enumerate(sha1s) ] @@ -973,9 +1137,8 @@ class ReferenceChange(Change): BRIEF_SUMMARY_TEMPLATE, action='new', text=subject, ) yield '\n' - for line in self.expand_lines(NEW_REVISIONS_TEMPLATE, tot=tot): - yield line - for line in self.generate_revision_change_log([r.rev.sha1 for r in new_revisions]): + for line in self.generate_new_revision_summary( + tot, [r.rev.sha1 for r in new_revisions], push): yield line else: for line in self.expand_lines(NO_NEW_REVISIONS_TEMPLATE): @@ -993,16 +1156,16 @@ class ReferenceChange(Change): # revisions in the summary even though we will not send # new notification emails for them. adds = list(generate_summaries( - '--topo-order', '--reverse', '%s..%s' - % (self.old.commit_sha1, self.new.commit_sha1,) - )) + '--topo-order', '--reverse', '%s..%s' + % (self.old.commit_sha1, self.new.commit_sha1,) + )) # List of the revisions that were removed from the branch # by this update. This will be empty except for # non-fast-forward updates. discards = list(generate_summaries( - '%s..%s' % (self.new.commit_sha1, self.old.commit_sha1,) - )) + '%s..%s' % (self.new.commit_sha1, self.old.commit_sha1,) + )) if adds: new_commits_list = push.get_new_commits(self) @@ -1071,13 +1234,14 @@ class ReferenceChange(Change): yield '\n' if new_commits: - for line in self.expand_lines(NEW_REVISIONS_TEMPLATE, tot=len(new_commits)): - yield line - for line in self.generate_revision_change_log(new_commits_list): + for line in self.generate_new_revision_summary( + len(new_commits), new_commits_list, push): yield line else: for line in self.expand_lines(NO_NEW_REVISIONS_TEMPLATE): yield line + for line in self.generate_revision_change_graph(push): + yield line # The diffstat is shown from the old revision to the new # revision. This is to show the truth of what happened in @@ -1089,11 +1253,11 @@ class ReferenceChange(Change): yield '\n' yield 'Summary of changes:\n' for line in read_git_lines( - ['diff-tree'] - + self.diffopts - + ['%s..%s' % (self.old.commit_sha1, self.new.commit_sha1,)], - keepends=True, - ): + ['diff-tree'] + + self.diffopts + + ['%s..%s' % (self.old.commit_sha1, self.new.commit_sha1,)], + keepends=True, + ): yield line elif self.old.commit_sha1 and not self.new.commit_sha1: @@ -1103,7 +1267,7 @@ class ReferenceChange(Change): sha1s = list(push.get_discarded_commits(self)) tot = len(sha1s) discarded_revisions = [ - Revision(self, GitObject(sha1), num=i+1, tot=tot) + Revision(self, GitObject(sha1), num=i + 1, tot=tot) for (i, sha1) in enumerate(sha1s) ] @@ -1116,6 +1280,8 @@ class ReferenceChange(Change): yield r.expand( BRIEF_SUMMARY_TEMPLATE, action='discards', text=subject, ) + for line in self.generate_revision_change_graph(push): + yield line else: for line in self.expand_lines(NO_DISCARDED_REVISIONS_TEMPLATE): yield line @@ -1161,6 +1327,150 @@ class BranchChange(ReferenceChange): old=old, new=new, rev=rev, ) self.recipients = environment.get_refchange_recipients(self) + self._single_revision = None + + def send_single_combined_email(self, known_added_sha1s): + if not self.environment.combine_when_single_commit: + return None + + # In the sadly-all-too-frequent usecase of people pushing only + # one of their commits at a time to a repository, users feel + # the reference change summary emails are noise rather than + # important signal. This is because, in this particular + # usecase, there is a reference change summary email for each + # new commit, and all these summaries do is point out that + # there is one new commit (which can readily be inferred by + # the existence of the individual revision email that is also + # sent). In such cases, our users prefer there to be a combined + # reference change summary/new revision email. + # + # So, if the change is an update and it doesn't discard any + # commits, and it adds exactly one non-merge commit (gerrit + # forces a workflow where every commit is individually merged + # and the git-multimail hook fired off for just this one + # change), then we send a combined refchange/revision email. + try: + # If this change is a reference update that doesn't discard + # any commits... + if self.change_type != 'update': + return None + + if read_git_lines( + ['merge-base', self.old.sha1, self.new.sha1] + ) != [self.old.sha1]: + return None + + # Check if this update introduced exactly one non-merge + # commit: + + def split_line(line): + """Split line into (sha1, [parent,...]).""" + + words = line.split() + return (words[0], words[1:]) + + # Get the new commits introduced by the push as a list of + # (sha1, [parent,...]) + new_commits = [ + split_line(line) + for line in read_git_lines( + [ + 'log', '-3', '--format=%H %P', + '%s..%s' % (self.old.sha1, self.new.sha1), + ] + ) + ] + + if not new_commits: + return None + + # If the newest commit is a merge, save it for a later check + # but otherwise ignore it + merge = None + tot = len(new_commits) + if len(new_commits[0][1]) > 1: + merge = new_commits[0][0] + del new_commits[0] + + # Our primary check: we can't combine if more than one commit + # is introduced. We also currently only combine if the new + # commit is a non-merge commit, though it may make sense to + # combine if it is a merge as well. + if not ( + len(new_commits) == 1 + and len(new_commits[0][1]) == 1 + and new_commits[0][0] in known_added_sha1s + ): + return None + + # We do not want to combine revision and refchange emails if + # those go to separate locations. + rev = Revision(self, GitObject(new_commits[0][0]), 1, tot) + if rev.recipients != self.recipients: + return None + + # We ignored the newest commit if it was just a merge of the one + # commit being introduced. But we don't want to ignore that + # merge commit it it involved conflict resolutions. Check that. + if merge and merge != read_git_output(['diff-tree', '--cc', merge]): + return None + + # We can combine the refchange and one new revision emails + # into one. Return the Revision that a combined email should + # be sent about. + return rev + except CommandError: + # Cannot determine number of commits in old..new or new..old; + # don't combine reference/revision emails: + return None + + def generate_combined_email(self, push, revision, body_filter=None, extra_header_values={}): + values = revision.get_values() + if extra_header_values: + values.update(extra_header_values) + if 'subject' not in extra_header_values: + values['subject'] = self.expand(COMBINED_REFCHANGE_REVISION_SUBJECT_TEMPLATE, **values) + + self._single_revision = revision + self.header_template = COMBINED_HEADER_TEMPLATE + self.intro_template = COMBINED_INTRO_TEMPLATE + self.footer_template = COMBINED_FOOTER_TEMPLATE + for line in self.generate_email(push, body_filter, values): + yield line + + def generate_email_body(self, push): + '''Call the appropriate body generation routine. + + If this is a combined refchange/revision email, the special logic + for handling this combined email comes from this function. For + other cases, we just use the normal handling.''' + + # If self._single_revision isn't set; don't override + if not self._single_revision: + for line in super(BranchChange, self).generate_email_body(push): + yield line + return + + # This is a combined refchange/revision email; we first provide + # some info from the refchange portion, and then call the revision + # generate_email_body function to handle the revision portion. + adds = list(generate_summaries( + '--topo-order', '--reverse', '%s..%s' + % (self.old.commit_sha1, self.new.commit_sha1,) + )) + + yield self.expand("The following commit(s) were added to %(refname)s by this push:\n") + for (sha1, subject) in adds: + yield self.expand( + BRIEF_SUMMARY_TEMPLATE, action='new', + rev_short=sha1, text=subject, + ) + + yield self._single_revision.rev.short + " is described below\n" + yield '\n' + + for line in self._single_revision.generate_email_body(push): + yield line class AnnotatedTagChange(ReferenceChange): @@ -1390,13 +1700,17 @@ class SendMailer(Mailer): sys.exit(1) try: p.stdin.writelines(lines) - except: + except Exception, e: sys.stderr.write( '*** Error while generating commit email\n' '*** - mail sending aborted.\n' ) - p.terminate() - raise + try: + # subprocess.terminate() is not available in Python 2.4 + p.terminate() + except AttributeError: + pass + raise e else: p.stdin.close() retcode = p.wait() @@ -1407,34 +1721,78 @@ class SendMailer(Mailer): class SMTPMailer(Mailer): """Send emails using Python's smtplib.""" - def __init__(self, envelopesender, smtpserver): + def __init__(self, envelopesender, smtpserver, + smtpservertimeout=10.0, smtpserverdebuglevel=0, + smtpencryption='none', + smtpuser='', smtppass='', + ): if not envelopesender: sys.stderr.write( 'fatal: git_multimail: cannot use SMTPMailer without a sender address.\n' 'please set either multimailhook.envelopeSender or user.email\n' ) sys.exit(1) + if smtpencryption == 'ssl' and not (smtpuser and smtppass): + raise ConfigurationException( + 'Cannot use SMTPMailer with security option ssl ' + 'without options username and password.' + ) self.envelopesender = envelopesender self.smtpserver = smtpserver + self.smtpservertimeout = smtpservertimeout + self.smtpserverdebuglevel = smtpserverdebuglevel + self.security = smtpencryption + self.username = smtpuser + self.password = smtppass try: - self.smtp = smtplib.SMTP(self.smtpserver) + def call(klass, server, timeout): + try: + return klass(server, timeout=timeout) + except TypeError: + # Old Python versions do not have timeout= argument. + return klass(server) + if self.security == 'none': + self.smtp = call(smtplib.SMTP, self.smtpserver, timeout=self.smtpservertimeout) + elif self.security == 'ssl': + self.smtp = call(smtplib.SMTP_SSL, self.smtpserver, timeout=self.smtpservertimeout) + elif self.security == 'tls': + if ':' not in self.smtpserver: + self.smtpserver += ':587' # default port for TLS + self.smtp = call(smtplib.SMTP, self.smtpserver, timeout=self.smtpservertimeout) + self.smtp.ehlo() + self.smtp.starttls() + self.smtp.ehlo() + else: + sys.stdout.write('*** Error: Control reached an invalid option. ***') + sys.exit(1) + if self.smtpserverdebuglevel > 0: + sys.stdout.write( + "*** Setting debug on for SMTP server connection (%s) ***\n" + % self.smtpserverdebuglevel) + self.smtp.set_debuglevel(self.smtpserverdebuglevel) except Exception, e: - sys.stderr.write('*** Error establishing SMTP connection to %s***\n' % self.smtpserver) + sys.stderr.write( + '*** Error establishing SMTP connection to %s ***\n' + % self.smtpserver) sys.stderr.write('*** %s\n' % str(e)) sys.exit(1) def __del__(self): - self.smtp.quit() + if hasattr(self, 'smtp'): + self.smtp.quit() def send(self, lines, to_addrs): try: + if self.username or self.password: + sys.stderr.write("*** Authenticating as %s ***\n" % self.username) + self.smtp.login(self.username, self.password) msg = ''.join(lines) # turn comma-separated list into Python list if needed. if isinstance(to_addrs, basestring): to_addrs = [email for (name, email) in getaddresses([to_addrs])] self.smtp.sendmail(self.envelopesender, to_addrs, msg) except Exception, e: - sys.stderr.write('*** Error sending email***\n') + sys.stderr.write('*** Error sending email ***\n') sys.stderr.write('*** %s\n' % str(e)) self.smtp.quit() sys.exit(1) @@ -1549,6 +1907,10 @@ class Environment(object): True iff announce emails should include a shortlog. + refchange_showgraph (bool) + + True iff refchanges emails should include a detailed graph. + refchange_showlog (bool) True iff refchanges emails should include a detailed log. @@ -1559,6 +1921,12 @@ class Environment(object): summary email. The value should be a list of strings representing words to be passed to the command. + graphopts (list of strings) + + Analogous to diffopts, but contains options passed to + 'git log --graph' when generating the detailed graph for + a set of commits (see refchange_showgraph) + logopts (list of strings) Analogous to diffopts, but contains options passed to @@ -1571,6 +1939,17 @@ class Environment(object): commit mail. The value should be a list of strings representing words to be passed to the command. + quiet (bool) + On success do not write to stderr + + stdout (bool) + Write email to stdout rather than emailing. Useful for debugging + + combine_when_single_commit (bool) + + True if a combined email should be produced when a single + new commit is pushed to a branch, False otherwise. + """ REPO_NAME_RE = re.compile(r'^(?P<name>.+?)(?:\.git)$') @@ -1580,9 +1959,14 @@ class Environment(object): self.announce_show_shortlog = False self.maxcommitemails = 500 self.diffopts = ['--stat', '--summary', '--find-copies-harder'] + self.graphopts = ['--oneline', '--decorate'] self.logopts = [] + self.refchange_showgraph = False self.refchange_showlog = False self.commitlogopts = ['-C', '--stat', '-p', '--cc'] + self.quiet = False + self.stdout = False + self.combine_when_single_commit = True self.COMPUTED_KEYS = [ 'administrator', @@ -1614,6 +1998,14 @@ class Environment(object): def get_pusher_email(self): return None + def get_fromaddr(self): + config = Config('user') + fromname = config.get('name', default='') + fromemail = config.get('email', default='') + if fromemail: + return formataddr([fromname, fromemail]) + return self.get_sender() + def get_administrator(self): return 'the administrator of this repository' @@ -1631,7 +2023,7 @@ class Environment(object): return CHARSET def get_values(self): - """Return a dictionary {keyword : expansion} for this Environment. + """Return a dictionary {keyword: expansion} for this Environment. This method is called by Change._compute_values(). The keys in the returned dictionary are available to be used in any of @@ -1699,6 +2091,24 @@ class Environment(object): return lines + def log_msg(self, msg): + """Write the string msg on a log file or on stderr. + + Sends the text to stderr by default, override to change the behavior.""" + sys.stderr.write(msg) + + def log_warning(self, msg): + """Write the string msg on a log file or on stderr. + + Sends the text to stderr by default, override to change the behavior.""" + sys.stderr.write(msg) + + def log_error(self, msg): + """Write the string msg on a log file or on stderr. + + Sends the text to stderr by default, override to change the behavior.""" + sys.stderr.write(msg) + class ConfigEnvironmentMixin(Environment): """A mixin that sets self.config to its constructor's config argument. @@ -1723,20 +2133,23 @@ class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin): config=config, **kw ) - self.announce_show_shortlog = config.get_bool( - 'announceshortlog', default=self.announce_show_shortlog - ) - - self.refchange_showlog = config.get_bool( - 'refchangeshowlog', default=self.refchange_showlog - ) + for var, cfg in ( + ('announce_show_shortlog', 'announceshortlog'), + ('refchange_showgraph', 'refchangeShowGraph'), + ('refchange_showlog', 'refchangeshowlog'), + ('quiet', 'quiet'), + ('stdout', 'stdout'), + ): + val = config.get_bool(cfg) + if val is not None: + setattr(self, var, val) maxcommitemails = config.get('maxcommitemails') if maxcommitemails is not None: try: self.maxcommitemails = int(maxcommitemails) except ValueError: - sys.stderr.write( + self.log_warning( '*** Malformed value for multimailhook.maxCommitEmails: %s\n' % maxcommitemails + '*** Expected a number. Ignoring.\n' ) @@ -1745,6 +2158,10 @@ class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin): if diffopts is not None: self.diffopts = shlex.split(diffopts) + graphopts = config.get('graphOpts') + if graphopts is not None: + self.graphopts = shlex.split(graphopts) + logopts = config.get('logopts') if logopts is not None: self.logopts = shlex.split(logopts) @@ -1756,14 +2173,18 @@ class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin): reply_to = config.get('replyTo') self.__reply_to_refchange = config.get('replyToRefchange', default=reply_to) if ( - self.__reply_to_refchange is not None - and self.__reply_to_refchange.lower() == 'author' - ): + self.__reply_to_refchange is not None + and self.__reply_to_refchange.lower() == 'author' + ): raise ConfigurationException( '"author" is not an allowed setting for replyToRefchange' ) self.__reply_to_commit = config.get('replyToCommit', default=reply_to) + combine = config.get_bool('combineWhenSingleCommit') + if combine is not None: + self.combine_when_single_commit = combine + def get_administrator(self): return ( self.config.get('administrator') @@ -1779,8 +2200,12 @@ class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin): def get_emailprefix(self): emailprefix = self.config.get('emailprefix') - if emailprefix and emailprefix.strip(): - return emailprefix.strip() + ' ' + if emailprefix is not None: + emailprefix = emailprefix.strip() + if emailprefix: + return emailprefix + ' ' + else: + return '' else: return '[%s] ' % (self.get_repo_shortname(),) @@ -1791,14 +2216,7 @@ class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin): fromaddr = self.config.get('from') if fromaddr: return fromaddr - else: - config = Config('user') - fromname = config.get('name', default='') - fromemail = config.get('email', default='') - if fromemail: - return formataddr([fromname, fromemail]) - else: - return self.get_sender() + return super(ConfigOptionsEnvironmentMixin, self).get_fromaddr() def get_reply_to_refchange(self, refchange): if self.__reply_to_refchange is None: @@ -1814,7 +2232,7 @@ class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin): if self.__reply_to_commit is None: return super(ConfigOptionsEnvironmentMixin, self).get_reply_to_commit(revision) elif self.__reply_to_commit.lower() == 'author': - return revision.get_author() + return revision.author elif self.__reply_to_commit.lower() == 'pusher': return self.get_pusher_email() elif self.__reply_to_commit.lower() == 'none': @@ -1822,6 +2240,9 @@ class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin): else: return self.__reply_to_commit + def get_scancommitforcc(self): + return self.config.get('scancommitforcc') + class FilterLinesEnvironmentMixin(Environment): """Handle encoding and maximum line length of body lines. @@ -1862,9 +2283,9 @@ class FilterLinesEnvironmentMixin(Environment): class ConfigFilterLinesEnvironmentMixin( - ConfigEnvironmentMixin, - FilterLinesEnvironmentMixin, - ): + ConfigEnvironmentMixin, + FilterLinesEnvironmentMixin, + ): """Handle encoding and maximum line length based on config.""" def __init__(self, config, **kw): @@ -1896,9 +2317,9 @@ class MaxlinesEnvironmentMixin(Environment): class ConfigMaxlinesEnvironmentMixin( - ConfigEnvironmentMixin, - MaxlinesEnvironmentMixin, - ): + ConfigEnvironmentMixin, + MaxlinesEnvironmentMixin, + ): """Limit the email body to the number of lines specified in config.""" def __init__(self, config, **kw): @@ -1927,9 +2348,9 @@ class FQDNEnvironmentMixin(Environment): class ConfigFQDNEnvironmentMixin( - ConfigEnvironmentMixin, - FQDNEnvironmentMixin, - ): + ConfigEnvironmentMixin, + FQDNEnvironmentMixin, + ): """Read the FQDN from the config.""" def __init__(self, config, **kw): @@ -1970,10 +2391,10 @@ class StaticRecipientsEnvironmentMixin(Environment): """Set recipients statically based on constructor parameters.""" def __init__( - self, - refchange_recipients, announce_recipients, revision_recipients, - **kw - ): + self, + refchange_recipients, announce_recipients, revision_recipients, scancommitforcc, + **kw + ): super(StaticRecipientsEnvironmentMixin, self).__init__(**kw) # The recipients for various types of notification emails, as @@ -1985,7 +2406,8 @@ class StaticRecipientsEnvironmentMixin(Environment): # compute them once and for all: if not (refchange_recipients or announce_recipients - or revision_recipients): + or revision_recipients + or scancommitforcc): raise ConfigurationException('No email recipients configured!') self.__refchange_recipients = refchange_recipients self.__announce_recipients = announce_recipients @@ -2002,9 +2424,9 @@ class StaticRecipientsEnvironmentMixin(Environment): class ConfigRecipientsEnvironmentMixin( - ConfigEnvironmentMixin, - StaticRecipientsEnvironmentMixin - ): + ConfigEnvironmentMixin, + StaticRecipientsEnvironmentMixin + ): """Determine recipients statically based on config.""" def __init__(self, config, **kw): @@ -2019,6 +2441,7 @@ class ConfigRecipientsEnvironmentMixin( revision_recipients=self._get_recipients( config, 'commitlist', 'mailinglist', ), + scancommitforcc=config.get('scancommitforcc'), **kw ) @@ -2067,20 +2490,20 @@ class ProjectdescEnvironmentMixin(Environment): class GenericEnvironmentMixin(Environment): def get_pusher(self): - return self.osenv.get('USER', 'unknown user') + return self.osenv.get('USER', self.osenv.get('USERNAME', 'unknown user')) class GenericEnvironment( - ProjectdescEnvironmentMixin, - ConfigMaxlinesEnvironmentMixin, - ComputeFQDNEnvironmentMixin, - ConfigFilterLinesEnvironmentMixin, - ConfigRecipientsEnvironmentMixin, - PusherDomainEnvironmentMixin, - ConfigOptionsEnvironmentMixin, - GenericEnvironmentMixin, - Environment, - ): + ProjectdescEnvironmentMixin, + ConfigMaxlinesEnvironmentMixin, + ComputeFQDNEnvironmentMixin, + ConfigFilterLinesEnvironmentMixin, + ConfigRecipientsEnvironmentMixin, + PusherDomainEnvironmentMixin, + ConfigOptionsEnvironmentMixin, + GenericEnvironmentMixin, + Environment, + ): pass @@ -2097,6 +2520,45 @@ class GitoliteEnvironmentMixin(Environment): def get_pusher(self): return self.osenv.get('GL_USER', 'unknown user') + def get_fromaddr(self): + GL_USER = self.osenv.get('GL_USER') + if GL_USER is not None: + # Find the path to gitolite.conf. Note that gitolite v3 + # did away with the GL_ADMINDIR and GL_CONF environment + # variables (they are now hard-coded). + GL_ADMINDIR = self.osenv.get( + 'GL_ADMINDIR', + os.path.expanduser(os.path.join('~', '.gitolite'))) + GL_CONF = self.osenv.get( + 'GL_CONF', + os.path.join(GL_ADMINDIR, 'conf', 'gitolite.conf')) + if os.path.isfile(GL_CONF): + f = open(GL_CONF, 'rU') + try: + in_user_emails_section = False + re_template = r'^\s*#\s*{}\s*$' + re_begin, re_user, re_end = ( + re.compile(re_template.format(x)) + for x in ( + r'BEGIN\s+USER\s+EMAILS', + re.escape(GL_USER) + r'\s+(.*)', + r'END\s+USER\s+EMAILS', + )) + for l in f: + l = l.rstrip('\n') + if not in_user_emails_section: + if re_begin.match(l): + in_user_emails_section = True + continue + if re_end.match(l): + break + m = re_user.match(l) + if m: + return m.group(1) + finally: + f.close() + return super(GitoliteEnvironmentMixin, self).get_fromaddr() + class IncrementalDateTime(object): """Simple wrapper to give incremental date/times. @@ -2116,16 +2578,16 @@ class IncrementalDateTime(object): class GitoliteEnvironment( - ProjectdescEnvironmentMixin, - ConfigMaxlinesEnvironmentMixin, - ComputeFQDNEnvironmentMixin, - ConfigFilterLinesEnvironmentMixin, - ConfigRecipientsEnvironmentMixin, - PusherDomainEnvironmentMixin, - ConfigOptionsEnvironmentMixin, - GitoliteEnvironmentMixin, - Environment, - ): + ProjectdescEnvironmentMixin, + ConfigMaxlinesEnvironmentMixin, + ComputeFQDNEnvironmentMixin, + ConfigFilterLinesEnvironmentMixin, + ConfigRecipientsEnvironmentMixin, + PusherDomainEnvironmentMixin, + ConfigOptionsEnvironmentMixin, + GitoliteEnvironmentMixin, + Environment, + ): pass @@ -2149,9 +2611,9 @@ class Push(object): references. The first step is to determine the "other" references--those - unaffected by the current push. They are computed by - Push._compute_other_ref_sha1s() by listing all references then - removing any affected by this push. + unaffected by the current push. They are computed by listing all + references then removing any affected by this push. The results + are stored in Push._other_ref_sha1s. The commits contained in the repository before this push were @@ -2187,7 +2649,7 @@ class Push(object): possible and working with SHA1s thereafter (because SHA1s are immutable).""" - # A map {(changeclass, changetype) : integer} specifying the order + # A map {(changeclass, changetype): integer} specifying the order # that reference changes will be processed if multiple reference # changes are included in a single push. The order is significant # mostly because new commit notifications are threaded together @@ -2211,66 +2673,134 @@ class Push(object): ]) ) - def __init__(self, changes): + def __init__(self, changes, ignore_other_refs=False): self.changes = sorted(changes, key=self._sort_key) + self.__other_ref_sha1s = None + self.__cached_commits_spec = {} - # The SHA-1s of commits referred to by references unaffected - # by this push: - other_ref_sha1s = self._compute_other_ref_sha1s() + if ignore_other_refs: + self.__other_ref_sha1s = set() - self._old_rev_exclusion_spec = self._compute_rev_exclusion_spec( - other_ref_sha1s.union( - change.old.sha1 + @classmethod + def _sort_key(klass, change): + return (klass.SORT_ORDER[change.__class__, change.change_type], change.refname,) + + @property + def _other_ref_sha1s(self): + """The GitObjects referred to by references unaffected by this push. + """ + if self.__other_ref_sha1s is None: + # The refnames being changed by this push: + updated_refs = set( + change.refname for change in self.changes - if change.old.type in ['commit', 'tag'] ) - ) - self._new_rev_exclusion_spec = self._compute_rev_exclusion_spec( - other_ref_sha1s.union( - change.new.sha1 - for change in self.changes - if change.new.type in ['commit', 'tag'] + + # The SHA-1s of commits referred to by all references in this + # repository *except* updated_refs: + sha1s = set() + fmt = ( + '%(objectname) %(objecttype) %(refname)\n' + '%(*objectname) %(*objecttype) %(refname)' ) - ) + for line in read_git_lines( + ['for-each-ref', '--format=%s' % (fmt,)]): + (sha1, type, name) = line.split(' ', 2) + if sha1 and type == 'commit' and name not in updated_refs: + sha1s.add(sha1) - @classmethod - def _sort_key(klass, change): - return (klass.SORT_ORDER[change.__class__, change.change_type], change.refname,) + self.__other_ref_sha1s = sha1s + + return self.__other_ref_sha1s + + def _get_commits_spec_incl(self, new_or_old, reference_change=None): + """Get new or old SHA-1 from one or each of the changed refs. - def _compute_other_ref_sha1s(self): - """Return the GitObjects referred to by references unaffected by this push.""" + Return a list of SHA-1 commit identifier strings suitable as + arguments to 'git rev-list' (or 'git log' or ...). The + returned identifiers are either the old or new values from one + or all of the changed references, depending on the values of + new_or_old and reference_change. - # The refnames being changed by this push: - updated_refs = set( - change.refname + new_or_old is either the string 'new' or the string 'old'. If + 'new', the returned SHA-1 identifiers are the new values from + each changed reference. If 'old', the SHA-1 identifiers are + the old values from each changed reference. + + If reference_change is specified and not None, only the new or + old reference from the specified reference is included in the + return value. + + This function returns None if there are no matching revisions + (e.g., because a branch was deleted and new_or_old is 'new'). + """ + + if not reference_change: + incl_spec = sorted( + getattr(change, new_or_old).sha1 + for change in self.changes + if getattr(change, new_or_old) + ) + if not incl_spec: + incl_spec = None + elif not getattr(reference_change, new_or_old).commit_sha1: + incl_spec = None + else: + incl_spec = [getattr(reference_change, new_or_old).commit_sha1] + return incl_spec + + def _get_commits_spec_excl(self, new_or_old): + """Get exclusion revisions for determining new or discarded commits. + + Return a list of strings suitable as arguments to 'git + rev-list' (or 'git log' or ...) that will exclude all + commits that, depending on the value of new_or_old, were + either previously in the repository (useful for determining + which commits are new to the repository) or currently in the + repository (useful for determining which commits were + discarded from the repository). + + new_or_old is either the string 'new' or the string 'old'. If + 'new', the commits to be excluded are those that were in the + repository before the push. If 'old', the commits to be + excluded are those that are currently in the repository. """ + + old_or_new = {'old': 'new', 'new': 'old'}[new_or_old] + excl_revs = self._other_ref_sha1s.union( + getattr(change, old_or_new).sha1 for change in self.changes + if getattr(change, old_or_new).type in ['commit', 'tag'] ) + return ['^' + sha1 for sha1 in sorted(excl_revs)] - # The SHA-1s of commits referred to by all references in this - # repository *except* updated_refs: - sha1s = set() - fmt = ( - '%(objectname) %(objecttype) %(refname)\n' - '%(*objectname) %(*objecttype) %(refname)' - ) - for line in read_git_lines(['for-each-ref', '--format=%s' % (fmt,)]): - (sha1, type, name) = line.split(' ', 2) - if sha1 and type == 'commit' and name not in updated_refs: - sha1s.add(sha1) + def get_commits_spec(self, new_or_old, reference_change=None): + """Get rev-list arguments for added or discarded commits. - return sha1s + Return a list of strings suitable as arguments to 'git + rev-list' (or 'git log' or ...) that select those commits + that, depending on the value of new_or_old, are either new to + the repository or were discarded from the repository. - def _compute_rev_exclusion_spec(self, sha1s): - """Return an exclusion specification for 'git rev-list'. + new_or_old is either the string 'new' or the string 'old'. If + 'new', the returned list is used to select commits that are + new to the repository. If 'old', the returned value is used + to select the commits that have been discarded from the + repository. - git_objects is an iterable over GitObject instances. Return a - string that can be passed to the standard input of 'git - rev-list --stdin' to exclude all of the commits referred to by - git_objects.""" + If reference_change is specified and not None, the new or + discarded commits are limited to those that are reachable from + the new or old value of the specified reference. - return ''.join( - ['^%s\n' % (sha1,) for sha1 in sorted(sha1s)] - ) + This function returns None if there are no added (or discarded) + revisions. + """ + key = (new_or_old, reference_change) + if key not in self.__cached_commits_spec: + ret = self._get_commits_spec_incl(new_or_old, reference_change) + if ret is not None: + ret.extend(self._get_commits_spec_excl(new_or_old)) + self.__cached_commits_spec[key] = ret + return self.__cached_commits_spec[key] def get_new_commits(self, reference_change=None): """Return a list of commits added by this push. @@ -2280,19 +2810,8 @@ class Push(object): reference_change is None, then return a list of *all* commits added by this push.""" - if not reference_change: - new_revs = sorted( - change.new.sha1 - for change in self.changes - if change.new - ) - elif not reference_change.new.commit_sha1: - return [] - else: - new_revs = [reference_change.new.commit_sha1] - - cmd = ['rev-list', '--stdin'] + new_revs - return read_git_lines(cmd, input=self._old_rev_exclusion_spec) + spec = self.get_commits_spec('new', reference_change) + return git_rev_list(spec) def get_discarded_commits(self, reference_change): """Return a list of commits discarded by this push. @@ -2301,13 +2820,8 @@ class Push(object): entirely discarded from the repository by the part of this push represented by reference_change.""" - if not reference_change.old.commit_sha1: - return [] - else: - old_revs = [reference_change.old.commit_sha1] - - cmd = ['rev-list', '--stdin'] + old_revs - return read_git_lines(cmd, input=self._new_rev_exclusion_spec) + spec = self.get_commits_spec('old', reference_change) + return git_rev_list(spec) def send_emails(self, mailer, body_filter=None): """Use send all of the notification emails needed for this push. @@ -2325,30 +2839,43 @@ class Push(object): unhandled_sha1s = set(self.get_new_commits()) send_date = IncrementalDateTime() for change in self.changes: + sha1s = [] + for sha1 in reversed(list(self.get_new_commits(change))): + if sha1 in unhandled_sha1s: + sha1s.append(sha1) + unhandled_sha1s.remove(sha1) + # Check if we've got anyone to send to if not change.recipients: - sys.stderr.write( + change.environment.log_warning( '*** no recipients configured so no email will be sent\n' '*** for %r update %s->%s\n' % (change.refname, change.old.sha1, change.new.sha1,) ) else: - sys.stderr.write('Sending notification emails to: %s\n' % (change.recipients,)) - extra_values = {'send_date' : send_date.next()} - mailer.send( - change.generate_email(self, body_filter, extra_values), - change.recipients, - ) + if not change.environment.quiet: + change.environment.log_msg( + 'Sending notification emails to: %s\n' % (change.recipients,)) + extra_values = {'send_date': send_date.next()} - sha1s = [] - for sha1 in reversed(list(self.get_new_commits(change))): - if sha1 in unhandled_sha1s: - sha1s.append(sha1) - unhandled_sha1s.remove(sha1) + rev = change.send_single_combined_email(sha1s) + if rev: + mailer.send( + change.generate_combined_email(self, rev, body_filter, extra_values), + rev.recipients, + ) + # This change is now fully handled; no need to handle + # individual revisions any further. + continue + else: + mailer.send( + change.generate_email(self, body_filter, extra_values), + change.recipients, + ) max_emails = change.environment.maxcommitemails if max_emails and len(sha1s) > max_emails: - sys.stderr.write( + change.environment.log_warning( '*** Too many new commits (%d), not sending commit emails.\n' % len(sha1s) + '*** Try setting multimailhook.maxCommitEmails to a greater value\n' + '*** Currently, multimailhook.maxCommitEmails=%d\n' % max_emails @@ -2356,9 +2883,13 @@ class Push(object): return for (num, sha1) in enumerate(sha1s): - rev = Revision(change, GitObject(sha1), num=num+1, tot=len(sha1s)) + rev = Revision(change, GitObject(sha1), num=num + 1, tot=len(sha1s)) + if not rev.recipients and rev.cc_recipients: + change.environment.log_msg('*** Replacing Cc: with To:\n') + rev.recipients = rev.cc_recipients + rev.cc_recipients = None if rev.recipients: - extra_values = {'send_date' : send_date.next()} + extra_values = {'send_date': send_date.next()} mailer.send( rev.generate_email(self, body_filter, extra_values), rev.recipients, @@ -2366,7 +2897,7 @@ class Push(object): # Consistency check: if unhandled_sha1s: - sys.stderr.write( + change.environment.log_error( 'ERROR: No emails were sent for the following new commits:\n' ' %s\n' % ('\n '.join(sorted(unhandled_sha1s)),) @@ -2384,7 +2915,7 @@ def run_as_post_receive_hook(environment, mailer): push.send_emails(mailer, body_filter=environment.filter_body) -def run_as_update_hook(environment, mailer, refname, oldrev, newrev): +def run_as_update_hook(environment, mailer, refname, oldrev, newrev, force_send=False): changes = [ ReferenceChange.create( environment, @@ -2393,7 +2924,7 @@ def run_as_update_hook(environment, mailer, refname, oldrev, newrev): refname, ), ] - push = Push(changes) + push = Push(changes, force_send) push.send_emails(mailer, body_filter=environment.filter_body) @@ -2402,9 +2933,18 @@ def choose_mailer(config, environment): if mailer == 'smtp': smtpserver = config.get('smtpserver', default='localhost') + smtpservertimeout = float(config.get('smtpservertimeout', default=10.0)) + smtpserverdebuglevel = int(config.get('smtpserverdebuglevel', default=0)) + smtpencryption = config.get('smtpencryption', default='none') + smtpuser = config.get('smtpuser', default='') + smtppass = config.get('smtppass', default='') mailer = SMTPMailer( envelopesender=(environment.get_sender() or environment.get_fromaddr()), - smtpserver=smtpserver, + smtpserver=smtpserver, smtpservertimeout=smtpservertimeout, + smtpserverdebuglevel=smtpserverdebuglevel, + smtpencryption=smtpencryption, + smtpuser=smtpuser, + smtppass=smtppass, ) elif mailer == 'sendmail': command = config.get('sendmailcommand') @@ -2412,7 +2952,7 @@ def choose_mailer(config, environment): command = shlex.split(command) mailer = SendMailer(command=command, envelopesender=environment.get_sender()) else: - sys.stderr.write( + environment.log_error( 'fatal: multimailhook.mailer is set to an incorrect value: "%s"\n' % mailer + 'please use one of "smtp" or "sendmail".\n' ) @@ -2421,8 +2961,8 @@ def choose_mailer(config, environment): KNOWN_ENVIRONMENTS = { - 'generic' : GenericEnvironmentMixin, - 'gitolite' : GitoliteEnvironmentMixin, + 'generic': GenericEnvironmentMixin, + 'gitolite': GitoliteEnvironmentMixin, } @@ -2439,8 +2979,8 @@ def choose_environment(config, osenv=None, env=None, recipients=None): ConfigOptionsEnvironmentMixin, ] environment_kw = { - 'osenv' : osenv, - 'config' : config, + 'osenv': osenv, + 'config': config, } if not env: @@ -2459,6 +2999,7 @@ def choose_environment(config, osenv=None, env=None, recipients=None): environment_kw['refchange_recipients'] = recipients environment_kw['announce_recipients'] = recipients environment_kw['revision_recipients'] = recipients + environment_kw['scancommitforcc'] = config.get('scancommitforcc') else: environment_mixins.insert(0, ConfigRecipientsEnvironmentMixin) @@ -2499,6 +3040,14 @@ def main(args): '(intended for debugging purposes).' ), ) + parser.add_option( + '--force-send', action='store_true', default=False, + help=( + 'Force sending refchange email when using as an update hook. ' + 'This is useful to work around the unreliable new commits ' + 'detection in this mode.' + ), + ) (options, args) = parser.parse_args(args) @@ -2513,11 +3062,11 @@ def main(args): if options.show_env: sys.stderr.write('Environment values:\n') - for (k,v) in sorted(environment.get_values().items()): - sys.stderr.write(' %s : %r\n' % (k,v)) + for (k, v) in sorted(environment.get_values().items()): + sys.stderr.write(' %s : %r\n' % (k, v)) sys.stderr.write('\n') - if options.stdout: + if options.stdout or environment.stdout: mailer = OutputMailer(sys.stdout) else: mailer = choose_mailer(config, environment) @@ -2528,7 +3077,7 @@ def main(args): if len(args) != 3: parser.error('Need zero or three non-option arguments') (refname, oldrev, newrev) = args - run_as_update_hook(environment, mailer, refname, oldrev, newrev) + run_as_update_hook(environment, mailer, refname, oldrev, newrev, options.force_send) else: run_as_post_receive_hook(environment, mailer) except ConfigurationException, e: diff --git a/contrib/hooks/multimail/migrate-mailhook-config b/contrib/hooks/multimail/migrate-mailhook-config index 04eeaac..d0e9b39 100755 --- a/contrib/hooks/multimail/migrate-mailhook-config +++ b/contrib/hooks/multimail/migrate-mailhook-config @@ -22,6 +22,7 @@ OLD_NAMES = [ 'showrev', 'emailmaxlines', 'diffopts', + 'scancommitforcc', ] NEW_NAMES = [ @@ -38,6 +39,7 @@ NEW_NAMES = [ 'emailmaxlines', 'diffopts', 'emaildomain', + 'scancommitforcc', ] @@ -61,7 +63,7 @@ def _check_old_config_exists(old): """Check that at least one old configuration value is set.""" for name in OLD_NAMES: - if old.has_key(name): + if name in old: return True return False @@ -72,7 +74,7 @@ def _check_new_config_clear(new): retval = True for name in NEW_NAMES: - if new.has_key(name): + if name in new: if retval: sys.stderr.write('INFO: The following configuration values already exist:\n\n') sys.stderr.write(' "%s.%s"\n' % (new.section, name)) @@ -83,7 +85,7 @@ def _check_new_config_clear(new): def erase_values(config, names): for name in names: - if config.has_key(name): + if name in config: try: sys.stderr.write('...unsetting "%s.%s"\n' % (config.section, name)) config.unset_all(name) @@ -170,7 +172,7 @@ def migrate_config(strict=False, retain=False, overwrite=False): ) name = 'showrev' - if old.has_key(name): + if name in old: msg = 'git-multimail does not support "%s.%s"' % (old.section, name,) if strict: sys.exit( @@ -182,7 +184,7 @@ def migrate_config(strict=False, retain=False, overwrite=False): sys.stderr.write('\nWARNING: %s (ignoring).\n\n' % (msg,)) for name in ['mailinglist', 'announcelist']: - if old.has_key(name): + if name in old: sys.stderr.write( '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name) ) @@ -198,15 +200,15 @@ def migrate_config(strict=False, retain=False, overwrite=False): ) new.set('announceshortlog', 'true') - for name in ['envelopesender', 'emailmaxlines', 'diffopts']: - if old.has_key(name): + for name in ['envelopesender', 'emailmaxlines', 'diffopts', 'scancommitforcc']: + if name in old: sys.stderr.write( '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name) ) new.set(name, old.get(name)) name = 'emailprefix' - if old.has_key(name): + if name in old: sys.stderr.write( '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name) ) diff --git a/contrib/hooks/multimail/post-receive b/contrib/hooks/multimail/post-receive.example index 4d46828..43f7b6b 100755 --- a/contrib/hooks/multimail/post-receive +++ b/contrib/hooks/multimail/post-receive.example @@ -2,16 +2,18 @@ """Example post-receive hook based on git-multimail. -This script is a simple example of a post-receive hook implemented -using git_multimail.py as a Python module. It is intended to be -customized before use; see the comments in the script to help you get -started. +The simplest way to use git-multimail is to use the script +git_multimail.py directly as a post-receive hook, and to configure it +using Git's configuration files and command-line parameters. You can +also write your own Python wrapper for more advanced configurability, +using git_multimail.py as a Python module. -It is possible to use git_multimail.py itself as a post-receive or -update hook, configured via git config settings and/or command-line -parameters. But for more flexibility, it can also be imported as a -Python module by a custom post-receive script as done here. The -latter has the following advantages: +This script is a simple example of such a post-receive hook. It is +intended to be customized before use; see the comments in the script +to help you get started. + +Using git-multimail as a Python module as done here provides more +flexibility. It has the following advantages: * The tool's behavior can be customized using arbitrary Python code, without having to edit git_multimail.py. @@ -56,8 +58,11 @@ config = git_multimail.Config('multimailhook') # Select the type of environment: -environment = git_multimail.GenericEnvironment(config=config) -#environment = git_multimail.GitoliteEnvironment(config=config) +try: + environment = git_multimail.GenericEnvironment(config=config) + #environment = git_multimail.GitoliteEnvironment(config=config) +except git_multimail.ConfigurationException, e: + sys.exit(str(e)) # Choose the method of sending emails based on the git config: diff --git a/contrib/hooks/pre-auto-gc-battery b/contrib/hooks/pre-auto-gc-battery index 9d0c2d1..6a2cdeb 100755 --- a/contrib/hooks/pre-auto-gc-battery +++ b/contrib/hooks/pre-auto-gc-battery @@ -33,7 +33,7 @@ elif grep -q "AC Power \+: 1" /proc/pmu/info 2>/dev/null then exit 0 elif test -x /usr/bin/pmset && /usr/bin/pmset -g batt | - grep -q "Currently drawing from 'AC Power'" + grep -q "drawing from 'AC Power'" then exit 0 fi diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh index 6309d12..bd3df97 100755 --- a/contrib/subtree/t/t7900-subtree.sh +++ b/contrib/subtree/t/t7900-subtree.sh @@ -62,17 +62,17 @@ last_commit_message() } test_expect_success 'init subproj' ' - test_create_repo subproj + test_create_repo subproj ' # To the subproject! cd subproj test_expect_success 'add sub1' ' - create sub1 && - git commit -m "sub1" && - git branch sub1 && - git branch -m master subproj + create sub1 && + git commit -m "sub1" && + git branch sub1 && + git branch -m master subproj ' # Save this hash for testing later. @@ -80,133 +80,134 @@ test_expect_success 'add sub1' ' subdir_hash=$(git rev-parse HEAD) test_expect_success 'add sub2' ' - create sub2 && - git commit -m "sub2" && - git branch sub2 + create sub2 && + git commit -m "sub2" && + git branch sub2 ' test_expect_success 'add sub3' ' - create sub3 && - git commit -m "sub3" && - git branch sub3 + create sub3 && + git commit -m "sub3" && + git branch sub3 ' # Back to mainline cd .. test_expect_success 'add main4' ' - create main4 && - git commit -m "main4" && - git branch -m master mainline && - git branch subdir + create main4 && + git commit -m "main4" && + git branch -m master mainline && + git branch subdir ' test_expect_success 'fetch subproj history' ' - git fetch ./subproj sub1 && - git branch sub1 FETCH_HEAD + git fetch ./subproj sub1 && + git branch sub1 FETCH_HEAD ' test_expect_success 'no subtree exists in main tree' ' - test_must_fail git subtree merge --prefix=subdir sub1 + test_must_fail git subtree merge --prefix=subdir sub1 ' test_expect_success 'no pull from non-existant subtree' ' - test_must_fail git subtree pull --prefix=subdir ./subproj sub1 + test_must_fail git subtree pull --prefix=subdir ./subproj sub1 ' test_expect_success 'check if --message works for add' ' - git subtree add --prefix=subdir --message="Added subproject" sub1 && - check_equal ''"$(last_commit_message)"'' "Added subproject" && - undo + git subtree add --prefix=subdir --message="Added subproject" sub1 && + check_equal ''"$(last_commit_message)"'' "Added subproject" && + undo ' test_expect_success 'check if --message works as -m and --prefix as -P' ' - git subtree add -P subdir -m "Added subproject using git subtree" sub1 && - check_equal ''"$(last_commit_message)"'' "Added subproject using git subtree" && - undo + git subtree add -P subdir -m "Added subproject using git subtree" sub1 && + check_equal ''"$(last_commit_message)"'' "Added subproject using git subtree" && + undo ' test_expect_success 'check if --message works with squash too' ' - git subtree add -P subdir -m "Added subproject with squash" --squash sub1 && - check_equal ''"$(last_commit_message)"'' "Added subproject with squash" && - undo + git subtree add -P subdir -m "Added subproject with squash" --squash sub1 && + check_equal ''"$(last_commit_message)"'' "Added subproject with squash" && + undo ' test_expect_success 'add subproj to mainline' ' - git subtree add --prefix=subdir/ FETCH_HEAD && - check_equal ''"$(last_commit_message)"'' "Add '"'subdir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'" + git subtree add --prefix=subdir/ FETCH_HEAD && + check_equal ''"$(last_commit_message)"'' "Add '"'subdir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'" ' # this shouldn't actually do anything, since FETCH_HEAD is already a parent test_expect_success 'merge fetched subproj' ' - git merge -m "merge -s -ours" -s ours FETCH_HEAD + git merge -m "merge -s -ours" -s ours FETCH_HEAD ' test_expect_success 'add main-sub5' ' - create subdir/main-sub5 && - git commit -m "main-sub5" + create subdir/main-sub5 && + git commit -m "main-sub5" ' test_expect_success 'add main6' ' - create main6 && - git commit -m "main6 boring" + create main6 && + git commit -m "main6 boring" ' test_expect_success 'add main-sub7' ' - create subdir/main-sub7 && - git commit -m "main-sub7" + create subdir/main-sub7 && + git commit -m "main-sub7" ' test_expect_success 'fetch new subproj history' ' - git fetch ./subproj sub2 && - git branch sub2 FETCH_HEAD + git fetch ./subproj sub2 && + git branch sub2 FETCH_HEAD ' test_expect_success 'check if --message works for merge' ' - git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2 && - check_equal ''"$(last_commit_message)"'' "Merged changes from subproject" && - undo + git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2 && + check_equal ''"$(last_commit_message)"'' "Merged changes from subproject" && + undo ' test_expect_success 'check if --message for merge works with squash too' ' - git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2 && - check_equal ''"$(last_commit_message)"'' "Merged changes from subproject using squash" && - undo + git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2 && + check_equal ''"$(last_commit_message)"'' "Merged changes from subproject using squash" && + undo ' test_expect_success 'merge new subproj history into subdir' ' - git subtree merge --prefix=subdir FETCH_HEAD && - git branch pre-split && - check_equal ''"$(last_commit_message)"'' "Merge commit '"'"'"$(git rev-parse sub2)"'"'"' into mainline" + git subtree merge --prefix=subdir FETCH_HEAD && + git branch pre-split && + check_equal ''"$(last_commit_message)"'' "Merge commit '"'"'"$(git rev-parse sub2)"'"'"' into mainline" && + undo ' test_expect_success 'Check that prefix argument is required for split' ' - echo "You must provide the --prefix option." > expected && - test_must_fail git subtree split > actual 2>&1 && + echo "You must provide the --prefix option." > expected && + test_must_fail git subtree split > actual 2>&1 && test_debug "printf '"'"'expected: '"'"'" && - test_debug "cat expected" && + test_debug "cat expected" && test_debug "printf '"'"'actual: '"'"'" && - test_debug "cat actual" && - test_cmp expected actual && - rm -f expected actual + test_debug "cat actual" && + test_cmp expected actual && + rm -f expected actual ' test_expect_success 'Check that the <prefix> exists for a split' ' - echo "'"'"'non-existent-directory'"'"'" does not exist\; use "'"'"'git subtree add'"'"'" > expected && - test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 && + echo "'"'"'non-existent-directory'"'"'" does not exist\; use "'"'"'git subtree add'"'"'" > expected && + test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 && test_debug "printf '"'"'expected: '"'"'" && - test_debug "cat expected" && + test_debug "cat expected" && test_debug "printf '"'"'actual: '"'"'" && - test_debug "cat actual" && - test_cmp expected actual -# rm -f expected actual + test_debug "cat actual" && + test_cmp expected actual +# rm -f expected actual ' test_expect_success 'check if --message works for split+rejoin' ' - spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' && - git branch spl1 "$spl1" && - check_equal ''"$(last_commit_message)"'' "Split & rejoin" && - undo + spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' && + git branch spl1 "$spl1" && + check_equal ''"$(last_commit_message)"'' "Split & rejoin" && + undo ' test_expect_success 'check split with --branch' ' @@ -218,79 +219,76 @@ test_expect_success 'check split with --branch' ' test_expect_success 'check hash of split' ' spl1=$(git subtree split --prefix subdir) && - undo && git subtree split --prefix subdir --branch splitbr1test && - check_equal ''"$(git rev-parse splitbr1test)"'' "$spl1" - git checkout splitbr1test && - new_hash=$(git rev-parse HEAD~2) && - git checkout mainline && + check_equal ''"$(git rev-parse splitbr1test)"'' "$spl1" && + new_hash=$(git rev-parse splitbr1test~2) && check_equal ''"$new_hash"'' "$subdir_hash" ' test_expect_success 'check split with --branch for an existing branch' ' - spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' && - undo && - git branch splitbr2 sub1 && - git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr2 && - check_equal ''"$(git rev-parse splitbr2)"'' "$spl1" + spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' && + undo && + git branch splitbr2 sub1 && + git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr2 && + check_equal ''"$(git rev-parse splitbr2)"'' "$spl1" ' test_expect_success 'check split with --branch for an incompatible branch' ' - test_must_fail git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir + test_must_fail git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir ' test_expect_success 'check split+rejoin' ' - spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' && - undo && - git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --rejoin && - check_equal ''"$(last_commit_message)"'' "Split '"'"'subdir/'"'"' into commit '"'"'"$spl1"'"'"'" + spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' && + undo && + git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --rejoin && + check_equal ''"$(last_commit_message)"'' "Split '"'"'subdir/'"'"' into commit '"'"'"$spl1"'"'"'" ' test_expect_success 'add main-sub8' ' - create subdir/main-sub8 && - git commit -m "main-sub8" + create subdir/main-sub8 && + git commit -m "main-sub8" ' # To the subproject! cd ./subproj test_expect_success 'merge split into subproj' ' - git fetch .. spl1 && - git branch spl1 FETCH_HEAD && - git merge FETCH_HEAD + git fetch .. spl1 && + git branch spl1 FETCH_HEAD && + git merge FETCH_HEAD ' test_expect_success 'add sub9' ' - create sub9 && - git commit -m "sub9" + create sub9 && + git commit -m "sub9" ' # Back to mainline cd .. test_expect_success 'split for sub8' ' - split2=''"$(git subtree split --annotate='"'*'"' --prefix subdir/ --rejoin)"'' - git branch split2 "$split2" + split2=''"$(git subtree split --annotate='"'*'"' --prefix subdir/ --rejoin)"'' && + git branch split2 "$split2" ' test_expect_success 'add main-sub10' ' - create subdir/main-sub10 && - git commit -m "main-sub10" + create subdir/main-sub10 && + git commit -m "main-sub10" ' test_expect_success 'split for sub10' ' - spl3=''"$(git subtree split --annotate='"'*'"' --prefix subdir --rejoin)"'' && - git branch spl3 "$spl3" + spl3=''"$(git subtree split --annotate='"'*'"' --prefix subdir --rejoin)"'' && + git branch spl3 "$spl3" ' # To the subproject! cd ./subproj test_expect_success 'merge split into subproj' ' - git fetch .. spl3 && - git branch spl3 FETCH_HEAD && - git merge FETCH_HEAD && - git branch subproj-merge-spl3 + git fetch .. spl3 && + git branch spl3 FETCH_HEAD && + git merge FETCH_HEAD && + git branch subproj-merge-spl3 ' chkm="main4 main6" @@ -300,44 +298,44 @@ chks="sub1 sub2 sub3 sub9" chks_sub=$(echo $chks | multiline | sed 's,^,subdir/,' | fixnl) test_expect_success 'make sure exactly the right set of files ends up in the subproj' ' - subfiles=''"$(git ls-files | fixnl)"'' && - check_equal "$subfiles" "$chkms $chks" + subfiles=''"$(git ls-files | fixnl)"'' && + check_equal "$subfiles" "$chkms $chks" ' test_expect_success 'make sure the subproj history *only* contains commits that affect the subdir' ' - allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' && - check_equal "$allchanges" "$chkms $chks" + allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' && + check_equal "$allchanges" "$chkms $chks" ' # Back to mainline cd .. test_expect_success 'pull from subproj' ' - git fetch ./subproj subproj-merge-spl3 && - git branch subproj-merge-spl3 FETCH_HEAD && - git subtree pull --prefix=subdir ./subproj subproj-merge-spl3 + git fetch ./subproj subproj-merge-spl3 && + git branch subproj-merge-spl3 FETCH_HEAD && + git subtree pull --prefix=subdir ./subproj subproj-merge-spl3 ' test_expect_success 'make sure exactly the right set of files ends up in the mainline' ' - mainfiles=''"$(git ls-files | fixnl)"'' && - check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub" + mainfiles=''"$(git ls-files | fixnl)"'' && + check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub" ' test_expect_success 'make sure each filename changed exactly once in the entire history' ' - # main-sub?? and /subdir/main-sub?? both change, because those are the - # changes that were split into their own history. And subdir/sub?? never - # change, since they were *only* changed in the subtree branch. - allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' && - check_equal "$allchanges" ''"$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"'' + # main-sub?? and /subdir/main-sub?? both change, because those are the + # changes that were split into their own history. And subdir/sub?? never + # change, since they were *only* changed in the subtree branch. + allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' && + check_equal "$allchanges" ''"$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"'' ' test_expect_success 'make sure the --rejoin commits never make it into subproj' ' - check_equal ''"$(git log --pretty=format:'"'%s'"' HEAD^2 | grep -i split)"'' "" + check_equal ''"$(git log --pretty=format:'"'%s'"' HEAD^2 | grep -i split)"'' "" ' test_expect_success 'make sure no "git subtree" tagged commits make it into subproj' ' - # They are meaningless to subproj since one side of the merge refers to the mainline - check_equal ''"$(git log --pretty=format:'"'%s%n%b'"' HEAD^2 | grep "git-subtree.*:")"'' "" + # They are meaningless to subproj since one side of the merge refers to the mainline + check_equal ''"$(git log --pretty=format:'"'%s%n%b'"' HEAD^2 | grep "git-subtree.*:")"'' "" ' # prepare second pair of repositories @@ -345,27 +343,27 @@ mkdir test2 cd test2 test_expect_success 'init main' ' - test_create_repo main + test_create_repo main ' cd main test_expect_success 'add main1' ' - create main1 && - git commit -m "main1" + create main1 && + git commit -m "main1" ' cd .. test_expect_success 'init sub' ' - test_create_repo sub + test_create_repo sub ' cd sub test_expect_success 'add sub2' ' - create sub2 && - git commit -m "sub2" + create sub2 && + git commit -m "sub2" ' cd ../main @@ -373,33 +371,33 @@ cd ../main # check if split can find proper base without --onto test_expect_success 'add sub as subdir in main' ' - git fetch ../sub master && - git branch sub2 FETCH_HEAD && - git subtree add --prefix subdir sub2 + git fetch ../sub master && + git branch sub2 FETCH_HEAD && + git subtree add --prefix subdir sub2 ' cd ../sub test_expect_success 'add sub3' ' - create sub3 && - git commit -m "sub3" + create sub3 && + git commit -m "sub3" ' cd ../main test_expect_success 'merge from sub' ' - git fetch ../sub master && - git branch sub3 FETCH_HEAD && - git subtree merge --prefix subdir sub3 + git fetch ../sub master && + git branch sub3 FETCH_HEAD && + git subtree merge --prefix subdir sub3 ' test_expect_success 'add main-sub4' ' - create subdir/main-sub4 && - git commit -m "main-sub4" + create subdir/main-sub4 && + git commit -m "main-sub4" ' test_expect_success 'split for main-sub4 without --onto' ' - git subtree split --prefix subdir --branch mainsub4 + git subtree split --prefix subdir --branch mainsub4 ' # at this point, the new commit parent should be sub3 if it is not, @@ -408,21 +406,21 @@ test_expect_success 'split for main-sub4 without --onto' ' # itself) test_expect_success 'check that the commit parent is sub3' ' - check_equal ''"$(git log --pretty=format:%P -1 mainsub4)"'' ''"$(git rev-parse sub3)"'' + check_equal ''"$(git log --pretty=format:%P -1 mainsub4)"'' ''"$(git rev-parse sub3)"'' ' test_expect_success 'add main-sub5' ' - mkdir subdir2 && - create subdir2/main-sub5 && - git commit -m "main-sub5" + mkdir subdir2 && + create subdir2/main-sub5 && + git commit -m "main-sub5" ' test_expect_success 'split for main-sub5 without --onto' ' - # also test that we still can split out an entirely new subtree - # if the parent of the first commit in the tree is not empty, + # also test that we still can split out an entirely new subtree + # if the parent of the first commit in the tree is not empty, # then the new subtree has accidentally been attached to something - git subtree split --prefix subdir2 --branch mainsub5 && - check_equal ''"$(git log --pretty=format:%P -1 mainsub5)"'' "" + git subtree split --prefix subdir2 --branch mainsub5 && + check_equal ''"$(git log --pretty=format:%P -1 mainsub5)"'' "" ' # make sure no patch changes more than one file. The original set of commits @@ -450,20 +448,20 @@ joincommits() } test_expect_success 'verify one file change per commit' ' - x= && - list=''"$(git log --pretty=format:'"'commit: %H'"' | joincommits)"'' && -# test_debug "echo HERE" && -# test_debug "echo ''"$list"''" && - (git log --pretty=format:'"'commit: %H'"' | joincommits | - ( while read commit a b; do - test_debug "echo Verifying commit "''"$commit"'' - test_debug "echo a: "''"$a"'' - test_debug "echo b: "''"$b"'' - check_equal "$b" "" - x=1 - done - check_equal "$x" 1 - )) + x= && + list=''"$(git log --pretty=format:'"'commit: %H'"' | joincommits)"'' && +# test_debug "echo HERE" && +# test_debug "echo ''"$list"''" && + (git log --pretty=format:'"'commit: %H'"' | joincommits | + ( while read commit a b; do + test_debug "echo Verifying commit "''"$commit"'' + test_debug "echo a: "''"$a"'' + test_debug "echo b: "''"$b"'' + check_equal "$b" "" + x=1 + done + check_equal "$x" 1 + )) ' test_done @@ -212,11 +212,6 @@ int run_diff_files(struct rev_info *revs, unsigned int option) ce->sha1, !is_null_sha1(ce->sha1), ce->name, 0); continue; - } else if (ce->ce_flags & CE_INTENT_TO_ADD) { - diff_addremove(&revs->diffopt, '+', ce->ce_mode, - EMPTY_BLOB_SHA1_BIN, 0, - ce->name, 0); - continue; } changed = match_stat_with_submodule(&revs->diffopt, ce, &st, @@ -381,13 +376,6 @@ static void do_oneway_diff(struct unpack_trees_options *o, struct rev_info *revs = o->unpack_data; int match_missing, cached; - /* i-t-a entries do not actually exist in the index */ - if (idx && (idx->ce_flags & CE_INTENT_TO_ADD)) { - idx = NULL; - if (!tree) - return; /* nothing to diff.. */ - } - /* if the entry is not checked out, don't examine work tree */ cached = o->index_only || (idx && ((idx->ce_flags & CE_VALID) || ce_skip_worktree(idx))); @@ -3653,7 +3653,12 @@ static void enable_patch_output(int *fmt) { static int parse_one_token(const char **arg, const char *token) { - return skip_prefix(*arg, token, arg) && (!**arg || **arg == ','); + const char *rest; + if (skip_prefix(*arg, token, &rest) && (!*rest || *rest == ',')) { + *arg = rest; + return 1; + } + return 0; } static int parse_ws_error_highlight(struct diff_options *opt, const char *arg) diff --git a/environment.c b/environment.c index 61c685b..a533aed 100644 --- a/environment.c +++ b/environment.c @@ -47,6 +47,7 @@ const char *askpass_program; const char *excludes_file; enum auto_crlf auto_crlf = AUTO_CRLF_FALSE; int check_replace_refs = 1; +char *git_replace_ref_base; enum eol core_eol = EOL_UNSET; enum safe_crlf safe_crlf = SAFE_CRLF_WARN; unsigned whitespace_rule_cfg = WS_DEFAULT_RULE; @@ -110,6 +111,7 @@ const char * const local_repo_env[] = { GRAFT_ENVIRONMENT, INDEX_ENVIRONMENT, NO_REPLACE_OBJECTS_ENVIRONMENT, + GIT_REPLACE_REF_BASE_ENVIRONMENT, GIT_PREFIX_ENVIRONMENT, GIT_SHALLOW_FILE_ENVIRONMENT, GIT_COMMON_DIR_ENVIRONMENT, @@ -156,6 +158,7 @@ static void setup_git_env(void) struct strbuf sb = STRBUF_INIT; const char *gitfile; const char *shallow_file; + const char *replace_ref_base; git_dir = getenv(GIT_DIR_ENVIRONMENT); if (!git_dir) @@ -173,6 +176,9 @@ static void setup_git_env(void) "info/grafts", &git_graft_env); if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT)) check_replace_refs = 0; + replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT); + git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base + : "refs/replace/"); namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT)); namespace_len = strlen(namespace); shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT); @@ -231,6 +237,8 @@ void set_git_work_tree(const char *new_work_tree) } git_work_tree_initialized = 1; work_tree = xstrdup(real_path(new_work_tree)); + if (setenv(GIT_WORK_TREE_ENVIRONMENT, work_tree, 1)) + die("could not set GIT_WORK_TREE to '%s'", work_tree); } const char *get_git_work_tree(void) diff --git a/ewah/bitmap.c b/ewah/bitmap.c index 710e58c..47ad674 100644 --- a/ewah/bitmap.c +++ b/ewah/bitmap.c @@ -20,8 +20,8 @@ #include "git-compat-util.h" #include "ewok.h" -#define MASK(x) ((eword_t)1 << (x % BITS_IN_WORD)) -#define BLOCK(x) (x / BITS_IN_WORD) +#define EWAH_MASK(x) ((eword_t)1 << (x % BITS_IN_EWORD)) +#define EWAH_BLOCK(x) (x / BITS_IN_EWORD) struct bitmap *bitmap_new(void) { @@ -33,7 +33,7 @@ struct bitmap *bitmap_new(void) void bitmap_set(struct bitmap *self, size_t pos) { - size_t block = BLOCK(pos); + size_t block = EWAH_BLOCK(pos); if (block >= self->word_alloc) { size_t old_size = self->word_alloc; @@ -45,22 +45,22 @@ void bitmap_set(struct bitmap *self, size_t pos) (self->word_alloc - old_size) * sizeof(eword_t)); } - self->words[block] |= MASK(pos); + self->words[block] |= EWAH_MASK(pos); } void bitmap_clear(struct bitmap *self, size_t pos) { - size_t block = BLOCK(pos); + size_t block = EWAH_BLOCK(pos); if (block < self->word_alloc) - self->words[block] &= ~MASK(pos); + self->words[block] &= ~EWAH_MASK(pos); } int bitmap_get(struct bitmap *self, size_t pos) { - size_t block = BLOCK(pos); + size_t block = EWAH_BLOCK(pos); return block < self->word_alloc && - (self->words[block] & MASK(pos)) != 0; + (self->words[block] & EWAH_MASK(pos)) != 0; } struct ewah_bitmap *bitmap_to_ewah(struct bitmap *bitmap) @@ -127,7 +127,7 @@ void bitmap_and_not(struct bitmap *self, struct bitmap *other) void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other) { size_t original_size = self->word_alloc; - size_t other_final = (other->bit_size / BITS_IN_WORD) + 1; + size_t other_final = (other->bit_size / BITS_IN_EWORD) + 1; size_t i = 0; struct ewah_iterator it; eword_t word; @@ -155,17 +155,17 @@ void bitmap_each_bit(struct bitmap *self, ewah_callback callback, void *data) uint32_t offset; if (word == (eword_t)~0) { - for (offset = 0; offset < BITS_IN_WORD; ++offset) + for (offset = 0; offset < BITS_IN_EWORD; ++offset) callback(pos++, data); } else { - for (offset = 0; offset < BITS_IN_WORD; ++offset) { + for (offset = 0; offset < BITS_IN_EWORD; ++offset) { if ((word >> offset) == 0) break; offset += ewah_bit_ctz64(word >> offset); callback(pos + offset, data); } - pos += BITS_IN_WORD; + pos += BITS_IN_EWORD; } } } diff --git a/ewah/ewah_bitmap.c b/ewah/ewah_bitmap.c index fccb42b..b522437 100644 --- a/ewah/ewah_bitmap.c +++ b/ewah/ewah_bitmap.c @@ -102,7 +102,7 @@ size_t ewah_add_empty_words(struct ewah_bitmap *self, int v, size_t number) if (number == 0) return 0; - self->bit_size += number * BITS_IN_WORD; + self->bit_size += number * BITS_IN_EWORD; return add_empty_words(self, v, number); } @@ -152,7 +152,7 @@ void ewah_add_dirty_words( self->buffer_size += can_add; } - self->bit_size += can_add * BITS_IN_WORD; + self->bit_size += can_add * BITS_IN_EWORD; if (number - can_add == 0) break; @@ -197,7 +197,7 @@ static size_t add_empty_word(struct ewah_bitmap *self, int v) size_t ewah_add(struct ewah_bitmap *self, eword_t word) { - self->bit_size += BITS_IN_WORD; + self->bit_size += BITS_IN_EWORD; if (word == 0) return add_empty_word(self, 0); @@ -211,8 +211,8 @@ size_t ewah_add(struct ewah_bitmap *self, eword_t word) void ewah_set(struct ewah_bitmap *self, size_t i) { const size_t dist = - (i + BITS_IN_WORD) / BITS_IN_WORD - - (self->bit_size + BITS_IN_WORD - 1) / BITS_IN_WORD; + (i + BITS_IN_EWORD) / BITS_IN_EWORD - + (self->bit_size + BITS_IN_EWORD - 1) / BITS_IN_EWORD; assert(i >= self->bit_size); @@ -222,19 +222,19 @@ void ewah_set(struct ewah_bitmap *self, size_t i) if (dist > 1) add_empty_words(self, 0, dist - 1); - add_literal(self, (eword_t)1 << (i % BITS_IN_WORD)); + add_literal(self, (eword_t)1 << (i % BITS_IN_EWORD)); return; } if (rlw_get_literal_words(self->rlw) == 0) { rlw_set_running_len(self->rlw, rlw_get_running_len(self->rlw) - 1); - add_literal(self, (eword_t)1 << (i % BITS_IN_WORD)); + add_literal(self, (eword_t)1 << (i % BITS_IN_EWORD)); return; } self->buffer[self->buffer_size - 1] |= - ((eword_t)1 << (i % BITS_IN_WORD)); + ((eword_t)1 << (i % BITS_IN_EWORD)); /* check if we just completed a stream of 1s */ if (self->buffer[self->buffer_size - 1] == (eword_t)(~0)) { @@ -255,11 +255,11 @@ void ewah_each_bit(struct ewah_bitmap *self, void (*callback)(size_t, void*), vo eword_t *word = &self->buffer[pointer]; if (rlw_get_run_bit(word)) { - size_t len = rlw_get_running_len(word) * BITS_IN_WORD; + size_t len = rlw_get_running_len(word) * BITS_IN_EWORD; for (k = 0; k < len; ++k, ++pos) callback(pos, payload); } else { - pos += rlw_get_running_len(word) * BITS_IN_WORD; + pos += rlw_get_running_len(word) * BITS_IN_EWORD; } ++pointer; @@ -268,7 +268,7 @@ void ewah_each_bit(struct ewah_bitmap *self, void (*callback)(size_t, void*), vo int c; /* todo: zero count optimization */ - for (c = 0; c < BITS_IN_WORD; ++c, ++pos) { + for (c = 0; c < BITS_IN_EWORD; ++c, ++pos) { if ((self->buffer[pointer] & ((eword_t)1 << c)) != 0) callback(pos, payload); } diff --git a/ewah/ewok.h b/ewah/ewok.h index e732525..6e2c5e1 100644 --- a/ewah/ewok.h +++ b/ewah/ewok.h @@ -32,7 +32,7 @@ struct strbuf; typedef uint64_t eword_t; -#define BITS_IN_WORD (sizeof(eword_t) * 8) +#define BITS_IN_EWORD (sizeof(eword_t) * 8) /** * Do not use __builtin_popcountll. The GCC implementation diff --git a/fetch-pack.c b/fetch-pack.c index a912935..a136772 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -809,7 +809,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, sort_ref_list(&ref, ref_compare_name); qsort(sought, nr_sought, sizeof(*sought), cmp_ref_by_name); - if (is_repository_shallow() && !server_supports("shallow")) + if ((args->depth > 0 || is_repository_shallow()) && !server_supports("shallow")) die("Server does not support shallow clients"); if (server_supports("multi_ack_detailed")) { if (args->verbose) @@ -241,8 +241,8 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func) return retval; } -static int require_end_of_header(const void *data, unsigned long size, - struct object *obj, fsck_error error_func) +static int verify_headers(const void *data, unsigned long size, + struct object *obj, fsck_error error_func) { const char *buffer = (const char *)data; unsigned long i; @@ -258,6 +258,15 @@ static int require_end_of_header(const void *data, unsigned long size, } } + /* + * We did not find double-LF that separates the header + * and the body. Not having a body is not a crime but + * we do want to see the terminating LF for the last header + * line. + */ + if (size && buffer[size - 1] == '\n') + return 0; + return error_func(obj, FSCK_ERROR, "unterminated header"); } @@ -308,7 +317,7 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer, unsigned parent_count, parent_line_count = 0; int err; - if (require_end_of_header(buffer, size, &commit->object, error_func)) + if (verify_headers(buffer, size, &commit->object, error_func)) return -1; if (!skip_prefix(buffer, "tree ", &buffer)) @@ -387,7 +396,7 @@ static int fsck_tag_buffer(struct tag *tag, const char *data, } } - if (require_end_of_header(buffer, size, &tag->object, error_func)) + if (verify_headers(buffer, size, &tag->object, error_func)) goto done; if (!skip_prefix(buffer, "object ", &buffer)) { @@ -69,6 +69,8 @@ then cmdline="$cmdline -3" fi +empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904 + sq () { git rev-parse --sq-quote "$@" } @@ -85,7 +87,7 @@ safe_to_abort () { return 1 fi - if ! test -s "$dotest/abort-safety" + if ! test -f "$dotest/abort-safety" then return 0 fi @@ -177,7 +179,8 @@ It does not apply to blobs recorded in its index.")" then GIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY fi - git-merge-recursive $orig_tree -- HEAD $his_tree || { + our_tree=$(git rev-parse --verify -q HEAD || echo $empty_tree) + git-merge-recursive $orig_tree -- $our_tree $his_tree || { git rerere $allow_rerere_autoupdate die "$(gettext "Failed to merge in the changes.")" } @@ -297,6 +300,7 @@ split_patches () { ;; stgit) this=0 + test 0 -eq "$#" && set -- - for stgit in "$@" do this=$(expr "$this" + 1) @@ -318,7 +322,7 @@ split_patches () { print "Subject: ", $_ ; $subject = 1; } - ' < "$stgit" > "$dotest/$msgnum" || clean_abort + ' -- "$stgit" >"$dotest/$msgnum" || clean_abort done echo "$this" > "$dotest/last" this= @@ -326,6 +330,7 @@ split_patches () { ;; hg) this=0 + test 0 -eq "$#" && set -- - for hg in "$@" do this=$(( $this + 1 )) @@ -342,17 +347,17 @@ split_patches () { elsif (/^\# User /) { s/\# User/From:/ ; print ; } elsif (/^\# Date /) { my ($hashsign, $str, $time, $tz) = split ; - $tz = sprintf "%+05d", (0-$tz)/36; + $tz_str = sprintf "%+05d", (0-$tz)/36; print "Date: " . strftime("%a, %d %b %Y %H:%M:%S ", - localtime($time)) - . "$tz\n"; + gmtime($time-$tz)) + . "$tz_str\n"; } elsif (/^\# /) { next ; } else { print "\n", $_ ; $subject = 1; } - ' <"$hg" >"$dotest/$msgnum" || clean_abort + ' -- "$hg" >"$dotest/$msgnum" || clean_abort done echo "$this" >"$dotest/last" this= @@ -378,6 +383,7 @@ committer_date_is_author_date= ignore_date= allow_rerere_autoupdate= gpg_sign_opt= +threeway= if test "$(git config --bool --get am.messageid)" = true then @@ -502,10 +508,11 @@ then ;; t,) git rerere clear - git read-tree --reset -u HEAD HEAD - orig_head=$(cat "$GIT_DIR/ORIG_HEAD") - git reset HEAD - git update-ref ORIG_HEAD $orig_head + head_tree=$(git rev-parse --verify -q HEAD || echo $empty_tree) && + git read-tree --reset -u $head_tree $head_tree && + index_tree=$(git write-tree) && + git read-tree -m -u $index_tree $head_tree + git read-tree $head_tree ;; ,t) if test -f "$dotest/rebasing" @@ -515,8 +522,19 @@ then git rerere clear if safe_to_abort then - git read-tree --reset -u HEAD ORIG_HEAD - git reset ORIG_HEAD + head_tree=$(git rev-parse --verify -q HEAD || echo $empty_tree) && + git read-tree --reset -u $head_tree $head_tree && + index_tree=$(git write-tree) && + orig_head=$(git rev-parse --verify -q ORIG_HEAD || echo $empty_tree) && + git read-tree -m -u $index_tree $orig_head + if git rev-parse --verify -q ORIG_HEAD >/dev/null 2>&1 + then + git reset ORIG_HEAD + else + git read-tree $empty_tree + curr_branch=$(git symbolic-ref HEAD 2>/dev/null) && + git update-ref -d $curr_branch + fi fi rm -fr "$dotest" exit ;; diff --git a/git-compat-util.h b/git-compat-util.h index 0cc7ae8..c6d391f 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -58,15 +58,13 @@ #define BUILD_ASSERT_OR_ZERO(cond) \ (sizeof(char [1 - 2*!(cond)]) - 1) -#if defined(__GNUC__) && (__GNUC__ >= 3) -# if GIT_GNUC_PREREQ(3, 1) +#if GIT_GNUC_PREREQ(3, 1) /* &arr[0] degrades to a pointer: a different type from an array */ # define BARF_UNLESS_AN_ARRAY(arr) \ BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(__typeof__(arr), \ __typeof__(&(arr)[0]))) -# else -# define BARF_UNLESS_AN_ARRAY(arr) 0 -# endif +#else +# define BARF_UNLESS_AN_ARRAY(arr) 0 #endif /* * ARRAY_SIZE - get the number of elements in a visible array diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 82ecb03..1e4e65a 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -921,7 +921,7 @@ sub commit { # (See check_refname_component in refs.c.) 1 while $xtag =~ s/ (?: \.\. # Tag cannot contain '..'. - | \@{ # Tag cannot contain '@{'. + | \@\{ # Tag cannot contain '@{'. | ^ - # Tag cannot begin with '-'. | \.lock $ # Tag cannot end with '.lock'. | ^ \. # Tag cannot begin... diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh index 14b039d..54ac8e4 100644 --- a/git-mergetool--lib.sh +++ b/git-mergetool--lib.sh @@ -305,6 +305,7 @@ guess_merge_tool () { EOF # Loop over each candidate and stop when a valid merge tool is found. + IFS=' ' for tool in $tools do is_available "$tool" && echo "$tool" && return 0 @@ -43,6 +43,9 @@ verbose = False # Only labels/tags matching this will be imported/exported defaultLabelRegexp = r'[a-zA-Z0-9_\-.]+$' +# Grab changes in blocks of this many revisions, unless otherwise requested +defaultBlockSize = 512 + def p4_build_cmd(cmd): """Build a suitable p4 command line. @@ -249,6 +252,10 @@ def p4_reopen(type, f): def p4_move(src, dest): p4_system(["move", "-k", wildcard_encode(src), wildcard_encode(dest)]) +def p4_last_change(): + results = p4CmdList(["changes", "-m", "1"]) + return int(results[0]['change']) + def p4_describe(change): """Make sure it returns a valid result by checking for the presence of field "time". Return a dict of the @@ -742,43 +749,77 @@ def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent def originP4BranchesExist(): return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master") -def p4ChangesForPaths(depotPaths, changeRange, block_size): + +def p4ParseNumericChangeRange(parts): + changeStart = int(parts[0][1:]) + if parts[1] == '#head': + changeEnd = p4_last_change() + else: + changeEnd = int(parts[1]) + + return (changeStart, changeEnd) + +def chooseBlockSize(blockSize): + if blockSize: + return blockSize + else: + return defaultBlockSize + +def p4ChangesForPaths(depotPaths, changeRange, requestedBlockSize): assert depotPaths - assert block_size - # Parse the change range into start and end + # Parse the change range into start and end. Try to find integer + # revision ranges as these can be broken up into blocks to avoid + # hitting server-side limits (maxrows, maxscanresults). But if + # that doesn't work, fall back to using the raw revision specifier + # strings, without using block mode. + if changeRange is None or changeRange == '': - changeStart = '@1' - changeEnd = '#head' + changeStart = 1 + changeEnd = p4_last_change() + block_size = chooseBlockSize(requestedBlockSize) else: parts = changeRange.split(',') assert len(parts) == 2 - changeStart = parts[0] - changeEnd = parts[1] + try: + (changeStart, changeEnd) = p4ParseNumericChangeRange(parts) + block_size = chooseBlockSize(requestedBlockSize) + except: + changeStart = parts[0][1:] + changeEnd = parts[1] + if requestedBlockSize: + die("cannot use --changes-block-size with non-numeric revisions") + block_size = None # Accumulate change numbers in a dictionary to avoid duplicates changes = {} for p in depotPaths: # Retrieve changes a block at a time, to prevent running - # into a MaxScanRows error from the server. - start = changeStart - end = changeEnd - get_another_block = True - while get_another_block: - new_changes = [] + # into a MaxResults/MaxScanRows error from the server. + + while True: cmd = ['changes'] - cmd += ['-m', str(block_size)] - cmd += ["%s...%s,%s" % (p, start, end)] + + if block_size: + end = min(changeEnd, changeStart + block_size) + revisionRange = "%d,%d" % (changeStart, end) + else: + revisionRange = "%s,%s" % (changeStart, changeEnd) + + cmd += ["%s...@%s" % (p, revisionRange)] + for line in p4_read_pipe_lines(cmd): changeNum = int(line.split(" ")[1]) - new_changes.append(changeNum) changes[changeNum] = True - if len(new_changes) == block_size: - get_another_block = True - end = '@' + str(min(new_changes)) - else: - get_another_block = False + + if not block_size: + break + + if end >= changeEnd: + break + + changeStart = end + 1 changelist = changes.keys() changelist.sort() @@ -1974,7 +2015,7 @@ class P4Sync(Command, P4UserMap): self.syncWithOrigin = True self.importIntoRemotes = True self.maxChanges = "" - self.changes_block_size = 500 + self.changes_block_size = None self.keepRepoPath = False self.depotPaths = None self.p4BranchesInGit = [] diff --git a/git-rebase--am.sh b/git-rebase--am.sh index f923732..9ae898b 100644 --- a/git-rebase--am.sh +++ b/git-rebase--am.sh @@ -78,7 +78,7 @@ else As a result, git cannot rebase them. EOF - return $? + return $ret fi git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" \ diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index dc3133f..2f6ce55 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -740,10 +740,15 @@ collapse_todo_ids() { # "pick sha1 fixup!/squash! msg" appears in it so that the latter # comes immediately after the former, and change "pick" to # "fixup"/"squash". +# +# Note that if the config has specified a custom instruction format +# each log message will be re-retrieved in order to normalize the +# autosquash arrangement rearrange_squash () { # extract fixup!/squash! lines and resolve any referenced sha1's while read -r pick sha1 message do + test -z "${format}" || message=$(git log -n 1 --format="%s" ${sha1}) case "$message" in "squash! "*|"fixup! "*) action="${message%%!*}" @@ -785,6 +790,7 @@ rearrange_squash () { *" $sha1 "*) continue ;; esac printf '%s\n' "$pick $sha1 $message" + test -z "${format}" || message=$(git log -n 1 --format="%s" ${sha1}) used="$used$sha1 " while read -r squash action msg_prefix msg_content do @@ -802,8 +808,13 @@ rearrange_squash () { case "$message" in "$msg_content"*) emit=1;; esac ;; esac if test $emit = 1; then - real_prefix=$(echo "$msg_prefix" | sed "s/,/! /g") - printf '%s\n' "$action $squash ${real_prefix}$msg_content" + if test -n "${format}" + then + msg_content=$(git log -n 1 --format="${format}" ${squash}) + else + msg_content="$(echo "$msg_prefix" | sed "s/,/! /g")$msg_content" + fi + printf '%s\n' "$action $squash $msg_content" used="$used$squash " fi done <"$1.sq" @@ -849,7 +860,11 @@ continue) # do we have anything to commit? if git diff-index --cached --quiet HEAD -- then - : Nothing to commit -- skip this + # Nothing to commit -- skip this commit + + test ! -f "$GIT_DIR"/CHERRY_PICK_HEAD || + rm "$GIT_DIR"/CHERRY_PICK_HEAD || + die "Could not remove CHERRY_PICK_HEAD" else if ! test -f "$author_script" then @@ -977,7 +992,10 @@ else revisions=$onto...$orig_head shortrevisions=$shorthead fi -git rev-list $merges_option --pretty=oneline --reverse --left-right --topo-order \ +format=$(git config --get rebase.instructionFormat) +# the 'rev-list .. | sed' requires %m to parse; the instruction requires %H to parse +git rev-list $merges_option --format="%m%H ${format:-%s}" \ + --reverse --left-right --topo-order \ $revisions ${restrict_revision+^$restrict_revision} | \ sed -n "s/^>//p" | while read -r sha1 rest diff --git a/git-send-email.perl b/git-send-email.perl index e1e9b14..b660cc2 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -460,25 +460,11 @@ my ($repoauthor, $repocommitter); ($repoauthor) = Git::ident_person(@repo, 'author'); ($repocommitter) = Git::ident_person(@repo, 'committer'); -# Verify the user input - -foreach my $entry (@initial_to) { - die "Comma in --to entry: $entry'\n" unless $entry !~ m/,/; -} - -foreach my $entry (@initial_cc) { - die "Comma in --cc entry: $entry'\n" unless $entry !~ m/,/; -} - -foreach my $entry (@bcclist) { - die "Comma in --bcclist entry: $entry'\n" unless $entry !~ m/,/; -} - sub parse_address_line { if ($have_mail_address) { return map { $_->format } Mail::Address->parse($_[0]); } else { - return split_addrs($_[0]); + return Git::parse_mailboxes($_[0]); } } @@ -487,6 +473,37 @@ sub split_addrs { } my %aliases; + +sub parse_sendmail_alias { + local $_ = shift; + if (/"/) { + print STDERR "warning: sendmail alias with quotes is not supported: $_\n"; + } elsif (/:include:/) { + print STDERR "warning: `:include:` not supported: $_\n"; + } elsif (/[\/|]/) { + print STDERR "warning: `/file` or `|pipe` redirection not supported: $_\n"; + } elsif (/^(\S+?)\s*:\s*(.+)$/) { + my ($alias, $addr) = ($1, $2); + $aliases{$alias} = [ split_addrs($addr) ]; + } else { + print STDERR "warning: sendmail line is not recognized: $_\n"; + } +} + +sub parse_sendmail_aliases { + my $fh = shift; + my $s = ''; + while (<$fh>) { + chomp; + next if /^\s*$/ || /^\s*#/; + $s .= $_, next if $s =~ s/\\$// || s/^\s+//; + parse_sendmail_alias($s) if $s; + $s = $_; + } + $s =~ s/\\$//; # silently tolerate stray '\' on last line + parse_sendmail_alias($s) if $s; +} + my %parse_alias = ( # multiline formats can be supported in the future mutt => sub { my $fh = shift; while (<$fh>) { @@ -515,7 +532,7 @@ my %parse_alias = ( $aliases{$alias} = [ split_addrs($addr) ]; } } }, - + sendmail => \&parse_sendmail_aliases, gnus => sub { my $fh = shift; while (<$fh>) { if (/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) { $aliases{$1} = [ $2 ]; @@ -530,8 +547,6 @@ if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) { } } -($sender) = expand_aliases($sender) if defined $sender; - # is_format_patch_arg($f) returns 0 if $f names a patch, or 1 if # $f is a revision list specification to be passed to format-patch. sub is_format_patch_arg { @@ -776,7 +791,10 @@ if (!$force) { } } -if (!defined $sender) { +if (defined $sender) { + $sender =~ s/^\s+|\s+$//g; + ($sender) = expand_aliases($sender); +} else { $sender = $repoauthor || $repocommitter || ''; } @@ -808,12 +826,9 @@ sub expand_one_alias { return $aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) : $alias; } -@initial_to = expand_aliases(@initial_to); -@initial_to = validate_address_list(sanitize_address_list(@initial_to)); -@initial_cc = expand_aliases(@initial_cc); -@initial_cc = validate_address_list(sanitize_address_list(@initial_cc)); -@bcclist = expand_aliases(@bcclist); -@bcclist = validate_address_list(sanitize_address_list(@bcclist)); +@initial_to = process_address_list(@initial_to); +@initial_cc = process_address_list(@initial_cc); +@bcclist = process_address_list(@bcclist); if ($thread && !defined $initial_reply_to && $prompting) { $initial_reply_to = ask( @@ -1006,15 +1021,17 @@ sub sanitize_address { return $recipient; } + # remove non-escaped quotes + $recipient_name =~ s/(^|[^\\])"/$1/g; + # rfc2047 is needed if a non-ascii char is included if ($recipient_name =~ /[^[:ascii:]]/) { - $recipient_name =~ s/^"(.*)"$/$1/; $recipient_name = quote_rfc2047($recipient_name); } # double quotes are needed if specials or CTLs are included elsif ($recipient_name =~ /[][()<>@,;:\\".\000-\037\177]/) { - $recipient_name =~ s/(["\\\r])/\\$1/g; + $recipient_name =~ s/([\\\r])/\\$1/g; $recipient_name = qq["$recipient_name"]; } @@ -1026,6 +1043,14 @@ sub sanitize_address_list { return (map { sanitize_address($_) } @_); } +sub process_address_list { + my @addr_list = map { parse_address_line($_) } @_; + @addr_list = expand_aliases(@addr_list); + @addr_list = sanitize_address_list(@addr_list); + @addr_list = validate_address_list(@addr_list); + return @addr_list; +} + # Returns the local Fully Qualified Domain Name (FQDN) if available. # # Tightly configured MTAa require that a caller sends a real DNS @@ -1535,8 +1560,8 @@ foreach my $t (@files) { ($confirm =~ /^(?:auto|compose)$/ && $compose && $message_num == 1)); $needs_confirm = "inform" if ($needs_confirm && $confirm_unconfigured && @cc); - @to = validate_address_list(sanitize_address_list(@to)); - @cc = validate_address_list(sanitize_address_list(@cc)); + @to = process_address_list(@to); + @cc = process_address_list(@cc); @to = (@initial_to, @to); @cc = (@initial_cc, @cc); diff --git a/git-stash.sh b/git-stash.sh index 1f5ea87..8e9e2cd 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -457,8 +457,6 @@ apply_stash () { assert_stash_like "$@" git update-index -q --refresh || die "$(gettext "unable to refresh index")" - git diff-index --cached --quiet --ignore-submodules HEAD -- || - die "$(gettext "Cannot apply stash: Your index contains uncommitted changes.")" # current index state c_tree=$(git write-tree) || @@ -382,7 +382,7 @@ static struct cmd_struct commands[] = { { "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE }, { "check-mailmap", cmd_check_mailmap, RUN_SETUP }, { "check-ref-format", cmd_check_ref_format }, - { "checkout", cmd_checkout, RUN_SETUP }, + { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE }, { "checkout-index", cmd_checkout_index, RUN_SETUP | NEED_WORK_TREE}, { "cherry", cmd_cherry, RUN_SETUP }, @@ -445,6 +445,7 @@ static struct cmd_struct commands[] = { { "pickaxe", cmd_blame, RUN_SETUP }, { "prune", cmd_prune, RUN_SETUP }, { "prune-packed", cmd_prune_packed, RUN_SETUP }, + { "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE }, { "push", cmd_push, RUN_SETUP }, { "read-tree", cmd_read_tree, RUN_SETUP }, { "receive-pack", cmd_receive_pack }, @@ -483,6 +484,7 @@ static struct cmd_struct commands[] = { { "verify-tag", cmd_verify_tag, RUN_SETUP }, { "version", cmd_version }, { "whatchanged", cmd_whatchanged, RUN_SETUP }, + { "worktree", cmd_worktree, RUN_SETUP }, { "write-tree", cmd_write_tree, RUN_SETUP }, }; diff --git a/gpg-interface.c b/gpg-interface.c index 68b0c81..3dc2fe3 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -60,6 +60,43 @@ void parse_gpg_output(struct signature_check *sigc) } } +int check_signature(const char *payload, size_t plen, const char *signature, + size_t slen, struct signature_check *sigc) +{ + struct strbuf gpg_output = STRBUF_INIT; + struct strbuf gpg_status = STRBUF_INIT; + int status; + + sigc->result = 'N'; + + status = verify_signed_buffer(payload, plen, signature, slen, + &gpg_output, &gpg_status); + if (status && !gpg_output.len) + goto out; + sigc->payload = xmemdupz(payload, plen); + sigc->gpg_output = strbuf_detach(&gpg_output, NULL); + sigc->gpg_status = strbuf_detach(&gpg_status, NULL); + parse_gpg_output(sigc); + + out: + strbuf_release(&gpg_status); + strbuf_release(&gpg_output); + + return sigc->result != 'G' && sigc->result != 'U'; +} + +void print_signature_buffer(const struct signature_check *sigc, unsigned flags) +{ + const char *output = flags & GPG_VERIFY_RAW ? + sigc->gpg_status : sigc->gpg_output; + + if (flags & GPG_VERIFY_VERBOSE && sigc->payload) + fputs(sigc->payload, stdout); + + if (output) + fputs(output, stderr); +} + /* * Look at GPG signed content (e.g. a signed tag object), whose * payload is followed by a detached signature on it. Return the diff --git a/gpg-interface.h b/gpg-interface.h index 87a4f2e..ea68885 100644 --- a/gpg-interface.h +++ b/gpg-interface.h @@ -1,6 +1,9 @@ #ifndef GPG_INTERFACE_H #define GPG_INTERFACE_H +#define GPG_VERIFY_VERBOSE 1 +#define GPG_VERIFY_RAW 2 + struct signature_check { char *payload; char *gpg_output; @@ -27,5 +30,8 @@ extern int verify_signed_buffer(const char *payload, size_t payload_size, const extern int git_gpg_config(const char *, const char *, void *); extern void set_signing_key(const char *); extern const char *get_signing_key(void); +extern int check_signature(const char *payload, size_t plen, + const char *signature, size_t slen, struct signature_check *sigc); +void print_signature_buffer(const struct signature_check *sigc, unsigned flags); #endif @@ -394,7 +394,7 @@ const char *help_unknown_cmd(const char *cmd) if (autocorrect > 0) { fprintf_ln(stderr, _("in %0.1f seconds automatically..."), (float)autocorrect/10.0); - poll(NULL, 0, autocorrect * 100); + sleep_millisec(autocorrect * 100); } return assumed; } @@ -416,10 +416,10 @@ static CURL *get_curl_handle(void) if (curl_http_proxy) { curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy); + } #if LIBCURL_VERSION_NUM >= 0x070a07 - curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY); + curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY); #endif - } set_curl_keepalive(result); @@ -9,6 +9,7 @@ #include "xdiff-interface.h" #include "run-command.h" #include "ll-merge.h" +#include "quote.h" struct ll_merge_driver; @@ -166,17 +167,20 @@ static int ll_ext_merge(const struct ll_merge_driver *fn, { char temp[4][50]; struct strbuf cmd = STRBUF_INIT; - struct strbuf_expand_dict_entry dict[5]; + struct strbuf_expand_dict_entry dict[6]; + struct strbuf path_sq = STRBUF_INIT; const char *args[] = { NULL, NULL }; int status, fd, i; struct stat st; assert(opts); + sq_quote_buf(&path_sq, path); dict[0].placeholder = "O"; dict[0].value = temp[0]; dict[1].placeholder = "A"; dict[1].value = temp[1]; dict[2].placeholder = "B"; dict[2].value = temp[2]; dict[3].placeholder = "L"; dict[3].value = temp[3]; - dict[4].placeholder = NULL; dict[4].value = NULL; + dict[4].placeholder = "P"; dict[4].value = path_sq.buf; + dict[5].placeholder = NULL; dict[5].value = NULL; if (fn->cmdline == NULL) die("custom merge driver %s lacks command line.", fn->name); @@ -210,6 +214,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn, for (i = 0; i < 3; i++) unlink_or_warn(temp[i]); strbuf_release(&cmd); + strbuf_release(&path_sq); return status; } @@ -269,6 +274,7 @@ static int read_merge_config(const char *var, const char *value, void *cb) * %A - temporary file name for our version. * %B - temporary file name for the other branches' version. * %L - conflict marker length + * %P - the original path (safely quoted for the shell) * * The external merge driver should write the results in the * file named by %A, and signal that it has done with zero exit @@ -157,14 +157,6 @@ static int lock_file(struct lock_file *lk, const char *path, int flags) return lk->fd; } -static int sleep_microseconds(long us) -{ - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = us; - return select(0, NULL, NULL, NULL, &tv); -} - /* * Constants defining the gaps between attempts to lock a file. The * first backoff period is approximately INITIAL_BACKOFF_MS @@ -184,27 +176,22 @@ static int lock_file_timeout(struct lock_file *lk, const char *path, { int n = 1; int multiplier = 1; - long remaining_us = 0; + long remaining_ms = 0; static int random_initialized = 0; if (timeout_ms == 0) return lock_file(lk, path, flags); if (!random_initialized) { - srandom((unsigned int)getpid()); + srand((unsigned int)getpid()); random_initialized = 1; } - if (timeout_ms > 0) { - /* avoid overflow */ - if (timeout_ms <= LONG_MAX / 1000) - remaining_us = timeout_ms * 1000; - else - remaining_us = LONG_MAX; - } + if (timeout_ms > 0) + remaining_ms = timeout_ms; while (1) { - long backoff_ms, wait_us; + long backoff_ms, wait_ms; int fd; fd = lock_file(lk, path, flags); @@ -213,14 +200,14 @@ static int lock_file_timeout(struct lock_file *lk, const char *path, return fd; /* success */ else if (errno != EEXIST) return -1; /* failure other than lock held */ - else if (timeout_ms > 0 && remaining_us <= 0) + else if (timeout_ms > 0 && remaining_ms <= 0) return -1; /* failure due to timeout */ backoff_ms = multiplier * INITIAL_BACKOFF_MS; /* back off for between 0.75*backoff_ms and 1.25*backoff_ms */ - wait_us = (750 + random() % 500) * backoff_ms; - sleep_microseconds(wait_us); - remaining_us -= wait_us; + wait_ms = (750 + rand() % 500) * backoff_ms / 1000; + sleep_millisec(wait_ms); + remaining_ms -= wait_ms; /* Recursion: (n+1)^2 = n^2 + 2n + 1 */ multiplier += 2*n + 1; @@ -97,11 +97,12 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid, assert(cb_data == NULL); - if (starts_with(refname, "refs/replace/")) { + if (starts_with(refname, git_replace_ref_base)) { struct object_id original_oid; if (!check_replace_refs) return 0; - if (get_oid_hex(refname + 13, &original_oid)) { + if (get_oid_hex(refname + strlen(git_replace_ref_base), + &original_oid)) { warning("invalid replace ref %s", refname); return 0; } diff --git a/pack-bitmap.c b/pack-bitmap.c index 2b3ff23..637770a 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c @@ -622,7 +622,7 @@ static void show_objects_for_type( while (i < objects->word_alloc && ewah_iterator_next(&filter, &it)) { eword_t word = objects->words[i] & filter; - for (offset = 0; offset < BITS_IN_WORD; ++offset) { + for (offset = 0; offset < BITS_IN_EWORD; ++offset) { const unsigned char *sha1; struct revindex_entry *entry; uint32_t hash = 0; @@ -644,7 +644,7 @@ static void show_objects_for_type( show_reach(sha1, object_type, 0, hash, bitmap_git.pack, entry->offset); } - pos += BITS_IN_WORD; + pos += BITS_IN_EWORD; i++; } } @@ -776,7 +776,7 @@ int reuse_partial_packfile_from_bitmap(struct packed_git **packfile, break; } - reuse_objects += BITS_IN_WORD; + reuse_objects += BITS_IN_EWORD; } #ifdef GIT_BITMAP_DEBUG @@ -1001,7 +1001,7 @@ static int rebuild_bitmap(uint32_t *reposition, while (ewah_iterator_next(&word, &it)) { uint32_t offset, bit_pos; - for (offset = 0; offset < BITS_IN_WORD; ++offset) { + for (offset = 0; offset < BITS_IN_EWORD; ++offset) { if ((word >> offset) == 0) break; @@ -1014,7 +1014,7 @@ static int rebuild_bitmap(uint32_t *reposition, return -1; } - pos += BITS_IN_WORD; + pos += BITS_IN_EWORD; } return 0; } @@ -78,6 +78,7 @@ void setup_pager(void) argv_array_push(&pager_process.env_array, "LESS=FRX"); if (!getenv("LV")) argv_array_push(&pager_process.env_array, "LV=-c"); + argv_array_push(&pager_process.env_array, "GIT_PAGER_IN_USE"); if (start_command(&pager_process)) return; diff --git a/parse-options-cb.c b/parse-options-cb.c index be8c413..5ab6ed6 100644 --- a/parse-options-cb.c +++ b/parse-options-cb.c @@ -4,6 +4,7 @@ #include "commit.h" #include "color.h" #include "string-list.h" +#include "argv-array.h" /*----- some often used options -----*/ @@ -134,3 +135,71 @@ int parse_opt_noop_cb(const struct option *opt, const char *arg, int unset) { return 0; } + +/** + * Recreates the command-line option in the strbuf. + */ +static int recreate_opt(struct strbuf *sb, const struct option *opt, + const char *arg, int unset) +{ + strbuf_reset(sb); + + if (opt->long_name) { + strbuf_addstr(sb, unset ? "--no-" : "--"); + strbuf_addstr(sb, opt->long_name); + if (arg) { + strbuf_addch(sb, '='); + strbuf_addstr(sb, arg); + } + } else if (opt->short_name && !unset) { + strbuf_addch(sb, '-'); + strbuf_addch(sb, opt->short_name); + if (arg) + strbuf_addstr(sb, arg); + } else + return -1; + + return 0; +} + +/** + * For an option opt, recreates the command-line option in opt->value which + * must be an char* initialized to NULL. This is useful when we need to pass + * the command-line option to another command. Since any previous value will be + * overwritten, this callback should only be used for options where the last + * one wins. + */ +int parse_opt_passthru(const struct option *opt, const char *arg, int unset) +{ + static struct strbuf sb = STRBUF_INIT; + char **opt_value = opt->value; + + if (recreate_opt(&sb, opt, arg, unset) < 0) + return -1; + + if (*opt_value) + free(*opt_value); + + *opt_value = strbuf_detach(&sb, NULL); + + return 0; +} + +/** + * For an option opt, recreate the command-line option, appending it to + * opt->value which must be a argv_array. This is useful when we need to pass + * the command-line option, which can be specified multiple times, to another + * command. + */ +int parse_opt_passthru_argv(const struct option *opt, const char *arg, int unset) +{ + static struct strbuf sb = STRBUF_INIT; + struct argv_array *opt_value = opt->value; + + if (recreate_opt(&sb, opt, arg, unset) < 0) + return -1; + + argv_array_push(opt_value, sb.buf); + + return 0; +} diff --git a/parse-options.c b/parse-options.c index 80106c0..3eceba4 100644 --- a/parse-options.c +++ b/parse-options.c @@ -180,6 +180,23 @@ static int get_value(struct parse_opt_ctx_t *p, return opterror(opt, "expects a numerical value", flags); return 0; + case OPTION_MAGNITUDE: + if (unset) { + *(unsigned long *)opt->value = 0; + return 0; + } + if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + *(unsigned long *)opt->value = opt->defval; + return 0; + } + if (get_arg(p, opt, flags, &arg)) + return -1; + if (!git_parse_ulong(arg, opt->value)) + return opterror(opt, + "expects a non-negative integer value with an optional k/m/g suffix", + flags); + return 0; + default: die("should not happen, someone must be hit on the forehead"); } diff --git a/parse-options.h b/parse-options.h index c71e9da..6ca8388 100644 --- a/parse-options.h +++ b/parse-options.h @@ -16,6 +16,7 @@ enum parse_opt_type { /* options with arguments (usually) */ OPTION_STRING, OPTION_INTEGER, + OPTION_MAGNITUDE, OPTION_CALLBACK, OPTION_LOWLEVEL_CALLBACK, OPTION_FILENAME @@ -129,6 +130,8 @@ struct option { #define OPT_CMDMODE(s, l, v, h, i) { OPTION_CMDMODE, (s), (l), (v), NULL, \ (h), PARSE_OPT_NOARG|PARSE_OPT_NONEG, NULL, (i) } #define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), N_("n"), (h) } +#define OPT_MAGNITUDE(s, l, v, h) { OPTION_MAGNITUDE, (s), (l), (v), \ + N_("n"), (h), PARSE_OPT_NONEG } #define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v), (a), (h) } #define OPT_STRING_LIST(s, l, v, a, h) \ { OPTION_CALLBACK, (s), (l), (v), (a), \ @@ -224,6 +227,8 @@ extern int parse_opt_with_commit(const struct option *, const char *, int); 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); +extern int parse_opt_passthru(const struct option *, const char *, int); +extern int parse_opt_passthru_argv(const struct option *, const char *, int); #define OPT__VERBOSE(var, h) OPT_COUNTUP('v', "verbose", (var), (h)) #define OPT__QUIET(var, h) OPT_COUNTUP('q', "quiet", (var), (h)) @@ -242,5 +247,9 @@ extern int parse_opt_noop_cb(const struct option *, const char *, int); OPT_COLOR_FLAG(0, "color", (var), (h)) #define OPT_COLUMN(s, l, v, h) \ { OPTION_CALLBACK, (s), (l), (v), N_("style"), (h), PARSE_OPT_OPTARG, parseopt_column_callback } +#define OPT_PASSTHRU(s, l, v, a, h, f) \ + { OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), parse_opt_passthru } +#define OPT_PASSTHRU_ARGV(s, l, v, a, h, f) \ + { OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), parse_opt_passthru_argv } #endif diff --git a/perl/Git.pm b/perl/Git.pm index 9026a7b..19ef081 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -864,6 +864,73 @@ sub ident_person { return "$ident[0] <$ident[1]>"; } +=item parse_mailboxes + +Return an array of mailboxes extracted from a string. + +=cut + +sub parse_mailboxes { + my $re_comment = qr/\((?:[^)]*)\)/; + my $re_quote = qr/"(?:[^\"\\]|\\.)*"/; + my $re_word = qr/(?:[^]["\s()<>:;@\\,.]|\\.)+/; + + # divide the string in tokens of the above form + my $re_token = qr/(?:$re_quote|$re_word|$re_comment|\S)/; + my @tokens = map { $_ =~ /\s*($re_token)\s*/g } @_; + + # add a delimiter to simplify treatment for the last mailbox + push @tokens, ","; + + my (@addr_list, @phrase, @address, @comment, @buffer) = (); + foreach my $token (@tokens) { + if ($token =~ /^[,;]$/) { + # if buffer still contains undeterminated strings + # append it at the end of @address or @phrase + if (@address) { + push @address, @buffer; + } else { + push @phrase, @buffer; + } + + my $str_phrase = join ' ', @phrase; + my $str_address = join '', @address; + my $str_comment = join ' ', @comment; + + # quote are necessary if phrase contains + # special characters + if ($str_phrase =~ /[][()<>:;@\\,.\000-\037\177]/) { + $str_phrase =~ s/(^|[^\\])"/$1/g; + $str_phrase = qq["$str_phrase"]; + } + + # add "<>" around the address if necessary + if ($str_address ne "" && $str_phrase ne "") { + $str_address = qq[<$str_address>]; + } + + my $str_mailbox = "$str_phrase $str_address $str_comment"; + $str_mailbox =~ s/^\s*|\s*$//g; + push @addr_list, $str_mailbox if ($str_mailbox); + + @phrase = @address = @comment = @buffer = (); + } elsif ($token =~ /^\(/) { + push @comment, $token; + } elsif ($token eq "<") { + push @phrase, (splice @address), (splice @buffer); + } elsif ($token eq ">") { + push @address, (splice @buffer); + } elsif ($token eq "@") { + push @address, (splice @buffer), "@"; + } elsif ($token eq ".") { + push @address, (splice @buffer), "."; + } else { + push @buffer, $token; + } + } + + return @addr_list; +} =item hash_object ( TYPE, FILENAME ) @@ -4,16 +4,51 @@ char packet_buffer[LARGE_PACKET_MAX]; static const char *packet_trace_prefix = "git"; static struct trace_key trace_packet = TRACE_KEY_INIT(PACKET); +static struct trace_key trace_pack = TRACE_KEY_INIT(PACKFILE); void packet_trace_identity(const char *prog) { packet_trace_prefix = xstrdup(prog); } +static int packet_trace_pack(const char *buf, unsigned int len, int sideband) +{ + if (!sideband) { + trace_verbatim(&trace_pack, buf, len); + return 1; + } else if (len && *buf == '\1') { + trace_verbatim(&trace_pack, buf + 1, len - 1); + return 1; + } else { + /* it's another non-pack sideband */ + return 0; + } +} + static void packet_trace(const char *buf, unsigned int len, int write) { int i; struct strbuf out; + static int in_pack, sideband; + + if (!trace_want(&trace_packet) && !trace_want(&trace_pack)) + return; + + if (in_pack) { + if (packet_trace_pack(buf, len, sideband)) + return; + } else if (starts_with(buf, "PACK") || starts_with(buf, "\1PACK")) { + in_pack = 1; + sideband = *buf == '\1'; + packet_trace_pack(buf, len, sideband); + + /* + * Make a note in the human-readable trace that the pack data + * started. + */ + buf = "PACK ..."; + len = strlen(buf); + } if (!trace_want(&trace_packet)) return; @@ -24,22 +59,15 @@ 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 && starts_with(buf, "PACK")) || - (len >= 5 && starts_with(buf+1, "PACK"))) { - strbuf_addstr(&out, "PACK ..."); - trace_disable(&trace_packet); - } - else { - /* XXX we should really handle printable utf8 */ - for (i = 0; i < len; i++) { - /* suppress newlines */ - if (buf[i] == '\n') - continue; - if (buf[i] >= 0x20 && buf[i] <= 0x7e) - strbuf_addch(&out, buf[i]); - else - strbuf_addf(&out, "\\%o", buf[i]); - } + /* XXX we should really handle printable utf8 */ + for (i = 0; i < len; i++) { + /* suppress newlines */ + if (buf[i] == '\n') + continue; + if (buf[i] >= 0x20 && buf[i] <= 0x7e) + strbuf_addch(&out, buf[i]); + else + strbuf_addf(&out, "\\%o", buf[i]); } strbuf_addch(&out, '\n'); @@ -1,7 +1,7 @@ # Bulgarian translation of git po-file. -# Copyright (C) 2014 Alexander Shopov <ash@kambanaria.org>. +# Copyright (C) 2014, 2015 Alexander Shopov <ash@kambanaria.org>. # This file is distributed under the same license as the git package. -# Alexander Shopov <ash@kambanaria.org>, 2014. +# Alexander Shopov <ash@kambanaria.org>, 2014, 2015. # # ======================== # DICTIONARY TO MERGE IN GIT GUI @@ -21,8 +21,8 @@ # git bundle пратка на git # bisect двоично търсене # am прилагам поредица от кръпки -# working directory/tree — винаги работно дърво, git следи цялото дърво, а не директории -# switch to branch прехвърлям се/преминавам към клон +# working directory/tree — винаги работно дърво, git следи цялото дърво, а не директории, работна директория за cwd +# switch to branch преминавам към клон # sparse entry частично изтеглена директория/път # revision range диапазон на версиите # cover letter придружаващо писмо @@ -56,6 +56,10 @@ # mainline базово подаване - при cherry-pick на merge - към коя версия да се изчислява разликата # token лексема # trailer епилог/завършек на съобщение +# cwd текуща работна директория +# untracked cache кеш за неследените файлове +# broken/corrupt повреден +# restore възстановявам # ---- # FAILED to parse неуспешен анализ на... -> неразпозната стойност на # blob обект BLOB @@ -91,11 +95,6 @@ # grep ^#: new.po | cut -c4- |tr ' ' '\n'| sed -e 's/:[0-9][0-9]*//' -e 's%.*/%%' -e 's/[.][^.]*$//' | sort -u # # ======================== -# STRINGS statistics -# ------------------------ -# 2228t -# -# ======================== # MOST IMPORTANT name asc ordering # ------------------------ # add, blame, branch, checkout, clone, commit, common-cmds, config, diff, fetch, fsck, gc, git-rebase, git-stash, grep, log, mv, parse-options, push, reflog, remote, reset, revert, rm, wt-status @@ -105,33 +104,12 @@ # ------------------------ # apply, archive, check-ignore, clean, date, describe, git-am, git-bisect, git-submodule, hash-object, init-db, ls-tree, merge, merge-base, merge-file, merge-recursive, name-rev, pathspec, show-ref, tag # -# ======================== -# WHOLE STATISTICS strings desc, name asc ordering -# ------------------------ -# 144 [remote] 47 [push] 26 [revert] 12 [checkout-index] 6 [prune] 2 [verify-tag] -# 137 [commit] 46 [pack-objects] 24 [mv] 11 [date] 6 [gpg-interface] 2 [update-server-info] -# 114 [branch] 41 [help] 23 [repack] 11 [column] 6 [check-attr] 2 [run-command] -# 111 [apply] 39 [init-db] 21 [rm] 10 [urlmatch] 5 [write-tree] 2 [rerere] -# 101 [notes] 36 [add] 21 [common-cmds] 10 [shortlog] 5 [sha1_name] 2 [read-cache] -# 100 [wt-status] 35 [reset] 19 [show-branch] 10 [merge-file] 5 [rev-parse] 2 [obstack] -# 97 [merge] 35 [archive] 19 [read-tree] 10 [merge-base] 4 [wrapper] 2 [advice] -# 78 [checkout] 34 [config] 19 [bundle] 10 [ls-tree] 4 [prune-packed] 1 [unpack-trees] -# 77 [log] 32 [clean] 16 [parse-options] 10 [hash-object] 4 [notes-utils] 1 [unpack-objects] -# 69 [clone] 30 [git-am] 15 [fsck] 10 [for-each-ref] 4 [mktree] 1 [progress] -# 68 [index-pack] 30 [describe] 14 [show-ref] 10 [cat-file] 4 [check-mailmap] 1 [precompose_utf8] -# 68 [fetch] 29 [git-stash] 14 [gc] 9 [update-ref] 3 [verify-pack] 1 [object] -# 64 [tag] 29 [git-bisect] 14 [fast-export] 9 [submodule] 3 [reflog] 1 [git] -# 64 [grep] 28 [update-index] 13 [diff] 8 [replace] 3 [pack-refs] 1 [diffcore-rename] -# 56 [sequencer] 28 [blame] 13 [check-ignore] 8 [git-pull] 3 [count-objects] 1 [diffcore-order] -# 55 [merge-recursive] 27 [ls-files] 12 [pathspec] 8 [fmt-merge-msg] 3 [connected] 1 [attr] -# 54 [git-submodule] 27 [git-rebase] 12 [name-rev] 7 [symbolic-ref] 3 [bisect--helper] -# msgid "" msgstr "" "Project-Id-Version: git master\n" "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n" -"POT-Creation-Date: 2015-04-03 08:13+0800\n" -"PO-Revision-Date: 2015-04-07 11:21+0300\n" +"POT-Creation-Date: 2015-07-14 07:19+0800\n" +"PO-Revision-Date: 2015-07-14 19:05+0300\n" "Last-Translator: Alexander Shopov <ash@kambanaria.org>\n" "Language-Team: Bulgarian <dict@fsa-bg.org>\n" "Language: bg\n" @@ -173,7 +151,7 @@ msgstr "" msgid "git archive --remote <repo> [--exec <cmd>] --list" msgstr "git archive --remote ХРАНИЛИЩЕ [--exec КОМАНДА] --list" -#: archive.c:342 builtin/add.c:137 builtin/add.c:427 builtin/rm.c:328 +#: archive.c:342 builtin/add.c:137 builtin/add.c:428 builtin/rm.c:327 #, c-format msgid "pathspec '%s' did not match any files" msgstr "пътят „%s“ не съвпада с никой файл" @@ -196,9 +174,9 @@ msgstr "добавяне на този префикс към всеки път #: archive.c:430 builtin/archive.c:88 builtin/blame.c:2516 #: builtin/blame.c:2517 builtin/config.c:57 builtin/fast-export.c:986 -#: builtin/fast-export.c:988 builtin/grep.c:712 builtin/hash-object.c:101 +#: builtin/fast-export.c:988 builtin/grep.c:712 builtin/hash-object.c:99 #: builtin/ls-files.c:446 builtin/ls-files.c:449 builtin/notes.c:394 -#: builtin/notes.c:557 builtin/read-tree.c:109 parse-options.h:151 +#: builtin/notes.c:557 builtin/read-tree.c:109 parse-options.h:150 msgid "file" msgstr "файл" @@ -230,7 +208,7 @@ msgstr "добро компресиране" msgid "list supported archive formats" msgstr "извеждане на списъка с поддържаните формати" -#: archive.c:449 builtin/archive.c:90 builtin/clone.c:86 +#: archive.c:449 builtin/archive.c:90 builtin/clone.c:77 msgid "repo" msgstr "хранилище" @@ -246,7 +224,7 @@ msgstr "команда" msgid "path to the remote git-upload-archive command" msgstr "път към отдалечената команда „git-upload-archive“" -#: attr.c:264 +#: attr.c:265 msgid "" "Negative patterns are ignored in git attributes\n" "Use '\\!' for literal leading exclamation." @@ -386,8 +364,8 @@ msgid "Repository lacks these prerequisite commits:" msgstr "В хранилището липсват следните необходими подавания:" #: bundle.c:163 sequencer.c:650 sequencer.c:1105 builtin/blame.c:2705 -#: builtin/branch.c:659 builtin/commit.c:1045 builtin/log.c:330 -#: builtin/log.c:823 builtin/log.c:1432 builtin/log.c:1669 builtin/merge.c:358 +#: builtin/branch.c:651 builtin/commit.c:1045 builtin/log.c:330 +#: builtin/log.c:825 builtin/log.c:1432 builtin/log.c:1666 builtin/merge.c:358 #: builtin/shortlog.c:158 msgid "revision walk setup failed" msgstr "неуспешно настройване на обхождането на версиите" @@ -639,17 +617,17 @@ msgstr "" "Грешки в настройката „diff.dirstat“:\n" "%s" -#: diff.c:2956 +#: diff.c:2997 #, c-format msgid "external diff died, stopping at %s" msgstr "" "външната програма за разлики завърши неуспешно. Спиране на работата при „%s“" -#: diff.c:3351 +#: diff.c:3393 msgid "--follow requires exactly one pathspec" msgstr "Опцията „--follow“ изисква точно един път" -#: diff.c:3514 +#: diff.c:3556 #, c-format msgid "" "Failed to parse --dirstat/-X option parameter:\n" @@ -658,11 +636,19 @@ msgstr "" "Неразпознат параметър към опцията „--dirstat/-X“:\n" "%s" -#: diff.c:3528 +#: diff.c:3570 #, c-format msgid "Failed to parse --submodule option parameter: '%s'" msgstr "Неразпознат параметър към опцията „--submodule“: „%s“" +#: dir.c:1852 +msgid "failed to get kernel name and information" +msgstr "името и версията на ядрото не бяха получени" + +#: dir.c:1945 +msgid "Untracked cache is disabled on this system." +msgstr "Кеша за неследените файлове е изключен на тази система" + #: gpg-interface.c:129 gpg-interface.c:200 msgid "could not run gpg." msgstr "Програмата „gpg“ не може да бъде стартирана." @@ -709,11 +695,11 @@ msgstr "налични команди на git от „%s“" msgid "git commands available from elsewhere on your $PATH" msgstr "команди на git от други директории от „$PATH“" -#: help.c:230 -msgid "The most commonly used git commands are:" -msgstr "Най-често употребяваните команди на git са:" +#: help.c:246 +msgid "These are common Git commands used in various situations:" +msgstr "Това са най-често използваните команди на Git:" -#: help.c:289 +#: help.c:311 #, c-format msgid "" "'%s' appears to be a git command, but we were not\n" @@ -723,11 +709,11 @@ msgstr "" "бъде изпълнена. Вероятно пакетът „git-%s“ е повреден." # FIXME bad message -#: help.c:346 +#: help.c:368 msgid "Uh oh. Your system reports no Git commands at all." msgstr "Странно, изглежда, че на системата ви няма нито една команда на git." -#: help.c:368 +#: help.c:390 #, c-format msgid "" "WARNING: You called a Git command named '%s', which does not exist.\n" @@ -738,17 +724,17 @@ msgstr "" "съществува. Изпълнението автоматично продължава, като се счита, че имате " "предвид „%s“" -#: help.c:373 +#: help.c:395 #, c-format msgid "in %0.1f seconds automatically..." msgstr "след %0.1f секунди…" -#: help.c:380 +#: help.c:402 #, c-format msgid "git: '%s' is not a git command. See 'git --help'." -msgstr "git: „%s“ не е команда на git. Вижте изхода от „git --help“." +msgstr "git: „%s“ не е команда на git. Погледнете изхода от „git --help“." -#: help.c:384 help.c:444 +#: help.c:406 help.c:466 msgid "" "\n" "Did you mean this?" @@ -762,16 +748,16 @@ msgstr[1] "" "\n" "Команди с подобно име са:" -#: help.c:440 +#: help.c:462 #, c-format msgid "%s: %s - %s" msgstr "%s: %s — %s" -#: lockfile.c:283 +#: lockfile.c:345 msgid "BUG: reopen a lockfile that is still open" msgstr "ГРЕШКА В GIT: преотваряне на файл-ключалка" -#: lockfile.c:285 +#: lockfile.c:347 msgid "BUG: reopen a lockfile that has been committed" msgstr "ГРЕШКА В GIT: преотваряне на файл-ключалка, който е подаден" @@ -779,8 +765,8 @@ msgstr "ГРЕШКА В GIT: преотваряне на файл-ключалк msgid "failed to read the cache" msgstr "кешът не може да бъде прочетен" -#: merge.c:94 builtin/checkout.c:374 builtin/checkout.c:580 -#: builtin/clone.c:662 +#: merge.c:94 builtin/checkout.c:376 builtin/checkout.c:587 +#: builtin/clone.c:647 msgid "unable to write new index file" msgstr "неуспешно записване на новия индекс" @@ -798,67 +784,67 @@ msgstr "неуспешно изпълнение на „addinfo_cache“ за п msgid "error building trees" msgstr "грешка при изграждане на дърветата" -#: merge-recursive.c:688 +#: merge-recursive.c:687 #, c-format msgid "failed to create path '%s'%s" msgstr "грешка при създаването на пътя „%s“%s" -#: merge-recursive.c:699 +#: merge-recursive.c:698 #, c-format msgid "Removing %s to make room for subdirectory\n" msgstr "Изтриване на „%s“, за да се освободи място за поддиректория\n" # FIXME better message -#: merge-recursive.c:713 merge-recursive.c:734 +#: merge-recursive.c:712 merge-recursive.c:733 msgid ": perhaps a D/F conflict?" msgstr ": възможно е да има конфликт директория/файл." -#: merge-recursive.c:724 +#: merge-recursive.c:723 #, c-format msgid "refusing to lose untracked file at '%s'" msgstr "" "преустановяване на действието, за да не се изтрие неследеният файл „%s“" -#: merge-recursive.c:764 +#: merge-recursive.c:763 #, c-format msgid "cannot read object %s '%s'" msgstr "обектът „%s“ (%s) не може да бъде прочетен" -#: merge-recursive.c:766 +#: merge-recursive.c:765 #, c-format msgid "blob expected for %s '%s'" msgstr "обектът „%s“ (%s) се очакваше да е BLOB, а не е" -#: merge-recursive.c:789 builtin/clone.c:321 +#: merge-recursive.c:788 builtin/clone.c:306 #, c-format msgid "failed to open '%s'" msgstr "директорията „%s“ не може да бъде отворена" -#: merge-recursive.c:797 +#: merge-recursive.c:796 #, c-format msgid "failed to symlink '%s'" msgstr "неуспешно създаване на символната връзка „%s“" -#: merge-recursive.c:800 +#: merge-recursive.c:799 #, c-format msgid "do not know what to do with %06o %s '%s'" msgstr "" "не е ясно какво да се прави с обекта „%2$s“ (%3$s) с права за достъп „%1$06o“" -#: merge-recursive.c:938 +#: merge-recursive.c:937 msgid "Failed to execute internal merge" msgstr "Неуспешно вътрешно сливане" -#: merge-recursive.c:942 +#: merge-recursive.c:941 #, c-format msgid "Unable to add %s to database" msgstr "„%s“ не може да се добави в базата с данни" -#: merge-recursive.c:958 +#: merge-recursive.c:957 msgid "unsupported object type in the tree" msgstr "в дървото има неподдържан вид обект" -#: merge-recursive.c:1033 merge-recursive.c:1047 +#: merge-recursive.c:1032 merge-recursive.c:1046 #, c-format msgid "" "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left " @@ -867,7 +853,7 @@ msgstr "" "КОНФЛИКТ (%s/изтриване): „%s“ е изтрит в %s, а „%s“ в %s. Версия %s на „%s“ " "е оставена в дървото." -#: merge-recursive.c:1039 merge-recursive.c:1052 +#: merge-recursive.c:1038 merge-recursive.c:1051 #, c-format msgid "" "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left " @@ -876,20 +862,20 @@ msgstr "" "КОНФЛИКТ (%s/изтриване): „%s“ е изтрит в %s, а „%s“ в %s. Версия %s на „%s“ " "е оставена в дървото: %s." -#: merge-recursive.c:1093 +#: merge-recursive.c:1092 msgid "rename" msgstr "преименуване" -#: merge-recursive.c:1093 +#: merge-recursive.c:1092 msgid "renamed" msgstr "преименуван" -#: merge-recursive.c:1149 +#: merge-recursive.c:1148 #, c-format msgid "%s is a directory in %s adding as %s instead" msgstr "„%s“ е директория в „%s“, затова се добавя като „%s“" -#: merge-recursive.c:1171 +#: merge-recursive.c:1170 #, c-format msgid "" "CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s" @@ -898,151 +884,151 @@ msgstr "" "КОНФЛИКТ (преименуване/преименуване): „%s“ е преименуван на „%s“ в клон " "„%s“, а „%s“ е преименуван на „%s“ в „%s“/%s." -#: merge-recursive.c:1176 +#: merge-recursive.c:1175 msgid " (left unresolved)" msgstr " (некоригиран конфликт)" -#: merge-recursive.c:1230 +#: merge-recursive.c:1229 #, c-format msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s" msgstr "" "КОНФЛИКТ (преименуване/преименуване): „%s“ е преименуван на „%s“ в клон " "„%s“, а „%s“ е преименуван на „%s“ в „%s“" -#: merge-recursive.c:1260 +#: merge-recursive.c:1259 #, c-format msgid "Renaming %s to %s and %s to %s instead" msgstr "Преименуване на „%s“ на „%s“, а „%s“ на „%s“" -#: merge-recursive.c:1459 +#: merge-recursive.c:1458 #, c-format msgid "CONFLICT (rename/add): Rename %s->%s in %s. %s added in %s" msgstr "" "КОНФЛИКТ (преименуване/добавяне): „%s“ е преименуван на „%s“ в клон „%s“, а " "„%s“ е добавен в „%s“" -#: merge-recursive.c:1469 +#: merge-recursive.c:1468 #, c-format msgid "Adding merged %s" msgstr "Добавяне на слетия „%s“" -#: merge-recursive.c:1474 merge-recursive.c:1672 +#: merge-recursive.c:1473 merge-recursive.c:1671 #, c-format msgid "Adding as %s instead" msgstr "Добавяне като „%s“" -#: merge-recursive.c:1525 +#: merge-recursive.c:1524 #, c-format msgid "cannot read object %s" msgstr "обектът „%s“ не може да се прочете" -#: merge-recursive.c:1528 +#: merge-recursive.c:1527 #, c-format msgid "object %s is not a blob" msgstr "обектът „%s“ не е BLOB" -#: merge-recursive.c:1576 +#: merge-recursive.c:1575 msgid "modify" msgstr "промяна" -#: merge-recursive.c:1576 +#: merge-recursive.c:1575 msgid "modified" msgstr "променен" -#: merge-recursive.c:1586 +#: merge-recursive.c:1585 msgid "content" msgstr "съдържание" -#: merge-recursive.c:1593 +#: merge-recursive.c:1592 msgid "add/add" msgstr "добавяне/добавяне" -#: merge-recursive.c:1627 +#: merge-recursive.c:1626 #, c-format msgid "Skipped %s (merged same as existing)" msgstr "Прескачане на „%s“ (слетият резултат е идентичен със сегашния)" -#: merge-recursive.c:1641 +#: merge-recursive.c:1640 #, c-format msgid "Auto-merging %s" msgstr "Автоматично сливане на „%s“" -#: merge-recursive.c:1645 git-submodule.sh:1150 +#: merge-recursive.c:1644 git-submodule.sh:1150 msgid "submodule" msgstr "ПОДМОДУЛ" -#: merge-recursive.c:1646 +#: merge-recursive.c:1645 #, c-format msgid "CONFLICT (%s): Merge conflict in %s" msgstr "КОНФЛИКТ (%s): Конфликт при сливане на „%s“" -#: merge-recursive.c:1732 +#: merge-recursive.c:1731 #, c-format msgid "Removing %s" msgstr "Изтриване на „%s“" -#: merge-recursive.c:1757 +#: merge-recursive.c:1756 msgid "file/directory" msgstr "файл/директория" -#: merge-recursive.c:1763 +#: merge-recursive.c:1762 msgid "directory/file" msgstr "директория/файл" -#: merge-recursive.c:1768 +#: merge-recursive.c:1767 #, c-format msgid "CONFLICT (%s): There is a directory with name %s in %s. Adding %s as %s" msgstr "" "КОНФЛИКТ (%s): Съществува директория на име „%s“ в „%s“. Добавяне на „%s“ " "като „%s“" -#: merge-recursive.c:1778 +#: merge-recursive.c:1777 #, c-format msgid "Adding %s" msgstr "Добавяне на „%s“" -#: merge-recursive.c:1795 +#: merge-recursive.c:1794 msgid "Fatal merge failure, shouldn't happen." msgstr "Фатална грешка при сливане, а такава не трябва да възниква!" -#: merge-recursive.c:1814 +#: merge-recursive.c:1813 msgid "Already up-to-date!" msgstr "Вече е обновено!" -#: merge-recursive.c:1823 +#: merge-recursive.c:1822 #, c-format msgid "merging of trees %s and %s failed" msgstr "неуспешно сливане на дърветата „%s“ и „%s“" # FIXME message -#: merge-recursive.c:1853 +#: merge-recursive.c:1852 #, c-format msgid "Unprocessed path??? %s" msgstr "" "Пътят „%s“ не е обработен, това е грешка в Git, докладвайте я на " "разработчиците, като пратите е-писмо на адрес: „git@vger.kernel.org“." -#: merge-recursive.c:1901 +#: merge-recursive.c:1900 msgid "Merging:" msgstr "Сливане:" -#: merge-recursive.c:1914 +#: merge-recursive.c:1913 #, c-format msgid "found %u common ancestor:" msgid_plural "found %u common ancestors:" msgstr[0] "открит е %u общ предшественик:" msgstr[1] "открити са %u общи предшественици:" -#: merge-recursive.c:1951 +#: merge-recursive.c:1950 msgid "merge returned no commit" msgstr "сливането не върна подаване" -#: merge-recursive.c:2008 +#: merge-recursive.c:2007 #, c-format msgid "Could not parse object '%s'" msgstr "Неуспешен анализ на обекта „%s“" -#: merge-recursive.c:2019 builtin/merge.c:667 +#: merge-recursive.c:2018 builtin/merge.c:645 msgid "Unable to write index." msgstr "Индексът не може да бъде прочетен" @@ -1069,7 +1055,7 @@ msgstr "" msgid "Bad %s value: '%s'" msgstr "Зададена е лоша стойност на променливата „%s“: „%s“" -#: object.c:241 +#: object.c:242 #, c-format msgid "unable to parse object: %s" msgstr "обектът „%s“ не може да бъде анализиран" @@ -1167,11 +1153,11 @@ msgstr "" msgid "unable to parse --pretty format" msgstr "аргументът към опцията „--pretty“ не може да се анализира" -#: progress.c:225 +#: progress.c:236 msgid "done" msgstr "действието завърши" -#: read-cache.c:1275 +#: read-cache.c:1295 #, c-format msgid "" "index.version set, but the value is invalid.\n" @@ -1180,7 +1166,7 @@ msgstr "" "Зададена е неправилна стойност на настройката „index.version“.\n" "Ще се ползва версия %i" -#: read-cache.c:1285 +#: read-cache.c:1305 #, c-format msgid "" "GIT_INDEX_VERSION set, but the value is invalid.\n" @@ -1190,65 +1176,109 @@ msgstr "" "„GIT_INDEX_VERSION“.\n" "Ще се ползва версия %i" -#: remote.c:782 +#: remote.c:792 #, c-format msgid "Cannot fetch both %s and %s to %s" msgstr "Невъзможно е да се доставят едновременно и „%s“, и „%s“ към „%s“" -#: remote.c:786 +#: remote.c:796 #, c-format msgid "%s usually tracks %s, not %s" msgstr "„%s“ обикновено следи „%s“, а не „%s“" -#: remote.c:790 +#: remote.c:800 #, c-format msgid "%s tracks both %s and %s" msgstr "„%s“ следи както „%s“, така и „%s“" -#: remote.c:798 +#: remote.c:808 msgid "Internal error" msgstr "Вътрешна грешка" -#: remote.c:1980 +#: remote.c:1723 remote.c:1766 +msgid "HEAD does not point to a branch" +msgstr "Указателят „HEAD“ не сочи към клон" + +#: remote.c:1732 +#, c-format +msgid "no such branch: '%s'" +msgstr "няма клон на име „%s“" + +#: remote.c:1735 +#, c-format +msgid "no upstream configured for branch '%s'" +msgstr "не е зададен клон-източник за клона „%s“" + +#: remote.c:1741 +#, c-format +msgid "upstream branch '%s' not stored as a remote-tracking branch" +msgstr "клонът-източник „%s“ не е съхранен като следящ клон" + +#: remote.c:1756 +#, c-format +msgid "push destination '%s' on remote '%s' has no local tracking branch" +msgstr "" +"липсва локален следящ клон за местоположението за изтласкване „%s“ в " +"хранилището „%s“" + +#: remote.c:1771 +#, c-format +msgid "branch '%s' has no remote for pushing" +msgstr "няма информация клонът „%s“ да следи някой друг" + +#: remote.c:1782 +#, c-format +msgid "push refspecs for '%s' do not include '%s'" +msgstr "указателят за изтласкване на „%s“ не включва „%s“" + +#: remote.c:1795 +msgid "push has no destination (push.default is 'nothing')" +msgstr "указателят за изтласкване не включва цел („push.default“ е „nothing“)" + +#: remote.c:1817 +msgid "cannot resolve 'simple' push to a single destination" +msgstr "простото (simple) изтласкване не съответства на една цел" + +#: remote.c:2124 #, c-format msgid "Your branch is based on '%s', but the upstream is gone.\n" msgstr "Този клон следи „%s“, но следеният клон е изтрит.\n" -#: remote.c:1984 +#: remote.c:2128 msgid " (use \"git branch --unset-upstream\" to fixup)\n" msgstr " (за да коригирате това, използвайте „git branch --unset-upstream“)\n" -#: remote.c:1987 +#: remote.c:2131 #, c-format msgid "Your branch is up-to-date with '%s'.\n" msgstr "Клонът е актуализиран към „%s“.\n" -#: remote.c:1991 +#: remote.c:2135 #, c-format msgid "Your branch is ahead of '%s' by %d commit.\n" msgid_plural "Your branch is ahead of '%s' by %d commits.\n" msgstr[0] "Клонът ви е с %2$d подаване пред „%1$s“.\n" msgstr[1] "Клонът ви е с %2$d подавания пред „%1$s“.\n" -#: remote.c:1997 +#: remote.c:2141 msgid " (use \"git push\" to publish your local commits)\n" msgstr " (публикувайте локалните си промени чрез „git push“)\n" -#: remote.c:2000 +#: remote.c:2144 #, c-format msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n" msgid_plural "" "Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n" msgstr[0] "" -"Клонът ви е с %2$d подаване след „%1$s“ и може да бъде тривиално слят.\n" +"Клонът ви е с %2$d подаване зад „%1$s“ и може да бъде тривиално слят.\n" msgstr[1] "" -"Клонът ви е с %2$d подавания след „%1$s“ и може да бъде тривиално слят.\n" +"Клонът ви е с %2$d подавания зад „%1$s“ и може да бъде тривиално слят.\n" -#: remote.c:2008 +#: remote.c:2152 msgid " (use \"git pull\" to update your local branch)\n" msgstr " (обновете локалния си клон чрез „git pull“)\n" -#: remote.c:2011 +#: remote.c:2155 #, c-format msgid "" "Your branch and '%s' have diverged,\n" @@ -1263,11 +1293,11 @@ msgstr[1] "" "Текущият клон се е отделил от „%s“,\n" "двата имат съответно по %d и %d несъвпадащи подавания.\n" -#: remote.c:2021 +#: remote.c:2165 msgid " (use \"git pull\" to merge the remote branch into yours)\n" msgstr " (слейте отдалечения клон в локалния чрез „git pull“)\n" -#: revision.c:2348 +#: revision.c:2366 msgid "--first-parent is incompatible with --bisect" msgstr "опциите „--first-parent“ и „--bisect“ са несъвместими" @@ -1284,22 +1314,22 @@ msgstr "неуспешно изпълнение на dup2(%d,%d)" msgid "failed to sign the push certificate" msgstr "сертификатът за изтласкване не може да бъде подписан" -#: send-pack.c:356 +#: send-pack.c:378 msgid "the receiving end does not support --signed push" msgstr "отсрещната страна не поддържа изтласкване с опцията „--signed“" -#: send-pack.c:366 +#: send-pack.c:389 msgid "the receiving end does not support --atomic push" msgstr "получаващата страна не поддържа изтласкване с опцията „--atomic“" -#: sequencer.c:172 builtin/merge.c:782 builtin/merge.c:893 builtin/merge.c:995 -#: builtin/merge.c:1005 +#: sequencer.c:172 builtin/merge.c:760 builtin/merge.c:871 builtin/merge.c:973 +#: builtin/merge.c:983 #, c-format msgid "Could not open '%s' for writing" msgstr "„%s“ не може да бъде отворен за запис" -#: sequencer.c:174 builtin/merge.c:344 builtin/merge.c:785 builtin/merge.c:997 -#: builtin/merge.c:1010 +#: sequencer.c:174 builtin/merge.c:344 builtin/merge.c:763 builtin/merge.c:975 +#: builtin/merge.c:988 #, c-format msgid "Could not write to '%s'" msgstr "„%s“ не може да бъде записан" @@ -1505,7 +1535,7 @@ msgstr "" "действието не може да бъде преустановено, когато сте на клон, който тепърва " "предстои да бъде създаден" -#: sequencer.c:908 builtin/apply.c:4288 +#: sequencer.c:908 builtin/apply.c:4291 #, c-format msgid "cannot open %s: %s" msgstr "файлът „%s“ не може да бъде отворен: %s" @@ -1549,7 +1579,12 @@ msgstr "Първоначалното подаване не може да бъд msgid "Can't cherry-pick into empty head" msgstr "При празен връх не могат да се отбират подавания" -#: sha1_name.c:440 +#: setup.c:243 +#, c-format +msgid "failed to read %s" +msgstr "файлът „%s“ не може да бъде прочетен" + +#: sha1_name.c:453 msgid "" "Git normally never creates a ref that ends with 40 hex characters\n" "because it will be ignored when you just specify 40-hex. These refs\n" @@ -1573,25 +1608,6 @@ msgstr "" "спрете това съобщение като изпълните командата:\n" "„git config advice.objectNameWarning false“" -#: sha1_name.c:1068 -msgid "HEAD does not point to a branch" -msgstr "Указателят „HEAD“ не сочи към клон" - -#: sha1_name.c:1071 -#, c-format -msgid "No such branch: '%s'" -msgstr "Не съществува клон на име „%s“" - -#: sha1_name.c:1073 -#, c-format -msgid "No upstream configured for branch '%s'" -msgstr "Не е зададен клон-източник за клона „%s“" - -#: sha1_name.c:1077 -#, c-format -msgid "Upstream branch '%s' not stored as a remote-tracking branch" -msgstr "Клонът-източник „%s“ не е съхранен като следящ клон" - #: submodule.c:64 submodule.c:98 msgid "Cannot change unmerged .gitmodules, resolve merge conflicts first" msgstr "" @@ -1617,12 +1633,7 @@ msgstr "Записът „%s“ във файла „.gitmodules“ не мож msgid "staging updated .gitmodules failed" msgstr "неуспешно добавяне на променения файл „.gitmodules“ в индекса" -#: submodule.c:1109 builtin/init-db.c:371 -#, c-format -msgid "Could not create git link %s" -msgstr "Връзката в Git „%s“ не може да бъде създадена" - -#: submodule.c:1120 +#: submodule.c:1115 #, c-format msgid "Could not set core.worktree in %s" msgstr "Настройката „core.worktree“ не може да се зададе в „%s“" @@ -1652,7 +1663,7 @@ msgstr "входният файл „%s“ не може да бъде проч msgid "could not read from stdin" msgstr "от стандартния вход не може да се чете" -#: unpack-trees.c:202 +#: unpack-trees.c:203 msgid "Checking out files" msgstr "Изтегляне на файлове" @@ -1708,6 +1719,22 @@ msgstr "такъв потребител не съществува" msgid "unable to get current working directory" msgstr "текущата работна директория е недостъпна" +#: wrapper.c:575 +#, c-format +msgid "could not open %s for writing" +msgstr "„%s“ не може да бъде отворен за запис" + +# FIXME - must be the same as Could not write to '%s' above +#: wrapper.c:587 +#, c-format +msgid "could not write to %s" +msgstr "„%s“ не може да бъде записан" + +#: wrapper.c:593 +#, c-format +msgid "could not close %s" +msgstr "„%s“ не може да се затвори" + #: wt-status.c:150 msgid "Unmerged paths:" msgstr "Неслети пътища:" @@ -1738,11 +1765,11 @@ msgid " (use \"git rm <file>...\" to mark resolution)" msgstr "" " (използвайте „git rm ФАЙЛ…“, за да укажете разрешаването на конфликта)" -#: wt-status.c:198 wt-status.c:878 +#: wt-status.c:198 wt-status.c:881 msgid "Changes to be committed:" msgstr "Промени, които ще бъдат подадени:" -#: wt-status.c:216 wt-status.c:887 +#: wt-status.c:216 wt-status.c:890 msgid "Changes not staged for commit:" msgstr "Промени, които не са в индекса за подаване:" @@ -1857,15 +1884,15 @@ msgstr "неследено съдържание, " msgid "bug: unhandled diff status %c" msgstr "грешка: състоянието на промяната „%c“ не може да бъде обработено" -#: wt-status.c:753 +#: wt-status.c:755 msgid "Submodules changed but not updated:" msgstr "Подмодулите са променени, но не са обновени:" -#: wt-status.c:755 +#: wt-status.c:757 msgid "Submodule changes to be committed:" msgstr "Промени в подмодулите за подаване:" -#: wt-status.c:835 +#: wt-status.c:838 msgid "" "Do not touch the line above.\n" "Everything below will be removed." @@ -1873,193 +1900,193 @@ msgstr "" "Не променяйте горния ред.\n" "Всичко отдолу ще бъде изтрито." -#: wt-status.c:946 +#: wt-status.c:949 msgid "You have unmerged paths." msgstr "Някои пътища не са слети." -#: wt-status.c:949 +#: wt-status.c:952 msgid " (fix conflicts and run \"git commit\")" msgstr " (коригирайте конфликтите и изпълнете „git commit“)" -#: wt-status.c:952 +#: wt-status.c:955 msgid "All conflicts fixed but you are still merging." msgstr "Всички конфликти са решени, но продължавате сливането." -#: wt-status.c:955 +#: wt-status.c:958 msgid " (use \"git commit\" to conclude merge)" msgstr " (използвайте „git commit“, за да завършите сливането)" -#: wt-status.c:965 +#: wt-status.c:968 msgid "You are in the middle of an am session." msgstr "В момента прилагате поредица от кръпки чрез „git am“." -#: wt-status.c:968 +#: wt-status.c:971 msgid "The current patch is empty." msgstr "Текущата кръпка е празна." -#: wt-status.c:972 +#: wt-status.c:975 msgid " (fix conflicts and then run \"git am --continue\")" msgstr " (коригирайте конфликтите и изпълнете „git am --continue“)" -#: wt-status.c:974 +#: wt-status.c:977 msgid " (use \"git am --skip\" to skip this patch)" msgstr " (използвайте „git am --skip“, за да пропуснете тази кръпка)" -#: wt-status.c:976 +#: wt-status.c:979 msgid " (use \"git am --abort\" to restore the original branch)" msgstr "" " (използвайте „git am --abort“, за да възстановите първоначалния клон)" -#: wt-status.c:1036 wt-status.c:1053 +#: wt-status.c:1039 wt-status.c:1056 #, c-format msgid "You are currently rebasing branch '%s' on '%s'." msgstr "В момента пребазирате клона „%s“ върху „%s“." -#: wt-status.c:1041 wt-status.c:1058 +#: wt-status.c:1044 wt-status.c:1061 msgid "You are currently rebasing." msgstr "В момента пребазирате." -#: wt-status.c:1044 +#: wt-status.c:1047 msgid " (fix conflicts and then run \"git rebase --continue\")" msgstr " (коригирайте конфликтите и използвайте „git rebase --continue“)" -#: wt-status.c:1046 +#: wt-status.c:1049 msgid " (use \"git rebase --skip\" to skip this patch)" msgstr " (използвайте „git rebase --skip“, за да пропуснете тази кръпка)" -#: wt-status.c:1048 +#: wt-status.c:1051 msgid " (use \"git rebase --abort\" to check out the original branch)" msgstr "" " (използвайте „git rebase --abort“, за да възстановите първоначалния клон)" -#: wt-status.c:1061 +#: wt-status.c:1064 msgid " (all conflicts fixed: run \"git rebase --continue\")" msgstr " (всички конфликти са коригирани: изпълнете „git rebase --continue“)" -#: wt-status.c:1065 +#: wt-status.c:1068 #, c-format msgid "" "You are currently splitting a commit while rebasing branch '%s' on '%s'." msgstr "В момента разделяте подаване докато пребазирате клона „%s“ върху „%s“." -#: wt-status.c:1070 +#: wt-status.c:1073 msgid "You are currently splitting a commit during a rebase." msgstr "В момента разделяте подаване докато пребазирате." -#: wt-status.c:1073 +#: wt-status.c:1076 msgid " (Once your working directory is clean, run \"git rebase --continue\")" msgstr "" " (След като работното ви дърво стане чисто, използвайте „git rebase --" "continue“)" -#: wt-status.c:1077 +#: wt-status.c:1080 #, c-format msgid "You are currently editing a commit while rebasing branch '%s' on '%s'." msgstr "" "В момента редактирате подаване докато пребазирате клона „%s“ върху „%s“." -#: wt-status.c:1082 +#: wt-status.c:1085 msgid "You are currently editing a commit during a rebase." msgstr "В момента редактирате подаване докато пребазирате." -#: wt-status.c:1085 +#: wt-status.c:1088 msgid " (use \"git commit --amend\" to amend the current commit)" msgstr "" " (използвайте „git commit --amend“, за да редактирате текущото подаване)" -#: wt-status.c:1087 +#: wt-status.c:1090 msgid "" " (use \"git rebase --continue\" once you are satisfied with your changes)" msgstr "" " (използвайте „git rebase --continue“, след като завършите промените си)" -#: wt-status.c:1097 +#: wt-status.c:1100 #, c-format msgid "You are currently cherry-picking commit %s." msgstr "В момента отбирате подаването „%s“." -#: wt-status.c:1102 +#: wt-status.c:1105 msgid " (fix conflicts and run \"git cherry-pick --continue\")" msgstr " (коригирайте конфликтите и изпълнете „git cherry-pick --continue“)" -#: wt-status.c:1105 +#: wt-status.c:1108 msgid " (all conflicts fixed: run \"git cherry-pick --continue\")" msgstr "" " (всички конфликти са коригирани, изпълнете „git cherry-pick --continue“)" -#: wt-status.c:1107 +#: wt-status.c:1110 msgid " (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)" msgstr "" " (използвайте „git cherry-pick --abort“, за да отмените всички действия с " "отбиране)" -#: wt-status.c:1116 +#: wt-status.c:1119 #, c-format msgid "You are currently reverting commit %s." msgstr "В момента отменяте подаване „%s“." -#: wt-status.c:1121 +#: wt-status.c:1124 msgid " (fix conflicts and run \"git revert --continue\")" msgstr " (коригирайте конфликтите и изпълнете „git revert --continue“)" -#: wt-status.c:1124 +#: wt-status.c:1127 msgid " (all conflicts fixed: run \"git revert --continue\")" msgstr " (всички конфликти са коригирани, изпълнете „git revert --continue“)" -#: wt-status.c:1126 +#: wt-status.c:1129 msgid " (use \"git revert --abort\" to cancel the revert operation)" msgstr "" " (използвайте „git revert --abort“, за да преустановите отмяната на " "подаване)" -#: wt-status.c:1137 +#: wt-status.c:1140 #, c-format msgid "You are currently bisecting, started from branch '%s'." msgstr "В момента търсите двоично, като сте стартирали от клон „%s“." -#: wt-status.c:1141 +#: wt-status.c:1144 msgid "You are currently bisecting." msgstr "В момента търсите двоично." -#: wt-status.c:1144 +#: wt-status.c:1147 msgid " (use \"git bisect reset\" to get back to the original branch)" msgstr "" " (използвайте „git bisect reset“, за да се върнете към първоначалното " "състояние и клон)" -#: wt-status.c:1321 +#: wt-status.c:1324 msgid "On branch " msgstr "На клон " -#: wt-status.c:1328 +#: wt-status.c:1331 msgid "rebase in progress; onto " msgstr "извършвате пребазиране върху " -#: wt-status.c:1333 +#: wt-status.c:1336 msgid "HEAD detached at " msgstr "Указателят „HEAD“ не е свързан и е при " -#: wt-status.c:1335 +#: wt-status.c:1338 msgid "HEAD detached from " msgstr "Указателят „HEAD“ не е свързан и е отделѐн от " -#: wt-status.c:1338 +#: wt-status.c:1341 msgid "Not currently on any branch." msgstr "Извън всички клони." -#: wt-status.c:1355 +#: wt-status.c:1358 msgid "Initial commit" msgstr "Първоначално подаване" -#: wt-status.c:1369 +#: wt-status.c:1372 msgid "Untracked files" msgstr "Неследени файлове" -#: wt-status.c:1371 +#: wt-status.c:1374 msgid "Ignored files" msgstr "Игнорирани файлове" -#: wt-status.c:1375 +#: wt-status.c:1378 #, c-format msgid "" "It took %.2f seconds to enumerate untracked files. 'status -uno'\n" @@ -2071,32 +2098,32 @@ msgstr "" "изпълнението, но не трябва да забравяте ръчно да добавяте новите файлове.\n" "За повече подробности погледнете „git status help“." -#: wt-status.c:1381 +#: wt-status.c:1384 #, c-format msgid "Untracked files not listed%s" msgstr "Неследените файлове не са изведени%s" -#: wt-status.c:1383 +#: wt-status.c:1386 msgid " (use -u option to show untracked files)" msgstr " (използвайте опцията „-u“, за да изведете неследените файлове)" -#: wt-status.c:1389 +#: wt-status.c:1392 msgid "No changes" msgstr "Няма промени" -#: wt-status.c:1394 +#: wt-status.c:1397 #, c-format msgid "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n" msgstr "" "към индекса за подаване не са добавени промени (използвайте „git add“ и/или " "„git commit -a“)\n" -#: wt-status.c:1397 +#: wt-status.c:1400 #, c-format msgid "no changes added to commit\n" msgstr "към индекса за подаване не са добавени промени\n" -#: wt-status.c:1400 +#: wt-status.c:1403 #, c-format msgid "" "nothing added to commit but untracked files present (use \"git add\" to " @@ -2105,52 +2132,52 @@ msgstr "" "към индекса за подаване не са добавени промени, но има нови файлове " "(използвайте „git add“, за да започне тяхното следене)\n" -#: wt-status.c:1403 +#: wt-status.c:1406 #, c-format msgid "nothing added to commit but untracked files present\n" msgstr "към индекса за подаване не са добавени промени, но има нови файлове\n" -#: wt-status.c:1406 +#: wt-status.c:1409 #, c-format msgid "nothing to commit (create/copy files and use \"git add\" to track)\n" msgstr "" "липсват каквито и да е промени (създайте или копирайте файлове и използвайте " "„git add“, за да започне тяхното следене)\n" -#: wt-status.c:1409 wt-status.c:1414 +#: wt-status.c:1412 wt-status.c:1417 #, c-format msgid "nothing to commit\n" msgstr "липсват каквито и да е промени\n" -#: wt-status.c:1412 +#: wt-status.c:1415 #, c-format msgid "nothing to commit (use -u to show untracked files)\n" msgstr "" "липсват каквито и да е промени (използвайте опцията „-u“, за да се изведат и " "неследените файлове)\n" -#: wt-status.c:1416 +#: wt-status.c:1419 #, c-format msgid "nothing to commit, working directory clean\n" msgstr "липсват каквито и да е промени, работното дърво е чисто\n" -#: wt-status.c:1525 +#: wt-status.c:1528 msgid "HEAD (no branch)" msgstr "HEAD (извън клон)" -#: wt-status.c:1531 +#: wt-status.c:1534 msgid "Initial commit on " msgstr "Първоначално подаване на клон" -#: wt-status.c:1563 +#: wt-status.c:1561 msgid "gone" msgstr "изтрит" -#: wt-status.c:1565 wt-status.c:1573 +#: wt-status.c:1563 wt-status.c:1571 msgid "behind " msgstr "назад с " -#: compat/precompose_utf8.c:55 builtin/clone.c:360 +#: compat/precompose_utf8.c:55 builtin/clone.c:345 #, c-format msgid "failed to unlink '%s'" msgstr "неуспешно изтриване на „%s“" @@ -2177,7 +2204,7 @@ msgstr "изтриване на „%s“\n" msgid "Unstaged changes after refreshing the index:" msgstr "Промени, които и след обновяването на индекса не са добавени към него:" -#: builtin/add.c:194 builtin/rev-parse.c:785 +#: builtin/add.c:194 builtin/rev-parse.c:796 msgid "Could not read the index" msgstr "Индексът не може да бъде прочетен" @@ -2190,119 +2217,123 @@ msgstr "Файлът „%s“ не може да бъде отворен за з msgid "Could not write patch" msgstr "Кръпката не може да бъде записана" -#: builtin/add.c:214 +#: builtin/add.c:212 +msgid "editing patch failed" +msgstr "неуспешно редактиране на кръпка" + +#: builtin/add.c:215 #, c-format msgid "Could not stat '%s'" msgstr "Не може да се получи информация чрез „stat“ за файла „%s“" -#: builtin/add.c:216 +#: builtin/add.c:217 msgid "Empty patch. Aborted." msgstr "Празна кръпка, преустановяване на действието." -#: builtin/add.c:221 +#: builtin/add.c:222 #, c-format msgid "Could not apply '%s'" msgstr "Кръпката „%s“ не може да бъде приложена" -#: builtin/add.c:231 +#: builtin/add.c:232 msgid "The following paths are ignored by one of your .gitignore files:\n" msgstr "" "Следните пътища ще бъдат игнорирани според някой от файловете „.gitignore“:\n" -#: builtin/add.c:248 builtin/clean.c:876 builtin/fetch.c:107 builtin/mv.c:110 -#: builtin/prune-packed.c:55 builtin/push.c:508 builtin/remote.c:1372 -#: builtin/rm.c:269 +#: builtin/add.c:249 builtin/clean.c:874 builtin/fetch.c:107 builtin/mv.c:110 +#: builtin/prune-packed.c:55 builtin/push.c:508 builtin/remote.c:1369 +#: builtin/rm.c:268 msgid "dry run" msgstr "пробно изпълнeние" -#: builtin/add.c:249 builtin/apply.c:4577 builtin/check-ignore.c:19 -#: builtin/commit.c:1322 builtin/count-objects.c:63 builtin/fsck.c:608 +#: builtin/add.c:250 builtin/apply.c:4580 builtin/check-ignore.c:19 +#: builtin/commit.c:1322 builtin/count-objects.c:63 builtin/fsck.c:616 #: builtin/log.c:1617 builtin/mv.c:109 builtin/read-tree.c:114 msgid "be verbose" msgstr "повече подробности" -#: builtin/add.c:251 +#: builtin/add.c:252 msgid "interactive picking" msgstr "интерактивно отбиране на промени" -#: builtin/add.c:252 builtin/checkout.c:1126 builtin/reset.c:286 +#: builtin/add.c:253 builtin/checkout.c:1221 builtin/reset.c:286 msgid "select hunks interactively" msgstr "интерактивен избор на парчета код" -#: builtin/add.c:253 +#: builtin/add.c:254 msgid "edit current diff and apply" msgstr "редактиране на текущата разлика и прилагане" -#: builtin/add.c:254 +#: builtin/add.c:255 msgid "allow adding otherwise ignored files" msgstr "добавяне и на иначе игнорираните файлове" -#: builtin/add.c:255 +#: builtin/add.c:256 msgid "update tracked files" msgstr "обновяване на следените файлове" -#: builtin/add.c:256 +#: builtin/add.c:257 msgid "record only the fact that the path will be added later" msgstr "отбелязване само на факта, че пътят ще бъде добавен по-късно" -#: builtin/add.c:257 +#: builtin/add.c:258 msgid "add changes from all tracked and untracked files" msgstr "добавяне на всички промени в следените и неследените файлове" -#: builtin/add.c:260 +#: builtin/add.c:261 msgid "ignore paths removed in the working tree (same as --no-all)" msgstr "" "игнориране на пътищата, които са изтрити от работното дърво (същото като „--" "no-all“)" -#: builtin/add.c:262 +#: builtin/add.c:263 msgid "don't add, only refresh the index" msgstr "без добавяне на нови файлове, само обновяване на индекса" -#: builtin/add.c:263 +#: builtin/add.c:264 msgid "just skip files which cannot be added because of errors" msgstr "" "прескачане на файловете, които не могат да бъдат добавени поради грешки" -#: builtin/add.c:264 +#: builtin/add.c:265 msgid "check if - even missing - files are ignored in dry run" msgstr "" "проверка, че при пробно изпълнение всички файлове, дори и изтритите, се " "игнорират" -#: builtin/add.c:286 +#: builtin/add.c:287 #, c-format msgid "Use -f if you really want to add them.\n" msgstr "Използвайте опцията „-f“, за да ги добавите наистина.\n" -#: builtin/add.c:293 +#: builtin/add.c:294 msgid "adding files failed" msgstr "неуспешно добавяне на файлове" -#: builtin/add.c:329 +#: builtin/add.c:330 msgid "-A and -u are mutually incompatible" msgstr "опциите „-A“ и „-u“ са несъвместими" -#: builtin/add.c:336 +#: builtin/add.c:337 msgid "Option --ignore-missing can only be used together with --dry-run" msgstr "Опцията „--ignore-missing“ е съвместима само с „--dry-run“" -#: builtin/add.c:357 +#: builtin/add.c:358 #, c-format msgid "Nothing specified, nothing added.\n" msgstr "Нищо не е зададено и нищо не е добавено.\n" -#: builtin/add.c:358 +#: builtin/add.c:359 #, c-format msgid "Maybe you wanted to say 'git add .'?\n" msgstr "Вероятно искахте да използвате „git add .“?\n" -#: builtin/add.c:363 builtin/check-ignore.c:172 builtin/clean.c:920 -#: builtin/commit.c:335 builtin/mv.c:130 builtin/reset.c:235 builtin/rm.c:299 +#: builtin/add.c:364 builtin/check-ignore.c:172 builtin/clean.c:918 +#: builtin/commit.c:335 builtin/mv.c:130 builtin/reset.c:235 builtin/rm.c:298 msgid "index file corrupt" msgstr "файлът с индекса е повреден" -#: builtin/add.c:446 builtin/apply.c:4675 builtin/mv.c:279 builtin/rm.c:431 +#: builtin/add.c:447 builtin/apply.c:4678 builtin/mv.c:279 builtin/rm.c:430 msgid "Unable to write new index file" msgstr "Новият индекс не може да бъде записан" @@ -2388,66 +2419,66 @@ msgstr[1] "" "След съкращаването на първите %d части от компонентите на пътя, в заглавната " "част на „git diff“ липсва информация за име на файл (ред: %d)" -#: builtin/apply.c:1656 +#: builtin/apply.c:1659 msgid "new file depends on old contents" msgstr "новият файл зависи от старото съдържание на файла" -#: builtin/apply.c:1658 +#: builtin/apply.c:1661 msgid "deleted file still has contents" msgstr "изтритият файл не е празен" -#: builtin/apply.c:1684 +#: builtin/apply.c:1687 #, c-format msgid "corrupt patch at line %d" msgstr "грешка в кръпката на ред %d" -#: builtin/apply.c:1720 +#: builtin/apply.c:1723 #, c-format msgid "new file %s depends on old contents" msgstr "новият файл „%s“ зависи от старото съдържание на файла" -#: builtin/apply.c:1722 +#: builtin/apply.c:1725 #, c-format msgid "deleted file %s still has contents" msgstr "изтритият файл „%s“ не е празен" # FIXME - double **?? -#: builtin/apply.c:1725 +#: builtin/apply.c:1728 #, c-format msgid "** warning: file %s becomes empty but is not deleted" msgstr "●● предупреждение: файлът „%s“ вече е празен, но не е изтрит" -#: builtin/apply.c:1871 +#: builtin/apply.c:1874 #, c-format msgid "corrupt binary patch at line %d: %.*s" msgstr "грешка в двоичната кръпка на ред %d: %.*s" -#: builtin/apply.c:1900 +#: builtin/apply.c:1903 #, c-format msgid "unrecognized binary patch at line %d" msgstr "неразпозната двоичната кръпка на ред %d" -#: builtin/apply.c:2051 +#: builtin/apply.c:2054 #, c-format msgid "patch with only garbage at line %d" msgstr "кръпката е с изцяло повредени данни на ред %d" -#: builtin/apply.c:2141 +#: builtin/apply.c:2144 #, c-format msgid "unable to read symlink %s" msgstr "символната връзка „%s“ не може да бъде прочетена" -#: builtin/apply.c:2145 +#: builtin/apply.c:2148 #, c-format msgid "unable to open or read %s" msgstr "файлът „%s“ не може да бъде отворен или прочетен" -#: builtin/apply.c:2778 +#: builtin/apply.c:2781 #, c-format msgid "invalid start of line: '%c'" msgstr "неправилно начало на ред: „%c“" -#: builtin/apply.c:2897 +#: builtin/apply.c:2900 #, c-format msgid "Hunk #%d succeeded at %d (offset %d line)." msgid_plural "Hunk #%d succeeded at %d (offset %d lines)." @@ -2456,13 +2487,13 @@ msgstr[0] "" msgstr[1] "" "%d-то парче код бе успешно приложено на ред %d (отместване от %d реда)." -#: builtin/apply.c:2909 +#: builtin/apply.c:2912 #, c-format msgid "Context reduced to (%ld/%ld) to apply fragment at %d" msgstr "" "Контекстът е намален на (%ld/%ld) за прилагането на парчето код на ред %d" -#: builtin/apply.c:2915 +#: builtin/apply.c:2918 #, c-format msgid "" "while searching for:\n" @@ -2471,345 +2502,345 @@ msgstr "" "при търсене за:\n" "%.*s" -#: builtin/apply.c:2935 +#: builtin/apply.c:2938 #, c-format msgid "missing binary patch data for '%s'" msgstr "липсват данните за двоичната кръпка за „%s“" -#: builtin/apply.c:3036 +#: builtin/apply.c:3039 #, c-format msgid "binary patch does not apply to '%s'" msgstr "двоичната кръпка не може да бъде приложена върху „%s“" -#: builtin/apply.c:3042 +#: builtin/apply.c:3045 #, c-format msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)" msgstr "" "двоичната кръпка за „%s“ води до неправилни резултати (очакваше се SHA1: " "„%s“, а бе получено: „%s“)" -#: builtin/apply.c:3063 +#: builtin/apply.c:3066 #, c-format msgid "patch failed: %s:%ld" msgstr "неуспешно прилагане на кръпка: „%s:%ld“" -#: builtin/apply.c:3187 +#: builtin/apply.c:3190 #, c-format msgid "cannot checkout %s" msgstr "„%s“ не може да се изтегли" -#: builtin/apply.c:3232 builtin/apply.c:3243 builtin/apply.c:3288 +#: builtin/apply.c:3235 builtin/apply.c:3246 builtin/apply.c:3291 #, c-format msgid "read of %s failed" msgstr "неуспешно прочитане на „%s“" -#: builtin/apply.c:3240 +#: builtin/apply.c:3243 #, c-format msgid "reading from '%s' beyond a symbolic link" msgstr "изчитане на „%s“ след проследяване на символна връзка" -#: builtin/apply.c:3268 builtin/apply.c:3490 +#: builtin/apply.c:3271 builtin/apply.c:3493 #, c-format msgid "path %s has been renamed/deleted" msgstr "обектът с път „%s“ е преименуван или изтрит" -#: builtin/apply.c:3349 builtin/apply.c:3504 +#: builtin/apply.c:3352 builtin/apply.c:3507 #, c-format msgid "%s: does not exist in index" msgstr "„%s“ не съществува в индекса" -#: builtin/apply.c:3353 builtin/apply.c:3496 builtin/apply.c:3518 +#: builtin/apply.c:3356 builtin/apply.c:3499 builtin/apply.c:3521 #, c-format msgid "%s: %s" msgstr "„%s“: %s" -#: builtin/apply.c:3358 builtin/apply.c:3512 +#: builtin/apply.c:3361 builtin/apply.c:3515 #, c-format msgid "%s: does not match index" msgstr "„%s“ не съответства на индекса" -#: builtin/apply.c:3460 +#: builtin/apply.c:3463 msgid "removal patch leaves file contents" msgstr "изтриващата кръпка оставя файла непразен" -#: builtin/apply.c:3529 +#: builtin/apply.c:3532 #, c-format msgid "%s: wrong type" msgstr "„%s“: неправилен вид" -#: builtin/apply.c:3531 +#: builtin/apply.c:3534 #, c-format msgid "%s has type %o, expected %o" msgstr "„%s“ е от вид „%o“, а се очакваше „%o“" -#: builtin/apply.c:3690 builtin/apply.c:3692 +#: builtin/apply.c:3693 builtin/apply.c:3695 #, c-format msgid "invalid path '%s'" msgstr "неправилен път: „%s“" -#: builtin/apply.c:3747 +#: builtin/apply.c:3750 #, c-format msgid "%s: already exists in index" msgstr "„%s“: вече съществува в индекса" -#: builtin/apply.c:3750 +#: builtin/apply.c:3753 #, c-format msgid "%s: already exists in working directory" -msgstr "„%s“: вече съществува в работната директория" +msgstr "„%s“: вече съществува в работното дърво" -#: builtin/apply.c:3770 +#: builtin/apply.c:3773 #, c-format msgid "new mode (%o) of %s does not match old mode (%o)" msgstr "новите права за достъп (%o) на „%s“ не съвпадат със старите (%o)" -#: builtin/apply.c:3775 +#: builtin/apply.c:3778 #, c-format msgid "new mode (%o) of %s does not match old mode (%o) of %s" msgstr "" "новите права за достъп (%o) на „%s“ не съвпадат със старите (%o) на „%s“" -#: builtin/apply.c:3795 +#: builtin/apply.c:3798 #, c-format msgid "affected file '%s' is beyond a symbolic link" msgstr "засегнатият файл „%s“ е след символна връзка" -#: builtin/apply.c:3799 +#: builtin/apply.c:3802 #, c-format msgid "%s: patch does not apply" msgstr "Кръпката „%s“ не може да бъде приложена" -#: builtin/apply.c:3813 +#: builtin/apply.c:3816 #, c-format msgid "Checking patch %s..." msgstr "Проверяване на кръпката „%s“…" -#: builtin/apply.c:3906 builtin/checkout.c:231 builtin/reset.c:135 +#: builtin/apply.c:3909 builtin/checkout.c:233 builtin/reset.c:135 #, c-format msgid "make_cache_entry failed for path '%s'" msgstr "неуспешно създаване на запис в кеша чрез „make_cache_entry“ за „%s“" -#: builtin/apply.c:4049 +#: builtin/apply.c:4052 #, c-format msgid "unable to remove %s from index" msgstr "„%s“ не може да се извади от индекса" -#: builtin/apply.c:4078 +#: builtin/apply.c:4081 #, c-format msgid "corrupt patch for submodule %s" msgstr "повредена кръпка за модула „%s“" -#: builtin/apply.c:4082 +#: builtin/apply.c:4085 #, c-format msgid "unable to stat newly created file '%s'" msgstr "" "не може да се получи информация чрез „stat“ за новосъздадения файл „%s“" -#: builtin/apply.c:4087 +#: builtin/apply.c:4090 #, c-format msgid "unable to create backing store for newly created file %s" msgstr "" "не може да се за създаде мястото за съхранение на новосъздадения файл „%s“" -#: builtin/apply.c:4090 builtin/apply.c:4198 +#: builtin/apply.c:4093 builtin/apply.c:4201 #, c-format msgid "unable to add cache entry for %s" msgstr "не може да се добави запис в кеша за „%s“" -#: builtin/apply.c:4123 +#: builtin/apply.c:4126 #, c-format msgid "closing file '%s'" msgstr "затваряне на файла „%s“" -#: builtin/apply.c:4172 +#: builtin/apply.c:4175 #, c-format msgid "unable to write file '%s' mode %o" msgstr "файлът „%s“ не може да се запише с режим на достъп „%o“" -#: builtin/apply.c:4259 +#: builtin/apply.c:4262 #, c-format msgid "Applied patch %s cleanly." msgstr "Кръпката „%s“ бе приложена чисто." -#: builtin/apply.c:4267 +#: builtin/apply.c:4270 msgid "internal error" msgstr "вътрешна грешка" -#: builtin/apply.c:4270 +#: builtin/apply.c:4273 #, c-format msgid "Applying patch %%s with %d reject..." msgid_plural "Applying patch %%s with %d rejects..." msgstr[0] "Прилагане на кръпката „%%s“ с %d отхвърлено парче…" msgstr[1] "Прилагане на кръпката „%%s“ с %d отхвърлени парчета…" -#: builtin/apply.c:4280 +#: builtin/apply.c:4283 #, c-format msgid "truncating .rej filename to %.*s.rej" msgstr "съкращаване на името на файла с отхвърлените парчета на „ %.*s.rej“" -#: builtin/apply.c:4301 +#: builtin/apply.c:4304 #, c-format msgid "Hunk #%d applied cleanly." msgstr "%d-то парче бе успешно приложено." -#: builtin/apply.c:4304 +#: builtin/apply.c:4307 #, c-format msgid "Rejected hunk #%d." msgstr "%d-то парче бе отхвърлено." -#: builtin/apply.c:4394 +#: builtin/apply.c:4397 msgid "unrecognized input" msgstr "непознат вход" -#: builtin/apply.c:4405 +#: builtin/apply.c:4408 msgid "unable to read index file" msgstr "индексът не може да бъде записан" -#: builtin/apply.c:4522 builtin/apply.c:4525 builtin/clone.c:92 +#: builtin/apply.c:4525 builtin/apply.c:4528 builtin/clone.c:85 #: builtin/fetch.c:92 msgid "path" msgstr "път" -#: builtin/apply.c:4523 +#: builtin/apply.c:4526 msgid "don't apply changes matching the given path" msgstr "без прилагане на промените напасващи на дадения път" -#: builtin/apply.c:4526 +#: builtin/apply.c:4529 msgid "apply changes matching the given path" msgstr "прилагане на промените напасващи на дадения път" -#: builtin/apply.c:4528 +#: builtin/apply.c:4531 msgid "num" msgstr "БРОЙ" -#: builtin/apply.c:4529 +#: builtin/apply.c:4532 msgid "remove <num> leading slashes from traditional diff paths" msgstr "премахване на този БРОЙ водещи елементи от пътищата в разликата" -#: builtin/apply.c:4532 +#: builtin/apply.c:4535 msgid "ignore additions made by the patch" msgstr "игнориране на редовете добавени от тази кръпка" -#: builtin/apply.c:4534 +#: builtin/apply.c:4537 msgid "instead of applying the patch, output diffstat for the input" msgstr "извеждане на статистика на промените без прилагане на кръпката" -#: builtin/apply.c:4538 +#: builtin/apply.c:4541 msgid "show number of added and deleted lines in decimal notation" msgstr "извеждане на броя на добавените и изтритите редове" -#: builtin/apply.c:4540 +#: builtin/apply.c:4543 msgid "instead of applying the patch, output a summary for the input" msgstr "извеждане на статистика на входните данни без прилагане на кръпката" -#: builtin/apply.c:4542 +#: builtin/apply.c:4545 msgid "instead of applying the patch, see if the patch is applicable" msgstr "проверка дали кръпката може да се приложи, без действително прилагане" -#: builtin/apply.c:4544 +#: builtin/apply.c:4547 msgid "make sure the patch is applicable to the current index" msgstr "проверка дали кръпката може да бъде приложена към текущия индекс" -#: builtin/apply.c:4546 +#: builtin/apply.c:4549 msgid "apply a patch without touching the working tree" msgstr "прилагане на кръпката без промяна на работното дърво" -#: builtin/apply.c:4548 +#: builtin/apply.c:4551 msgid "accept a patch that touches outside the working area" msgstr "прилагане на кръпка, която променя и файлове извън работното дърво" -#: builtin/apply.c:4550 +#: builtin/apply.c:4553 msgid "also apply the patch (use with --stat/--summary/--check)" msgstr "" "кръпката да бъде приложена. Опцията се комбинира с „--check“/„--stat“/„--" "summary“" -#: builtin/apply.c:4552 +#: builtin/apply.c:4555 msgid "attempt three-way merge if a patch does not apply" msgstr "пробване с тройно сливане, ако кръпката не може да се приложи директно" -#: builtin/apply.c:4554 +#: builtin/apply.c:4557 msgid "build a temporary index based on embedded index information" msgstr "" "създаване на временен индекс на база на включената информация за индекса" -#: builtin/apply.c:4556 builtin/checkout-index.c:198 builtin/ls-files.c:412 +#: builtin/apply.c:4559 builtin/checkout-index.c:198 builtin/ls-files.c:412 msgid "paths are separated with NUL character" msgstr "разделяне на пътищата с нулевия знак „NUL“" -#: builtin/apply.c:4559 +#: builtin/apply.c:4562 msgid "ensure at least <n> lines of context match" msgstr "да се осигури контекст от поне такъв БРОЙ съвпадащи редове" -#: builtin/apply.c:4560 +#: builtin/apply.c:4563 msgid "action" msgstr "действие" -#: builtin/apply.c:4561 +#: builtin/apply.c:4564 msgid "detect new or modified lines that have whitespace errors" msgstr "засичане на нови или променени редове с грешки в знаците за интервали" -#: builtin/apply.c:4564 builtin/apply.c:4567 +#: builtin/apply.c:4567 builtin/apply.c:4570 msgid "ignore changes in whitespace when finding context" msgstr "" "игнориране на промените в знаците за интервали при откриване на контекста" -#: builtin/apply.c:4570 +#: builtin/apply.c:4573 msgid "apply the patch in reverse" msgstr "прилагане на кръпката в обратна посока" -#: builtin/apply.c:4572 +#: builtin/apply.c:4575 msgid "don't expect at least one line of context" msgstr "без изискване на дори и един ред контекст" -#: builtin/apply.c:4574 +#: builtin/apply.c:4577 msgid "leave the rejected hunks in corresponding *.rej files" msgstr "оставяне на отхвърлените парчета във файлове с разширение „.rej“" -#: builtin/apply.c:4576 +#: builtin/apply.c:4579 msgid "allow overlapping hunks" msgstr "позволяване на застъпващи се парчета" -#: builtin/apply.c:4579 +#: builtin/apply.c:4582 msgid "tolerate incorrectly detected missing new-line at the end of file" msgstr "пренебрегване на неправилно липсващ знак за нов ред в края на файл" -#: builtin/apply.c:4582 +#: builtin/apply.c:4585 msgid "do not trust the line counts in the hunk headers" msgstr "без доверяване на номерата на редовете в заглавните части на парчетата" -#: builtin/apply.c:4584 +#: builtin/apply.c:4587 msgid "root" msgstr "НАЧАЛНА_ДИРЕКТОРИЯ" -#: builtin/apply.c:4585 +#: builtin/apply.c:4588 msgid "prepend <root> to all filenames" msgstr "добавяне на тази НАЧАЛНА_ДИРЕКТОРИЯ към имената на всички файлове" -#: builtin/apply.c:4607 +#: builtin/apply.c:4610 msgid "--3way outside a repository" msgstr "като „--3way“, но извън хранилище" -#: builtin/apply.c:4615 +#: builtin/apply.c:4618 msgid "--index outside a repository" msgstr "като „--index“, но извън хранилище" -#: builtin/apply.c:4618 +#: builtin/apply.c:4621 msgid "--cached outside a repository" msgstr "като „--cached“, но извън хранилище" -#: builtin/apply.c:4637 +#: builtin/apply.c:4640 #, c-format msgid "can't open patch '%s'" msgstr "кръпката „%s“ не може да бъде отворена" -#: builtin/apply.c:4651 +#: builtin/apply.c:4654 #, c-format msgid "squelched %d whitespace error" msgid_plural "squelched %d whitespace errors" msgstr[0] "пренебрегната е %d грешка в знаците за интервали" msgstr[1] "пренебрегнати са %d грешки в знаците за интервали" -#: builtin/apply.c:4657 builtin/apply.c:4667 +#: builtin/apply.c:4660 builtin/apply.c:4670 #, c-format msgid "%d line adds whitespace errors." msgid_plural "%d lines add whitespace errors." @@ -2864,11 +2895,11 @@ msgid "update BISECT_HEAD instead of checking out the current commit" msgstr "" "обновяване на указателя „BISECT_HEAD“ вместо да се използва текущото подаване" -#: builtin/blame.c:30 -msgid "git blame [<options>] [<rev-opts>] [<rev>] [--] file" +#: builtin/blame.c:31 +msgid "git blame [<options>] [<rev-opts>] [<rev>] [--] <file>" msgstr "git blame [ОПЦИЯ…] [ОПЦИЯ_ЗА_ВЕРСИЯТА…] [ВЕРСИЯ] [--] ФАЙЛ" -#: builtin/blame.c:35 +#: builtin/blame.c:36 msgid "<rev-opts> are documented in git-rev-list(1)" msgstr "ОПЦИИте_ЗА_ВЕРСИЯТА са документирани в ръководството git-rev-list(1)" @@ -3009,7 +3040,7 @@ msgstr "git branch [ОПЦИЯ…] [-r] (-d | -D) ИМЕ_НА_КЛОН…" msgid "git branch [<options>] (-m | -M) [<old-branch>] <new-branch>" msgstr "git branch [ОПЦИЯ…] (-m | -M) [СТАР_КЛОН] НОВ_КЛОН" -#: builtin/branch.c:152 +#: builtin/branch.c:150 #, c-format msgid "" "deleting branch '%s' that has been merged to\n" @@ -3018,7 +3049,7 @@ msgstr "" "изтриване на клона „%s“, който е слят към „%s“,\n" " но още не е слят към върха „HEAD“." -#: builtin/branch.c:156 +#: builtin/branch.c:154 #, c-format msgid "" "not deleting branch '%s' that is not yet merged to\n" @@ -3027,12 +3058,12 @@ msgstr "" "отказване на изтриване на клона „%s“, който не е слят към\n" " „%s“, но е слят към върха „HEAD“." -#: builtin/branch.c:170 +#: builtin/branch.c:168 #, c-format msgid "Couldn't look up commit object for '%s'" msgstr "Обектът-подаване за „%s“ не може да бъде открит" -#: builtin/branch.c:174 +#: builtin/branch.c:172 #, c-format msgid "" "The branch '%s' is not fully merged.\n" @@ -3041,310 +3072,310 @@ msgstr "" "Клонът „%s“ не е слят напълно. Ако сте сигурни, че искате\n" "да го изтриете, изпълнете „git branch -D %s“." -#: builtin/branch.c:187 +#: builtin/branch.c:185 msgid "Update of config-file failed" msgstr "Неуспешно обновяване на конфигурационния файл" -#: builtin/branch.c:215 +#: builtin/branch.c:213 msgid "cannot use -a with -d" msgstr "Опцията „-a“ е несъвместима с опцията „-d“" -#: builtin/branch.c:221 +#: builtin/branch.c:219 msgid "Couldn't look up commit object for HEAD" msgstr "Обектът-подаване, сочен от указателя „HEAD“, не може да бъде открит" -#: builtin/branch.c:229 +#: builtin/branch.c:227 #, c-format msgid "Cannot delete the branch '%s' which you are currently on." msgstr "Не можете да изтриете клона „%s“, защото в момента е текущ." -#: builtin/branch.c:245 +#: builtin/branch.c:243 #, c-format -msgid "remote branch '%s' not found." -msgstr "отдалеченият клон „%s“ не може да бъде открит." +msgid "remote-tracking branch '%s' not found." +msgstr "следящият клон „%s“ не може да бъде открит." -#: builtin/branch.c:246 +#: builtin/branch.c:244 #, c-format msgid "branch '%s' not found." msgstr "клонът „%s“ не може да бъде открит." -#: builtin/branch.c:260 +#: builtin/branch.c:258 #, c-format -msgid "Error deleting remote branch '%s'" -msgstr "Грешка при изтриването на отдалечения клон „%s“" +msgid "Error deleting remote-tracking branch '%s'" +msgstr "Грешка при изтриването на следящия клон „%s“" -#: builtin/branch.c:261 +#: builtin/branch.c:259 #, c-format msgid "Error deleting branch '%s'" msgstr "Грешка при изтриването на клона „%s“" -#: builtin/branch.c:268 +#: builtin/branch.c:266 #, c-format -msgid "Deleted remote branch %s (was %s).\n" -msgstr "Изтрит отдалечен клон „%s“ (той сочеше към „%s“).\n" +msgid "Deleted remote-tracking branch %s (was %s).\n" +msgstr "Изтрит следящ клон „%s“ (той сочеше към „%s“).\n" -#: builtin/branch.c:269 +#: builtin/branch.c:267 #, c-format msgid "Deleted branch %s (was %s).\n" msgstr "Изтрит клон „%s“ (той сочеше към „%s“).\n" -#: builtin/branch.c:370 +#: builtin/branch.c:368 #, c-format msgid "branch '%s' does not point at a commit" msgstr "клонът „%s“ не сочи към подаване" -#: builtin/branch.c:459 +#: builtin/branch.c:451 #, c-format msgid "[%s: gone]" msgstr "[%s: изтрит]" -#: builtin/branch.c:464 +#: builtin/branch.c:456 #, c-format msgid "[%s]" msgstr "[%s]" -#: builtin/branch.c:469 +#: builtin/branch.c:461 #, c-format msgid "[%s: behind %d]" msgstr "[%s: назад с %d]" -#: builtin/branch.c:471 +#: builtin/branch.c:463 #, c-format msgid "[behind %d]" msgstr "[назад с %d]" -#: builtin/branch.c:475 +#: builtin/branch.c:467 #, c-format msgid "[%s: ahead %d]" msgstr "[%s: напред с %d]" -#: builtin/branch.c:477 +#: builtin/branch.c:469 #, c-format msgid "[ahead %d]" msgstr "[напред с %d]" -#: builtin/branch.c:480 +#: builtin/branch.c:472 #, c-format msgid "[%s: ahead %d, behind %d]" msgstr "[%s: напред с %d, назад с %d]" -#: builtin/branch.c:483 +#: builtin/branch.c:475 #, c-format msgid "[ahead %d, behind %d]" msgstr "[напред с %d, назад с %d]" # FIXME ** how many?? -#: builtin/branch.c:496 +#: builtin/branch.c:488 msgid " **** invalid ref ****" msgstr " ●●●● неправилен указател ●●●●" -#: builtin/branch.c:587 +#: builtin/branch.c:579 #, c-format msgid "(no branch, rebasing %s)" msgstr "(извън клон, пребазиране на „%s“)" -#: builtin/branch.c:590 +#: builtin/branch.c:582 #, c-format msgid "(no branch, bisect started on %s)" msgstr "(извън клон, двоично търсене от „%s“)" -#: builtin/branch.c:596 +#: builtin/branch.c:588 #, c-format msgid "(HEAD detached at %s)" msgstr "(Указателят „HEAD“ не е свързан и е при „%s“)" -#: builtin/branch.c:599 +#: builtin/branch.c:591 #, c-format msgid "(HEAD detached from %s)" msgstr "Указателят „HEAD“ не е свързан и е отделѐн от „%s“" -#: builtin/branch.c:603 +#: builtin/branch.c:595 msgid "(no branch)" msgstr "(извън клон)" -#: builtin/branch.c:650 +#: builtin/branch.c:642 #, c-format msgid "object '%s' does not point to a commit" msgstr "обектът „%s“ не сочи към подаване" -#: builtin/branch.c:698 +#: builtin/branch.c:690 msgid "some refs could not be read" msgstr "някои указатели не могат да бъдат прочетени" -#: builtin/branch.c:711 +#: builtin/branch.c:703 msgid "cannot rename the current branch while not on any." msgstr "" "не можете да преименувате текущия клон, защото сте извън който и да е клон" -#: builtin/branch.c:721 +#: builtin/branch.c:713 #, c-format msgid "Invalid branch name: '%s'" msgstr "Неправилно име на клон: „%s“" -#: builtin/branch.c:736 +#: builtin/branch.c:728 msgid "Branch rename failed" msgstr "Неуспешно преименуване на клон" -#: builtin/branch.c:740 +#: builtin/branch.c:732 #, c-format msgid "Renamed a misnamed branch '%s' away" msgstr "На клона с неправилно име „%s“ е дадено служебно име" -#: builtin/branch.c:744 +#: builtin/branch.c:736 #, c-format msgid "Branch renamed to %s, but HEAD is not updated!" msgstr "Клонът е преименуван на „%s“, но указателят „HEAD“ не е обновен" -#: builtin/branch.c:751 +#: builtin/branch.c:743 msgid "Branch is renamed, but update of config-file failed" msgstr "Клонът е преименуван, но конфигурационният файл не е обновен" -#: builtin/branch.c:766 +#: builtin/branch.c:758 #, c-format msgid "malformed object name %s" msgstr "неправилно име на обект „%s“" -#: builtin/branch.c:790 +#: builtin/branch.c:780 #, c-format msgid "could not write branch description template: %s" msgstr "шаблонът за описание на клон не бе записан: „%s“" -#: builtin/branch.c:820 +#: builtin/branch.c:810 msgid "Generic options" msgstr "Общи настройки" -#: builtin/branch.c:822 +#: builtin/branch.c:812 msgid "show hash and subject, give twice for upstream branch" msgstr "" "извеждане на хеша и темата. Повтарянето на опцията прибавя отдалечените клони" -#: builtin/branch.c:823 +#: builtin/branch.c:813 msgid "suppress informational messages" msgstr "без информационни съобщения" -#: builtin/branch.c:824 +#: builtin/branch.c:814 msgid "set up tracking mode (see git-pull(1))" msgstr "задаване на режима на следене (виж git-pull(1))" -#: builtin/branch.c:826 +#: builtin/branch.c:816 msgid "change upstream info" msgstr "смяна на следения клон" -#: builtin/branch.c:830 +#: builtin/branch.c:820 msgid "use colored output" msgstr "цветен изход" -#: builtin/branch.c:831 +#: builtin/branch.c:821 msgid "act on remote-tracking branches" msgstr "действие върху следящите клони" -#: builtin/branch.c:834 builtin/branch.c:840 builtin/branch.c:861 -#: builtin/branch.c:867 builtin/commit.c:1582 builtin/commit.c:1583 -#: builtin/commit.c:1584 builtin/commit.c:1585 builtin/tag.c:616 +#: builtin/branch.c:824 builtin/branch.c:830 builtin/branch.c:851 +#: builtin/branch.c:857 builtin/commit.c:1581 builtin/commit.c:1582 +#: builtin/commit.c:1583 builtin/commit.c:1584 builtin/tag.c:616 #: builtin/tag.c:622 msgid "commit" msgstr "подаване" -#: builtin/branch.c:835 builtin/branch.c:841 +#: builtin/branch.c:825 builtin/branch.c:831 msgid "print only branches that contain the commit" msgstr "извеждане само на клоните, които съдържат това подаване" -#: builtin/branch.c:847 +#: builtin/branch.c:837 msgid "Specific git-branch actions:" msgstr "Специални действия на „git-branch“:" -#: builtin/branch.c:848 +#: builtin/branch.c:838 msgid "list both remote-tracking and local branches" msgstr "извеждане както на следящите, така и на локалните клони" -#: builtin/branch.c:850 +#: builtin/branch.c:840 msgid "delete fully merged branch" msgstr "изтриване на клони, които са напълно слети" -#: builtin/branch.c:851 +#: builtin/branch.c:841 msgid "delete branch (even if not merged)" msgstr "изтриване и на клони, които не са напълно слети" -#: builtin/branch.c:852 +#: builtin/branch.c:842 msgid "move/rename a branch and its reflog" msgstr "" "преместване/преименуване на клон и принадлежащият му журнал на указателите" -#: builtin/branch.c:853 +#: builtin/branch.c:843 msgid "move/rename a branch, even if target exists" msgstr "преместване/преименуване на клон, дори ако има вече клон с такова име" -#: builtin/branch.c:854 +#: builtin/branch.c:844 msgid "list branch names" msgstr "извеждане на имената на клоните" -#: builtin/branch.c:855 +#: builtin/branch.c:845 msgid "create the branch's reflog" msgstr "създаване на журнала на указателите на клона" -#: builtin/branch.c:857 +#: builtin/branch.c:847 msgid "edit the description for the branch" msgstr "редактиране на описанието на клона" -#: builtin/branch.c:858 +#: builtin/branch.c:848 msgid "force creation, move/rename, deletion" msgstr "принудително създаване, преместване, преименуване, изтриване" -#: builtin/branch.c:861 +#: builtin/branch.c:851 msgid "print only not merged branches" msgstr "извеждане само на неслетите клони" -#: builtin/branch.c:867 +#: builtin/branch.c:857 msgid "print only merged branches" msgstr "извеждане само на слетите клони" -#: builtin/branch.c:871 +#: builtin/branch.c:861 msgid "list branches in columns" msgstr "извеждане по колони" -#: builtin/branch.c:884 +#: builtin/branch.c:874 msgid "Failed to resolve HEAD as a valid ref." msgstr "Не може да се открие към какво сочи указателят „HEAD“" -#: builtin/branch.c:888 builtin/clone.c:637 +#: builtin/branch.c:878 builtin/clone.c:622 msgid "HEAD not found below refs/heads!" msgstr "В директорията „refs/heads“ липсва файл „HEAD“" -#: builtin/branch.c:910 +#: builtin/branch.c:900 msgid "--column and --verbose are incompatible" msgstr "Опциите „--column“ и „--verbose“ са несъвместими" -#: builtin/branch.c:921 builtin/branch.c:960 +#: builtin/branch.c:911 builtin/branch.c:950 msgid "branch name required" msgstr "Необходимо е име на клон" -#: builtin/branch.c:936 +#: builtin/branch.c:926 msgid "Cannot give description to detached HEAD" msgstr "Не може да зададете описание на „HEAD“ извън клон" -#: builtin/branch.c:941 +#: builtin/branch.c:931 msgid "cannot edit description of more than one branch" msgstr "Не може да редактирате описанието на повече от един клон едновременно" -#: builtin/branch.c:948 +#: builtin/branch.c:938 #, c-format msgid "No commit on branch '%s' yet." msgstr "В клона „%s“ все още няма подавания." -#: builtin/branch.c:951 +#: builtin/branch.c:941 #, c-format msgid "No branch named '%s'." msgstr "Липсва клон на име „%s“." -#: builtin/branch.c:966 +#: builtin/branch.c:956 msgid "too many branches for a rename operation" msgstr "Прекалено много клони за преименуване" -#: builtin/branch.c:971 +#: builtin/branch.c:961 msgid "too many branches to set new upstream" msgstr "Прекалено много клони за задаване на следене" -#: builtin/branch.c:975 +#: builtin/branch.c:965 #, c-format msgid "" "could not set upstream of HEAD to %s when it does not point to any branch." @@ -3352,39 +3383,39 @@ msgstr "" "Следеното от „HEAD“ не може да се зададе да е „%s“, защото то не сочи към " "никой клон." -#: builtin/branch.c:978 builtin/branch.c:1000 builtin/branch.c:1021 +#: builtin/branch.c:968 builtin/branch.c:990 builtin/branch.c:1011 #, c-format msgid "no such branch '%s'" msgstr "Няма клон на име „%s“." -#: builtin/branch.c:982 +#: builtin/branch.c:972 #, c-format msgid "branch '%s' does not exist" msgstr "Не съществува клон на име „%s“." -#: builtin/branch.c:994 +#: builtin/branch.c:984 msgid "too many branches to unset upstream" msgstr "Прекалено много клони за махане на следене" -#: builtin/branch.c:998 +#: builtin/branch.c:988 msgid "could not unset upstream of HEAD when it does not point to any branch." msgstr "" "Следеното от „HEAD“ не може да махне, защото то не сочи към никой клон." -#: builtin/branch.c:1004 +#: builtin/branch.c:994 #, c-format msgid "Branch '%s' has no upstream information" msgstr "Няма информация клонът „%s“ да следи някой друг" -#: builtin/branch.c:1018 +#: builtin/branch.c:1008 msgid "it does not make sense to create 'HEAD' manually" msgstr "Няма никакъв смисъл ръчно да създавате „HEAD“." -#: builtin/branch.c:1024 +#: builtin/branch.c:1014 msgid "-a and -r options to 'git branch' do not make sense with a branch name" msgstr "Опциите „-a“ и „-r“ на „git branch“ са несъвместими с име на клон" -#: builtin/branch.c:1027 +#: builtin/branch.c:1017 #, c-format msgid "" "The --set-upstream flag is deprecated and will be removed. Consider using --" @@ -3393,7 +3424,7 @@ msgstr "" "Опцията „--set-upstream“ вече е остаряла и предстои да бъде махната. " "Използвайте „--track“ или „--set-upstream-to“\n" -#: builtin/branch.c:1044 +#: builtin/branch.c:1034 #, c-format msgid "" "\n" @@ -3404,74 +3435,91 @@ msgstr "" "За да накарате „%s“ да следи „%s“, изпълнете следната команда:\n" "\n" -#: builtin/branch.c:1045 +#: builtin/branch.c:1035 #, c-format msgid " git branch -d %s\n" msgstr " git branch -d %s\n" -#: builtin/branch.c:1046 +#: builtin/branch.c:1036 #, c-format msgid " git branch --set-upstream-to %s\n" msgstr " git branch --set-upstream-to %s\n" -#: builtin/bundle.c:47 +#: builtin/bundle.c:51 #, c-format msgid "%s is okay\n" msgstr "Пратката „%s“ е наред\n" -#: builtin/bundle.c:56 +#: builtin/bundle.c:64 msgid "Need a repository to create a bundle." msgstr "За създаването на пратка е необходимо хранилище." -#: builtin/bundle.c:60 +#: builtin/bundle.c:68 msgid "Need a repository to unbundle." msgstr "За приемането на пратка е необходимо хранилище." -#: builtin/cat-file.c:326 -msgid "git cat-file (-t | -s | -e | -p | <type> | --textconv) <object>" -msgstr "git cat-file (-t | -s | -e | -p | ВИД | --textconv) ОБЕКТ" +#: builtin/cat-file.c:369 +msgid "" +"git cat-file (-t [--allow-unknown-type]|-s [--allow-unknown-type]|-e|-p|" +"<type>|--textconv) <object>" +msgstr "" +"git cat-file (-t [--allow-unknown-type] | -s [--allow-unknown-type] | -e | -" +"p | ВИД | --textconv) ОБЕКТ" -#: builtin/cat-file.c:327 -msgid "git cat-file (--batch | --batch-check) < <list-of-objects>" -msgstr "git cat-file (--batch | --batch-check) < СПИСЪК_С_ОБЕКТИ" +#: builtin/cat-file.c:370 +msgid "" +"git cat-file (--batch | --batch-check) [--follow-symlinks] < <list-of-" +"objects>" +msgstr "" +"git cat-file (--batch | --batch-check) [--follow-symlinks] < СПИСЪК_С_ОБЕКТИ" -#: builtin/cat-file.c:364 +#: builtin/cat-file.c:407 msgid "<type> can be one of: blob, tree, commit, tag" msgstr "" "ВИДът може да е: „blob“ (обект BLOB), „tree“ (дърво), „commit“ (подаване), " "„tag“ (етикет)" -#: builtin/cat-file.c:365 +#: builtin/cat-file.c:408 msgid "show object type" msgstr "извеждане на вида на обект" -#: builtin/cat-file.c:366 +#: builtin/cat-file.c:409 msgid "show object size" msgstr "извеждане на размера на обект" -#: builtin/cat-file.c:368 +#: builtin/cat-file.c:411 msgid "exit with zero when there's no error" msgstr "изход с 0, когато няма грешка" -#: builtin/cat-file.c:369 +#: builtin/cat-file.c:412 msgid "pretty-print object's content" msgstr "форматирано извеждане на съдържанието на обекта" -#: builtin/cat-file.c:371 +#: builtin/cat-file.c:414 msgid "for blob objects, run textconv on object's content" msgstr "" "да се стартира програмата зададена в настройката „textconv“ за преобразуване " "на съдържанието на обекта" -#: builtin/cat-file.c:373 +#: builtin/cat-file.c:416 +msgid "allow -s and -t to work with broken/corrupt objects" +msgstr "позволяване на опциите „-s“ и „-t“ да работят с повредени обекти" + +#: builtin/cat-file.c:418 msgid "show info and content of objects fed from the standard input" msgstr "" "извеждане на информация и съдържание на обектите подадени на стандартния вход" -#: builtin/cat-file.c:376 +#: builtin/cat-file.c:421 msgid "show info about objects fed from the standard input" msgstr "извеждане на информация за обектите подадени на стандартния вход" +#: builtin/cat-file.c:424 +msgid "follow in-tree symlinks (used with --batch or --batch-check)" +msgstr "" +"следване на символните връзки сочещи в дървото (ползва се с „--batch“ или „--" +"batch-check“)" + #: builtin/check-attr.c:11 msgid "git check-attr [-a | --all | <attr>...] [--] <pathname>..." msgstr "git check-attr [-a | --all | АТРИБУТ…] [--] ПЪТ…" @@ -3488,7 +3536,7 @@ msgstr "извеждане на всички атрибути, зададени msgid "use .gitattributes only from the index" msgstr "използване на файла „.gitattributes“ само от индекса" -#: builtin/check-attr.c:21 builtin/check-ignore.c:22 builtin/hash-object.c:98 +#: builtin/check-attr.c:21 builtin/check-ignore.c:22 builtin/hash-object.c:96 msgid "read file names from stdin" msgstr "изчитане на имената на файловете от стандартния вход" @@ -3496,7 +3544,7 @@ msgstr "изчитане на имената на файловете от ста msgid "terminate input and output records by a NUL character" msgstr "разделяне на входните и изходните записи с нулевия знак „NUL“" -#: builtin/check-ignore.c:18 builtin/checkout.c:1107 builtin/gc.c:274 +#: builtin/check-ignore.c:18 builtin/checkout.c:1202 builtin/gc.c:279 msgid "suppress progress reporting" msgstr "без показване на напредъка" @@ -3596,117 +3644,116 @@ msgstr "при създаването на нови файлове да се д msgid "copy out the files from named stage" msgstr "копиране на файловете от това състояние на сливане" -#: builtin/checkout.c:25 +#: builtin/checkout.c:24 msgid "git checkout [<options>] <branch>" msgstr "git checkout [ОПЦИЯ…] КЛОН" -#: builtin/checkout.c:26 +#: builtin/checkout.c:25 msgid "git checkout [<options>] [<branch>] -- <file>..." msgstr "git checkout [ОПЦИЯ…] [КЛОН] -- ФАЙЛ…" -#: builtin/checkout.c:132 builtin/checkout.c:165 +#: builtin/checkout.c:134 builtin/checkout.c:167 #, c-format msgid "path '%s' does not have our version" msgstr "вашата версия липсва в пътя „%s“" -#: builtin/checkout.c:134 builtin/checkout.c:167 +#: builtin/checkout.c:136 builtin/checkout.c:169 #, c-format msgid "path '%s' does not have their version" msgstr "чуждата версия липсва в пътя „%s“" # FIXME SAME AS [1] -#: builtin/checkout.c:150 +#: builtin/checkout.c:152 #, c-format msgid "path '%s' does not have all necessary versions" msgstr "някоя от необходимите версии липсва в пътя „%s“" # FIXME SAME AS [1] -#: builtin/checkout.c:194 +#: builtin/checkout.c:196 #, c-format msgid "path '%s' does not have necessary versions" msgstr "някоя от необходимите версии липсва в пътя „%s“" -#: builtin/checkout.c:211 +#: builtin/checkout.c:213 #, c-format msgid "path '%s': cannot merge" msgstr "пътят „%s“ не може да бъде слян" -#: builtin/checkout.c:228 +#: builtin/checkout.c:230 #, c-format msgid "Unable to add merge result for '%s'" msgstr "Резултатът за „%s“ не може да бъде слян" -#: builtin/checkout.c:249 builtin/checkout.c:252 builtin/checkout.c:255 -#: builtin/checkout.c:258 +#: builtin/checkout.c:251 builtin/checkout.c:254 builtin/checkout.c:257 +#: builtin/checkout.c:260 #, c-format msgid "'%s' cannot be used with updating paths" msgstr "Опцията „%s“ е несъвместима с обновяването на пътища" -#: builtin/checkout.c:261 builtin/checkout.c:264 +#: builtin/checkout.c:263 builtin/checkout.c:266 #, c-format msgid "'%s' cannot be used with %s" msgstr "Опцията „%s“ е несъвместима с „%s“" -#: builtin/checkout.c:267 +#: builtin/checkout.c:269 #, c-format msgid "Cannot update paths and switch to branch '%s' at the same time." msgstr "" -"Невъзможно е едновременно да обновявате пътища и да се прехвърлите към клона " -"„%s“." +"Невъзможно е едновременно да обновявате пътища и да преминете към клона „%s“." -#: builtin/checkout.c:278 builtin/checkout.c:467 +#: builtin/checkout.c:280 builtin/checkout.c:474 msgid "corrupt index file" msgstr "повреден файл на индекса" -#: builtin/checkout.c:338 builtin/checkout.c:345 +#: builtin/checkout.c:340 builtin/checkout.c:347 #, c-format msgid "path '%s' is unmerged" msgstr "пътят „%s“ не е слят" -#: builtin/checkout.c:489 +#: builtin/checkout.c:496 msgid "you need to resolve your current index first" msgstr "първо трябва да коригирате индекса си" -#: builtin/checkout.c:615 +#: builtin/checkout.c:627 #, c-format msgid "Can not do reflog for '%s'\n" msgstr "Журналът на указателите за „%s“ не може да бъде създаден\n" -#: builtin/checkout.c:653 +#: builtin/checkout.c:663 msgid "HEAD is now at" msgstr "Указателят „HEAD“ в момента сочи към" -#: builtin/checkout.c:660 +#: builtin/checkout.c:670 #, c-format msgid "Reset branch '%s'\n" msgstr "Зануляване на клона „%s“\n" -#: builtin/checkout.c:663 +#: builtin/checkout.c:673 #, c-format msgid "Already on '%s'\n" msgstr "Вече сте на „%s“\n" -#: builtin/checkout.c:667 +#: builtin/checkout.c:677 #, c-format msgid "Switched to and reset branch '%s'\n" msgstr "Преминаване към клона „%s“ и зануляване на промените\n" -#: builtin/checkout.c:669 builtin/checkout.c:1050 +#: builtin/checkout.c:679 builtin/checkout.c:1134 #, c-format msgid "Switched to a new branch '%s'\n" msgstr "Преминахте към новия клон „%s“\n" -#: builtin/checkout.c:671 +#: builtin/checkout.c:681 #, c-format msgid "Switched to branch '%s'\n" msgstr "Преминахте към клона „%s“\n" -#: builtin/checkout.c:723 +#: builtin/checkout.c:733 #, c-format msgid " ... and %d more.\n" msgstr "… и още %d.\n" -#: builtin/checkout.c:729 +#: builtin/checkout.c:739 #, c-format msgid "" "Warning: you are leaving %d commit behind, not connected to\n" @@ -3728,158 +3775,180 @@ msgstr[1] "" "\n" "%s\n" -#: builtin/checkout.c:747 +#: builtin/checkout.c:758 #, c-format msgid "" +"If you want to keep it by creating a new branch, this may be a good time\n" +"to do so with:\n" +"\n" +" git branch <new-branch-name> %s\n" +"\n" +msgid_plural "" "If you want to keep them by creating a new branch, this may be a good time\n" "to do so with:\n" "\n" " git branch <new-branch-name> %s\n" "\n" -msgstr "" +msgstr[0] "" +"Ако все пак искате да запазите тази промяна чрез създаване на клон,\n" +"сега е най-подходящият за това чрез командата:\n" +"\n" +" git branch ИМЕ_НА_НОВИЯ_КЛОН %s\n" +"\n" +msgstr[1] "" "Ако все пак искате да запазите тези промени чрез създаване на клон,\n" "сега е най-подходящият за това чрез командата:\n" "\n" " git branch ИМЕ_НА_НОВИЯ_КЛОН %s\n" "\n" -#: builtin/checkout.c:777 +#: builtin/checkout.c:794 msgid "internal error in revision walk" msgstr "вътрешна грешка при обхождането на версиите" -#: builtin/checkout.c:781 +#: builtin/checkout.c:798 msgid "Previous HEAD position was" msgstr "Преди това „HEAD“ сочеше към" -#: builtin/checkout.c:808 builtin/checkout.c:1045 +#: builtin/checkout.c:825 builtin/checkout.c:1129 msgid "You are on a branch yet to be born" msgstr "В момента сте на клон, който предстои да бъде създаден" -#: builtin/checkout.c:952 +#: builtin/checkout.c:931 +#, c-format +msgid "'%s' is already checked out at '%s'" +msgstr "„%s“ вече е изтеглен в „%s“" + +#: builtin/checkout.c:1036 #, c-format msgid "only one reference expected, %d given." msgstr "очакваше се един указател, а сте подали %d." -#: builtin/checkout.c:991 +#: builtin/checkout.c:1075 #, c-format msgid "invalid reference: %s" msgstr "неправилен указател: %s" -#: builtin/checkout.c:1020 +#: builtin/checkout.c:1104 #, c-format msgid "reference is not a tree: %s" msgstr "указателят не сочи към обект-дърво: %s" -#: builtin/checkout.c:1059 +#: builtin/checkout.c:1143 msgid "paths cannot be used with switching branches" msgstr "задаването на път е несъвместимо с преминаването от един клон към друг" -#: builtin/checkout.c:1062 builtin/checkout.c:1066 +#: builtin/checkout.c:1146 builtin/checkout.c:1150 #, c-format msgid "'%s' cannot be used with switching branches" msgstr "опцията „%s“ е несъвместима с преминаването от един клон към друг" -#: builtin/checkout.c:1070 builtin/checkout.c:1073 builtin/checkout.c:1078 -#: builtin/checkout.c:1081 +#: builtin/checkout.c:1154 builtin/checkout.c:1157 builtin/checkout.c:1162 +#: builtin/checkout.c:1165 #, c-format msgid "'%s' cannot be used with '%s'" msgstr "опцията „%s“ е несъвместима с „%s“" -#: builtin/checkout.c:1086 +#: builtin/checkout.c:1170 #, c-format msgid "Cannot switch branch to a non-commit '%s'" msgstr "" "За да преминете към клон, подайте указател, който сочи към подаване. „%s“ не " "е такъв" -#: builtin/checkout.c:1108 builtin/checkout.c:1110 builtin/clone.c:90 -#: builtin/remote.c:159 builtin/remote.c:161 +#: builtin/checkout.c:1203 builtin/checkout.c:1205 builtin/clone.c:83 +#: builtin/remote.c:159 builtin/remote.c:161 builtin/worktree.c:282 +#: builtin/worktree.c:284 msgid "branch" msgstr "клон" -#: builtin/checkout.c:1109 +#: builtin/checkout.c:1204 msgid "create and checkout a new branch" msgstr "създаване и преминаване към нов клон" -#: builtin/checkout.c:1111 +#: builtin/checkout.c:1206 msgid "create/reset and checkout a branch" msgstr "създаване/зануляване на клон и преминаване към него" -#: builtin/checkout.c:1112 +#: builtin/checkout.c:1207 msgid "create reflog for new branch" msgstr "създаване на журнал на указателите за нов клон" -#: builtin/checkout.c:1113 +#: builtin/checkout.c:1208 msgid "detach the HEAD at named commit" msgstr "отделяне на указателя „HEAD“ към указаното подаване" -#: builtin/checkout.c:1114 +#: builtin/checkout.c:1209 msgid "set upstream info for new branch" msgstr "задаване на кой клон бива следен при създаването на новия клон" -#: builtin/checkout.c:1116 +#: builtin/checkout.c:1211 msgid "new-branch" msgstr "НОВ_КЛОН" -#: builtin/checkout.c:1116 +#: builtin/checkout.c:1211 msgid "new unparented branch" msgstr "нов клон без родител" -#: builtin/checkout.c:1117 +#: builtin/checkout.c:1212 msgid "checkout our version for unmerged files" msgstr "изтегляне на вашата версия на неслетите файлове" -#: builtin/checkout.c:1119 +#: builtin/checkout.c:1214 msgid "checkout their version for unmerged files" msgstr "изтегляне на чуждата версия на неслетите файлове" -#: builtin/checkout.c:1121 +#: builtin/checkout.c:1216 msgid "force checkout (throw away local modifications)" msgstr "принудително изтегляне (вашите промени ще бъдат занулени)" -#: builtin/checkout.c:1122 +#: builtin/checkout.c:1217 msgid "perform a 3-way merge with the new branch" msgstr "извършване на тройно сливане с новия клон" -#: builtin/checkout.c:1123 builtin/merge.c:227 +#: builtin/checkout.c:1218 builtin/merge.c:227 msgid "update ignored files (default)" msgstr "обновяване на игнорираните файлове (стандартно)" -#: builtin/checkout.c:1124 builtin/log.c:1239 parse-options.h:245 +#: builtin/checkout.c:1219 builtin/log.c:1239 parse-options.h:244 msgid "style" msgstr "стил" -#: builtin/checkout.c:1125 +#: builtin/checkout.c:1220 msgid "conflict style (merge or diff3)" msgstr "действие при конфликт (сливане или тройна разлика)" -#: builtin/checkout.c:1128 +#: builtin/checkout.c:1223 msgid "do not limit pathspecs to sparse entries only" msgstr "без ограничаване на изброените пътища само до частично изтеглените" -#: builtin/checkout.c:1130 +#: builtin/checkout.c:1225 msgid "second guess 'git checkout <no-such-branch>'" msgstr "" "опит за отгатване на име на клон след неуспешен опит с „git checkout " "НЕСЪЩЕСТВУВАЩ_КЛОН“" -#: builtin/checkout.c:1153 +#: builtin/checkout.c:1227 +msgid "do not check if another worktree is holding the given ref" +msgstr "без проверка дали друго работно дърво държи указателя" + +#: builtin/checkout.c:1252 msgid "-b, -B and --orphan are mutually exclusive" msgstr "Опциите „-b“, „-B“ и „--orphan“ са несъвместими една с друга" -#: builtin/checkout.c:1170 +#: builtin/checkout.c:1269 msgid "--track needs a branch name" msgstr "опцията „--track“ изисква име на клон" -#: builtin/checkout.c:1175 +#: builtin/checkout.c:1274 msgid "Missing branch name; try -b" msgstr "Липсва име на клон, използвайте опцията „-b“" -#: builtin/checkout.c:1212 +#: builtin/checkout.c:1310 msgid "invalid path specification" msgstr "указан е неправилен път" -#: builtin/checkout.c:1219 +#: builtin/checkout.c:1317 #, c-format msgid "" "Cannot update paths and switch to branch '%s' at the same time.\n" @@ -3888,12 +3957,12 @@ msgstr "" "Не можете едновременно да обновявате пътища и да преминете към клона „%s“.\n" "Дали не искате да изтеглите „%s“, който не сочи към подаване?" -#: builtin/checkout.c:1224 +#: builtin/checkout.c:1322 #, c-format msgid "git checkout: --detach does not take a path argument '%s'" msgstr "git checkout: опцията „--detach“ не приема аргумент-път „%s“" -#: builtin/checkout.c:1228 +#: builtin/checkout.c:1326 msgid "" "git checkout: --ours/--theirs, --force and --merge are incompatible when\n" "checking out of the index." @@ -3964,38 +4033,38 @@ msgstr "" " — (празно) завършване на избирането" # FIXME WTF does this mean -#: builtin/clean.c:517 +#: builtin/clean.c:515 #, c-format msgid "Huh (%s)?" msgstr "Неправилен избор (%s). Изберете отново." # FIXME - should we use >> or sth else -#: builtin/clean.c:659 +#: builtin/clean.c:657 #, c-format msgid "Input ignore patterns>> " msgstr "Шаблони за игнорирани елементи≫ " -#: builtin/clean.c:696 +#: builtin/clean.c:694 #, c-format msgid "WARNING: Cannot find items matched by: %s" msgstr "ПРЕДУПРЕЖДЕНИЕ: Никой обект не напасва на „%s“" -#: builtin/clean.c:717 +#: builtin/clean.c:715 msgid "Select items to delete" msgstr "Избиране на обекти за изтриване" #. TRANSLATORS: Make sure to keep [y/N] as is -#: builtin/clean.c:758 +#: builtin/clean.c:756 #, c-format msgid "Remove %s [y/N]? " msgstr "Да се изтрие ли „%s“? „y“ — да, „N“ — НЕ" # FIXME improve message -#: builtin/clean.c:783 +#: builtin/clean.c:781 msgid "Bye." msgstr "Изход." -#: builtin/clean.c:791 +#: builtin/clean.c:789 msgid "" "clean - start cleaning\n" "filter by pattern - exclude items from deletion\n" @@ -4014,63 +4083,63 @@ msgstr "" "? — подсказка за шаблоните" # FIXME how many *** -#: builtin/clean.c:818 +#: builtin/clean.c:816 msgid "*** Commands ***" msgstr "●●● Команди ●●●" # FIXME improve message -#: builtin/clean.c:819 +#: builtin/clean.c:817 msgid "What now" msgstr "Избор на следващо действие" -#: builtin/clean.c:827 +#: builtin/clean.c:825 msgid "Would remove the following item:" msgid_plural "Would remove the following items:" msgstr[0] "Следният обект ще бъде изтрит:" msgstr[1] "Следните обекти ще бъдат изтрити:" -#: builtin/clean.c:844 +#: builtin/clean.c:842 msgid "No more files to clean, exiting." msgstr "Файловете за изчистване свършиха. Изход от програмата." -#: builtin/clean.c:875 +#: builtin/clean.c:873 msgid "do not print names of files removed" msgstr "без извеждане на имената на файловете, които ще бъдат изтрити" -#: builtin/clean.c:877 +#: builtin/clean.c:875 msgid "force" msgstr "принудително изтриване" -#: builtin/clean.c:878 +#: builtin/clean.c:876 msgid "interactive cleaning" msgstr "интерактивно изтриване" -#: builtin/clean.c:880 +#: builtin/clean.c:878 msgid "remove whole directories" msgstr "изтриване на цели директории" -#: builtin/clean.c:881 builtin/describe.c:407 builtin/grep.c:714 -#: builtin/ls-files.c:443 builtin/name-rev.c:311 builtin/show-ref.c:185 +#: builtin/clean.c:879 builtin/describe.c:407 builtin/grep.c:714 +#: builtin/ls-files.c:443 builtin/name-rev.c:311 builtin/show-ref.c:187 msgid "pattern" msgstr "шаблон" -#: builtin/clean.c:882 +#: builtin/clean.c:880 msgid "add <pattern> to ignore rules" msgstr "добавяне на ШАБЛОН от файлове, които да не се трият" -#: builtin/clean.c:883 +#: builtin/clean.c:881 msgid "remove ignored files, too" msgstr "изтриване и на игнорираните файлове" -#: builtin/clean.c:885 +#: builtin/clean.c:883 msgid "remove only ignored files" msgstr "изтриване само на игнорирани файлове" -#: builtin/clean.c:903 +#: builtin/clean.c:901 msgid "-x and -X cannot be used together" msgstr "опциите „-x“ и „-X“ са несъвместими" -#: builtin/clean.c:907 +#: builtin/clean.c:905 msgid "" "clean.requireForce set to true and neither -i, -n, nor -f given; refusing to " "clean" @@ -4078,7 +4147,7 @@ msgstr "" "Настройката „clean.requireForce“ е зададена като истина, което изисква някоя " "от опциите „-i“, „-n“ или „-f“. Няма да се извърши изчистване" -#: builtin/clean.c:910 +#: builtin/clean.c:908 msgid "" "clean.requireForce defaults to true and neither -i, -n, nor -f given; " "refusing to clean" @@ -4091,153 +4160,153 @@ msgstr "" msgid "git clone [<options>] [--] <repo> [<dir>]" msgstr "git clone [ОПЦИЯ…] [--] ХРАНИЛИЩЕ [ДИРЕКТОРИЯ]" -#: builtin/clone.c:66 builtin/fetch.c:111 builtin/merge.c:224 +#: builtin/clone.c:57 builtin/fetch.c:111 builtin/merge.c:224 #: builtin/push.c:523 msgid "force progress reporting" msgstr "извеждане на напредъка" -#: builtin/clone.c:68 +#: builtin/clone.c:59 msgid "don't create a checkout" msgstr "без създаване на работно дърво" -#: builtin/clone.c:69 builtin/clone.c:71 builtin/init-db.c:496 +#: builtin/clone.c:60 builtin/clone.c:62 builtin/init-db.c:503 msgid "create a bare repository" msgstr "създаване на голо хранилище" -#: builtin/clone.c:73 +#: builtin/clone.c:64 msgid "create a mirror repository (implies bare)" msgstr "" "създаване на хранилище-огледало (включва опцията „--bare“ за голо хранилище)" -#: builtin/clone.c:75 +#: builtin/clone.c:66 msgid "to clone from a local repository" msgstr "клониране от локално хранилище" -#: builtin/clone.c:77 +#: builtin/clone.c:68 msgid "don't use local hardlinks, always copy" msgstr "без твърди връзки, файловете винаги да се копират" -#: builtin/clone.c:79 +#: builtin/clone.c:70 msgid "setup as shared repository" msgstr "настройване за споделено хранилище" -#: builtin/clone.c:81 builtin/clone.c:83 +#: builtin/clone.c:72 builtin/clone.c:74 msgid "initialize submodules in the clone" msgstr "инициализиране на подмодулите при това клониране" -#: builtin/clone.c:84 builtin/init-db.c:493 +#: builtin/clone.c:75 builtin/init-db.c:500 msgid "template-directory" msgstr "директория с шаблони" -#: builtin/clone.c:85 builtin/init-db.c:494 +#: builtin/clone.c:76 builtin/init-db.c:501 msgid "directory from which templates will be used" msgstr "директория, която съдържа шаблоните, които да се ползват" -#: builtin/clone.c:87 +#: builtin/clone.c:78 msgid "reference repository" msgstr "еталонно хранилище" -#: builtin/clone.c:88 builtin/column.c:26 builtin/merge-file.c:44 +#: builtin/clone.c:80 +msgid "use --reference only while cloning" +msgstr "опцията „--reference“ може да се използва само при клониране" + +#: builtin/clone.c:81 builtin/column.c:26 builtin/merge-file.c:44 msgid "name" msgstr "ИМЕ" -#: builtin/clone.c:89 +#: builtin/clone.c:82 msgid "use <name> instead of 'origin' to track upstream" msgstr "използване на това ИМЕ вместо „origin“ при проследяване на клони" -#: builtin/clone.c:91 +#: builtin/clone.c:84 msgid "checkout <branch> instead of the remote's HEAD" msgstr "изтегляне на този КЛОН, а не соченият от отдалечения указател „HEAD“" -#: builtin/clone.c:93 +#: builtin/clone.c:86 msgid "path to git-upload-pack on the remote" msgstr "път към командата „git-upload-pack“ на отдалеченото хранилище" -#: builtin/clone.c:94 builtin/fetch.c:112 builtin/grep.c:659 +#: builtin/clone.c:87 builtin/fetch.c:112 builtin/grep.c:659 msgid "depth" msgstr "ДЪЛБОЧИНА" -#: builtin/clone.c:95 +#: builtin/clone.c:88 msgid "create a shallow clone of that depth" msgstr "плитко клониране до тази ДЪЛБОЧИНА" -#: builtin/clone.c:97 +#: builtin/clone.c:90 msgid "clone only one branch, HEAD or --branch" msgstr "" "клониране само на един клон — или сочения от отдалечения „HEAD“, или изрично " "зададения с „--branch“" -#: builtin/clone.c:99 -msgid "use --reference only while cloning" -msgstr "опцията „--reference“ може да се използва само при клониране" - -#: builtin/clone.c:100 builtin/init-db.c:502 +#: builtin/clone.c:91 builtin/init-db.c:509 msgid "gitdir" msgstr "СЛУЖЕБНА_ДИРЕКТОРИЯ" -#: builtin/clone.c:101 builtin/init-db.c:503 +#: builtin/clone.c:92 builtin/init-db.c:510 msgid "separate git dir from working tree" msgstr "отделна СЛУЖЕБНА_ДИРЕКТОРИЯ за git извън работното дърво" -#: builtin/clone.c:102 +#: builtin/clone.c:93 msgid "key=value" msgstr "КЛЮЧ=СТОЙНОСТ" -#: builtin/clone.c:103 +#: builtin/clone.c:94 msgid "set config inside the new repository" msgstr "задаване на настройките на новото хранилище" -#: builtin/clone.c:256 +#: builtin/clone.c:240 #, c-format msgid "reference repository '%s' is not a local repository." msgstr "еталонното хранилище „%s“ не е локално" -#: builtin/clone.c:260 +#: builtin/clone.c:244 #, c-format msgid "reference repository '%s' is shallow" msgstr "еталонното хранилище „%s“ е плитко" -#: builtin/clone.c:263 +#: builtin/clone.c:247 #, c-format msgid "reference repository '%s' is grafted" msgstr "еталонното хранилище „%s“ е с присаждане" -#: builtin/clone.c:325 +#: builtin/clone.c:310 #, c-format msgid "failed to create directory '%s'" msgstr "директорията „%s“ не може да бъде създадена" -#: builtin/clone.c:327 builtin/diff.c:84 +#: builtin/clone.c:312 builtin/diff.c:84 #, c-format msgid "failed to stat '%s'" msgstr "не може да бъде получена информация чрез „stat“ за „%s“" -#: builtin/clone.c:329 +#: builtin/clone.c:314 #, c-format msgid "%s exists and is not a directory" msgstr "„%s“ съществува и не е директория" -#: builtin/clone.c:343 +#: builtin/clone.c:328 #, c-format msgid "failed to stat %s\n" msgstr "не може да бъде получена информация чрез „stat“ за „%s“\n" -#: builtin/clone.c:365 +#: builtin/clone.c:350 #, c-format msgid "failed to create link '%s'" msgstr "връзката „%s“ не може да бъде създадена" -#: builtin/clone.c:369 +#: builtin/clone.c:354 #, c-format msgid "failed to copy file to '%s'" msgstr "файлът не може да бъде копиран като „%s“" -#: builtin/clone.c:392 builtin/clone.c:566 +#: builtin/clone.c:377 builtin/clone.c:551 #, c-format msgid "done.\n" msgstr "действието завърши.\n" -#: builtin/clone.c:404 +#: builtin/clone.c:389 msgid "" "Clone succeeded, but checkout failed.\n" "You can inspect what was checked out with 'git status'\n" @@ -4248,7 +4317,7 @@ msgstr "" "клон в момента са изтеглени с командата „git status“. Можете да\n" "завършите изтеглянето на клона с командата „git checkout -f HEAD“.\n" -#: builtin/clone.c:481 +#: builtin/clone.c:466 #, c-format msgid "Could not find remote branch %s to clone." msgstr "" @@ -4256,119 +4325,120 @@ msgstr "" "и който следва да бъде изтеглен, не съществува." # FIXME translator note that the space at end is necesssary -#: builtin/clone.c:561 +#: builtin/clone.c:546 #, c-format msgid "Checking connectivity... " msgstr "Проверка на връзката… " -#: builtin/clone.c:564 +#: builtin/clone.c:549 msgid "remote did not send all necessary objects" msgstr "отдалеченото хранилище не изпрати всички необходими обекти." -#: builtin/clone.c:628 +#: builtin/clone.c:613 msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n" msgstr "" "указателят „HEAD“ от отдалеченото хранилище сочи към нещо,\n" "което не съществува. Не може да се изтегли определен клон.\n" -#: builtin/clone.c:659 +#: builtin/clone.c:644 msgid "unable to checkout working tree" msgstr "работното дърво не може да бъде подготвено" -#: builtin/clone.c:746 +#: builtin/clone.c:731 msgid "cannot repack to clean up" msgstr "не може да се извърши пакетиране за изчистване на файловете" -#: builtin/clone.c:748 +#: builtin/clone.c:733 msgid "cannot unlink temporary alternates file" msgstr "временният файл за алтернативни обекти не може да бъде изтрит" -#: builtin/clone.c:778 +#: builtin/clone.c:763 msgid "Too many arguments." msgstr "Прекалено много аргументи." -#: builtin/clone.c:782 +#: builtin/clone.c:767 msgid "You must specify a repository to clone." msgstr "Трябва да укажете кое хранилище искате да клонирате." -#: builtin/clone.c:793 +#: builtin/clone.c:778 #, c-format msgid "--bare and --origin %s options are incompatible." msgstr "опциите „--bare“ и „--origin %s“ са несъвместими." -#: builtin/clone.c:796 +#: builtin/clone.c:781 msgid "--bare and --separate-git-dir are incompatible." msgstr "опциите „--bare“ и „--separate-git-dir“ са несъвместими." -#: builtin/clone.c:809 +#: builtin/clone.c:794 #, c-format msgid "repository '%s' does not exist" msgstr "не съществува хранилище „%s“" -#: builtin/clone.c:815 builtin/fetch.c:1156 +#: builtin/clone.c:800 builtin/fetch.c:1160 #, c-format msgid "depth %s is not a positive number" msgstr "дълбочината трябва да е положително цяло число, а не „%s“" -#: builtin/clone.c:825 +#: builtin/clone.c:810 #, c-format msgid "destination path '%s' already exists and is not an empty directory." msgstr "целевият път „%s“ съществува и не е празна директория." -#: builtin/clone.c:835 +#: builtin/clone.c:820 #, c-format msgid "working tree '%s' already exists." msgstr "в „%s“ вече съществува работно дърво." -#: builtin/clone.c:850 builtin/clone.c:861 +#: builtin/clone.c:835 builtin/clone.c:846 builtin/worktree.c:193 +#: builtin/worktree.c:220 #, c-format msgid "could not create leading directories of '%s'" msgstr "родителските директории на „%s“ не могат да бъдат създадени" -#: builtin/clone.c:853 +#: builtin/clone.c:838 #, c-format msgid "could not create work tree dir '%s'" msgstr "работното дърво в „%s“ не може да бъде създадено." -#: builtin/clone.c:871 +#: builtin/clone.c:856 #, c-format msgid "Cloning into bare repository '%s'...\n" msgstr "Клониране и създаване на голо хранилище в „%s“…\n" -#: builtin/clone.c:873 +#: builtin/clone.c:858 #, c-format msgid "Cloning into '%s'...\n" msgstr "Клониране и създаване на хранилище в „%s“…\n" -#: builtin/clone.c:898 +#: builtin/clone.c:883 msgid "--dissociate given, but there is no --reference" msgstr "Опцията „--dissociate“ е несъвместима с „--reference“" -#: builtin/clone.c:913 +#: builtin/clone.c:900 msgid "--depth is ignored in local clones; use file:// instead." msgstr "" "При локално клониране опцията „--depth“ се игнорира. Ползвайте схемата " "„file://“." -#: builtin/clone.c:916 +#: builtin/clone.c:903 msgid "source repository is shallow, ignoring --local" msgstr "клонираното хранилище е плитко, затова опцията „--local“ се игнорира" -#: builtin/clone.c:921 +#: builtin/clone.c:908 msgid "--local is ignored" msgstr "опцията „--local“ се игнорира" -#: builtin/clone.c:925 +#: builtin/clone.c:912 #, c-format msgid "Don't know how to clone %s" msgstr "Не се поддържа клониране на връзки от вида „%s“ " -#: builtin/clone.c:976 builtin/clone.c:984 +#: builtin/clone.c:961 builtin/clone.c:969 #, c-format msgid "Remote branch %s not found in upstream %s" msgstr "Отдалеченият клон „%s“ липсва в клонираното хранилище „%s“" -#: builtin/clone.c:987 +#: builtin/clone.c:972 msgid "You appear to have cloned an empty repository." msgstr "Изглежда клонирахте празно хранилище." @@ -4610,7 +4680,7 @@ msgstr "съобщението за сливане MERGE_MSG не може да msgid "could not read SQUASH_MSG" msgstr "съобщението за смачкване SQUASH_MSG не може да бъде прочетено" -#: builtin/commit.c:738 +#: builtin/commit.c:738 builtin/merge.c:1079 #, c-format msgid "could not read '%s'" msgstr "файлът „%s“ не може да бъде прочетен" @@ -4785,32 +4855,32 @@ msgstr "Несъществуващ режим на изчистване „%s“ msgid "Paths with -a does not make sense." msgstr "Опцията „-a“ е несъвместима със задаването на пътища." -#: builtin/commit.c:1324 builtin/commit.c:1604 +#: builtin/commit.c:1324 builtin/commit.c:1603 msgid "show status concisely" msgstr "кратка информация за състоянието" -#: builtin/commit.c:1326 builtin/commit.c:1606 +#: builtin/commit.c:1326 builtin/commit.c:1605 msgid "show branch information" msgstr "информация за клоните" -#: builtin/commit.c:1328 builtin/commit.c:1608 builtin/push.c:509 +#: builtin/commit.c:1328 builtin/commit.c:1607 builtin/push.c:509 msgid "machine-readable output" msgstr "формат на изхода за четене от програма" -#: builtin/commit.c:1331 builtin/commit.c:1610 +#: builtin/commit.c:1331 builtin/commit.c:1609 msgid "show status in long format (default)" msgstr "подробна информация за състоянието (стандартно)" -#: builtin/commit.c:1334 builtin/commit.c:1613 +#: builtin/commit.c:1334 builtin/commit.c:1612 msgid "terminate entries with NUL" msgstr "разделяне на елементите с нулевия знак „NUL“" -#: builtin/commit.c:1336 builtin/commit.c:1616 builtin/fast-export.c:980 +#: builtin/commit.c:1336 builtin/commit.c:1615 builtin/fast-export.c:980 #: builtin/fast-export.c:983 builtin/tag.c:603 msgid "mode" msgstr "режим" -#: builtin/commit.c:1337 builtin/commit.c:1616 +#: builtin/commit.c:1337 builtin/commit.c:1615 msgid "show untracked files, optional modes: all, normal, no. (Default: all)" msgstr "" "извеждане на неследените файлове. Възможните режими са „all“ (подробна\n" @@ -4821,7 +4891,7 @@ msgstr "" msgid "show ignored files" msgstr "извеждане на игнорираните файлове" -#: builtin/commit.c:1341 parse-options.h:153 +#: builtin/commit.c:1341 parse-options.h:152 msgid "when" msgstr "кога" @@ -4838,209 +4908,209 @@ msgstr "" msgid "list untracked files in columns" msgstr "извеждане на неследените файлове в колони" -#: builtin/commit.c:1431 +#: builtin/commit.c:1430 msgid "couldn't look up newly created commit" msgstr "току що създаденото подаване не може да бъде открито" -#: builtin/commit.c:1433 +#: builtin/commit.c:1432 msgid "could not parse newly created commit" msgstr "току що създаденото подаване не може да бъде анализирано" -#: builtin/commit.c:1478 +#: builtin/commit.c:1477 msgid "detached HEAD" msgstr "несвързан връх „HEAD“" -#: builtin/commit.c:1481 +#: builtin/commit.c:1480 msgid " (root-commit)" msgstr " (начално подаване)" -#: builtin/commit.c:1574 +#: builtin/commit.c:1573 msgid "suppress summary after successful commit" msgstr "без информация след успешно подаване" -#: builtin/commit.c:1575 +#: builtin/commit.c:1574 msgid "show diff in commit message template" msgstr "добавяне на разликата към шаблона за съобщението при подаване" -#: builtin/commit.c:1577 +#: builtin/commit.c:1576 msgid "Commit message options" msgstr "Опции за съобщението при подаване" -#: builtin/commit.c:1578 builtin/tag.c:601 +#: builtin/commit.c:1577 builtin/tag.c:601 msgid "read message from file" msgstr "взимане на съобщението от файл" -#: builtin/commit.c:1579 +#: builtin/commit.c:1578 msgid "author" msgstr "автор" -#: builtin/commit.c:1579 +#: builtin/commit.c:1578 msgid "override author for commit" msgstr "задаване на автор за подаването" -#: builtin/commit.c:1580 builtin/gc.c:275 +#: builtin/commit.c:1579 builtin/gc.c:280 msgid "date" msgstr "дата" -#: builtin/commit.c:1580 +#: builtin/commit.c:1579 msgid "override date for commit" msgstr "задаване на дата за подаването" -#: builtin/commit.c:1581 builtin/merge.c:218 builtin/notes.c:391 +#: builtin/commit.c:1580 builtin/merge.c:218 builtin/notes.c:391 #: builtin/notes.c:554 builtin/tag.c:599 msgid "message" msgstr "съобщение" -#: builtin/commit.c:1581 +#: builtin/commit.c:1580 msgid "commit message" msgstr "съобщение при подаване" -#: builtin/commit.c:1582 +#: builtin/commit.c:1581 msgid "reuse and edit message from specified commit" msgstr "преизползване и редактиране на съобщението от указаното подаване" -#: builtin/commit.c:1583 +#: builtin/commit.c:1582 msgid "reuse message from specified commit" msgstr "преизползване на съобщението от указаното подаване" -#: builtin/commit.c:1584 +#: builtin/commit.c:1583 msgid "use autosquash formatted message to fixup specified commit" msgstr "" "използване на автоматичното съобщение при смачкване за вкарване на " "указаното\n" "подаване в предното без следа" -#: builtin/commit.c:1585 +#: builtin/commit.c:1584 msgid "use autosquash formatted message to squash specified commit" msgstr "" "използване на автоматичното съобщение при смачкване за смачкване на " "указаното\n" "подаване в предното" -#: builtin/commit.c:1586 +#: builtin/commit.c:1585 msgid "the commit is authored by me now (used with -C/-c/--amend)" msgstr "" "смяна на автора да съвпада с подаващия (използва се с „-C“/„-c“/„--amend“)" -#: builtin/commit.c:1587 builtin/log.c:1191 builtin/revert.c:86 +#: builtin/commit.c:1586 builtin/log.c:1191 builtin/revert.c:86 msgid "add Signed-off-by:" msgstr "добавяне на поле за подпис — „Signed-off-by:“" -#: builtin/commit.c:1588 +#: builtin/commit.c:1587 msgid "use specified template file" msgstr "използване на указания шаблонен файл" -#: builtin/commit.c:1589 +#: builtin/commit.c:1588 msgid "force edit of commit" msgstr "редактиране на подаване" -#: builtin/commit.c:1590 +#: builtin/commit.c:1589 msgid "default" msgstr "стандартно" -#: builtin/commit.c:1590 builtin/tag.c:604 +#: builtin/commit.c:1589 builtin/tag.c:604 msgid "how to strip spaces and #comments from message" msgstr "кои празни знаци и #коментари да се махат от съобщенията" -#: builtin/commit.c:1591 +#: builtin/commit.c:1590 msgid "include status in commit message template" msgstr "вмъкване на състоянието в шаблона за съобщението при подаване" -#: builtin/commit.c:1592 builtin/merge.c:225 builtin/revert.c:92 +#: builtin/commit.c:1591 builtin/merge.c:225 builtin/revert.c:92 #: builtin/tag.c:605 msgid "key-id" msgstr "ИДЕНТИФИКАТОР_НА_КЛЮЧ" -#: builtin/commit.c:1593 builtin/merge.c:226 builtin/revert.c:93 +#: builtin/commit.c:1592 builtin/merge.c:226 builtin/revert.c:93 msgid "GPG sign commit" msgstr "подписване на подаването с GPG" -#: builtin/commit.c:1596 +#: builtin/commit.c:1595 msgid "Commit contents options" msgstr "Опции за избор на файлове при подаване" -#: builtin/commit.c:1597 +#: builtin/commit.c:1596 msgid "commit all changed files" msgstr "подаване на всички променени файлове" -#: builtin/commit.c:1598 +#: builtin/commit.c:1597 msgid "add specified files to index for commit" msgstr "добавяне на указаните файлове към индекса за подаване" -#: builtin/commit.c:1599 +#: builtin/commit.c:1598 msgid "interactively add files" msgstr "интерактивно добавяне на файлове" -#: builtin/commit.c:1600 +#: builtin/commit.c:1599 msgid "interactively add changes" msgstr "интерактивно добавяне на промени" -#: builtin/commit.c:1601 +#: builtin/commit.c:1600 msgid "commit only specified files" msgstr "подаване само на указаните файлове" -#: builtin/commit.c:1602 +#: builtin/commit.c:1601 msgid "bypass pre-commit hook" msgstr "без изпълнение на куката преди подаване (pre-commit)" -#: builtin/commit.c:1603 +#: builtin/commit.c:1602 msgid "show what would be committed" msgstr "отпечатване на това, което би било подадено" -#: builtin/commit.c:1614 +#: builtin/commit.c:1613 msgid "amend previous commit" msgstr "поправяне на предишното подаване" -#: builtin/commit.c:1615 +#: builtin/commit.c:1614 msgid "bypass post-rewrite hook" msgstr "без изпълнение на куката след презаписване (post-rewrite)" -#: builtin/commit.c:1620 +#: builtin/commit.c:1619 msgid "ok to record an empty change" msgstr "позволяване на празни подавания" -#: builtin/commit.c:1622 +#: builtin/commit.c:1621 msgid "ok to record a change with an empty message" msgstr "позволяване на подавания с празни съобщения" -#: builtin/commit.c:1651 +#: builtin/commit.c:1650 msgid "could not parse HEAD commit" msgstr "върховото подаване „HEAD“ не може да бъде прочетено" -#: builtin/commit.c:1690 builtin/merge.c:519 +#: builtin/commit.c:1689 builtin/merge.c:1076 #, c-format msgid "could not open '%s' for reading" msgstr "файлът не може да бъде прочетен: „%s“" -#: builtin/commit.c:1697 +#: builtin/commit.c:1696 #, c-format msgid "Corrupt MERGE_HEAD file (%s)" msgstr "Повреден файл за върха за сливането „MERGE_HEAD“ (%s)" -#: builtin/commit.c:1704 +#: builtin/commit.c:1703 msgid "could not read MERGE_MODE" msgstr "режимът на сливане „MERGE_MODE“ не може да бъде прочетен" -#: builtin/commit.c:1723 +#: builtin/commit.c:1722 #, c-format msgid "could not read commit message: %s" msgstr "съобщението за подаване не може да бъде прочетено: %s" -#: builtin/commit.c:1734 +#: builtin/commit.c:1733 #, c-format msgid "Aborting commit; you did not edit the message.\n" msgstr "Неизвършване на подаване поради нередактирано съобщение.\n" -#: builtin/commit.c:1739 +#: builtin/commit.c:1738 #, c-format msgid "Aborting commit due to empty commit message.\n" msgstr "Неизвършване на подаване поради празно съобщение.\n" -#: builtin/commit.c:1754 builtin/merge.c:851 builtin/merge.c:876 +#: builtin/commit.c:1753 builtin/merge.c:829 builtin/merge.c:854 msgid "failed to write commit object" msgstr "обектът за подаването не може да бъде записан" -#: builtin/commit.c:1787 +#: builtin/commit.c:1786 msgid "" "Repository has been updated, but unable to write\n" "new_index file. Check that disk is not full and quota is\n" @@ -5188,18 +5258,18 @@ msgstr "неразпозната стойност на стандартния ц #, c-format msgid "" "# This is Git's per-user configuration file.\n" -"[core]\n" +"[user]\n" "# Please adapt and uncomment the following lines:\n" -"#\tuser = %s\n" +"#\tname = %s\n" "#\temail = %s\n" msgstr "" "# Това е потребителският ви конфигурационен файл за Git.\n" -"[core]\n" +"[user]\n" "# Проверете и разкоментирайте следните два реда:\n" -"#\tuser = %s\n" +"#\tname = %s\n" "#\temail = %s\n" -#: builtin/config.c:589 +#: builtin/config.c:587 #, c-format msgid "cannot create configuration file %s" msgstr "конфигурационният файл „%s“ не може да бъде създаден" @@ -5533,84 +5603,84 @@ msgstr "карта с указатели" msgid "specify fetch refmap" msgstr "указване на картата с указатели за доставяне" -#: builtin/fetch.c:375 +#: builtin/fetch.c:377 msgid "Couldn't find remote ref HEAD" msgstr "Указателят „HEAD“ в отдалеченото хранилище не може да бъде открит" -#: builtin/fetch.c:455 +#: builtin/fetch.c:457 #, c-format msgid "object %s not found" msgstr "обектът „%s“ липсва" -#: builtin/fetch.c:460 +#: builtin/fetch.c:462 msgid "[up to date]" msgstr "[актуализиран]" -#: builtin/fetch.c:474 +#: builtin/fetch.c:476 #, c-format msgid "! %-*s %-*s -> %s (can't fetch in current branch)" msgstr "! %-*s %-*s → %s (в текущия клон не може да се доставя)" -#: builtin/fetch.c:475 builtin/fetch.c:561 +#: builtin/fetch.c:477 builtin/fetch.c:563 msgid "[rejected]" msgstr "[отхвърлен]" -#: builtin/fetch.c:486 +#: builtin/fetch.c:488 msgid "[tag update]" msgstr "[обновяване на етикетите]" -#: builtin/fetch.c:488 builtin/fetch.c:523 builtin/fetch.c:541 +#: builtin/fetch.c:490 builtin/fetch.c:525 builtin/fetch.c:543 msgid " (unable to update local ref)" msgstr " (локалните указатели не могат да бъдат обновени)" -#: builtin/fetch.c:506 +#: builtin/fetch.c:508 msgid "[new tag]" msgstr "[нов етикет]" -#: builtin/fetch.c:509 +#: builtin/fetch.c:511 msgid "[new branch]" msgstr "[нов клон]" -#: builtin/fetch.c:512 +#: builtin/fetch.c:514 msgid "[new ref]" msgstr "[нов указател]" -#: builtin/fetch.c:557 +#: builtin/fetch.c:559 msgid "unable to update local ref" msgstr "локален указател не може да бъде обновен" -#: builtin/fetch.c:557 +#: builtin/fetch.c:559 msgid "forced update" msgstr "принудително обновяване" -#: builtin/fetch.c:563 +#: builtin/fetch.c:565 msgid "(non-fast-forward)" msgstr "(сливането не е тривиално)" -#: builtin/fetch.c:596 builtin/fetch.c:829 +#: builtin/fetch.c:599 builtin/fetch.c:832 #, c-format msgid "cannot open %s: %s\n" msgstr "файлът „%s“ не може да бъде отворен: %s\n" -#: builtin/fetch.c:605 +#: builtin/fetch.c:608 #, c-format msgid "%s did not send all necessary objects\n" msgstr "хранилището „%s“ не изпрати всички необходими обекти\n" -#: builtin/fetch.c:623 +#: builtin/fetch.c:626 #, c-format msgid "reject %s because shallow roots are not allowed to be updated" msgstr "" "отхвърляне на върха „%s“, защото плитките хранилища не могат да бъдат " "обновявани" -#: builtin/fetch.c:711 builtin/fetch.c:794 +#: builtin/fetch.c:714 builtin/fetch.c:797 #, c-format msgid "From %.*s\n" msgstr "От %.*s\n" # FIXME - is the space necessary -#: builtin/fetch.c:722 +#: builtin/fetch.c:725 #, c-format msgid "" "some local refs could not be updated; try running\n" @@ -5620,55 +5690,55 @@ msgstr "" "„git remote prune %s“, за да премахнете остарелите клони, които\n" "предизвикват конфликта" -#: builtin/fetch.c:774 +#: builtin/fetch.c:777 #, c-format msgid " (%s will become dangling)" msgstr " (обектът „%s“ ще се окаже извън клон)" -#: builtin/fetch.c:775 +#: builtin/fetch.c:778 #, c-format msgid " (%s has become dangling)" msgstr " (обектът „%s“ вече е извън клон)" -#: builtin/fetch.c:799 +#: builtin/fetch.c:802 msgid "[deleted]" msgstr "[изтрит]" -#: builtin/fetch.c:800 builtin/remote.c:1060 +#: builtin/fetch.c:803 builtin/remote.c:1057 msgid "(none)" msgstr "(нищо)" -#: builtin/fetch.c:819 +#: builtin/fetch.c:822 #, c-format msgid "Refusing to fetch into current branch %s of non-bare repository" msgstr "Не може да изтегляте в текущия клон „%s“ на хранилище, което не е голо" -#: builtin/fetch.c:838 +#: builtin/fetch.c:841 #, c-format msgid "Option \"%s\" value \"%s\" is not valid for %s" msgstr "Стойността „%2$s“ за опцията „%1$s“ не е съвместима с „%3$s“" -#: builtin/fetch.c:841 +#: builtin/fetch.c:844 #, c-format msgid "Option \"%s\" is ignored for %s\n" msgstr "Опцията „%s“ се игнорира при „%s“\n" -#: builtin/fetch.c:897 +#: builtin/fetch.c:900 #, c-format msgid "Don't know how to fetch from %s" msgstr "Не се поддържа доставяне от „%s“" -#: builtin/fetch.c:1059 +#: builtin/fetch.c:1063 #, c-format msgid "Fetching %s\n" msgstr "Доставяне на „%s“\n" -#: builtin/fetch.c:1061 builtin/remote.c:90 +#: builtin/fetch.c:1065 builtin/remote.c:90 #, c-format msgid "Could not fetch %s" msgstr "„%s“ не може да се достави" -#: builtin/fetch.c:1079 +#: builtin/fetch.c:1083 msgid "" "No remote repository specified. Please, specify either a URL or a\n" "remote name from which new revisions should be fetched." @@ -5676,33 +5746,33 @@ msgstr "" "Не сте указали отдалечено хранилище. Задайте или адрес, или име\n" "на отдалечено хранилище, откъдето да се доставят новите версии." -#: builtin/fetch.c:1102 +#: builtin/fetch.c:1106 msgid "You need to specify a tag name." msgstr "Трябва да укажете име на етикет." -#: builtin/fetch.c:1144 +#: builtin/fetch.c:1148 msgid "--depth and --unshallow cannot be used together" msgstr "опциите „--depth“ и „--unshallow“ са несъвместими" -#: builtin/fetch.c:1146 +#: builtin/fetch.c:1150 msgid "--unshallow on a complete repository does not make sense" msgstr "не можете да използвате опцията „--unshallow“ върху пълно хранилище" -#: builtin/fetch.c:1169 +#: builtin/fetch.c:1173 msgid "fetch --all does not take a repository argument" msgstr "към „git fetch --all“ не можете да добавите аргумент — хранилище" -#: builtin/fetch.c:1171 +#: builtin/fetch.c:1175 msgid "fetch --all does not make sense with refspecs" msgstr "" "към „git fetch --all“ не можете да добавите аргумент — указател на версия" -#: builtin/fetch.c:1182 +#: builtin/fetch.c:1186 #, c-format msgid "No such remote or remote group: %s" msgstr "Няма нито отдалечено хранилище, нито група от хранилища на име „%s“" -#: builtin/fetch.c:1190 +#: builtin/fetch.c:1194 msgid "Fetching a group and specifying refspecs does not make sense" msgstr "Указването на група и указването на версия са несъвместими" @@ -5712,76 +5782,76 @@ msgid "" msgstr "" "git fmt-merge-msg [-m СЪОБЩЕНИЕ] [--log[=БРОЙ] | --no-log] [--file ФАЙЛ]" -#: builtin/fmt-merge-msg.c:662 builtin/fmt-merge-msg.c:665 builtin/grep.c:698 +#: builtin/fmt-merge-msg.c:668 builtin/fmt-merge-msg.c:671 builtin/grep.c:698 #: builtin/merge.c:198 builtin/repack.c:178 builtin/repack.c:182 -#: builtin/show-branch.c:657 builtin/show-ref.c:178 builtin/tag.c:590 -#: parse-options.h:132 parse-options.h:239 +#: builtin/show-branch.c:664 builtin/show-ref.c:180 builtin/tag.c:590 +#: parse-options.h:131 parse-options.h:238 msgid "n" msgstr "БРОЙ" -#: builtin/fmt-merge-msg.c:663 +#: builtin/fmt-merge-msg.c:669 msgid "populate log with at most <n> entries from shortlog" msgstr "" "вмъкване на журнал състоящ се от не повече от БРОЙ записа от съкратения " "журнал" -#: builtin/fmt-merge-msg.c:666 +#: builtin/fmt-merge-msg.c:672 msgid "alias for --log (deprecated)" msgstr "синоним на „--log“ (остаряло)" -#: builtin/fmt-merge-msg.c:669 +#: builtin/fmt-merge-msg.c:675 msgid "text" msgstr "ТЕКСТ" -#: builtin/fmt-merge-msg.c:670 +#: builtin/fmt-merge-msg.c:676 msgid "use <text> as start of message" msgstr "за начало на съобщението да се ползва ТЕКСТ" -#: builtin/fmt-merge-msg.c:671 +#: builtin/fmt-merge-msg.c:677 msgid "file to read from" msgstr "файл, от който да се чете" -#: builtin/for-each-ref.c:675 +#: builtin/for-each-ref.c:687 msgid "unable to parse format" msgstr "форматът не може да бъде анализиран" -#: builtin/for-each-ref.c:1063 +#: builtin/for-each-ref.c:1083 msgid "git for-each-ref [<options>] [<pattern>]" msgstr "git for-each-ref [ОПЦИЯ…] [ШАБЛОН]" -#: builtin/for-each-ref.c:1078 +#: builtin/for-each-ref.c:1098 msgid "quote placeholders suitably for shells" msgstr "цитиране подходящо за командни интерпретатори на обвивката" -#: builtin/for-each-ref.c:1080 +#: builtin/for-each-ref.c:1100 msgid "quote placeholders suitably for perl" msgstr "цитиране подходящо за perl" -#: builtin/for-each-ref.c:1082 +#: builtin/for-each-ref.c:1102 msgid "quote placeholders suitably for python" msgstr "цитиране подходящо за python" -#: builtin/for-each-ref.c:1084 +#: builtin/for-each-ref.c:1104 msgid "quote placeholders suitably for Tcl" msgstr "цитиране подходящо за tcl" -#: builtin/for-each-ref.c:1087 +#: builtin/for-each-ref.c:1107 msgid "show only <n> matched refs" msgstr "извеждане само на този БРОЙ напаснати указатели" -#: builtin/for-each-ref.c:1088 builtin/replace.c:438 +#: builtin/for-each-ref.c:1108 builtin/replace.c:438 msgid "format" msgstr "ФОРМАТ" -#: builtin/for-each-ref.c:1088 +#: builtin/for-each-ref.c:1108 msgid "format to use for the output" msgstr "ФОРМАТ за изхода" -#: builtin/for-each-ref.c:1089 +#: builtin/for-each-ref.c:1109 msgid "key" msgstr "ключ" -#: builtin/for-each-ref.c:1090 +#: builtin/for-each-ref.c:1110 msgid "field name to sort on" msgstr "име на полето, по което да е подредбата" @@ -5789,56 +5859,56 @@ msgstr "име на полето, по което да е подредбата" msgid "Checking connectivity" msgstr "Проверка на връзката" -#: builtin/fsck.c:540 +#: builtin/fsck.c:548 msgid "Checking object directories" msgstr "Проверка на директориите с обекти" -#: builtin/fsck.c:603 +#: builtin/fsck.c:611 msgid "git fsck [<options>] [<object>...]" msgstr "git fsck [ОПЦИЯ…] [ОБЕКТ…]" -#: builtin/fsck.c:609 +#: builtin/fsck.c:617 msgid "show unreachable objects" msgstr "показване на недостижимите обекти" -#: builtin/fsck.c:610 +#: builtin/fsck.c:618 msgid "show dangling objects" msgstr "показване на обектите извън клоните" -#: builtin/fsck.c:611 +#: builtin/fsck.c:619 msgid "report tags" msgstr "показване на етикетите" -#: builtin/fsck.c:612 +#: builtin/fsck.c:620 msgid "report root nodes" msgstr "показване на кореновите възли" -#: builtin/fsck.c:613 +#: builtin/fsck.c:621 msgid "make index objects head nodes" msgstr "задаване на обекти от индекса да са коренови" # FIXME bad message -#: builtin/fsck.c:614 +#: builtin/fsck.c:622 msgid "make reflogs head nodes (default)" msgstr "проследяване на указателите от журнала като глави (стандартно)" -#: builtin/fsck.c:615 +#: builtin/fsck.c:623 msgid "also consider packs and alternate objects" msgstr "допълнително да се проверяват пакетите и алтернативните обекти" -#: builtin/fsck.c:616 +#: builtin/fsck.c:624 msgid "enable more strict checking" msgstr "по-строги проверки" -#: builtin/fsck.c:618 +#: builtin/fsck.c:626 msgid "write dangling objects in .git/lost-found" msgstr "запазване на обектите извън клоните в директорията „.git/lost-found“" -#: builtin/fsck.c:619 builtin/prune.c:108 +#: builtin/fsck.c:627 builtin/prune.c:107 msgid "show progress" msgstr "показване на напредъка" -#: builtin/fsck.c:669 +#: builtin/fsck.c:677 msgid "Checking objects" msgstr "Проверка на обектите" @@ -5847,53 +5917,54 @@ msgstr "Проверка на обектите" msgid "git gc [<options>]" msgstr "git gc [ОПЦИЯ…]" -#: builtin/gc.c:79 +#: builtin/gc.c:67 #, c-format -msgid "Invalid gc.pruneexpire: '%s'" -msgstr "Неправилна стойност за настройката „gc.pruneexpire“: %s" +msgid "Invalid %s: '%s'" +msgstr "Неправилен %s: „%s“" -#: builtin/gc.c:107 +#: builtin/gc.c:112 #, c-format msgid "insanely long object directory %.*s" msgstr "прекалено дълга директория с обекти „%.*s“" -#: builtin/gc.c:276 +#: builtin/gc.c:281 msgid "prune unreferenced objects" msgstr "окастряне на обектите, към които нищо не сочи" -#: builtin/gc.c:278 +#: builtin/gc.c:283 msgid "be more thorough (increased runtime)" msgstr "изчерпателно търсене на боклука (за сметка на повече време работа)" -#: builtin/gc.c:279 +#: builtin/gc.c:284 msgid "enable auto-gc mode" msgstr "включване на автоматичното събиране на боклука (auto-gc)" -#: builtin/gc.c:280 +#: builtin/gc.c:285 msgid "force running gc even if there may be another gc running" msgstr "" "изрично стартиране на събирането на боклука, дори и ако вече работи друго " "събиране" -#: builtin/gc.c:321 +#: builtin/gc.c:327 #, c-format msgid "Auto packing the repository in background for optimum performance.\n" msgstr "" "Автоматично пакетиране на заден фон на хранилището за по-добра " "производителност.\n" -#: builtin/gc.c:323 +#: builtin/gc.c:329 #, c-format msgid "Auto packing the repository for optimum performance.\n" msgstr "Автоматично пакетиране на хранилището за по-добра производителност.\n" -#: builtin/gc.c:324 +#: builtin/gc.c:330 #, c-format msgid "See \"git help gc\" for manual housekeeping.\n" msgstr "" -"Вижте ръководството за повече информация как да изпълните „git help gc“.\n" +"Погледнете ръководството за повече информация как да изпълните „git help " +"gc“.\n" -#: builtin/gc.c:342 +#: builtin/gc.c:348 #, c-format msgid "" "gc is already running on machine '%s' pid %<PRIuMAX> (use --force if not)" @@ -5902,7 +5973,7 @@ msgstr "" "процеса: %<PRIuMAX> (ако сте сигурни, че това не е вярно, това използвайте\n" "опцията „--force“)" -#: builtin/gc.c:364 +#: builtin/gc.c:376 msgid "" "There are too many unreachable loose objects; run 'git prune' to remove them." msgstr "" @@ -6118,7 +6189,7 @@ msgstr "" "позволяване на стартирането на grep(1) (текущият компилат пренебрегва тази " "опция)" -#: builtin/grep.c:741 builtin/show-ref.c:187 +#: builtin/grep.c:741 builtin/show-ref.c:189 msgid "show usage" msgstr "извеждане на начина на употреба на командата" @@ -6150,7 +6221,7 @@ msgstr "" msgid "both --cached and trees are given." msgstr "опцията „--cached“ е несъвместима със задаване на дърво." -#: builtin/hash-object.c:82 +#: builtin/hash-object.c:80 msgid "" "git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] " "[--] <file>..." @@ -6159,38 +6230,38 @@ msgstr "" "ФАЙЛ…" # FIXME - list of paths or path... -#: builtin/hash-object.c:83 +#: builtin/hash-object.c:81 msgid "git hash-object --stdin-paths < <list-of-paths>" msgstr "git hash-object --stdin-paths < ПЪТ…" -#: builtin/hash-object.c:94 builtin/tag.c:612 +#: builtin/hash-object.c:92 builtin/tag.c:612 msgid "type" msgstr "ВИД" -#: builtin/hash-object.c:94 +#: builtin/hash-object.c:92 msgid "object type" msgstr "вид на обекта" -#: builtin/hash-object.c:95 +#: builtin/hash-object.c:93 msgid "write the object into the object database" msgstr "записване на обекта в базата от данни за обектите" -#: builtin/hash-object.c:97 +#: builtin/hash-object.c:95 msgid "read the object from stdin" msgstr "изчитане на обекта от стандартния вход" -#: builtin/hash-object.c:99 +#: builtin/hash-object.c:97 msgid "store file as is without filters" msgstr "запазване на файла както е — без филтри" -#: builtin/hash-object.c:100 +#: builtin/hash-object.c:98 msgid "" "just hash any random garbage to create corrupt objects for debugging Git" msgstr "" "създаване и хеширане на произволни данни за повредени обекти за трасиране на " "Git" -#: builtin/hash-object.c:101 +#: builtin/hash-object.c:99 msgid "process file as it were from this path" msgstr "обработване на файла все едно е с този път" @@ -6321,281 +6392,281 @@ msgid "`git %s' is aliased to `%s'" msgstr "„git %s“ е синоним на „%s“" # FIXME merge with next? -#: builtin/index-pack.c:150 +#: builtin/index-pack.c:151 #, c-format msgid "unable to open %s" msgstr "обектът „%s“ не може да бъде отворен" -#: builtin/index-pack.c:200 +#: builtin/index-pack.c:201 #, c-format msgid "object type mismatch at %s" msgstr "неправилен вид на обекта „%s“" -#: builtin/index-pack.c:220 +#: builtin/index-pack.c:221 #, c-format msgid "did not receive expected object %s" msgstr "очакваният обект „%s“ не бе получен" -#: builtin/index-pack.c:223 +#: builtin/index-pack.c:224 #, c-format msgid "object %s: expected type %s, found %s" msgstr "неправилен вид на обекта „%s“: очакваше се „%s“, а бе получен „%s“" -#: builtin/index-pack.c:265 +#: builtin/index-pack.c:266 #, c-format msgid "cannot fill %d byte" msgid_plural "cannot fill %d bytes" msgstr[0] "не може да се запълни %d байт" msgstr[1] "не може да се запълнят %d байта" -#: builtin/index-pack.c:275 +#: builtin/index-pack.c:276 msgid "early EOF" msgstr "неочакван край на файл" -#: builtin/index-pack.c:276 +#: builtin/index-pack.c:277 msgid "read error on input" msgstr "грешка при четене на входните данни" -#: builtin/index-pack.c:288 +#: builtin/index-pack.c:289 msgid "used more bytes than were available" msgstr "използвани са повече от наличните байтове" -#: builtin/index-pack.c:295 +#: builtin/index-pack.c:296 msgid "pack too large for current definition of off_t" msgstr "пакетният файл е прекалено голям за текущата стойност на типа „off_t“" -#: builtin/index-pack.c:311 +#: builtin/index-pack.c:312 #, c-format msgid "unable to create '%s'" msgstr "пакетният файл „%s“ не може да бъде създаден" -#: builtin/index-pack.c:316 +#: builtin/index-pack.c:317 #, c-format msgid "cannot open packfile '%s'" msgstr "пакетният файл „%s“ не може да бъде отворен" -#: builtin/index-pack.c:330 +#: builtin/index-pack.c:331 msgid "pack signature mismatch" msgstr "несъответствие в подписа към пакетния файл" -#: builtin/index-pack.c:332 +#: builtin/index-pack.c:333 #, c-format msgid "pack version %<PRIu32> unsupported" msgstr "не се поддържа пакетиране вeрсия „%<PRIu32>“" -#: builtin/index-pack.c:350 +#: builtin/index-pack.c:351 #, c-format msgid "pack has bad object at offset %lu: %s" msgstr "повреден обект в пакетния файл при отместване %lu: %s" -#: builtin/index-pack.c:471 +#: builtin/index-pack.c:472 #, c-format msgid "inflate returned %d" msgstr "декомпресирането с „inflate“ върна %d" -#: builtin/index-pack.c:520 +#: builtin/index-pack.c:521 msgid "offset value overflow for delta base object" msgstr "стойността на отместването за обекта-разлика води до препълване" -#: builtin/index-pack.c:528 +#: builtin/index-pack.c:529 msgid "delta base offset is out of bound" msgstr "стойността на отместването за обекта-разлика е извън диапазона" -#: builtin/index-pack.c:536 +#: builtin/index-pack.c:537 #, c-format msgid "unknown object type %d" msgstr "непознат вид обект %d" -#: builtin/index-pack.c:567 +#: builtin/index-pack.c:568 msgid "cannot pread pack file" msgstr "пакетният файл не може да бъде прочетен" -#: builtin/index-pack.c:569 +#: builtin/index-pack.c:570 #, c-format msgid "premature end of pack file, %lu byte missing" msgid_plural "premature end of pack file, %lu bytes missing" msgstr[0] "неочакван край на файл, липсва %lu байт" msgstr[1] "неочакван край на файл, липсват %lu байта" -#: builtin/index-pack.c:595 +#: builtin/index-pack.c:596 msgid "serious inflate inconsistency" msgstr "сериозна грешка при декомпресиране с „inflate“" -#: builtin/index-pack.c:686 builtin/index-pack.c:692 builtin/index-pack.c:715 -#: builtin/index-pack.c:749 builtin/index-pack.c:758 +#: builtin/index-pack.c:742 builtin/index-pack.c:748 builtin/index-pack.c:771 +#: builtin/index-pack.c:805 builtin/index-pack.c:814 #, c-format msgid "SHA1 COLLISION FOUND WITH %s !" msgstr "" "СЪВПАДЕНИЕ НА СТОЙНОСТИТЕ ЗА СУМИТЕ ЗА SHA1: „%s“ НА ДВА РАЗЛИЧНИ ОБЕКТА!" # FIXME merge with next? -#: builtin/index-pack.c:689 builtin/pack-objects.c:162 +#: builtin/index-pack.c:745 builtin/pack-objects.c:162 #: builtin/pack-objects.c:254 #, c-format msgid "unable to read %s" msgstr "обектът „%s“ не може да бъде прочетен" -#: builtin/index-pack.c:755 +#: builtin/index-pack.c:811 #, c-format msgid "cannot read existing object %s" msgstr "съществуващият обект „%s“ не може да бъде прочетен" -#: builtin/index-pack.c:769 +#: builtin/index-pack.c:825 #, c-format msgid "invalid blob object %s" msgstr "неправилен обект BLOB „%s“" # FIXME perhaps invalid object -#: builtin/index-pack.c:783 +#: builtin/index-pack.c:839 #, c-format msgid "invalid %s" msgstr "неправилен обект „%s“" -#: builtin/index-pack.c:787 +#: builtin/index-pack.c:843 msgid "Error in object" msgstr "Грешка в обекта" -#: builtin/index-pack.c:789 +#: builtin/index-pack.c:845 #, c-format msgid "Not all child objects of %s are reachable" msgstr "Някои обекти, наследници на „%s“, не могат да бъдат достигнати" -#: builtin/index-pack.c:861 builtin/index-pack.c:890 +#: builtin/index-pack.c:917 builtin/index-pack.c:948 msgid "failed to apply delta" msgstr "разликата не може да бъде приложена" -#: builtin/index-pack.c:1055 +#: builtin/index-pack.c:1118 msgid "Receiving objects" msgstr "Получаване на обекти" -#: builtin/index-pack.c:1055 +#: builtin/index-pack.c:1118 msgid "Indexing objects" msgstr "Индексиране на обекти" -#: builtin/index-pack.c:1081 +#: builtin/index-pack.c:1150 msgid "pack is corrupted (SHA1 mismatch)" msgstr "пакетният файл е повреден (нееднакви суми по SHA1)" -#: builtin/index-pack.c:1086 +#: builtin/index-pack.c:1155 msgid "cannot fstat packfile" msgstr "не може да се получи информация за пакетния файл с „fstat“" -#: builtin/index-pack.c:1089 +#: builtin/index-pack.c:1158 msgid "pack has junk at the end" msgstr "в края на пакетния файл има повредени данни" # FIXME WTF message -#: builtin/index-pack.c:1100 +#: builtin/index-pack.c:1169 msgid "confusion beyond insanity in parse_pack_objects()" msgstr "" "фатална грешка във функцията „parse_pack_objects“. Това е грешка в Git, " "докладвайте я на разработчиците, като пратите е-писмо на адрес: „git@vger." "kernel.org“." -#: builtin/index-pack.c:1123 +#: builtin/index-pack.c:1194 msgid "Resolving deltas" msgstr "Откриване на съответните разлики" -#: builtin/index-pack.c:1133 +#: builtin/index-pack.c:1205 #, c-format msgid "unable to create thread: %s" msgstr "не може да се създаде нишка: %s" # FIXME WTF message -#: builtin/index-pack.c:1175 +#: builtin/index-pack.c:1247 msgid "confusion beyond insanity" msgstr "" "фатална грешка във функцията „conclude_pack“. Това е грешка в Git, " "докладвайте я на разработчиците, като пратите е-писмо на адрес: „git@vger." "kernel.org“." -#: builtin/index-pack.c:1181 +#: builtin/index-pack.c:1253 #, c-format msgid "completed with %d local objects" msgstr "действието завърши с %d локални обекта" -#: builtin/index-pack.c:1191 +#: builtin/index-pack.c:1263 #, c-format msgid "Unexpected tail checksum for %s (disk corruption?)" msgstr "" "Неочаквана последваща сума за грешки за „%s“ (причината може да е грешка в " "диска)" -#: builtin/index-pack.c:1195 +#: builtin/index-pack.c:1267 #, c-format msgid "pack has %d unresolved delta" msgid_plural "pack has %d unresolved deltas" msgstr[0] "в пакета има %d ненапасваща разлика" msgstr[1] "в пакета има %d ненапасващи разлики" -#: builtin/index-pack.c:1219 +#: builtin/index-pack.c:1291 #, c-format msgid "unable to deflate appended object (%d)" msgstr "добавеният обект не може да се компресира с „deflate“: %d" -#: builtin/index-pack.c:1298 +#: builtin/index-pack.c:1367 #, c-format msgid "local object %s is corrupt" msgstr "локалният обект „%s“ е повреден" -#: builtin/index-pack.c:1322 +#: builtin/index-pack.c:1391 msgid "error while closing pack file" msgstr "грешка при затварянето на пакетния файл" -#: builtin/index-pack.c:1335 +#: builtin/index-pack.c:1404 #, c-format msgid "cannot write keep file '%s'" msgstr "" "грешка при записването на файла „%s“, осигуряващ запазване на директория" -#: builtin/index-pack.c:1343 +#: builtin/index-pack.c:1412 #, c-format msgid "cannot close written keep file '%s'" msgstr "" "грешка при затварянето на записания файл „%s“, осигуряващ запазване на " "директория" -#: builtin/index-pack.c:1356 +#: builtin/index-pack.c:1425 msgid "cannot store pack file" msgstr "пакетният файл не може да бъде запазен" -#: builtin/index-pack.c:1367 +#: builtin/index-pack.c:1436 msgid "cannot store index file" msgstr "файлът за индекса не може да бъде съхранен" -#: builtin/index-pack.c:1400 +#: builtin/index-pack.c:1469 #, c-format msgid "bad pack.indexversion=%<PRIu32>" msgstr "зададена е неправилна версия пакетиране: „pack.indexversion=%<PRIu32>“" -#: builtin/index-pack.c:1406 +#: builtin/index-pack.c:1475 #, c-format msgid "invalid number of threads specified (%d)" msgstr "зададен е неправилен брой нишки: %d" -#: builtin/index-pack.c:1410 builtin/index-pack.c:1589 +#: builtin/index-pack.c:1479 builtin/index-pack.c:1658 #, c-format msgid "no threads support, ignoring %s" msgstr "липсва поддръжка за нишки. „%s“ ще се пренебрегне" -#: builtin/index-pack.c:1468 +#: builtin/index-pack.c:1537 #, c-format msgid "Cannot open existing pack file '%s'" msgstr "Съществуващият пакетен файл „%s“ не може да бъде отворен" -#: builtin/index-pack.c:1470 +#: builtin/index-pack.c:1539 #, c-format msgid "Cannot open existing pack idx file for '%s'" msgstr "Съществуващият индекс за пакетния файл „%s“ не може да бъде отворен" -#: builtin/index-pack.c:1517 +#: builtin/index-pack.c:1586 #, c-format msgid "non delta: %d object" msgid_plural "non delta: %d objects" msgstr[0] "%d обект не е разлика" msgstr[1] "%d обекта не са разлика" -#: builtin/index-pack.c:1524 +#: builtin/index-pack.c:1593 #, c-format msgid "chain length = %d: %lu object" msgid_plural "chain length = %d: %lu objects" @@ -6603,26 +6674,26 @@ msgstr[0] "дължината на веригата е %d: %lu обект" msgstr[1] "дължината на веригата е %d: %lu обекта" # FIXME it is not the cwd it is the previous cwd -#: builtin/index-pack.c:1553 +#: builtin/index-pack.c:1622 msgid "Cannot come back to cwd" msgstr "Процесът не може да се върне към предишната работна директория" -#: builtin/index-pack.c:1601 builtin/index-pack.c:1604 -#: builtin/index-pack.c:1616 builtin/index-pack.c:1620 +#: builtin/index-pack.c:1670 builtin/index-pack.c:1673 +#: builtin/index-pack.c:1685 builtin/index-pack.c:1689 #, c-format msgid "bad %s" msgstr "неправилна стойност „%s“" -#: builtin/index-pack.c:1634 +#: builtin/index-pack.c:1703 msgid "--fix-thin cannot be used without --stdin" msgstr "опцията „--fix-thin“ изисква „--stdin“" -#: builtin/index-pack.c:1638 builtin/index-pack.c:1647 +#: builtin/index-pack.c:1707 builtin/index-pack.c:1716 #, c-format msgid "packfile name '%s' does not end with '.pack'" msgstr "името на пакетния файл „%s“ не завършва на „.pack“" -#: builtin/index-pack.c:1655 +#: builtin/index-pack.c:1724 msgid "--verify with no packfile name given" msgstr "опцията „--verify“ изисква име на пакетен файл" @@ -6695,22 +6766,22 @@ msgid "not copying templates of a wrong format version %d from '%s'" msgstr "" "шаблоните с неправилен номер на формата %d няма да бъдат копирани от „%s“" -#: builtin/init-db.c:197 +#: builtin/init-db.c:211 #, c-format msgid "insane git directory %s" msgstr "твърде дълго име на директория на Git: „%s“" -#: builtin/init-db.c:331 builtin/init-db.c:334 +#: builtin/init-db.c:343 builtin/init-db.c:346 #, c-format msgid "%s already exists" msgstr "Директорията „%s“ вече съществува" -#: builtin/init-db.c:363 +#: builtin/init-db.c:374 #, c-format msgid "unable to handle file type %d" msgstr "файлове от вид %d не се поддържат" -#: builtin/init-db.c:366 +#: builtin/init-db.c:377 #, c-format msgid "unable to move %s to %s" msgstr "„%s“ не може да се премести в „%s“" @@ -6718,24 +6789,24 @@ msgstr "„%s“ не може да се премести в „%s“" #. TRANSLATORS: The first '%s' is either "Reinitialized #. existing" or "Initialized empty", the second " shared" or #. "", and the last '%s%s' is the verbatim directory name. -#: builtin/init-db.c:426 +#: builtin/init-db.c:433 #, c-format msgid "%s%s Git repository in %s%s\n" msgstr "%s%s хранилище на Git в „%s%s“\n" -#: builtin/init-db.c:427 +#: builtin/init-db.c:434 msgid "Reinitialized existing" msgstr "Наново инициализирано, съществуващо" -#: builtin/init-db.c:427 +#: builtin/init-db.c:434 msgid "Initialized empty" msgstr "Инициализирано празно" -#: builtin/init-db.c:428 +#: builtin/init-db.c:435 msgid " shared" msgstr ", споделено" -#: builtin/init-db.c:475 +#: builtin/init-db.c:482 msgid "" "git init [-q | --quiet] [--bare] [--template=<template-directory>] [--" "shared[=<permissions>]] [<directory>]" @@ -6743,31 +6814,31 @@ msgstr "" "git init [-q | --quiet] [--bare] [--template=ДИРЕКТОРИЯ_С_ШАБЛОНИ] [--" "shared[=ПРАВА]] [ДИРЕКТОРИЯ]" -#: builtin/init-db.c:498 +#: builtin/init-db.c:505 msgid "permissions" msgstr "права" -#: builtin/init-db.c:499 +#: builtin/init-db.c:506 msgid "specify that the git repository is to be shared amongst several users" msgstr "" "указване, че хранилището на Git ще бъде споделено от повече от един " "потребител" -#: builtin/init-db.c:501 builtin/prune-packed.c:57 builtin/repack.c:171 +#: builtin/init-db.c:508 builtin/prune-packed.c:57 builtin/repack.c:171 msgid "be quiet" msgstr "без извеждане на информация" -#: builtin/init-db.c:533 builtin/init-db.c:538 +#: builtin/init-db.c:540 builtin/init-db.c:545 #, c-format msgid "cannot mkdir %s" msgstr "директорията „%s“ не може да бъде създадена" -#: builtin/init-db.c:542 +#: builtin/init-db.c:549 #, c-format msgid "cannot chdir to %s" msgstr "не може да се влезе в директорията „%s“" -#: builtin/init-db.c:563 +#: builtin/init-db.c:570 #, c-format msgid "" "%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-" @@ -6776,7 +6847,7 @@ msgstr "" "%s (или --work-tree=ДИРЕКТОРИЯ) изисква указването на %s (или --git-" "dir=ДИРЕКТОРИЯ)" -#: builtin/init-db.c:591 +#: builtin/init-db.c:598 #, c-format msgid "Cannot access work tree '%s'" msgstr "Работното дърво в „%s“ е недостъпно" @@ -6802,7 +6873,7 @@ msgid "trailer(s) to add" msgstr "епилог(зи) за добавяне" #: builtin/log.c:41 -msgid "git log [<options>] [<revision range>] [[--] <path>...]" +msgid "git log [<options>] [<revision-range>] [[--] <path>...]" msgstr "git log [ОПЦИЯ…] [ДИАПАЗОН_НА_ВЕРСИИТЕ] [[--] ПЪТ…]" #: builtin/log.c:42 @@ -6875,7 +6946,7 @@ msgstr "Файлът-кръпка „%s“ не може да бъде отво msgid "Need exactly one range." msgstr "Трябва да зададете точно един диапазон." -#: builtin/log.c:811 +#: builtin/log.c:813 msgid "Not a range." msgstr "Не е диапазон." @@ -7085,7 +7156,7 @@ msgstr "Изходните файлове не могат да бъдат съз msgid "git cherry [-v] [<upstream> [<head> [<limit>]]]" msgstr "git cherry [-v] [ОТДАЛЕЧЕН_КЛОН [ВРЪХ [ПРЕДЕЛ]]]" -#: builtin/log.c:1638 +#: builtin/log.c:1637 #, c-format msgid "" "Could not find a tracked remote branch, please specify <upstream> manually.\n" @@ -7093,7 +7164,7 @@ msgstr "" "Следеният отдалечен клон не бе открит, затова изрично задайте " "ОТДАЛЕЧЕН_КЛОН.\n" -#: builtin/log.c:1651 builtin/log.c:1653 builtin/log.c:1665 +#: builtin/log.c:1648 builtin/log.c:1650 builtin/log.c:1662 #, c-format msgid "Unknown commit %s" msgstr "Непознато подаване „%s“" @@ -7374,42 +7445,42 @@ msgstr "" msgid "'%s' does not point to a commit" msgstr "„%s“ не сочи към подаване" -#: builtin/merge.c:559 +#: builtin/merge.c:537 #, c-format msgid "Bad branch.%s.mergeoptions string: %s" msgstr "Неправилен низ за настройката „branch.%s.mergeoptions“: „%s“" -#: builtin/merge.c:654 +#: builtin/merge.c:632 msgid "git write-tree failed to write a tree" msgstr "Командата „git write-tree“ не успя да запише обект-дърво" -#: builtin/merge.c:678 +#: builtin/merge.c:656 msgid "Not handling anything other than two heads merge." msgstr "Поддържа се само сливане на точно две истории." -#: builtin/merge.c:692 +#: builtin/merge.c:670 #, c-format msgid "Unknown option for merge-recursive: -X%s" msgstr "Непозната опция за рекурсивното сливане „merge-recursive“: „-X%s“" -#: builtin/merge.c:705 +#: builtin/merge.c:683 #, c-format msgid "unable to write %s" msgstr "„%s“ не може да бъде записан" -#: builtin/merge.c:794 +#: builtin/merge.c:772 #, c-format msgid "Could not read from '%s'" msgstr "От „%s“ не може да се чете" -#: builtin/merge.c:803 +#: builtin/merge.c:781 #, c-format msgid "Not committing merge; use 'git commit' to complete the merge.\n" msgstr "" "Сливането няма да бъде подадено. За завършването му и подаването му " "използвайте командата „git commit“.\n" -#: builtin/merge.c:809 +#: builtin/merge.c:787 #, c-format msgid "" "Please enter a commit message to explain why this merge is necessary,\n" @@ -7424,50 +7495,55 @@ msgstr "" "Редовете, които започват с „%c“ ще бъдат пропуснати, а празно съобщение\n" "преустановява подаването.\n" -#: builtin/merge.c:833 +#: builtin/merge.c:811 msgid "Empty commit message." msgstr "Празно съобщение при подаване." # FIXME - WTF is wonderful. -#: builtin/merge.c:845 +#: builtin/merge.c:823 #, c-format msgid "Wonderful.\n" msgstr "Първият етап на тривиалното сливане завърши.\n" -#: builtin/merge.c:900 +#: builtin/merge.c:878 #, c-format msgid "Automatic merge failed; fix conflicts and then commit the result.\n" msgstr "Неуспешно сливане — коригирайте конфликтите и подайте резултата.\n" -#: builtin/merge.c:916 +#: builtin/merge.c:894 #, c-format msgid "'%s' is not a commit" msgstr "„%s“ не е подаване" -#: builtin/merge.c:957 +#: builtin/merge.c:935 msgid "No current branch." msgstr "Няма текущ клон." -#: builtin/merge.c:959 +#: builtin/merge.c:937 msgid "No remote for the current branch." msgstr "Текущият клон не следи никой." -#: builtin/merge.c:961 +#: builtin/merge.c:939 msgid "No default upstream defined for the current branch." msgstr "Текущият клон не следи никой клон." -#: builtin/merge.c:966 +#: builtin/merge.c:944 #, c-format msgid "No remote-tracking branch for %s from %s" msgstr "Никой клон не следи клона „%s“ от хранилището „%s“" -#: builtin/merge.c:1122 +#: builtin/merge.c:1081 +#, c-format +msgid "could not close '%s'" +msgstr "„%s“ не може да се затвори" + +#: builtin/merge.c:1208 msgid "There is no merge to abort (MERGE_HEAD missing)." msgstr "" "Не може да преустановите сливане, защото в момента не се извършва такова " "(липсва указател „MERGE_HEAD“)." -#: builtin/merge.c:1138 +#: builtin/merge.c:1224 msgid "" "You have not concluded your merge (MERGE_HEAD exists).\n" "Please, commit your changes before you merge." @@ -7475,11 +7551,11 @@ msgstr "" "Не сте завършили сливане. (Указателят „MERGE_HEAD“ съществува).\n" "Подайте промените си, преди да започнете ново сливане." -#: builtin/merge.c:1141 git-pull.sh:34 +#: builtin/merge.c:1227 git-pull.sh:74 msgid "You have not concluded your merge (MERGE_HEAD exists)." msgstr "Не сте завършили сливане. (Указателят „MERGE_HEAD“ съществува)." -#: builtin/merge.c:1145 +#: builtin/merge.c:1231 msgid "" "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n" "Please, commit your changes before you merge." @@ -7487,111 +7563,111 @@ msgstr "" "Не сте завършили отбиране на подаване (указателят „CHERRY_PICK_HEAD“\n" "съществува). Подайте промените си, преди да започнете ново сливане." -#: builtin/merge.c:1148 +#: builtin/merge.c:1234 msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)." msgstr "" "Не сте завършили отбиране на подаване (указателят „CHERRY_PICK_HEAD“\n" "съществува)." -#: builtin/merge.c:1157 +#: builtin/merge.c:1243 msgid "You cannot combine --squash with --no-ff." msgstr "Опцията „--squash“ е несъвместима с „--no-ff“." -#: builtin/merge.c:1166 +#: builtin/merge.c:1251 msgid "No commit specified and merge.defaultToUpstream not set." msgstr "" "Не е указано подаване и настройката „merge.defaultToUpstream“ не е зададена." -#: builtin/merge.c:1198 -msgid "Can merge only exactly one commit into empty head" -msgstr "Можете да слеете точно едно подаване във връх без история" - -#: builtin/merge.c:1201 +#: builtin/merge.c:1268 msgid "Squash commit into empty head not supported yet" msgstr "Подаване със смачкване във връх без история все още не се поддържа" -#: builtin/merge.c:1203 +#: builtin/merge.c:1270 msgid "Non-fast-forward commit does not make sense into an empty head" msgstr "" "Понеже върхът е без история, всички сливания са тривиални, не може да се " "извърши нетривиално сливане изисквано от опцията „--no-ff“" -#: builtin/merge.c:1208 +#: builtin/merge.c:1276 #, c-format msgid "%s - not something we can merge" msgstr "„%s“ — не е нещо, което може да се слее" -#: builtin/merge.c:1259 +#: builtin/merge.c:1278 +msgid "Can merge only exactly one commit into empty head" +msgstr "Можете да слеете точно едно подаване във връх без история" + +#: builtin/merge.c:1333 #, c-format msgid "Commit %s has an untrusted GPG signature, allegedly by %s." msgstr "" "Подаването „%s“ е с недоверен подпис от GPG, който твърди, че е на „%s“." -#: builtin/merge.c:1262 +#: builtin/merge.c:1336 #, c-format msgid "Commit %s has a bad GPG signature allegedly by %s." msgstr "" "Подаването „%s“ е с неправилен подпис от GPG, който твърди, че е на „%s“." -#: builtin/merge.c:1265 +#: builtin/merge.c:1339 #, c-format msgid "Commit %s does not have a GPG signature." msgstr "Подаването „%s“ е без подпис от GPG." -#: builtin/merge.c:1268 +#: builtin/merge.c:1342 #, c-format msgid "Commit %s has a good GPG signature by %s\n" msgstr "Подаването „%s“ е с коректен подпис от GPG на „%s“.\n" -#: builtin/merge.c:1349 +#: builtin/merge.c:1423 #, c-format msgid "Updating %s..%s\n" msgstr "Обновяване „%s..%s“\n" -#: builtin/merge.c:1388 +#: builtin/merge.c:1462 #, c-format msgid "Trying really trivial in-index merge...\n" msgstr "Проба с най-тривиалното сливане в рамките на индекса…\n" # FIXME WTF message -#: builtin/merge.c:1395 +#: builtin/merge.c:1469 #, c-format msgid "Nope.\n" msgstr "Неуспешно сливане.\n" -#: builtin/merge.c:1427 +#: builtin/merge.c:1501 msgid "Not possible to fast-forward, aborting." msgstr "" "Не може да се извърши тривиално сливане, преустановяване на действието." -#: builtin/merge.c:1450 builtin/merge.c:1529 +#: builtin/merge.c:1524 builtin/merge.c:1603 #, c-format msgid "Rewinding the tree to pristine...\n" msgstr "Привеждане на дървото към първоначалното…\n" -#: builtin/merge.c:1454 +#: builtin/merge.c:1528 #, c-format msgid "Trying merge strategy %s...\n" msgstr "Пробване със стратегията за сливане „%s“…\n" -#: builtin/merge.c:1520 +#: builtin/merge.c:1594 #, c-format msgid "No merge strategy handled the merge.\n" msgstr "Никоя стратегия за сливане не може да извърши сливането.\n" -#: builtin/merge.c:1522 +#: builtin/merge.c:1596 #, c-format msgid "Merge with strategy %s failed.\n" msgstr "Неуспешно сливане със стратегия „%s“.\n" -#: builtin/merge.c:1531 +#: builtin/merge.c:1605 #, c-format msgid "Using the %s to prepare resolving by hand.\n" msgstr "" "Ползва се стратегията „%s“, която ще подготви дървото за коригиране на " "ръка.\n" -#: builtin/merge.c:1543 +#: builtin/merge.c:1617 #, c-format msgid "Automatic merge went well; stopped before committing as requested\n" msgstr "" @@ -7786,7 +7862,7 @@ msgstr "%s, обект: „%s“, цел: „%s“" msgid "Renaming %s to %s\n" msgstr "Преименуване на „%s“ на „%s“\n" -#: builtin/mv.c:256 builtin/remote.c:728 builtin/repack.c:359 +#: builtin/mv.c:256 builtin/remote.c:725 builtin/repack.c:361 #, c-format msgid "renaming '%s' failed" msgstr "неуспешно преименуване на „%s“" @@ -8173,7 +8249,7 @@ msgstr "УКАЗАТЕЛ_ЗА_БЕЛЕЖКА" msgid "use notes from <notes-ref>" msgstr "да се използва бележката сочена от този УКАЗАТЕЛ_ЗА_БЕЛЕЖКА" -#: builtin/notes.c:989 builtin/remote.c:1621 +#: builtin/notes.c:989 builtin/remote.c:1618 #, c-format msgid "Unknown subcommand: %s" msgstr "Непозната подкоманда: %s" @@ -8199,187 +8275,187 @@ msgstr "грешка при декомпресиране с „deflate“ (%d)" msgid "Writing objects" msgstr "Записване на обектите" -#: builtin/pack-objects.c:1013 +#: builtin/pack-objects.c:1011 msgid "disabling bitmap writing, as some objects are not being packed" msgstr "" "изключване на записването на битовата маска, защото някои обекти няма да се " "пакетират" -#: builtin/pack-objects.c:2173 +#: builtin/pack-objects.c:2171 msgid "Compressing objects" msgstr "Компресиране на обектите" -#: builtin/pack-objects.c:2570 +#: builtin/pack-objects.c:2568 #, c-format msgid "unsupported index version %s" msgstr "неподдържана версия на индекса „%s“" -#: builtin/pack-objects.c:2574 +#: builtin/pack-objects.c:2572 #, c-format msgid "bad index version '%s'" msgstr "неправилна версия на индекса „%s“" -#: builtin/pack-objects.c:2597 +#: builtin/pack-objects.c:2595 #, c-format msgid "option %s does not accept negative form" msgstr "опцията „%s“ не притежава отрицателна версия" -#: builtin/pack-objects.c:2601 +#: builtin/pack-objects.c:2599 #, c-format msgid "unable to parse value '%s' for option %s" msgstr "неразпозната стойност „%s“ за опцията „%s“" -#: builtin/pack-objects.c:2621 +#: builtin/pack-objects.c:2619 msgid "do not show progress meter" msgstr "без извеждане на напредъка" -#: builtin/pack-objects.c:2623 +#: builtin/pack-objects.c:2621 msgid "show progress meter" msgstr "извеждане на напредъка" -#: builtin/pack-objects.c:2625 +#: builtin/pack-objects.c:2623 msgid "show progress meter during object writing phase" msgstr "извеждане на напредъка във фазата на запазване на обектите" -#: builtin/pack-objects.c:2628 +#: builtin/pack-objects.c:2626 msgid "similar to --all-progress when progress meter is shown" msgstr "" "същото действие като опцията „--all-progress“ при извеждането на напредъка" -#: builtin/pack-objects.c:2629 +#: builtin/pack-objects.c:2627 msgid "version[,offset]" msgstr "ВЕРСИЯ[,ОТМЕСТВАНЕ]" -#: builtin/pack-objects.c:2630 +#: builtin/pack-objects.c:2628 msgid "write the pack index file in the specified idx format version" msgstr "" "запазване на индекса на пакетните файлове във форма̀та с указаната версия" -#: builtin/pack-objects.c:2633 +#: builtin/pack-objects.c:2631 msgid "maximum size of each output pack file" msgstr "максимален размер на всеки пакетен файл" -#: builtin/pack-objects.c:2635 +#: builtin/pack-objects.c:2633 msgid "ignore borrowed objects from alternate object store" msgstr "игнориране на обектите заети от други хранилища на обекти" -#: builtin/pack-objects.c:2637 +#: builtin/pack-objects.c:2635 msgid "ignore packed objects" msgstr "игнориране на пакетираните обекти" -#: builtin/pack-objects.c:2639 +#: builtin/pack-objects.c:2637 msgid "limit pack window by objects" msgstr "ограничаване на прозореца за пакетиране по брой обекти" -#: builtin/pack-objects.c:2641 +#: builtin/pack-objects.c:2639 msgid "limit pack window by memory in addition to object limit" msgstr "" "ограничаване на прозореца за пакетиране и по памет освен по брой обекти" -#: builtin/pack-objects.c:2643 +#: builtin/pack-objects.c:2641 msgid "maximum length of delta chain allowed in the resulting pack" msgstr "" "максимална дължина на веригата от разлики, която е позволена в пакетния файл" -#: builtin/pack-objects.c:2645 +#: builtin/pack-objects.c:2643 msgid "reuse existing deltas" msgstr "преизползване на съществуващите разлики" -#: builtin/pack-objects.c:2647 +#: builtin/pack-objects.c:2645 msgid "reuse existing objects" msgstr "преизползване на съществуващите обекти" -#: builtin/pack-objects.c:2649 +#: builtin/pack-objects.c:2647 msgid "use OFS_DELTA objects" msgstr "използване на обекти „OFS_DELTA“" -#: builtin/pack-objects.c:2651 +#: builtin/pack-objects.c:2649 msgid "use threads when searching for best delta matches" msgstr "" "стартиране на нишки за претърсване на най-добрите съвпадения на разликите" -#: builtin/pack-objects.c:2653 +#: builtin/pack-objects.c:2651 msgid "do not create an empty pack output" msgstr "без създаване на празен пакетен файл" -#: builtin/pack-objects.c:2655 +#: builtin/pack-objects.c:2653 msgid "read revision arguments from standard input" msgstr "изчитане на версиите от стандартния вход" -#: builtin/pack-objects.c:2657 +#: builtin/pack-objects.c:2655 msgid "limit the objects to those that are not yet packed" msgstr "ограничаване до все още непакетираните обекти" -#: builtin/pack-objects.c:2660 +#: builtin/pack-objects.c:2658 msgid "include objects reachable from any reference" msgstr "" "включване на всички обекти, които могат да се достигнат от произволен " "указател" -#: builtin/pack-objects.c:2663 +#: builtin/pack-objects.c:2661 msgid "include objects referred by reflog entries" msgstr "включване и на обектите сочени от записите в журнала на указателите" -#: builtin/pack-objects.c:2666 +#: builtin/pack-objects.c:2664 msgid "include objects referred to by the index" msgstr "включване и на обектите сочени от индекса" -#: builtin/pack-objects.c:2669 +#: builtin/pack-objects.c:2667 msgid "output pack to stdout" msgstr "извеждане на пакета на стандартния изход" -#: builtin/pack-objects.c:2671 +#: builtin/pack-objects.c:2669 msgid "include tag objects that refer to objects to be packed" msgstr "" "включване и на обектите-етикети, които сочат към обектите, които ще бъдат " "пакетирани" -#: builtin/pack-objects.c:2673 +#: builtin/pack-objects.c:2671 msgid "keep unreachable objects" msgstr "запазване на недостижимите обекти" -#: builtin/pack-objects.c:2674 parse-options.h:140 +#: builtin/pack-objects.c:2672 parse-options.h:139 msgid "time" msgstr "ВРЕМЕ" -#: builtin/pack-objects.c:2675 +#: builtin/pack-objects.c:2673 msgid "unpack unreachable objects newer than <time>" msgstr "разпакетиране на недостижимите обекти, които са по-нови от това ВРЕМЕ" -#: builtin/pack-objects.c:2678 +#: builtin/pack-objects.c:2676 msgid "create thin packs" msgstr "създаване на съкратени пакети" -#: builtin/pack-objects.c:2680 +#: builtin/pack-objects.c:2678 msgid "create packs suitable for shallow fetches" msgstr "пакетиране подходящо за плитко доставяне" -#: builtin/pack-objects.c:2682 +#: builtin/pack-objects.c:2680 msgid "ignore packs that have companion .keep file" msgstr "игнориране на пакетите, които са придружени от файл „.keep“" -#: builtin/pack-objects.c:2684 +#: builtin/pack-objects.c:2682 msgid "pack compression level" msgstr "ниво на компресиране при пакетиране" -#: builtin/pack-objects.c:2686 +#: builtin/pack-objects.c:2684 msgid "do not hide commits by grafts" msgstr "" "извеждане на всички родители — дори и тези, които нормално са скрити при " "присажданията" -#: builtin/pack-objects.c:2688 +#: builtin/pack-objects.c:2686 msgid "use a bitmap index if available to speed up counting objects" msgstr "" "използване на съществуващи индекси на база битови маски за ускоряване на " "преброяването на обектите" -#: builtin/pack-objects.c:2690 +#: builtin/pack-objects.c:2688 msgid "write a bitmap index together with the pack index" msgstr "" "запазване и на индекс на база побитова маска, заедно с индекса за пакета" -#: builtin/pack-objects.c:2781 +#: builtin/pack-objects.c:2779 msgid "Counting objects" msgstr "Преброяване на обектите" @@ -8403,19 +8479,19 @@ msgstr "git prune-packed [-n | --dry-run] [-q | --quiet]" msgid "Removing duplicate objects" msgstr "Изтриване на повтарящите се обекти" -#: builtin/prune.c:12 +#: builtin/prune.c:11 msgid "git prune [-n] [-v] [--expire <time>] [--] [<head>...]" msgstr "git prune [-n] [-v] [--expire ВРЕМЕ] [--] [ВРЪХ…]" -#: builtin/prune.c:106 +#: builtin/prune.c:105 builtin/worktree.c:112 msgid "do not remove, show only" msgstr "само извеждане без действително окастряне" -#: builtin/prune.c:107 +#: builtin/prune.c:106 builtin/worktree.c:113 msgid "report pruned objects" msgstr "информация за окастрените обекти" -#: builtin/prune.c:110 +#: builtin/prune.c:109 builtin/worktree.c:115 msgid "expire objects older than <time>" msgstr "окастряне на обектите по-стари от това ВРЕМЕ" @@ -8437,8 +8513,8 @@ msgid "" "To choose either option permanently, see push.default in 'git help config'." msgstr "" "\n" -"За да включите тази опция за постоянно, вижте документацията за настройката " -"„push.default“ в „git help config“." +"За да включите тази опция за постоянно, погледнете документацията за " +"настройката „push.default“ в „git help config“." #: builtin/push.c:142 #, c-format @@ -8596,7 +8672,7 @@ msgstr "" "Обновяването е отхвърлено, защото върхът на изтласквания клон е преди върха\n" "на отдалечения клон. Проверете клона и внесете отдалечените промени (напр.\n" "с командата „git pull…“), преди отново да изтласкате промените. За повече\n" -"информация вижте раздела „Note about fast-forwards“ в страницата от\n" +"информация погледнете раздела „Note about fast-forwards“ в страницата от\n" "ръководството „git push --help“." #: builtin/push.c:291 @@ -8614,9 +8690,9 @@ msgstr "" "че някой друг е изтласквал към същия клон. Първо внесете отдалечените " "промени\n" "(напр. с командата „git pull…“), преди отново да изтласкате промените.\n" -"За повече информация вижте раздела „Note about fast-forwards“ в страницата " -"от\n" -"ръководството „git push --help“." +"За повече информация погледнете раздела „Note about fast-forwards“ в " +"страницата\n" +"от ръководството „git push --help“." #: builtin/push.c:298 msgid "Updates were rejected because the tag already exists in the remote." @@ -8848,12 +8924,12 @@ msgid "debug unpack-trees" msgstr "изчистване на грешки в командата „unpack-trees“" # FIXME -#: builtin/reflog.c:429 +#: builtin/reflog.c:430 #, c-format msgid "%s' for '%s' is not a valid timestamp" msgstr "„%s“ не е правилна стойност за време за „%s“" -#: builtin/reflog.c:546 builtin/reflog.c:551 +#: builtin/reflog.c:547 builtin/reflog.c:552 #, c-format msgid "'%s' is not a valid timestamp" msgstr "„%s“ не е правилна стойност за време" @@ -8996,12 +9072,12 @@ msgstr "" "указването на следени клони е смислено само за отдалечени хранилища, от " "които се доставя" -#: builtin/remote.c:187 builtin/remote.c:643 +#: builtin/remote.c:187 builtin/remote.c:640 #, c-format msgid "remote %s already exists." msgstr "вече съществува отдалечено хранилище с име „%s“." -#: builtin/remote.c:191 builtin/remote.c:647 +#: builtin/remote.c:191 builtin/remote.c:644 #, c-format msgid "'%s' is not a valid remote name" msgstr "„%s“ е неправилно име за отдалечено хранилище" @@ -9024,28 +9100,28 @@ msgstr "(съвпадащи)" msgid "(delete)" msgstr "(за изтриване)" -#: builtin/remote.c:592 builtin/remote.c:598 builtin/remote.c:604 +#: builtin/remote.c:589 builtin/remote.c:595 builtin/remote.c:601 #, c-format msgid "Could not append '%s' to '%s'" msgstr "„%s“ не може да се добави към „%s“" -#: builtin/remote.c:636 builtin/remote.c:795 builtin/remote.c:895 +#: builtin/remote.c:633 builtin/remote.c:792 builtin/remote.c:892 #, c-format msgid "No such remote: %s" msgstr "Такова отдалечено хранилище няма: %s" -#: builtin/remote.c:653 +#: builtin/remote.c:650 #, c-format msgid "Could not rename config section '%s' to '%s'" msgstr "Разделът „%s“ в настройките не може да бъде преименуван на „%s“" -#: builtin/remote.c:659 builtin/remote.c:847 +#: builtin/remote.c:656 builtin/remote.c:844 #, c-format msgid "Could not remove config section '%s'" msgstr "Разделът „%s“ в настройките не може да бъде изтрит" # FIXME tabulator -#: builtin/remote.c:674 +#: builtin/remote.c:671 #, c-format msgid "" "Not updating non-default fetch refspec\n" @@ -9056,32 +9132,32 @@ msgstr "" " %s\n" " Променете настройките ръчно, ако е необходимо." -#: builtin/remote.c:680 +#: builtin/remote.c:677 #, c-format msgid "Could not append '%s'" msgstr "Разделът „%s“ не може да бъде добавен в настройките" -#: builtin/remote.c:691 +#: builtin/remote.c:688 #, c-format msgid "Could not set '%s'" msgstr "Разделът „%s“ не може да бъде зададен в настройките" -#: builtin/remote.c:713 +#: builtin/remote.c:710 #, c-format msgid "deleting '%s' failed" msgstr "неуспешно изтриване на „%s“" -#: builtin/remote.c:747 +#: builtin/remote.c:744 #, c-format msgid "creating '%s' failed" msgstr "неуспешно създаване на „%s“" -#: builtin/remote.c:766 +#: builtin/remote.c:763 #, c-format msgid "Could not remove branch %s" msgstr "Клонът „%s“ не може да бъде изтрит" -#: builtin/remote.c:833 +#: builtin/remote.c:830 msgid "" "Note: A branch outside the refs/remotes/ hierarchy was not removed;\n" "to delete it, use:" @@ -9095,125 +9171,125 @@ msgstr[1] "" "Бележка: Няколко клона извън йерархията „refs/remotes/“ не бяха изтрити.\n" "Изтрийте ги чрез командата:" -#: builtin/remote.c:948 +#: builtin/remote.c:945 #, c-format msgid " new (next fetch will store in remotes/%s)" msgstr " нов (следващото доставяне ще го разположи в „remotes/%s“)" -#: builtin/remote.c:951 +#: builtin/remote.c:948 msgid " tracked" msgstr " следен" -#: builtin/remote.c:953 +#: builtin/remote.c:950 msgid " stale (use 'git remote prune' to remove)" msgstr " стар (изтрийте чрез „git remote prune“)" # FIXME -#: builtin/remote.c:955 +#: builtin/remote.c:952 msgid " ???" msgstr " неясно състояние" # CHECK -#: builtin/remote.c:996 +#: builtin/remote.c:993 #, c-format msgid "invalid branch.%s.merge; cannot rebase onto > 1 branch" msgstr "" "неправилен клон за сливане „%s“. Невъзможно е да пребазирате върху повече от " "1 клон" -#: builtin/remote.c:1003 +#: builtin/remote.c:1000 #, c-format msgid "rebases onto remote %s" msgstr "пребазиране върху отдалечения клон „%s“" -#: builtin/remote.c:1006 +#: builtin/remote.c:1003 #, c-format msgid " merges with remote %s" msgstr " сливане с отдалечения клон „%s“" -#: builtin/remote.c:1007 +#: builtin/remote.c:1004 msgid " and with remote" msgstr " и с отдалечения клон" -#: builtin/remote.c:1009 +#: builtin/remote.c:1006 #, c-format msgid "merges with remote %s" msgstr "сливане с отдалечения клон „%s“" -#: builtin/remote.c:1010 +#: builtin/remote.c:1007 msgid " and with remote" msgstr " и с отдалечения клон" -#: builtin/remote.c:1056 +#: builtin/remote.c:1053 msgid "create" msgstr "създаден" -#: builtin/remote.c:1059 +#: builtin/remote.c:1056 msgid "delete" msgstr "изтрит" -#: builtin/remote.c:1063 +#: builtin/remote.c:1060 msgid "up to date" msgstr "актуален" -#: builtin/remote.c:1066 +#: builtin/remote.c:1063 msgid "fast-forwardable" msgstr "може да се слее тривиално" -#: builtin/remote.c:1069 +#: builtin/remote.c:1066 msgid "local out of date" msgstr "локалният е изостанал" -#: builtin/remote.c:1076 +#: builtin/remote.c:1073 #, c-format msgid " %-*s forces to %-*s (%s)" msgstr " %-*s принудително изтласква към %-*s (%s)" -#: builtin/remote.c:1079 +#: builtin/remote.c:1076 #, c-format msgid " %-*s pushes to %-*s (%s)" msgstr " %-*s изтласква към %-*s (%s)" -#: builtin/remote.c:1083 +#: builtin/remote.c:1080 #, c-format msgid " %-*s forces to %s" msgstr " %-*s принудително изтласква към %s" -#: builtin/remote.c:1086 +#: builtin/remote.c:1083 #, c-format msgid " %-*s pushes to %s" msgstr " %-*s изтласква към %s" -#: builtin/remote.c:1154 +#: builtin/remote.c:1151 msgid "do not query remotes" msgstr "без заявки към отдалечените хранилища" -#: builtin/remote.c:1181 +#: builtin/remote.c:1178 #, c-format msgid "* remote %s" msgstr "● отдалечено хранилище „%s“" -#: builtin/remote.c:1182 +#: builtin/remote.c:1179 #, c-format msgid " Fetch URL: %s" msgstr " Адрес за доставяне: %s" -#: builtin/remote.c:1183 builtin/remote.c:1334 +#: builtin/remote.c:1180 builtin/remote.c:1331 msgid "(no URL)" msgstr "(без адрес)" # FIXME spaces betwen Push and URL -#: builtin/remote.c:1192 builtin/remote.c:1194 +#: builtin/remote.c:1189 builtin/remote.c:1191 #, c-format msgid " Push URL: %s" msgstr " Адрес за изтласкване: %s" -#: builtin/remote.c:1196 builtin/remote.c:1198 builtin/remote.c:1200 +#: builtin/remote.c:1193 builtin/remote.c:1195 builtin/remote.c:1197 #, c-format msgid " HEAD branch: %s" msgstr " клон сочен от HEAD: %s" -#: builtin/remote.c:1202 +#: builtin/remote.c:1199 #, c-format msgid "" " HEAD branch (remote HEAD is ambiguous, may be one of the following):\n" @@ -9222,146 +9298,146 @@ msgstr "" "хранилище е\n" " нееднозначен и е някой от следните):\n" -#: builtin/remote.c:1214 +#: builtin/remote.c:1211 #, c-format msgid " Remote branch:%s" msgid_plural " Remote branches:%s" msgstr[0] " Отдалечен клон:%s" msgstr[1] " Отдалечени клони:%s" -#: builtin/remote.c:1217 builtin/remote.c:1244 +#: builtin/remote.c:1214 builtin/remote.c:1241 msgid " (status not queried)" msgstr " (състоянието не бе проверено)" -#: builtin/remote.c:1226 +#: builtin/remote.c:1223 msgid " Local branch configured for 'git pull':" msgid_plural " Local branches configured for 'git pull':" msgstr[0] " Локален клон настроен за издърпване чрез „git pull“:" msgstr[1] " Локални клони настроени за издърпване чрез „git pull“:" -#: builtin/remote.c:1234 +#: builtin/remote.c:1231 msgid " Local refs will be mirrored by 'git push'" msgstr " Локалните указатели ще бъдат пренесени чрез „ push“" -#: builtin/remote.c:1241 +#: builtin/remote.c:1238 #, c-format msgid " Local ref configured for 'git push'%s:" msgid_plural " Local refs configured for 'git push'%s:" msgstr[0] " Локалният указател, настроен за „git push“%s:" msgstr[1] " Локалните указатели, настроени за „git push“%s:" -#: builtin/remote.c:1262 +#: builtin/remote.c:1259 msgid "set refs/remotes/<name>/HEAD according to remote" msgstr "задаване на refs/remotes/ИМЕ/HEAD според отдалеченото хранилище" -#: builtin/remote.c:1264 +#: builtin/remote.c:1261 msgid "delete refs/remotes/<name>/HEAD" msgstr "изтриване на refs/remotes/ИМЕ/HEAD" -#: builtin/remote.c:1279 +#: builtin/remote.c:1276 msgid "Cannot determine remote HEAD" msgstr "Не може да се установи отдалеченият връх" -#: builtin/remote.c:1281 +#: builtin/remote.c:1278 msgid "Multiple remote HEAD branches. Please choose one explicitly with:" msgstr "" "Множество клони с върхове. Изберете изрично някой от тях чрез командата:" -#: builtin/remote.c:1291 +#: builtin/remote.c:1288 #, c-format msgid "Could not delete %s" msgstr "„%s“ не може да бъде изтрит" -#: builtin/remote.c:1299 +#: builtin/remote.c:1296 #, c-format msgid "Not a valid ref: %s" msgstr "Неправилен указател: %s" -#: builtin/remote.c:1301 +#: builtin/remote.c:1298 #, c-format msgid "Could not setup %s" msgstr "„%s“ не може да се настрои" -#: builtin/remote.c:1319 +#: builtin/remote.c:1316 #, c-format msgid " %s will become dangling!" msgstr "„%s“ ще се превърне в обект извън клоните!" -#: builtin/remote.c:1320 +#: builtin/remote.c:1317 #, c-format msgid " %s has become dangling!" msgstr "„%s“ се превърна в обект извън клоните!" -#: builtin/remote.c:1330 +#: builtin/remote.c:1327 #, c-format msgid "Pruning %s" msgstr "Окастряне на „%s“" -#: builtin/remote.c:1331 +#: builtin/remote.c:1328 #, c-format msgid "URL: %s" msgstr "адрес: %s" -#: builtin/remote.c:1354 +#: builtin/remote.c:1351 #, c-format msgid " * [would prune] %s" msgstr " ● [ще бъде окастрено] %s" -#: builtin/remote.c:1357 +#: builtin/remote.c:1354 #, c-format msgid " * [pruned] %s" msgstr " ● [окастрено] %s" -#: builtin/remote.c:1402 +#: builtin/remote.c:1399 msgid "prune remotes after fetching" msgstr "окастряне на огледалата на отдалечените хранилища след доставяне" -#: builtin/remote.c:1468 builtin/remote.c:1542 +#: builtin/remote.c:1465 builtin/remote.c:1539 #, c-format msgid "No such remote '%s'" msgstr "Няма отдалечено хранилище на име „%s“" -#: builtin/remote.c:1488 +#: builtin/remote.c:1485 msgid "add branch" msgstr "добавяне на клон" -#: builtin/remote.c:1495 +#: builtin/remote.c:1492 msgid "no remote specified" msgstr "не е указано отдалечено хранилище" -#: builtin/remote.c:1517 +#: builtin/remote.c:1514 msgid "manipulate push URLs" msgstr "промяна на адресите за изтласкване" -#: builtin/remote.c:1519 +#: builtin/remote.c:1516 msgid "add URL" msgstr "добавяне на адреси" -#: builtin/remote.c:1521 +#: builtin/remote.c:1518 msgid "delete URLs" msgstr "изтриване на адреси" # FIXME message - incompatible -#: builtin/remote.c:1528 +#: builtin/remote.c:1525 msgid "--add --delete doesn't make sense" msgstr "опциите „--add“ и „--delete“ са несъвместими" -#: builtin/remote.c:1568 +#: builtin/remote.c:1565 #, c-format msgid "Invalid old URL pattern: %s" msgstr "Неправилен (стар) формат за адрес: %s" -#: builtin/remote.c:1576 +#: builtin/remote.c:1573 #, c-format msgid "No such URL found: %s" msgstr "Такъв адрес не е открит: %s" # FIXME CHECK MEANING -#: builtin/remote.c:1578 +#: builtin/remote.c:1575 msgid "Will not delete all non-push URLs" msgstr "Никой от адресите, които не са за изтласкване, няма да се изтрие" -#: builtin/remote.c:1592 +#: builtin/remote.c:1589 msgid "be verbose; must be placed before a subcommand" msgstr "повече подробности. Поставя се пред подкоманда" @@ -9442,7 +9518,7 @@ msgstr "максимален размер на всеки пакет" msgid "repack objects in packs marked with .keep" msgstr "препакетиране на обектите в пакети белязани с „.keep“" -#: builtin/repack.c:375 +#: builtin/repack.c:377 #, c-format msgid "removing '%s' failed" msgstr "неуспешно изтриване на „%s“" @@ -9826,7 +9902,7 @@ msgstr "" "\n" "(ако искате да ги изтриете заедно с цялата им история, използвайте „rm -rf“)" -#: builtin/rm.c:231 +#: builtin/rm.c:230 msgid "" "the following file has staged content different from both the\n" "file and the HEAD:" @@ -9842,7 +9918,7 @@ msgstr[1] "" "съдържание и\n" "различно от съответстващото на HEAD:" -#: builtin/rm.c:236 +#: builtin/rm.c:235 msgid "" "\n" "(use -f to force removal)" @@ -9850,13 +9926,13 @@ msgstr "" "\n" "(за принудително изтриване използвайте опцията „-f“)" -#: builtin/rm.c:240 +#: builtin/rm.c:239 msgid "the following file has changes staged in the index:" msgid_plural "the following files have changes staged in the index:" msgstr[0] "следният файл е с променено съдържание в индекса:" msgstr[1] "следните файлове са с променено съдържание в индекса:" -#: builtin/rm.c:244 builtin/rm.c:255 +#: builtin/rm.c:243 builtin/rm.c:254 msgid "" "\n" "(use --cached to keep the file, or -f to force removal)" @@ -9865,46 +9941,46 @@ msgstr "" "(за запазване на файла използвайте опцията „--cached“, а за принудително\n" "изтриване — „-f“)" -#: builtin/rm.c:252 +#: builtin/rm.c:251 msgid "the following file has local modifications:" msgid_plural "the following files have local modifications:" msgstr[0] "следният файл е с променено съдържание" msgstr[1] "следните файлове са с променено съдържание" -#: builtin/rm.c:270 +#: builtin/rm.c:269 msgid "do not list removed files" msgstr "да не се извеждат изтритите файлове" -#: builtin/rm.c:271 +#: builtin/rm.c:270 msgid "only remove from the index" msgstr "изтриване само от индекса" -#: builtin/rm.c:272 +#: builtin/rm.c:271 msgid "override the up-to-date check" msgstr "въпреки проверката за актуалността на съдържанието" -#: builtin/rm.c:273 +#: builtin/rm.c:272 msgid "allow recursive removal" msgstr "рекурсивно изтриване" -#: builtin/rm.c:275 +#: builtin/rm.c:274 msgid "exit with a zero status even if nothing matched" msgstr "" "изходният код да е 0, дори ако никой файл нe e напаснал с шаблона за " "изтриване" -#: builtin/rm.c:318 +#: builtin/rm.c:317 msgid "Please, stage your changes to .gitmodules or stash them to proceed" msgstr "" "За да продължите, или вкарайте промените по файла „.gitmodules“ в индекса,\n" "или ги скатайте" -#: builtin/rm.c:336 +#: builtin/rm.c:335 #, c-format msgid "not removing '%s' recursively without -r" msgstr "без използването на опцията „-r“ „%s“ няма да се изтрие рекурсивно" -#: builtin/rm.c:375 +#: builtin/rm.c:374 #, c-format msgid "git rm: unable to remove %s" msgstr "git rm: „%s“ не може да се изтрие" @@ -9957,69 +10033,69 @@ msgstr "" msgid "git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]" msgstr "git show-branch (-g | --reflog)[=БРОЙ[,БАЗА]] [--list] [УКАЗАТЕЛ]" -#: builtin/show-branch.c:652 +#: builtin/show-branch.c:659 msgid "show remote-tracking and local branches" msgstr "извеждане на следящите и локалните клони" -#: builtin/show-branch.c:654 +#: builtin/show-branch.c:661 msgid "show remote-tracking branches" msgstr "извеждане на следящите клони" -#: builtin/show-branch.c:656 +#: builtin/show-branch.c:663 msgid "color '*!+-' corresponding to the branch" msgstr "оцветяване на „*!+-“ според клоните" -#: builtin/show-branch.c:658 +#: builtin/show-branch.c:665 msgid "show <n> more commits after the common ancestor" msgstr "извеждане на такъв БРОЙ подавания от общия предшественик" -#: builtin/show-branch.c:660 +#: builtin/show-branch.c:667 msgid "synonym to more=-1" msgstr "синоним на „more=-1“" -#: builtin/show-branch.c:661 +#: builtin/show-branch.c:668 msgid "suppress naming strings" msgstr "без низове за имената на клоните" -#: builtin/show-branch.c:663 +#: builtin/show-branch.c:670 msgid "include the current branch" msgstr "включване и на текущия клон" -#: builtin/show-branch.c:665 +#: builtin/show-branch.c:672 msgid "name commits with their object names" msgstr "именуване на подаванията с имената им на обекти" -#: builtin/show-branch.c:667 +#: builtin/show-branch.c:674 msgid "show possible merge bases" msgstr "извеждане на възможните бази за сливания" -#: builtin/show-branch.c:669 +#: builtin/show-branch.c:676 msgid "show refs unreachable from any other ref" msgstr "извеждане на недостижимите указатели" -#: builtin/show-branch.c:671 +#: builtin/show-branch.c:678 msgid "show commits in topological order" msgstr "извеждане на подаванията в топологическа подредба" -#: builtin/show-branch.c:674 +#: builtin/show-branch.c:681 msgid "show only commits not on the first branch" msgstr "извеждане само на подаванията, които не са от първия клон" -#: builtin/show-branch.c:676 +#: builtin/show-branch.c:683 msgid "show merges reachable from only one tip" msgstr "извеждане на сливанията, които могат да се достигнат само от един връх" -#: builtin/show-branch.c:678 +#: builtin/show-branch.c:685 msgid "topologically sort, maintaining date order where possible" msgstr "" "топологическа подредба, при запазване на подредбата по дата, доколкото е\n" "възможно" -#: builtin/show-branch.c:681 +#: builtin/show-branch.c:688 msgid "<n>[,<base>]" msgstr "БРОЙ[,БАЗА]" -#: builtin/show-branch.c:682 +#: builtin/show-branch.c:689 msgid "show <n> most recent ref-log entries starting at base" msgstr "показване на най-много БРОЙ журнални записа с начало съответната БАЗА" @@ -10037,37 +10113,37 @@ msgstr "" msgid "git show-ref --exclude-existing[=pattern] < ref-list" msgstr "git show-ref --exclude-existing[=ШАБЛОН] < СПИСЪК_С_УКАЗАТЕЛИ" -#: builtin/show-ref.c:168 +#: builtin/show-ref.c:170 msgid "only show tags (can be combined with heads)" msgstr "извеждане на етикетите (може да се комбинира с върховете)" -#: builtin/show-ref.c:169 +#: builtin/show-ref.c:171 msgid "only show heads (can be combined with tags)" msgstr "извеждане на върховете (може да се комбинира с етикетите)" -#: builtin/show-ref.c:170 +#: builtin/show-ref.c:172 msgid "stricter reference checking, requires exact ref path" msgstr "строга проверка на указателите, изисква се указател с пълен път" -#: builtin/show-ref.c:173 builtin/show-ref.c:175 +#: builtin/show-ref.c:175 builtin/show-ref.c:177 msgid "show the HEAD reference, even if it would be filtered out" msgstr "задължително извеждане и на указателя HEAD" -#: builtin/show-ref.c:177 +#: builtin/show-ref.c:179 msgid "dereference tags into object IDs" msgstr "да се извеждат идентификаторите на обектите-етикети" -#: builtin/show-ref.c:179 +#: builtin/show-ref.c:181 msgid "only show SHA1 hash using <n> digits" msgstr "извеждане само на този БРОЙ цифри от всяка сума по SHA1" -#: builtin/show-ref.c:183 +#: builtin/show-ref.c:185 msgid "do not print results to stdout (useful with --verify)" msgstr "" "без извеждане на резултатите на стандартния вход (полезно с опцията „--" "verify“)" -#: builtin/show-ref.c:185 +#: builtin/show-ref.c:187 msgid "show refs from stdin that aren't in local repository" msgstr "" "извеждане на указателите приети от стандартния вход, които липсват в " @@ -10333,131 +10409,210 @@ msgstr "Обновен етикет „%s“ (бе „%s“)\n" msgid "Unpacking objects" msgstr "Разпакетиране на обектите" -#: builtin/update-index.c:403 +#: builtin/update-index.c:70 +#, c-format +msgid "failed to create directory %s" +msgstr "директорията „%s“ не може да бъде създадена" + +#: builtin/update-index.c:76 +#, c-format +msgid "failed to stat %s" +msgstr "не може да бъде получена информация чрез „stat“ за „%s“" + +#: builtin/update-index.c:86 +#, c-format +msgid "failed to create file %s" +msgstr "файлът „%s“ не може да бъде създаден" + +#: builtin/update-index.c:94 +#, c-format +msgid "failed to delete file %s" +msgstr "файлът „%s“ не може да бъде изтрит" + +#: builtin/update-index.c:101 builtin/update-index.c:203 +#, c-format +msgid "failed to delete directory %s" +msgstr "директорията „%s“ не може да бъде изтрита" + +#: builtin/update-index.c:124 +#, c-format +msgid "Testing " +msgstr "Проба" + +#: builtin/update-index.c:136 +msgid "directory stat info does not change after adding a new file" +msgstr "" +"информацията получена чрез „stat“ за директорията не се променя след " +"добавянето на нов файл" + +#: builtin/update-index.c:149 +msgid "directory stat info does not change after adding a new directory" +msgstr "" +"информацията получена чрез „stat“ за директорията не се променя след " +"добавянето на нова директория" + +#: builtin/update-index.c:162 +msgid "directory stat info changes after updating a file" +msgstr "" +"информацията получена чрез „stat“ за директорията се променя след " +"обновяването на нов файл" + +#: builtin/update-index.c:173 +msgid "directory stat info changes after adding a file inside subdirectory" +msgstr "" +"информацията получена чрез „stat“ за директорията се променя след добавянето " +"на файл в поддиректория" + +#: builtin/update-index.c:184 +msgid "directory stat info does not change after deleting a file" +msgstr "" +"информацията получена чрез „stat“ за директорията не се променя след " +"изтриването на файл" + +#: builtin/update-index.c:197 +msgid "directory stat info does not change after deleting a directory" +msgstr "" +"информацията получена чрез „stat“ за директорията не се променя след " +"изтриването на директория" + +#: builtin/update-index.c:204 +msgid " OK" +msgstr " Добре" + +#: builtin/update-index.c:564 msgid "git update-index [<options>] [--] [<file>...]" msgstr "git update-index [ОПЦИЯ…] [--] [ФАЙЛ…]" -#: builtin/update-index.c:757 +#: builtin/update-index.c:918 msgid "continue refresh even when index needs update" msgstr "" "продължаване с обновяването, дори когато индексът трябва да бъде обновен" -#: builtin/update-index.c:760 +#: builtin/update-index.c:921 msgid "refresh: ignore submodules" msgstr "подмодулите да се игнорират при обновяването" -#: builtin/update-index.c:763 +#: builtin/update-index.c:924 msgid "do not ignore new files" msgstr "новите файлове да не се игнорират" -#: builtin/update-index.c:765 +#: builtin/update-index.c:926 msgid "let files replace directories and vice-versa" msgstr "файлове да могат да заменят директории и обратно" -#: builtin/update-index.c:767 +#: builtin/update-index.c:928 msgid "notice files missing from worktree" msgstr "предупреждаване при липсващи в работното дърво файлове" -#: builtin/update-index.c:769 +#: builtin/update-index.c:930 msgid "refresh even if index contains unmerged entries" msgstr "обновяване дори и индексът да съдържа неслети обекти" -#: builtin/update-index.c:772 +#: builtin/update-index.c:933 msgid "refresh stat information" msgstr "обновяване на информацията от функцията „stat“" -#: builtin/update-index.c:776 +#: builtin/update-index.c:937 msgid "like --refresh, but ignore assume-unchanged setting" msgstr "" "като опцията „--refresh“, но да се проверят и обектите, които са били приети " "за непроменени" -#: builtin/update-index.c:780 +#: builtin/update-index.c:941 msgid "<mode>,<object>,<path>" msgstr "РЕЖИМ,ОБЕКТ,ПЪТ" -#: builtin/update-index.c:781 +#: builtin/update-index.c:942 msgid "add the specified entry to the index" msgstr "добавяне на изброените обекти към индекса" -#: builtin/update-index.c:785 +#: builtin/update-index.c:946 msgid "(+/-)x" msgstr "(+/-)x" -#: builtin/update-index.c:786 +#: builtin/update-index.c:947 msgid "override the executable bit of the listed files" msgstr "изрично задаване на стойността на флага дали файлът е изпълним" -#: builtin/update-index.c:790 +#: builtin/update-index.c:951 msgid "mark files as \"not changing\"" msgstr "задаване на флаг, че файлът не се променя" -#: builtin/update-index.c:793 +#: builtin/update-index.c:954 msgid "clear assumed-unchanged bit" msgstr "изчистване на флага, че файлът не се променя" -#: builtin/update-index.c:796 +#: builtin/update-index.c:957 msgid "mark files as \"index-only\"" msgstr "задаване на флаг, че файловете са само за индекса" -#: builtin/update-index.c:799 +#: builtin/update-index.c:960 msgid "clear skip-worktree bit" msgstr "изчистване на флага, че файловете са само за индекса" -#: builtin/update-index.c:802 +#: builtin/update-index.c:963 msgid "add to index only; do not add content to object database" msgstr "добавяне само към индекса без добавяне към базата от данни за обектите" -#: builtin/update-index.c:804 +#: builtin/update-index.c:965 msgid "remove named paths even if present in worktree" msgstr "изтриване на указаните пътища, дори и да съществуват в работното дърво" -#: builtin/update-index.c:806 +#: builtin/update-index.c:967 msgid "with --stdin: input lines are terminated by null bytes" msgstr "" "при комбиниране с опцията „--stdin“ — входните редове са разделени с нулевия " "байт" -#: builtin/update-index.c:808 +#: builtin/update-index.c:969 msgid "read list of paths to be updated from standard input" msgstr "изчитане на списъка с пътища за обновяване от стандартния вход" -#: builtin/update-index.c:812 +#: builtin/update-index.c:973 msgid "add entries from standard input to the index" msgstr "добавяне на елементите от стандартния вход към индекса" -#: builtin/update-index.c:816 +#: builtin/update-index.c:977 msgid "repopulate stages #2 and #3 for the listed paths" msgstr "" "възстановяване на състоянието преди сливане или нужда от обновяване за " "изброените пътища" -#: builtin/update-index.c:820 +#: builtin/update-index.c:981 msgid "only update entries that differ from HEAD" msgstr "добавяне само на съдържанието, което се различава от това в „HEAD“" -#: builtin/update-index.c:824 +#: builtin/update-index.c:985 msgid "ignore files missing from worktree" msgstr "игнориране на файловете, които липсват в работното дърво" -#: builtin/update-index.c:827 +#: builtin/update-index.c:988 msgid "report actions to standard output" msgstr "извеждане на действията на стандартния изход" -#: builtin/update-index.c:829 +#: builtin/update-index.c:990 msgid "(for porcelains) forget saved unresolved conflicts" msgstr "" "забравяне на записаната информация за неразрешени конфликти — за командите " "от потребителско ниво" -#: builtin/update-index.c:833 +#: builtin/update-index.c:994 msgid "write index in this format" msgstr "записване на индекса в този формат" -#: builtin/update-index.c:835 +#: builtin/update-index.c:996 msgid "enable or disable split index" msgstr "включване или изключване на разделянето на индекса" +#: builtin/update-index.c:998 +msgid "enable/disable untracked cache" +msgstr "включване/изключване на кеша за неследените файлове" + +#: builtin/update-index.c:1000 +msgid "enable untracked cache without testing the filesystem" +msgstr "" +"включване на кеша за неследените файлове без проверка на файловата система" + #: builtin/update-ref.c:9 msgid "git update-ref [<options>] -d <refname> [<old-val>]" msgstr "git update-ref [ОПЦИЯ…] -d ИМЕ_НА_УКАЗАТЕЛ [СТАРА_СТОЙНОСТ]" @@ -10525,6 +10680,86 @@ msgstr "git verify-tag [-v | --verbose] ЕТИКЕТ…" msgid "print tag contents" msgstr "извеждане на съдържанието на ЕТИКЕТи" +#: builtin/worktree.c:11 +msgid "git worktree add [<options>] <path> <branch>" +msgstr "git worktree add [ОПЦИЯ…] ПЪТ КЛОН" + +#: builtin/worktree.c:12 +msgid "git worktree prune [<options>]" +msgstr "git worktree prune [ОПЦИЯ…]" + +#: builtin/worktree.c:27 +#, c-format +msgid "Removing worktrees/%s: not a valid directory" +msgstr "Изтриване на „worktrees/%s“: не е правилна поддиректория" + +#: builtin/worktree.c:33 +#, c-format +msgid "Removing worktrees/%s: gitdir file does not exist" +msgstr "Изтриване на „worktrees/%s“: файлът „gitdir“ не съществува" + +#: builtin/worktree.c:38 +#, c-format +msgid "Removing worktrees/%s: unable to read gitdir file (%s)" +msgstr "" +"Изтриване на „worktrees/%s“: файлът „gitdir“ (%s) не може да бъде прочетен" + +#: builtin/worktree.c:49 +#, c-format +msgid "Removing worktrees/%s: invalid gitdir file" +msgstr "Изтриване на „worktrees/%s“: неправилен файл „gitdir“" + +#: builtin/worktree.c:65 +#, c-format +msgid "Removing worktrees/%s: gitdir file points to non-existent location" +msgstr "" +"Изтриване на „worktrees/%s“: файлът „gitdir“ сочи несъществуващо " +"местоположение" + +#: builtin/worktree.c:100 +#, c-format +msgid "failed to remove: %s" +msgstr "„%s“ не може да се изтрие" + +#: builtin/worktree.c:186 +#, c-format +msgid "'%s' already exists" +msgstr "„%s“ вече съществува" + +#: builtin/worktree.c:207 +#, c-format +msgid "could not create directory of '%s'" +msgstr "директорията „%s“ не може да бъде създадена" + +#: builtin/worktree.c:241 +msgid "unable to resolve HEAD" +msgstr "соченото от указателя „HEAD“ не може да бъде открито" + +#: builtin/worktree.c:249 +#, c-format +msgid "Enter %s (identifier %s)" +msgstr "Въведете %s (идентификатор %s)" + +#: builtin/worktree.c:281 +msgid "checkout <branch> even if already checked out in other worktree" +msgstr "Изтегляне КЛОНа, дори и да е изтеглен в друго работно дърво" + +#: builtin/worktree.c:283 +msgid "create a new branch" +msgstr "създаване на нов клон" + +#: builtin/worktree.c:285 +msgid "create or reset a branch" +msgstr "създаване или зануляване на клони" + +#: builtin/worktree.c:286 +msgid "detach HEAD at named commit" +msgstr "отделяне на указателя „HEAD“ към указаното подаване" + +#: builtin/worktree.c:292 +msgid "-b and -B are mutually exclusive" +msgstr "опциите „-b“ и „-B“ са несъвместими" + #: builtin/write-tree.c:13 msgid "git write-tree [--missing-ok] [--prefix=<prefix>/]" msgstr "git write-tree [--missing-ok] [--prefix=ПРЕФИКС/]" @@ -10557,110 +10792,131 @@ msgstr "" "за\n" "някое определено ПОНЯТИЕ използвайте „git help ПОНЯТИЕ“." -#: common-cmds.h:8 +#: common-cmds.h:10 +msgid "start a working area (see also: git help tutorial)" +msgstr "създаване на работно дърво (погледнете: „git help tutorial“)" + +#: common-cmds.h:11 +msgid "work on the current change (see also: git help everyday)" +msgstr "работа по текущата промяна (погледнете: „git help everyday“)" + +#: common-cmds.h:12 +msgid "examine the history and state (see also: git help revisions)" +msgstr "преглед на историята и състоянието (погледнете: „git help revisions“)" + +#: common-cmds.h:13 +msgid "grow, mark and tweak your common history" +msgstr "увеличаване, отбелязване и промяна на общата история" + +#: common-cmds.h:14 +msgid "collaborate (see also: git help workflows)" +msgstr "съвместна работа (погледнете: „git help workflows“)" + +#: common-cmds.h:18 msgid "Add file contents to the index" msgstr "Добавяне на съдържанието на файла към индекса" -#: common-cmds.h:9 +#: common-cmds.h:19 msgid "Find by binary search the change that introduced a bug" msgstr "Двоично търсене на промяната, която е причинила грешка" # FIXME - should be similar to tag -#: common-cmds.h:10 +#: common-cmds.h:20 msgid "List, create, or delete branches" msgstr "Извеждане, създаване, изтриване на клони" -#: common-cmds.h:11 -msgid "Checkout a branch or paths to the working tree" -msgstr "Изтегляне на цял клон или файлове/директории в работното дърво" +#: common-cmds.h:21 +msgid "Switch branches or restore working tree files" +msgstr "" +"Преминаване към друг клон или възстановяване на файловете в работното дърво" -#: common-cmds.h:12 +#: common-cmds.h:22 msgid "Clone a repository into a new directory" msgstr "Клониране на хранилище в нова директория" -#: common-cmds.h:13 +#: common-cmds.h:23 msgid "Record changes to the repository" msgstr "Подаване на промени в хранилището" -#: common-cmds.h:14 +#: common-cmds.h:24 msgid "Show changes between commits, commit and working tree, etc" msgstr "Извеждане на разликите между подаванията, версиите, работното дърво" -#: common-cmds.h:15 +#: common-cmds.h:25 msgid "Download objects and refs from another repository" msgstr "Изтегляне на обекти и указатели от друго хранилище" -#: common-cmds.h:16 +#: common-cmds.h:26 msgid "Print lines matching a pattern" msgstr "Извеждане на редовете напасващи на шаблон" -#: common-cmds.h:17 +#: common-cmds.h:27 msgid "Create an empty Git repository or reinitialize an existing one" msgstr "Създаване на празно хранилище на Git или зануляване на съществуващо" -#: common-cmds.h:18 +#: common-cmds.h:28 msgid "Show commit logs" msgstr "Извеждане на журнала с подаванията" -#: common-cmds.h:19 +#: common-cmds.h:29 msgid "Join two or more development histories together" msgstr "Сливане на две или повече поредици/истории от промени" -#: common-cmds.h:20 +#: common-cmds.h:30 msgid "Move or rename a file, a directory, or a symlink" msgstr "Преместване или преименуване на файл, директория или символна връзка" -#: common-cmds.h:21 +#: common-cmds.h:31 msgid "Fetch from and integrate with another repository or a local branch" msgstr "Доставяне и интегрирането на промените от друго хранилище или клон" -#: common-cmds.h:22 +#: common-cmds.h:32 msgid "Update remote refs along with associated objects" msgstr "Обновяване на отдалечените указатели и свързаните с тях обекти" -#: common-cmds.h:23 +#: common-cmds.h:33 msgid "Forward-port local commits to the updated upstream head" msgstr "Пребазиране на промени към нова основа" -#: common-cmds.h:24 +#: common-cmds.h:34 msgid "Reset current HEAD to the specified state" msgstr "Привеждане на указателя „HEAD“ към зададеното състояние" -#: common-cmds.h:25 +#: common-cmds.h:35 msgid "Remove files from the working tree and from the index" msgstr "Изтриване на файлове от работното дърво и индекса" -#: common-cmds.h:26 +#: common-cmds.h:36 msgid "Show various types of objects" msgstr "Извеждане на различните видове обекти в Git" -#: common-cmds.h:27 +#: common-cmds.h:37 msgid "Show the working tree status" msgstr "Извеждане на състоянието на работното дърво" # FIXME - should be similar to branch -#: common-cmds.h:28 +#: common-cmds.h:38 msgid "Create, list, delete or verify a tag object signed with GPG" msgstr "Извеждане, създаване, изтриване, проверка на етикети подписани с GPG" -#: parse-options.h:143 +#: parse-options.h:142 msgid "expiry-date" msgstr "период на валидност/запазване" -#: parse-options.h:158 +#: parse-options.h:157 msgid "no-op (backward compatibility)" msgstr "нулева операция (за съвместимост с предишни версии)" -#: parse-options.h:232 +#: parse-options.h:231 msgid "be more verbose" msgstr "повече подробности" -#: parse-options.h:234 +#: parse-options.h:233 msgid "be more quiet" msgstr "по-малко подробности" # FIXME SHA-1 -> SHA1 -#: parse-options.h:240 +#: parse-options.h:239 msgid "use <n> digits to display SHA-1s" msgstr "да се показват такъв БРОЙ цифри от сумите по SHA1" @@ -10674,7 +10930,7 @@ msgstr "" msgid "You need to set your committer info first" msgstr "Първо трябва да зададете информация за себе си" -#: git-am.sh:98 +#: git-am.sh:100 msgid "" "You seem to have moved HEAD since the last 'am' failure.\n" "Not rewinding to ORIG_HEAD" @@ -10685,7 +10941,7 @@ msgstr "" "към\n" "„ORIG_HEAD“" -#: git-am.sh:108 +#: git-am.sh:110 #, sh-format msgid "" "When you have resolved this problem, run \"$cmdline --continue\".\n" @@ -10699,21 +10955,21 @@ msgstr "" "на \n" "кръпки, изпълнете командата „$cmdline --abort“." -#: git-am.sh:124 +#: git-am.sh:126 msgid "Cannot fall back to three-way merge." msgstr "Не може да се премине към тройно сливане." -#: git-am.sh:140 +#: git-am.sh:142 |