- LINUX_GIT_LFS_VERSION="1.2.0"
- DEFAULT_TEST_TARGET=prove
- GIT_PROVE_OPTS="--timer --jobs 3 --state=failed,slow,save"
- - GIT_TEST_OPTS="--verbose --tee"
+ - GIT_TEST_OPTS="--verbose-log"
- GIT_TEST_HTTPD=true
- GIT_TEST_CLONE_2GB=YesPlease
# t9810 occasionally fails on Travis CI OS X
that were detected without dying itself, but under some conditions
it didn't and died instead, which has been fixed.
+ * When "git fetch" tries to find where the history of the repository
+ it runs in has diverged from what the other side has, it has a
+ mechanism to avoid digging too deep into irrelevant side branches.
+ This however did not work well over the "smart-http" transport due
+ to a design bug, which has been fixed.
+
+ * When we started cURL to talk to imap server when a new enough
+ version of cURL library is available, we forgot to explicitly add
+ imap(s):// before the destination. To some folks, that didn't work
+ and the library tried to make HTTP(s) requests instead.
+
+ * The ./configure script generated from configure.ac was taught how
+ to detect support of SSL by libcurl better.
+
+ * http.emptyauth configuration is a way to allow an empty username to
+ pass when attempting to authenticate using mechanisms like
+ Kerberos. We took an unspecified (NULL) username and sent ":"
+ (i.e. no username, no password) to CURLOPT_USERPWD, but did not do
+ the same when the username is explicitly set to an empty string.
+
+ * "git clone" of a local repository can be done at the filesystem
+ level, but the codepath did not check errors while copying and
+ adjusting the file that lists alternate object stores.
+
+ * Documentation for "git commit" was updated to clarify that "commit
+ -p <paths>" adds to the current contents of the index to come up
+ with what to commit.
+
+ * A stray symbolic link in $GIT_DIR/refs/ directory could make name
+ resolution loop forever, which has been corrected.
+
+ * The "submodule.<name>.path" stored in .gitmodules is never copied
+ to .git/config and such a key in .git/config has no meaning, but
+ the documentation described it and submodule.<name>.url next to
+ each other as if both belong to .git/config. This has been fixed.
+
+ * Recent git allows submodule.<name>.branch to use a special token
+ "." instead of the branch name; the documentation has been updated
+ to describe it.
+
+ * In a worktree connected to a repository elsewhere, created via "git
+ worktree", "git checkout" attempts to protect users from confusion
+ by refusing to check out a branch that is already checked out in
+ another worktree. However, this also prevented checking out a
+ branch, which is designated as the primary branch of a bare
+ reopsitory, in a worktree that is connected to the bare
+ repository. The check has been corrected to allow it.
+
+ * "git rebase" immediately after "git clone" failed to find the fork
+ point from the upstream.
+
+ * When fetching from a remote that has many tags that are irrelevant
+ to branches we are following, we used to waste way too many cycles
+ when checking if the object pointed at by a tag (that we are not
+ going to fetch!) exists in our repository too carefully.
+
+ * The Travis CI configuration we ship ran the tests with --verbose
+ option but this risks non-TAP output that happens to be "ok" to be
+ misinterpreted as TAP signalling a test that passed. This resulted
+ in unnecessary failure. This has been corrected by introducing a
+ new mode to run our tests in the test harness to send the verbose
+ output separately to the log file.
+
+ * Some AsciiDoc formatter mishandles a displayed illustration with
+ tabs in it. Adjust a few of them in merge-base documentation to
+ work around them.
Also contains minor documentation updates and code clean-ups.
Git 2.11 Release Notes
======================
+Backward compatibility notes.
+
+ * An empty string used as a pathspec element has always meant
+ 'everything matches', but it is too easy to write a script that
+ finds a path to remove in $path and run 'git rm "$paht"', which
+ ends up removing everything. This release starts warning about the
+ use of an empty string that is used for 'everything matches' and
+ asks users to use a more explicit '.' for that instead.
+
+ The hope is that existing users will not mind this change, and
+ eventually the warning can be turned into a hard error, upgrading
+ the deprecation into removal of this (mis)feature.
+
+ * The historical argument order "git merge <msg> HEAD <commit>..."
+ has been deprecated for quite some time, and will be removed in the
+ next release (not this one).
+
+ * The default abbreviation length, which has historically been 7, now
+ scales as the repository grows, using the approximate number of
+ objects in the reopsitory and a bit of math around the birthday
+ paradox. The logic suggests to use 12 hexdigits for the Linux
+ kernel, and 9 to 10 for Git itself.
+
+
Updates since v2.10
-------------------
UI, Workflows & Features
+ * Comes with new version of git-gui, now at its 0.21.0 tag.
+
* "git format-patch --cover-letter HEAD^" to format a single patch
with a separate cover letter now numbers the output as [PATCH 0/1]
and [PATCH 1/1] by default.
* "git mergetool" learned to honor "-O<orderfile>" to control the
order of paths to present to the end user.
+ * "git diff/log --ws-error-highlight=<kind>" lacked the corresponding
+ configuration variable to set it by default.
+
+ * "git ls-files" learned "--recurse-submodules" option that can be
+ used to get a listing of tracked files across submodules (i.e. this
+ only works with "--cached" option, not for listing untracked or
+ ignored files). This would be a useful tool to sit on the upstream
+ side of a pipe that is read with xargs to work on all working tree
+ files from the top-level superproject.
+
+ * A new credential helper that talks via "libsecret" with
+ implementations of XDG Secret Service API has been added to
+ contrib/credential/.
+
+ * The GPG verification status shown in "%G?" pretty format specifier
+ was not rich enough to differentiate a signature made by an expired
+ key, a signature made by a revoked key, etc. New output letters
+ have been assigned to express them.
+
+ * In addition to purely abbreviated commit object names, "gitweb"
+ learned to turn "git describe" output (e.g. v2.9.3-599-g2376d31787)
+ into clickable links in its output.
+
+ * When new paths were added by "git add -N" to the index, it was
+ enough to circumvent the check by "git commit" to refrain from
+ making an empty commit without "--allow-empty". The same logic
+ prevented "git status" to show such a path as "new file" in the
+ "Changes not staged for commit" section.
+
Performance, Internal Implementation, Development Support etc.
decide if we accept the check, and once we decide, either migrate
them to the repository or purge them immediately.
+ * The require_clean_work_tree() helper was recreated in C when "git
+ pull" was rewritten from shell; the helper is now made available to
+ other callers in preparation for upcoming "rebase -i" work.
+
+ * "git upload-pack" had its code cleaned-up and performance improved
+ by reducing use of timestamp-ordered commit-list, which was
+ replaced with a priority queue.
+
+ * "git diff --no-index" codepath has been updated not to try to peek
+ into .git/ directory that happens to be under the current
+ directory, when we know we are operating outside any repository.
+
+ * Update of the sequencer codebase to make it reusable to reimplement
+ "rebase -i" continues.
+
Also contains various documentation updates and code clean-ups.
each other as if both belong to .git/config. This has been fixed.
(merge 72710165c9 sb/submodule-config-doc-drop-path later to maint).
+ * In a worktree connected to a repository elsewhere, created via "git
+ worktree", "git checkout" attempts to protect users from confusion
+ by refusing to check out a branch that is already checked out in
+ another worktree. However, this also prevented checking out a
+ branch, which is designated as the primary branch of a bare
+ reopsitory, in a worktree that is connected to the bare
+ repository. The check has been corrected to allow it.
+ (merge 171c646f8c dk/worktree-dup-checkout-with-bare-is-ok later to maint).
+
+ * "git rebase" immediately after "git clone" failed to find the fork
+ point from the upstream.
+ (merge 4f21454b55 jk/merge-base-fork-point-without-reflog later to maint).
+
+ * When fetching from a remote that has many tags that are irrelevant
+ to branches we are following, we used to waste way too many cycles
+ when checking if the object pointed at by a tag (that we are not
+ going to fetch!) exists in our repository too carefully.
+ (merge 5827a03545 jk/fetch-quick-tag-following later to maint).
+
+ * Protect our code from over-eager compilers.
+ (merge 0ac52a38e8 jk/tighten-alloc later to maint).
+
+ * Recent git allows submodule.<name>.branch to use a special token
+ "." instead of the branch name; the documentation has been updated
+ to describe it.
+ (merge 15ef78008a bw/submodule-branch-dot-doc later to maint).
+
+ * A hot-fix for a test added by a recent topic that went to both
+ 'master' and 'maint' already.
+ (merge 76e368c378 tg/add-chmod+x-fix later to maint).
+
+ * "git send-email" attempts to pick up valid e-mails from the
+ trailers, but people in real world write non-addresses there, like
+ "Cc: Stable <add@re.ss> # 4.8+", which broke the output depending
+ on the availability and vintage of Mail::Address perl module.
+ (merge dcfafc5214 mm/send-email-cc-cruft-after-address later to maint).
+
+ * The Travis CI configuration we ship ran the tests with --verbose
+ option but this risks non-TAP output that happens to be "ok" to be
+ misinterpreted as TAP signalling a test that passed. This resulted
+ in unnecessary failure. This has been corrected by introducing a
+ new mode to run our tests in the test harness to send the verbose
+ output separately to the log file.
+ (merge 614fe01521 jk/tap-verbose-fix later to maint).
+
+ * Some AsciiDoc formatter mishandles a displayed illustration with
+ tabs in it. Adjust a few of them in merge-base documentation to
+ work around them.
+ (merge 6750f62699 po/fix-doc-merge-base-illustration later to maint).
+
+ * A minor regression fix for "git submodule" that was introduced
+ when more helper functions were reimplemented in C.
+ (merge 77b63ac31e sb/submodule-ignore-trailing-slash later to maint).
+
+ * The code that we have used for the past 10+ years to cycle
+ 4-element ring buffers turns out to be not quite portable in
+ theoretical world.
+ (merge bb84735c80 rs/ring-buffer-wraparound later to maint).
+
+ * "git daemon" used fixed-length buffers to turn URL to the
+ repository the client asked for into the server side directory
+ path, using snprintf() to avoid overflowing these buffers, but
+ allowed possibly truncated paths to the directory. This has been
+ tightened to reject such a request that causes overlong path to be
+ required to serve.
+ (merge 6bdb0083be jk/daemon-path-ok-check-truncation later to maint).
+
* Other minor doc, test and build updates and code cleanups.
(merge a94bb68397 rs/cocci later to maint).
(merge 641c900b2c js/reset-usage later to maint).
(merge 30cfe72d37 rs/pretty-format-color-doc-fix later to maint).
+ (merge d709f1fb9d jc/diff-unique-abbrev-comments later to maint).
+ (merge 13092a916d jc/cocci-xstrdup-or-null later to maint).
+ (merge 86009f32bb pb/test-parse-options-expect later to maint).
+ (merge 749a2279a4 yk/git-tag-remove-mention-of-old-layout-in-doc later to maint).
low-occurrence common elements".
--
+
+
+diff.wsErrorHighlight::
+ A comma separated list of `old`, `new`, `context`, that
+ specifies how whitespace errors on lines are highlighted
+ with `color.diff.whitespace`. Can be overridden by the
+ command line option `--ws-error-highlight=<kind>`
lines are highlighted. E.g. `--ws-error-highlight=new,old`
highlights whitespace errors on both deleted and added lines.
`all` can be used as a short-hand for `old,new,context`.
+ The `diff.wsErrorHighlight` configuration variable can be
+ used to specify the default behaviour.
endif::git-format-patch[]
--line-prefix=<prefix>::
Prepend an additional prefix to every line of output.
+--ita-invisible-in-index::
+ By default entries added by "git add -N" appear as an existing
+ empty file in "git diff" and a new file in "git diff --cached".
+ This option makes the entry appear as a new file in "git diff"
+ and non-existent in "git diff --cached". This option could be
+ reverted with `--ita-visible-in-index`. Both options are
+ experimental and could be removed in future.
+
For more detailed explanation on these common options, see also
linkgit:gitdiffcore[7].
will be added before the new trailer.
Existing trailers are extracted from the input message by looking for
-a group of one or more lines that contain a colon (by default), where
-the group is preceded by one or more empty (or whitespace-only) lines.
+a group of one or more lines that (i) are all trailers, or (ii) contains at
+least one Git-generated trailer and consists of at least 25% trailers.
+The group must be preceded by one or more empty (or whitespace-only) lines.
The group must either be at the end of the message or be the last
non-whitespace lines before a line that starts with '---'. Such three
minus signs start the patch part of the message.
-When reading trailers, there can be whitespaces before and after the
+When reading trailers, there can be whitespaces after the
token, the separator and the value. There can also be whitespaces
-inside the token and the value.
+inside the token and the value. The value may be split over multiple lines with
+each subsequent line starting with whitespace, like the "folding" in RFC 822.
Note that 'trailers' do not follow and are not intended to follow many
-rules for RFC 822 headers. For example they do not follow the line
-folding rules, the encoding rules and probably many other rules.
+rules for RFC 822 headers. For example they do not follow
+the encoding rules and probably many other rules.
OPTIONS
-------
[--exclude-per-directory=<file>]
[--exclude-standard]
[--error-unmatch] [--with-tree=<tree-ish>]
- [--full-name] [--abbrev] [--] [<file>...]
+ [--full-name] [--recurse-submodules]
+ [--abbrev] [--] [<file>...]
DESCRIPTION
-----------
option forces paths to be output relative to the project
top directory.
+--recurse-submodules::
+ Recursively calls ls-files on each submodule in the repository.
+ Currently there is only support for the --cached mode.
+
--abbrev[=<n>]::
Instead of showing the full 40-byte hexadecimal object
lines, show only a partial prefix.
For example, with this topology:
- o---o---o---B
- /
+ o---o---o---B
+ /
---o---1---o---o---o---A
the merge base between 'A' and 'B' is '1'.
When the history involves criss-cross merges, there can be more than one
'best' common ancestor for two commits. For example, with this topology:
- ---1---o---A
- \ /
- X
- / \
- ---2---o---o---B
+ ---1---o---A
+ \ /
+ X
+ / \
+ ---2---o---o---B
both '1' and '2' are merge-bases of A and B. Neither one is better than
the other (both are 'best' merge bases). When the `--all` option is not given,
`origin/master` may have been rewound and rebuilt, leading to a
history of this shape:
- o---B1
- /
+ o---B1
+ /
---o---o---B2--o---o---o---B (origin/master)
- \
- B3
- \
- Derived (topic)
+ \
+ B3
+ \
+ Derived (topic)
where `origin/master` used to point at commits B3, B2, B1 and now it
points at B, and your `topic` branch was started on top of it back
--branch::
Branch of repository to add as submodule.
The name of the branch is recorded as `submodule.<name>.branch` in
- `.gitmodules` for `update --remote`.
+ `.gitmodules` for `update --remote`. A special value of `.` is used to
+ indicate that the name of the branch in the submodule should be the
+ same name as the current branch in the current repository.
-f::
--force::
~~~~~~~~~~~~~~~~~~~~~~
If you are following somebody else's tree, you are most likely
-using remote-tracking branches (`refs/heads/origin` in traditional
-layout, or `refs/remotes/origin/master` in the separate-remote
-layout). You usually want the tags from the other end.
+using remote-tracking branches (eg. `refs/remotes/origin/master`).
+You usually want the tags from the other end.
On the other hand, if you are fetching because you would want a
one-shot merge from somebody else, you typically do not want to
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p|--paginate|--no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
+ [--super-prefix=<path>]
<command> [<args>]
DESCRIPTION
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v2.10.1/git.html[documentation for release 2.10.1]
+* link:v2.10.2/git.html[documentation for release 2.10.2]
* release notes for
+ link:RelNotes/2.10.2.txt[2.10.2],
link:RelNotes/2.10.1.txt[2.10.1],
link:RelNotes/2.10.0.txt[2.10].
details. Equivalent to setting the `GIT_NAMESPACE` environment
variable.
+--super-prefix=<path>::
+ Currently for internal use only. Set a prefix which gives a path from
+ above a repository down to its root. One use is to give submodules
+ context about the superproject that invoked it.
+
--bare::
Treat the repository as a bare repository. If GIT_DIR
environment is not set, it is set to the current working
fed the blob object from its standard input, and its standard
output is used to update the worktree file. Similarly, the
`clean` command is used to convert the contents of worktree file
-upon checkin.
+upon checkin. By default these commands process only a single
+blob and terminate. If a long running `process` filter is used
+in place of `clean` and/or `smudge` filters, then Git can process
+all blobs with a single filter command invocation for the entire
+life of a single Git command, for example `git add --all`. If a
+long running `process` filter is configured then it always takes
+precedence over a configured single blob filter. See section
+below for the description of the protocol used to communicate with
+a `process` filter.
One use of the content filtering is to massage the content into a shape
that is more convenient for the platform, filesystem, and the user to use.
should not try to access the file on disk, but only act as filters on the
content provided to them on standard input.
+Long Running Filter Process
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If the filter command (a string value) is defined via
+`filter.<driver>.process` then Git can process all blobs with a
+single filter invocation for the entire life of a single Git
+command. This is achieved by using a packet format (pkt-line,
+see technical/protocol-common.txt) based protocol over standard
+input and standard output as follows. All packets, except for the
+"*CONTENT" packets and the "0000" flush packet, are considered
+text and therefore are terminated by a LF.
+
+Git starts the filter when it encounters the first file
+that needs to be cleaned or smudged. After the filter started
+Git sends a welcome message ("git-filter-client"), a list of supported
+protocol version numbers, and a flush packet. Git expects to read a welcome
+response message ("git-filter-server"), exactly one protocol version number
+from the previously sent list, and a flush packet. All further
+communication will be based on the selected version. The remaining
+protocol description below documents "version=2". Please note that
+"version=42" in the example below does not exist and is only there
+to illustrate how the protocol would look like with more than one
+version.
+
+After the version negotiation Git sends a list of all capabilities that
+it supports and a flush packet. Git expects to read a list of desired
+capabilities, which must be a subset of the supported capabilities list,
+and a flush packet as response:
+------------------------
+packet: git> git-filter-client
+packet: git> version=2
+packet: git> version=42
+packet: git> 0000
+packet: git< git-filter-server
+packet: git< version=2
+packet: git< 0000
+packet: git> capability=clean
+packet: git> capability=smudge
+packet: git> capability=not-yet-invented
+packet: git> 0000
+packet: git< capability=clean
+packet: git< capability=smudge
+packet: git< 0000
+------------------------
+Supported filter capabilities in version 2 are "clean" and
+"smudge".
+
+Afterwards Git sends a list of "key=value" pairs terminated with
+a flush packet. The list will contain at least the filter command
+(based on the supported capabilities) and the pathname of the file
+to filter relative to the repository root. Right after the flush packet
+Git sends the content split in zero or more pkt-line packets and a
+flush packet to terminate content. Please note, that the filter
+must not send any response before it received the content and the
+final flush packet.
+------------------------
+packet: git> command=smudge
+packet: git> pathname=path/testfile.dat
+packet: git> 0000
+packet: git> CONTENT
+packet: git> 0000
+------------------------
+
+The filter is expected to respond with a list of "key=value" pairs
+terminated with a flush packet. If the filter does not experience
+problems then the list must contain a "success" status. Right after
+these packets the filter is expected to send the content in zero
+or more pkt-line packets and a flush packet at the end. Finally, a
+second list of "key=value" pairs terminated with a flush packet
+is expected. The filter can change the status in the second list
+or keep the status as is with an empty list. Please note that the
+empty list must be terminated with a flush packet regardless.
+
+------------------------
+packet: git< status=success
+packet: git< 0000
+packet: git< SMUDGED_CONTENT
+packet: git< 0000
+packet: git< 0000 # empty list, keep "status=success" unchanged!
+------------------------
+
+If the result content is empty then the filter is expected to respond
+with a "success" status and a flush packet to signal the empty content.
+------------------------
+packet: git< status=success
+packet: git< 0000
+packet: git< 0000 # empty content!
+packet: git< 0000 # empty list, keep "status=success" unchanged!
+------------------------
+
+In case the filter cannot or does not want to process the content,
+it is expected to respond with an "error" status.
+------------------------
+packet: git< status=error
+packet: git< 0000
+------------------------
+
+If the filter experiences an error during processing, then it can
+send the status "error" after the content was (partially or
+completely) sent.
+------------------------
+packet: git< status=success
+packet: git< 0000
+packet: git< HALF_WRITTEN_ERRONEOUS_CONTENT
+packet: git< 0000
+packet: git< status=error
+packet: git< 0000
+------------------------
+
+In case the filter cannot or does not want to process the content
+as well as any future content for the lifetime of the Git process,
+then it is expected to respond with an "abort" status at any point
+in the protocol.
+------------------------
+packet: git< status=abort
+packet: git< 0000
+------------------------
+
+Git neither stops nor restarts the filter process in case the
+"error"/"abort" status is set. However, Git sets its exit code
+according to the `filter.<driver>.required` flag, mimicking the
+behavior of the `filter.<driver>.clean` / `filter.<driver>.smudge`
+mechanism.
+
+If the filter dies during the communication or does not adhere to
+the protocol then Git will stop the filter process and restart it
+with the next file that needs to be processed. Depending on the
+`filter.<driver>.required` flag Git will interpret that as error.
+
+After the filter has processed a blob it is expected to wait for
+the next "key=value" list containing a command. Git will close
+the command pipe on exit. The filter is expected to detect EOF
+and exit gracefully on its own. Git will wait until the filter
+process has stopped.
+
+A long running filter demo implementation can be found in
+`contrib/long-running-filter/example.pl` located in the Git
+core repository. If you develop your own long running filter
+process then the `GIT_TRACE_PACKET` environment variables can be
+very helpful for debugging (see linkgit:git[1]).
+
+Please note that you cannot use an existing `filter.<driver>.clean`
+or `filter.<driver>.smudge` command with `filter.<driver>.process`
+because the former two use a different inter process communication
+protocol than the latter one.
+
+
Interaction between checkin/checkout attributes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
submodule.<name>.branch::
A remote branch name for tracking updates in the upstream submodule.
- If the option is not specified, it defaults to 'master'. See the
- `--remote` documentation in linkgit:git-submodule[1] for details.
+ If the option is not specified, it defaults to 'master'. A special
+ value of `.` is used to indicate that the name of the branch in the
+ submodule should be the same name as the current branch in the
+ current repository. See the `--remote` documentation in
+ linkgit:git-submodule[1] for details.
submodule.<name>.fetchRecurseSubmodules::
This option can be used to control recursive fetching of this
this:
---o---o---o---M---x---x---W
- /
+ /
---A---B
where A and B are on the side development that was not so good, M is the
may look like this:
---o---o---o---M---x---x---W---x
- /
+ /
---A---B-------------------C---D
where C and D are to fix what was broken in A and B, and you may already
which would make the history look like this:
---o---o---o---M---x---x---W---x---Y
- /
+ /
---A---B-------------------C---D
where Y is the revert of W. Such a "revert of the revert" can be done
changed) be equivalent to not having W or Y at all in the history:
---o---o---o---M---x---x-------x----
- /
+ /
---A---B-------------------C---D
and merging the side branch again will not have conflict arising from an
earlier revert and revert of the revert.
---o---o---o---M---x---x-------x-------*
- / /
+ / /
---A---B-------------------C---D
Of course the changes made in C and D still can conflict with what was
after the revert, the history would have looked like this:
---o---o---o---M---x---x---W---x---x
- / \
+ / \
---A---B A'--B'--C'
If you reverted the revert in such a case as in the previous example:
---o---o---o---M---x---x---W---x---x---Y---*
- / \ /
+ / \ /
---A---B A'--B'--C'
where Y is the revert of W, A' and B' are rerolled A and B, and there may
of revert" blindly without thinking..
---o---o---o---M---x---x---W---x---x
- / \
+ / \
---A---B A'--B'--C'
In the history with rebased side branch, W (and M) are behind the merge
- '%N': commit notes
endif::git-rev-list[]
- '%GG': raw verification message from GPG for a signed commit
-- '%G?': show "G" for a good (valid) signature, "B" for a bad signature,
- "U" for a good signature with unknown validity and "N" for no signature
+- '%G?': show "G" for a good (valid) signature,
+ "B" for a bad signature,
+ "U" for a good signature with unknown validity,
+ "X" for a good signature that has expired,
+ "Y" for a good signature made by an expired key,
+ "R" for a good signature made by a revoked key,
+ "E" if the signature cannot be checked (e.g. missing key)
+ and "N" for no signature
- '%GS': show the name of the signer for a signed commit
- '%GK': show the key used to sign a signed commit
- '%gD': reflog selector, e.g., `refs/stash@{1}` or
int is_not_gitdir = !startup_info->have_repository;
if (state->apply_with_reject && state->threeway)
- return error("--reject and --3way cannot be used together.");
+ return error(_("--reject and --3way cannot be used together."));
if (state->cached && state->threeway)
- return error("--cached and --3way cannot be used together.");
+ return error(_("--cached and --3way cannot be used together."));
if (state->threeway) {
if (is_not_gitdir)
return error(_("--3way outside a repository"));
patch->new_name = xstrdup(patch->def_name);
}
if (!patch->is_delete && !patch->new_name) {
- error("git diff header lacks filename information "
- "(line %d)", state->linenr);
+ error(_("git diff header lacks filename information "
+ "(line %d)"), state->linenr);
return -128;
}
patch->is_toplevel_relative = 1;
/* Binary patch is irreversible without the optional second hunk */
if (state->apply_in_reverse) {
if (!fragment->next)
- return error("cannot reverse-apply a binary patch "
- "without the reverse hunk to '%s'",
+ return error(_("cannot reverse-apply a binary patch "
+ "without the reverse hunk to '%s'"),
patch->new_name
? patch->new_name : patch->old_name);
fragment = fragment->next;
strlen(patch->new_sha1_prefix) != 40 ||
get_oid_hex(patch->old_sha1_prefix, &oid) ||
get_oid_hex(patch->new_sha1_prefix, &oid))
- return error("cannot apply binary patch to '%s' "
- "without full index line", name);
+ return error(_("cannot apply binary patch to '%s' "
+ "without full index line"), name);
if (patch->old_name) {
/*
*/
hash_sha1_file(img->buf, img->len, blob_type, oid.hash);
if (strcmp(oid_to_hex(&oid), patch->old_sha1_prefix))
- return error("the patch applies to '%s' (%s), "
- "which does not match the "
- "current contents.",
+ return error(_("the patch applies to '%s' (%s), "
+ "which does not match the "
+ "current contents."),
name, oid_to_hex(&oid));
}
else {
/* Otherwise, the old one must be empty. */
if (img->len)
- return error("the patch applies to an empty "
- "'%s' but it is not empty", name);
+ return error(_("the patch applies to an empty "
+ "'%s' but it is not empty"), name);
}
get_oid_hex(patch->new_sha1_prefix, &oid);
result = read_sha1_file(oid.hash, &type, &size);
if (!result)
- return error("the necessary postimage %s for "
- "'%s' cannot be read",
+ return error(_("the necessary postimage %s for "
+ "'%s' cannot be read"),
patch->new_sha1_prefix, name);
clear_image(img);
img->buf = result;
write_sha1_file("", 0, blob_type, pre_oid.hash);
else if (get_sha1(patch->old_sha1_prefix, pre_oid.hash) ||
read_blob_object(&buf, &pre_oid, patch->old_mode))
- return error("repository lacks the necessary blob to fall back on 3-way merge.");
+ return error(_("repository lacks the necessary blob to fall back on 3-way merge."));
if (state->apply_verbosity > verbosity_silent)
- fprintf(stderr, "Falling back to three-way merge...\n");
+ fprintf(stderr, _("Falling back to three-way merge...\n"));
img = strbuf_detach(&buf, &len);
prepare_image(&tmp_image, img, len, 1);
/* our_oid is ours */
if (patch->is_new) {
if (load_current(state, &tmp_image, patch))
- return error("cannot read the current contents of '%s'",
+ return error(_("cannot read the current contents of '%s'"),
patch->new_name);
} else {
if (load_preimage(state, &tmp_image, patch, st, ce))
- return error("cannot read the current contents of '%s'",
+ return error(_("cannot read the current contents of '%s'"),
patch->old_name);
}
write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_oid.hash);
if (status < 0) {
if (state->apply_verbosity > verbosity_silent)
fprintf(stderr,
- "Failed to fall back on three-way merge...\n");
+ _("Failed to fall back on three-way merge...\n"));
return status;
}
oidcpy(&patch->threeway_stage[2], &post_oid);
if (state->apply_verbosity > verbosity_silent)
fprintf(stderr,
- "Applied patch to '%s' with conflicts.\n",
+ _("Applied patch to '%s' with conflicts.\n"),
patch->new_name);
} else {
if (state->apply_verbosity > verbosity_silent)
fprintf(stderr,
- "Applied patch to '%s' cleanly.\n",
+ _("Applied patch to '%s' cleanly.\n"),
patch->new_name);
}
return 0;
if (!preimage_oid_in_gitlink_patch(patch, &oid))
; /* ok, the textual part looks sane */
else
- return error("sha1 information is lacking or "
- "useless for submodule %s", name);
+ return error(_("sha1 information is lacking or "
+ "useless for submodule %s"), name);
} else if (!get_sha1_blob(patch->old_sha1_prefix, oid.hash)) {
; /* ok */
} else if (!patch->lines_added && !patch->lines_deleted) {
/* mode-only change: update the current */
if (get_current_oid(state, patch->old_name, &oid))
- return error("mode change for %s, which is not "
- "in current HEAD", name);
+ return error(_("mode change for %s, which is not "
+ "in current HEAD"), name);
} else
- return error("sha1 information is lacking or useless "
- "(%s).", name);
+ return error(_("sha1 information is lacking or useless "
+ "(%s)."), name);
ce = make_cache_entry(patch->old_mode, oid.hash, name, 0, 0);
if (!ce)
name);
if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD)) {
free(ce);
- return error("Could not add %s to temporary index",
+ return error(_("could not add %s to temporary index"),
name);
}
}
discard_index(&result);
if (res)
- return error("Could not write temporary index to %s",
+ return error(_("could not write temporary index to %s"),
state->fake_ancestor);
return 0;
goto end;
}
if (state->applied_after_fixing_ws && state->apply)
- warning("%d line%s applied after"
- " fixing whitespace errors.",
- state->applied_after_fixing_ws,
- state->applied_after_fixing_ws == 1 ? "" : "s");
+ warning(Q_("%d line applied after"
+ " fixing whitespace errors.",
+ "%d lines applied after"
+ " fixing whitespace errors.",
+ state->applied_after_fixing_ws),
+ state->applied_after_fixing_ws);
else if (state->whitespace_error)
warning(Q_("%d line adds whitespace errors.",
"%d lines add whitespace errors.",
debug_push(elem);
}
- elem = read_attr_from_file(git_path_info_attributes(), 1);
+ if (startup_info->have_repository)
+ elem = read_attr_from_file(git_path_info_attributes(), 1);
+ else
+ elem = NULL;
+
if (!elem)
elem = xcalloc(1, sizeof(*elem));
elem->origin = NULL;
if (name_hint) {
const char *format = archive_format_from_filename(name_hint);
if (format)
- packet_write(fd[1], "argument --format=%s\n", format);
+ packet_write_fmt(fd[1], "argument --format=%s\n", format);
}
for (i = 1; i < argc; i++)
- packet_write(fd[1], "argument %s\n", argv[i]);
+ packet_write_fmt(fd[1], "argument %s\n", argv[i]);
packet_flush(fd[1]);
buf = packet_read_line(fd[0], NULL);
whence = FROM_MERGE;
else if (file_exists(git_path_cherry_pick_head())) {
whence = FROM_CHERRY_PICK;
- if (file_exists(git_path(SEQ_DIR)))
+ if (file_exists(git_path_seq_dir()))
sequencer_in_use = 1;
}
else
if (amend)
parent = "HEAD^1";
- if (get_sha1(parent, sha1))
- commitable = !!active_nr;
- else {
+ if (get_sha1(parent, sha1)) {
+ int i, ita_nr = 0;
+
+ for (i = 0; i < active_nr; i++)
+ if (ce_intent_to_add(active_cache[i]))
+ ita_nr++;
+ commitable = active_nr - ita_nr > 0;
+ } else {
/*
* Unless the user did explicitly request a submodule
* ignore mode by passing a command line option we do
if (ignore_submodule_arg &&
!strcmp(ignore_submodule_arg, "all"))
diff_flags |= DIFF_OPT_IGNORE_SUBMODULES;
- commitable = index_differs_from(parent, diff_flags);
+ commitable = index_differs_from(parent, diff_flags, 1);
}
}
strbuf_release(&committer_ident);
* as one to ignore by setting util to NULL.
*/
if (ends_with(ref->name, "^{}")) {
- if (item && !has_object_file(&ref->old_oid) &&
+ if (item &&
+ !has_object_file_with_flags(&ref->old_oid, HAS_SHA1_QUICK) &&
!will_fetch(head, ref->old_oid.hash) &&
- !has_sha1_file(item->util) &&
+ !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) &&
!will_fetch(head, item->util))
item->util = NULL;
item = NULL;
* to check if it is a lightweight tag that we want to
* fetch.
*/
- if (item && !has_sha1_file(item->util) &&
+ if (item &&
+ !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) &&
!will_fetch(head, item->util))
item->util = NULL;
* We may have a final lightweight tag that needs to be
* checked to see if it needs fetching.
*/
- if (item && !has_sha1_file(item->util) &&
+ if (item &&
+ !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) &&
!will_fetch(head, item->util))
item->util = NULL;
static void format_display(struct strbuf *display, char code,
const char *summary, const char *error,
- const char *remote, const char *local)
+ const char *remote, const char *local,
+ int summary_width)
{
- strbuf_addf(display, "%c %-*s ", code, TRANSPORT_SUMMARY(summary));
+ int width = (summary_width + strlen(summary) - gettext_width(summary));
+
+ strbuf_addf(display, "%c %-*s ", code, width, summary);
if (!compact_format)
print_remote_to_local(display, remote, local);
else
static int update_local_ref(struct ref *ref,
const char *remote,
const struct ref *remote_ref,
- struct strbuf *display)
+ struct strbuf *display,
+ int summary_width)
{
struct commit *current = NULL, *updated;
enum object_type type;
if (!oidcmp(&ref->old_oid, &ref->new_oid)) {
if (verbosity > 0)
format_display(display, '=', _("[up to date]"), NULL,
- remote, pretty_ref);
+ remote, pretty_ref, summary_width);
return 0;
}
*/
format_display(display, '!', _("[rejected]"),
_("can't fetch in current branch"),
- remote, pretty_ref);
+ remote, pretty_ref, summary_width);
return 1;
}
r = s_update_ref("updating tag", ref, 0);
format_display(display, r ? '!' : 't', _("[tag update]"),
r ? _("unable to update local ref") : NULL,
- remote, pretty_ref);
+ remote, pretty_ref, summary_width);
return r;
}
r = s_update_ref(msg, ref, 0);
format_display(display, r ? '!' : '*', what,
r ? _("unable to update local ref") : NULL,
- remote, pretty_ref);
+ remote, pretty_ref, summary_width);
return r;
}
r = s_update_ref("fast-forward", ref, 1);
format_display(display, r ? '!' : ' ', quickref.buf,
r ? _("unable to update local ref") : NULL,
- remote, pretty_ref);
+ remote, pretty_ref, summary_width);
strbuf_release(&quickref);
return r;
} else if (force || ref->force) {
r = s_update_ref("forced-update", ref, 1);
format_display(display, r ? '!' : '+', quickref.buf,
r ? _("unable to update local ref") : _("forced update"),
- remote, pretty_ref);
+ remote, pretty_ref, summary_width);
strbuf_release(&quickref);
return r;
} else {
format_display(display, '!', _("[rejected]"), _("non-fast-forward"),
- remote, pretty_ref);
+ remote, pretty_ref, summary_width);
return 1;
}
}
char *url;
const char *filename = dry_run ? "/dev/null" : git_path_fetch_head();
int want_status;
+ int summary_width = transport_summary_width(ref_map);
fp = fopen(filename, "a");
if (!fp)
strbuf_reset(¬e);
if (ref) {
- rc |= update_local_ref(ref, what, rm, ¬e);
+ rc |= update_local_ref(ref, what, rm, ¬e,
+ summary_width);
free(ref);
} else
format_display(¬e, '*',
*kind ? kind : "branch", NULL,
*what ? what : "HEAD",
- "FETCH_HEAD");
+ "FETCH_HEAD", summary_width);
if (note.len) {
if (verbosity >= 0 && !shown_url) {
fprintf(stderr, _("From %.*s\n"),
int url_len, i, result = 0;
struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);
char *url;
+ int summary_width = transport_summary_width(stale_refs);
const char *dangling_msg = dry_run
? _(" (%s will become dangling)")
: _(" (%s has become dangling)");
shown_url = 1;
}
format_display(&sb, '-', _("[deleted]"), NULL,
- _("(none)"), prettify_refname(ref->name));
+ _("(none)"), prettify_refname(ref->name),
+ summary_width);
fprintf(stderr, " %s\n",sb.buf);
strbuf_release(&sb);
warn_dangling_symref(stderr, dangling_msg, ref->name);
#include "resolve-undo.h"
#include "string-list.h"
#include "pathspec.h"
+#include "run-command.h"
static int abbrev;
static int show_deleted;
static int line_terminator = '\n';
static int debug_mode;
static int show_eol;
+static int recurse_submodules;
+static struct argv_array submodules_options = ARGV_ARRAY_INIT;
static const char *prefix;
+static const char *super_prefix;
static int max_prefix_len;
static int prefix_len;
static struct pathspec pathspec;
static void write_name(const char *name)
{
+ /*
+ * Prepend the super_prefix to name to construct the full_name to be
+ * written.
+ */
+ struct strbuf full_name = STRBUF_INIT;
+ if (super_prefix) {
+ strbuf_addstr(&full_name, super_prefix);
+ strbuf_addstr(&full_name, name);
+ name = full_name.buf;
+ }
+
/*
* With "--full-name", prefix_len=0; this caller needs to pass
* an empty string in that case (a NULL is good for "").
*/
write_name_quoted_relative(name, prefix_len ? prefix : NULL,
stdout, line_terminator);
+
+ strbuf_release(&full_name);
}
static void show_dir_entry(const char *tag, struct dir_entry *ent)
}
}
+/*
+ * Compile an argv_array with all of the options supported by --recurse_submodules
+ */
+static void compile_submodule_options(const struct dir_struct *dir, int show_tag)
+{
+ if (line_terminator == '\0')
+ argv_array_push(&submodules_options, "-z");
+ if (show_tag)
+ argv_array_push(&submodules_options, "-t");
+ if (show_valid_bit)
+ argv_array_push(&submodules_options, "-v");
+ if (show_cached)
+ argv_array_push(&submodules_options, "--cached");
+ if (show_eol)
+ argv_array_push(&submodules_options, "--eol");
+ if (debug_mode)
+ argv_array_push(&submodules_options, "--debug");
+}
+
+/**
+ * Recursively call ls-files on a submodule
+ */
+static void show_gitlink(const struct cache_entry *ce)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ int status;
+ int i;
+
+ argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
+ super_prefix ? super_prefix : "",
+ ce->name);
+ argv_array_push(&cp.args, "ls-files");
+ argv_array_push(&cp.args, "--recurse-submodules");
+
+ /* add supported options */
+ argv_array_pushv(&cp.args, submodules_options.argv);
+
+ /*
+ * Pass in the original pathspec args. The submodule will be
+ * responsible for prepending the 'submodule_prefix' prior to comparing
+ * against the pathspec for matches.
+ */
+ argv_array_push(&cp.args, "--");
+ for (i = 0; i < pathspec.nr; i++)
+ argv_array_push(&cp.args, pathspec.items[i].original);
+
+ cp.git_cmd = 1;
+ cp.dir = ce->name;
+ status = run_command(&cp);
+ if (status)
+ exit(status);
+}
+
static void show_ce_entry(const char *tag, const struct cache_entry *ce)
{
+ struct strbuf name = STRBUF_INIT;
int len = max_prefix_len;
+ if (super_prefix)
+ strbuf_addstr(&name, super_prefix);
+ strbuf_addstr(&name, ce->name);
if (len >= ce_namelen(ce))
die("git ls-files: internal error - cache entry not superset of prefix");
- if (!match_pathspec(&pathspec, ce->name, ce_namelen(ce),
- len, ps_matched,
- S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode)))
- return;
+ if (recurse_submodules && S_ISGITLINK(ce->ce_mode) &&
+ submodule_path_match(&pathspec, name.buf, ps_matched)) {
+ show_gitlink(ce);
+ } else if (match_pathspec(&pathspec, name.buf, name.len,
+ len, ps_matched,
+ S_ISDIR(ce->ce_mode) ||
+ S_ISGITLINK(ce->ce_mode))) {
+ if (tag && *tag && show_valid_bit &&
+ (ce->ce_flags & CE_VALID)) {
+ static char alttag[4];
+ memcpy(alttag, tag, 3);
+ if (isalpha(tag[0]))
+ alttag[0] = tolower(tag[0]);
+ else if (tag[0] == '?')
+ alttag[0] = '!';
+ else {
+ alttag[0] = 'v';
+ alttag[1] = tag[0];
+ alttag[2] = ' ';
+ alttag[3] = 0;
+ }
+ tag = alttag;
+ }
- if (tag && *tag && show_valid_bit &&
- (ce->ce_flags & CE_VALID)) {
- static char alttag[4];
- memcpy(alttag, tag, 3);
- if (isalpha(tag[0]))
- alttag[0] = tolower(tag[0]);
- else if (tag[0] == '?')
- alttag[0] = '!';
- else {
- alttag[0] = 'v';
- alttag[1] = tag[0];
- alttag[2] = ' ';
- alttag[3] = 0;
+ if (!show_stage) {
+ fputs(tag, stdout);
+ } else {
+ printf("%s%06o %s %d\t",
+ tag,
+ ce->ce_mode,
+ find_unique_abbrev(ce->oid.hash, abbrev),
+ ce_stage(ce));
+ }
+ write_eolinfo(ce, ce->name);
+ write_name(ce->name);
+ if (debug_mode) {
+ const struct stat_data *sd = &ce->ce_stat_data;
+
+ printf(" ctime: %d:%d\n", sd->sd_ctime.sec, sd->sd_ctime.nsec);
+ printf(" mtime: %d:%d\n", sd->sd_mtime.sec, sd->sd_mtime.nsec);
+ printf(" dev: %d\tino: %d\n", sd->sd_dev, sd->sd_ino);
+ printf(" uid: %d\tgid: %d\n", sd->sd_uid, sd->sd_gid);
+ printf(" size: %d\tflags: %x\n", sd->sd_size, ce->ce_flags);
}
- tag = alttag;
}
- if (!show_stage) {
- fputs(tag, stdout);
- } else {
- printf("%s%06o %s %d\t",
- tag,
- ce->ce_mode,
- find_unique_abbrev(ce->oid.hash,abbrev),
- ce_stage(ce));
- }
- write_eolinfo(ce, ce->name);
- write_name(ce->name);
- if (debug_mode) {
- const struct stat_data *sd = &ce->ce_stat_data;
-
- printf(" ctime: %d:%d\n", sd->sd_ctime.sec, sd->sd_ctime.nsec);
- printf(" mtime: %d:%d\n", sd->sd_mtime.sec, sd->sd_mtime.nsec);
- printf(" dev: %d\tino: %d\n", sd->sd_dev, sd->sd_ino);
- printf(" uid: %d\tgid: %d\n", sd->sd_uid, sd->sd_gid);
- printf(" size: %d\tflags: %x\n", sd->sd_size, ce->ce_flags);
- }
+ strbuf_release(&name);
}
static void show_ru_info(void)
{ OPTION_SET_INT, 0, "full-name", &prefix_len, NULL,
N_("make the output relative to the project top directory"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
+ OPT_BOOL(0, "recurse-submodules", &recurse_submodules,
+ N_("recurse through submodules")),
OPT_BOOL(0, "error-unmatch", &error_unmatch,
N_("if any <file> is not in the index, treat this as an error")),
OPT_STRING(0, "with-tree", &with_tree, N_("tree-ish"),
prefix = cmd_prefix;
if (prefix)
prefix_len = strlen(prefix);
+ super_prefix = get_super_prefix();
git_config(git_default_config, NULL);
if (read_cache() < 0)
if (require_work_tree && !is_inside_work_tree())
setup_work_tree();
+ if (recurse_submodules)
+ compile_submodule_options(&dir, show_tag);
+
+ if (recurse_submodules &&
+ (show_stage || show_deleted || show_others || show_unmerged ||
+ show_killed || show_modified || show_resolve_undo || with_tree))
+ die("ls-files --recurse-submodules unsupported mode");
+
+ if (recurse_submodules && error_unmatch)
+ die("ls-files --recurse-submodules does not support "
+ "--error-unmatch");
+
parse_pathspec(&pathspec, 0,
PATHSPEC_PREFER_CWD |
PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
prefix, argv);
- /* Find common prefix for all pathspec's */
- max_prefix = common_prefix(&pathspec);
+ /*
+ * Find common prefix for all pathspec's
+ * This is used as a performance optimization which unfortunately cannot
+ * be done when recursing into submodules
+ */
+ if (recurse_submodules)
+ max_prefix = NULL;
+ else
+ max_prefix = common_prefix(&pathspec);
max_prefix_len = max_prefix ? strlen(max_prefix) : 0;
/* Treat unmatching pathspec elements as errors */
revs.initial = 1;
for_each_reflog_ent(refname, collect_one_reflog_ent, &revs);
+ if (!revs.nr && !get_sha1(refname, sha1))
+ add_one_commit(sha1, &revs);
+
for (i = 0; i < revs.nr; i++)
revs.commit[i]->object.flags &= ~TMP_MARK;
struct commit *commit;
if (verbosity >= 0) {
- char from[GIT_SHA1_HEXSZ + 1], to[GIT_SHA1_HEXSZ + 1];
- find_unique_abbrev_r(from, head_commit->object.oid.hash,
- DEFAULT_ABBREV);
- find_unique_abbrev_r(to, remoteheads->item->object.oid.hash,
- DEFAULT_ABBREV);
- printf(_("Updating %s..%s\n"), from, to);
+ printf(_("Updating %s..%s\n"),
+ find_unique_abbrev(head_commit->object.oid.hash,
+ DEFAULT_ABBREV),
+ find_unique_abbrev(remoteheads->item->object.oid.hash,
+ DEFAULT_ABBREV));
}
strbuf_addstr(&msg, "Fast-forward");
if (have_message)
if (!is_pack_valid(reuse_packfile))
die("packfile is invalid: %s", reuse_packfile->pack_name);
- fd = git_open_noatime(reuse_packfile->pack_name);
+ fd = git_open(reuse_packfile->pack_name);
if (fd < 0)
die_errno("unable to open packfile for reuse: %s",
reuse_packfile->pack_name);
#include "revision.h"
#include "tempfile.h"
#include "lockfile.h"
+#include "wt-status.h"
enum rebase_type {
REBASE_INVALID = -1,
return git_default_config(var, value, cb);
}
-/**
- * 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.
die(_("Updating an unborn branch with changes added to the index."));
if (!autostash)
- die_on_unclean_work_tree(prefix);
+ require_clean_work_tree(N_("pull with rebase"),
+ _("please commit or stash them."), 1, 0);
if (get_rebase_fork_point(rebase_fork_point, repo, *refspecs))
hashclr(rebase_fork_point);
static void show_ref(const char *path, const unsigned char *sha1)
{
if (sent_capabilities) {
- packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
+ packet_write_fmt(1, "%s %s\n", sha1_to_hex(sha1), path);
} else {
struct strbuf cap = STRBUF_INIT;
if (advertise_push_options)
strbuf_addstr(&cap, " push-options");
strbuf_addf(&cap, " agent=%s", git_user_agent_sanitized());
- packet_write(1, "%s %s%c%s\n",
+ packet_write_fmt(1, "%s %s%c%s\n",
sha1_to_hex(sha1), path, 0, cap.buf);
strbuf_release(&cap);
sent_capabilities = 1;
struct string_list_item *item;
struct command *dst_cmd;
unsigned char sha1[GIT_SHA1_RAWSZ];
- char cmd_oldh[GIT_SHA1_HEXSZ + 1],
- cmd_newh[GIT_SHA1_HEXSZ + 1],
- dst_oldh[GIT_SHA1_HEXSZ + 1],
- dst_newh[GIT_SHA1_HEXSZ + 1];
int flag;
strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
dst_cmd->skip_update = 1;
- find_unique_abbrev_r(cmd_oldh, cmd->old_sha1, DEFAULT_ABBREV);
- find_unique_abbrev_r(cmd_newh, cmd->new_sha1, DEFAULT_ABBREV);
- find_unique_abbrev_r(dst_oldh, dst_cmd->old_sha1, DEFAULT_ABBREV);
- find_unique_abbrev_r(dst_newh, dst_cmd->new_sha1, DEFAULT_ABBREV);
rp_error("refusing inconsistent update between symref '%s' (%s..%s) and"
" its target '%s' (%s..%s)",
- cmd->ref_name, cmd_oldh, cmd_newh,
- dst_cmd->ref_name, dst_oldh, dst_newh);
+ cmd->ref_name,
+ find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV),
+ find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV),
+ dst_cmd->ref_name,
+ find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV),
+ find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));
cmd->error_string = dst_cmd->error_string =
"inconsistent aliased update";
const char *vhost)
{
if (!vhost)
- packet_write(stdin_fd, "%s %s%c", serv, repo, 0);
+ packet_write_fmt(stdin_fd, "%s %s%c", serv, repo, 0);
else
- packet_write(stdin_fd, "%s %s%chost=%s%c", serv, repo, 0,
+ packet_write_fmt(stdin_fd, "%s %s%chost=%s%c", serv, repo, 0,
vhost, 0);
}
*/
if (buf.len && buf.buf[buf.len - 1] == '\n')
graph_show_padding(revs->graph);
- putchar('\n');
+ putchar(info->hdr_termination);
} else {
/*
* If the message buffer is empty, just show
filter &= ~(DO_FLAGS|DO_NOREV);
verify = 1;
abbrev = DEFAULT_ABBREV;
- if (arg[7] == '=')
- abbrev = strtoul(arg + 8, NULL, 10);
+ if (!arg[7])
+ continue;
+ abbrev = strtoul(arg + 8, NULL, 10);
if (abbrev < MINIMUM_ABBREV)
abbrev = MINIMUM_ABBREV;
else if (40 <= abbrev)
die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt);
}
-static void parse_args(int argc, const char **argv, struct replay_opts *opts)
+static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
{
const char * const * usage_str = revert_or_cherry_pick_usage(opts);
const char *me = action_name(opts);
if (opts->keep_redundant_commits)
opts->allow_empty = 1;
- /* Set the subcommand */
- if (cmd == 'q')
- opts->subcommand = REPLAY_REMOVE_STATE;
- else if (cmd == 'c')
- opts->subcommand = REPLAY_CONTINUE;
- else if (cmd == 'a')
- opts->subcommand = REPLAY_ROLLBACK;
- else
- opts->subcommand = REPLAY_NONE;
-
/* Check for incompatible command line arguments */
- if (opts->subcommand != REPLAY_NONE) {
+ if (cmd) {
char *this_operation;
- if (opts->subcommand == REPLAY_REMOVE_STATE)
+ if (cmd == 'q')
this_operation = "--quit";
- else if (opts->subcommand == REPLAY_CONTINUE)
+ else if (cmd == 'c')
this_operation = "--continue";
else {
- assert(opts->subcommand == REPLAY_ROLLBACK);
+ assert(cmd == 'a');
this_operation = "--abort";
}
"--edit", opts->edit,
NULL);
- if (opts->subcommand != REPLAY_NONE) {
+ if (cmd) {
opts->revs = NULL;
} else {
struct setup_revision_opt s_r_opt;
if (argc > 1)
usage_with_options(usage_str, options);
+
+ /* These option values will be free()d */
+ opts->gpg_sign = xstrdup_or_null(opts->gpg_sign);
+ opts->strategy = xstrdup_or_null(opts->strategy);
+
+ if (cmd == 'q')
+ return sequencer_remove_state(opts);
+ if (cmd == 'c')
+ return sequencer_continue(opts);
+ if (cmd == 'a')
+ return sequencer_rollback(opts);
+ return sequencer_pick_revisions(opts);
}
int cmd_revert(int argc, const char **argv, const char *prefix)
{
- struct replay_opts opts;
+ struct replay_opts opts = REPLAY_OPTS_INIT;
int res;
- memset(&opts, 0, sizeof(opts));
if (isatty(0))
opts.edit = 1;
opts.action = REPLAY_REVERT;
git_config(git_default_config, NULL);
- parse_args(argc, argv, &opts);
- res = sequencer_pick_revisions(&opts);
+ res = run_sequencer(argc, argv, &opts);
if (res < 0)
die(_("revert failed"));
return res;
int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
{
- struct replay_opts opts;
+ struct replay_opts opts = REPLAY_OPTS_INIT;
int res;
- memset(&opts, 0, sizeof(opts));
opts.action = REPLAY_PICK;
git_config(git_default_config, NULL);
- parse_args(argc, argv, &opts);
- res = sequencer_pick_revisions(&opts);
+ res = run_sequencer(argc, argv, &opts);
if (res < 0)
die(_("cherry-pick failed"));
return res;
* NEEDSWORK: This works incorrectly on the domain and protocol part.
* remote_url url outcome expectation
* http://a.com/b ../c http://a.com/c as is
+ * http://a.com/b/ ../c http://a.com/c same as previous line, but
+ * ignore trailing slash in url
* http://a.com/b ../../c http://c error out
* http://a.com/b ../../../c http:/c error out
* http://a.com/b ../../../../c http:c error out
struct strbuf sb = STRBUF_INIT;
size_t len = strlen(remoteurl);
- if (is_dir_sep(remoteurl[len]))
- remoteurl[len] = '\0';
+ if (is_dir_sep(remoteurl[len-1]))
+ remoteurl[len-1] = '\0';
if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl))
is_relative = 0;
}
strbuf_reset(&sb);
strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url);
+ if (ends_with(url, "/"))
+ strbuf_setlen(&sb, sb.len - 1);
free(remoteurl);
if (starts_with_dot_slash(sb.buf))
writer.git_cmd = 1;
if (start_command(&writer)) {
int err = errno;
- packet_write(1, "NACK unable to spawn subprocess\n");
+ packet_write_fmt(1, "NACK unable to spawn subprocess\n");
die("upload-archive: %s", strerror(err));
}
- packet_write(1, "ACK\n");
+ packet_write_fmt(1, "ACK\n");
packet_flush(1);
while (1) {
#define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
#define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
+#define GIT_SUPER_PREFIX_ENVIRONMENT "GIT_INTERNAL_SUPER_PREFIX"
#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
#define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
#define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
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_super_prefix(void);
extern const char *get_git_work_tree(void);
/*
* The result will be at least `len` characters long, and will be NUL
* terminated.
*
- * The non-`_r` version returns a static buffer which will be overwritten by
- * subsequent calls.
+ * The non-`_r` version returns a static buffer which remains valid until 4
+ * more calls to find_unique_abbrev are made.
*
* The `_r` variant writes to a buffer supplied by the caller, which must be at
* least `GIT_SHA1_HEXSZ + 1` bytes. The return value is the number of bytes
extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, unsigned char *sha1, unsigned flags);
extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
extern int force_object_loose(const unsigned char *sha1, time_t mtime);
-extern int git_open_noatime(const char *name);
+extern int git_open(const char *name);
extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
/* Same as the above, except for struct object_id. */
extern int has_object_file(const struct object_id *oid);
+extern int has_object_file_with_flags(const struct object_id *oid, int flags);
/*
* Return true iff an alternate object database has a loose object
#define MINIMUM_ABBREV minimum_abbrev
#define DEFAULT_ABBREV default_abbrev
+/* used when the code does not know or care what the default abbrev is */
+#define FALLBACK_DEFAULT_ABBREV 7
+
struct object_context {
unsigned char tree[20];
char path[PATH_MAX];
extern void reprepare_packed_git(void);
extern void install_packed_git(struct packed_git *pack);
+/*
+ * Give a rough count of objects in the repository. This sacrifices accuracy
+ * for speed.
+ */
+unsigned long approximate_object_count(void);
+
extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
struct packed_git *packs);
/* Show sha1's */
for (i = 0; i < num_parent; i++)
- printf(" %s", diff_unique_abbrev(p->parent[i].oid.hash,
- opt->abbrev));
- printf(" %s ", diff_unique_abbrev(p->oid.hash, opt->abbrev));
+ printf(" %s", diff_aligned_abbrev(&p->parent[i].oid,
+ opt->abbrev));
+ printf(" %s ", diff_aligned_abbrev(&p->oid, opt->abbrev));
}
if (opt->output_format & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS)) {
* Note: Do not add any other headers here! Doing so
* will cause older git-daemon servers to crash.
*/
- packet_write(fd[1],
+ packet_write_fmt(fd[1],
"%s %s%chost=%s%c",
prog, path, 0,
target_host, 0);
--- /dev/null
+@@
+expression E;
+expression V;
+@@
+- if (E)
+- V = xstrdup(E);
++ V = xstrdup_or_null(E);
--- /dev/null
+MAIN:=git-credential-libsecret
+all:: $(MAIN)
+
+CC = gcc
+RM = rm -f
+CFLAGS = -g -O2 -Wall
+PKG_CONFIG = pkg-config
+
+-include ../../../config.mak.autogen
+-include ../../../config.mak
+
+INCS:=$(shell $(PKG_CONFIG) --cflags libsecret-1 glib-2.0)
+LIBS:=$(shell $(PKG_CONFIG) --libs libsecret-1 glib-2.0)
+
+SRCS:=$(MAIN).c
+OBJS:=$(SRCS:.c=.o)
+
+%.o: %.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) $(INCS) -o $@ -c $<
+
+$(MAIN): $(OBJS)
+ $(CC) -o $@ $(LDFLAGS) $^ $(LIBS)
+
+clean:
+ @$(RM) $(MAIN) $(OBJS)
--- /dev/null
+/*
+ * Copyright (C) 2011 John Szakmeister <john@szakmeister.net>
+ * 2012 Philipp A. Hartmann <pah@qo.cx>
+ * 2016 Mantas Mikulėnas <grawity@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Credits:
+ * - GNOME Keyring API handling originally written by John Szakmeister
+ * - ported to credential helper API by Philipp A. Hartmann
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <libsecret/secret.h>
+
+/*
+ * This credential struct and API is simplified from git's credential.{h,c}
+ */
+struct credential {
+ char *protocol;
+ char *host;
+ unsigned short port;
+ char *path;
+ char *username;
+ char *password;
+};
+
+#define CREDENTIAL_INIT { NULL, NULL, 0, NULL, NULL, NULL }
+
+typedef int (*credential_op_cb)(struct credential *);
+
+struct credential_operation {
+ char *name;
+ credential_op_cb op;
+};
+
+#define CREDENTIAL_OP_END { NULL, NULL }
+
+/* ----------------- Secret Service functions ----------------- */
+
+static char *make_label(struct credential *c)
+{
+ if (c->port)
+ return g_strdup_printf("Git: %s://%s:%hu/%s",
+ c->protocol, c->host, c->port, c->path ? c->path : "");
+ else
+ return g_strdup_printf("Git: %s://%s/%s",
+ c->protocol, c->host, c->path ? c->path : "");
+}
+
+static GHashTable *make_attr_list(struct credential *c)
+{
+ GHashTable *al = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
+
+ if (c->username)
+ g_hash_table_insert(al, "user", g_strdup(c->username));
+ if (c->protocol)
+ g_hash_table_insert(al, "protocol", g_strdup(c->protocol));
+ if (c->host)
+ g_hash_table_insert(al, "server", g_strdup(c->host));
+ if (c->port)
+ g_hash_table_insert(al, "port", g_strdup_printf("%hu", c->port));
+ if (c->path)
+ g_hash_table_insert(al, "object", g_strdup(c->path));
+
+ return al;
+}
+
+static int keyring_get(struct credential *c)
+{
+ SecretService *service = NULL;
+ GHashTable *attributes = NULL;
+ GError *error = NULL;
+ GList *items = NULL;
+
+ if (!c->protocol || !(c->host || c->path))
+ return EXIT_FAILURE;
+
+ service = secret_service_get_sync(0, NULL, &error);
+ if (error != NULL) {
+ g_critical("could not connect to Secret Service: %s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ attributes = make_attr_list(c);
+ items = secret_service_search_sync(service,
+ SECRET_SCHEMA_COMPAT_NETWORK,
+ attributes,
+ SECRET_SEARCH_LOAD_SECRETS,
+ NULL,
+ &error);
+ g_hash_table_unref(attributes);
+ if (error != NULL) {
+ g_critical("lookup failed: %s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ if (items != NULL) {
+ SecretItem *item;
+ SecretValue *secret;
+ const char *s;
+
+ item = items->data;
+ secret = secret_item_get_secret(item);
+ attributes = secret_item_get_attributes(item);
+
+ s = g_hash_table_lookup(attributes, "user");
+ if (s) {
+ g_free(c->username);
+ c->username = g_strdup(s);
+ }
+
+ s = secret_value_get_text(secret);
+ if (s) {
+ g_free(c->password);
+ c->password = g_strdup(s);
+ }
+
+ g_hash_table_unref(attributes);
+ secret_value_unref(secret);
+ g_list_free_full(items, g_object_unref);
+ }
+
+ return EXIT_SUCCESS;
+}
+
+
+static int keyring_store(struct credential *c)
+{
+ char *label = NULL;
+ GHashTable *attributes = NULL;
+ GError *error = NULL;
+
+ /*
+ * Sanity check that what we are storing is actually sensible.
+ * In particular, we can't make a URL without a protocol field.
+ * Without either a host or pathname (depending on the scheme),
+ * we have no primary key. And without a username and password,
+ * we are not actually storing a credential.
+ */
+ if (!c->protocol || !(c->host || c->path) ||
+ !c->username || !c->password)
+ return EXIT_FAILURE;
+
+ label = make_label(c);
+ attributes = make_attr_list(c);
+ secret_password_storev_sync(SECRET_SCHEMA_COMPAT_NETWORK,
+ attributes,
+ NULL,
+ label,
+ c->password,
+ NULL,
+ &error);
+ g_free(label);
+ g_hash_table_unref(attributes);
+
+ if (error != NULL) {
+ g_critical("store failed: %s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+static int keyring_erase(struct credential *c)
+{
+ GHashTable *attributes = NULL;
+ GError *error = NULL;
+
+ /*
+ * Sanity check that we actually have something to match
+ * against. The input we get is a restrictive pattern,
+ * so technically a blank credential means "erase everything".
+ * But it is too easy to accidentally send this, since it is equivalent
+ * to empty input. So explicitly disallow it, and require that the
+ * pattern have some actual content to match.
+ */
+ if (!c->protocol && !c->host && !c->path && !c->username)
+ return EXIT_FAILURE;
+
+ attributes = make_attr_list(c);
+ secret_password_clearv_sync(SECRET_SCHEMA_COMPAT_NETWORK,
+ attributes,
+ NULL,
+ &error);
+ g_hash_table_unref(attributes);
+
+ if (error != NULL) {
+ g_critical("erase failed: %s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+/*
+ * Table with helper operation callbacks, used by generic
+ * credential helper main function.
+ */
+static struct credential_operation const credential_helper_ops[] = {
+ { "get", keyring_get },
+ { "store", keyring_store },
+ { "erase", keyring_erase },
+ CREDENTIAL_OP_END
+};
+
+/* ------------------ credential functions ------------------ */
+
+static void credential_init(struct credential *c)
+{
+ memset(c, 0, sizeof(*c));
+}
+
+static void credential_clear(struct credential *c)
+{
+ g_free(c->protocol);
+ g_free(c->host);
+ g_free(c->path);
+ g_free(c->username);
+ g_free(c->password);
+
+ credential_init(c);
+}
+
+static int credential_read(struct credential *c)
+{
+ char *buf;
+ size_t line_len;
+ char *key;
+ char *value;
+
+ key = buf = g_malloc(1024);
+
+ while (fgets(buf, 1024, stdin)) {
+ line_len = strlen(buf);
+
+ if (line_len && buf[line_len-1] == '\n')
+ buf[--line_len] = '\0';
+
+ if (!line_len)
+ break;
+
+ value = strchr(buf, '=');
+ if (!value) {
+ g_warning("invalid credential line: %s", key);
+ g_free(buf);
+ return -1;
+ }
+ *value++ = '\0';
+
+ if (!strcmp(key, "protocol")) {
+ g_free(c->protocol);
+ c->protocol = g_strdup(value);
+ } else if (!strcmp(key, "host")) {
+ g_free(c->host);
+ c->host = g_strdup(value);
+ value = strrchr(c->host, ':');
+ if (value) {
+ *value++ = '\0';
+ c->port = atoi(value);
+ }
+ } else if (!strcmp(key, "path")) {
+ g_free(c->path);
+ c->path = g_strdup(value);
+ } else if (!strcmp(key, "username")) {
+ g_free(c->username);
+ c->username = g_strdup(value);
+ } else if (!strcmp(key, "password")) {
+ g_free(c->password);
+ c->password = g_strdup(value);
+ while (*value)
+ *value++ = '\0';
+ }
+ /*
+ * Ignore other lines; we don't know what they mean, but
+ * this future-proofs us when later versions of git do
+ * learn new lines, and the helpers are updated to match.
+ */
+ }
+
+ g_free(buf);
+
+ return 0;
+}
+
+static void credential_write_item(FILE *fp, const char *key, const char *value)
+{
+ if (!value)
+ return;
+ fprintf(fp, "%s=%s\n", key, value);
+}
+
+static void credential_write(const struct credential *c)
+{
+ /* only write username/password, if set */
+ credential_write_item(stdout, "username", c->username);
+ credential_write_item(stdout, "password", c->password);
+}
+
+static void usage(const char *name)
+{
+ struct credential_operation const *try_op = credential_helper_ops;
+ const char *basename = strrchr(name, '/');
+
+ basename = (basename) ? basename + 1 : name;
+ fprintf(stderr, "usage: %s <", basename);
+ while (try_op->name) {
+ fprintf(stderr, "%s", (try_op++)->name);
+ if (try_op->name)
+ fprintf(stderr, "%s", "|");
+ }
+ fprintf(stderr, "%s", ">\n");
+}
+
+int main(int argc, char *argv[])
+{
+ int ret = EXIT_SUCCESS;
+
+ struct credential_operation const *try_op = credential_helper_ops;
+ struct credential cred = CREDENTIAL_INIT;
+
+ if (!argv[1]) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ g_set_application_name("Git Credential Helper");
+
+ /* lookup operation callback */
+ while (try_op->name && strcmp(argv[1], try_op->name))
+ try_op++;
+
+ /* unsupported operation given -- ignore silently */
+ if (!try_op->name || !try_op->op)
+ goto out;
+
+ ret = credential_read(&cred);
+ if (ret)
+ goto out;
+
+ /* perform credential operation */
+ ret = (*try_op->op)(&cred);
+
+ credential_write(&cred);
+
+out:
+ credential_clear(&cred);
+ return ret;
+}
--- /dev/null
+#!/usr/bin/perl
+#
+# Example implementation for the Git filter protocol version 2
+# See Documentation/gitattributes.txt, section "Filter Protocol"
+#
+# Please note, this pass-thru filter is a minimal skeleton. No proper
+# error handling was implemented.
+#
+
+use strict;
+use warnings;
+
+my $MAX_PACKET_CONTENT_SIZE = 65516;
+
+sub packet_bin_read {
+ my $buffer;
+ my $bytes_read = read STDIN, $buffer, 4;
+ if ( $bytes_read == 0 ) {
+
+ # EOF - Git stopped talking to us!
+ exit();
+ }
+ elsif ( $bytes_read != 4 ) {
+ die "invalid packet: '$buffer'";
+ }
+ my $pkt_size = hex($buffer);
+ if ( $pkt_size == 0 ) {
+ return ( 1, "" );
+ }
+ elsif ( $pkt_size > 4 ) {
+ my $content_size = $pkt_size - 4;
+ $bytes_read = read STDIN, $buffer, $content_size;
+ if ( $bytes_read != $content_size ) {
+ die "invalid packet ($content_size bytes expected; $bytes_read bytes read)";
+ }
+ return ( 0, $buffer );
+ }
+ else {
+ die "invalid packet size: $pkt_size";
+ }
+}
+
+sub packet_txt_read {
+ my ( $res, $buf ) = packet_bin_read();
+ unless ( $buf =~ s/\n$// ) {
+ die "A non-binary line MUST be terminated by an LF.";
+ }
+ return ( $res, $buf );
+}
+
+sub packet_bin_write {
+ my $buf = shift;
+ print STDOUT sprintf( "%04x", length($buf) + 4 );
+ print STDOUT $buf;
+ STDOUT->flush();
+}
+
+sub packet_txt_write {
+ packet_bin_write( $_[0] . "\n" );
+}
+
+sub packet_flush {
+ print STDOUT sprintf( "%04x", 0 );
+ STDOUT->flush();
+}
+
+( packet_txt_read() eq ( 0, "git-filter-client" ) ) || die "bad initialize";
+( packet_txt_read() eq ( 0, "version=2" ) ) || die "bad version";
+( packet_bin_read() eq ( 1, "" ) ) || die "bad version end";
+
+packet_txt_write("git-filter-server");
+packet_txt_write("version=2");
+packet_flush();
+
+( packet_txt_read() eq ( 0, "capability=clean" ) ) || die "bad capability";
+( packet_txt_read() eq ( 0, "capability=smudge" ) ) || die "bad capability";
+( packet_bin_read() eq ( 1, "" ) ) || die "bad capability end";
+
+packet_txt_write("capability=clean");
+packet_txt_write("capability=smudge");
+packet_flush();
+
+while (1) {
+ my ($command) = packet_txt_read() =~ /^command=([^=]+)$/;
+ my ($pathname) = packet_txt_read() =~ /^pathname=([^=]+)$/;
+
+ packet_bin_read();
+
+ my $input = "";
+ {
+ binmode(STDIN);
+ my $buffer;
+ my $done = 0;
+ while ( !$done ) {
+ ( $done, $buffer ) = packet_bin_read();
+ $input .= $buffer;
+ }
+ }
+
+ my $output;
+ if ( $command eq "clean" ) {
+ ### Perform clean here ###
+ $output = $input;
+ }
+ elsif ( $command eq "smudge" ) {
+ ### Perform smudge here ###
+ $output = $input;
+ }
+ else {
+ die "bad command '$command'";
+ }
+
+ packet_txt_write("status=success");
+ packet_flush();
+ while ( length($output) > 0 ) {
+ my $packet = substr( $output, 0, $MAX_PACKET_CONTENT_SIZE );
+ packet_bin_write($packet);
+ if ( length($output) > $MAX_PACKET_CONTENT_SIZE ) {
+ $output = substr( $output, $MAX_PACKET_CONTENT_SIZE );
+ }
+ else {
+ $output = "";
+ }
+ }
+ packet_flush(); # flush content!
+ packet_flush(); # empty list, keep "status=success" unchanged!
+
+}
#include "run-command.h"
#include "quote.h"
#include "sigchain.h"
+#include "pkt-line.h"
/*
* convert.c - convert a file when checking it out and checking it in.
* CRLFs would not be restored by checkout
*/
if (checksafe == SAFE_CRLF_WARN)
- warning("CRLF will be replaced by LF in %s.\nThe file will have its original line endings in your working directory.", path);
+ warning(_("CRLF will be replaced by LF in %s.\n"
+ "The file will have its original line"
+ " endings in your working directory."), path);
else /* i.e. SAFE_CRLF_FAIL */
- die("CRLF would be replaced by LF in %s.", path);
+ die(_("CRLF would be replaced by LF in %s."), path);
} else if (old_stats->lonelf && !new_stats->lonelf ) {
/*
* CRLFs would be added by checkout
*/
if (checksafe == SAFE_CRLF_WARN)
- warning("LF will be replaced by CRLF in %s.\nThe file will have its original line endings in your working directory.", path);
+ warning(_("LF will be replaced by CRLF in %s.\n"
+ "The file will have its original line"
+ " endings in your working directory."), path);
else /* i.e. SAFE_CRLF_FAIL */
- die("LF would be replaced by CRLF in %s", path);
+ die(_("LF would be replaced by CRLF in %s"), path);
}
}
child_process.out = out;
if (start_command(&child_process))
- return error("cannot fork to run external filter %s", params->cmd);
+ return error("cannot fork to run external filter '%s'", params->cmd);
sigchain_push(SIGPIPE, SIG_IGN);
if (close(child_process.in))
write_err = 1;
if (write_err)
- error("cannot feed the input to external filter %s", params->cmd);
+ error("cannot feed the input to external filter '%s'", params->cmd);
sigchain_pop(SIGPIPE);
status = finish_command(&child_process);
if (status)
- error("external filter %s failed %d", params->cmd, status);
+ error("external filter '%s' failed %d", params->cmd, status);
strbuf_release(&cmd);
return (write_err || status);
}
-static int apply_filter(const char *path, const char *src, size_t len, int fd,
+static int apply_single_file_filter(const char *path, const char *src, size_t len, int fd,
struct strbuf *dst, const char *cmd)
{
/*
*
* (child --> cmd) --> us
*/
- int ret = 1;
+ int err = 0;
struct strbuf nbuf = STRBUF_INIT;
struct async async;
struct filter_params params;
- if (!cmd || !*cmd)
- return 0;
-
- if (!dst)
- return 1;
-
memset(&async, 0, sizeof(async));
async.proc = filter_buffer_or_fd;
async.data = ¶ms;
return 0; /* error was already reported */
if (strbuf_read(&nbuf, async.out, len) < 0) {
- error("read from external filter %s failed", cmd);
- ret = 0;
+ err = error("read from external filter '%s' failed", cmd);
}
if (close(async.out)) {
- error("read from external filter %s failed", cmd);
- ret = 0;
+ err = error("read from external filter '%s' failed", cmd);
}
if (finish_async(&async)) {
- error("external filter %s failed", cmd);
- ret = 0;
+ err = error("external filter '%s' failed", cmd);
}
- if (ret) {
+ if (!err) {
strbuf_swap(dst, &nbuf);
}
strbuf_release(&nbuf);
- return ret;
+ return !err;
+}
+
+#define CAP_CLEAN (1u<<0)
+#define CAP_SMUDGE (1u<<1)
+
+struct cmd2process {
+ struct hashmap_entry ent; /* must be the first member! */
+ unsigned int supported_capabilities;
+ const char *cmd;
+ struct child_process process;
+};
+
+static int cmd_process_map_initialized;
+static struct hashmap cmd_process_map;
+
+static int cmd2process_cmp(const struct cmd2process *e1,
+ const struct cmd2process *e2,
+ const void *unused)
+{
+ return strcmp(e1->cmd, e2->cmd);
+}
+
+static struct cmd2process *find_multi_file_filter_entry(struct hashmap *hashmap, const char *cmd)
+{
+ struct cmd2process key;
+ hashmap_entry_init(&key, strhash(cmd));
+ key.cmd = cmd;
+ return hashmap_get(hashmap, &key, NULL);
+}
+
+static int packet_write_list(int fd, const char *line, ...)
+{
+ va_list args;
+ int err;
+ va_start(args, line);
+ for (;;) {
+ if (!line)
+ break;
+ if (strlen(line) > LARGE_PACKET_DATA_MAX)
+ return -1;
+ err = packet_write_fmt_gently(fd, "%s\n", line);
+ if (err)
+ return err;
+ line = va_arg(args, const char*);
+ }
+ va_end(args);
+ return packet_flush_gently(fd);
+}
+
+static void read_multi_file_filter_status(int fd, struct strbuf *status)
+{
+ struct strbuf **pair;
+ char *line;
+ for (;;) {
+ line = packet_read_line(fd, NULL);
+ if (!line)
+ break;
+ pair = strbuf_split_str(line, '=', 2);
+ if (pair[0] && pair[0]->len && pair[1]) {
+ /* the last "status=<foo>" line wins */
+ if (!strcmp(pair[0]->buf, "status=")) {
+ strbuf_reset(status);
+ strbuf_addbuf(status, pair[1]);
+ }
+ }
+ strbuf_list_free(pair);
+ }
+}
+
+static void kill_multi_file_filter(struct hashmap *hashmap, struct cmd2process *entry)
+{
+ if (!entry)
+ return;
+
+ entry->process.clean_on_exit = 0;
+ kill(entry->process.pid, SIGTERM);
+ finish_command(&entry->process);
+
+ hashmap_remove(hashmap, entry, NULL);
+ free(entry);
+}
+
+static void stop_multi_file_filter(struct child_process *process)
+{
+ sigchain_push(SIGPIPE, SIG_IGN);
+ /* Closing the pipe signals the filter to initiate a shutdown. */
+ close(process->in);
+ close(process->out);
+ sigchain_pop(SIGPIPE);
+ /* Finish command will wait until the shutdown is complete. */
+ finish_command(process);
+}
+
+static struct cmd2process *start_multi_file_filter(struct hashmap *hashmap, const char *cmd)
+{
+ int err;
+ struct cmd2process *entry;
+ struct child_process *process;
+ const char *argv[] = { cmd, NULL };
+ struct string_list cap_list = STRING_LIST_INIT_NODUP;
+ char *cap_buf;
+ const char *cap_name;
+
+ entry = xmalloc(sizeof(*entry));
+ entry->cmd = cmd;
+ entry->supported_capabilities = 0;
+ process = &entry->process;
+
+ child_process_init(process);
+ process->argv = argv;
+ process->use_shell = 1;
+ process->in = -1;
+ process->out = -1;
+ process->clean_on_exit = 1;
+ process->clean_on_exit_handler = stop_multi_file_filter;
+
+ if (start_command(process)) {
+ error("cannot fork to run external filter '%s'", cmd);
+ return NULL;
+ }
+
+ hashmap_entry_init(entry, strhash(cmd));
+
+ sigchain_push(SIGPIPE, SIG_IGN);
+
+ err = packet_write_list(process->in, "git-filter-client", "version=2", NULL);
+ if (err)
+ goto done;
+
+ err = strcmp(packet_read_line(process->out, NULL), "git-filter-server");
+ if (err) {
+ error("external filter '%s' does not support filter protocol version 2", cmd);
+ goto done;
+ }
+ err = strcmp(packet_read_line(process->out, NULL), "version=2");
+ if (err)
+ goto done;
+ err = packet_read_line(process->out, NULL) != NULL;
+ if (err)
+ goto done;
+
+ err = packet_write_list(process->in, "capability=clean", "capability=smudge", NULL);
+
+ for (;;) {
+ cap_buf = packet_read_line(process->out, NULL);
+ if (!cap_buf)
+ break;
+ string_list_split_in_place(&cap_list, cap_buf, '=', 1);
+
+ if (cap_list.nr != 2 || strcmp(cap_list.items[0].string, "capability"))
+ continue;
+
+ cap_name = cap_list.items[1].string;
+ if (!strcmp(cap_name, "clean")) {
+ entry->supported_capabilities |= CAP_CLEAN;
+ } else if (!strcmp(cap_name, "smudge")) {
+ entry->supported_capabilities |= CAP_SMUDGE;
+ } else {
+ warning(
+ "external filter '%s' requested unsupported filter capability '%s'",
+ cmd, cap_name
+ );
+ }
+
+ string_list_clear(&cap_list, 0);
+ }
+
+done:
+ sigchain_pop(SIGPIPE);
+
+ if (err || errno == EPIPE) {
+ error("initialization for external filter '%s' failed", cmd);
+ kill_multi_file_filter(hashmap, entry);
+ return NULL;
+ }
+
+ hashmap_add(hashmap, entry);
+ return entry;
+}
+
+static int apply_multi_file_filter(const char *path, const char *src, size_t len,
+ int fd, struct strbuf *dst, const char *cmd,
+ const unsigned int wanted_capability)
+{
+ int err;
+ struct cmd2process *entry;
+ struct child_process *process;
+ struct strbuf nbuf = STRBUF_INIT;
+ struct strbuf filter_status = STRBUF_INIT;
+ const char *filter_type;
+
+ if (!cmd_process_map_initialized) {
+ cmd_process_map_initialized = 1;
+ hashmap_init(&cmd_process_map, (hashmap_cmp_fn) cmd2process_cmp, 0);
+ entry = NULL;
+ } else {
+ entry = find_multi_file_filter_entry(&cmd_process_map, cmd);
+ }
+
+ fflush(NULL);
+
+ if (!entry) {
+ entry = start_multi_file_filter(&cmd_process_map, cmd);
+ if (!entry)
+ return 0;
+ }
+ process = &entry->process;
+
+ if (!(wanted_capability & entry->supported_capabilities))
+ return 0;
+
+ if (CAP_CLEAN & wanted_capability)
+ filter_type = "clean";
+ else if (CAP_SMUDGE & wanted_capability)
+ filter_type = "smudge";
+ else
+ die("unexpected filter type");
+
+ sigchain_push(SIGPIPE, SIG_IGN);
+
+ assert(strlen(filter_type) < LARGE_PACKET_DATA_MAX - strlen("command=\n"));
+ err = packet_write_fmt_gently(process->in, "command=%s\n", filter_type);
+ if (err)
+ goto done;
+
+ err = strlen(path) > LARGE_PACKET_DATA_MAX - strlen("pathname=\n");
+ if (err) {
+ error("path name too long for external filter");
+ goto done;
+ }
+
+ err = packet_write_fmt_gently(process->in, "pathname=%s\n", path);
+ if (err)
+ goto done;
+
+ err = packet_flush_gently(process->in);
+ if (err)
+ goto done;
+
+ if (fd >= 0)
+ err = write_packetized_from_fd(fd, process->in);
+ else
+ err = write_packetized_from_buf(src, len, process->in);
+ if (err)
+ goto done;
+
+ read_multi_file_filter_status(process->out, &filter_status);
+ err = strcmp(filter_status.buf, "success");
+ if (err)
+ goto done;
+
+ err = read_packetized_to_strbuf(process->out, &nbuf) < 0;
+ if (err)
+ goto done;
+
+ read_multi_file_filter_status(process->out, &filter_status);
+ err = strcmp(filter_status.buf, "success");
+
+done:
+ sigchain_pop(SIGPIPE);
+
+ if (err || errno == EPIPE) {
+ if (!strcmp(filter_status.buf, "error")) {
+ /* The filter signaled a problem with the file. */
+ } else if (!strcmp(filter_status.buf, "abort")) {
+ /*
+ * The filter signaled a permanent problem. Don't try to filter
+ * files with the same command for the lifetime of the current
+ * Git process.
+ */
+ entry->supported_capabilities &= ~wanted_capability;
+ } else {
+ /*
+ * Something went wrong with the protocol filter.
+ * Force shutdown and restart if another blob requires filtering.
+ */
+ error("external filter '%s' failed", cmd);
+ kill_multi_file_filter(&cmd_process_map, entry);
+ }
+ } else {
+ strbuf_swap(dst, &nbuf);
+ }
+ strbuf_release(&nbuf);
+ return !err;
}
static struct convert_driver {
struct convert_driver *next;
const char *smudge;
const char *clean;
+ const char *process;
int required;
} *user_convert, **user_convert_tail;
+static int apply_filter(const char *path, const char *src, size_t len,
+ int fd, struct strbuf *dst, struct convert_driver *drv,
+ const unsigned int wanted_capability)
+{
+ const char *cmd = NULL;
+
+ if (!drv)
+ return 0;
+
+ if (!dst)
+ return 1;
+
+ if ((CAP_CLEAN & wanted_capability) && !drv->process && drv->clean)
+ cmd = drv->clean;
+ else if ((CAP_SMUDGE & wanted_capability) && !drv->process && drv->smudge)
+ cmd = drv->smudge;
+
+ if (cmd && *cmd)
+ return apply_single_file_filter(path, src, len, fd, dst, cmd);
+ else if (drv->process && *drv->process)
+ return apply_multi_file_filter(path, src, len, fd, dst, drv->process, wanted_capability);
+
+ return 0;
+}
+
static int read_convert_config(const char *var, const char *value, void *cb)
{
const char *key, *name;
if (!strcmp("clean", key))
return git_config_string(&drv->clean, var, value);
+ if (!strcmp("process", key))
+ return git_config_string(&drv->process, var, value);
+
if (!strcmp("required", key)) {
drv->required = git_config_bool(var, value);
return 0;
if (!ca.drv->required)
return 0;
- return apply_filter(path, NULL, 0, -1, NULL, ca.drv->clean);
+ return apply_filter(path, NULL, 0, -1, NULL, ca.drv, CAP_CLEAN);
}
const char *get_convert_attr_ascii(const char *path)
struct strbuf *dst, enum safe_crlf checksafe)
{
int ret = 0;
- const char *filter = NULL;
- int required = 0;
struct conv_attrs ca;
convert_attrs(&ca, path);
- if (ca.drv) {
- filter = ca.drv->clean;
- required = ca.drv->required;
- }
- ret |= apply_filter(path, src, len, -1, dst, filter);
- if (!ret && required)
+ ret |= apply_filter(path, src, len, -1, dst, ca.drv, CAP_CLEAN);
+ if (!ret && ca.drv && ca.drv->required)
die("%s: clean filter '%s' failed", path, ca.drv->name);
if (ret && dst) {
convert_attrs(&ca, path);
assert(ca.drv);
- assert(ca.drv->clean);
+ assert(ca.drv->clean || ca.drv->process);
- if (!apply_filter(path, NULL, 0, fd, dst, ca.drv->clean))
+ if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN))
die("%s: clean filter '%s' failed", path, ca.drv->name);
crlf_to_git(path, dst->buf, dst->len, dst, ca.crlf_action, checksafe);
int normalizing)
{
int ret = 0, ret_filter = 0;
- const char *filter = NULL;
- int required = 0;
struct conv_attrs ca;
convert_attrs(&ca, path);
- if (ca.drv) {
- filter = ca.drv->smudge;
- required = ca.drv->required;
- }
ret |= ident_to_worktree(path, src, len, dst, ca.ident);
if (ret) {
}
/*
* CRLF conversion can be skipped if normalizing, unless there
- * is a smudge filter. The filter might expect CRLFs.
+ * is a smudge or process filter (even if the process filter doesn't
+ * support smudge). The filters might expect CRLFs.
*/
- if (filter || !normalizing) {
+ if ((ca.drv && (ca.drv->smudge || ca.drv->process)) || !normalizing) {
ret |= crlf_to_worktree(path, src, len, dst, ca.crlf_action);
if (ret) {
src = dst->buf;
}
}
- ret_filter = apply_filter(path, src, len, -1, dst, filter);
- if (!ret_filter && required)
+ ret_filter = apply_filter(path, src, len, -1, dst, ca.drv, CAP_SMUDGE);
+ if (!ret_filter && ca.drv && ca.drv->required)
die("%s: smudge filter %s failed", path, ca.drv->name);
return ret | ret_filter;
struct stream_filter *filter = NULL;
convert_attrs(&ca, path);
- if (ca.drv && (ca.drv->smudge || ca.drv->clean))
+ if (ca.drv && (ca.drv->process || ca.drv->smudge || ca.drv->clean))
return NULL;
if (ca.crlf_action == CRLF_AUTO || ca.crlf_action == CRLF_AUTO_CRLF)
close(fd);
}
-static const char permissions_advice[] =
+static const char permissions_advice[] = N_(
"The permissions on your socket directory are too loose; other\n"
"users may be able to read your cached credentials. Consider running:\n"
"\n"
-" chmod 0700 %s";
+" chmod 0700 %s");
static void init_socket_directory(const char *path)
{
struct stat st;
if (!stat(dir, &st)) {
if (st.st_mode & 077)
- die(permissions_advice, dir);
+ die(_(permissions_advice), dir);
} else {
/*
* We must be sure to create the directory with the correct mode,
{
static char rpath[PATH_MAX];
static char interp_path[PATH_MAX];
+ size_t rlen;
const char *path;
const char *dir;
namlen = slash - dir;
restlen -= namlen;
loginfo("userpath <%s>, request <%s>, namlen %d, restlen %d, slash <%s>", user_path, dir, namlen, restlen, slash);
- snprintf(rpath, PATH_MAX, "%.*s/%s%.*s",
- namlen, dir, user_path, restlen, slash);
+ rlen = snprintf(rpath, sizeof(rpath), "%.*s/%s%.*s",
+ namlen, dir, user_path, restlen, slash);
+ if (rlen >= sizeof(rpath)) {
+ logerror("user-path too large: %s", rpath);
+ return NULL;
+ }
dir = rpath;
}
}
strbuf_expand(&expanded_path, interpolated_path,
expand_path, &context);
- strlcpy(interp_path, expanded_path.buf, PATH_MAX);
+
+ rlen = strlcpy(interp_path, expanded_path.buf,
+ sizeof(interp_path));
+ if (rlen >= sizeof(interp_path)) {
+ logerror("interpolated path too large: %s",
+ interp_path);
+ return NULL;
+ }
+
strbuf_release(&expanded_path);
loginfo("Interpolated dir '%s'", interp_path);
logerror("'%s': Non-absolute path denied (base-path active)", dir);
return NULL;
}
- snprintf(rpath, PATH_MAX, "%s%s", base_path, dir);
+ rlen = snprintf(rpath, sizeof(rpath), "%s%s", base_path, dir);
+ if (rlen >= sizeof(rpath)) {
+ logerror("base-path too large: %s", rpath);
+ return NULL;
+ }
dir = rpath;
}
{
if (!informative_errors)
msg = "access denied or repository not exported";
- packet_write(1, "ERR %s: %s", msg, dir);
+ packet_write_fmt(1, "ERR %s: %s", msg, dir);
return -1;
}
!is_null_oid(&ce->oid),
ce->name, 0);
continue;
+ } else if (revs->diffopt.ita_invisible_in_index &&
+ ce_intent_to_add(ce)) {
+ 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,
struct rev_info *revs = o->unpack_data;
int match_missing, cached;
+ /* i-t-a entries do not actually exist in the index */
+ if (revs->diffopt.ita_invisible_in_index &&
+ idx && ce_intent_to_add(idx)) {
+ 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)));
return 0;
}
-int index_differs_from(const char *def, int diff_flags)
+int index_differs_from(const char *def, int diff_flags,
+ int ita_invisible_in_index)
{
struct rev_info rev;
struct setup_revision_opt opt;
DIFF_OPT_SET(&rev.diffopt, QUICK);
DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
rev.diffopt.flags |= diff_flags;
+ rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
run_diff_index(&rev, 1);
if (rev.pending.alloc)
free(rev.pending.objects);
static int diff_dirstat_permille_default = 30;
static struct diff_options default_diff_options;
static long diff_algorithm;
+static unsigned ws_error_highlight_default = WSEH_NEW;
static char diff_colors[][COLOR_MAXLEN] = {
GIT_COLOR_RESET,
return -1;
}
+static int parse_one_token(const char **arg, const char *token)
+{
+ const char *rest;
+ if (skip_prefix(*arg, token, &rest) && (!*rest || *rest == ',')) {
+ *arg = rest;
+ return 1;
+ }
+ return 0;
+}
+
+static int parse_ws_error_highlight(const char *arg)
+{
+ const char *orig_arg = arg;
+ unsigned val = 0;
+
+ while (*arg) {
+ if (parse_one_token(&arg, "none"))
+ val = 0;
+ else if (parse_one_token(&arg, "default"))
+ val = WSEH_NEW;
+ else if (parse_one_token(&arg, "all"))
+ val = WSEH_NEW | WSEH_OLD | WSEH_CONTEXT;
+ else if (parse_one_token(&arg, "new"))
+ val |= WSEH_NEW;
+ else if (parse_one_token(&arg, "old"))
+ val |= WSEH_OLD;
+ else if (parse_one_token(&arg, "context"))
+ val |= WSEH_CONTEXT;
+ else {
+ return -1 - (int)(arg - orig_arg);
+ }
+ if (*arg)
+ arg++;
+ }
+ return val;
+}
+
/*
* These are to give UI layer defaults.
* The core-level commands such as git-diff-files should
if (git_diff_heuristic_config(var, value, cb) < 0)
return -1;
+
+ if (!strcmp(var, "diff.wserrorhighlight")) {
+ int val = parse_ws_error_highlight(value);
+ if (val < 0)
+ return -1;
+ ws_error_highlight_default = val;
+ return 0;
+ }
+
if (git_color_config(var, value, cb) < 0)
return -1;
return p->score * 100 / MAX_SCORE;
}
+static const char *diff_abbrev_oid(const struct object_id *oid, int abbrev)
+{
+ if (startup_info->have_repository)
+ return find_unique_abbrev(oid->hash, abbrev);
+ else {
+ char *hex = oid_to_hex(oid);
+ if (abbrev < 0)
+ abbrev = FALLBACK_DEFAULT_ABBREV;
+ if (abbrev > GIT_SHA1_HEXSZ)
+ die("BUG: oid abbreviation out of range: %d", abbrev);
+ hex[abbrev] = '\0';
+ return hex;
+ }
+}
+
static void fill_metainfo(struct strbuf *msg,
const char *name,
const char *other,
(!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
abbrev = 40;
}
- strbuf_addf(msg, "%s%sindex %s..", line_prefix, set,
- find_unique_abbrev(one->oid.hash, abbrev));
- strbuf_add_unique_abbrev(msg, two->oid.hash, abbrev);
+ strbuf_addf(msg, "%s%sindex %s..%s", line_prefix, set,
+ diff_abbrev_oid(&one->oid, abbrev),
+ diff_abbrev_oid(&two->oid, abbrev));
if (one->mode == two->mode)
strbuf_addf(msg, " %06o", one->mode);
strbuf_addf(msg, "%s\n", reset);
options->rename_limit = -1;
options->dirstat_permille = diff_dirstat_permille_default;
options->context = diff_context_default;
- options->ws_error_highlight = WSEH_NEW;
+ options->ws_error_highlight = ws_error_highlight_default;
DIFF_OPT_SET(options, RENAME_EMPTY);
/* pathchange left =NULL by default */
*/
read_cache();
}
- if (options->abbrev <= 0 || 40 < options->abbrev)
+ if (40 < options->abbrev)
options->abbrev = 40; /* full */
/*
*fmt |= DIFF_FORMAT_PATCH;
}
-static int parse_one_token(const char **arg, const char *token)
+static int parse_ws_error_highlight_opt(struct diff_options *opt, const char *arg)
{
- const char *rest;
- if (skip_prefix(*arg, token, &rest) && (!*rest || *rest == ',')) {
- *arg = rest;
- return 1;
- }
- return 0;
-}
+ int val = parse_ws_error_highlight(arg);
-static int parse_ws_error_highlight(struct diff_options *opt, const char *arg)
-{
- const char *orig_arg = arg;
- unsigned val = 0;
- while (*arg) {
- if (parse_one_token(&arg, "none"))
- val = 0;
- else if (parse_one_token(&arg, "default"))
- val = WSEH_NEW;
- else if (parse_one_token(&arg, "all"))
- val = WSEH_NEW | WSEH_OLD | WSEH_CONTEXT;
- else if (parse_one_token(&arg, "new"))
- val |= WSEH_NEW;
- else if (parse_one_token(&arg, "old"))
- val |= WSEH_OLD;
- else if (parse_one_token(&arg, "context"))
- val |= WSEH_CONTEXT;
- else {
- error("unknown value after ws-error-highlight=%.*s",
- (int)(arg - orig_arg), orig_arg);
- return 0;
- }
- if (*arg)
- arg++;
+ if (val < 0) {
+ error("unknown value after ws-error-highlight=%.*s",
+ -1 - val, arg);
+ return 0;
}
opt->ws_error_highlight = val;
return 1;
else if (skip_prefix(arg, "--submodule=", &arg))
return parse_submodule_opt(options, arg);
else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
- return parse_ws_error_highlight(options, arg);
+ return parse_ws_error_highlight_opt(options, arg);
+ else if (!strcmp(arg, "--ita-invisible-in-index"))
+ options->ita_invisible_in_index = 1;
+ else if (!strcmp(arg, "--ita-visible-in-index"))
+ options->ita_invisible_in_index = 0;
/* misc options */
else if (!strcmp(arg, "-z"))
free(p);
}
-/* This is different from find_unique_abbrev() in that
- * it stuffs the result with dots for alignment.
- */
-const char *diff_unique_abbrev(const unsigned char *sha1, int len)
+const char *diff_aligned_abbrev(const struct object_id *oid, int len)
{
int abblen;
const char *abbrev;
- if (len == 40)
- return sha1_to_hex(sha1);
- abbrev = find_unique_abbrev(sha1, len);
+ if (len == GIT_SHA1_HEXSZ)
+ return oid_to_hex(oid);
+
+ abbrev = diff_abbrev_oid(oid, len);
abblen = strlen(abbrev);
- if (abblen < 37) {
- static char hex[41];
+
+ /*
+ * In well-behaved cases, where the abbbreviated result is the
+ * same as the requested length, append three dots after the
+ * abbreviation (hence the whole logic is limited to the case
+ * where abblen < 37); when the actual abbreviated result is a
+ * bit longer than the requested length, we reduce the number
+ * of dots so that they match the well-behaved ones. However,
+ * if the actual abbreviation is longer than the requested
+ * length by more than three, we give up on aligning, and add
+ * three dots anyway, to indicate that the output is not the
+ * full object name. Yes, this may be suboptimal, but this
+ * appears only in "diff --raw --abbrev" output and it is not
+ * worth the effort to change it now. Note that this would
+ * likely to work fine when the automatic sizing of default
+ * abbreviation length is used--we would be fed -1 in "len" in
+ * that case, and will end up always appending three-dots, but
+ * the automatic sizing is supposed to give abblen that ensures
+ * uniqueness across all objects (statistically speaking).
+ */
+ if (abblen < GIT_SHA1_HEXSZ - 3) {
+ static char hex[GIT_SHA1_HEXSZ + 1];
if (len < abblen && abblen <= len + 2)
xsnprintf(hex, sizeof(hex), "%s%.*s", abbrev, len+3-abblen, "..");
else
xsnprintf(hex, sizeof(hex), "%s...", abbrev);
return hex;
}
- return sha1_to_hex(sha1);
+
+ return oid_to_hex(oid);
}
static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt)
fprintf(opt->file, "%s", diff_line_prefix(opt));
if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) {
fprintf(opt->file, ":%06o %06o %s ", p->one->mode, p->two->mode,
- diff_unique_abbrev(p->one->oid.hash, opt->abbrev));
+ diff_aligned_abbrev(&p->one->oid, opt->abbrev));
fprintf(opt->file, "%s ",
- diff_unique_abbrev(p->two->oid.hash, opt->abbrev));
+ diff_aligned_abbrev(&p->two->oid, opt->abbrev));
}
if (p->score) {
fprintf(opt->file, "%c%03d%c", p->status, similarity_index(p),
}
static const char rename_limit_warning[] =
-"inexact rename detection was skipped due to too many files.";
+N_("inexact rename detection was skipped due to too many files.");
static const char degrade_cc_to_c_warning[] =
-"only found copies from modified paths due to too many files.";
+N_("only found copies from modified paths due to too many files.");
static const char rename_limit_advice[] =
-"you may want to set your %s variable to at least "
-"%d and retry the command.";
+N_("you may want to set your %s variable to at least "
+ "%d and retry the command.");
void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc)
{
if (degraded_cc)
- warning(degrade_cc_to_c_warning);
+ warning(_(degrade_cc_to_c_warning));
else if (needed)
- warning(rename_limit_warning);
+ warning(_(rename_limit_warning));
else
return;
if (0 < needed && needed < 32767)
- warning(rename_limit_advice, varname, needed);
+ warning(_(rename_limit_advice), varname, needed);
}
void diff_flush(struct diff_options *options)
int dirstat_permille;
int setup;
int abbrev;
+ int ita_invisible_in_index;
/* white-space error highlighting */
#define WSEH_NEW 1
#define WSEH_CONTEXT 2
#define DIFF_STATUS_FILTER_AON '*'
#define DIFF_STATUS_FILTER_BROKEN 'B'
-extern const char *diff_unique_abbrev(const unsigned char *, int);
+/*
+ * This is different from find_unique_abbrev() in that
+ * it stuffs the result with dots for alignment.
+ */
+extern const char *diff_aligned_abbrev(const struct object_id *sha1, int);
/* do not report anything on removed paths */
#define DIFF_SILENT_ON_REMOVED 01
extern void diff_no_index(struct rev_info *, int, const char **);
-extern int index_differs_from(const char *def, int diff_flags);
+extern int index_differs_from(const char *def, int diff_flags, int ita_invisible_in_index);
/*
* Fill the contents of the filespec "df", respecting any textconv defined by
return 1;
}
-#define DO_MATCH_EXCLUDE 1
-#define DO_MATCH_DIRECTORY 2
+#define DO_MATCH_EXCLUDE (1<<0)
+#define DO_MATCH_DIRECTORY (1<<1)
+#define DO_MATCH_SUBMODULE (1<<2)
/*
* Does 'match' match the given name?
item->nowildcard_len - prefix))
return MATCHED_FNMATCH;
+ /* Perform checks to see if "name" is a super set of the pathspec */
+ if (flags & DO_MATCH_SUBMODULE) {
+ /* name is a literal prefix of the pathspec */
+ if ((namelen < matchlen) &&
+ (match[namelen] == '/') &&
+ !ps_strncmp(item, match, name, namelen))
+ return MATCHED_RECURSIVELY;
+
+ /* name" doesn't match up to the first wild character */
+ if (item->nowildcard_len < item->len &&
+ ps_strncmp(item, match, name,
+ item->nowildcard_len - prefix))
+ return 0;
+
+ /*
+ * Here is where we would perform a wildmatch to check if
+ * "name" can be matched as a directory (or a prefix) against
+ * the pathspec. Since wildmatch doesn't have this capability
+ * at the present we have to punt and say that it is a match,
+ * potentially returning a false positive
+ * The submodules themselves will be able to perform more
+ * accurate matching to determine if the pathspec matches.
+ */
+ return MATCHED_RECURSIVELY;
+ }
+
return 0;
}
return negative ? 0 : positive;
}
+/**
+ * Check if a submodule is a superset of the pathspec
+ */
+int submodule_path_match(const struct pathspec *ps,
+ const char *submodule_name,
+ char *seen)
+{
+ int matched = do_match_pathspec(ps, submodule_name,
+ strlen(submodule_name),
+ 0, seen,
+ DO_MATCH_DIRECTORY |
+ DO_MATCH_SUBMODULE);
+ return matched;
+}
+
int report_path_error(const char *ps_matched,
const struct pathspec *pathspec,
const char *prefix)
void setup_standard_excludes(struct dir_struct *dir)
{
- const char *path;
-
dir->exclude_per_dir = ".gitignore";
/* core.excludefile defaulting to $XDG_HOME/git/ignore */
dir->untracked ? &dir->ss_excludes_file : NULL);
/* per repository user preference */
- path = git_path_info_exclude();
- if (!access_or_warn(path, R_OK, 0))
- add_excludes_from_file_1(dir, path,
- dir->untracked ? &dir->ss_info_exclude : NULL);
+ if (startup_info->have_repository) {
+ const char *path = git_path_info_exclude();
+ if (!access_or_warn(path, R_OK, 0))
+ add_excludes_from_file_1(dir, path,
+ dir->untracked ? &dir->ss_info_exclude : NULL);
+ }
}
int remove_path(const char *name)
const char *pattern, const char *string,
int prefix);
+extern int submodule_path_match(const struct pathspec *ps,
+ const char *submodule_name,
+ char *seen);
+
static inline int ce_path_match(const struct cache_entry *ce,
const struct pathspec *pathspec,
char *seen)
int trust_ctime = 1;
int check_stat = 1;
int has_symlinks = 1;
-int minimum_abbrev = 4, default_abbrev = 7;
+int minimum_abbrev = 4, default_abbrev = -1;
int ignore_case;
int assume_unchanged;
int prefer_symlink_refs;
static const char *namespace;
static size_t namespace_len;
+static const char *super_prefix;
+
static const char *git_dir, *git_common_dir;
static char *git_object_dir, *git_index_file, *git_graft_file;
int git_db_env, git_index_env, git_graft_env, git_common_dir_env;
NO_REPLACE_OBJECTS_ENVIRONMENT,
GIT_REPLACE_REF_BASE_ENVIRONMENT,
GIT_PREFIX_ENVIRONMENT,
+ GIT_SUPER_PREFIX_ENVIRONMENT,
GIT_SHALLOW_FILE_ENVIRONMENT,
GIT_COMMON_DIR_ENVIRONMENT,
NULL
return namespaced_ref + namespace_len;
}
+const char *get_super_prefix(void)
+{
+ static int initialized;
+ if (!initialized) {
+ super_prefix = getenv(GIT_SUPER_PREFIX_ENVIRONMENT);
+ initialized = 1;
+ }
+ return super_prefix;
+}
+
static int git_work_tree_initialized;
/*
* times, and it must be assignable as an lvalue.
*/
#define FLEX_ALLOC_MEM(x, flexname, buf, len) do { \
- (x) = NULL; /* silence -Wuninitialized for offset calculation */ \
- (x) = xalloc_flex(sizeof(*(x)), (char *)(&((x)->flexname)) - (char *)(x), (buf), (len)); \
+ size_t flex_array_len_ = (len); \
+ (x) = xcalloc(1, st_add3(sizeof(*(x)), flex_array_len_, 1)); \
+ memcpy((void *)(x)->flexname, (buf), flex_array_len_); \
} while (0)
#define FLEXPTR_ALLOC_MEM(x, ptrname, buf, len) do { \
- (x) = xalloc_flex(sizeof(*(x)), sizeof(*(x)), (buf), (len)); \
+ size_t flex_array_len_ = (len); \
+ (x) = xcalloc(1, st_add3(sizeof(*(x)), flex_array_len_, 1)); \
+ memcpy((x) + 1, (buf), flex_array_len_); \
(x)->ptrname = (void *)((x)+1); \
} while(0)
#define FLEX_ALLOC_STR(x, flexname, str) \
#define FLEXPTR_ALLOC_STR(x, ptrname, str) \
FLEXPTR_ALLOC_MEM((x), ptrname, (str), strlen(str))
-static inline void *xalloc_flex(size_t base_len, size_t offset,
- const void *src, size_t src_len)
-{
- unsigned char *ret = xcalloc(1, st_add3(base_len, src_len, 1));
- memcpy(ret + offset, src, src_len);
- return ret;
-}
-
static inline char *xstrdup_or_null(const char *str)
{
return str ? xstrdup(str) : NULL;
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=0.20.GITGUI
+DEF_VER=0.21.GITGUI
LF='
'
rm -f $@ ; \
echo '# Autogenerated by git-gui Makefile' >$@ && \
echo >>$@ && \
- $(foreach p,$(PRELOAD_FILES) $(ALL_LIBFILES),echo '$(subst lib/,,$p)' >>$@ &&) \
+ $(foreach p,$(PRELOAD_FILES) $(sort $(ALL_LIBFILES)),echo '$(subst lib/,,$p)' >>$@ &&) \
echo >>$@ ; \
fi
set _iscygwin 0
} else {
set _iscygwin 1
+ # Handle MSys2 which is only cygwin when MSYSTEM is MSYS.
+ if {[info exists ::env(MSYSTEM)] && $::env(MSYSTEM) ne "MSYS"} {
+ set _iscygwin 0
+ }
}
} else {
set _iscygwin 0
}
proc git {args} {
- set opt [list]
-
- while {1} {
- switch -- [lindex $args 0] {
- --nice {
- _lappend_nice opt
- }
-
- default {
- break
- }
-
- }
-
- set args [lrange $args 1 end]
- }
-
- set cmdp [_git_cmd [lindex $args 0]]
- set args [lrange $args 1 end]
-
- _trace_exec [concat $opt $cmdp $args]
- set result [eval exec $opt $cmdp $args]
+ set fd [eval [list git_read] $args]
+ fconfigure $fd -translation binary -encoding utf-8
+ set result [string trimright [read $fd] "\n"]
+ close $fd
if {$::_trace} {
puts stderr "< $result"
}
[list git_read config] \
$args \
[list --null --list]]
- fconfigure $fd_rc -translation binary
+ fconfigure $fd_rc -translation binary -encoding utf-8
set buf [read $fd_rc]
close $fd_rc
}
if {[file isfile [gitdir MERGE_MSG]]} {
set pcm_source "merge"
set fd_mm [open [gitdir MERGE_MSG] r]
+ fconfigure $fd_mm -encoding utf-8
puts -nonewline $fd_pcm [read $fd_mm]
close $fd_mm
} elseif {[file isfile [gitdir SQUASH_MSG]]} {
set pcm_source "squash"
set fd_sm [open [gitdir SQUASH_MSG] r]
+ fconfigure $fd_sm -encoding utf-8
puts -nonewline $fd_pcm [read $fd_sm]
close $fd_sm
} else {
set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }]
set p [string range $buf_rdi $z1 [expr {$z2 - 1}]]
merge_state \
- [encoding convertfrom $p] \
+ [encoding convertfrom utf-8 $p] \
[lindex $i 4]? \
[list [lindex $i 0] [lindex $i 2]] \
[list]
set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }]
set p [string range $buf_rdf $z1 [expr {$z2 - 1}]]
merge_state \
- [encoding convertfrom $p] \
+ [encoding convertfrom utf-8 $p] \
?[lindex $i 4] \
[list] \
[list [lindex $i 0] [lindex $i 2]]
set pck [split $buf_rlo "\0"]
set buf_rlo [lindex $pck end]
foreach p [lrange $pck 0 end-1] {
- set p [encoding convertfrom $p]
+ set p [encoding convertfrom utf-8 $p]
if {[string index $p end] eq {/}} {
set p [string range $p 0 end-1]
}
}
}
-proc toggle_or_diff {w x y} {
+proc toggle_or_diff {mode w args} {
global file_states file_lists current_diff_path ui_index ui_workdir
global last_clicked selected_paths
- set pos [split [$w index @$x,$y] .]
- set lno [lindex $pos 0]
- set col [lindex $pos 1]
+ if {$mode eq "click"} {
+ foreach {x y} $args break
+ set pos [split [$w index @$x,$y] .]
+ foreach {lno col} $pos break
+ } else {
+ if {$last_clicked ne {}} {
+ set lno [lindex $last_clicked 1]
+ } else {
+ set lno [expr {int([lindex [$w tag ranges in_diff] 0])}]
+ }
+ if {$mode eq "toggle"} {
+ set col 0; set y 2
+ } else {
+ incr lno [expr {$mode eq "up" ? -1 : 1}]
+ set col 1
+ }
+ }
+
set path [lindex $file_lists($w) [expr {$lno - 1}]]
if {$path eq {}} {
set last_clicked {}
}
set last_clicked [list $w $lno]
+ focus $w
array unset selected_paths
$ui_index tag remove in_sel 0.0 end
$ui_workdir tag remove in_sel 0.0 end
global file_lists last_clicked selected_paths
if {[lindex $last_clicked 0] ne $w} {
- toggle_or_diff $w $x $y
+ toggle_or_diff click $w $x $y
return
}
set subcommand_args {}
proc usage {} {
- set s "usage: $::argv0 $::subcommand $::subcommand_args"
+ set s "[mc usage:] $::argv0 $::subcommand $::subcommand_args"
if {[tk windowingsystem] eq "win32"} {
wm withdraw .
tk_messageBox -icon info -message $s \
# fall through to setup UI for commits
}
default {
- set err "usage: $argv0 \[{blame|browser|citool}\]"
+ set err "[mc usage:] $argv0 \[{blame|browser|citool}\]"
if {[tk windowingsystem] eq "win32"} {
wm withdraw .
tk_messageBox -icon error -message $err \
}
pack .vpane -anchor n -side top -fill both -expand 1
+# -- Working Directory File List
+
+textframe .vpane.files.workdir -height 100 -width 200
+tlabel .vpane.files.workdir.title -text [mc "Unstaged Changes"] \
+ -background lightsalmon -foreground black
+ttext $ui_workdir -background white -foreground black \
+ -borderwidth 0 \
+ -width 20 -height 10 \
+ -wrap none \
+ -takefocus 1 -highlightthickness 1\
+ -cursor $cursor_ptr \
+ -xscrollcommand {.vpane.files.workdir.sx set} \
+ -yscrollcommand {.vpane.files.workdir.sy set} \
+ -state disabled
+${NS}::scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview]
+${NS}::scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview]
+pack .vpane.files.workdir.title -side top -fill x
+pack .vpane.files.workdir.sx -side bottom -fill x
+pack .vpane.files.workdir.sy -side right -fill y
+pack $ui_workdir -side left -fill both -expand 1
+
# -- Index File List
#
-${NS}::frame .vpane.files.index -height 100 -width 200
+textframe .vpane.files.index -height 100 -width 200
tlabel .vpane.files.index.title \
-text [mc "Staged Changes (Will Commit)"] \
-background lightgreen -foreground black
-text $ui_index -background white -foreground black \
+ttext $ui_index -background white -foreground black \
-borderwidth 0 \
-width 20 -height 10 \
-wrap none \
+ -takefocus 1 -highlightthickness 1\
-cursor $cursor_ptr \
-xscrollcommand {.vpane.files.index.sx set} \
-yscrollcommand {.vpane.files.index.sy set} \
pack .vpane.files.index.sy -side right -fill y
pack $ui_index -side left -fill both -expand 1
-# -- Working Directory File List
+# -- Insert the workdir and index into the panes
#
-${NS}::frame .vpane.files.workdir -height 100 -width 200
-tlabel .vpane.files.workdir.title -text [mc "Unstaged Changes"] \
- -background lightsalmon -foreground black
-text $ui_workdir -background white -foreground black \
- -borderwidth 0 \
- -width 20 -height 10 \
- -wrap none \
- -cursor $cursor_ptr \
- -xscrollcommand {.vpane.files.workdir.sx set} \
- -yscrollcommand {.vpane.files.workdir.sy set} \
- -state disabled
-${NS}::scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview]
-${NS}::scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview]
-pack .vpane.files.workdir.title -side top -fill x
-pack .vpane.files.workdir.sx -side bottom -fill x
-pack .vpane.files.workdir.sy -side right -fill y
-pack $ui_workdir -side left -fill both -expand 1
-
.vpane.files add .vpane.files.workdir
.vpane.files add .vpane.files.index
if {!$use_ttk} {
#
${NS}::frame .vpane.lower.commarea.buffer
${NS}::frame .vpane.lower.commarea.buffer.header
-set ui_comm .vpane.lower.commarea.buffer.t
+set ui_comm .vpane.lower.commarea.buffer.frame.t
set ui_coml .vpane.lower.commarea.buffer.header.l
if {![is_enabled nocommit]} {
pack .vpane.lower.commarea.buffer.header.new -side right
}
-text $ui_comm -background white -foreground black \
+textframe .vpane.lower.commarea.buffer.frame
+ttext $ui_comm -background white -foreground black \
-borderwidth 1 \
-undo true \
-maxundo 20 \
-autoseparators true \
+ -takefocus 1 \
+ -highlightthickness 1 \
-relief sunken \
-width $repo_config(gui.commitmsgwidth) -height 9 -wrap none \
-font font_diff \
- -yscrollcommand {.vpane.lower.commarea.buffer.sby set}
-${NS}::scrollbar .vpane.lower.commarea.buffer.sby \
+ -yscrollcommand {.vpane.lower.commarea.buffer.frame.sby set}
+${NS}::scrollbar .vpane.lower.commarea.buffer.frame.sby \
-command [list $ui_comm yview]
-pack .vpane.lower.commarea.buffer.header -side top -fill x
-pack .vpane.lower.commarea.buffer.sby -side right -fill y
+
+pack .vpane.lower.commarea.buffer.frame.sby -side right -fill y
pack $ui_comm -side left -fill y
+pack .vpane.lower.commarea.buffer.header -side top -fill x
+pack .vpane.lower.commarea.buffer.frame -side left -fill y
pack .vpane.lower.commarea.buffer -side left -fill y
# -- Commit Message Buffer Context Menu
# -- Diff Body
#
-${NS}::frame .vpane.lower.diff.body
+textframe .vpane.lower.diff.body
set ui_diff .vpane.lower.diff.body.t
-text $ui_diff -background white -foreground black \
+ttext $ui_diff -background white -foreground black \
-borderwidth 0 \
-width 80 -height 5 -wrap none \
-font font_diff \
+ -takefocus 1 -highlightthickness 1 \
-xscrollcommand {.vpane.lower.diff.body.sbx set} \
-yscrollcommand {.vpane.lower.diff.body.sby set} \
-state disabled
bind . <$M1B-Key-R> ui_do_rescan
bind . <$M1B-Key-s> do_signoff
bind . <$M1B-Key-S> do_signoff
-bind . <$M1B-Key-t> do_add_selection
-bind . <$M1B-Key-T> do_add_selection
-bind . <$M1B-Key-u> do_unstage_selection
-bind . <$M1B-Key-U> do_unstage_selection
+bind . <$M1B-Key-t> { toggle_or_diff toggle %W }
+bind . <$M1B-Key-T> { toggle_or_diff toggle %W }
+bind . <$M1B-Key-u> { toggle_or_diff toggle %W }
+bind . <$M1B-Key-U> { toggle_or_diff toggle %W }
bind . <$M1B-Key-j> do_revert_selection
bind . <$M1B-Key-J> do_revert_selection
bind . <$M1B-Key-i> do_add_all
bind . <$M1B-Key-KP_Add> {show_more_context;break}
bind . <$M1B-Key-Return> do_commit
foreach i [list $ui_index $ui_workdir] {
- bind $i <Button-1> "toggle_or_diff $i %x %y; break"
- bind $i <$M1B-Button-1> "add_one_to_selection $i %x %y; break"
- bind $i <Shift-Button-1> "add_range_to_selection $i %x %y; break"
+ bind $i <Button-1> { toggle_or_diff click %W %x %y; break }
+ bind $i <$M1B-Button-1> { add_one_to_selection %W %x %y; break }
+ bind $i <Shift-Button-1> { add_range_to_selection %W %x %y; break }
+ bind $i <Key-Up> { toggle_or_diff up %W; break }
+ bind $i <Key-Down> { toggle_or_diff down %W; break }
}
unset i
set path $i_path
make_toplevel top w
- wm title $top [append "[appname] ([reponame]): " [mc "File Viewer"]]
+ wm title $top [mc "%s (%s): File Viewer" [appname] [reponame]]
set font_w [font measure font_diff "0"]
global use_ttk NS
make_dialog top w
wm withdraw $w
- wm title $top [append "[appname] ([reponame]): " [mc "Checkout Branch"]]
+ wm title $top [mc "%s (%s): Checkout Branch" [appname] [reponame]]
if {$top ne {.}} {
wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
}
make_dialog top w
wm withdraw $w
- wm title $top [append "[appname] ([reponame]): " [mc "Create Branch"]]
+ wm title $top [mc "%s (%s): Create Branch" [appname] [reponame]]
if {$top ne {.}} {
wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
}
make_dialog top w
wm withdraw $w
- wm title $top [append "[appname] ([reponame]): " [mc "Delete Branch"]]
+ wm title $top [mc "%s (%s): Delete Branch" [appname] [reponame]]
if {$top ne {.}} {
wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
}
set b [lindex $i 0]
set o [lindex $i 1]
if {[catch {git branch -D $b} err]} {
- append failed " - $b: $err\n"
+ append failed [mc " - %s:" $b] " $err\n"
}
}
make_dialog top w
wm withdraw $w
- wm title $top [append "[appname] ([reponame]): " [mc "Rename Branch"]]
+ wm title $top [mc "%s (%s): Rename Branch" [appname] [reponame]]
if {$top ne {.}} {
wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
}
global cursor_ptr M1B use_ttk NS
make_dialog top w
wm withdraw $top
- wm title $top [append "[appname] ([reponame]): " [mc "File Browser"]]
+ wm title $top [mc "%s (%s): File Browser" [appname] [reponame]]
if {$path ne {}} {
if {[string index $path end] ne {/}} {
$w conf -state disabled
set fd [git_read ls-tree -z $tree_id]
- fconfigure $fd -blocking 0 -translation binary -encoding binary
+ fconfigure $fd -blocking 0 -translation binary -encoding utf-8
fileevent $fd readable [cb _read $fd]
}
global use_ttk NS
make_dialog top w
wm withdraw $top
- wm title $top [append "[appname] ([reponame]): " [mc "Browse Branch Files"]]
+ wm title $top [mc "%s (%s): Browse Branch Files" [appname] [reponame]]
if {$top ne {.}} {
wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
wm transient $top .
# Copyright (C) 2006, 2007 Shawn Pearce
proc load_last_commit {} {
- global HEAD PARENT MERGE_HEAD commit_type ui_comm
+ global HEAD PARENT MERGE_HEAD commit_type ui_comm commit_author
global repo_config
if {[llength $PARENT] == 0} {
lappend parents [string range $line 7 end]
} elseif {[string match {encoding *} $line]} {
set enc [string tolower [string range $line 9 end]]
+ } elseif {[regexp "author (.*)\\s<(.*)>\\s(\\d.*$)" $line all name email time]} {
+ set commit_author [list name $name email $email date $time]
}
}
set msg [read $fd]
}
proc create_new_commit {} {
- global commit_type ui_comm
+ global commit_type ui_comm commit_author
set commit_type normal
+ unset -nocomplain commit_author
$ui_comm delete 0.0 end
$ui_comm edit reset
$ui_comm edit modified false
}
proc commit_committree {fd_wt curHEAD msg_p} {
- global HEAD PARENT MERGE_HEAD commit_type
+ global HEAD PARENT MERGE_HEAD commit_type commit_author
global current_branch
global ui_comm selected_commit_type
global file_states selected_paths rescan_active
global repo_config
+ global env
gets $fd_wt tree_id
if {[catch {close $fd_wt} err]} {
}
}
+ if {[info exists commit_author]} {
+ set old_author [commit_author_ident $commit_author]
+ }
# -- Create the commit.
#
set cmd [list commit-tree $tree_id]
error_popup [strcat [mc "commit-tree failed:"] "\n\n$err"]
ui_status [mc "Commit failed."]
unlock_index
+ unset -nocomplain commit_author
+ commit_author_reset $old_author
return
}
+ if {[info exists commit_author]} {
+ unset -nocomplain commit_author
+ commit_author_reset $old_author
+ }
# -- Update the HEAD ref.
#
}
fconfigure $fd_ph -blocking 0
}
+
+proc commit_author_ident {details} {
+ global env
+ array set author $details
+ set old [array get env GIT_AUTHOR_*]
+ set env(GIT_AUTHOR_NAME) $author(name)
+ set env(GIT_AUTHOR_EMAIL) $author(email)
+ set env(GIT_AUTHOR_DATE) $author(date)
+ return $old
+}
+proc commit_author_reset {details} {
+ global env
+ unset env(GIT_AUTHOR_NAME) env(GIT_AUTHOR_EMAIL) env(GIT_AUTHOR_DATE)
+ if {$details ne {}} {
+ array set env $details
+ }
+}
set value "$value[lindex $s 2]"
}
- ${NS}::label $w.stat.l_$name -text "$label:" -anchor w
+ ${NS}::label $w.stat.l_$name -text [mc "%s:" $label] -anchor w
${NS}::label $w.stat.v_$name -text $value -anchor w
grid $w.stat.l_$name $w.stat.v_$name -sticky we -padx {0 5}
}
bind $w <Visibility> "grab $w; focus $w.buttons.close"
bind $w <Key-Escape> [list destroy $w]
bind $w <Key-Return> [list destroy $w]
- wm title $w [append "[appname] ([reponame]): " [mc "Database Statistics"]]
+ wm title $w [mc "%s (%s): Database Statistics" [appname] [reponame]]
wm deiconify $w
tkwait window $w
}
} else {
start_show_diff $cont_info
}
+
+ global current_diff_path selected_paths
+ set selected_paths($current_diff_path) 1
}
proc show_unmerged_diff {cont_info} {
}
$ui_diff conf -state normal
if {$type eq {submodule}} {
- $ui_diff insert end [append \
- "* " \
- [mc "Git Repository (subproject)"] \
- "\n"] d_info
+ $ui_diff insert end \
+ "* [mc "Git Repository (subproject)"]\n" \
+ d_info
} elseif {![catch {set type [exec file $path]}]} {
set n [string length $path]
if {[string equal -length $n $path $type]} {
puts -nonewline $p $current_diff_header
puts -nonewline $p [$ui_diff get $s_lno $e_lno]
close $p} err]} {
- error_popup [append $failed_msg "\n\n$err"]
+ error_popup "$failed_msg\n\n$err"
unlock_index
return
}
puts -nonewline $p $current_diff_header
puts -nonewline $p $wholepatch
close $p} err]} {
- error_popup [append $failed_msg "\n\n$err"]
+ error_popup "$failed_msg\n\n$err"
}
unlock_index
set cmd [list tk_messageBox \
-icon error \
-type ok \
- -title [append "$title: " [mc "error"]] \
+ -title [mc "%s: error" $title] \
-message $msg]
if {[winfo ismapped [_error_parent]]} {
lappend cmd -parent [_error_parent]
set cmd [list tk_messageBox \
-icon warning \
-type ok \
- -title [append "$title: " [mc "warning"]] \
+ -title [mc "%s: warning" $title] \
-message $msg]
if {[winfo ismapped [_error_parent]]} {
lappend cmd -parent [_error_parent]
wm withdraw $w
${NS}::frame $w.m
- ${NS}::label $w.m.l1 -text "$hook hook failed:" \
+ ${NS}::label $w.m.l1 -text [mc "%s hook failed:" $hook] \
-anchor w \
-justify left \
-font font_uibold
bind $w <Visibility> "grab $w; focus $w"
bind $w <Key-Return> "destroy $w"
- wm title $w [strcat "[appname] ([reponame]): " [mc "error"]]
+ wm title $w [mc "%s (%s): error" [appname] [reponame]]
wm deiconify $w
tkwait window $w
}
set info [lindex $s 2]
if {$info eq {}} continue
- puts -nonewline $fd "$info\t[encoding convertto $path]\0"
+ puts -nonewline $fd "$info\t[encoding convertto utf-8 $path]\0"
display_file $path $new
}
?M {set new M_}
?? {continue}
}
- puts -nonewline $fd "[encoding convertto $path]\0"
+ puts -nonewline $fd "[encoding convertto utf-8 $path]\0"
display_file $path $new
}
?M -
?T -
?D {
- puts -nonewline $fd "[encoding convertto $path]\0"
+ puts -nonewline $fd "[encoding convertto utf-8 $path]\0"
display_file $path ?_
}
}
close $fh
set _last_merged_branch $branch
- set cmd [list git merge --strategy=recursive FETCH_HEAD]
+ if {[git-version >= "2.5.0"]} {
+ set cmd [list git merge --strategy=recursive FETCH_HEAD]
+ } else {
+ set cmd [list git]
+ lappend cmd merge
+ lappend cmd --strategy=recursive
+ lappend cmd [git fmt-merge-msg <[gitdir FETCH_HEAD]]
+ lappend cmd HEAD
+ lappend cmd $name
+ }
ui_status [mc "Merging %s and %s..." $current_branch $stitle]
set cons [console::new [mc "Merge"] "merge $stitle"]
}
make_dialog top w
- wm title $top [append "[appname] ([reponame]): " [mc "Merge"]]
+ wm title $top [mc "%s (%s): Merge" [appname] [reponame]]
if {$top ne {.}} {
wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
}
i-* {
regexp -- {-(\d+)\.\.(\d+)$} $type _junk min max
${NS}::frame $w.$f.$optid
- ${NS}::label $w.$f.$optid.l -text "$text:"
+ ${NS}::label $w.$f.$optid.l -text [mc "%s:" $text]
pack $w.$f.$optid.l -side left -anchor w -fill x
tspinbox $w.$f.$optid.v \
-textvariable ${f}_config_new($name) \
c -
t {
${NS}::frame $w.$f.$optid
- ${NS}::label $w.$f.$optid.l -text "$text:"
+ ${NS}::label $w.$f.$optid.l -text [mc "%s:" $text]
${NS}::entry $w.$f.$optid.v \
-width 20 \
-textvariable ${f}_config_new($name)
s {
set opts [eval [lindex $option 3]]
${NS}::frame $w.$f.$optid
- ${NS}::label $w.$f.$optid.l -text "$text:"
+ ${NS}::label $w.$f.$optid.l -text [mc "%s:" $text]
if {$use_ttk} {
ttk::combobox $w.$f.$optid.v \
-textvariable ${f}_config_new($name) \
[font configure $font -size]
${NS}::frame $w.global.$name
- ${NS}::label $w.global.$name.l -text "$text:"
+ ${NS}::label $w.global.$name.l -text [mc "%s:" $text]
${NS}::button $w.global.$name.b \
-text [mc "Change Font"] \
-command [list \
if {$have_remote > 1} {
make_sure_remote_submenues_exist $remote_m
if {[$fetch_m type end] eq "command" \
- && [$fetch_m entrycget end -label] ne "All"} {
+ && [$fetch_m entrycget end -label] ne [mc "All"]} {
$fetch_m insert end separator
$fetch_m insert end command \
- -label "All" \
+ -label [mc "All"] \
-command fetch_from_all
$prune_m insert end separator
$prune_m insert end command \
- -label "All" \
+ -label [mc "All"] \
-command prune_from_all
}
} else {
if {[winfo exists $fetch_m]} {
if {[$fetch_m type end] eq "command" \
- && [$fetch_m entrycget end -label] eq "All"} {
+ && [$fetch_m entrycget end -label] eq [mc "All"]} {
delete_from_menu $fetch_m end
delete_from_menu $fetch_m end
make_dialog top w
wm withdraw $top
- wm title $top [append "[appname] ([reponame]): " [mc "Add Remote"]]
+ wm title $top [mc "%s (%s): Add Remote" [appname] [reponame]]
if {$top ne {.}} {
wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
}
global all_remotes M1B use_ttk NS
make_dialog top w
- wm title $top [append "[appname] ([reponame]): " [mc "Delete Branch Remotely"]]
+ wm title $top [mc "%s (%s): Delete Branch Remotely" [appname] [reponame]]
if {$top ne {.}} {
wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
}
global _gitworktree
set fn [tk_getSaveFile \
-parent . \
- -title [append "[appname] ([reponame]): " [mc "Create Desktop Icon"]] \
+ -title [mc "%s (%s): Create Desktop Icon" [appname] [reponame]] \
-initialfile "Git [reponame].lnk"]
if {$fn != {}} {
if {[file extension $fn] ne {.lnk}} {
set fn ${fn}.lnk
}
+ # Use git-gui.exe if available (ie: git-for-windows)
+ set cmdLine [auto_execok git-gui.exe]
+ if {$cmdLine eq {}} {
+ set cmdLine [list [info nameofexecutable] \
+ [file normalize $::argv0]]
+ }
if {[catch {
- win32_create_lnk $fn [list \
- [info nameofexecutable] \
- [file normalize $::argv0] \
- ] \
+ win32_create_lnk $fn $cmdLine \
[file normalize $_gitworktree]
} err]} {
error_popup [strcat [mc "Cannot write shortcut:"] "\n\n$err"]
}
set fn [tk_getSaveFile \
-parent . \
- -title [append "[appname] ([reponame]): " [mc "Create Desktop Icon"]] \
+ -title [mc "%s (%s): Create Desktop Icon" [appname] [reponame]] \
-initialdir $desktop \
-initialfile "Git [reponame].lnk"]
if {$fn != {}} {
set fn [tk_getSaveFile \
-parent . \
- -title [append "[appname] ([reponame]): " [mc "Create Desktop Icon"]] \
+ -title [mc "%s (%s): Create Desktop Icon" [appname] [reponame]] \
-initialdir [file join $env(HOME) Desktop] \
-initialfile "Git [reponame].app"]
if {$fn != {}} {
}
}
+# Define a style used for the surround of text widgets.
+proc InitEntryFrame {} {
+ ttk::style theme settings default {
+ ttk::style layout EntryFrame {
+ EntryFrame.field -sticky nswe -border 0 -children {
+ EntryFrame.fill -sticky nswe -children {
+ EntryFrame.padding -sticky nswe
+ }
+ }
+ }
+ ttk::style configure EntryFrame -padding 1 -relief sunken
+ ttk::style map EntryFrame -background {}
+ }
+ ttk::style theme settings classic {
+ ttk::style configure EntryFrame -padding 2 -relief sunken
+ ttk::style map EntryFrame -background {}
+ }
+ ttk::style theme settings alt {
+ ttk::style configure EntryFrame -padding 2
+ ttk::style map EntryFrame -background {}
+ }
+ ttk::style theme settings clam {
+ ttk::style configure EntryFrame -padding 2
+ ttk::style map EntryFrame -background {}
+ }
+
+ # Ignore errors for missing native themes
+ catch {
+ ttk::style theme settings winnative {
+ ttk::style configure EntryFrame -padding 2
+ }
+ ttk::style theme settings xpnative {
+ ttk::style configure EntryFrame -padding 1
+ ttk::style element create EntryFrame.field vsapi \
+ EDIT 1 {disabled 4 focus 3 active 2 {} 1} -padding 1
+ }
+ ttk::style theme settings vista {
+ ttk::style configure EntryFrame -padding 2
+ ttk::style element create EntryFrame.field vsapi \
+ EDIT 6 {disabled 4 focus 3 active 2 {} 1} -padding 2
+ }
+ }
+
+ bind EntryFrame <Enter> {%W instate !disabled {%W state active}}
+ bind EntryFrame <Leave> {%W state !active}
+ bind EntryFrame <<ThemeChanged>> {
+ set pad [ttk::style lookup EntryFrame -padding]
+ %W configure -padding [expr {$pad eq {} ? 1 : $pad}]
+ }
+}
+
proc gold_frame {w args} {
global use_ttk
if {$use_ttk} {
# place a themed frame over the surface.
proc Dialog {w args} {
eval [linsert $args 0 toplevel $w -class Dialog]
- catch {wm attributes $w -type dialog}
+ catch {wm attributes $w -type dialog}
pave_toplevel $w
return $w
}
}
}
+# Create a text widget with any theme specific properties.
+proc ttext {w args} {
+ global use_ttk
+ if {$use_ttk} {
+ switch -- [ttk::style theme use] {
+ "vista" - "xpnative" {
+ lappend args -highlightthickness 0 -borderwidth 0
+ }
+ }
+ }
+ set w [eval [linsert $args 0 text $w]]
+ if {$use_ttk} {
+ if {[winfo class [winfo parent $w]] eq "EntryFrame"} {
+ bind $w <FocusIn> {[winfo parent %W] state focus}
+ bind $w <FocusOut> {[winfo parent %W] state !focus}
+ }
+ }
+ return $w
+}
+
+# themed frame suitable for surrounding a text field.
+proc textframe {w args} {
+ global use_ttk
+ if {$use_ttk} {
+ if {[catch {ttk::style layout EntryFrame}]} {
+ InitEntryFrame
+ }
+ eval [linsert $args 0 ttk::frame $w -class EntryFrame -style EntryFrame]
+ } else {
+ eval [linsert $args 0 frame $w]
+ }
+ return $w
+}
+
proc tentry {w args} {
global use_ttk
if {$use_ttk} {
proc tools_exec {fullname} {
global repo_config env current_diff_path
global current_branch is_detached
+ global selected_paths
if {[is_config_true "guitool.$fullname.needsfile"]} {
if {$current_diff_path eq {}} {
set env(GIT_GUITOOL) $fullname
set env(FILENAME) $current_diff_path
+ set env(FILENAMES) [join [array names selected_paths] \n]
if {$is_detached} {
set env(CUR_BRANCH) ""
} else {
unset env(GIT_GUITOOL)
unset env(FILENAME)
+ unset env(FILENAMES)
unset env(CUR_BRANCH)
catch { unset env(ARGS) }
catch { unset env(REVISION) }
global repo_config use_ttk NS
make_dialog top w
- wm title $top [append "[appname] ([reponame]): " [mc "Add Tool"]]
+ wm title $top [mc "%s (%s): Add Tool" [appname] [reponame]]
if {$top ne {.}} {
wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
wm transient $top .
load_config 1
make_dialog top w
- wm title $top [append "[appname] ([reponame]): " [mc "Remove Tool"]]
+ wm title $top [mc "%s (%s): Remove Tool" [appname] [reponame]]
if {$top ne {.}} {
wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
wm transient $top .
}
make_dialog top w -autodelete 0
- wm title $top [append "[appname] ([reponame]): " $title]
+ wm title $top "[mc "%s (%s):" [appname] [reponame]] $title"
if {$top ne {.}} {
wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
wm transient $top .
bind $w <Visibility> "grab $w; focus $w.buttons.create"
bind $w <Key-Escape> "destroy $w"
bind $w <Key-Return> [list start_push_anywhere_action $w]
- wm title $w [append "[appname] ([reponame]): " [mc "Push"]]
+ wm title $w [mc "%s (%s): Push" [appname] [reponame]]
wm deiconify $w
tkwait window $w
}
# Bulgarian translation of git-gui po-file.
-# Copyright (C) 2012, 2013, 2014, 2015 Alexander Shopov <ash@kambanaria.org>.
+# Copyright (C) 2012, 2013, 2014, 2015, 2016 Alexander Shopov <ash@kambanaria.org>.
# This file is distributed under the same license as the git package.
-# Alexander Shopov <ash@kambanaria.org>, 2012, 2013, 2014, 2015.
+# Alexander Shopov <ash@kambanaria.org>, 2012, 2013, 2014, 2015, 2016.
#
#
msgid ""
msgstr ""
"Project-Id-Version: git-gui master\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-04-07 07:37+0300\n"
-"PO-Revision-Date: 2015-04-07 07:46+0300\n"
+"POT-Creation-Date: 2016-10-13 15:16+0300\n"
+"PO-Revision-Date: 2016-10-13 15:16+0300\n"
"Last-Translator: Alexander Shopov <ash@kambanaria.org>\n"
"Language-Team: Bulgarian <dict@fsa-bg.org>\n"
"Language: bg\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#: git-gui.sh:861
+#: git-gui.sh:865
#, tcl-format
msgid "Invalid font specified in %s:"
msgstr "Указан е неправилен шрифт в „%s“:"
-#: git-gui.sh:915
+#: git-gui.sh:919
msgid "Main Font"
msgstr "Основен шрифт"
-#: git-gui.sh:916
+#: git-gui.sh:920
msgid "Diff/Console Font"
msgstr "Шрифт за разликите/конзолата"
-#: git-gui.sh:931 git-gui.sh:945 git-gui.sh:958 git-gui.sh:1048
-#: git-gui.sh:1067 git-gui.sh:3125
+#: git-gui.sh:935 git-gui.sh:949 git-gui.sh:962 git-gui.sh:1052 git-gui.sh:1071
+#: git-gui.sh:3147
msgid "git-gui: fatal error"
msgstr "git-gui: фатална грешка"
-#: git-gui.sh:932
+#: git-gui.sh:936
msgid "Cannot find git in PATH."
msgstr "Командата git липсва в пътя (PATH)."
-#: git-gui.sh:959
+#: git-gui.sh:963
msgid "Cannot parse Git version string:"
msgstr "Низът с версията на Git не може да бъде интерпретиран:"
-#: git-gui.sh:984
+#: git-gui.sh:988
#, tcl-format
msgid ""
"Git version cannot be determined.\n"
"\n"
"Да се приеме ли, че „%s“ е версия „1.5.0“?\n"
-#: git-gui.sh:1281
+#: git-gui.sh:1285
msgid "Git directory not found:"
msgstr "Директорията на Git не е открита:"
-#: git-gui.sh:1315
+#: git-gui.sh:1319
msgid "Cannot move to top of working directory:"
msgstr "Не може да се премине към родителската директория."
-#: git-gui.sh:1323
+#: git-gui.sh:1327
msgid "Cannot use bare repository:"
msgstr "Голо хранилище не може да се използва:"
-#: git-gui.sh:1331
+#: git-gui.sh:1335
msgid "No working directory"
msgstr "Работната директория липсва"
-#: git-gui.sh:1503 lib/checkout_op.tcl:306
+#: git-gui.sh:1507 lib/checkout_op.tcl:306
msgid "Refreshing file status..."
msgstr "Обновяване на състоянието на файла…"
-#: git-gui.sh:1563
+#: git-gui.sh:1567
msgid "Scanning for modified files ..."
msgstr "Проверка за променени файлове…"
-#: git-gui.sh:1639
+#: git-gui.sh:1645
msgid "Calling prepare-commit-msg hook..."
msgstr "Куката „prepare-commit-msg“ се изпълнява в момента…"
-#: git-gui.sh:1656
+#: git-gui.sh:1662
msgid "Commit declined by prepare-commit-msg hook."
msgstr "Подаването е отхвърлено от куката „prepare-commit-msg“."
-#: git-gui.sh:1814 lib/browser.tcl:252
+#: git-gui.sh:1820 lib/browser.tcl:252
msgid "Ready."
msgstr "Готово."
-#: git-gui.sh:1978
+#: git-gui.sh:1984
#, tcl-format
msgid ""
"Display limit (gui.maxfilesdisplayed = %s) reached, not showing all %s files."
msgstr ""
-"Достигнат е максималният брой файлове за показване (gui.maxfilesdisplayed = "
-"%s). Файловете са общо %s."
+"Достигнат е максималният размер на списъка за извеждане(gui."
+"maxfilesdisplayed = %s), съответно не са показани всички %s файла."
-#: git-gui.sh:2101
+#: git-gui.sh:2107
msgid "Unmodified"
msgstr "Непроменен"
-#: git-gui.sh:2103
+#: git-gui.sh:2109
msgid "Modified, not staged"
msgstr "Променен, но не е в индекса"
-#: git-gui.sh:2104 git-gui.sh:2116
+#: git-gui.sh:2110 git-gui.sh:2122
msgid "Staged for commit"
msgstr "В индекса за подаване"
-#: git-gui.sh:2105 git-gui.sh:2117
+#: git-gui.sh:2111 git-gui.sh:2123
msgid "Portions staged for commit"
msgstr "Части са в индекса за подаване"
-#: git-gui.sh:2106 git-gui.sh:2118
+#: git-gui.sh:2112 git-gui.sh:2124
msgid "Staged for commit, missing"
msgstr "В индекса за подаване, но липсва"
-#: git-gui.sh:2108
+#: git-gui.sh:2114
msgid "File type changed, not staged"
msgstr "Видът на файла е сменен, но не е в индекса"
-#: git-gui.sh:2109 git-gui.sh:2110
+#: git-gui.sh:2115 git-gui.sh:2116
msgid "File type changed, old type staged for commit"
-msgstr "Ð\92идÑ\8aÑ\82 на Ñ\84айла е Ñ\81менен, но в индекÑ\81а е вÑ\81е оÑ\89е Ñ\81Ñ\82аÑ\80иÑ\8fÑ\82"
+msgstr "Ð\92идÑ\8aÑ\82 на Ñ\84айла е Ñ\81менен, но новиÑ\8fÑ\82 вид не е в индекÑ\81а"
-#: git-gui.sh:2111
+#: git-gui.sh:2117
msgid "File type changed, staged"
msgstr "Видът на файла е сменен и е в индекса"
-#: git-gui.sh:2112
+#: git-gui.sh:2118
msgid "File type change staged, modification not staged"
-msgstr "Видът на файла е сменен, но промяната не е в индекса"
+msgstr "Видът на файла е сменен в индекса, но не и съдържанието"
-#: git-gui.sh:2113
+#: git-gui.sh:2119
msgid "File type change staged, file missing"
-msgstr "Видът на файла е сменен, файлът липсва"
+msgstr "Видът на файла е сменен в индекса, но файлът липсва"
-#: git-gui.sh:2115
+#: git-gui.sh:2121
msgid "Untracked, not staged"
msgstr "Неследен"
-#: git-gui.sh:2120
+#: git-gui.sh:2126
msgid "Missing"
msgstr "Липсващ"
-#: git-gui.sh:2121
+#: git-gui.sh:2127
msgid "Staged for removal"
msgstr "В индекса за изтриване"
-#: git-gui.sh:2122
+#: git-gui.sh:2128
msgid "Staged for removal, still present"
msgstr "В индекса за изтриване, но още го има"
-#: git-gui.sh:2124 git-gui.sh:2125 git-gui.sh:2126 git-gui.sh:2127
-#: git-gui.sh:2128 git-gui.sh:2129
+#: git-gui.sh:2130 git-gui.sh:2131 git-gui.sh:2132 git-gui.sh:2133
+#: git-gui.sh:2134 git-gui.sh:2135
msgid "Requires merge resolution"
msgstr "Изисква коригиране при сливане"
-#: git-gui.sh:2164
+#: git-gui.sh:2170
msgid "Starting gitk... please wait..."
msgstr "Стартиране на „gitk“…, изчакайте…"
-#: git-gui.sh:2176
+#: git-gui.sh:2182
msgid "Couldn't find gitk in PATH"
msgstr "Командата „gitk“ липсва в пътищата, определени от променливата PATH."
-#: git-gui.sh:2235
+#: git-gui.sh:2241
msgid "Couldn't find git gui in PATH"
msgstr ""
"Командата „git gui“ липсва в пътищата, определени от променливата PATH."
-#: git-gui.sh:2654 lib/choose_repository.tcl:41
+#: git-gui.sh:2676 lib/choose_repository.tcl:41
msgid "Repository"
msgstr "Хранилище"
-#: git-gui.sh:2655
+#: git-gui.sh:2677
msgid "Edit"
msgstr "Редактиране"
-#: git-gui.sh:2657 lib/choose_rev.tcl:567
+#: git-gui.sh:2679 lib/choose_rev.tcl:567
msgid "Branch"
msgstr "Клон"
-#: git-gui.sh:2660 lib/choose_rev.tcl:554
+#: git-gui.sh:2682 lib/choose_rev.tcl:554
msgid "Commit@@noun"
msgstr "Подаване"
-#: git-gui.sh:2663 lib/merge.tcl:123 lib/merge.tcl:152 lib/merge.tcl:170
+#: git-gui.sh:2685 lib/merge.tcl:127 lib/merge.tcl:174
msgid "Merge"
msgstr "Сливане"
-#: git-gui.sh:2664 lib/choose_rev.tcl:563
+#: git-gui.sh:2686 lib/choose_rev.tcl:563
msgid "Remote"
msgstr "Отдалечено хранилище"
-#: git-gui.sh:2667
+#: git-gui.sh:2689
msgid "Tools"
msgstr "Команди"
-#: git-gui.sh:2676
+#: git-gui.sh:2698
msgid "Explore Working Copy"
msgstr "Разглеждане на работното копие"
-#: git-gui.sh:2682
+#: git-gui.sh:2704
msgid "Git Bash"
msgstr "Bash за Git"
-#: git-gui.sh:2692
+#: git-gui.sh:2714
msgid "Browse Current Branch's Files"
msgstr "Разглеждане на файловете в текущия клон"
-#: git-gui.sh:2696
+#: git-gui.sh:2718
msgid "Browse Branch Files..."
msgstr "Разглеждане на текущия клон…"
-#: git-gui.sh:2701
+#: git-gui.sh:2723
msgid "Visualize Current Branch's History"
msgstr "Визуализация на историята на текущия клон"
-#: git-gui.sh:2705
+#: git-gui.sh:2727
msgid "Visualize All Branch History"
msgstr "Визуализация на историята на всички клонове"
-#: git-gui.sh:2712
+#: git-gui.sh:2734
#, tcl-format
msgid "Browse %s's Files"
-msgstr "Разглеждане на файловете в %s"
+msgstr "Разглеждане на файловете в „%s“"
-#: git-gui.sh:2714
+#: git-gui.sh:2736
#, tcl-format
msgid "Visualize %s's History"
-msgstr "Визуализация на историята на %s"
+msgstr "Визуализация на историята на „%s“"
-#: git-gui.sh:2719 lib/database.tcl:40 lib/database.tcl:66
+#: git-gui.sh:2741 lib/database.tcl:40
msgid "Database Statistics"
msgstr "Статистика на базата от данни"
-#: git-gui.sh:2722 lib/database.tcl:33
+#: git-gui.sh:2744 lib/database.tcl:33
msgid "Compress Database"
msgstr "Компресиране на базата от данни"
-#: git-gui.sh:2725
+#: git-gui.sh:2747
msgid "Verify Database"
msgstr "Проверка на базата от данни"
-#: git-gui.sh:2732 git-gui.sh:2736 git-gui.sh:2740 lib/shortcut.tcl:8
-#: lib/shortcut.tcl:40 lib/shortcut.tcl:72
+#: git-gui.sh:2754 git-gui.sh:2758 git-gui.sh:2762
msgid "Create Desktop Icon"
msgstr "Добавяне на икона на работния плот"
-#: git-gui.sh:2748 lib/choose_repository.tcl:193 lib/choose_repository.tcl:201
+#: git-gui.sh:2770 lib/choose_repository.tcl:193 lib/choose_repository.tcl:201
msgid "Quit"
msgstr "Спиране на програмата"
-#: git-gui.sh:2756
+#: git-gui.sh:2778
msgid "Undo"
msgstr "Отмяна"
-#: git-gui.sh:2759
+#: git-gui.sh:2781
msgid "Redo"
msgstr "Повторение"
-#: git-gui.sh:2763 git-gui.sh:3368
+#: git-gui.sh:2785 git-gui.sh:3399
msgid "Cut"
msgstr "Отрязване"
-#: git-gui.sh:2766 git-gui.sh:3371 git-gui.sh:3445 git-gui.sh:3530
+#: git-gui.sh:2788 git-gui.sh:3402 git-gui.sh:3476 git-gui.sh:3562
#: lib/console.tcl:69
msgid "Copy"
msgstr "Копиране"
-#: git-gui.sh:2769 git-gui.sh:3374
+#: git-gui.sh:2791 git-gui.sh:3405
msgid "Paste"
msgstr "Поставяне"
-#: git-gui.sh:2772 git-gui.sh:3377 lib/remote_branch_delete.tcl:39
-#: lib/branch_delete.tcl:28
+#: git-gui.sh:2794 git-gui.sh:3408 lib/branch_delete.tcl:28
+#: lib/remote_branch_delete.tcl:39
msgid "Delete"
msgstr "Изтриване"
-#: git-gui.sh:2776 git-gui.sh:3381 git-gui.sh:3534 lib/console.tcl:71
+#: git-gui.sh:2798 git-gui.sh:3412 git-gui.sh:3566 lib/console.tcl:71
msgid "Select All"
msgstr "Избиране на всичко"
-#: git-gui.sh:2785
+#: git-gui.sh:2807
msgid "Create..."
msgstr "Създаване…"
-#: git-gui.sh:2791
+#: git-gui.sh:2813
msgid "Checkout..."
msgstr "Изтегляне…"
-#: git-gui.sh:2797
+#: git-gui.sh:2819
msgid "Rename..."
msgstr "Преименуване…"
-#: git-gui.sh:2802
+#: git-gui.sh:2824
msgid "Delete..."
msgstr "Изтриване…"
-#: git-gui.sh:2807
+#: git-gui.sh:2829
msgid "Reset..."
msgstr "Отмяна на промените…"
-#: git-gui.sh:2817
+#: git-gui.sh:2839
msgid "Done"
msgstr "Готово"
-#: git-gui.sh:2819
+#: git-gui.sh:2841
msgid "Commit@@verb"
msgstr "Подаване"
-#: git-gui.sh:2828 git-gui.sh:3309
+#: git-gui.sh:2850 git-gui.sh:3335
msgid "New Commit"
msgstr "Ново подаване"
-#: git-gui.sh:2836 git-gui.sh:3316
+#: git-gui.sh:2858 git-gui.sh:3342
msgid "Amend Last Commit"
msgstr "Поправяне на последното подаване"
-#: git-gui.sh:2846 git-gui.sh:3270 lib/remote_branch_delete.tcl:101
+#: git-gui.sh:2868 git-gui.sh:3296 lib/remote_branch_delete.tcl:101
msgid "Rescan"
msgstr "Обновяване"
-#: git-gui.sh:2852
+#: git-gui.sh:2874
msgid "Stage To Commit"
msgstr "Към индекса за подаване"
-#: git-gui.sh:2858
+#: git-gui.sh:2880
msgid "Stage Changed Files To Commit"
msgstr "Всички променени файлове към индекса за подаване"
-#: git-gui.sh:2864
+#: git-gui.sh:2886
msgid "Unstage From Commit"
msgstr "Изваждане от индекса за подаване"
-#: git-gui.sh:2870 lib/index.tcl:442
+#: git-gui.sh:2892 lib/index.tcl:442
msgid "Revert Changes"
msgstr "Връщане на оригинала"
-#: git-gui.sh:2878 git-gui.sh:3581 git-gui.sh:3612
+#: git-gui.sh:2900 git-gui.sh:3613 git-gui.sh:3644
msgid "Show Less Context"
msgstr "По-малко контекст"
-#: git-gui.sh:2882 git-gui.sh:3585 git-gui.sh:3616
+#: git-gui.sh:2904 git-gui.sh:3617 git-gui.sh:3648
msgid "Show More Context"
msgstr "Повече контекст"
-#: git-gui.sh:2889 git-gui.sh:3283 git-gui.sh:3392
+#: git-gui.sh:2911 git-gui.sh:3309 git-gui.sh:3423
msgid "Sign Off"
msgstr "Подписване"
-#: git-gui.sh:2905
+#: git-gui.sh:2927
msgid "Local Merge..."
msgstr "Локално сливане…"
-#: git-gui.sh:2910
+#: git-gui.sh:2932
msgid "Abort Merge..."
msgstr "Преустановяване на сливане…"
-#: git-gui.sh:2922 git-gui.sh:2950
+#: git-gui.sh:2944 git-gui.sh:2972
msgid "Add..."
msgstr "Добавяне…"
-#: git-gui.sh:2926
+#: git-gui.sh:2948
msgid "Push..."
-msgstr "Избутване…"
+msgstr "Изтласкване…"
-#: git-gui.sh:2930
+#: git-gui.sh:2952
msgid "Delete Branch..."
msgstr "Изтриване на клон…"
-#: git-gui.sh:2940 git-gui.sh:3563
+#: git-gui.sh:2962 git-gui.sh:3595
msgid "Options..."
msgstr "Опции…"
-#: git-gui.sh:2951
+#: git-gui.sh:2973
msgid "Remove..."
msgstr "Премахване…"
-#: git-gui.sh:2960 lib/choose_repository.tcl:55
+#: git-gui.sh:2982 lib/choose_repository.tcl:55
msgid "Help"
msgstr "Помощ"
-#: git-gui.sh:2964 git-gui.sh:2968 lib/choose_repository.tcl:49
-#: lib/choose_repository.tcl:58 lib/about.tcl:14
+#: git-gui.sh:2986 git-gui.sh:2990 lib/about.tcl:14
+#: lib/choose_repository.tcl:49 lib/choose_repository.tcl:58
#, tcl-format
msgid "About %s"
msgstr "Относно %s"
-#: git-gui.sh:2992
+#: git-gui.sh:3014
msgid "Online Documentation"
msgstr "Документация в Интернет"
-#: git-gui.sh:2995 lib/choose_repository.tcl:52 lib/choose_repository.tcl:61
+#: git-gui.sh:3017 lib/choose_repository.tcl:52 lib/choose_repository.tcl:61
msgid "Show SSH Key"
msgstr "Показване на ключа за SSH"
-#: git-gui.sh:3014 git-gui.sh:3146
+#: git-gui.sh:3032 git-gui.sh:3164
+msgid "usage:"
+msgstr "употреба:"
+
+#: git-gui.sh:3036 git-gui.sh:3168
msgid "Usage"
msgstr "Употреба"
-#: git-gui.sh:3095 lib/blame.tcl:573
+#: git-gui.sh:3117 lib/blame.tcl:573
msgid "Error"
msgstr "Грешка"
-#: git-gui.sh:3126
+#: git-gui.sh:3148
#, tcl-format
msgid "fatal: cannot stat path %s: No such file or directory"
msgstr ""
"ФАТАЛНА ГРЕШКА: пътят %s не може да бъде открит: такъв файл или директория "
"няма"
-#: git-gui.sh:3159
+#: git-gui.sh:3181
msgid "Current Branch:"
msgstr "Текущ клон:"
-#: git-gui.sh:3185
-msgid "Staged Changes (Will Commit)"
-msgstr "Промени в индекса (за подаване)"
-
-#: git-gui.sh:3205
+#: git-gui.sh:3206
msgid "Unstaged Changes"
msgstr "Промени извън индекса"
-#: git-gui.sh:3276
+#: git-gui.sh:3228
+msgid "Staged Changes (Will Commit)"
+msgstr "Промени в индекса (за подаване)"
+
+#: git-gui.sh:3302
msgid "Stage Changed"
msgstr "Индексът е променен"
-#: git-gui.sh:3295 lib/transport.tcl:137 lib/transport.tcl:229
+#: git-gui.sh:3321 lib/transport.tcl:137
msgid "Push"
msgstr "Изтласкване"
-#: git-gui.sh:3330
+#: git-gui.sh:3356
msgid "Initial Commit Message:"
msgstr "Първоначално съобщение при подаване:"
-#: git-gui.sh:3331
+#: git-gui.sh:3357
msgid "Amended Commit Message:"
msgstr "Поправено съобщение при подаване:"
-#: git-gui.sh:3332
+#: git-gui.sh:3358
msgid "Amended Initial Commit Message:"
msgstr "Поправено първоначално съобщение при подаване:"
-#: git-gui.sh:3333
+#: git-gui.sh:3359
msgid "Amended Merge Commit Message:"
msgstr "Поправено съобщение при подаване със сливане:"
-#: git-gui.sh:3334
+#: git-gui.sh:3360
msgid "Merge Commit Message:"
msgstr "Съобщение при подаване със сливане:"
-#: git-gui.sh:3335
+#: git-gui.sh:3361
msgid "Commit Message:"
msgstr "Съобщение при подаване:"
-#: git-gui.sh:3384 git-gui.sh:3538 lib/console.tcl:73
+#: git-gui.sh:3415 git-gui.sh:3570 lib/console.tcl:73
msgid "Copy All"
msgstr "Копиране на всичко"
-#: git-gui.sh:3408 lib/blame.tcl:105
+#: git-gui.sh:3439 lib/blame.tcl:105
msgid "File:"
msgstr "Файл:"
-#: git-gui.sh:3526
+#: git-gui.sh:3558
msgid "Refresh"
msgstr "Обновяване"
-#: git-gui.sh:3547
+#: git-gui.sh:3579
msgid "Decrease Font Size"
msgstr "По-едър шрифт"
-#: git-gui.sh:3551
+#: git-gui.sh:3583
msgid "Increase Font Size"
msgstr "По-дребен шрифт"
-#: git-gui.sh:3559 lib/blame.tcl:294
+#: git-gui.sh:3591 lib/blame.tcl:294
msgid "Encoding"
msgstr "Кодиране"
-#: git-gui.sh:3570
+#: git-gui.sh:3602
msgid "Apply/Reverse Hunk"
msgstr "Прилагане/връщане на парче"
-#: git-gui.sh:3575
+#: git-gui.sh:3607
msgid "Apply/Reverse Line"
msgstr "Прилагане/връщане на ред"
-#: git-gui.sh:3594
+#: git-gui.sh:3626
msgid "Run Merge Tool"
msgstr "Изпълнение на програмата за сливане"
-#: git-gui.sh:3599
+#: git-gui.sh:3631
msgid "Use Remote Version"
msgstr "Версия от отдалеченото хранилище"
-#: git-gui.sh:3603
+#: git-gui.sh:3635
msgid "Use Local Version"
msgstr "Локална версия"
-#: git-gui.sh:3607
+#: git-gui.sh:3639
msgid "Revert To Base"
msgstr "Връщане към родителската версия"
-#: git-gui.sh:3625
+#: git-gui.sh:3657
msgid "Visualize These Changes In The Submodule"
msgstr "Визуализиране на промените в подмодула"
-#: git-gui.sh:3629
+#: git-gui.sh:3661
msgid "Visualize Current Branch History In The Submodule"
msgstr "Визуализация на историята на текущия клон в историята за подмодула"
-#: git-gui.sh:3633
+#: git-gui.sh:3665
msgid "Visualize All Branch History In The Submodule"
msgstr "Визуализация на историята на всички клони в историята за подмодула"
-#: git-gui.sh:3638
+#: git-gui.sh:3670
msgid "Start git gui In The Submodule"
msgstr "Стартиране на „git gui“ за подмодула"
-#: git-gui.sh:3673
+#: git-gui.sh:3705
msgid "Unstage Hunk From Commit"
msgstr "Изваждане на парчето от подаването"
-#: git-gui.sh:3675
+#: git-gui.sh:3707
msgid "Unstage Lines From Commit"
msgstr "Изваждане на редовете от подаването"
-#: git-gui.sh:3677
+#: git-gui.sh:3709
msgid "Unstage Line From Commit"
msgstr "Изваждане на реда от подаването"
-#: git-gui.sh:3680
+#: git-gui.sh:3712
msgid "Stage Hunk For Commit"
msgstr "Добавяне на парчето за подаване"
-#: git-gui.sh:3682
+#: git-gui.sh:3714
msgid "Stage Lines For Commit"
msgstr "Добавяне на редовете за подаване"
-#: git-gui.sh:3684
+#: git-gui.sh:3716
msgid "Stage Line For Commit"
msgstr "Добавяне на реда за подаване"
-#: git-gui.sh:3709
+#: git-gui.sh:3741
msgid "Initializing..."
msgstr "Инициализиране…"
-#: git-gui.sh:3852
+#: git-gui.sh:3886
#, tcl-format
msgid ""
"Possible environment issues exist.\n"
"от %s:\n"
"\n"
-#: git-gui.sh:3881
+#: git-gui.sh:3915
msgid ""
"\n"
"This is due to a known issue with the\n"
"Това е познат проблем и се дължи на\n"
"версията на Tcl включена в Cygwin."
-#: git-gui.sh:3886
+#: git-gui.sh:3920
#, tcl-format
msgid ""
"\n"
"е да поставите настройките „user.name“ и\n"
"„user.email“ в личния си файл „~/.gitconfig“.\n"
-#: lib/spellcheck.tcl:57
-msgid "Unsupported spell checker"
-msgstr "Тази програма за проверка на правописа не се поддържа"
-
-#: lib/spellcheck.tcl:65
-msgid "Spell checking is unavailable"
-msgstr "Липсва програма за проверка на правописа"
-
-#: lib/spellcheck.tcl:68
-msgid "Invalid spell checking configuration"
-msgstr "Неправилни настройки на проверката на правописа"
+#: lib/about.tcl:26
+msgid "git-gui - a graphical user interface for Git."
+msgstr "git-gui — графичен интерфейс за Git."
-#: lib/spellcheck.tcl:70
+#: lib/blame.tcl:73
#, tcl-format
-msgid "Reverting dictionary to %s."
-msgstr "Ползване на речник за език „%s“."
-
-#: lib/spellcheck.tcl:73
-msgid "Spell checker silently failed on startup"
-msgstr "Програмата за правопис даже не стартира успешно."
-
-#: lib/spellcheck.tcl:80
-msgid "Unrecognized spell checker"
-msgstr "Непозната програма за проверка на правописа"
-
-#: lib/spellcheck.tcl:186
-msgid "No Suggestions"
-msgstr "Няма предложения"
-
-#: lib/spellcheck.tcl:388
-msgid "Unexpected EOF from spell checker"
-msgstr "Неочакван край на файл от програмата за проверка на правописа"
-
-#: lib/spellcheck.tcl:392
-msgid "Spell Checker Failed"
-msgstr "Грешка в програмата за проверка на правописа"
-
-#: lib/remote_add.tcl:20
-msgid "Add Remote"
-msgstr "Добавяне на отдалечено хранилище"
-
-#: lib/remote_add.tcl:25
-msgid "Add New Remote"
-msgstr "Добавяне на отдалечено хранилище"
-
-#: lib/remote_add.tcl:30 lib/tools_dlg.tcl:37
-msgid "Add"
-msgstr "Добавяне"
-
-#: lib/remote_add.tcl:34 lib/browser.tcl:292 lib/branch_checkout.tcl:30
-#: lib/transport.tcl:141 lib/branch_rename.tcl:32 lib/choose_font.tcl:45
-#: lib/option.tcl:127 lib/tools_dlg.tcl:41 lib/tools_dlg.tcl:202
-#: lib/tools_dlg.tcl:345 lib/remote_branch_delete.tcl:43
-#: lib/checkout_op.tcl:579 lib/branch_create.tcl:37 lib/branch_delete.tcl:34
-#: lib/merge.tcl:174
-msgid "Cancel"
-msgstr "Отказване"
-
-#: lib/remote_add.tcl:39
-msgid "Remote Details"
-msgstr "Данни за отдалеченото хранилище"
-
-#: lib/remote_add.tcl:41 lib/tools_dlg.tcl:51 lib/branch_create.tcl:44
-msgid "Name:"
-msgstr "Име:"
+msgid "%s (%s): File Viewer"
+msgstr "%s (%s): Преглед на файлове"
-#: lib/remote_add.tcl:50
-msgid "Location:"
-msgstr "Ð\9cеÑ\81Ñ\82оположение:"
+#: lib/blame.tcl:79
+msgid "Commit:"
+msgstr "Ð\9fодаване:"
-#: lib/remote_add.tcl:60
-msgid "Further Action"
-msgstr "СледваÑ\89о дейÑ\81Ñ\82вие"
+#: lib/blame.tcl:280
+msgid "Copy Commit"
+msgstr "Ð\9aопиÑ\80ане на подаване"
-#: lib/remote_add.tcl:63
-msgid "Fetch Immediately"
-msgstr "Ð\9dезабавно доÑ\81Ñ\82авÑ\8fне"
+#: lib/blame.tcl:284
+msgid "Find Text..."
+msgstr "ТÑ\8aÑ\80Ñ\81ене на Ñ\82екÑ\81Ñ\82â\80¦"
-#: lib/remote_add.tcl:69
-msgid "Initialize Remote Repository and Push"
-msgstr "Ð\98ниÑ\86иализиÑ\80ане на оÑ\82далеÑ\87еноÑ\82о Ñ\85Ñ\80анилиÑ\89е и изÑ\82лаÑ\81кване на пÑ\80омениÑ\82е"
+#: lib/blame.tcl:288
+msgid "Goto Line..."
+msgstr "Ð\9aÑ\8aм Ñ\80едâ\80¦"
-#: lib/remote_add.tcl:75
-msgid "Do Nothing Else Now"
-msgstr "Ð\94а не Ñ\81е пÑ\80ави ниÑ\89о"
+#: lib/blame.tcl:297
+msgid "Do Full Copy Detection"
+msgstr "Ð\9fÑ\8aлно Ñ\82Ñ\8aÑ\80Ñ\81ене на копиÑ\80ане"
-#: lib/remote_add.tcl:100
-msgid "Please supply a remote name."
-msgstr "Ð\97адайÑ\82е име за оÑ\82далеÑ\87еноÑ\82о Ñ\85Ñ\80анилиÑ\89е."
+#: lib/blame.tcl:301
+msgid "Show History Context"
+msgstr "Ð\9fоказване на конÑ\82екÑ\81Ñ\82а оÑ\82 иÑ\81Ñ\82оÑ\80иÑ\8fÑ\82а"
-#: lib/remote_add.tcl:113
-#, tcl-format
-msgid "'%s' is not an acceptable remote name."
-msgstr "Отдалечено хранилище не може да се казва „%s“."
+#: lib/blame.tcl:304
+msgid "Blame Parent Commit"
+msgstr "Анотиране на родителското подаване"
-#: lib/remote_add.tcl:124
+#: lib/blame.tcl:466
#, tcl-format
-msgid "Failed to add remote '%s' of location '%s'."
-msgstr "Ð\9dеÑ\83Ñ\81пеÑ\88но добавÑ\8fне на оÑ\82далеÑ\87еноÑ\82о Ñ\85Ñ\80анилиÑ\89е â\80\9e%sâ\80\9c оÑ\82 адÑ\80еÑ\81 â\80\9e%sâ\80\9c."
+msgid "Reading %s..."
+msgstr "ЧеÑ\82е Ñ\81е â\80\9e%sâ\80\9câ\80¦"
-#: lib/remote_add.tcl:132 lib/transport.tcl:6
-#, tcl-format
-msgid "fetch %s"
-msgstr "доставяне на „%s“"
+#: lib/blame.tcl:594
+msgid "Loading copy/move tracking annotations..."
+msgstr "Зареждане на анотациите за проследяване на копирането/преместването…"
-#: lib/remote_add.tcl:133
-#, tcl-format
-msgid "Fetching the %s"
-msgstr "Доставяне на „%s“"
+#: lib/blame.tcl:614
+msgid "lines annotated"
+msgstr "реда анотирани"
-#: lib/remote_add.tcl:156
-#, tcl-format
-msgid "Do not know how to initialize repository at location '%s'."
-msgstr "Хранилището с местоположение „%s“ не може да бъде инициализирано."
+#: lib/blame.tcl:806
+msgid "Loading original location annotations..."
+msgstr "Зареждане на анотациите за първоначалното местоположение…"
-#: lib/remote_add.tcl:162 lib/transport.tcl:54 lib/transport.tcl:92
-#: lib/transport.tcl:110
-#, tcl-format
-msgid "push %s"
-msgstr "изтласкване на „%s“"
+#: lib/blame.tcl:809
+msgid "Annotation complete."
+msgstr "Анотирането завърши."
-#: lib/remote_add.tcl:163
-#, tcl-format
-msgid "Setting up the %s (at %s)"
-msgstr "Добавяне на хранилище „%s“ (с адрес „%s“)"
+#: lib/blame.tcl:839
+msgid "Busy"
+msgstr "Операцията не е завършила"
-#: lib/browser.tcl:17
-msgid "Starting..."
-msgstr "СÑ\82аÑ\80Ñ\82иÑ\80анеâ\80¦"
+#: lib/blame.tcl:840
+msgid "Annotation process is already running."
+msgstr "Ð\92 моменÑ\82а Ñ\82еÑ\87е пÑ\80оÑ\86еÑ\81 на аноÑ\82иÑ\80ане."
-#: lib/browser.tcl:27
-msgid "File Browser"
-msgstr "Файлов бÑ\80аÑ\83зÑ\8aÑ\80"
+#: lib/blame.tcl:879
+msgid "Running thorough copy detection..."
+msgstr "Ð\98зпÑ\8aлнÑ\8fва Ñ\81е Ñ\86Ñ\8fлоÑ\81Ñ\82ен пÑ\80оÑ\86еÑ\81 на оÑ\82кÑ\80иване на копиÑ\80анеâ\80¦"
-#: lib/browser.tcl:132 lib/browser.tcl:149
-#, tcl-format
-msgid "Loading %s..."
-msgstr "Зареждане на „%s“…"
+#: lib/blame.tcl:947
+msgid "Loading annotation..."
+msgstr "Зареждане на анотации…"
-#: lib/browser.tcl:193
-msgid "[Up To Parent]"
-msgstr "[Към родителя]"
+#: lib/blame.tcl:1000
+msgid "Author:"
+msgstr "Автор:"
-#: lib/browser.tcl:275 lib/browser.tcl:282
-msgid "Browse Branch Files"
-msgstr "Разглеждане на Ñ\84айловеÑ\82е в клона"
+#: lib/blame.tcl:1004
+msgid "Committer:"
+msgstr "Ð\9fодал:"
-#: lib/browser.tcl:288 lib/choose_repository.tcl:422
-#: lib/choose_repository.tcl:509 lib/choose_repository.tcl:518
-#: lib/choose_repository.tcl:1074
-msgid "Browse"
-msgstr "Разглеждане"
+#: lib/blame.tcl:1009
+msgid "Original File:"
+msgstr "Първоначален файл:"
-#: lib/browser.tcl:297 lib/branch_checkout.tcl:35 lib/tools_dlg.tcl:321
-msgid "Revision"
-msgstr "Ð\92еÑ\80Ñ\81иÑ\8f"
+#: lib/blame.tcl:1057
+msgid "Cannot find HEAD commit:"
+msgstr "Ð\9fодаванеÑ\82о за вÑ\80Ñ\8aÑ\85 â\80\9eHEADâ\80\9c не може да Ñ\81е оÑ\82кÑ\80ие:"
-#: lib/tools.tcl:75
-#, tcl-format
-msgid "Running %s requires a selected file."
-msgstr "За изпълнението на „%s“ трябва да изберете файл."
+#: lib/blame.tcl:1112
+msgid "Cannot find parent commit:"
+msgstr "Родителското подаване не може да бъде открито"
-#: lib/tools.tcl:91
-#, tcl-format
-msgid "Are you sure you want to run %1$s on file \"%2$s\"?"
-msgstr "Сигурни ли сте, че искате да изпълните „%1$s“ върху файла „%2$s“?"
+#: lib/blame.tcl:1127
+msgid "Unable to display parent"
+msgstr "Родителят не може да бъде показан"
-#: lib/tools.tcl:95
-#, tcl-format
-msgid "Are you sure you want to run %s?"
-msgstr "Сигурни ли сте, че искате да изпълните „%s“?"
+#: lib/blame.tcl:1128 lib/diff.tcl:358
+msgid "Error loading diff:"
+msgstr "Грешка при зареждане на разлика:"
-#: lib/tools.tcl:116
-#, tcl-format
-msgid "Tool: %s"
-msgstr "Команда: %s"
+#: lib/blame.tcl:1269
+msgid "Originally By:"
+msgstr "Първоначално от:"
-#: lib/tools.tcl:117
-#, tcl-format
-msgid "Running: %s"
-msgstr "Изпълнение: %s"
+#: lib/blame.tcl:1275
+msgid "In File:"
+msgstr "Във файл:"
-#: lib/tools.tcl:155
-#, tcl-format
-msgid "Tool completed successfully: %s"
-msgstr "Командата завърши успешно: %s"
+#: lib/blame.tcl:1280
+msgid "Copied Or Moved Here By:"
+msgstr "Копирано или преместено тук от:"
-#: lib/tools.tcl:157
+#: lib/branch_checkout.tcl:16
#, tcl-format
-msgid "Tool failed: %s"
-msgstr "Командата върна грешка: %s"
+msgid "%s (%s): Checkout Branch"
+msgstr "%s (%s): Клон за изтегляне"
-#: lib/branch_checkout.tcl:16 lib/branch_checkout.tcl:21
+#: lib/branch_checkout.tcl:21
msgid "Checkout Branch"
msgstr "Клон за изтегляне"
msgid "Checkout"
msgstr "Изтегляне"
-#: lib/branch_checkout.tcl:39 lib/option.tcl:310 lib/branch_create.tcl:69
+#: lib/branch_checkout.tcl:30 lib/branch_create.tcl:37 lib/branch_delete.tcl:34
+#: lib/branch_rename.tcl:32 lib/browser.tcl:292 lib/checkout_op.tcl:579
+#: lib/choose_font.tcl:45 lib/merge.tcl:178 lib/option.tcl:127
+#: lib/remote_add.tcl:34 lib/remote_branch_delete.tcl:43 lib/tools_dlg.tcl:41
+#: lib/tools_dlg.tcl:202 lib/tools_dlg.tcl:345 lib/transport.tcl:141
+msgid "Cancel"
+msgstr "Отказване"
+
+#: lib/branch_checkout.tcl:35 lib/browser.tcl:297 lib/tools_dlg.tcl:321
+msgid "Revision"
+msgstr "Версия"
+
+#: lib/branch_checkout.tcl:39 lib/branch_create.tcl:69 lib/option.tcl:310
msgid "Options"
msgstr "Опции"
msgid "Detach From Local Branch"
msgstr "Изтриване от локалния клон"
-#: lib/transport.tcl:7
+#: lib/branch_create.tcl:23
#, tcl-format
-msgid "Fetching new changes from %s"
-msgstr "Доставяне на промените от „%s“"
+msgid "%s (%s): Create Branch"
+msgstr "%s (%s): Създаване на клон"
-#: lib/transport.tcl:18
-#, tcl-format
-msgid "remote prune %s"
-msgstr "окастряне на следящите клони към „%s“"
-
-#: lib/transport.tcl:19
-#, tcl-format
-msgid "Pruning tracking branches deleted from %s"
-msgstr "Окастряне на следящите клони на изтритите клони от „%s“"
-
-#: lib/transport.tcl:25
-msgid "fetch all remotes"
-msgstr "доставяне на всички отдалечени хранилища"
-
-#: lib/transport.tcl:26
-msgid "Fetching new changes from all remotes"
-msgstr "Доставяне на новите промени от всички отдалечени хранилища"
-
-#: lib/transport.tcl:40
-msgid "remote prune all remotes"
-msgstr "окастряне на всички следящи клони"
+#: lib/branch_create.tcl:28
+msgid "Create New Branch"
+msgstr "Създаване на нов клон"
-#: lib/transport.tcl:41
-msgid "Pruning tracking branches deleted from all remotes"
-msgstr ""
-"Окастряне на всички клони, които следят изтрити клони от отдалечени хранилища"
+#: lib/branch_create.tcl:33 lib/choose_repository.tcl:407
+msgid "Create"
+msgstr "Създаване"
-#: lib/transport.tcl:55
-#, tcl-format
-msgid "Pushing changes to %s"
-msgstr "Изтласкване на промените към „%s“"
+#: lib/branch_create.tcl:42
+msgid "Branch Name"
+msgstr "Име на клона"
-#: lib/transport.tcl:93
-#, tcl-format
-msgid "Mirroring to %s"
-msgstr "Изтласкване на всичко към „%s“"
+#: lib/branch_create.tcl:44 lib/remote_add.tcl:41 lib/tools_dlg.tcl:51
+msgid "Name:"
+msgstr "Име:"
-#: lib/transport.tcl:111
-#, tcl-format
-msgid "Pushing %s %s to %s"
-msgstr "Изтласкване на %s „%s“ към „%s“"
+#: lib/branch_create.tcl:57
+msgid "Match Tracking Branch Name"
+msgstr "Съвпадане по името на следения клон"
-#: lib/transport.tcl:132
-msgid "Push Branches"
-msgstr "Ð\9aлони за изÑ\82лаÑ\81кване"
+#: lib/branch_create.tcl:66
+msgid "Starting Revision"
+msgstr "Ð\9dаÑ\87ална веÑ\80Ñ\81иÑ\8f"
-#: lib/transport.tcl:147
-msgid "Source Branches"
-msgstr "Ð\9aлони-изÑ\82оÑ\87ниÑ\86и"
+#: lib/branch_create.tcl:72
+msgid "Update Existing Branch:"
+msgstr "Ð\9eбновÑ\8fване на Ñ\81Ñ\8aÑ\89еÑ\81Ñ\82вÑ\83ваÑ\89 клон:"
-#: lib/transport.tcl:162
-msgid "Destination Repository"
-msgstr "Целево Ñ\85Ñ\80анилиÑ\89е"
+#: lib/branch_create.tcl:75
+msgid "No"
+msgstr "Ð\9dе"
-#: lib/transport.tcl:165 lib/remote_branch_delete.tcl:51
-msgid "Remote:"
-msgstr "Ð\9eÑ\82далеÑ\87ено Ñ\85Ñ\80анилиÑ\89е:"
+#: lib/branch_create.tcl:80
+msgid "Fast Forward Only"
+msgstr "Само Ñ\82Ñ\80ивиално пÑ\80евÑ\8aÑ\80Ñ\82аÑ\89о Ñ\81ливане"
-#: lib/transport.tcl:187 lib/remote_branch_delete.tcl:72
-msgid "Arbitrary Location:"
-msgstr "Ð\9fÑ\80оизволно меÑ\81Ñ\82оположение:"
+#: lib/branch_create.tcl:85 lib/checkout_op.tcl:571
+msgid "Reset"
+msgstr "Ð\9eÑ\82наÑ\87ало"
-#: lib/transport.tcl:205
-msgid "Transfer Options"
-msgstr "Ð\9dаÑ\81Ñ\82Ñ\80ойки пÑ\80и пÑ\80енаÑ\81Ñ\8fнеÑ\82о"
+#: lib/branch_create.tcl:97
+msgid "Checkout After Creation"
+msgstr "Ð\9fÑ\80еминаване кÑ\8aм клона Ñ\81лед Ñ\81Ñ\8aздаванеÑ\82о мÑ\83"
-#: lib/transport.tcl:207
-msgid "Force overwrite existing branch (may discard changes)"
-msgstr ""
-"Изрично презаписване на съществуващ клон (някои промени може да бъдат "
-"загубени)"
+#: lib/branch_create.tcl:132
+msgid "Please select a tracking branch."
+msgstr "Изберете клон за следени."
-#: lib/transport.tcl:211
-msgid "Use thin pack (for slow network connections)"
-msgstr "Максимална компресия (за бавни мрежови връзки)"
+#: lib/branch_create.tcl:141
+#, tcl-format
+msgid "Tracking branch %s is not a branch in the remote repository."
+msgstr "Следящият клон — „%s“, не съществува в отдалеченото хранилище."
-#: lib/transport.tcl:215
-msgid "Include tags"
-msgstr "Ð\92клÑ\8eÑ\87ване на еÑ\82икеÑ\82иÑ\82е"
+#: lib/branch_create.tcl:154 lib/branch_rename.tcl:92
+msgid "Please supply a branch name."
+msgstr "Ð\94айÑ\82е име на клона."
-#: lib/status_bar.tcl:87
+#: lib/branch_create.tcl:165 lib/branch_rename.tcl:112
#, tcl-format
-msgid "%s ... %*i of %*i %s (%3i%%)"
-msgstr "%s… %*i от общо %*i %s (%3i%%)"
+msgid "'%s' is not an acceptable branch name."
+msgstr "„%s“ не може да се използва за име на клон."
-#: lib/remote.tcl:200
-msgid "Push to"
-msgstr "Изтласкване към"
+#: lib/branch_delete.tcl:16
+#, tcl-format
+msgid "%s (%s): Delete Branch"
+msgstr "%s (%s): Изтриване на клон"
-#: lib/remote.tcl:218
-msgid "Remove Remote"
-msgstr "Ð\9fÑ\80емаÑ\85ване на оÑ\82далеÑ\87ено Ñ\85Ñ\80анилиÑ\89е"
+#: lib/branch_delete.tcl:21
+msgid "Delete Local Branch"
+msgstr "Ð\98зÑ\82Ñ\80иване на локален клон"
-#: lib/remote.tcl:223
-msgid "Prune from"
-msgstr "Ð\9eкаÑ\81Ñ\82Ñ\80Ñ\8fне оÑ\82"
+#: lib/branch_delete.tcl:39
+msgid "Local Branches"
+msgstr "Ð\9bокални клони"
-#: lib/remote.tcl:228
-msgid "Fetch from"
-msgstr "Ð\94оÑ\81Ñ\82авÑ\8fне оÑ\82"
+#: lib/branch_delete.tcl:51
+msgid "Delete Only If Merged Into"
+msgstr "Ð\98зÑ\82Ñ\80иване, Ñ\81амо ако пÑ\80омениÑ\82е Ñ\81а Ñ\81леÑ\82и и дÑ\80Ñ\83гаде"
-#: lib/sshkey.tcl:31
-msgid "No keys found."
-msgstr "Ð\9dе Ñ\81а оÑ\82кÑ\80иÑ\82и клÑ\8eÑ\87ове."
+#: lib/branch_delete.tcl:53 lib/remote_branch_delete.tcl:120
+msgid "Always (Do not perform merge checks)"
+msgstr "Ð\92инаги (без пÑ\80овеÑ\80ка за Ñ\81ливане)"
-#: lib/sshkey.tcl:34
+#: lib/branch_delete.tcl:103
#, tcl-format
-msgid "Found a public key in: %s"
-msgstr "Открит е публичен ключ в „%s“"
-
-#: lib/sshkey.tcl:40
-msgid "Generate Key"
-msgstr "Генериране на ключ"
-
-#: lib/sshkey.tcl:55 lib/checkout_op.tcl:146 lib/console.tcl:81
-#: lib/database.tcl:30
-msgid "Close"
-msgstr "Затваряне"
-
-#: lib/sshkey.tcl:58
-msgid "Copy To Clipboard"
-msgstr "Копиране към системния буфер"
+msgid "The following branches are not completely merged into %s:"
+msgstr "Не всички промени в клоните са слети в „%s“:"
-#: lib/sshkey.tcl:72
-msgid "Your OpenSSH Public Key"
-msgstr "Публичният ви ключ за OpenSSH"
+#: lib/branch_delete.tcl:115 lib/remote_branch_delete.tcl:218
+msgid ""
+"Recovering deleted branches is difficult.\n"
+"\n"
+"Delete the selected branches?"
+msgstr ""
+"Възстановяването на изтрити клони може да е трудно.\n"
+"\n"
+"Сигурни ли сте, че искате да триете?"
-#: lib/sshkey.tcl:80
-msgid "Generating..."
-msgstr "Генериране…"
+#: lib/branch_delete.tcl:131
+#, tcl-format
+msgid " - %s:"
+msgstr " — „%s:“"
-#: lib/sshkey.tcl:86
+#: lib/branch_delete.tcl:141
#, tcl-format
msgid ""
-"Could not start ssh-keygen:\n"
-"\n"
+"Failed to delete branches:\n"
"%s"
msgstr ""
-"Програмата „ssh-keygen“ не може да бъде стартирана:\n"
-"\n"
+"Неуспешно триене на клони:\n"
"%s"
-#: lib/sshkey.tcl:113
-msgid "Generation failed."
-msgstr "Неуспешно генериране."
-
-#: lib/sshkey.tcl:120
-msgid "Generation succeeded, but no keys found."
-msgstr "Генерирането завърши успешно, а не са намерени ключове."
-
-#: lib/sshkey.tcl:123
+#: lib/branch_rename.tcl:15
#, tcl-format
-msgid "Your key is in: %s"
-msgstr "Ключът ви е в „%s“"
+msgid "%s (%s): Rename Branch"
+msgstr "%s (%s): Преименуване на клон"
-#: lib/branch_rename.tcl:15 lib/branch_rename.tcl:23
+#: lib/branch_rename.tcl:23
msgid "Rename Branch"
msgstr "Преименуване на клон"
msgid "Please select a branch to rename."
msgstr "Изберете клон за преименуване."
-#: lib/branch_rename.tcl:92 lib/branch_create.tcl:154
-msgid "Please supply a branch name."
-msgstr "Дайте име на клона."
-
#: lib/branch_rename.tcl:102 lib/checkout_op.tcl:202
#, tcl-format
msgid "Branch '%s' already exists."
msgstr "Клонът „%s“ вече съществува."
-#: lib/branch_rename.tcl:112 lib/branch_create.tcl:165
-#, tcl-format
-msgid "'%s' is not an acceptable branch name."
-msgstr "„%s“ не може да се използва за име на клон."
-
#: lib/branch_rename.tcl:123
#, tcl-format
msgid "Failed to rename '%s'."
msgstr "Неуспешно преименуване на „%s“."
-#: lib/choose_font.tcl:41
-msgid "Select"
-msgstr "Избор"
-
-#: lib/choose_font.tcl:55
-msgid "Font Family"
-msgstr "Шрифт"
+#: lib/browser.tcl:17
+msgid "Starting..."
+msgstr "Стартиране…"
-#: lib/choose_font.tcl:76
-msgid "Font Size"
-msgstr "Размер"
+#: lib/browser.tcl:27
+#, tcl-format
+msgid "%s (%s): File Browser"
+msgstr "%s (%s): Файлов браузър"
-#: lib/choose_font.tcl:93
-msgid "Font Example"
-msgstr "Мостра"
+#: lib/browser.tcl:132 lib/browser.tcl:149
+#, tcl-format
+msgid "Loading %s..."
+msgstr "Зареждане на „%s“…"
-#: lib/choose_font.tcl:105
-msgid ""
-"This is example text.\n"
-"If you like this text, it can be your font."
-msgstr ""
-"Това е примерен текст.\n"
-"Ако ви харесва как изглежда, изберете шрифта."
+#: lib/browser.tcl:193
+msgid "[Up To Parent]"
+msgstr "[Към родителя]"
-#: lib/option.tcl:11
+#: lib/browser.tcl:275
#, tcl-format
-msgid "Invalid global encoding '%s'"
-msgstr "Неправилно глобално кодиране „%s“"
+msgid "%s (%s): Browse Branch Files"
+msgstr "%s (%s): Разглеждане на файловете в клона"
-#: lib/option.tcl:19
-#, tcl-format
-msgid "Invalid repo encoding '%s'"
-msgstr "Неправилно кодиране „%s“ на хранилището"
+#: lib/browser.tcl:282
+msgid "Browse Branch Files"
+msgstr "Разглеждане на файловете в клона"
-#: lib/option.tcl:119
-msgid "Restore Defaults"
-msgstr "Стандартни настройки"
+#: lib/browser.tcl:288 lib/choose_repository.tcl:422
+#: lib/choose_repository.tcl:509 lib/choose_repository.tcl:518
+#: lib/choose_repository.tcl:1074
+msgid "Browse"
+msgstr "Разглеждане"
-#: lib/option.tcl:123
-msgid "Save"
-msgstr "Запазване"
+#: lib/checkout_op.tcl:85
+#, tcl-format
+msgid "Fetching %s from %s"
+msgstr "Доставяне на „%s“ от „%s“"
-#: lib/option.tcl:133
+#: lib/checkout_op.tcl:133
#, tcl-format
-msgid "%s Repository"
-msgstr "Хранилище „%s“"
+msgid "fatal: Cannot resolve %s"
+msgstr "фатална грешка: „%s“ не може да се открие"
-#: lib/option.tcl:134
-msgid "Global (All Repositories)"
-msgstr "Глобално (за всички хранилища)"
+#: lib/checkout_op.tcl:146 lib/console.tcl:81 lib/database.tcl:30
+#: lib/sshkey.tcl:55
+msgid "Close"
+msgstr "Затваряне"
-#: lib/option.tcl:140
-msgid "User Name"
-msgstr "Потребителско име"
+#: lib/checkout_op.tcl:175
+#, tcl-format
+msgid "Branch '%s' does not exist."
+msgstr "Клонът „%s“ не съществува."
-#: lib/option.tcl:141
-msgid "Email Address"
-msgstr "Адрес на е-поща"
+#: lib/checkout_op.tcl:194
+#, tcl-format
+msgid "Failed to configure simplified git-pull for '%s'."
+msgstr "Неуспешно настройване на опростен git-pull за „%s“."
-#: lib/option.tcl:143
-msgid "Summarize Merge Commits"
-msgstr "Обобщаване на подаванията при сливане"
+#: lib/checkout_op.tcl:229
+#, tcl-format
+msgid ""
+"Branch '%s' already exists.\n"
+"\n"
+"It cannot fast-forward to %s.\n"
+"A merge is required."
+msgstr ""
+"Клонът „%s“ съществува.\n"
+"\n"
+"Той не може да бъде тривиално слят до „%s“.\n"
+"Необходимо е сливане."
-#: lib/option.tcl:144
-msgid "Merge Verbosity"
-msgstr "Подробности при сливанията"
+#: lib/checkout_op.tcl:243
+#, tcl-format
+msgid "Merge strategy '%s' not supported."
+msgstr "Стратегия за сливане „%s“ не се поддържа."
-#: lib/option.tcl:145
-msgid "Show Diffstat After Merge"
-msgstr "Извеждане на статистика след сливанията"
+#: lib/checkout_op.tcl:262
+#, tcl-format
+msgid "Failed to update '%s'."
+msgstr "Неуспешно обновяване на „%s“."
-#: lib/option.tcl:146
-msgid "Use Merge Tool"
-msgstr "Ð\98зползване на пÑ\80огÑ\80ама за Ñ\81ливане"
+#: lib/checkout_op.tcl:274
+msgid "Staging area (index) is already locked."
+msgstr "Ð\98ндекÑ\81Ñ\8aÑ\82 веÑ\87е е заклÑ\8eÑ\87ен."
-#: lib/option.tcl:148
-msgid "Trust File Modification Timestamps"
-msgstr "Доверие във времето на промяна на файловете"
+#: lib/checkout_op.tcl:289
+msgid ""
+"Last scanned state does not match repository state.\n"
+"\n"
+"Another Git program has modified this repository since the last scan. A "
+"rescan must be performed before the current branch can be changed.\n"
+"\n"
+"The rescan will be automatically started now.\n"
+msgstr ""
+"Състоянието при последната проверка не отговаря на състоянието на "
+"хранилището.\n"
+"\n"
+"Някой друг процес за Git е променил хранилището междувременно. Състоянието "
+"трябва да бъде проверено, преди да се премине към нов клон.\n"
+"\n"
+"Автоматично ще започне нова проверка.\n"
-#: lib/option.tcl:149
-msgid "Prune Tracking Branches During Fetch"
-msgstr "Окастряне на следящите клонове при доставяне"
+#: lib/checkout_op.tcl:345
+#, tcl-format
+msgid "Updating working directory to '%s'..."
+msgstr "Работната директория се привежда към „%s“…"
-#: lib/option.tcl:150
-msgid "Match Tracking Branches"
-msgstr "Напасване на следящите клонове"
+#: lib/checkout_op.tcl:346
+msgid "files checked out"
+msgstr "файла са изтеглени"
-#: lib/option.tcl:151
-msgid "Use Textconv For Diffs and Blames"
+#: lib/checkout_op.tcl:376
+#, tcl-format
+msgid "Aborted checkout of '%s' (file level merging is required)."
msgstr ""
-"Преобразуване на текста с „textconv“ при анотиране и извеждане на разлики"
+"Преустановяване на изтеглянето на „%s“ (необходимо е пофайлово сливане)."
-#: lib/option.tcl:152
-msgid "Blame Copy Only On Changed Files"
-msgstr "Ð\90ноÑ\82иÑ\80ане на копиеÑ\82о Ñ\81амо по пÑ\80оменениÑ\82е Ñ\84айлове"
+#: lib/checkout_op.tcl:377
+msgid "File level merge required."
+msgstr "Ð\9dеобÑ\85одимо е поÑ\84айлово Ñ\81ливане."
-#: lib/option.tcl:153
-msgid "Maximum Length of Recent Repositories List"
-msgstr "Максимална дължина на списъка със скоро ползвани хранилища"
+#: lib/checkout_op.tcl:381
+#, tcl-format
+msgid "Staying on branch '%s'."
+msgstr "Оставане върху клона „%s“."
-#: lib/option.tcl:154
-msgid "Minimum Letters To Blame Copy On"
-msgstr "Минимален брой знаци за анотиране на копието"
+#: lib/checkout_op.tcl:452
+msgid ""
+"You are no longer on a local branch.\n"
+"\n"
+"If you wanted to be on a branch, create one now starting from 'This Detached "
+"Checkout'."
+msgstr ""
+"Вече не сте на локален клон.\n"
+"\n"
+"Ако искате да сте на клон, създайте базиран на „Това несвързано изтегляне“."
-#: lib/option.tcl:155
-msgid "Blame History Context Radius (days)"
-msgstr "Исторически обхват за анотиране в дни"
+#: lib/checkout_op.tcl:503 lib/checkout_op.tcl:507
+#, tcl-format
+msgid "Checked out '%s'."
+msgstr "„%s“ е изтеглен."
-#: lib/option.tcl:156
-msgid "Number of Diff Context Lines"
-msgstr "Брой редове за контекста при извеждане на разликите"
+#: lib/checkout_op.tcl:535
+#, tcl-format
+msgid "Resetting '%s' to '%s' will lose the following commits:"
+msgstr ""
+"Зануляването на „%s“ към „%s“ ще доведе до загубването на следните подавания:"
-#: lib/option.tcl:157
-msgid "Additional Diff Parameters"
-msgstr "Ð\94опÑ\8aлниÑ\82елни аÑ\80гÑ\83менÑ\82и кÑ\8aм â\80\9egit diffâ\80\9c"
+#: lib/checkout_op.tcl:557
+msgid "Recovering lost commits may not be easy."
+msgstr "Ð\92Ñ\8aзÑ\81Ñ\82ановÑ\8fванеÑ\82о на загÑ\83бениÑ\82е подаваниÑ\8f може да е Ñ\82Ñ\80Ñ\83дно."
-#: lib/option.tcl:158
-msgid "Commit Message Text Width"
-msgstr "Широчина на текста на съобщението при подаване"
+#: lib/checkout_op.tcl:562
+#, tcl-format
+msgid "Reset '%s'?"
+msgstr "Зануляване на „%s“?"
-#: lib/option.tcl:159
-msgid "New Branch Name Template"
-msgstr "Шаблон за имеÑ\82о на новиÑ\82е клони"
+#: lib/checkout_op.tcl:567 lib/merge.tcl:170 lib/tools_dlg.tcl:336
+msgid "Visualize"
+msgstr "Ð\92изÑ\83ализаÑ\86иÑ\8f"
-#: lib/option.tcl:160
-msgid "Default File Contents Encoding"
-msgstr "Стандартно кодиране на файловете"
+#: lib/checkout_op.tcl:635
+#, tcl-format
+msgid ""
+"Failed to set current branch.\n"
+"\n"
+"This working directory is only partially switched. We successfully updated "
+"your files, but failed to update an internal Git file.\n"
+"\n"
+"This should not have occurred. %s will now close and give up."
+msgstr ""
+"Неуспешно задаване на текущия клон.\n"
+"\n"
+"Работната директория е само частично обновена: файловете са обновени "
+"успешно, но някой от вътрешните, служебни файлове на Git не е бил.\n"
+"\n"
+"Това състояние е аварийно и не трябва да се случва. Програмата „%s“ ще "
+"преустанови работа."
-#: lib/option.tcl:161
-msgid "Warn before committing to a detached head"
-msgstr "Ð\9fÑ\80едÑ\83пÑ\80еждаване пÑ\80и подаванеÑ\82о пÑ\80и неÑ\81вÑ\8aÑ\80зан вÑ\80Ñ\8aÑ\85"
+#: lib/choose_font.tcl:41
+msgid "Select"
+msgstr "Ð\98збоÑ\80"
-#: lib/option.tcl:162
-msgid "Staging of untracked files"
-msgstr "Ð\92каÑ\80ване на неÑ\81ледени Ñ\84айлове в индекÑ\81а"
+#: lib/choose_font.tcl:55
+msgid "Font Family"
+msgstr "ШÑ\80иÑ\84Ñ\82"
-#: lib/option.tcl:163
-msgid "Show untracked files"
-msgstr "Ð\9fоказване на неÑ\81ледениÑ\82е Ñ\84айлове"
+#: lib/choose_font.tcl:76
+msgid "Font Size"
+msgstr "РазмеÑ\80"
-#: lib/option.tcl:164
-msgid "Tab spacing"
-msgstr "РазмеÑ\80 на Ñ\82абÑ\83лаÑ\86иÑ\8fÑ\82а в инÑ\82еÑ\80вали"
+#: lib/choose_font.tcl:93
+msgid "Font Example"
+msgstr "Ð\9cоÑ\81Ñ\82Ñ\80а"
-#: lib/option.tcl:210
-msgid "Change"
-msgstr "Смяна"
+#: lib/choose_font.tcl:105
+msgid ""
+"This is example text.\n"
+"If you like this text, it can be your font."
+msgstr ""
+"Това е примерен текст.\n"
+"Ако ви харесва как изглежда, изберете шрифта."
+
+#: lib/choose_repository.tcl:33
+msgid "Git Gui"
+msgstr "ГПИ на Git"
+
+#: lib/choose_repository.tcl:92 lib/choose_repository.tcl:412
+msgid "Create New Repository"
+msgstr "Създаване на ново хранилище"
+
+#: lib/choose_repository.tcl:98
+msgid "New..."
+msgstr "Ново…"
+
+#: lib/choose_repository.tcl:105 lib/choose_repository.tcl:496
+msgid "Clone Existing Repository"
+msgstr "Клониране на съществуващо хранилище"
+
+#: lib/choose_repository.tcl:116
+msgid "Clone..."
+msgstr "Клониране…"
+
+#: lib/choose_repository.tcl:123 lib/choose_repository.tcl:1064
+msgid "Open Existing Repository"
+msgstr "Отваряне на съществуващо хранилище"
+
+#: lib/choose_repository.tcl:129
+msgid "Open..."
+msgstr "Отваряне…"
+
+#: lib/choose_repository.tcl:142
+msgid "Recent Repositories"
+msgstr "Скоро ползвани"
+
+#: lib/choose_repository.tcl:148
+msgid "Open Recent Repository:"
+msgstr "Отваряне на хранилище ползвано наскоро:"
+
+#: lib/choose_repository.tcl:316 lib/choose_repository.tcl:323
+#: lib/choose_repository.tcl:330
+#, tcl-format
+msgid "Failed to create repository %s:"
+msgstr "Неуспешно създаване на хранилището „%s“:"
+
+#: lib/choose_repository.tcl:417
+msgid "Directory:"
+msgstr "Директория:"
+
+#: lib/choose_repository.tcl:447 lib/choose_repository.tcl:573
+#: lib/choose_repository.tcl:1098
+msgid "Git Repository"
+msgstr "Хранилище на Git"
+
+#: lib/choose_repository.tcl:472
+#, tcl-format
+msgid "Directory %s already exists."
+msgstr "Вече съществува директория „%s“."
+
+#: lib/choose_repository.tcl:476
+#, tcl-format
+msgid "File %s already exists."
+msgstr "Вече съществува файл „%s“."
+
+#: lib/choose_repository.tcl:491
+msgid "Clone"
+msgstr "Клониране"
+
+#: lib/choose_repository.tcl:504
+msgid "Source Location:"
+msgstr "Адрес на източника:"
+
+#: lib/choose_repository.tcl:513
+msgid "Target Directory:"
+msgstr "Целева директория:"
+
+#: lib/choose_repository.tcl:523
+msgid "Clone Type:"
+msgstr "Вид клониране:"
+
+#: lib/choose_repository.tcl:528
+msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
+msgstr "Стандартно (бързо, частично споделяне на файлове, твърди връзки)"
+
+#: lib/choose_repository.tcl:533
+msgid "Full Copy (Slower, Redundant Backup)"
+msgstr "Пълно (бавно, пълноценно резервно копие)"
+
+#: lib/choose_repository.tcl:538
+msgid "Shared (Fastest, Not Recommended, No Backup)"
+msgstr "Споделено (най-бързо, не се препоръчва, не прави резервно копие)"
+
+#: lib/choose_repository.tcl:545
+msgid "Recursively clone submodules too"
+msgstr "Рекурсивно клониране и на подмодулите"
+
+#: lib/choose_repository.tcl:579 lib/choose_repository.tcl:626
+#: lib/choose_repository.tcl:772 lib/choose_repository.tcl:842
+#: lib/choose_repository.tcl:1104 lib/choose_repository.tcl:1112
+#, tcl-format
+msgid "Not a Git repository: %s"
+msgstr "Това не е хранилище на Git: %s"
+
+#: lib/choose_repository.tcl:615
+msgid "Standard only available for local repository."
+msgstr "Само локални хранилища могат да се клонират стандартно"
+
+#: lib/choose_repository.tcl:619
+msgid "Shared only available for local repository."
+msgstr "Само локални хранилища могат да се клонират споделено"
+
+#: lib/choose_repository.tcl:640
+#, tcl-format
+msgid "Location %s already exists."
+msgstr "Местоположението „%s“ вече съществува."
+
+#: lib/choose_repository.tcl:651
+msgid "Failed to configure origin"
+msgstr "Неуспешно настройване на хранилището-източник"
+
+#: lib/choose_repository.tcl:663
+msgid "Counting objects"
+msgstr "Преброяване на обекти"
+
+#: lib/choose_repository.tcl:664
+msgid "buckets"
+msgstr "клетки"
+
+#: lib/choose_repository.tcl:688
+#, tcl-format
+msgid "Unable to copy objects/info/alternates: %s"
+msgstr "Обектите/информацията/синонимите не могат да бъдат копирани: %s"
+
+#: lib/choose_repository.tcl:724
+#, tcl-format
+msgid "Nothing to clone from %s."
+msgstr "Няма какво да се клонира от „%s“."
+
+#: lib/choose_repository.tcl:726 lib/choose_repository.tcl:940
+#: lib/choose_repository.tcl:952
+msgid "The 'master' branch has not been initialized."
+msgstr "Основният клон — „master“ не е инициализиран."
+
+#: lib/choose_repository.tcl:739
+msgid "Hardlinks are unavailable. Falling back to copying."
+msgstr "Не се поддържат твърди връзки. Преминава се към копиране."
+
+#: lib/choose_repository.tcl:751
+#, tcl-format
+msgid "Cloning from %s"
+msgstr "Клониране на „%s“"
+
+#: lib/choose_repository.tcl:782
+msgid "Copying objects"
+msgstr "Копиране на обекти"
+
+#: lib/choose_repository.tcl:783
+msgid "KiB"
+msgstr "KiB"
+
+#: lib/choose_repository.tcl:807
+#, tcl-format
+msgid "Unable to copy object: %s"
+msgstr "Неуспешно копиране на обект: %s"
+
+#: lib/choose_repository.tcl:817
+msgid "Linking objects"
+msgstr "Създаване на връзки към обектите"
+
+#: lib/choose_repository.tcl:818
+msgid "objects"
+msgstr "обекти"
+
+#: lib/choose_repository.tcl:826
+#, tcl-format
+msgid "Unable to hardlink object: %s"
+msgstr "Неуспешно създаване на твърда връзка към обект: %s"
+
+#: lib/choose_repository.tcl:881
+msgid "Cannot fetch branches and objects. See console output for details."
+msgstr ""
+"Клоните и обектите не могат да бъдат изтеглени. За повече информация "
+"погледнете изхода на конзолата."
+
+#: lib/choose_repository.tcl:892
+msgid "Cannot fetch tags. See console output for details."
+msgstr ""
+"Етикетите не могат да бъдат изтеглени. За повече информация погледнете "
+"изхода на конзолата."
+
+#: lib/choose_repository.tcl:916
+msgid "Cannot determine HEAD. See console output for details."
+msgstr ""
+"Върхът „HEAD“ не може да бъде определен. За повече информация погледнете "
+"изхода на конзолата."
+
+#: lib/choose_repository.tcl:925
+#, tcl-format
+msgid "Unable to cleanup %s"
+msgstr "„%s“ не може да се зачисти"
+
+#: lib/choose_repository.tcl:931
+msgid "Clone failed."
+msgstr "Неуспешно клониране."
+
+#: lib/choose_repository.tcl:938
+msgid "No default branch obtained."
+msgstr "Не е получен клон по подразбиране."
+
+#: lib/choose_repository.tcl:949
+#, tcl-format
+msgid "Cannot resolve %s as a commit."
+msgstr "Няма подаване отговарящо на „%s“."
+
+#: lib/choose_repository.tcl:961
+msgid "Creating working directory"
+msgstr "Създаване на работната директория"
+
+#: lib/choose_repository.tcl:962 lib/index.tcl:70 lib/index.tcl:136
+#: lib/index.tcl:207
+msgid "files"
+msgstr "файлове"
+
+#: lib/choose_repository.tcl:981
+msgid "Cannot clone submodules."
+msgstr "Подмодулите не могат да се клонират."
+
+#: lib/choose_repository.tcl:990
+msgid "Cloning submodules"
+msgstr "Клониране на подмодули"
+
+#: lib/choose_repository.tcl:1015
+msgid "Initial file checkout failed."
+msgstr "Неуспешно първоначално изтегляне."
+
+#: lib/choose_repository.tcl:1059
+msgid "Open"
+msgstr "Отваряне"
+
+#: lib/choose_repository.tcl:1069
+msgid "Repository:"
+msgstr "Хранилище:"
+
+#: lib/choose_repository.tcl:1118
+#, tcl-format
+msgid "Failed to open repository %s:"
+msgstr "Неуспешно отваряне на хранилището „%s“:"
+
+#: lib/choose_rev.tcl:52
+msgid "This Detached Checkout"
+msgstr "Това несвързано изтегляне"
+
+#: lib/choose_rev.tcl:60
+msgid "Revision Expression:"
+msgstr "Израз за версия:"
+
+#: lib/choose_rev.tcl:72
+msgid "Local Branch"
+msgstr "Локален клон"
-#: lib/option.tcl:254
-msgid "Spelling Dictionary:"
-msgstr "Ð\9fÑ\80авопиÑ\81ен Ñ\80еÑ\87ник:"
+#: lib/choose_rev.tcl:77
+msgid "Tracking Branch"
+msgstr "СледÑ\8fÑ\89 клон"
-#: lib/option.tcl:284
-msgid "Change Font"
-msgstr "СмÑ\8fна на Ñ\88Ñ\80иÑ\84Ñ\82а"
+#: lib/choose_rev.tcl:82 lib/choose_rev.tcl:544
+msgid "Tag"
+msgstr "Ð\95Ñ\82икеÑ\82"
-#: lib/option.tcl:288
+#: lib/choose_rev.tcl:321
#, tcl-format
-msgid "Choose %s"
-msgstr "Ð\98збоÑ\80 на â\80\9e%sâ\80\9c"
+msgid "Invalid revision: %s"
+msgstr "Ð\9dепÑ\80авилна веÑ\80Ñ\81иÑ\8f: %s"
-#: lib/option.tcl:294
-msgid "pt."
-msgstr "тчк."
+#: lib/choose_rev.tcl:342
+msgid "No revision selected."
+msgstr "Не е избрана версия."
-#: lib/option.tcl:308
-msgid "Preferences"
-msgstr "Ð\9dаÑ\81Ñ\82Ñ\80ойки"
+#: lib/choose_rev.tcl:350
+msgid "Revision expression is empty."
+msgstr "Ð\98зÑ\80азÑ\8aÑ\82 за веÑ\80Ñ\81иÑ\8f е пÑ\80азен."
-#: lib/option.tcl:345
-msgid "Failed to completely save options:"
-msgstr "Ð\9dеÑ\83Ñ\81пеÑ\88но запазване на наÑ\81Ñ\82Ñ\80ойкиÑ\82е:"
+#: lib/choose_rev.tcl:537
+msgid "Updated"
+msgstr "Ð\9eбновен"
-#: lib/encoding.tcl:443
-msgid "Default"
-msgstr "СÑ\82андаÑ\80Ñ\82ноÑ\82о"
+#: lib/choose_rev.tcl:565
+msgid "URL"
+msgstr "Ð\90дÑ\80еÑ\81"
-#: lib/encoding.tcl:448
-#, tcl-format
-msgid "System (%s)"
-msgstr "Системното (%s)"
+#: lib/commit.tcl:9
+msgid ""
+"There is nothing to amend.\n"
+"\n"
+"You are about to create the initial commit. There is no commit before this "
+"to amend.\n"
+msgstr ""
+"Няма какво да се поправи.\n"
+"\n"
+"Ще създадете първоначалното подаване. Преди него няма други подавания, които "
+"да поправите.\n"
-#: lib/encoding.tcl:459 lib/encoding.tcl:465
-msgid "Other"
-msgstr "Друго"
+#: lib/commit.tcl:18
+msgid ""
+"Cannot amend while merging.\n"
+"\n"
+"You are currently in the middle of a merge that has not been fully "
+"completed. You cannot amend the prior commit unless you first abort the "
+"current merge activity.\n"
+msgstr ""
+"По време на сливане не може да поправяте.\n"
+"\n"
+"В момента все още не сте завършили операция по сливане. Не може да поправите "
+"предишното подаване, освен ако първо не преустановите текущото сливане.\n"
-#: lib/mergetool.tcl:8
-msgid "Force resolution to the base version?"
-msgstr "Ð\94а Ñ\81е използва базоваÑ\82а веÑ\80Ñ\81иÑ\8f"
+#: lib/commit.tcl:48
+msgid "Error loading commit data for amend:"
+msgstr "Ð\93Ñ\80еÑ\88ка пÑ\80и заÑ\80еждане на данниÑ\82е оÑ\82 подаване, коиÑ\82о да Ñ\81е попÑ\80авÑ\8fÑ\82:"
-#: lib/mergetool.tcl:9
-msgid "Force resolution to this branch?"
-msgstr "Ð\94а Ñ\81е използва веÑ\80Ñ\81иÑ\8fÑ\82а оÑ\82 Ñ\82ози клон"
+#: lib/commit.tcl:75
+msgid "Unable to obtain your identity:"
+msgstr "Ð\98денÑ\82иÑ\84икаÑ\86иÑ\8fÑ\82а ви не може да бÑ\8aде опÑ\80еделена:"
-#: lib/mergetool.tcl:10
-msgid "Force resolution to the other branch?"
-msgstr "Ð\94а Ñ\81е използва веÑ\80Ñ\81иÑ\8fÑ\82а оÑ\82 дÑ\80Ñ\83гиÑ\8f клон"
+#: lib/commit.tcl:80
+msgid "Invalid GIT_COMMITTER_IDENT:"
+msgstr "Ð\9dепÑ\80авилно поле â\80\9eGIT_COMMITTER_IDENTâ\80\9c:"
-#: lib/mergetool.tcl:14
+#: lib/commit.tcl:129
#, tcl-format
+msgid "warning: Tcl does not support encoding '%s'."
+msgstr "предупреждение: Tcl не поддържа кодирането „%s“."
+
+#: lib/commit.tcl:149
msgid ""
-"Note that the diff shows only conflicting changes.\n"
+"Last scanned state does not match repository state.\n"
"\n"
-"%s will be overwritten.\n"
+"Another Git program has modified this repository since the last scan. A "
+"rescan must be performed before another commit can be created.\n"
"\n"
-"This operation can be undone only by restarting the merge."
+"The rescan will be automatically started now.\n"
msgstr ""
-"Разликата показва само разликите с конфликт.\n"
+"Състоянието при последната проверка не отговаря на състоянието на "
+"хранилището.\n"
"\n"
-"Файлът „%s“ ще бъде презаписан.\n"
+"Някой друг процес за Git е променил хранилището междувременно. Състоянието "
+"трябва да бъде проверено преди ново подаване.\n"
"\n"
-"Тази опеÑ\80аÑ\86иÑ\8f може да бÑ\8aде оÑ\82менена Ñ\81амо Ñ\87Ñ\80ез запоÑ\87ване на Ñ\81ливанеÑ\82о наново."
+"Ð\90вÑ\82омаÑ\82иÑ\87но Ñ\89е запоÑ\87не нова пÑ\80овеÑ\80ка.\n"
-#: lib/mergetool.tcl:45
+#: lib/commit.tcl:173
#, tcl-format
-msgid "File %s seems to have unresolved conflicts, still stage?"
+msgid ""
+"Unmerged files cannot be committed.\n"
+"\n"
+"File %s has merge conflicts. You must resolve them and stage the file "
+"before committing.\n"
msgstr ""
-"Изглежда, че все още има некоригирани конфликти във файла „%s“. Да се добави "
-"ли файлът към индекса?"
+"Неслетите файлове не могат да бъдат подавани.\n"
+"\n"
+"Във файла „%s“ има конфликти при сливане. За да го подадете, трябва първо да "
+"коригирате конфликтите и да добавите файла към индекса за подаване.\n"
-#: lib/mergetool.tcl:60
+#: lib/commit.tcl:181
#, tcl-format
-msgid "Adding resolution for %s"
-msgstr "Добавяне на корекция на конфликтите в „%s“"
-
-#: lib/mergetool.tcl:141
-msgid "Cannot resolve deletion or link conflicts using a tool"
+msgid ""
+"Unknown file state %s detected.\n"
+"\n"
+"File %s cannot be committed by this program.\n"
msgstr ""
-"Конфликтите при символни връзки или изтриване не могат да бъдат коригирани с "
-"външна програма."
-
-#: lib/mergetool.tcl:146
-msgid "Conflict file does not exist"
-msgstr "Файлът, в който е конфликтът, не съществува"
-
-#: lib/mergetool.tcl:246
-#, tcl-format
-msgid "Not a GUI merge tool: '%s'"
-msgstr "Това не е графична програма за сливане: „%s“"
-
-#: lib/mergetool.tcl:275
-#, tcl-format
-msgid "Unsupported merge tool '%s'"
-msgstr "Неподдържана програма за сливане: „%s“"
-
-#: lib/mergetool.tcl:310
-msgid "Merge tool is already running, terminate it?"
-msgstr "Програмата за сливане вече е стартирана. Да бъде ли изключена?"
+"Непознато състояние на файл „%s“.\n"
+"\n"
+"Файлът „%s“ не може да бъде подаден чрез текущата програма.\n"
-#: lib/mergetool.tcl:330
-#, tcl-format
+#: lib/commit.tcl:189
msgid ""
-"Error retrieving versions:\n"
-"%s"
+"No changes to commit.\n"
+"\n"
+"You must stage at least 1 file before you can commit.\n"
msgstr ""
-"Грешка при изтеглянето на версии:\n"
-"%s"
+"Няма промени за подаване.\n"
+"\n"
+"Трябва да добавите поне един файл към индекса, за да подадете.\n"
-#: lib/mergetool.tcl:350
-#, tcl-format
+#: lib/commit.tcl:204
msgid ""
-"Could not start the merge tool:\n"
+"Please supply a commit message.\n"
"\n"
-"%s"
+"A good commit message has the following format:\n"
+"\n"
+"- First line: Describe in one sentence what you did.\n"
+"- Second line: Blank\n"
+"- Remaining lines: Describe why this change is good.\n"
msgstr ""
-"Ð\9fÑ\80огÑ\80амаÑ\82а за Ñ\81ливане не може да бÑ\8aде Ñ\81Ñ\82аÑ\80Ñ\82иÑ\80ана:\n"
+"Ð\97адайÑ\82е добÑ\80о Ñ\81Ñ\8aобÑ\89ение пÑ\80и подаване.\n"
"\n"
-"%s"
-
-#: lib/mergetool.tcl:354
-msgid "Running merge tool..."
-msgstr "Стартиране на програмата за сливане…"
+"Използвайте следния формат:\n"
+"\n"
+"● Първи ред: описание в едно изречение на промяната.\n"
+"● Втори ред: празен.\n"
+"● Останалите редове: опишете защо се налага тази промяна.\n"
-#: lib/mergetool.tcl:382 lib/mergetool.tcl:390
-msgid "Merge tool failed."
-msgstr "Ð\93Ñ\80еÑ\88ка в пÑ\80огÑ\80амаÑ\82а за Ñ\81ливане."
+#: lib/commit.tcl:235
+msgid "Calling pre-commit hook..."
+msgstr "Ð\98зпÑ\8aлнÑ\8fване на кÑ\83каÑ\82а пÑ\80еди подаванеâ\80¦"
-#: lib/tools_dlg.tcl:22
-msgid "Add Tool"
-msgstr "Ð\94обавÑ\8fне на команда"
+#: lib/commit.tcl:250
+msgid "Commit declined by pre-commit hook."
+msgstr "Ð\9fодаванеÑ\82о е оÑ\82Ñ\85вÑ\8aÑ\80лено оÑ\82 кÑ\83каÑ\82а пÑ\80еди подаване."
-#: lib/tools_dlg.tcl:28
-msgid "Add New Tool Command"
-msgstr "Добавяне на команда"
+#: lib/commit.tcl:269
+msgid ""
+"You are about to commit on a detached head. This is a potentially dangerous "
+"thing to do because if you switch to another branch you will lose your "
+"changes and it can be difficult to retrieve them later from the reflog. You "
+"should probably cancel this commit and create a new branch to continue.\n"
+" \n"
+" Do you really want to proceed with your Commit?"
+msgstr ""
+"Ще подадете към несвързан, отделѐн указател „HEAD“. Това е опасно, защото "
+"при преминаването към клон ще загубите промените си, като единственият начин "
+"да ги върнете ще е чрез журнала на указателите (reflog). Най-вероятно трябва "
+"да не правите това подаване, а да създадете нов клон, преди да продължите.\n"
+" \n"
+"Сигурни ли сте, че искате да извършите текущото подаване?"
-#: lib/tools_dlg.tcl:34
-msgid "Add globally"
-msgstr "Ð\93лобално добавÑ\8fне"
+#: lib/commit.tcl:290
+msgid "Calling commit-msg hook..."
+msgstr "Ð\98зпÑ\8aлнÑ\8fване на кÑ\83каÑ\82а за Ñ\81Ñ\8aобÑ\89ениеÑ\82о пÑ\80и подаванеâ\80¦"
-#: lib/tools_dlg.tcl:46
-msgid "Tool Details"
-msgstr "Подробности за командата"
+#: lib/commit.tcl:305
+msgid "Commit declined by commit-msg hook."
+msgstr "Подаването е отхвърлено от куката за съобщението при подаване."
-#: lib/tools_dlg.tcl:49
-msgid "Use '/' separators to create a submenu tree:"
-msgstr "Ð\97а Ñ\81Ñ\8aздаване на подменÑ\8eÑ\82а използвайÑ\82е знака â\80\9e/â\80\9c за Ñ\80азделиÑ\82ел:"
+#: lib/commit.tcl:318
+msgid "Committing changes..."
+msgstr "Ð\9fодаване на пÑ\80омениÑ\82еâ\80¦"
-#: lib/tools_dlg.tcl:60
-msgid "Command:"
-msgstr "Ð\9aоманда:"
+#: lib/commit.tcl:334
+msgid "write-tree failed:"
+msgstr "неÑ\83Ñ\81пеÑ\88но запазване на дÑ\8aÑ\80воÑ\82о (write-tree):"
-#: lib/tools_dlg.tcl:71
-msgid "Show a dialog before running"
-msgstr "Ð\9fÑ\80еди изпÑ\8aлнение да Ñ\81е извежда диалогов пÑ\80озоÑ\80еÑ\86"
+#: lib/commit.tcl:335 lib/commit.tcl:382 lib/commit.tcl:403
+msgid "Commit failed."
+msgstr "Ð\9dеÑ\83Ñ\81пеÑ\88но подаване."
-#: lib/tools_dlg.tcl:77
-msgid "Ask the user to select a revision (sets $REVISION)"
-msgstr "Потребителят да укаже версия (задаване на променливата $REVISION)"
+#: lib/commit.tcl:352
+#, tcl-format
+msgid "Commit %s appears to be corrupt"
+msgstr "Подаването „%s“ изглежда повредено"
-#: lib/tools_dlg.tcl:82
-msgid "Ask the user for additional arguments (sets $ARGS)"
+#: lib/commit.tcl:357
+msgid ""
+"No changes to commit.\n"
+"\n"
+"No files were modified by this commit and it was not a merge commit.\n"
+"\n"
+"A rescan will be automatically started now.\n"
msgstr ""
-"Потребителят да укаже допълнителни аргументи (задаване на променливата $ARGS)"
+"Няма промени за подаване.\n"
+"\n"
+"В това подаване не са променяни никакви файлове, а и не е подаване със "
+"сливане.\n"
+"\n"
+"Автоматично ще започне нова проверка.\n"
-#: lib/tools_dlg.tcl:89
-msgid "Don't show the command output window"
-msgstr "Ð\91ез показване на пÑ\80озоÑ\80еÑ\86 Ñ\81 изÑ\85ода оÑ\82 командаÑ\82а"
+#: lib/commit.tcl:364
+msgid "No changes to commit."
+msgstr "Ð\9dÑ\8fма пÑ\80омени за подаване."
-#: lib/tools_dlg.tcl:94
-msgid "Run only if a diff is selected ($FILENAME not empty)"
-msgstr ""
-"Стартиране само след избор на разлика (променливата $FILENAME не е празна)"
+#: lib/commit.tcl:381
+msgid "commit-tree failed:"
+msgstr "неуспешно подаване на дървото (commit-tree):"
-#: lib/tools_dlg.tcl:118
-msgid "Please supply a name for the tool."
-msgstr "Ð\97адайÑ\82е име за командаÑ\82а."
+#: lib/commit.tcl:402
+msgid "update-ref failed:"
+msgstr "неÑ\83Ñ\81пеÑ\88но обновÑ\8fване на Ñ\83казаÑ\82елиÑ\82е (update-ref):"
-#: lib/tools_dlg.tcl:126
+#: lib/commit.tcl:495
#, tcl-format
-msgid "Tool '%s' already exists."
-msgstr "Ð\9aомандаÑ\82а â\80\9e%sâ\80\9c веÑ\87е Ñ\81Ñ\8aÑ\89еÑ\81Ñ\82вÑ\83ва."
+msgid "Created commit %s: %s"
+msgstr "УÑ\81пеÑ\88но подаване %s: %s"
-#: lib/tools_dlg.tcl:148
-#, tcl-format
-msgid ""
-"Could not add tool:\n"
-"%s"
-msgstr ""
-"Командата не може да бъде добавена:\n"
-"%s"
+#: lib/console.tcl:59
+msgid "Working... please wait..."
+msgstr "В момента се извършва действие, изчакайте…"
-#: lib/tools_dlg.tcl:187
-msgid "Remove Tool"
-msgstr "Ð\9fÑ\80емаÑ\85ване на команда"
+#: lib/console.tcl:186
+msgid "Success"
+msgstr "УÑ\81пеÑ\85"
-#: lib/tools_dlg.tcl:193
-msgid "Remove Tool Commands"
-msgstr "Ð\9fÑ\80емаÑ\85ване на команди"
+#: lib/console.tcl:200
+msgid "Error: Command Failed"
+msgstr "Ð\93Ñ\80еÑ\88ка: неÑ\83Ñ\81пеÑ\88но изпÑ\8aлнение на команда"
-#: lib/tools_dlg.tcl:198
-msgid "Remove"
-msgstr "Ð\9fÑ\80емаÑ\85ване"
+#: lib/database.tcl:42
+msgid "Number of loose objects"
+msgstr "Ð\91Ñ\80ой непакеÑ\82иÑ\80ани обекÑ\82и"
-#: lib/tools_dlg.tcl:231
-msgid "(Blue denotes repository-local tools)"
-msgstr "(командите към локалното хранилище са обозначени в синьо)"
+#: lib/database.tcl:43
+msgid "Disk space used by loose objects"
+msgstr "Дисково пространство заето от непакетирани обекти"
-#: lib/tools_dlg.tcl:292
-#, tcl-format
-msgid "Run Command: %s"
-msgstr "Изпълнение на командата „%s“"
+#: lib/database.tcl:44
+msgid "Number of packed objects"
+msgstr "Брой пакетирани обекти"
-#: lib/tools_dlg.tcl:306
-msgid "Arguments"
-msgstr "Ð\90Ñ\80гÑ\83менти"
+#: lib/database.tcl:45
+msgid "Number of packs"
+msgstr "Ð\91Ñ\80ой пакети"
-#: lib/tools_dlg.tcl:336 lib/checkout_op.tcl:567 lib/merge.tcl:166
-msgid "Visualize"
-msgstr "Ð\92изÑ\83ализаÑ\86иÑ\8f"
+#: lib/database.tcl:46
+msgid "Disk space used by packed objects"
+msgstr "Ð\94иÑ\81ково пÑ\80оÑ\81Ñ\82Ñ\80анÑ\81Ñ\82во заеÑ\82о оÑ\82 пакеÑ\82иÑ\80ани обекÑ\82и"
-#: lib/tools_dlg.tcl:341
-msgid "OK"
-msgstr "Ð\94обÑ\80е"
+#: lib/database.tcl:47
+msgid "Packed objects waiting for pruning"
+msgstr "Ð\9fакеÑ\82иÑ\80ани обекÑ\82и за окаÑ\81Ñ\82Ñ\80Ñ\8fне"
-#: lib/search.tcl:48
-msgid "Find:"
-msgstr "ТÑ\8aÑ\80Ñ\81ене:"
+#: lib/database.tcl:48
+msgid "Garbage files"
+msgstr "Файлове за боклÑ\83ка"
-#: lib/search.tcl:50
-msgid "Next"
-msgstr "Следваща поява"
+#: lib/database.tcl:57 lib/option.tcl:182 lib/option.tcl:197 lib/option.tcl:220
+#: lib/option.tcl:282
+#, tcl-format
+msgid "%s:"
+msgstr "%s:"
-#: lib/search.tcl:51
-msgid "Prev"
-msgstr "Предишна поява"
+#: lib/database.tcl:66
+#, tcl-format
+msgid "%s (%s): Database Statistics"
+msgstr "%s (%s): Статистика на базата от данни"
-#: lib/search.tcl:52
-msgid "RegExp"
-msgstr "Рег. изÑ\80аз"
+#: lib/database.tcl:72
+msgid "Compressing the object database"
+msgstr "Ð\9aомпÑ\80еÑ\81иÑ\80ане на базаÑ\82а Ñ\81 данни за обекÑ\82иÑ\82е"
-#: lib/search.tcl:54
-msgid "Case"
-msgstr "РегиÑ\81Ñ\82Ñ\8aÑ\80"
+#: lib/database.tcl:83
+msgid "Verifying the object database with fsck-objects"
+msgstr "Ð\9fÑ\80овеÑ\80ка на базаÑ\82а Ñ\81 данни за обекÑ\82иÑ\82е Ñ\81 пÑ\80огÑ\80амаÑ\82а â\80\9efsck-objectsâ\80\9c"
-#: lib/shortcut.tcl:21 lib/shortcut.tcl:62
-msgid "Cannot write shortcut:"
-msgstr "Клавишната комбинация не може да бъде запазена:"
+#: lib/database.tcl:107
+#, tcl-format
+msgid ""
+"This repository currently has approximately %i loose objects.\n"
+"\n"
+"To maintain optimal performance it is strongly recommended that you compress "
+"the database.\n"
+"\n"
+"Compress the database now?"
+msgstr ""
+"В това хранилище в момента има към %i непакетирани обекти.\n"
+"\n"
+"За добра производителност се препоръчва да компресирате базата с данни за "
+"обектите.\n"
+"\n"
+"Да се започне ли компресирането?"
-#: lib/shortcut.tcl:137
-msgid "Cannot write icon:"
-msgstr "Иконата не може да бъде запазена:"
+#: lib/date.tcl:25
+#, tcl-format
+msgid "Invalid date from Git: %s"
+msgstr "Неправилни данни от Git: %s"
#: lib/diff.tcl:77
#, tcl-format
msgid "Loading diff of %s..."
msgstr "Зареждане на разликите в „%s“…"
-#: lib/diff.tcl:140
+#: lib/diff.tcl:143
msgid ""
"LOCAL: deleted\n"
"REMOTE:\n"
"ЛОКАЛНО: изтрит\n"
"ОТДАЛЕЧЕНО:\n"
-#: lib/diff.tcl:145
+#: lib/diff.tcl:148
msgid ""
"REMOTE: deleted\n"
"LOCAL:\n"
"ОТДАЛЕЧЕНО: изтрит\n"
"ЛОКАЛНО:\n"
-#: lib/diff.tcl:152
+#: lib/diff.tcl:155
msgid "LOCAL:\n"
msgstr "ЛОКАЛНО:\n"
-#: lib/diff.tcl:155
+#: lib/diff.tcl:158
msgid "REMOTE:\n"
msgstr "ОТДАЛЕЧЕНО:\n"
-#: lib/diff.tcl:217 lib/diff.tcl:355
+#: lib/diff.tcl:220 lib/diff.tcl:357
#, tcl-format
msgid "Unable to display %s"
msgstr "Файлът „%s“ не може да бъде показан"
-#: lib/diff.tcl:218
+#: lib/diff.tcl:221
msgid "Error loading file:"
msgstr "Грешка при зареждане на файл:"
-#: lib/diff.tcl:225
+#: lib/diff.tcl:227
msgid "Git Repository (subproject)"
msgstr "Хранилище на Git (подмодул)"
-#: lib/diff.tcl:237
+#: lib/diff.tcl:239
msgid "* Binary file (not showing content)."
msgstr "● Двоичен файл (съдържанието не се показва)."
-#: lib/diff.tcl:242
+#: lib/diff.tcl:244
#, tcl-format
msgid ""
"* Untracked file is %d bytes.\n"
"● Неследеният файл е %d байта.\n"
"● Показват се само първите %d байта.\n"
-#: lib/diff.tcl:248
-#, tcl-format
-msgid ""
-"\n"
-"* Untracked file clipped here by %s.\n"
-"* To see the entire file, use an external editor.\n"
-msgstr ""
-"\n"
-"● Неследеният файл е отрязан дотук от програмата „%s“.\n"
-"● Използвайте външен редактор, за да видите целия файл.\n"
-
-#: lib/diff.tcl:356 lib/blame.tcl:1128
-msgid "Error loading diff:"
-msgstr "Грешка при зареждане на разлика:"
-
-#: lib/diff.tcl:578
-msgid "Failed to unstage selected hunk."
-msgstr "Избраното парче не може да бъде извадено от индекса."
-
-#: lib/diff.tcl:585
-msgid "Failed to stage selected hunk."
-msgstr "Избраното парче не може да бъде добавено към индекса."
-
-#: lib/diff.tcl:664
-msgid "Failed to unstage selected line."
-msgstr "Избраният ред не може да бъде изваден от индекса."
-
-#: lib/diff.tcl:672
-msgid "Failed to stage selected line."
-msgstr "Избраният ред не може да бъде добавен към индекса."
-
-#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
-msgid "Delete Branch Remotely"
-msgstr "Изтриване на отдалечения клон"
-
-#: lib/remote_branch_delete.tcl:48
-msgid "From Repository"
-msgstr "От хранилище"
-
-#: lib/remote_branch_delete.tcl:88
-msgid "Branches"
-msgstr "Клони"
-
-#: lib/remote_branch_delete.tcl:110
-msgid "Delete Only If"
-msgstr "Изтриване, само ако"
-
-#: lib/remote_branch_delete.tcl:112
-msgid "Merged Into:"
-msgstr "Слят в:"
-
-#: lib/remote_branch_delete.tcl:120 lib/branch_delete.tcl:53
-msgid "Always (Do not perform merge checks)"
-msgstr "Винаги (без проверка за сливане)"
-
-#: lib/remote_branch_delete.tcl:153
-msgid "A branch is required for 'Merged Into'."
-msgstr "За данните „Слят в“ е необходимо да зададете клон."
-
-#: lib/remote_branch_delete.tcl:185
-#, tcl-format
-msgid ""
-"The following branches are not completely merged into %s:\n"
-"\n"
-" - %s"
-msgstr ""
-"Следните клони не са слети напълно в „%s“:\n"
-"\n"
-" ● %s"
-
-#: lib/remote_branch_delete.tcl:190
+#: lib/diff.tcl:250
#, tcl-format
msgid ""
-"One or more of the merge tests failed because you have not fetched the "
-"necessary commits. Try fetching from %s first."
-msgstr ""
-"Поне една от пробите за сливане е неуспешна, защото не сте доставили всички "
-"необходими подавания. Пробвайте първо да доставите подаванията от „%s“."
-
-#: lib/remote_branch_delete.tcl:208
-msgid "Please select one or more branches to delete."
-msgstr "Изберете поне един клон за изтриване."
-
-#: lib/remote_branch_delete.tcl:218 lib/branch_delete.tcl:115
-msgid ""
-"Recovering deleted branches is difficult.\n"
-"\n"
-"Delete the selected branches?"
-msgstr ""
-"Възстановяването на изтрити клони може да е трудно.\n"
"\n"
-"Сигурни ли сте, че искате да триете?"
-
-#: lib/remote_branch_delete.tcl:227
-#, tcl-format
-msgid "Deleting branches from %s"
-msgstr "Изтриване на клони от „%s“"
-
-#: lib/remote_branch_delete.tcl:300
-msgid "No repository selected."
-msgstr "Не е избрано хранилище."
-
-#: lib/remote_branch_delete.tcl:305
-#, tcl-format
-msgid "Scanning %s..."
-msgstr "Претърсване на „%s“…"
-
-#: lib/choose_repository.tcl:33
-msgid "Git Gui"
-msgstr "ГПИ на Git"
-
-#: lib/choose_repository.tcl:92 lib/choose_repository.tcl:412
-msgid "Create New Repository"
-msgstr "Създаване на ново хранилище"
-
-#: lib/choose_repository.tcl:98
-msgid "New..."
-msgstr "Ново…"
-
-#: lib/choose_repository.tcl:105 lib/choose_repository.tcl:496
-msgid "Clone Existing Repository"
-msgstr "Клониране на съществуващо хранилище"
+"* Untracked file clipped here by %s.\n"
+"* To see the entire file, use an external editor.\n"
+msgstr ""
+"\n"
+"● Неследеният файл е отрязан дотук от програмата „%s“.\n"
+"● Използвайте външен редактор, за да видите целия файл.\n"
-#: lib/choose_repository.tcl:116
-msgid "Clone..."
-msgstr "Ð\9aлониÑ\80анеâ\80¦"
+#: lib/diff.tcl:580
+msgid "Failed to unstage selected hunk."
+msgstr "Ð\98збÑ\80аноÑ\82о паÑ\80Ñ\87е не може да бÑ\8aде извадено оÑ\82 индекÑ\81а."
-#: lib/choose_repository.tcl:123 lib/choose_repository.tcl:1064
-msgid "Open Existing Repository"
-msgstr "Ð\9eÑ\82ваÑ\80Ñ\8fне на Ñ\81Ñ\8aÑ\89еÑ\81Ñ\82вÑ\83ваÑ\89о Ñ\85Ñ\80анилиÑ\89е"
+#: lib/diff.tcl:587
+msgid "Failed to stage selected hunk."
+msgstr "Ð\98збÑ\80аноÑ\82о паÑ\80Ñ\87е не може да бÑ\8aде добавено кÑ\8aм индекÑ\81а."
-#: lib/choose_repository.tcl:129
-msgid "Open..."
-msgstr "Ð\9eÑ\82ваÑ\80Ñ\8fнеâ\80¦"
+#: lib/diff.tcl:666
+msgid "Failed to unstage selected line."
+msgstr "Ð\98збÑ\80аниÑ\8fÑ\82 Ñ\80ед не може да бÑ\8aде изваден оÑ\82 индекÑ\81а."
-#: lib/choose_repository.tcl:142
-msgid "Recent Repositories"
-msgstr "СкоÑ\80о ползвани"
+#: lib/diff.tcl:674
+msgid "Failed to stage selected line."
+msgstr "Ð\98збÑ\80аниÑ\8fÑ\82 Ñ\80ед не може да бÑ\8aде добавен кÑ\8aм индекÑ\81а."
-#: lib/choose_repository.tcl:148
-msgid "Open Recent Repository:"
-msgstr "Ð\9eÑ\82ваÑ\80Ñ\8fне на Ñ\85Ñ\80анилиÑ\89е ползвано наÑ\81коÑ\80о:"
+#: lib/encoding.tcl:443
+msgid "Default"
+msgstr "СÑ\82андаÑ\80Ñ\82ноÑ\82о"
-#: lib/choose_repository.tcl:316 lib/choose_repository.tcl:323
-#: lib/choose_repository.tcl:330
+#: lib/encoding.tcl:448
#, tcl-format
-msgid "Failed to create repository %s:"
-msgstr "Неуспешно създаване на хранилището „%s“:"
-
-#: lib/choose_repository.tcl:407 lib/branch_create.tcl:33
-msgid "Create"
-msgstr "Създаване"
+msgid "System (%s)"
+msgstr "Системното (%s)"
-#: lib/choose_repository.tcl:417
-msgid "Directory:"
-msgstr "Директория:"
+#: lib/encoding.tcl:459 lib/encoding.tcl:465
+msgid "Other"
+msgstr "Друго"
-#: lib/choose_repository.tcl:447 lib/choose_repository.tcl:573
-#: lib/choose_repository.tcl:1098
-msgid "Git Repository"
-msgstr "Хранилище на Git"
+#: lib/error.tcl:20
+#, tcl-format
+msgid "%s: error"
+msgstr "%s: грешка"
-#: lib/choose_repository.tcl:472
+#: lib/error.tcl:36
#, tcl-format
-msgid "Directory %s already exists."
-msgstr "Вече съществува директория „%s“."
+msgid "%s: warning"
+msgstr "%s: предупреждение"
-#: lib/choose_repository.tcl:476
+#: lib/error.tcl:80
#, tcl-format
-msgid "File %s already exists."
-msgstr "Вече съществува файл „%s“."
+msgid "%s hook failed:"
+msgstr "%s: грешка от куката"
-#: lib/choose_repository.tcl:491
-msgid "Clone"
-msgstr "Ð\9aлониÑ\80ане"
+#: lib/error.tcl:96
+msgid "You must correct the above errors before committing."
+msgstr "Ð\9fÑ\80еди да можеÑ\82е да подадеÑ\82е, коÑ\80игиÑ\80айÑ\82е гоÑ\80ниÑ\82е гÑ\80еÑ\88ки."
-#: lib/choose_repository.tcl:504
-msgid "Source Location:"
-msgstr "Адрес на източника:"
+#: lib/error.tcl:116
+#, tcl-format
+msgid "%s (%s): error"
+msgstr "%s (%s): грешка"
-#: lib/choose_repository.tcl:513
-msgid "Target Directory:"
-msgstr "Целева диÑ\80екÑ\82оÑ\80иÑ\8f:"
+#: lib/index.tcl:6
+msgid "Unable to unlock the index."
+msgstr "Ð\98ндекÑ\81Ñ\8aÑ\82 не може да бÑ\8aде оÑ\82клÑ\8eÑ\87ен."
-#: lib/choose_repository.tcl:523
-msgid "Clone Type:"
-msgstr "Ð\92ид клониÑ\80ане:"
+#: lib/index.tcl:17
+msgid "Index Error"
+msgstr "Ð\93Ñ\80еÑ\88ка в индекÑ\81а"
-#: lib/choose_repository.tcl:528
-msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
-msgstr "Стандартно (бързо, частично споделяне на файлове, твърди връзки)"
+#: lib/index.tcl:19
+msgid ""
+"Updating the Git index failed. A rescan will be automatically started to "
+"resynchronize git-gui."
+msgstr ""
+"Неуспешно обновяване на индекса на Git. Автоматично ще започне нова проверка "
+"за синхронизирането на git-gui."
-#: lib/choose_repository.tcl:533
-msgid "Full Copy (Slower, Redundant Backup)"
-msgstr "Ð\9fÑ\8aлно (бавно, пÑ\8aлноÑ\86енно Ñ\80езеÑ\80вно копие)"
+#: lib/index.tcl:30
+msgid "Continue"
+msgstr "Ð\9fÑ\80одÑ\8aлжаване"
-#: lib/choose_repository.tcl:538
-msgid "Shared (Fastest, Not Recommended, No Backup)"
-msgstr "Споделено (най-бÑ\8aÑ\80зо, не Ñ\81е пÑ\80епоÑ\80Ñ\8aÑ\87ва, не пÑ\80ави Ñ\80езеÑ\80вно копие)"
+#: lib/index.tcl:33
+msgid "Unlock Index"
+msgstr "Ð\9eÑ\82клÑ\8eÑ\87ване на индекÑ\81а"
-#: lib/choose_repository.tcl:545
-msgid "Recursively clone submodules too"
-msgstr "РекÑ\83Ñ\80Ñ\81ивно клониÑ\80ане и на подмодÑ\83лиÑ\82е"
+#: lib/index.tcl:294
+msgid "Unstaging selected files from commit"
+msgstr "Ð\98зваждане на избÑ\80аниÑ\82е Ñ\84айлове оÑ\82 подаванеÑ\82о"
-#: lib/choose_repository.tcl:579 lib/choose_repository.tcl:626
-#: lib/choose_repository.tcl:772 lib/choose_repository.tcl:842
-#: lib/choose_repository.tcl:1104 lib/choose_repository.tcl:1112
+#: lib/index.tcl:298
#, tcl-format
-msgid "Not a Git repository: %s"
-msgstr "Това не е Ñ\85Ñ\80анилиÑ\89е на Git: %s"
+msgid "Unstaging %s from commit"
+msgstr "Ð\98зваждане на â\80\9e%sâ\80\9c оÑ\82 подаванеÑ\82о"
-#: lib/choose_repository.tcl:615
-msgid "Standard only available for local repository."
-msgstr "Само локални Ñ\85Ñ\80анилиÑ\89а могаÑ\82 да Ñ\81е клониÑ\80аÑ\82 Ñ\81Ñ\82андаÑ\80Ñ\82но"
+#: lib/index.tcl:337
+msgid "Ready to commit."
+msgstr "Ð\93оÑ\82овноÑ\81Ñ\82 за подаване."
-#: lib/choose_repository.tcl:619
-msgid "Shared only available for local repository."
-msgstr "Само локални Ñ\85Ñ\80анилиÑ\89а могаÑ\82 да Ñ\81е клониÑ\80аÑ\82 Ñ\81поделено"
+#: lib/index.tcl:346
+msgid "Adding selected files"
+msgstr "Ð\94обавÑ\8fне на избÑ\80аниÑ\82е Ñ\84айлове"
-#: lib/choose_repository.tcl:640
+#: lib/index.tcl:350
#, tcl-format
-msgid "Location %s already exists."
-msgstr "Местоположението „%s“ вече съществува."
-
-#: lib/choose_repository.tcl:651
-msgid "Failed to configure origin"
-msgstr "Неуспешно настройване на хранилището-източник"
+msgid "Adding %s"
+msgstr "Добавяне на „%s“"
-#: lib/choose_repository.tcl:663
-msgid "Counting objects"
-msgstr "Преброяване на обекти"
+#: lib/index.tcl:380
+#, tcl-format
+msgid "Stage %d untracked files?"
+msgstr "Да се добавят ли %d неследени файла към индекса?"
-#: lib/choose_repository.tcl:664
-msgid "buckets"
-msgstr "клеÑ\82ки"
+#: lib/index.tcl:388
+msgid "Adding all changed files"
+msgstr "Ð\94обавÑ\8fне на вÑ\81иÑ\87ки пÑ\80оменени Ñ\84айлове"
-#: lib/choose_repository.tcl:688
+#: lib/index.tcl:428
#, tcl-format
-msgid "Unable to copy objects/info/alternates: %s"
-msgstr "Ð\9eбекÑ\82иÑ\82е/инÑ\84оÑ\80маÑ\86иÑ\8fÑ\82а/Ñ\81инонимиÑ\82е не могаÑ\82 да бÑ\8aдаÑ\82 копиÑ\80ани: %s"
+msgid "Revert changes in file %s?"
+msgstr "Ð\94а Ñ\81е маÑ\85наÑ\82 ли пÑ\80омениÑ\82е вÑ\8aв Ñ\84айла â\80\9e%sâ\80\9c?"
-#: lib/choose_repository.tcl:724
+#: lib/index.tcl:430
#, tcl-format
-msgid "Nothing to clone from %s."
-msgstr "Няма какво да се клонира от „%s“."
-
-#: lib/choose_repository.tcl:726 lib/choose_repository.tcl:940
-#: lib/choose_repository.tcl:952
-msgid "The 'master' branch has not been initialized."
-msgstr "Основният клон — „master“ не е инициализиран."
-
-#: lib/choose_repository.tcl:739
-msgid "Hardlinks are unavailable. Falling back to copying."
-msgstr "Не се поддържат твърди връзки. Преминава се към копиране."
+msgid "Revert changes in these %i files?"
+msgstr "Да се махнат ли промените в тези %i файла?"
-#: lib/choose_repository.tcl:751
-#, tcl-format
-msgid "Cloning from %s"
-msgstr "Клониране на „%s“"
+#: lib/index.tcl:438
+msgid "Any unstaged changes will be permanently lost by the revert."
+msgstr ""
+"Всички промени, които не са били вкарани в индекса, ще бъдат безвъзвратно "
+"загубени."
-#: lib/choose_repository.tcl:782
-msgid "Copying objects"
-msgstr "Ð\9aопиÑ\80ане на обекÑ\82и"
+#: lib/index.tcl:441
+msgid "Do Nothing"
+msgstr "Ð\9dиÑ\89о да не Ñ\81е пÑ\80ави"
-#: lib/choose_repository.tcl:783
-msgid "KiB"
-msgstr "KiB"
+#: lib/index.tcl:459
+msgid "Reverting selected files"
+msgstr "Махане на промените в избраните файлове"
-#: lib/choose_repository.tcl:807
+#: lib/index.tcl:463
#, tcl-format
-msgid "Unable to copy object: %s"
-msgstr "Ð\9dеÑ\83Ñ\81пеÑ\88но копиÑ\80ане на обекÑ\82: %s"
+msgid "Reverting %s"
+msgstr "Ð\9cаÑ\85ане на пÑ\80омениÑ\82е в â\80\9e%sâ\80\9c"
-#: lib/choose_repository.tcl:817
-msgid "Linking objects"
-msgstr "СÑ\8aздаване на вÑ\80Ñ\8aзки кÑ\8aм обекÑ\82иÑ\82е"
+#: lib/line.tcl:17
+msgid "Goto Line:"
+msgstr "Ð\9aÑ\8aм Ñ\80ед:"
-#: lib/choose_repository.tcl:818
-msgid "objects"
-msgstr "обекÑ\82и"
+#: lib/line.tcl:23
+msgid "Go"
+msgstr "Ð\9fÑ\80идвижване"
-#: lib/choose_repository.tcl:826
-#, tcl-format
-msgid "Unable to hardlink object: %s"
-msgstr "Неуспешно създаване на твърда връзка към обект: %s"
+#: lib/merge.tcl:13
+msgid ""
+"Cannot merge while amending.\n"
+"\n"
+"You must finish amending this commit before starting any type of merge.\n"
+msgstr ""
+"По време на поправяне не може да сливане.\n"
+"\n"
+"Трябва да завършите поправянето на текущото подаване, преди да започнете "
+"сливане.\n"
-#: lib/choose_repository.tcl:881
-msgid "Cannot fetch branches and objects. See console output for details."
+#: lib/merge.tcl:27
+msgid ""
+"Last scanned state does not match repository state.\n"
+"\n"
+"Another Git program has modified this repository since the last scan. A "
+"rescan must be performed before a merge can be performed.\n"
+"\n"
+"The rescan will be automatically started now.\n"
msgstr ""
-"Клоните и обектите не могат да бъдат изтеглени. За повече информация "
-"погледнете изхода на конзолата."
+"Последно установеното състояние не отговаря на това в хранилището.\n"
+"\n"
+"Някой друг процес за Git е променил хранилището междувременно. Състоянието "
+"трябва да бъде проверено, преди да се извърши сливане.\n"
+"\n"
+"Автоматично ще започне нова проверка.\n"
+"\n"
-#: lib/choose_repository.tcl:892
-msgid "Cannot fetch tags. See console output for details."
+#: lib/merge.tcl:45
+#, tcl-format
+msgid ""
+"You are in the middle of a conflicted merge.\n"
+"\n"
+"File %s has merge conflicts.\n"
+"\n"
+"You must resolve them, stage the file, and commit to complete the current "
+"merge. Only then can you begin another merge.\n"
msgstr ""
-"Етикетите не могат да бъдат изтеглени. За повече информация погледнете "
-"изхода на конзолата."
+"В момента тече сливане, но има конфликти.\n"
+"\n"
+"Погледнете файла „%s“.\n"
+"\n"
+"Трябва да коригирате конфликтите в него, да го добавите към индекса и да "
+"завършите текущото сливане чрез подаване. Чак тогава може да започнете ново "
+"сливане.\n"
-#: lib/choose_repository.tcl:916
-msgid "Cannot determine HEAD. See console output for details."
+#: lib/merge.tcl:55
+#, tcl-format
+msgid ""
+"You are in the middle of a change.\n"
+"\n"
+"File %s is modified.\n"
+"\n"
+"You should complete the current commit before starting a merge. Doing so "
+"will help you abort a failed merge, should the need arise.\n"
msgstr ""
-"Върхът „HEAD“ не може да бъде определен. За повече информация погледнете "
-"изхода на конзолата."
+"В момента тече подаване.\n"
+"\n"
+"Файлът „%s“ е променен.\n"
+"\n"
+"Трябва да завършите текущото подаване, преди да започнете сливане. Така ще "
+"можете лесно да преустановите сливането, ако възникне нужда.\n"
-#: lib/choose_repository.tcl:925
+#: lib/merge.tcl:108
#, tcl-format
-msgid "Unable to cleanup %s"
-msgstr "„%s“ не може да се зачисти"
+msgid "%s of %s"
+msgstr "%s от общо %s"
-#: lib/choose_repository.tcl:931
-msgid "Clone failed."
-msgstr "Неуспешно клониране."
+#: lib/merge.tcl:126
+#, tcl-format
+msgid "Merging %s and %s..."
+msgstr "Сливане на „%s“ и „%s“…"
-#: lib/choose_repository.tcl:938
-msgid "No default branch obtained."
-msgstr "Ð\9dе е полÑ\83Ñ\87ен клон по подÑ\80азбиÑ\80ане."
+#: lib/merge.tcl:137
+msgid "Merge completed successfully."
+msgstr "СливанеÑ\82о завÑ\8aÑ\80Ñ\88и Ñ\83Ñ\81пеÑ\88но."
-#: lib/choose_repository.tcl:949
-#, tcl-format
-msgid "Cannot resolve %s as a commit."
-msgstr "Няма подаване отговарящо на „%s“."
+#: lib/merge.tcl:139
+msgid "Merge failed. Conflict resolution is required."
+msgstr "Неуспешно сливане — има конфликти за коригиране."
-#: lib/choose_repository.tcl:961
-msgid "Creating working directory"
-msgstr "Създаване на работната директория"
+#: lib/merge.tcl:156
+#, tcl-format
+msgid "%s (%s): Merge"
+msgstr "%s (%s): Сливане"
-#: lib/choose_repository.tcl:962 lib/index.tcl:70 lib/index.tcl:136
-#: lib/index.tcl:207
-msgid "files"
-msgstr "файлове"
+#: lib/merge.tcl:164
+#, tcl-format
+msgid "Merge Into %s"
+msgstr "Сливане в „%s“"
-#: lib/choose_repository.tcl:981
-msgid "Cannot clone submodules."
-msgstr "Ð\9fодмодÑ\83лиÑ\82е не могаÑ\82 да Ñ\81е клониÑ\80аÑ\82."
+#: lib/merge.tcl:183
+msgid "Revision To Merge"
+msgstr "Ð\92еÑ\80Ñ\81иÑ\8f за Ñ\81ливане"
-#: lib/choose_repository.tcl:990
-msgid "Cloning submodules"
-msgstr "Клониране на подмодулите"
+#: lib/merge.tcl:218
+msgid ""
+"Cannot abort while amending.\n"
+"\n"
+"You must finish amending this commit.\n"
+msgstr ""
+"Поправянето не може да бъде преустановено.\n"
+"\n"
+"Трябва да завършите поправката на това подаване.\n"
-#: lib/choose_repository.tcl:1015
-msgid "Initial file checkout failed."
-msgstr "Неуспешно първоначално изтегляне."
+#: lib/merge.tcl:228
+msgid ""
+"Abort merge?\n"
+"\n"
+"Aborting the current merge will cause *ALL* uncommitted changes to be lost.\n"
+"\n"
+"Continue with aborting the current merge?"
+msgstr ""
+"Да се преустанови ли сливането?\n"
+"\n"
+"В такъв случай ●ВСИЧКИ● неподадени промени ще бъдат безвъзвратно загубени.\n"
+"\n"
+"Наистина ли да се преустанови сливането?"
-#: lib/choose_repository.tcl:1059
-msgid "Open"
-msgstr "Отваряне"
+#: lib/merge.tcl:234
+msgid ""
+"Reset changes?\n"
+"\n"
+"Resetting the changes will cause *ALL* uncommitted changes to be lost.\n"
+"\n"
+"Continue with resetting the current changes?"
+msgstr ""
+"Да се занулят ли промените?\n"
+"\n"
+"В такъв случай ●ВСИЧКИ● неподадени промени ще бъдат безвъзвратно загубени.\n"
+"\n"
+"Наистина ли да се занулят промените?"
-#: lib/choose_repository.tcl:1069
-msgid "Repository:"
-msgstr "Ð¥Ñ\80анилиÑ\89е:"
+#: lib/merge.tcl:245
+msgid "Aborting"
+msgstr "Ð\9fÑ\80еÑ\83Ñ\81Ñ\82ановÑ\8fване"
-#: lib/choose_repository.tcl:1118
-#, tcl-format
-msgid "Failed to open repository %s:"
-msgstr "Неуспешно отваряне на хранилището „%s“:"
+#: lib/merge.tcl:245
+msgid "files reset"
+msgstr "файла със занулени промени"
-#: lib/about.tcl:26
-msgid "git-gui - a graphical user interface for Git."
-msgstr "git-gui — графичен интерфейс за Git."
+#: lib/merge.tcl:273
+msgid "Abort failed."
+msgstr "Неуспешно преустановяване."
-#: lib/checkout_op.tcl:85
-#, tcl-format
-msgid "Fetching %s from %s"
-msgstr "Доставяне на „%s“ от „%s“"
+#: lib/merge.tcl:275
+msgid "Abort completed. Ready."
+msgstr "Успешно преустановяване. Готовност за следващо действие."
-#: lib/checkout_op.tcl:133
-#, tcl-format
-msgid "fatal: Cannot resolve %s"
-msgstr "фатална грешка: „%s“ не може да се открие"
+#: lib/mergetool.tcl:8
+msgid "Force resolution to the base version?"
+msgstr "Да се използва базовата версия"
-#: lib/checkout_op.tcl:175
-#, tcl-format
-msgid "Branch '%s' does not exist."
-msgstr "Клонът „%s“ не съществува."
+#: lib/mergetool.tcl:9
+msgid "Force resolution to this branch?"
+msgstr "Да се използва версията от този клон"
-#: lib/checkout_op.tcl:194
-#, tcl-format
-msgid "Failed to configure simplified git-pull for '%s'."
-msgstr "Неуспешно настройване на опростен git-pull за „%s“."
+#: lib/mergetool.tcl:10
+msgid "Force resolution to the other branch?"
+msgstr "Да се използва версията от другия клон"
-#: lib/checkout_op.tcl:229
+#: lib/mergetool.tcl:14
#, tcl-format
msgid ""
-"Branch '%s' already exists.\n"
+"Note that the diff shows only conflicting changes.\n"
"\n"
-"It cannot fast-forward to %s.\n"
-"A merge is required."
+"%s will be overwritten.\n"
+"\n"
+"This operation can be undone only by restarting the merge."
msgstr ""
-"Ð\9aлонÑ\8aÑ\82 â\80\9e%sâ\80\9c Ñ\81Ñ\8aÑ\89еÑ\81Ñ\82вÑ\83ва.\n"
+"РазликаÑ\82а показва Ñ\81амо Ñ\80азликиÑ\82е Ñ\81 конÑ\84ликÑ\82.\n"
"\n"
-"Той не може да бъде тривиално слят до „%s“.\n"
-"Необходимо е сливане."
+"Файлът „%s“ ще бъде презаписан.\n"
+"\n"
+"Тази операция може да бъде отменена само чрез започване на сливането наново."
-#: lib/checkout_op.tcl:243
+#: lib/mergetool.tcl:45
#, tcl-format
-msgid "Merge strategy '%s' not supported."
-msgstr "Стратегия за сливане „%s“ не се поддържа."
+msgid "File %s seems to have unresolved conflicts, still stage?"
+msgstr ""
+"Изглежда, че все още има некоригирани конфликти във файла „%s“. Да се добави "
+"ли файлът към индекса?"
-#: lib/checkout_op.tcl:262
+#: lib/mergetool.tcl:60
#, tcl-format
-msgid "Failed to update '%s'."
-msgstr "Неуспешно обновяване на „%s“."
-
-#: lib/checkout_op.tcl:274
-msgid "Staging area (index) is already locked."
-msgstr "Индексът вече е заключен."
+msgid "Adding resolution for %s"
+msgstr "Добавяне на корекция на конфликтите в „%s“"
-#: lib/checkout_op.tcl:289
-msgid ""
-"Last scanned state does not match repository state.\n"
-"\n"
-"Another Git program has modified this repository since the last scan. A "
-"rescan must be performed before the current branch can be changed.\n"
-"\n"
-"The rescan will be automatically started now.\n"
+#: lib/mergetool.tcl:141
+msgid "Cannot resolve deletion or link conflicts using a tool"
msgstr ""
-"Състоянието при последната проверка не отговаря на състоянието на "
-"хранилището.\n"
-"\n"
-"Някой друг процес за Git е променил хранилището междувременно. Състоянието "
-"трябва да бъде проверено, преди да се премине към нов клон.\n"
-"\n"
-"Автоматично ще започне нова проверка.\n"
+"Конфликтите при символни връзки или изтриване не могат да бъдат коригирани с "
+"външна програма."
-#: lib/checkout_op.tcl:345
-#, tcl-format
-msgid "Updating working directory to '%s'..."
-msgstr "Работната директория се привежда към „%s“…"
+#: lib/mergetool.tcl:146
+msgid "Conflict file does not exist"
+msgstr "Файлът, в който е конфликтът, не съществува"
-#: lib/checkout_op.tcl:346
-msgid "files checked out"
-msgstr "файла са изтеглени"
+#: lib/mergetool.tcl:246
+#, tcl-format
+msgid "Not a GUI merge tool: '%s'"
+msgstr "Това не е графична програма за сливане: „%s“"
-#: lib/checkout_op.tcl:376
+#: lib/mergetool.tcl:275
#, tcl-format
-msgid "Aborted checkout of '%s' (file level merging is required)."
-msgstr ""
-"Преустановяване на изтеглянето на „%s“ (необходимо е пофайлово сливане)."
+msgid "Unsupported merge tool '%s'"
+msgstr "Неподдържана програма за сливане: „%s“"
-#: lib/checkout_op.tcl:377
-msgid "File level merge required."
-msgstr "Ð\9dеобÑ\85одимо е поÑ\84айлово Ñ\81ливане."
+#: lib/mergetool.tcl:310
+msgid "Merge tool is already running, terminate it?"
+msgstr "Ð\9fÑ\80огÑ\80амаÑ\82а за Ñ\81ливане веÑ\87е е Ñ\81Ñ\82аÑ\80Ñ\82иÑ\80ана. Ð\94а бÑ\8aде ли изклÑ\8eÑ\87ена?"
-#: lib/checkout_op.tcl:381
+#: lib/mergetool.tcl:330
#, tcl-format
-msgid "Staying on branch '%s'."
-msgstr "Оставане върху клона „%s“."
+msgid ""
+"Error retrieving versions:\n"
+"%s"
+msgstr ""
+"Грешка при изтеглянето на версии:\n"
+"%s"
-#: lib/checkout_op.tcl:452
+#: lib/mergetool.tcl:350
+#, tcl-format
msgid ""
-"You are no longer on a local branch.\n"
+"Could not start the merge tool:\n"
"\n"
-"If you wanted to be on a branch, create one now starting from 'This Detached "
-"Checkout'."
+"%s"
msgstr ""
-"Ð\92еÑ\87е не Ñ\81Ñ\82е на локален клон.\n"
+"Ð\9fÑ\80огÑ\80амаÑ\82а за Ñ\81ливане не може да бÑ\8aде Ñ\81Ñ\82аÑ\80Ñ\82иÑ\80ана:\n"
"\n"
-"Ако искате да сте на клон, създайте базиран на „Това несвързано изтегляне“."
+"%s"
-#: lib/checkout_op.tcl:503 lib/checkout_op.tcl:507
+#: lib/mergetool.tcl:354
+msgid "Running merge tool..."
+msgstr "Стартиране на програмата за сливане…"
+
+#: lib/mergetool.tcl:382 lib/mergetool.tcl:390
+msgid "Merge tool failed."
+msgstr "Грешка в програмата за сливане."
+
+#: lib/option.tcl:11
#, tcl-format
-msgid "Checked out '%s'."
-msgstr "„%s“ е изтеглен."
+msgid "Invalid global encoding '%s'"
+msgstr "Неправилно глобално кодиране „%s“"
-#: lib/checkout_op.tcl:535
+#: lib/option.tcl:19
#, tcl-format
-msgid "Resetting '%s' to '%s' will lose the following commits:"
-msgstr ""
-"Зануляването на „%s“ към „%s“ ще доведе до загубването на следните подавания:"
+msgid "Invalid repo encoding '%s'"
+msgstr "Неправилно кодиране „%s“ на хранилището"
-#: lib/checkout_op.tcl:557
-msgid "Recovering lost commits may not be easy."
-msgstr "Ð\92Ñ\8aзÑ\81Ñ\82ановÑ\8fванеÑ\82о на загÑ\83бениÑ\82е подаваниÑ\8f може да е Ñ\82Ñ\80Ñ\83дно."
+#: lib/option.tcl:119
+msgid "Restore Defaults"
+msgstr "СÑ\82андаÑ\80Ñ\82ни наÑ\81Ñ\82Ñ\80ойки"
-#: lib/checkout_op.tcl:562
+#: lib/option.tcl:123
+msgid "Save"
+msgstr "Запазване"
+
+#: lib/option.tcl:133
#, tcl-format
-msgid "Reset '%s'?"
-msgstr "Ð\97анÑ\83лÑ\8fване на â\80\9e%sâ\80\9c?"
+msgid "%s Repository"
+msgstr "Ð¥Ñ\80анилиÑ\89е â\80\9e%sâ\80\9c"
-#: lib/checkout_op.tcl:571 lib/branch_create.tcl:85
-msgid "Reset"
-msgstr "Ð\9eÑ\82наÑ\87ало"
+#: lib/option.tcl:134
+msgid "Global (All Repositories)"
+msgstr "Ð\93лобално (за вÑ\81иÑ\87ки Ñ\85Ñ\80анилиÑ\89а)"
-#: lib/checkout_op.tcl:635
-#, tcl-format
-msgid ""
-"Failed to set current branch.\n"
-"\n"
-"This working directory is only partially switched. We successfully updated "
-"your files, but failed to update an internal Git file.\n"
-"\n"
-"This should not have occurred. %s will now close and give up."
-msgstr ""
-"Неуспешно задаване на текущия клон.\n"
-"\n"
-"Работната директория е само частично обновена: файловете са обновени "
-"успешно, но някой от вътрешните, служебни файлове на Git не е бил.\n"
-"\n"
-"Това състояние е аварийно и не трябва да се случва. Програмата „%s“ ще "
-"преустанови работа."
+#: lib/option.tcl:140
+msgid "User Name"
+msgstr "Потребителско име"
-#: lib/branch_create.tcl:23
-msgid "Create Branch"
-msgstr "СÑ\8aздаване на клон"
+#: lib/option.tcl:141
+msgid "Email Address"
+msgstr "Ð\90дÑ\80еÑ\81 на е-поÑ\89а"
-#: lib/branch_create.tcl:28
-msgid "Create New Branch"
-msgstr "СÑ\8aздаване на нов клон"
+#: lib/option.tcl:143
+msgid "Summarize Merge Commits"
+msgstr "Ð\9eбобÑ\89аване на подаваниÑ\8fÑ\82а пÑ\80и Ñ\81ливане"
-#: lib/branch_create.tcl:42
-msgid "Branch Name"
-msgstr "Ð\98ме на клона"
+#: lib/option.tcl:144
+msgid "Merge Verbosity"
+msgstr "Ð\9fодÑ\80обноÑ\81Ñ\82и пÑ\80и Ñ\81ливаниÑ\8fÑ\82а"
-#: lib/branch_create.tcl:57
-msgid "Match Tracking Branch Name"
-msgstr "СÑ\8aвпадане по имеÑ\82о на Ñ\81ледениÑ\8f клон"
+#: lib/option.tcl:145
+msgid "Show Diffstat After Merge"
+msgstr "Ð\98звеждане на Ñ\81Ñ\82аÑ\82иÑ\81Ñ\82ика Ñ\81лед Ñ\81ливаниÑ\8fÑ\82а"
-#: lib/branch_create.tcl:66
-msgid "Starting Revision"
-msgstr "Ð\9dаÑ\87ална веÑ\80Ñ\81иÑ\8f"
+#: lib/option.tcl:146
+msgid "Use Merge Tool"
+msgstr "Ð\98зползване на пÑ\80огÑ\80ама за Ñ\81ливане"
-#: lib/branch_create.tcl:72
-msgid "Update Existing Branch:"
-msgstr "Ð\9eбновÑ\8fване на Ñ\81Ñ\8aÑ\89еÑ\81Ñ\82вÑ\83ваÑ\89 клон:"
+#: lib/option.tcl:148
+msgid "Trust File Modification Timestamps"
+msgstr "Ð\94овеÑ\80ие вÑ\8aв вÑ\80емеÑ\82о на пÑ\80омÑ\8fна на Ñ\84айловеÑ\82е"
-#: lib/branch_create.tcl:75
-msgid "No"
-msgstr "Ð\9dе"
+#: lib/option.tcl:149
+msgid "Prune Tracking Branches During Fetch"
+msgstr "Ð\9eкаÑ\81Ñ\82Ñ\80Ñ\8fне на Ñ\81ледÑ\8fÑ\89иÑ\82е клонове пÑ\80и доÑ\81Ñ\82авÑ\8fне"
-#: lib/branch_create.tcl:80
-msgid "Fast Forward Only"
-msgstr "Само Ñ\82Ñ\80ивиално пÑ\80евÑ\8aÑ\80Ñ\82аÑ\89о Ñ\81ливане"
+#: lib/option.tcl:150
+msgid "Match Tracking Branches"
+msgstr "Ð\9dапаÑ\81ване на Ñ\81ледÑ\8fÑ\89иÑ\82е клонове"
-#: lib/branch_create.tcl:97
-msgid "Checkout After Creation"
-msgstr "Ð\9fÑ\80еминаване кÑ\8aм клона Ñ\81лед Ñ\81Ñ\8aздаванеÑ\82о мÑ\83"
+#: lib/option.tcl:151
+msgid "Use Textconv For Diffs and Blames"
+msgstr "Ð\98зползване на â\80\9etextconvâ\80\9c за Ñ\80азликиÑ\82е и аноÑ\82иÑ\80анеÑ\82о"
-#: lib/branch_create.tcl:132
-msgid "Please select a tracking branch."
-msgstr "Ð\98збеÑ\80еÑ\82е клон за Ñ\81ледени."
+#: lib/option.tcl:152
+msgid "Blame Copy Only On Changed Files"
+msgstr "Ð\90ноÑ\82иÑ\80ане на копиеÑ\82о Ñ\81амо по пÑ\80оменениÑ\82е Ñ\84айлове"
-#: lib/branch_create.tcl:141
-#, tcl-format
-msgid "Tracking branch %s is not a branch in the remote repository."
-msgstr "Следящият клон — „%s“, не съществува в отдалеченото хранилище."
+#: lib/option.tcl:153
+msgid "Maximum Length of Recent Repositories List"
+msgstr "Максимален брой на списъка „Скоро ползвани“ хранилища"
-#: lib/console.tcl:59
-msgid "Working... please wait..."
-msgstr "Ð\92 моменÑ\82а Ñ\81е извÑ\8aÑ\80Ñ\88ва дейÑ\81Ñ\82вие, изÑ\87акайÑ\82еâ\80¦"
+#: lib/option.tcl:154
+msgid "Minimum Letters To Blame Copy On"
+msgstr "Ð\9cинимален бÑ\80ой знаÑ\86и за аноÑ\82иÑ\80ане на копиеÑ\82о"
-#: lib/console.tcl:186
-msgid "Success"
-msgstr "УÑ\81пеÑ\85"
+#: lib/option.tcl:155
+msgid "Blame History Context Radius (days)"
+msgstr "Ð\98Ñ\81Ñ\82оÑ\80иÑ\87еÑ\81ки обÑ\85ваÑ\82 за аноÑ\82иÑ\80ане в дни"
-#: lib/console.tcl:200
-msgid "Error: Command Failed"
-msgstr "Ð\93Ñ\80еÑ\88ка: неÑ\83Ñ\81пеÑ\88но изпÑ\8aлнение на команда"
+#: lib/option.tcl:156
+msgid "Number of Diff Context Lines"
+msgstr "Ð\91Ñ\80ой Ñ\80едове за конÑ\82екÑ\81Ñ\82а на Ñ\80азликиÑ\82е"
-#: lib/choose_rev.tcl:52
-msgid "This Detached Checkout"
-msgstr "Това неÑ\81вÑ\8aÑ\80зано изÑ\82еглÑ\8fне"
+#: lib/option.tcl:157
+msgid "Additional Diff Parameters"
+msgstr "Ð\90Ñ\80гÑ\83менÑ\82и кÑ\8aм командаÑ\82а за Ñ\80азликиÑ\82е"
-#: lib/choose_rev.tcl:60
-msgid "Revision Expression:"
-msgstr "Ð\98зÑ\80аз за веÑ\80Ñ\81иÑ\8f:"
+#: lib/option.tcl:158
+msgid "Commit Message Text Width"
+msgstr "ШиÑ\80оÑ\87ина на Ñ\82екÑ\81Ñ\82а на Ñ\81Ñ\8aобÑ\89ениеÑ\82о пÑ\80и подаване"
-#: lib/choose_rev.tcl:72
-msgid "Local Branch"
-msgstr "Ð\9bокален клон"
+#: lib/option.tcl:159
+msgid "New Branch Name Template"
+msgstr "Шаблон за имеÑ\82о на новиÑ\82е клони"
-#: lib/choose_rev.tcl:77
-msgid "Tracking Branch"
-msgstr "СледÑ\8fÑ\89 клон"
+#: lib/option.tcl:160
+msgid "Default File Contents Encoding"
+msgstr "Ð\9aодиÑ\80ане на Ñ\84айловеÑ\82е"
-#: lib/choose_rev.tcl:82 lib/choose_rev.tcl:544
-msgid "Tag"
-msgstr "Ð\95Ñ\82икеÑ\82"
+#: lib/option.tcl:161
+msgid "Warn before committing to a detached head"
+msgstr "Ð\9fÑ\80едÑ\83пÑ\80еждаване пÑ\80и подаване кÑ\8aм неÑ\81вÑ\8aÑ\80зан Ñ\83казаÑ\82ел"
-#: lib/choose_rev.tcl:321
-#, tcl-format
-msgid "Invalid revision: %s"
-msgstr "Неправилна версия: %s"
+#: lib/option.tcl:162
+msgid "Staging of untracked files"
+msgstr "Добавяне на неследените файлове към индекса"
-#: lib/choose_rev.tcl:342
-msgid "No revision selected."
-msgstr "Ð\9dе е избÑ\80ана веÑ\80Ñ\81иÑ\8f."
+#: lib/option.tcl:163
+msgid "Show untracked files"
+msgstr "Ð\9fоказване на неÑ\81ледениÑ\82е Ñ\84айлове"
-#: lib/choose_rev.tcl:350
-msgid "Revision expression is empty."
-msgstr "Ð\98зÑ\80азÑ\8aÑ\82 за веÑ\80Ñ\81иÑ\8f е пÑ\80азен."
+#: lib/option.tcl:164
+msgid "Tab spacing"
+msgstr "ШиÑ\80ина на Ñ\82абÑ\83лаÑ\86иÑ\8fÑ\82а"
-#: lib/choose_rev.tcl:537
-msgid "Updated"
-msgstr "Ð\9eбновен"
+#: lib/option.tcl:210
+msgid "Change"
+msgstr "СмÑ\8fна"
-#: lib/choose_rev.tcl:565
-msgid "URL"
-msgstr "Ð\90дÑ\80еÑ\81"
+#: lib/option.tcl:254
+msgid "Spelling Dictionary:"
+msgstr "Ð\9fÑ\80авопиÑ\81ен Ñ\80еÑ\87ник:"
-#: lib/line.tcl:17
-msgid "Goto Line:"
-msgstr "Ð\9aÑ\8aм Ñ\80ед:"
+#: lib/option.tcl:284
+msgid "Change Font"
+msgstr "СмÑ\8fна на Ñ\88Ñ\80иÑ\84Ñ\82а"
-#: lib/line.tcl:23
-msgid "Go"
-msgstr "Придвижване"
+#: lib/option.tcl:288
+#, tcl-format
+msgid "Choose %s"
+msgstr "Избор на „%s“"
-#: lib/commit.tcl:9
-msgid ""
-"There is nothing to amend.\n"
-"\n"
-"You are about to create the initial commit. There is no commit before this "
-"to amend.\n"
-msgstr ""
-"Няма какво да се поправи.\n"
-"\n"
-"Ще създадете първоначалното подаване. Преди него няма други подавания, които "
-"да поправите.\n"
+#: lib/option.tcl:294
+msgid "pt."
+msgstr "тчк."
-#: lib/commit.tcl:18
-msgid ""
-"Cannot amend while merging.\n"
-"\n"
-"You are currently in the middle of a merge that has not been fully "
-"completed. You cannot amend the prior commit unless you first abort the "
-"current merge activity.\n"
-msgstr ""
-"По време на сливане не може да поправяте.\n"
-"\n"
-"В момента все още не сте завършили операция по сливане. Не може да поправите "
-"предишното подаване, освен ако първо не преустановите текущото сливане.\n"
+#: lib/option.tcl:308
+msgid "Preferences"
+msgstr "Настройки"
-#: lib/commit.tcl:48
-msgid "Error loading commit data for amend:"
-msgstr "Ð\93Ñ\80еÑ\88ка пÑ\80и заÑ\80еждане на данниÑ\82е оÑ\82 подаване, коиÑ\82о да Ñ\81е попÑ\80авÑ\8fÑ\82:"
+#: lib/option.tcl:345
+msgid "Failed to completely save options:"
+msgstr "Ð\9dеÑ\83Ñ\81пеÑ\88но запазване на наÑ\81Ñ\82Ñ\80ойкиÑ\82е:"
-#: lib/commit.tcl:75
-msgid "Unable to obtain your identity:"
-msgstr "Ð\98денÑ\82иÑ\84икаÑ\86иÑ\8fÑ\82а ви не може да бÑ\8aде опÑ\80еделена:"
+#: lib/remote.tcl:200
+msgid "Push to"
+msgstr "Ð\98зÑ\82лаÑ\81кване кÑ\8aм"
-#: lib/commit.tcl:80
-msgid "Invalid GIT_COMMITTER_IDENT:"
-msgstr "Ð\9dепÑ\80авилно поле â\80\9eGIT_COMMITTER_IDENTâ\80\9c:"
+#: lib/remote.tcl:218
+msgid "Remove Remote"
+msgstr "Ð\9fÑ\80емаÑ\85ване на оÑ\82далеÑ\87ено Ñ\85Ñ\80анилиÑ\89е"
-#: lib/commit.tcl:129
-#, tcl-format
-msgid "warning: Tcl does not support encoding '%s'."
-msgstr "предупреждение: Tcl не поддържа кодирането „%s“."
+#: lib/remote.tcl:223
+msgid "Prune from"
+msgstr "Окастряне от"
+
+#: lib/remote.tcl:228
+msgid "Fetch from"
+msgstr "Доставяне от"
-#: lib/commit.tcl:149
-msgid ""
-"Last scanned state does not match repository state.\n"
-"\n"
-"Another Git program has modified this repository since the last scan. A "
-"rescan must be performed before another commit can be created.\n"
-"\n"
-"The rescan will be automatically started now.\n"
-msgstr ""
-"Състоянието при последната проверка не отговаря на състоянието на "
-"хранилището.\n"
-"\n"
-"Някой друг процес за Git е променил хранилището междувременно. Състоянието "
-"трябва да бъде проверено преди ново подаване.\n"
-"\n"
-"Автоматично ще започне нова проверка.\n"
+#: lib/remote.tcl:253 lib/remote.tcl:258
+msgid "All"
+msgstr "Всички"
-#: lib/commit.tcl:173
+#: lib/remote_add.tcl:20
#, tcl-format
-msgid ""
-"Unmerged files cannot be committed.\n"
-"\n"
-"File %s has merge conflicts. You must resolve them and stage the file "
-"before committing.\n"
-msgstr ""
-"Неслетите файлове не могат да бъдат подавани.\n"
-"\n"
-"Във файла „%s“ има конфликти при сливане. За да го подадете, трябва първо да "
-"коригирате конфликтите и да добавите файла към индекса за подаване.\n"
+msgid "%s (%s): Add Remote"
+msgstr "%s (%s): Добавяне на отдалечено хранилище"
-#: lib/commit.tcl:181
-#, tcl-format
-msgid ""
-"Unknown file state %s detected.\n"
-"\n"
-"File %s cannot be committed by this program.\n"
-msgstr ""
-"Непознато състояние на файл „%s“.\n"
-"\n"
-"Файлът „%s“ не може да бъде подаден чрез текущата програма.\n"
+#: lib/remote_add.tcl:25
+msgid "Add New Remote"
+msgstr "Добавяне на отдалечено хранилище"
-#: lib/commit.tcl:189
-msgid ""
-"No changes to commit.\n"
-"\n"
-"You must stage at least 1 file before you can commit.\n"
-msgstr ""
-"Няма промени за подаване.\n"
-"\n"
-"Трябва да добавите поне един файл към индекса, за да подадете.\n"
+#: lib/remote_add.tcl:30 lib/tools_dlg.tcl:37
+msgid "Add"
+msgstr "Добавяне"
-#: lib/commit.tcl:204
-msgid ""
-"Please supply a commit message.\n"
-"\n"
-"A good commit message has the following format:\n"
-"\n"
-"- First line: Describe in one sentence what you did.\n"
-"- Second line: Blank\n"
-"- Remaining lines: Describe why this change is good.\n"
-msgstr ""
-"Задайте добро съобщение при подаване.\n"
-"\n"
-"Използвайте следния формат:\n"
-"\n"
-"● Първи ред: описание в едно изречение на промяната.\n"
-"● Втори ред: празен.\n"
-"● Останалите редове: опишете защо се налага тази промяна.\n"
+#: lib/remote_add.tcl:39
+msgid "Remote Details"
+msgstr "Данни за отдалеченото хранилище"
-#: lib/commit.tcl:235
-msgid "Calling pre-commit hook..."
-msgstr "Ð\98зпÑ\8aлнÑ\8fване на кÑ\83каÑ\82а пÑ\80еди подаванеâ\80¦"
+#: lib/remote_add.tcl:50
+msgid "Location:"
+msgstr "Ð\9cеÑ\81Ñ\82оположение:"
-#: lib/commit.tcl:250
-msgid "Commit declined by pre-commit hook."
-msgstr "Ð\9fодаванеÑ\82о е оÑ\82Ñ\85вÑ\8aÑ\80лено оÑ\82 кÑ\83каÑ\82а пÑ\80еди подаване."
+#: lib/remote_add.tcl:60
+msgid "Further Action"
+msgstr "СледваÑ\89о дейÑ\81Ñ\82вие"
-#: lib/commit.tcl:269
-msgid ""
-"You are about to commit on a detached head. This is a potentially dangerous "
-"thing to do because if you switch to another branch you will lose your "
-"changes and it can be difficult to retrieve them later from the reflog. You "
-"should probably cancel this commit and create a new branch to continue.\n"
-" \n"
-" Do you really want to proceed with your Commit?"
-msgstr ""
-"Ще подавате към несвързан връх. Това е опасно — при изтеглянето на друг клон "
-"ще изгубите промените си. След това може да е невъзможно да ги възстановите "
-"от журнала на указателите „reflog“. Най-вероятно трябва да отмените това "
-"подаване и да създадете клон, в който да подадете.\n"
-" \n"
-"Сигурни ли сте, че искате да подадете към несвързан връх?"
+#: lib/remote_add.tcl:63
+msgid "Fetch Immediately"
+msgstr "Незабавно доставяне"
-#: lib/commit.tcl:290
-msgid "Calling commit-msg hook..."
-msgstr "Ð\98зпÑ\8aлнÑ\8fване на кÑ\83каÑ\82а за Ñ\81Ñ\8aобÑ\89ениеÑ\82о пÑ\80и подаванеâ\80¦"
+#: lib/remote_add.tcl:69
+msgid "Initialize Remote Repository and Push"
+msgstr "Ð\98ниÑ\86иализиÑ\80ане на оÑ\82далеÑ\87еноÑ\82о Ñ\85Ñ\80анилиÑ\89е и изÑ\82лаÑ\81кване на пÑ\80омениÑ\82е"
-#: lib/commit.tcl:305
-msgid "Commit declined by commit-msg hook."
-msgstr "Ð\9fодаванеÑ\82о е оÑ\82Ñ\85вÑ\8aÑ\80лено оÑ\82 кÑ\83каÑ\82а за Ñ\81Ñ\8aобÑ\89ениеÑ\82о пÑ\80и подаване."
+#: lib/remote_add.tcl:75
+msgid "Do Nothing Else Now"
+msgstr "Ð\94а не Ñ\81е пÑ\80ави ниÑ\89о"
-#: lib/commit.tcl:318
-msgid "Committing changes..."
-msgstr "Ð\9fодаване на пÑ\80омениÑ\82еâ\80¦"
+#: lib/remote_add.tcl:100
+msgid "Please supply a remote name."
+msgstr "Ð\97адайÑ\82е име за оÑ\82далеÑ\87еноÑ\82о Ñ\85Ñ\80анилиÑ\89е."
-#: lib/commit.tcl:334
-msgid "write-tree failed:"
-msgstr "неуспешно запазване на дървото (write-tree):"
+#: lib/remote_add.tcl:113
+#, tcl-format
+msgid "'%s' is not an acceptable remote name."
+msgstr "Отдалечено хранилище не може да се казва „%s“."
-#: lib/commit.tcl:335 lib/commit.tcl:379 lib/commit.tcl:400
-msgid "Commit failed."
-msgstr "Неуспешно подаване."
+#: lib/remote_add.tcl:124
+#, tcl-format
+msgid "Failed to add remote '%s' of location '%s'."
+msgstr "Неуспешно добавяне на отдалеченото хранилище „%s“ от адрес „%s“."
-#: lib/commit.tcl:352
+#: lib/remote_add.tcl:132 lib/transport.tcl:6
#, tcl-format
-msgid "Commit %s appears to be corrupt"
-msgstr "Ð\9fодаванеÑ\82о â\80\9e%sâ\80\9c изглежда повÑ\80едено"
+msgid "fetch %s"
+msgstr "доÑ\81Ñ\82авÑ\8fне на â\80\9e%sâ\80\9c"
-#: lib/commit.tcl:357
-msgid ""
-"No changes to commit.\n"
-"\n"
-"No files were modified by this commit and it was not a merge commit.\n"
-"\n"
-"A rescan will be automatically started now.\n"
-msgstr ""
-"Няма промени за подаване.\n"
-"\n"
-"В това подаване не са променяни никакви файлове, а и не е подаване със "
-"сливане.\n"
-"\n"
-"Автоматично ще започне нова проверка.\n"
+#: lib/remote_add.tcl:133
+#, tcl-format
+msgid "Fetching the %s"
+msgstr "Доставяне на „%s“"
-#: lib/commit.tcl:364
-msgid "No changes to commit."
-msgstr "Няма промени за подаване."
+#: lib/remote_add.tcl:156
+#, tcl-format
+msgid "Do not know how to initialize repository at location '%s'."
+msgstr "Хранилището с местоположение „%s“ не може да бъде инициализирано."
-#: lib/commit.tcl:378
-msgid "commit-tree failed:"
-msgstr "неуспешно подаване на дървото (commit-tree):"
+#: lib/remote_add.tcl:162 lib/transport.tcl:54 lib/transport.tcl:92
+#: lib/transport.tcl:110
+#, tcl-format
+msgid "push %s"
+msgstr "изтласкване на „%s“"
-#: lib/commit.tcl:399
-msgid "update-ref failed:"
-msgstr "неуспешно обновяване на указателите (update-ref):"
+#: lib/remote_add.tcl:163
+#, tcl-format
+msgid "Setting up the %s (at %s)"
+msgstr "Добавяне на хранилище „%s“ (с адрес „%s“)"
-#: lib/commit.tcl:492
+#: lib/remote_branch_delete.tcl:29
#, tcl-format
-msgid "Created commit %s: %s"
-msgstr "Успешно подаване %s: %s"
+msgid "%s (%s): Delete Branch Remotely"
+msgstr "%s (%s): Изтриване на отдалечения клон"
-#: lib/branch_delete.tcl:16
-msgid "Delete Branch"
-msgstr "Изтриване на клон"
+#: lib/remote_branch_delete.tcl:34
+msgid "Delete Branch Remotely"
+msgstr "Ð\98зÑ\82Ñ\80иване на оÑ\82далеÑ\87ениÑ\8f клон"
-#: lib/branch_delete.tcl:21
-msgid "Delete Local Branch"
-msgstr "Ð\98зÑ\82Ñ\80иване на локален клон"
+#: lib/remote_branch_delete.tcl:48
+msgid "From Repository"
+msgstr "Ð\9eÑ\82 Ñ\85Ñ\80анилиÑ\89е"
-#: lib/branch_delete.tcl:39
-msgid "Local Branches"
-msgstr "Ð\9bокални клони"
+#: lib/remote_branch_delete.tcl:51 lib/transport.tcl:165
+msgid "Remote:"
+msgstr "Ð\9eÑ\82далеÑ\87ено Ñ\85Ñ\80анилиÑ\89е:"
-#: lib/branch_delete.tcl:51
-msgid "Delete Only If Merged Into"
-msgstr "Ð\98зÑ\82Ñ\80иване, Ñ\81амо ако пÑ\80омениÑ\82е Ñ\81а Ñ\81леÑ\82и и дÑ\80Ñ\83гаде"
+#: lib/remote_branch_delete.tcl:72 lib/transport.tcl:187
+msgid "Arbitrary Location:"
+msgstr "Ð\9fÑ\80оизволно меÑ\81Ñ\82оположение:"
-#: lib/branch_delete.tcl:103
-#, tcl-format
-msgid "The following branches are not completely merged into %s:"
-msgstr "Не всички промени в клоните са слети в „%s“:"
+#: lib/remote_branch_delete.tcl:88
+msgid "Branches"
+msgstr "Клони"
-#: lib/branch_delete.tcl:141
+#: lib/remote_branch_delete.tcl:110
+msgid "Delete Only If"
+msgstr "Изтриване, само ако"
+
+#: lib/remote_branch_delete.tcl:112
+msgid "Merged Into:"
+msgstr "Слят в:"
+
+#: lib/remote_branch_delete.tcl:153
+msgid "A branch is required for 'Merged Into'."
+msgstr "За данните „Слят в“ е необходимо да зададете клон."
+
+#: lib/remote_branch_delete.tcl:185
#, tcl-format
msgid ""
-"Failed to delete branches:\n"
-"%s"
+"The following branches are not completely merged into %s:\n"
+"\n"
+" - %s"
msgstr ""
-"Неуспешно триене на клони:\n"
-"%s"
+"Следните клони не са слети напълно в „%s“:\n"
+"\n"
+" ● %s"
-#: lib/blame.tcl:73
-msgid "File Viewer"
-msgstr "Преглед на файлове"
+#: lib/remote_branch_delete.tcl:190
+#, tcl-format
+msgid ""
+"One or more of the merge tests failed because you have not fetched the "
+"necessary commits. Try fetching from %s first."
+msgstr ""
+"Поне една от пробите за сливане е неуспешна, защото не сте доставили всички "
+"необходими подавания. Пробвайте първо да доставите подаванията от „%s“."
-#: lib/blame.tcl:79
-msgid "Commit:"
-msgstr "Ð\9fодаване:"
+#: lib/remote_branch_delete.tcl:208
+msgid "Please select one or more branches to delete."
+msgstr "Ð\98збеÑ\80еÑ\82е поне един клон за изÑ\82Ñ\80иване."
-#: lib/blame.tcl:280
-msgid "Copy Commit"
-msgstr "Копиране на подаване"
+#: lib/remote_branch_delete.tcl:227
+#, tcl-format
+msgid "Deleting branches from %s"
+msgstr "Изтриване на клони от „%s“"
-#: lib/blame.tcl:284
-msgid "Find Text..."
-msgstr "ТÑ\8aÑ\80Ñ\81ене на Ñ\82екÑ\81Ñ\82â\80¦"
+#: lib/remote_branch_delete.tcl:300
+msgid "No repository selected."
+msgstr "Ð\9dе е избÑ\80ано Ñ\85Ñ\80анилиÑ\89е."
-#: lib/blame.tcl:288
-msgid "Goto Line..."
-msgstr "Към ред…"
+#: lib/remote_branch_delete.tcl:305
+#, tcl-format
+msgid "Scanning %s..."
+msgstr "Претърсване на „%s“…"
-#: lib/blame.tcl:297
-msgid "Do Full Copy Detection"
-msgstr "Ð\9fÑ\8aлно Ñ\82Ñ\8aÑ\80Ñ\81ене на копиÑ\80ане"
+#: lib/search.tcl:48
+msgid "Find:"
+msgstr "ТÑ\8aÑ\80Ñ\81ене:"
-#: lib/blame.tcl:301
-msgid "Show History Context"
-msgstr "Ð\9fоказване на конÑ\82екÑ\81Ñ\82а оÑ\82 иÑ\81Ñ\82оÑ\80иÑ\8fÑ\82а"
+#: lib/search.tcl:50
+msgid "Next"
+msgstr "СледваÑ\89а поÑ\8fва"
-#: lib/blame.tcl:304
-msgid "Blame Parent Commit"
-msgstr "Ð\90ноÑ\82иÑ\80ане на Ñ\80одиÑ\82елÑ\81коÑ\82о подаване"
+#: lib/search.tcl:51
+msgid "Prev"
+msgstr "Ð\9fÑ\80едиÑ\88на поÑ\8fва"
-#: lib/blame.tcl:466
-#, tcl-format
-msgid "Reading %s..."
-msgstr "Чете се „%s“…"
+#: lib/search.tcl:52
+msgid "RegExp"
+msgstr "РегИзр"
-#: lib/blame.tcl:594
-msgid "Loading copy/move tracking annotations..."
-msgstr "Ð\97аÑ\80еждане на аноÑ\82аÑ\86ииÑ\82е за пÑ\80оÑ\81ледÑ\8fване на копиÑ\80анеÑ\82о/пÑ\80емеÑ\81Ñ\82ванеÑ\82оâ\80¦"
+#: lib/search.tcl:54
+msgid "Case"
+msgstr "Ð\93лавни/малки"
-#: lib/blame.tcl:614
-msgid "lines annotated"
-msgstr "реда анотирани"
+#: lib/shortcut.tcl:8 lib/shortcut.tcl:43 lib/shortcut.tcl:75
+#, tcl-format
+msgid "%s (%s): Create Desktop Icon"
+msgstr "%s (%s): Добавяне на икона на работния плот"
-#: lib/blame.tcl:806
-msgid "Loading original location annotations..."
-msgstr "Ð\97аÑ\80еждане на аноÑ\82аÑ\86ииÑ\82е за пÑ\8aÑ\80вонаÑ\87алноÑ\82о меÑ\81Ñ\82оположениеâ\80¦"
+#: lib/shortcut.tcl:24 lib/shortcut.tcl:65
+msgid "Cannot write shortcut:"
+msgstr "Ð\9aлавиÑ\88наÑ\82а комбинаÑ\86иÑ\8f не може да бÑ\8aде запазена:"
-#: lib/blame.tcl:809
-msgid "Annotation complete."
-msgstr "Ð\90ноÑ\82иÑ\80анеÑ\82о завÑ\8aÑ\80Ñ\88и."
+#: lib/shortcut.tcl:140
+msgid "Cannot write icon:"
+msgstr "Ð\98конаÑ\82а не може да бÑ\8aде запазена:"
-#: lib/blame.tcl:839
-msgid "Busy"
-msgstr "Ð\9eпеÑ\80аÑ\86иÑ\8fÑ\82а не е завÑ\8aÑ\80Ñ\88ила"
+#: lib/spellcheck.tcl:57
+msgid "Unsupported spell checker"
+msgstr "Тази пÑ\80огÑ\80ама за пÑ\80овеÑ\80ка на пÑ\80авопиÑ\81а не Ñ\81е поддÑ\8aÑ\80жа"
-#: lib/blame.tcl:840
-msgid "Annotation process is already running."
-msgstr "Ð\92 моменÑ\82а Ñ\82еÑ\87е пÑ\80оÑ\86еÑ\81 на аноÑ\82иÑ\80ане."
+#: lib/spellcheck.tcl:65
+msgid "Spell checking is unavailable"
+msgstr "Ð\9bипÑ\81ва пÑ\80огÑ\80ама за пÑ\80овеÑ\80ка на пÑ\80авопиÑ\81а"
-#: lib/blame.tcl:879
-msgid "Running thorough copy detection..."
-msgstr "Ð\98зпÑ\8aлнÑ\8fва Ñ\81е Ñ\86Ñ\8fлоÑ\81Ñ\82ен пÑ\80оÑ\86еÑ\81 на оÑ\82кÑ\80иване на копиÑ\80анеâ\80¦"
+#: lib/spellcheck.tcl:68
+msgid "Invalid spell checking configuration"
+msgstr "Ð\9dепÑ\80авилни наÑ\81Ñ\82Ñ\80ойки на пÑ\80овеÑ\80каÑ\82а на пÑ\80авопиÑ\81а"
-#: lib/blame.tcl:947
-msgid "Loading annotation..."
-msgstr "Зареждане на анотации…"
+#: lib/spellcheck.tcl:70
+#, tcl-format
+msgid "Reverting dictionary to %s."
+msgstr "Ползване на речник за език „%s“."
-#: lib/blame.tcl:1000
-msgid "Author:"
-msgstr "Ð\90вÑ\82оÑ\80:"
+#: lib/spellcheck.tcl:73
+msgid "Spell checker silently failed on startup"
+msgstr "Ð\9fÑ\80огÑ\80амаÑ\82а за пÑ\80авопиÑ\81 даже не Ñ\81Ñ\82аÑ\80Ñ\82иÑ\80а Ñ\83Ñ\81пеÑ\88но."
-#: lib/blame.tcl:1004
-msgid "Committer:"
-msgstr "Ð\9fодал:"
+#: lib/spellcheck.tcl:80
+msgid "Unrecognized spell checker"
+msgstr "Ð\9dепознаÑ\82а пÑ\80огÑ\80ама за пÑ\80овеÑ\80ка на пÑ\80авопиÑ\81а"
-#: lib/blame.tcl:1009
-msgid "Original File:"
-msgstr "Ð\9fÑ\8aÑ\80вонаÑ\87ален Ñ\84айл:"
+#: lib/spellcheck.tcl:186
+msgid "No Suggestions"
+msgstr "Ð\9dÑ\8fма пÑ\80едложениÑ\8f"
-#: lib/blame.tcl:1057
-msgid "Cannot find HEAD commit:"
-msgstr "Ð\9fодаванеÑ\82о за вÑ\80Ñ\8aÑ\85 â\80\9eHEADâ\80\9c не може да Ñ\81е оÑ\82кÑ\80ие:"
+#: lib/spellcheck.tcl:388
+msgid "Unexpected EOF from spell checker"
+msgstr "Ð\9dеоÑ\87акван кÑ\80ай на Ñ\84айл оÑ\82 пÑ\80огÑ\80амаÑ\82а за пÑ\80овеÑ\80ка на пÑ\80авопиÑ\81а"
-#: lib/blame.tcl:1112
-msgid "Cannot find parent commit:"
-msgstr "РодиÑ\82елÑ\81коÑ\82о подаване не може да бÑ\8aде оÑ\82кÑ\80иÑ\82о"
+#: lib/spellcheck.tcl:392
+msgid "Spell Checker Failed"
+msgstr "Ð\93Ñ\80еÑ\88ка в пÑ\80огÑ\80амаÑ\82а за пÑ\80овеÑ\80ка на пÑ\80авопиÑ\81а"
-#: lib/blame.tcl:1127
-msgid "Unable to display parent"
-msgstr "РодиÑ\82елÑ\8fÑ\82 не може да бÑ\8aде показан"
+#: lib/sshkey.tcl:31
+msgid "No keys found."
+msgstr "Ð\9dе Ñ\81а оÑ\82кÑ\80иÑ\82и клÑ\8eÑ\87ове."
-#: lib/blame.tcl:1269
-msgid "Originally By:"
-msgstr "Първоначално от:"
+#: lib/sshkey.tcl:34
+#, tcl-format
+msgid "Found a public key in: %s"
+msgstr "Открит е публичен ключ в „%s“"
-#: lib/blame.tcl:1275
-msgid "In File:"
-msgstr "Ð\92Ñ\8aв Ñ\84айл:"
+#: lib/sshkey.tcl:40
+msgid "Generate Key"
+msgstr "Ð\93енеÑ\80иÑ\80ане на клÑ\8eÑ\87"
-#: lib/blame.tcl:1280
-msgid "Copied Or Moved Here By:"
-msgstr "Ð\9aопиÑ\80ано или пÑ\80емеÑ\81Ñ\82ено Ñ\82Ñ\83к оÑ\82:"
+#: lib/sshkey.tcl:58
+msgid "Copy To Clipboard"
+msgstr "Ð\9aопиÑ\80ане кÑ\8aм Ñ\81иÑ\81Ñ\82емниÑ\8f бÑ\83Ñ\84еÑ\80"
-#: lib/index.tcl:6
-msgid "Unable to unlock the index."
-msgstr "Ð\98ндекÑ\81Ñ\8aÑ\82 не може да бÑ\8aде оÑ\82клÑ\8eÑ\87ен."
+#: lib/sshkey.tcl:72
+msgid "Your OpenSSH Public Key"
+msgstr "Ð\9fÑ\83блиÑ\87ниÑ\8fÑ\82 ви клÑ\8eÑ\87 за OpenSSH"
-#: lib/index.tcl:17
-msgid "Index Error"
-msgstr "Грешка в индекса"
+#: lib/sshkey.tcl:80
+msgid "Generating..."
+msgstr "Генериране…"
-#: lib/index.tcl:19
+#: lib/sshkey.tcl:86
+#, tcl-format
msgid ""
-"Updating the Git index failed. A rescan will be automatically started to "
-"resynchronize git-gui."
+"Could not start ssh-keygen:\n"
+"\n"
+"%s"
msgstr ""
-"Неуспешно обновяване на индекса на Git. Автоматично ще започне нова проверка "
-"за синхронизирането на git-gui."
+"Програмата „ssh-keygen“ не може да бъде стартирана:\n"
+"\n"
+"%s"
-#: lib/index.tcl:30
-msgid "Continue"
-msgstr "Ð\9fÑ\80одÑ\8aлжаване"
+#: lib/sshkey.tcl:113
+msgid "Generation failed."
+msgstr "Ð\9dеÑ\83Ñ\81пеÑ\88но генеÑ\80иÑ\80ане."
-#: lib/index.tcl:33
-msgid "Unlock Index"
-msgstr "Ð\9eÑ\82клÑ\8eÑ\87ване на индекÑ\81а"
+#: lib/sshkey.tcl:120
+msgid "Generation succeeded, but no keys found."
+msgstr "Ð\93енеÑ\80иÑ\80анеÑ\82о завÑ\8aÑ\80Ñ\88и Ñ\83Ñ\81пеÑ\88но, а не Ñ\81а намеÑ\80ени клÑ\8eÑ\87ове."
-#: lib/index.tcl:298
+#: lib/sshkey.tcl:123
#, tcl-format
-msgid "Unstaging %s from commit"
-msgstr "Ð\98зваждане на â\80\9e%sâ\80\9c оÑ\82 подаванеÑ\82о"
+msgid "Your key is in: %s"
+msgstr "Ð\9aлÑ\8eÑ\87Ñ\8aÑ\82 ви е в â\80\9e%sâ\80\9c"
-#: lib/index.tcl:337
-msgid "Ready to commit."
-msgstr "Готовност за подаване."
+#: lib/status_bar.tcl:87
+#, tcl-format
+msgid "%s ... %*i of %*i %s (%3i%%)"
+msgstr "%s… %*i от общо %*i %s (%3i%%)"
-#: lib/index.tcl:350
+#: lib/tools.tcl:76
#, tcl-format
-msgid "Adding %s"
-msgstr "Ð\94обавÑ\8fне на â\80\9e%sâ\80\9c"
+msgid "Running %s requires a selected file."
+msgstr "Ð\97а изпÑ\8aлнениеÑ\82о на â\80\9e%sâ\80\9c Ñ\82Ñ\80Ñ\8fбва да избеÑ\80еÑ\82е Ñ\84айл."
-#: lib/index.tcl:380
+#: lib/tools.tcl:92
#, tcl-format
-msgid "Stage %d untracked files?"
-msgstr "Ð\94а Ñ\81е вкаÑ\80аÑ\82 ли %d неÑ\81ледени Ñ\84айла в индекÑ\81а?"
+msgid "Are you sure you want to run %1$s on file \"%2$s\"?"
+msgstr "СигÑ\83Ñ\80ни ли Ñ\81Ñ\82е, Ñ\87е иÑ\81каÑ\82е да изпÑ\8aлниÑ\82е â\80\9e%1$sâ\80\9c вÑ\8aÑ\80Ñ\85Ñ\83 Ñ\84айла â\80\9e%2$sâ\80\9c?"
-#: lib/index.tcl:428
+#: lib/tools.tcl:96
#, tcl-format
-msgid "Revert changes in file %s?"
-msgstr "Ð\94а Ñ\81е маÑ\85наÑ\82 ли пÑ\80омениÑ\82е вÑ\8aв Ñ\84айла „%s“?"
+msgid "Are you sure you want to run %s?"
+msgstr "СигÑ\83Ñ\80ни ли Ñ\81Ñ\82е, Ñ\87е иÑ\81каÑ\82е да изпÑ\8aлниÑ\82е „%s“?"
-#: lib/index.tcl:430
+#: lib/tools.tcl:118
#, tcl-format
-msgid "Revert changes in these %i files?"
-msgstr "Ð\94а Ñ\81е маÑ\85наÑ\82 ли пÑ\80омениÑ\82е в Ñ\82ези %i Ñ\84айла?"
+msgid "Tool: %s"
+msgstr "Ð\9aоманда: %s"
-#: lib/index.tcl:438
-msgid "Any unstaged changes will be permanently lost by the revert."
+#: lib/tools.tcl:119
+#, tcl-format
+msgid "Running: %s"
+msgstr "Изпълнение: %s"
+
+#: lib/tools.tcl:158
+#, tcl-format
+msgid "Tool completed successfully: %s"
+msgstr "Командата завърши успешно: %s"
+
+#: lib/tools.tcl:160
+#, tcl-format
+msgid "Tool failed: %s"
+msgstr "Командата върна грешка: %s"
+
+#: lib/tools_dlg.tcl:22
+#, tcl-format
+msgid "%s (%s): Add Tool"
+msgstr "%s (%s): Добавяне на команда"
+
+#: lib/tools_dlg.tcl:28
+msgid "Add New Tool Command"
+msgstr "Добавяне на команда"
+
+#: lib/tools_dlg.tcl:34
+msgid "Add globally"
+msgstr "Глобално добавяне"
+
+#: lib/tools_dlg.tcl:46
+msgid "Tool Details"
+msgstr "Подробности за командата"
+
+#: lib/tools_dlg.tcl:49
+msgid "Use '/' separators to create a submenu tree:"
+msgstr "За създаване на подменюта използвайте знака „/“ за разделител:"
+
+#: lib/tools_dlg.tcl:60
+msgid "Command:"
+msgstr "Команда:"
+
+#: lib/tools_dlg.tcl:71
+msgid "Show a dialog before running"
+msgstr "Преди изпълнение да се извежда диалогов прозорец"
+
+#: lib/tools_dlg.tcl:77
+msgid "Ask the user to select a revision (sets $REVISION)"
+msgstr "Потребителят да укаже версия (задаване на променливата $REVISION)"
+
+#: lib/tools_dlg.tcl:82
+msgid "Ask the user for additional arguments (sets $ARGS)"
msgstr ""
-"Всички промени, които не са били вкарани в индекса, ще бъдат безвъзвратно "
-"загубени."
+"Потребителят да укаже допълнителни аргументи (задаване на променливата $ARGS)"
-#: lib/index.tcl:441
-msgid "Do Nothing"
-msgstr "Нищо да не се прави"
+#: lib/tools_dlg.tcl:89
+msgid "Don't show the command output window"
+msgstr "Без показване на прозорец с изхода от командата"
+
+#: lib/tools_dlg.tcl:94
+msgid "Run only if a diff is selected ($FILENAME not empty)"
+msgstr ""
+"Стартиране само след избор на разлика (променливата $FILENAME не е празна)"
-#: lib/index.tcl:459
-msgid "Reverting selected files"
-msgstr "Ð\9cаÑ\85ане на пÑ\80омениÑ\82е в избÑ\80аниÑ\82е Ñ\84айлове"
+#: lib/tools_dlg.tcl:118
+msgid "Please supply a name for the tool."
+msgstr "Ð\97адайÑ\82е име за командаÑ\82а."
-#: lib/index.tcl:463
+#: lib/tools_dlg.tcl:126
#, tcl-format
-msgid "Reverting %s"
-msgstr "Ð\9cаÑ\85ане на пÑ\80омениÑ\82е в â\80\9e%sâ\80\9c"
+msgid "Tool '%s' already exists."
+msgstr "Ð\9aомандаÑ\82а â\80\9e%sâ\80\9c веÑ\87е Ñ\81Ñ\8aÑ\89еÑ\81Ñ\82вÑ\83ва."
-#: lib/date.tcl:25
+#: lib/tools_dlg.tcl:148
#, tcl-format
-msgid "Invalid date from Git: %s"
-msgstr "Неправилни данни от Git: %s"
-
-#: lib/database.tcl:42
-msgid "Number of loose objects"
-msgstr "Брой непакетирани обекти"
+msgid ""
+"Could not add tool:\n"
+"%s"
+msgstr ""
+"Командата не може да бъде добавена:\n"
+"%s"
-#: lib/database.tcl:43
-msgid "Disk space used by loose objects"
-msgstr "Дисково пространство заето от непакетирани обекти"
+#: lib/tools_dlg.tcl:187
+#, tcl-format
+msgid "%s (%s): Remove Tool"
+msgstr "%s (%s): Премахване на команда"
-#: lib/database.tcl:44
-msgid "Number of packed objects"
-msgstr "Ð\91Ñ\80ой пакеÑ\82иÑ\80ани обекÑ\82и"
+#: lib/tools_dlg.tcl:193
+msgid "Remove Tool Commands"
+msgstr "Ð\9fÑ\80емаÑ\85ване на команди"
-#: lib/database.tcl:45
-msgid "Number of packs"
-msgstr "Ð\91Ñ\80ой пакеÑ\82и"
+#: lib/tools_dlg.tcl:198
+msgid "Remove"
+msgstr "Ð\9fÑ\80емаÑ\85ване"
-#: lib/database.tcl:46
-msgid "Disk space used by packed objects"
-msgstr "Дисково пространство заето от пакетирани обекти"
+#: lib/tools_dlg.tcl:231
+msgid "(Blue denotes repository-local tools)"
+msgstr "(командите към локалното хранилище са обозначени в синьо)"
-#: lib/database.tcl:47
-msgid "Packed objects waiting for pruning"
-msgstr "Пакетирани обекти за окастряне"
+#: lib/tools_dlg.tcl:283
+#, tcl-format
+msgid "%s (%s):"
+msgstr "%s (%s):"
-#: lib/database.tcl:48
-msgid "Garbage files"
-msgstr "Файлове за боклука"
+#: lib/tools_dlg.tcl:292
+#, tcl-format
+msgid "Run Command: %s"
+msgstr "Изпълнение на командата „%s“"
-#: lib/database.tcl:72
-msgid "Compressing the object database"
-msgstr "Ð\9aомпÑ\80еÑ\81иÑ\80ане на базаÑ\82а Ñ\81 данни за обекÑ\82иÑ\82е"
+#: lib/tools_dlg.tcl:306
+msgid "Arguments"
+msgstr "Ð\90Ñ\80гÑ\83менÑ\82и"
-#: lib/database.tcl:83
-msgid "Verifying the object database with fsck-objects"
-msgstr "Ð\9fÑ\80овеÑ\80ка на базаÑ\82а Ñ\81 данни за обекÑ\82иÑ\82е Ñ\81 пÑ\80огÑ\80амаÑ\82а â\80\9efsck-objectsâ\80\9c"
+#: lib/tools_dlg.tcl:341
+msgid "OK"
+msgstr "Ð\94обÑ\80е"
-#: lib/database.tcl:107
+#: lib/transport.tcl:7
#, tcl-format
-msgid ""
-"This repository currently has approximately %i loose objects.\n"
-"\n"
-"To maintain optimal performance it is strongly recommended that you compress "
-"the database.\n"
-"\n"
-"Compress the database now?"
-msgstr ""
-"В това хранилище в момента има към %i непакетирани обекти.\n"
-"\n"
-"За добра производителност се препоръчва да компресирате базата с данни за "
-"обектите.\n"
-"\n"
-"Да се започне ли компресирането?"
+msgid "Fetching new changes from %s"
+msgstr "Доставяне на промените от „%s“"
-#: lib/error.tcl:20 lib/error.tcl:116
-msgid "error"
-msgstr "грешка"
+#: lib/transport.tcl:18
+#, tcl-format
+msgid "remote prune %s"
+msgstr "окастряне на следящите клони към „%s“"
-#: lib/error.tcl:36
-msgid "warning"
-msgstr "предупреждение"
+#: lib/transport.tcl:19
+#, tcl-format
+msgid "Pruning tracking branches deleted from %s"
+msgstr "Окастряне на следящите клони на изтритите клони от „%s“"
-#: lib/error.tcl:96
-msgid "You must correct the above errors before committing."
-msgstr "Ð\9fÑ\80еди да можеÑ\82е да подадеÑ\82е, коÑ\80игиÑ\80айÑ\82е гоÑ\80ниÑ\82е гÑ\80еÑ\88ки."
+#: lib/transport.tcl:25
+msgid "fetch all remotes"
+msgstr "доÑ\81Ñ\82авÑ\8fне оÑ\82 вÑ\81иÑ\87ки оÑ\82далеÑ\87ени"
-#: lib/merge.tcl:13
-msgid ""
-"Cannot merge while amending.\n"
-"\n"
-"You must finish amending this commit before starting any type of merge.\n"
-msgstr ""
-"По време на поправяне не може да сливане.\n"
-"\n"
-"Трябва да завършите поправянето на текущото подаване, преди да започнете "
-"сливане.\n"
+#: lib/transport.tcl:26
+msgid "Fetching new changes from all remotes"
+msgstr "Доставяне на промените от всички отдалечени хранилища"
-#: lib/merge.tcl:27
-msgid ""
-"Last scanned state does not match repository state.\n"
-"\n"
-"Another Git program has modified this repository since the last scan. A "
-"rescan must be performed before a merge can be performed.\n"
-"\n"
-"The rescan will be automatically started now.\n"
-msgstr ""
-"Последно установеното състояние не отговаря на това в хранилището.\n"
-"\n"
-"Някой друг процес за Git е променил хранилището междувременно. Състоянието "
-"трябва да бъде проверено, преди да се извърши сливане.\n"
-"\n"
-"Автоматично ще започне нова проверка.\n"
-"\n"
+#: lib/transport.tcl:40
+msgid "remote prune all remotes"
+msgstr "окастряне на следящите изтрити"
-#: lib/merge.tcl:45
-#, tcl-format
-msgid ""
-"You are in the middle of a conflicted merge.\n"
-"\n"
-"File %s has merge conflicts.\n"
-"\n"
-"You must resolve them, stage the file, and commit to complete the current "
-"merge. Only then can you begin another merge.\n"
+#: lib/transport.tcl:41
+msgid "Pruning tracking branches deleted from all remotes"
msgstr ""
-"В момента тече сливане, но има конфликти.\n"
-"\n"
-"Погледнете файла „%s“.\n"
-"\n"
-"Трябва да коригирате конфликтите в него, да го добавите към индекса и да "
-"завършите текущото сливане чрез подаване. Чак тогава може да започнете ново "
-"сливане.\n"
+"Окастряне на следящите клони на изтритите клони от всички отдалечени "
+"хранилища"
-#: lib/merge.tcl:55
+#: lib/transport.tcl:55
#, tcl-format
-msgid ""
-"You are in the middle of a change.\n"
-"\n"
-"File %s is modified.\n"
-"\n"
-"You should complete the current commit before starting a merge. Doing so "
-"will help you abort a failed merge, should the need arise.\n"
-msgstr ""
-"В момента тече подаване.\n"
-"\n"
-"Файлът „%s“ е променен.\n"
-"\n"
-"Трябва да завършите текущото подаване, преди да започнете сливане. Така ще "
-"можете лесно да преустановите сливането, ако възникне нужда.\n"
+msgid "Pushing changes to %s"
+msgstr "Изтласкване на промените към „%s“"
-#: lib/merge.tcl:108
+#: lib/transport.tcl:93
#, tcl-format
-msgid "%s of %s"
-msgstr "%s от общо %s"
+msgid "Mirroring to %s"
+msgstr "Изтласкване на всичко към „%s“"
-#: lib/merge.tcl:122
+#: lib/transport.tcl:111
#, tcl-format
-msgid "Merging %s and %s..."
-msgstr "Сливане на „%s“ и „%s“…"
-
-#: lib/merge.tcl:133
-msgid "Merge completed successfully."
-msgstr "Сливането завърши успешно."
-
-#: lib/merge.tcl:135
-msgid "Merge failed. Conflict resolution is required."
-msgstr "Неуспешно сливане — има конфликти за коригиране."
+msgid "Pushing %s %s to %s"
+msgstr "Изтласкване на %s „%s“ към „%s“"
-#: lib/merge.tcl:160
-#, tcl-format
-msgid "Merge Into %s"
-msgstr "Сливане в „%s“"
+#: lib/transport.tcl:132
+msgid "Push Branches"
+msgstr "Клони за изтласкване"
-#: lib/merge.tcl:179
-msgid "Revision To Merge"
-msgstr "Ð\92еÑ\80Ñ\81иÑ\8f за Ñ\81ливане"
+#: lib/transport.tcl:147
+msgid "Source Branches"
+msgstr "Ð\9aлони-изÑ\82оÑ\87ниÑ\86и"
-#: lib/merge.tcl:214
-msgid ""
-"Cannot abort while amending.\n"
-"\n"
-"You must finish amending this commit.\n"
-msgstr ""
-"Поправянето не може да бъде преустановено.\n"
-"\n"
-"Трябва да завършите поправката на това подаване.\n"
+#: lib/transport.tcl:162
+msgid "Destination Repository"
+msgstr "Целево хранилище"
-#: lib/merge.tcl:224
-msgid ""
-"Abort merge?\n"
-"\n"
-"Aborting the current merge will cause *ALL* uncommitted changes to be lost.\n"
-"\n"
-"Continue with aborting the current merge?"
-msgstr ""
-"Да се преустанови ли сливането?\n"
-"\n"
-"В такъв случай ●ВСИЧКИ● неподадени промени ще бъдат безвъзвратно загубени.\n"
-"\n"
-"Наистина ли да се преустанови сливането?"
+#: lib/transport.tcl:205
+msgid "Transfer Options"
+msgstr "Настройки при пренасянето"
-#: lib/merge.tcl:230
-msgid ""
-"Reset changes?\n"
-"\n"
-"Resetting the changes will cause *ALL* uncommitted changes to be lost.\n"
-"\n"
-"Continue with resetting the current changes?"
+#: lib/transport.tcl:207
+msgid "Force overwrite existing branch (may discard changes)"
msgstr ""
-"Да се занулят ли промените?\n"
-"\n"
-"В такъв случай ●ВСИЧКИ● неподадени промени ще бъдат безвъзвратно загубени.\n"
-"\n"
-"Наистина ли да се занулят промените?"
-
-#: lib/merge.tcl:241
-msgid "Aborting"
-msgstr "Преустановяване"
+"Изрично презаписване на съществуващ клон (някои промени може да бъдат "
+"загубени)"
-#: lib/merge.tcl:241
-msgid "files reset"
-msgstr "файла със занулени промени"
+#: lib/transport.tcl:211
+msgid "Use thin pack (for slow network connections)"
+msgstr "Максимална компресия (за бавни мрежови връзки)"
-#: lib/merge.tcl:269
-msgid "Abort failed."
-msgstr "Ð\9dеÑ\83Ñ\81пеÑ\88но пÑ\80еÑ\83Ñ\81Ñ\82ановÑ\8fване."
+#: lib/transport.tcl:215
+msgid "Include tags"
+msgstr "Ð\92клÑ\8eÑ\87ване на еÑ\82икеÑ\82иÑ\82е"
-#: lib/merge.tcl:271
-msgid "Abort completed. Ready."
-msgstr "Успешно преустановяване. Готовност за следващо действие."
+#: lib/transport.tcl:229
+#, tcl-format
+msgid "%s (%s): Push"
+msgstr "%s (%s): Изтласкване"
# Translation of git-gui to russian
# Copyright (C) 2007 Shawn Pearce
# This file is distributed under the same license as the git-gui package.
-# Irina Riesen <irina.riesen@gmail.com>, 2007.
-#
+# Translators:
+# Dimitriy Ryazantcev <DJm00n@mail.ru>, 2015-2016
+# Irina Riesen <irina.riesen@gmail.com>, 2007
msgid ""
msgstr ""
-"Project-Id-Version: git-gui\n"
+"Project-Id-Version: Git Russian Localization Project\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-01-26 15:47-0800\n"
-"PO-Revision-Date: 2007-10-22 22:30-0200\n"
-"Last-Translator: Alex Riesen <raa.lkml@gmail.com>\n"
-"Language-Team: Russian Translation <git@vger.kernel.org>\n"
+"PO-Revision-Date: 2016-06-30 12:39+0000\n"
+"Last-Translator: Dimitriy Ryazantcev <DJm00n@mail.ru>\n"
+"Language-Team: Russian (http://www.transifex.com/djm00n/git-po-ru/language/ru/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Language: ru\n"
+"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
#: git-gui.sh:41 git-gui.sh:793 git-gui.sh:807 git-gui.sh:820 git-gui.sh:903
#: git-gui.sh:922
"%s requires at least Git 1.5.0 or later.\n"
"\n"
"Assume '%s' is version 1.5.0?\n"
-msgstr ""
-"Невозможно определить версию Git\n"
-"\n"
-"%s указывает на версию '%s'.\n"
-"\n"
-"для %s требуется версия Git, начиная с 1.5.0\n"
-"\n"
-"Принять '%s' как версию 1.5.0?\n"
+msgstr "Невозможно определить версию Git\n\n%s указывает на версию «%s».\n\nдля %s требуется версия Git, начиная с 1.5.0\n\nПредположить, что «%s» и есть версия 1.5.0?\n"
#: git-gui.sh:1128
msgid "Git directory not found:"
#: git-gui.sh:1334 lib/checkout_op.tcl:306
msgid "Refreshing file status..."
-msgstr "Обновление информации о состоянии файлов..."
+msgstr "Обновление информации о состоянии файлов…"
#: git-gui.sh:1390
msgid "Scanning for modified files ..."
-msgstr "Поиск измененных файлов..."
+msgstr "Поиск измененных файлов…"
#: git-gui.sh:1454
msgid "Calling prepare-commit-msg hook..."
-msgstr "Вызов программы поддержки репозитория prepare-commit-msg..."
+msgstr "Вызов перехватчика prepare-commit-msg…"
#: git-gui.sh:1471
msgid "Commit declined by prepare-commit-msg hook."
-msgstr ""
-"Сохранение прервано программой поддержки репозитория prepare-commit-msg"
+msgstr "Коммит прерван перехватчиком prepare-commit-msg."
#: git-gui.sh:1629 lib/browser.tcl:246
msgid "Ready."
#: git-gui.sh:1915
msgid "Modified, not staged"
-msgstr "Ð\98зменено, не подгоÑ\82овлено"
+msgstr "Ð\98зменено, не в индекÑ\81е"
#: git-gui.sh:1916 git-gui.sh:1924
msgid "Staged for commit"
-msgstr "Ð\9fодгоÑ\82овлено длÑ\8f Ñ\81оÑ\85Ñ\80анениÑ\8f"
+msgstr "Ð\92 индекÑ\81е длÑ\8f коммиÑ\82а"
#: git-gui.sh:1917 git-gui.sh:1925
msgid "Portions staged for commit"
-msgstr "ЧаÑ\81Ñ\82и, подгоÑ\82овленнÑ\8bе длÑ\8f Ñ\81оÑ\85Ñ\80анениÑ\8f"
+msgstr "ЧаÑ\81Ñ\82и, в индекÑ\81е длÑ\8f коммиÑ\82а"
#: git-gui.sh:1918 git-gui.sh:1926
msgid "Staged for commit, missing"
-msgstr "Ð\9fодгоÑ\82овлено длÑ\8f Ñ\81оÑ\85Ñ\80анениÑ\8f, отсутствует"
+msgstr "Ð\92 индекÑ\81е длÑ\8f коммиÑ\82а, отсутствует"
#: git-gui.sh:1920
msgid "File type changed, not staged"
-msgstr "Тип Ñ\84айла изменÑ\91н, не подгоÑ\82овлено"
+msgstr "Тип Ñ\84айла изменÑ\91н, не в индекÑ\81е"
#: git-gui.sh:1921
msgid "File type changed, staged"
-msgstr "Тип Ñ\84айла изменÑ\91н, подгоÑ\82овлено"
+msgstr "Тип Ñ\84айла изменÑ\91н, в индекÑ\81е"
#: git-gui.sh:1923
msgid "Untracked, not staged"
-msgstr "Ð\9dе оÑ\82Ñ\81леживаеÑ\82Ñ\81Ñ\8f, не подгоÑ\82овлено"
+msgstr "Ð\9dе оÑ\82Ñ\81леживаеÑ\82Ñ\81Ñ\8f, не в индекÑ\81е"
#: git-gui.sh:1928
msgid "Missing"
#: git-gui.sh:1929
msgid "Staged for removal"
-msgstr "Ð\9fодгоÑ\82овлено для удаления"
+msgstr "Ð\92 индекÑ\81е для удаления"
#: git-gui.sh:1930
msgid "Staged for removal, still present"
-msgstr "Ð\9fодгоÑ\82овлено для удаления, еще не удалено"
+msgstr "Ð\92 индекÑ\81е для удаления, еще не удалено"
#: git-gui.sh:1932 git-gui.sh:1933 git-gui.sh:1934 git-gui.sh:1935
#: git-gui.sh:1936 git-gui.sh:1937
#: git-gui.sh:1972
msgid "Starting gitk... please wait..."
-msgstr "Запускается gitk... Подождите, пожалуйста..."
+msgstr "Запускается gitk… Подождите, пожалуйста…"
#: git-gui.sh:1984
msgid "Couldn't find gitk in PATH"
#: git-gui.sh:2458 lib/choose_rev.tcl:561
msgid "Branch"
-msgstr "Ð\92еÑ\82вÑ\8c"
+msgstr "Ð\92еÑ\82ка"
#: git-gui.sh:2461 lib/choose_rev.tcl:548
msgid "Commit@@noun"
-msgstr "СоÑ\81Ñ\82оÑ\8fние"
+msgstr "Ð\9aоммиÑ\82"
#: git-gui.sh:2464 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168
msgid "Merge"
#: git-gui.sh:2483
msgid "Browse Current Branch's Files"
-msgstr "Ð\9fÑ\80оÑ\81моÑ\82Ñ\80еÑ\82Ñ\8c Ñ\84айлÑ\8b Ñ\82екÑ\83Ñ\89ей веÑ\82ви"
+msgstr "Ð\9fÑ\80оÑ\81моÑ\82Ñ\80еÑ\82Ñ\8c Ñ\84айлÑ\8b Ñ\82екÑ\83Ñ\89ей веÑ\82ки"
#: git-gui.sh:2487
msgid "Browse Branch Files..."
-msgstr "Ð\9fоказаÑ\82Ñ\8c Ñ\84айлÑ\8b веÑ\82ви..."
+msgstr "Ð\9fоказаÑ\82Ñ\8c Ñ\84айлÑ\8b веÑ\82киâ\80¦"
#: git-gui.sh:2492
msgid "Visualize Current Branch's History"
-msgstr "Ð\9fоказаÑ\82Ñ\8c иÑ\81Ñ\82оÑ\80иÑ\8e Ñ\82екÑ\83Ñ\89ей веÑ\82ви"
+msgstr "Ð\9fоказаÑ\82Ñ\8c иÑ\81Ñ\82оÑ\80иÑ\8e Ñ\82екÑ\83Ñ\89ей веÑ\82ки"
#: git-gui.sh:2496
msgid "Visualize All Branch History"
-msgstr "Ð\9fоказаÑ\82Ñ\8c иÑ\81Ñ\82оÑ\80иÑ\8e вÑ\81еÑ\85 веÑ\82вей"
+msgstr "Ð\9fоказаÑ\82Ñ\8c иÑ\81Ñ\82оÑ\80иÑ\8e вÑ\81еÑ\85 веÑ\82ок"
#: git-gui.sh:2503
#, tcl-format
msgid "Browse %s's Files"
-msgstr "Ð\9fоказаÑ\82Ñ\8c Ñ\84айлÑ\8b веÑ\82ви %s"
+msgstr "Ð\9fоказаÑ\82Ñ\8c Ñ\84айлÑ\8b веÑ\82ки %s"
#: git-gui.sh:2505
#, tcl-format
msgid "Visualize %s's History"
-msgstr "Ð\9fоказаÑ\82Ñ\8c иÑ\81Ñ\82оÑ\80иÑ\8e веÑ\82ви %s"
+msgstr "Ð\9fоказаÑ\82Ñ\8c иÑ\81Ñ\82оÑ\80иÑ\8e веÑ\82ки %s"
#: git-gui.sh:2510 lib/database.tcl:27 lib/database.tcl:67
msgid "Database Statistics"
#: git-gui.sh:2576
msgid "Create..."
-msgstr "Создать..."
+msgstr "Создать…"
#: git-gui.sh:2582
msgid "Checkout..."
-msgstr "Перейти..."
+msgstr "Перейти…"
#: git-gui.sh:2588
msgid "Rename..."
-msgstr "Переименовать..."
+msgstr "Переименовать…"
#: git-gui.sh:2593
msgid "Delete..."
-msgstr "Удалить..."
+msgstr "Удалить…"
#: git-gui.sh:2598
msgid "Reset..."
-msgstr "Сбросить..."
+msgstr "Сбросить…"
#: git-gui.sh:2608
msgid "Done"
#: git-gui.sh:2610
msgid "Commit@@verb"
-msgstr "СоÑ\85Ñ\80анить"
+msgstr "Ð\97акоммиÑ\82ить"
#: git-gui.sh:2619 git-gui.sh:3050
msgid "New Commit"
-msgstr "Новое состояние"
+msgstr "Новый коммит"
#: git-gui.sh:2627 git-gui.sh:3057
msgid "Amend Last Commit"
-msgstr "Ð\98Ñ\81пÑ\80авиÑ\82Ñ\8c поÑ\81леднее Ñ\81оÑ\81Ñ\82оÑ\8fние"
+msgstr "Ð\98Ñ\81пÑ\80авиÑ\82Ñ\8c поÑ\81ледний коммиÑ\82"
#: git-gui.sh:2637 git-gui.sh:3011 lib/remote_branch_delete.tcl:99
msgid "Rescan"
#: git-gui.sh:2643
msgid "Stage To Commit"
-msgstr "Ð\9fодгоÑ\82овиÑ\82Ñ\8c длÑ\8f Ñ\81оÑ\85Ñ\80анениÑ\8f"
+msgstr "Ð\94обавиÑ\82Ñ\8c в индекÑ\81"
#: git-gui.sh:2649
msgid "Stage Changed Files To Commit"
-msgstr "Ð\9fодгоÑ\82овиÑ\82Ñ\8c измененнÑ\8bе Ñ\84айлÑ\8b длÑ\8f Ñ\81оÑ\85Ñ\80анениÑ\8f"
+msgstr "Ð\94обавиÑ\82Ñ\8c изменÑ\91ннÑ\8bе Ñ\84айлÑ\8b в индекÑ\81"
#: git-gui.sh:2655
msgid "Unstage From Commit"
-msgstr "УбÑ\80аÑ\82Ñ\8c из подгоÑ\82овленного"
+msgstr "УбÑ\80аÑ\82Ñ\8c из издекÑ\81а"
#: git-gui.sh:2661 lib/index.tcl:412
msgid "Revert Changes"
-msgstr "Отменить изменения"
+msgstr "Обратить изменения"
#: git-gui.sh:2669 git-gui.sh:3310 git-gui.sh:3341
msgid "Show Less Context"
#: git-gui.sh:2696
msgid "Local Merge..."
-msgstr "Локальное слияние..."
+msgstr "Локальное слияние…"
#: git-gui.sh:2701
msgid "Abort Merge..."
-msgstr "Прервать слияние..."
+msgstr "Прервать слияние…"
#: git-gui.sh:2713 git-gui.sh:2741
msgid "Add..."
-msgstr "Добавить..."
+msgstr "Добавить…"
#: git-gui.sh:2717
msgid "Push..."
-msgstr "Отправить..."
+msgstr "Отправить…"
#: git-gui.sh:2721
msgid "Delete Branch..."
-msgstr "УдалиÑ\82Ñ\8c веÑ\82вÑ\8c..."
+msgstr "УдалиÑ\82Ñ\8c веÑ\82кÑ\83â\80¦"
#: git-gui.sh:2731 git-gui.sh:3292
msgid "Options..."
-msgstr "Настройки..."
+msgstr "Настройки…"
#: git-gui.sh:2742
msgid "Remove..."
-msgstr "Удалить..."
+msgstr "Удалить…"
#: git-gui.sh:2751 lib/choose_repository.tcl:50
msgid "Help"
#: git-gui.sh:2926
msgid "Current Branch:"
-msgstr "ТекÑ\83Ñ\89аÑ\8f веÑ\82вÑ\8c:"
+msgstr "ТекÑ\83Ñ\89аÑ\8f веÑ\82ка:"
#: git-gui.sh:2947
msgid "Staged Changes (Will Commit)"
-msgstr "Ð\9fодгоÑ\82овлено (бÑ\83деÑ\82 Ñ\81оÑ\85Ñ\80анено)"
+msgstr "Ð\98зменениÑ\8f в индекÑ\81е (бÑ\83дÑ\83Ñ\82 закоммиÑ\87енÑ\8b)"
#: git-gui.sh:2967
msgid "Unstaged Changes"
#: git-gui.sh:3017
msgid "Stage Changed"
-msgstr "Ð\9fодгоÑ\82овиÑ\82Ñ\8c вÑ\81е"
+msgstr "Ð\98ндекÑ\81иÑ\80оваÑ\82Ñ\8c вÑ\81Ñ\91"
#: git-gui.sh:3036 lib/transport.tcl:104 lib/transport.tcl:193
msgid "Push"
#: git-gui.sh:3071
msgid "Initial Commit Message:"
-msgstr "Ð\9aомменÑ\82аÑ\80ий к пеÑ\80вомÑ\83 Ñ\81оÑ\81Ñ\82оÑ\8fниÑ\8e:"
+msgstr "СообÑ\89ение пеÑ\80вого коммиÑ\82а:"
#: git-gui.sh:3072
msgid "Amended Commit Message:"
-msgstr "Ð\9aомменÑ\82аÑ\80ий к иÑ\81пÑ\80авленномÑ\83 Ñ\81оÑ\81Ñ\82оÑ\8fниÑ\8e:"
+msgstr "СообÑ\89ение иÑ\81пÑ\80авленного коммиÑ\82а:"
#: git-gui.sh:3073
msgid "Amended Initial Commit Message:"
-msgstr "Ð\9aомменÑ\82аÑ\80ий к иÑ\81пÑ\80авленномÑ\83 пеÑ\80вонаÑ\87алÑ\8cномÑ\83 Ñ\81оÑ\81Ñ\82оÑ\8fниÑ\8e:"
+msgstr "СообÑ\89ение иÑ\81пÑ\80авленного пеÑ\80вого коммиÑ\82а:"
#: git-gui.sh:3074
msgid "Amended Merge Commit Message:"
-msgstr "Ð\9aомменÑ\82аÑ\80ий к иÑ\81пÑ\80авленномÑ\83 Ñ\81лиÑ\8fниÑ\8e:"
+msgstr "СообÑ\89ение иÑ\81пÑ\80авленного Ñ\81лиÑ\8fниÑ\8f:"
#: git-gui.sh:3075
msgid "Merge Commit Message:"
-msgstr "Ð\9aомменÑ\82аÑ\80ий к Ñ\81лиÑ\8fниÑ\8e:"
+msgstr "СообÑ\89ение Ñ\81лиÑ\8fниÑ\8f:"
#: git-gui.sh:3076
msgid "Commit Message:"
-msgstr "Ð\9aомменÑ\82аÑ\80ий к Ñ\81оÑ\81Ñ\82оÑ\8fниÑ\8e:"
+msgstr "СообÑ\89ение коммиÑ\82а:"
#: git-gui.sh:3125 git-gui.sh:3267 lib/console.tcl:73
msgid "Copy All"
#: git-gui.sh:3336
msgid "Revert To Base"
-msgstr "Отменить изменения"
+msgstr "Обратить изменения"
#: git-gui.sh:3354
msgid "Visualize These Changes In The Submodule"
-msgstr ""
+msgstr "Показать эти изменения подмодуля"
#: git-gui.sh:3358
msgid "Visualize Current Branch History In The Submodule"
-msgstr "Ð\9fоказаÑ\82Ñ\8c иÑ\81Ñ\82оÑ\80иÑ\8e Ñ\82екÑ\83Ñ\89ей веÑ\82ви подмодуля"
+msgstr "Ð\9fоказаÑ\82Ñ\8c иÑ\81Ñ\82оÑ\80иÑ\8e Ñ\82екÑ\83Ñ\89ей веÑ\82ки подмодуля"
#: git-gui.sh:3362
msgid "Visualize All Branch History In The Submodule"
-msgstr "Ð\9fоказаÑ\82Ñ\8c иÑ\81Ñ\82оÑ\80иÑ\8e вÑ\81еÑ\85 веÑ\82вей подмодуля"
+msgstr "Ð\9fоказаÑ\82Ñ\8c иÑ\81Ñ\82оÑ\80иÑ\8e вÑ\81еÑ\85 веÑ\82ок подмодуля"
#: git-gui.sh:3367
msgid "Start git gui In The Submodule"
-msgstr ""
+msgstr "Запустить git gui в подмодуле"
#: git-gui.sh:3389
msgid "Unstage Hunk From Commit"
-msgstr "Ð\9dе Ñ\81оÑ\85Ñ\80анÑ\8fÑ\82Ñ\8c Ñ\87аÑ\81Ñ\82Ñ\8c"
+msgstr "УбÑ\80аÑ\82Ñ\8c блок из индекÑ\81а"
#: git-gui.sh:3391
msgid "Unstage Lines From Commit"
-msgstr "УбÑ\80аÑ\82Ñ\8c Ñ\81Ñ\82Ñ\80оки из подгоÑ\82овленного"
+msgstr "УбÑ\80аÑ\82Ñ\8c Ñ\81Ñ\82Ñ\80оки из индекÑ\81а"
#: git-gui.sh:3393
msgid "Unstage Line From Commit"
-msgstr "УбÑ\80аÑ\82Ñ\8c Ñ\81Ñ\82Ñ\80окÑ\83 из подгоÑ\82овленного"
+msgstr "УбÑ\80аÑ\82Ñ\8c Ñ\81Ñ\82Ñ\80окÑ\83 из индекÑ\81а"
#: git-gui.sh:3396
msgid "Stage Hunk For Commit"
-msgstr "Ð\9fодгоÑ\82овиÑ\82Ñ\8c Ñ\87аÑ\81Ñ\82Ñ\8c длÑ\8f Ñ\81оÑ\85Ñ\80анениÑ\8f"
+msgstr "Ð\94обавиÑ\82Ñ\8c блок в индекÑ\81"
#: git-gui.sh:3398
msgid "Stage Lines For Commit"
-msgstr "Ð\9fодгоÑ\82овиÑ\82Ñ\8c Ñ\81Ñ\82Ñ\80оки длÑ\8f Ñ\81оÑ\85Ñ\80анениÑ\8f"
+msgstr "Ð\94обавиÑ\82Ñ\8c Ñ\81Ñ\82Ñ\80оки в индекÑ\81"
#: git-gui.sh:3400
msgid "Stage Line For Commit"
-msgstr "Ð\9fодгоÑ\82овиÑ\82Ñ\8c Ñ\81Ñ\82Ñ\80окÑ\83 длÑ\8f Ñ\81оÑ\85Ñ\80анениÑ\8f"
+msgstr "Ð\94обавиÑ\82Ñ\8c Ñ\81Ñ\82Ñ\80окÑ\83 в индекÑ\81"
#: git-gui.sh:3424
msgid "Initializing..."
-msgstr "Инициализация..."
+msgstr "Инициализация…"
#: git-gui.sh:3541
#, tcl-format
"going to be ignored by any Git subprocess run\n"
"by %s:\n"
"\n"
-msgstr ""
-"Возможны ошибки в переменных окружения.\n"
-"\n"
-"Переменные окружения, которые возможно\n"
-"будут проигнорированы командами Git,\n"
-"запущенными из %s\n"
-"\n"
+msgstr "Возможны ошибки в переменных окружения.\n\nПеременные окружения, которые возможно\nбудут проигнорированы командами Git,\nзапущенными из %s\n\n"
#: git-gui.sh:3570
msgid ""
"\n"
"This is due to a known issue with the\n"
"Tcl binary distributed by Cygwin."
-msgstr ""
-"\n"
-"Это известная проблема с Tcl,\n"
-"распространяемым Cygwin."
+msgstr "\nЭто известная проблема с Tcl,\nраспространяемым Cygwin."
#: git-gui.sh:3575
#, tcl-format
"is placing values for the user.name and\n"
"user.email settings into your personal\n"
"~/.gitconfig file.\n"
-msgstr ""
-"\n"
-"\n"
-"Вместо использования %s можно\n"
-"сохранить значения user.name и\n"
-"user.email в Вашем персональном\n"
-"файле ~/.gitconfig.\n"
+msgstr "\n\nВместо использования %s можно\nсохранить значения user.name и\nuser.email в Вашем персональном\nфайле ~/.gitconfig.\n"
#: lib/about.tcl:26
msgid "git-gui - a graphical user interface for Git."
#: lib/blame.tcl:78
msgid "Commit:"
-msgstr "СоÑ\85Ñ\80аненное Ñ\81оÑ\81Ñ\82оÑ\8fние:"
+msgstr "Ð\9aоммиÑ\82:"
#: lib/blame.tcl:271
msgid "Copy Commit"
-msgstr "Скопировать SHA-1"
+msgstr "Ð\9aопировать SHA-1"
#: lib/blame.tcl:275
msgid "Find Text..."
-msgstr "Найти текст..."
+msgstr "Найти текст…"
#: lib/blame.tcl:284
msgid "Do Full Copy Detection"
#: lib/blame.tcl:291
msgid "Blame Parent Commit"
-msgstr "РаÑ\81Ñ\81моÑ\82Ñ\80еÑ\82Ñ\8c Ñ\81оÑ\81Ñ\82оÑ\8fние пÑ\80едка"
+msgstr "Ð\90вÑ\82оÑ\80Ñ\8b Ñ\80одиÑ\82елÑ\8cÑ\81кого коммиÑ\82а"
#: lib/blame.tcl:450
#, tcl-format
msgid "Reading %s..."
-msgstr "Чтение %s..."
+msgstr "Чтение %s…"
#: lib/blame.tcl:557
msgid "Loading copy/move tracking annotations..."
-msgstr "Загрузка аннотации копирований/переименований..."
+msgstr "Загрузка аннотации копирований/переименований…"
#: lib/blame.tcl:577
msgid "lines annotated"
#: lib/blame.tcl:769
msgid "Loading original location annotations..."
-msgstr "Загрузка аннотаций первоначального положения объекта..."
+msgstr "Загрузка аннотаций первоначального положения объекта…"
#: lib/blame.tcl:772
msgid "Annotation complete."
#: lib/blame.tcl:842
msgid "Running thorough copy detection..."
-msgstr "Выполнение полного поиска копий..."
+msgstr "Выполнение полного поиска копий…"
#: lib/blame.tcl:910
msgid "Loading annotation..."
-msgstr "Загрузка аннотации..."
+msgstr "Загрузка аннотации…"
#: lib/blame.tcl:963
msgid "Author:"
#: lib/blame.tcl:967
msgid "Committer:"
-msgstr "СоÑ\85Ñ\80анил:"
+msgstr "Ð\9aоммиÑ\82еÑ\80:"
#: lib/blame.tcl:972
msgid "Original File:"
#: lib/blame.tcl:1020
msgid "Cannot find HEAD commit:"
-msgstr "Невозможно найти текущее состояние:"
+msgstr "Не удалось найти текущее состояние:"
#: lib/blame.tcl:1075
msgid "Cannot find parent commit:"
-msgstr "Невозможно найти состояние предка:"
+msgstr "Не удалось найти родительское состояние:"
#: lib/blame.tcl:1090
msgid "Unable to display parent"
#: lib/branch_checkout.tcl:14 lib/branch_checkout.tcl:19
msgid "Checkout Branch"
-msgstr "Ð\9fеÑ\80ейÑ\82и на веÑ\82вÑ\8c"
+msgstr "Ð\9fеÑ\80ейÑ\82и на веÑ\82кÑ\83"
#: lib/branch_checkout.tcl:23
msgid "Checkout"
#: lib/branch_checkout.tcl:39 lib/branch_create.tcl:92
msgid "Fetch Tracking Branch"
-msgstr "Ð\9fолÑ\83Ñ\87иÑ\82Ñ\8c изменениÑ\8f из внеÑ\88ней веÑ\82ви"
+msgstr "Ð\98звлеÑ\87Ñ\8c изменениÑ\8f из внеÑ\88ней веÑ\82ки"
#: lib/branch_checkout.tcl:44
msgid "Detach From Local Branch"
-msgstr "Ð\9eÑ\82Ñ\81оединиÑ\82Ñ\8c оÑ\82 локалÑ\8cной веÑ\82ви"
+msgstr "Ð\9eÑ\82Ñ\81оединиÑ\82Ñ\8c оÑ\82 локалÑ\8cной веÑ\82ки"
#: lib/branch_create.tcl:22
msgid "Create Branch"
-msgstr "Создание ветви"
+msgstr "Создать ветку"
#: lib/branch_create.tcl:27
msgid "Create New Branch"
-msgstr "СоздаÑ\82Ñ\8c новÑ\83Ñ\8e веÑ\82вÑ\8c"
+msgstr "СоздаÑ\82Ñ\8c новÑ\83Ñ\8e веÑ\82кÑ\83"
#: lib/branch_create.tcl:31 lib/choose_repository.tcl:381
msgid "Create"
#: lib/branch_create.tcl:40
msgid "Branch Name"
-msgstr "Ð\9dазвание веÑ\82ви"
+msgstr "Ð\98мÑ\8f веÑ\82ки"
#: lib/branch_create.tcl:43 lib/remote_add.tcl:39 lib/tools_dlg.tcl:50
msgid "Name:"
#: lib/branch_create.tcl:58
msgid "Match Tracking Branch Name"
-msgstr "Ð\92зÑ\8fÑ\82Ñ\8c из имен веÑ\82вей Ñ\81лежениÑ\8f"
+msgstr "СооÑ\82веÑ\82Ñ\81Ñ\82воваÑ\82Ñ\8c имени оÑ\82Ñ\81леживаемой веÑ\82ки"
#: lib/branch_create.tcl:66
msgid "Starting Revision"
#: lib/branch_create.tcl:72
msgid "Update Existing Branch:"
-msgstr "Ð\9eбновиÑ\82Ñ\8c имеÑ\8eÑ\89Ñ\83Ñ\8eÑ\81Ñ\8f веÑ\82вÑ\8c:"
+msgstr "Ð\9eбновиÑ\82Ñ\8c имеÑ\8eÑ\89Ñ\83Ñ\8eÑ\81Ñ\8f веÑ\82кÑ\83:"
#: lib/branch_create.tcl:75
msgid "No"
#: lib/branch_create.tcl:131
msgid "Please select a tracking branch."
-msgstr "УкажиÑ\82е веÑ\82вÑ\8c Ñ\81лежениÑ\8f."
+msgstr "УкажиÑ\82е оÑ\82леживаемÑ\83Ñ\8e веÑ\82кÑ\83."
#: lib/branch_create.tcl:140
#, tcl-format
msgid "Tracking branch %s is not a branch in the remote repository."
-msgstr "Ð\92еÑ\82вÑ\8c Ñ\81лежениÑ\8f %s не Ñ\8fвлÑ\8fеÑ\82Ñ\81Ñ\8f веÑ\82вÑ\8cÑ\8e во внешнем репозитории."
+msgstr "Ð\9eÑ\82Ñ\81леживаемаÑ\8f веÑ\82ка %s не Ñ\8fвлÑ\8fеÑ\82Ñ\81Ñ\8f веÑ\82кой на внешнем репозитории."
#: lib/branch_create.tcl:153 lib/branch_rename.tcl:86
msgid "Please supply a branch name."
-msgstr "УкажиÑ\82е название веÑ\82ви."
+msgstr "УкажиÑ\82е имÑ\8f веÑ\82ки."
#: lib/branch_create.tcl:164 lib/branch_rename.tcl:106
#, tcl-format
msgid "'%s' is not an acceptable branch name."
-msgstr "Ð\9dедопÑ\83Ñ\81Ñ\82имое название веÑ\82ви '%s'."
+msgstr "Ð\9dедопÑ\83Ñ\81Ñ\82имое имÑ\8f веÑ\82ки «%s»."
#: lib/branch_delete.tcl:15
msgid "Delete Branch"
-msgstr "Удаление веÑ\82ви"
+msgstr "Удаление веÑ\82ки"
#: lib/branch_delete.tcl:20
msgid "Delete Local Branch"
-msgstr "УдалиÑ\82Ñ\8c локалÑ\8cнÑ\83Ñ\8e веÑ\82вÑ\8c"
+msgstr "УдалиÑ\82Ñ\8c локалÑ\8cнÑ\83Ñ\8e веÑ\82кÑ\83"
#: lib/branch_delete.tcl:37
msgid "Local Branches"
-msgstr "Ð\9bокалÑ\8cнÑ\8bе веÑ\82ви"
+msgstr "Ð\9bокалÑ\8cнÑ\8bе веÑ\82ки"
#: lib/branch_delete.tcl:52
msgid "Delete Only If Merged Into"
#: lib/branch_delete.tcl:103
#, tcl-format
msgid "The following branches are not completely merged into %s:"
-msgstr "Ð\92еÑ\82ви, которые не полностью сливаются с %s:"
+msgstr "Ð\92еÑ\82ки, которые не полностью сливаются с %s:"
#: lib/branch_delete.tcl:115 lib/remote_branch_delete.tcl:217
msgid ""
"Recovering deleted branches is difficult.\n"
"\n"
"Delete the selected branches?"
-msgstr ""
-"Восстановить удаленные ветви сложно.\n"
-"\n"
-"Продолжить?"
+msgstr "Восстановить удаленные ветки сложно.\n\nПродолжить?"
#: lib/branch_delete.tcl:141
#, tcl-format
msgid ""
"Failed to delete branches:\n"
"%s"
-msgstr ""
-"Не удалось удалить ветви:\n"
-"%s"
+msgstr "Не удалось удалить ветки:\n%s"
#: lib/branch_rename.tcl:14 lib/branch_rename.tcl:22
msgid "Rename Branch"
-msgstr "Ð\9fеÑ\80еименование веÑ\82ви"
+msgstr "Ð\9fеÑ\80еименование веÑ\82ки"
#: lib/branch_rename.tcl:26
msgid "Rename"
#: lib/branch_rename.tcl:36
msgid "Branch:"
-msgstr "Ð\92еÑ\82вÑ\8c:"
+msgstr "Ð\92еÑ\82ка:"
#: lib/branch_rename.tcl:39
msgid "New Name:"
#: lib/branch_rename.tcl:75
msgid "Please select a branch to rename."
-msgstr "УкажиÑ\82е веÑ\82вÑ\8c для переименования."
+msgstr "УкажиÑ\82е веÑ\82кÑ\83 для переименования."
#: lib/branch_rename.tcl:96 lib/checkout_op.tcl:202
#, tcl-format
msgid "Branch '%s' already exists."
-msgstr "Ð\92еÑ\82вÑ\8c '%s' уже существует."
+msgstr "Ð\92еÑ\82ка «%s» уже существует."
#: lib/branch_rename.tcl:117
#, tcl-format
msgid "Failed to rename '%s'."
-msgstr "Не удалось переименовать '%s'. "
+msgstr "Не удалось переименовать «%s». "
#: lib/browser.tcl:17
msgid "Starting..."
-msgstr "Запуск..."
+msgstr "Запуск…"
#: lib/browser.tcl:26
msgid "File Browser"
#: lib/browser.tcl:126 lib/browser.tcl:143
#, tcl-format
msgid "Loading %s..."
-msgstr "Загрузка %s..."
+msgstr "Загрузка %s…"
#: lib/browser.tcl:187
msgid "[Up To Parent]"
#: lib/browser.tcl:267 lib/browser.tcl:273
msgid "Browse Branch Files"
-msgstr "Ð\9fоказаÑ\82Ñ\8c Ñ\84айлÑ\8b веÑ\82ви"
+msgstr "Ð\9fоказаÑ\82Ñ\8c Ñ\84айлÑ\8b веÑ\82ки"
#: lib/browser.tcl:278 lib/choose_repository.tcl:398
#: lib/choose_repository.tcl:486 lib/choose_repository.tcl:497
#: lib/checkout_op.tcl:85
#, tcl-format
msgid "Fetching %s from %s"
-msgstr "Ð\9fолÑ\83чение %s из %s "
+msgstr "Ð\98звлечение %s из %s "
#: lib/checkout_op.tcl:133
#, tcl-format
#: lib/checkout_op.tcl:175
#, tcl-format
msgid "Branch '%s' does not exist."
-msgstr "Ð\92еÑ\82вÑ\8c '%s' не Ñ\81Ñ\83Ñ\89еÑ\81Ñ\82вÑ\83еÑ\82 "
+msgstr "Ð\92еÑ\82ка «%s» не Ñ\81Ñ\83Ñ\89еÑ\81Ñ\82вÑ\83еÑ\82."
#: lib/checkout_op.tcl:194
#, tcl-format
msgid "Failed to configure simplified git-pull for '%s'."
-msgstr "Ошибка создания упрощённой конфигурации git pull для '%s'."
+msgstr "Ошибка создания упрощённой конфигурации git pull для «%s»."
#: lib/checkout_op.tcl:229
#, tcl-format
"\n"
"It cannot fast-forward to %s.\n"
"A merge is required."
-msgstr ""
-"Ветвь '%s' уже существует.\n"
-"\n"
-"Она не может быть прокручена(fast-forward) к %s.\n"
-"Требуется слияние."
+msgstr "Ветка «%s» уже существует.\n\nОна не может быть перемотана вперед к %s.\nТребуется слияние."
#: lib/checkout_op.tcl:243
#, tcl-format
msgid "Merge strategy '%s' not supported."
-msgstr "Неизвестная стратегия слияния: '%s'."
+msgstr "Неизвестная стратегия слияния «%s»."
#: lib/checkout_op.tcl:262
#, tcl-format
msgid "Failed to update '%s'."
-msgstr "Не удалось обновить '%s'."
+msgstr "Не удалось обновить «%s»."
#: lib/checkout_op.tcl:274
msgid "Staging area (index) is already locked."
msgid ""
"Last scanned state does not match repository state.\n"
"\n"
-"Another Git program has modified this repository since the last scan. A "
-"rescan must be performed before the current branch can be changed.\n"
+"Another Git program has modified this repository since the last scan. A rescan must be performed before the current branch can be changed.\n"
"\n"
"The rescan will be automatically started now.\n"
-msgstr ""
-"Последнее прочитанное состояние репозитория не соответствует текущему.\n"
-"\n"
-"С момента последней проверки репозиторий был изменен другой программой Git. "
-"Необходимо перечитать репозиторий, прежде чем изменять текущую ветвь.\n"
-"\n"
-"Это будет сделано сейчас автоматически.\n"
+msgstr "Последнее прочитанное состояние репозитория не соответствует текущему.\n\nС момента последней проверки репозиторий был изменен другой программой Git. Необходимо перечитать репозиторий, прежде чем текущая ветка может быть изменена.\n\nЭто будет сделано сейчас автоматически.\n"
#: lib/checkout_op.tcl:345
#, tcl-format
msgid "Updating working directory to '%s'..."
-msgstr "Обновление рабочего каталога из '%s'..."
+msgstr "Обновление рабочего каталога из «%s»…"
#: lib/checkout_op.tcl:346
msgid "files checked out"
#: lib/checkout_op.tcl:376
#, tcl-format
msgid "Aborted checkout of '%s' (file level merging is required)."
-msgstr "Прерван переход на '%s' (требуется слияние содержания файлов)"
+msgstr "Прерван переход на «%s» (требуется слияние содержимого файлов)"
#: lib/checkout_op.tcl:377
msgid "File level merge required."
#: lib/checkout_op.tcl:381
#, tcl-format
msgid "Staying on branch '%s'."
-msgstr "Ð\92еÑ\82вÑ\8c '%s' оÑ\81Ñ\82ается текущей."
+msgstr "Ð\92еÑ\82ка «%s» оÑ\81Ñ\82аÑ\91тся текущей."
#: lib/checkout_op.tcl:452
msgid ""
"You are no longer on a local branch.\n"
"\n"
-"If you wanted to be on a branch, create one now starting from 'This Detached "
-"Checkout'."
-msgstr ""
-"Вы находитесь не в локальной ветви.\n"
-"\n"
-"Если вы хотите снова вернуться к какой-нибудь ветви, создайте ее сейчас, "
-"начиная с 'Текущего отсоединенного состояния'."
+"If you wanted to be on a branch, create one now starting from 'This Detached Checkout'."
+msgstr "Вы более не находитесь на локальной ветке.\n\nЕсли вы хотите снова вернуться к какой-нибудь ветке, создайте её сейчас, начиная с «Текущего отсоединенного состояния»."
#: lib/checkout_op.tcl:503 lib/checkout_op.tcl:507
#, tcl-format
msgid "Checked out '%s'."
-msgstr "Ветвь '%s' сделана текущей."
+msgstr "Выполнен переход на «%s»."
#: lib/checkout_op.tcl:535
#, tcl-format
msgid "Resetting '%s' to '%s' will lose the following commits:"
-msgstr "Сброс '%s' в '%s' приведет к потере следующих сохраненных состояний: "
+msgstr "Сброс «%s» на «%s» приведет к потере следующих коммитов:"
#: lib/checkout_op.tcl:557
msgid "Recovering lost commits may not be easy."
-msgstr "Восстановить потерянные сохраненные состояния будет сложно."
+msgstr "Восстановить потерянные коммиты будет сложно."
#: lib/checkout_op.tcl:562
#, tcl-format
msgid "Reset '%s'?"
-msgstr "Сбросить '%s'?"
+msgstr "Сбросить «%s»?"
#: lib/checkout_op.tcl:567 lib/merge.tcl:164 lib/tools_dlg.tcl:343
msgid "Visualize"
msgid ""
"Failed to set current branch.\n"
"\n"
-"This working directory is only partially switched. We successfully updated "
-"your files, but failed to update an internal Git file.\n"
+"This working directory is only partially switched. We successfully updated your files, but failed to update an internal Git file.\n"
"\n"
"This should not have occurred. %s will now close and give up."
-msgstr ""
-"Не удалось установить текущую ветвь.\n"
-"\n"
-"Ваш рабочий каталог обновлен только частично. Были обновлены все файлы кроме "
-"служебных файлов Git. \n"
-"\n"
-"Этого не должно было произойти. %s завершается."
+msgstr "Не удалось установить текущую ветку.\n\nВаш рабочий каталог обновлён только частично. Были обновлены все файлы кроме служебных файлов Git. \n\nЭтого не должно было произойти. %s завершается."
#: lib/choose_font.tcl:39
msgid "Select"
msgid ""
"This is example text.\n"
"If you like this text, it can be your font."
-msgstr ""
-"Это пример текста.\n"
-"Если Вам нравится этот текст, это может быть Ваш шрифт."
+msgstr "Это пример текста.\nЕсли Вам нравится этот текст, это может быть Ваш шрифт."
#: lib/choose_repository.tcl:28
msgid "Git Gui"
#: lib/choose_repository.tcl:93
msgid "New..."
-msgstr "Новый..."
+msgstr "Новый…"
#: lib/choose_repository.tcl:100 lib/choose_repository.tcl:471
msgid "Clone Existing Repository"
#: lib/choose_repository.tcl:106
msgid "Clone..."
-msgstr "СклониÑ\80оваÑ\82Ñ\8c..."
+msgstr "Ð\9aлониÑ\80оваÑ\82Ñ\8câ\80¦"
#: lib/choose_repository.tcl:113 lib/choose_repository.tcl:1016
msgid "Open Existing Repository"
#: lib/choose_repository.tcl:119
msgid "Open..."
-msgstr "Открыть..."
+msgstr "Открыть…"
#: lib/choose_repository.tcl:132
msgid "Recent Repositories"
#: lib/choose_repository.tcl:508
msgid "Standard (Fast, Semi-Redundant, Hardlinks)"
-msgstr "Стандартный (Быстрый, полуизбыточный, \"жесткие\" ссылки)"
+msgstr "Стандартный (Быстрый, полуизбыточный, «жесткие» ссылки)"
#: lib/choose_repository.tcl:514
msgid "Full Copy (Slower, Redundant Backup)"
#: lib/choose_repository.tcl:641
msgid "buckets"
-msgstr ""
+msgstr "блоки"
#: lib/choose_repository.tcl:665
#, tcl-format
#: lib/choose_repository.tcl:703 lib/choose_repository.tcl:917
#: lib/choose_repository.tcl:929
msgid "The 'master' branch has not been initialized."
-msgstr "Не инициализирована ветвь 'master'."
+msgstr "Не инициализирована ветвь «master»."
#: lib/choose_repository.tcl:716
msgid "Hardlinks are unavailable. Falling back to copying."
-msgstr "\"Жесткие ссылки\" недоступны. Будет использовано копирование."
+msgstr "«Жесткие ссылки» недоступны. Будет использовано копирование."
#: lib/choose_repository.tcl:728
#, tcl-format
#: lib/choose_repository.tcl:803
#, tcl-format
msgid "Unable to hardlink object: %s"
-msgstr "Не могу \"жестко связать\" объект: %s"
+msgstr "Не могу создать «жесткую ссылку» на объект: %s"
#: lib/choose_repository.tcl:858
msgid "Cannot fetch branches and objects. See console output for details."
-msgstr ""
-"Не могу получить ветви и объекты. Дополнительная информация на консоли."
+msgstr "Не удалось извлечь ветки и объекты. Дополнительная информация на консоли."
#: lib/choose_repository.tcl:869
msgid "Cannot fetch tags. See console output for details."
-msgstr "Не могу получить метки. Дополнительная информация на консоли."
+msgstr "Не удалось извлечь метки. Дополнительная информация на консоли."
#: lib/choose_repository.tcl:893
msgid "Cannot determine HEAD. See console output for details."
#: lib/choose_repository.tcl:915
msgid "No default branch obtained."
-msgstr "Ð\9dе бÑ\8bло полÑ\83Ñ\87ено веÑ\82ви по Ñ\83молÑ\87аниÑ\8e."
+msgstr "Ð\92еÑ\82ка по Ñ\83молÑ\87аниÑ\8e не бÑ\8bла полÑ\83Ñ\87ена."
#: lib/choose_repository.tcl:926
#, tcl-format
msgid "Cannot resolve %s as a commit."
-msgstr "Не могу распознать %s как состояние."
+msgstr "Не могу распознать %s как коммит."
#: lib/choose_repository.tcl:938
msgid "Creating working directory"
#: lib/choose_rev.tcl:74
msgid "Local Branch"
-msgstr "Ð\9bокалÑ\8cнаÑ\8f веÑ\82вÑ\8c:"
+msgstr "Ð\9bокалÑ\8cнаÑ\8f веÑ\82ка:"
#: lib/choose_rev.tcl:79
msgid "Tracking Branch"
-msgstr "Ð\92еÑ\82вÑ\8c Ñ\81лежениÑ\8f"
+msgstr "Ð\9eÑ\82Ñ\81леживаемаÑ\8f веÑ\82ка"
#: lib/choose_rev.tcl:84 lib/choose_rev.tcl:538
msgid "Tag"
msgid ""
"There is nothing to amend.\n"
"\n"
-"You are about to create the initial commit. There is no commit before this "
-"to amend.\n"
-msgstr ""
-"Отсутствует состояние для исправления.\n"
-"\n"
-"Вы создаете первое состояние в репозитории, здесь еще нечего исправлять.\n"
+"You are about to create the initial commit. There is no commit before this to amend.\n"
+msgstr "Отсутствует коммиты для исправления.\n\nВы создаете начальный коммит, здесь еще нечего исправлять.\n"
#: lib/commit.tcl:18
msgid ""
"Cannot amend while merging.\n"
"\n"
-"You are currently in the middle of a merge that has not been fully "
-"completed. You cannot amend the prior commit unless you first abort the "
-"current merge activity.\n"
-msgstr ""
-"Невозможно исправить состояние во время операции слияния.\n"
-"\n"
-"Текущее слияние не завершено. Невозможно исправить предыдущее сохраненное "
-"состояние, не прерывая эту операцию.\n"
+"You are currently in the middle of a merge that has not been fully completed. You cannot amend the prior commit unless you first abort the current merge activity.\n"
+msgstr "Невозможно исправить коммит во время слияния.\n\nТекущее слияние не завершено. Невозможно исправить предыдуий коммит, не прерывая эту операцию.\n"
#: lib/commit.tcl:48
msgid "Error loading commit data for amend:"
-msgstr "Ошибка при загрузке данных для исправления сохраненного состояния:"
+msgstr "Ошибка при загрузке данных для исправления коммита:"
#: lib/commit.tcl:75
msgid "Unable to obtain your identity:"
#: lib/commit.tcl:80
msgid "Invalid GIT_COMMITTER_IDENT:"
-msgstr "Ð\9dевеÑ\80ный GIT_COMMITTER_IDENT:"
+msgstr "Ð\9dедопÑ\83Ñ\81Ñ\82имый GIT_COMMITTER_IDENT:"
#: lib/commit.tcl:129
#, tcl-format
msgid "warning: Tcl does not support encoding '%s'."
-msgstr "предупреждение: Tcl не поддерживает кодировку '%s'."
+msgstr "предупреждение: Tcl не поддерживает кодировку «%s»."
#: lib/commit.tcl:149
msgid ""
"Last scanned state does not match repository state.\n"
"\n"
-"Another Git program has modified this repository since the last scan. A "
-"rescan must be performed before another commit can be created.\n"
+"Another Git program has modified this repository since the last scan. A rescan must be performed before another commit can be created.\n"
"\n"
"The rescan will be automatically started now.\n"
-msgstr ""
-"Последнее прочитанное состояние репозитория не соответствует текущему.\n"
-"\n"
-"С момента последней проверки репозиторий был изменен другой программой Git. "
-"Необходимо перечитать репозиторий, прежде чем изменять текущую ветвь. \n"
-"\n"
-"Это будет сделано сейчас автоматически.\n"
+msgstr "Последнее прочитанное состояние репозитория не соответствует текущему.\n\nС момента последней проверки репозиторий был изменен другой программой Git. Необходимо перечитать репозиторий, прежде чем изменять текущую ветвь. \n\nЭто будет сделано сейчас автоматически.\n"
#: lib/commit.tcl:172
#, tcl-format
msgid ""
"Unmerged files cannot be committed.\n"
"\n"
-"File %s has merge conflicts. You must resolve them and stage the file "
-"before committing.\n"
-msgstr ""
-"Нельзя сохранить файлы с незавершённой операцией слияния.\n"
-"\n"
-"Для файла %s возник конфликт слияния. Разрешите конфликт и добавьте к "
-"подготовленным файлам перед сохранением.\n"
+"File %s has merge conflicts. You must resolve them and stage the file before committing.\n"
+msgstr "Нельзя выполнить коммит с незавершённой операцией слияния.\n\nДля файла %s возник конфликт слияния. Разрешите конфликт и добавьте их в индекс перед выполнением коммита.\n"
#: lib/commit.tcl:180
#, tcl-format
"Unknown file state %s detected.\n"
"\n"
"File %s cannot be committed by this program.\n"
-msgstr ""
-"Обнаружено неизвестное состояние файла %s.\n"
-"\n"
-"Файл %s не может быть сохранен данной программой.\n"
+msgstr "Обнаружено неизвестное состояние файла %s.\n\nФайл %s не может быть закоммичен этой программой.\n"
#: lib/commit.tcl:188
msgid ""
"No changes to commit.\n"
"\n"
"You must stage at least 1 file before you can commit.\n"
-msgstr ""
-"Отсутствуют изменения для сохранения.\n"
-"\n"
-"Подготовьте хотя бы один файл до создания сохраненного состояния.\n"
+msgstr "Отсутствуют изменения для сохранения.\n\nДобавьте в индекс хотя бы один файл перед выполнением коммита.\n"
#: lib/commit.tcl:203
msgid ""
"- First line: Describe in one sentence what you did.\n"
"- Second line: Blank\n"
"- Remaining lines: Describe why this change is good.\n"
-msgstr ""
-"Напишите комментарий к сохраненному состоянию.\n"
-"\n"
-"Рекомендуется следующий формат комментария:\n"
-"\n"
-"- первая строка: краткое описание сделанных изменений.\n"
-"- вторая строка пустая\n"
-"- оставшиеся строки: опишите, что дают ваши изменения.\n"
+msgstr "Укажите сообщение коммита.\n\nРекомендуется следующий формат сообщения:\n\n- в первой строке краткое описание сделанных изменений\n- вторая строка пустая\n- в оставшихся строках опишите, что дают ваши изменения\n"
#: lib/commit.tcl:234
msgid "Calling pre-commit hook..."
-msgstr "Вызов программы поддержки репозитория pre-commit..."
+msgstr "Вызов перехватчика pre-commit…"
#: lib/commit.tcl:249
msgid "Commit declined by pre-commit hook."
-msgstr "СоÑ\85Ñ\80анение пÑ\80еÑ\80вано пÑ\80огÑ\80аммой поддеÑ\80жки Ñ\80епозиÑ\82оÑ\80иÑ\8f pre-commit"
+msgstr "Ð\9aоммиÑ\82 пÑ\80еÑ\80ван пеÑ\80еваÑ\82Ñ\87иком pre-commit."
#: lib/commit.tcl:272
msgid "Calling commit-msg hook..."
-msgstr "Вызов программы поддержки репозитория commit-msg..."
+msgstr "Вызов перехватчика commit-msg…"
#: lib/commit.tcl:287
msgid "Commit declined by commit-msg hook."
-msgstr "СоÑ\85Ñ\80анение пÑ\80еÑ\80вано пÑ\80огÑ\80аммой поддеÑ\80жки Ñ\80епозиÑ\82оÑ\80иÑ\8f commit-msg"
+msgstr "Ð\9aоммиÑ\82 пÑ\80еÑ\80ван пеÑ\80еваÑ\82Ñ\87иком commit-msg"
#: lib/commit.tcl:300
msgid "Committing changes..."
-msgstr "СоÑ\85Ñ\80анение изменений..."
+msgstr "Ð\9aоммиÑ\82 измененийâ\80¦"
#: lib/commit.tcl:316
msgid "write-tree failed:"
#: lib/commit.tcl:317 lib/commit.tcl:361 lib/commit.tcl:382
msgid "Commit failed."
-msgstr "СоÑ\85Ñ\80аниÑ\82Ñ\8c Ñ\81оÑ\81Ñ\82оÑ\8fние не Ñ\83далоÑ\81Ñ\8c."
+msgstr "Ð\9dе Ñ\83далоÑ\81Ñ\8c закоммиÑ\82иÑ\82Ñ\8c изменениÑ\8f."
#: lib/commit.tcl:334
#, tcl-format
msgid "Commit %s appears to be corrupt"
-msgstr "СоÑ\81Ñ\82оÑ\8fние %s вÑ\8bглÑ\8fдиÑ\82 повÑ\80ежденнÑ\8bм"
+msgstr "Ð\9aоммиÑ\82 %s поÑ\85оже повÑ\80ежден"
#: lib/commit.tcl:339
msgid ""
"No files were modified by this commit and it was not a merge commit.\n"
"\n"
"A rescan will be automatically started now.\n"
-msgstr ""
-"Отсутствуют изменения для сохранения.\n"
-"\n"
-"Ни один файл не был изменен и не было слияния.\n"
-"\n"
-"Сейчас автоматически запустится перечитывание репозитория.\n"
+msgstr "Нет изменения для коммита.\n\nНи один файл не был изменен и не было слияния.\n\nСейчас автоматически запустится перечитывание репозитория.\n"
#: lib/commit.tcl:346
msgid "No changes to commit."
-msgstr "Ð\9eÑ\82Ñ\81Ñ\83Ñ\82Ñ\81Ñ\82вÑ\83Ñ\8eÑ\82 изменениÑ\8f длÑ\8f Ñ\81оÑ\85Ñ\80анениÑ\8f."
+msgstr "Ð\9dеÑ\82 изменениÑ\8f длÑ\8f коммиÑ\82а."
#: lib/commit.tcl:360
msgid "commit-tree failed:"
#: lib/commit.tcl:469
#, tcl-format
msgid "Created commit %s: %s"
-msgstr "Создано состояние %s: %s "
+msgstr "Создан коммит %s: %s "
#: lib/console.tcl:59
msgid "Working... please wait..."
-msgstr "В процессе... пожалуйста, ждите..."
+msgstr "В процессе… пожалуйста, ждите…"
#: lib/console.tcl:186
msgid "Success"
msgid ""
"This repository currently has approximately %i loose objects.\n"
"\n"
-"To maintain optimal performance it is strongly recommended that you compress "
-"the database.\n"
+"To maintain optimal performance it is strongly recommended that you compress the database.\n"
"\n"
"Compress the database now?"
-msgstr ""
-"Этот репозиторий сейчас содержит примерно %i свободных объектов\n"
-"\n"
-"Для лучшей производительности рекомендуется сжать базу данных.\n"
-"\n"
-"Сжать базу данных сейчас?"
+msgstr "Этот репозиторий сейчас содержит примерно %i свободных объектов\n\nДля лучшей производительности рекомендуется сжать базу данных.\n\nСжать базу данных сейчас?"
#: lib/date.tcl:25
#, tcl-format
"\n"
"%s has no changes.\n"
"\n"
-"The modification date of this file was updated by another application, but "
-"the content within the file was not changed.\n"
-"\n"
-"A rescan will be automatically started to find other files which may have "
-"the same state."
-msgstr ""
-"Изменений не обнаружено.\n"
+"The modification date of this file was updated by another application, but the content within the file was not changed.\n"
"\n"
-"в %s отсутствуют изменения.\n"
-"\n"
-"Дата изменения файла была обновлена другой программой, но содержимое файла "
-"осталось прежним.\n"
-"\n"
-"Сейчас будет запущено перечитывание репозитория, чтобы найти подобные файлы."
+"A rescan will be automatically started to find other files which may have the same state."
+msgstr "Изменений не обнаружено.\n\nв %s отсутствуют изменения.\n\nДата изменения файла была обновлена другой программой, но содержимое файла осталось прежним.\n\nСейчас будет запущено перечитывание репозитория, чтобы найти подобные файлы."
#: lib/diff.tcl:104
#, tcl-format
msgid "Loading diff of %s..."
-msgstr "Загрузка изменений в %s..."
+msgstr "Загрузка изменений %s…"
#: lib/diff.tcl:125
msgid ""
"LOCAL: deleted\n"
"REMOTE:\n"
-msgstr ""
-"ЛОКАЛЬНО: удалён\n"
-"ВНЕШНИЙ:\n"
+msgstr "ЛОКАЛЬНО: удалён\nВНЕШНИЙ:\n"
#: lib/diff.tcl:130
msgid ""
"REMOTE: deleted\n"
"LOCAL:\n"
-msgstr ""
-"ВНЕШНИЙ: удалён\n"
-"ЛОКАЛЬНО:\n"
+msgstr "ВНЕШНИЙ: удалён\nЛОКАЛЬНО:\n"
#: lib/diff.tcl:137
msgid "LOCAL:\n"
msgid ""
"* Untracked file is %d bytes.\n"
"* Showing only first %d bytes.\n"
-msgstr ""
-"* Размер неподготовленного файла %d байт.\n"
-"* Показано первых %d байт.\n"
+msgstr "* Размер неотслеживаемого файла %d байт.\n* Показано первых %d байт.\n"
#: lib/diff.tcl:233
#, tcl-format
"\n"
"* Untracked file clipped here by %s.\n"
"* To see the entire file, use an external editor.\n"
-msgstr ""
-"\n"
-"* Неподготовленный файл обрезан: %s.\n"
-"* Чтобы увидеть весь файл, используйте программу-редактор.\n"
+msgstr "\n* Неотслеживаемый файл обрезан: %s.\n* Чтобы увидеть весь файл, используйте внешний редактор.\n"
#: lib/diff.tcl:482
msgid "Failed to unstage selected hunk."
#: lib/diff.tcl:489
msgid "Failed to stage selected hunk."
-msgstr "Не удалось подготовить к сохранению выбранную часть."
+msgstr "Не удалось проиндексировать выбранный блок изменений."
#: lib/diff.tcl:568
msgid "Failed to unstage selected line."
#: lib/diff.tcl:576
msgid "Failed to stage selected line."
-msgstr "Не удалось подготовить к сохранению выбранную строку."
+msgstr "Не удалось проиндексировать выбранную строку."
#: lib/encoding.tcl:443
msgid "Default"
#: lib/error.tcl:94
msgid "You must correct the above errors before committing."
-msgstr "Прежде чем сохранить, исправьте вышеуказанные ошибки."
+msgstr "Перед коммитом, исправьте вышеуказанные ошибки."
#: lib/index.tcl:6
msgid "Unable to unlock the index."
msgid ""
"Updating the Git index failed. A rescan will be automatically started to "
"resynchronize git-gui."
-msgstr ""
-"Не удалось обновить индекс Git. Состояние репозитория будет перечитано "
-"автоматически."
+msgstr "Не удалось обновить индекс Git. Состояние репозитория будет перечитано автоматически."
#: lib/index.tcl:28
msgid "Continue"
#: lib/index.tcl:289
#, tcl-format
msgid "Unstaging %s from commit"
-msgstr "Удаление %s из подгоÑ\82овленного"
+msgstr "Удаление %s из индекÑ\81а"
#: lib/index.tcl:328
msgid "Ready to commit."
-msgstr "Ð\9fодгоÑ\82овлено длÑ\8f Ñ\81оÑ\85Ñ\80анениÑ\8f"
+msgstr "Ð\93оÑ\82ов длÑ\8f коммиÑ\82а."
#: lib/index.tcl:341
#, tcl-format
msgid "Adding %s"
-msgstr "Добавление %s..."
+msgstr "Добавление %s…"
#: lib/index.tcl:398
#, tcl-format
msgid "Revert changes in file %s?"
-msgstr "Отменить изменения в файле %s?"
+msgstr "Обратить изменения в файле %s?"
#: lib/index.tcl:400
#, tcl-format
msgid "Revert changes in these %i files?"
-msgstr "Отменить изменения в %i файле(-ах)?"
+msgstr "Обратить изменения в %i файле(-ах)?"
#: lib/index.tcl:408
msgid "Any unstaged changes will be permanently lost by the revert."
-msgstr ""
-"Любые изменения, не подготовленные к сохранению, будут потеряны при данной "
-"операции."
+msgstr "Любые непроиндексированные изменения, будут потеряны при обращении изменений."
#: lib/index.tcl:411
msgid "Do Nothing"
#: lib/index.tcl:429
msgid "Reverting selected files"
-msgstr "Удаление изменений в выбранных файлах"
+msgstr "Ð\9eбÑ\80аÑ\89ение изменений в выбранных файлах"
#: lib/index.tcl:433
#, tcl-format
msgid "Reverting %s"
-msgstr "Отмена изменений в %s"
+msgstr "Обращение изменений в %s"
#: lib/merge.tcl:13
msgid ""
"Cannot merge while amending.\n"
"\n"
"You must finish amending this commit before starting any type of merge.\n"
-msgstr ""
-"Невозможно выполнить слияние во время исправления.\n"
-"\n"
-"Завершите исправление данного состояния перед выполнением операции слияния.\n"
+msgstr "Невозможно выполнить слияние во время исправления.\n\nЗавершите исправление данного коммита перед выполнением операции слияния.\n"
#: lib/merge.tcl:27
msgid ""
"Last scanned state does not match repository state.\n"
"\n"
-"Another Git program has modified this repository since the last scan. A "
-"rescan must be performed before a merge can be performed.\n"
+"Another Git program has modified this repository since the last scan. A rescan must be performed before a merge can be performed.\n"
"\n"
"The rescan will be automatically started now.\n"
-msgstr ""
-"Последнее прочитанное состояние репозитория не соответствует текущему.\n"
-"\n"
-"С момента последней проверки репозиторий был изменен другой программой Git. "
-"Необходимо перечитать репозиторий, прежде чем изменять текущую ветвь.\n"
-"\n"
-"Это будет сделано сейчас автоматически.\n"
+msgstr "Последнее прочитанное состояние репозитория не соответствует текущему.\n\nС момента последней проверки репозиторий был изменен другой программой Git. Необходимо перечитать репозиторий, прежде чем слияние может быть сделано.\n\nЭто будет сделано сейчас автоматически.\n"
#: lib/merge.tcl:45
#, tcl-format
"\n"
"File %s has merge conflicts.\n"
"\n"
-"You must resolve them, stage the file, and commit to complete the current "
-"merge. Only then can you begin another merge.\n"
-msgstr ""
-"Предыдущее слияние не завершено из-за конфликта.\n"
-"\n"
-"Для файла %s возник конфликт слияния.\n"
-"\n"
-"Разрешите конфликт, подготовьте файл и сохраните. Только после этого можно "
-"начать следующее слияние.\n"
+"You must resolve them, stage the file, and commit to complete the current merge. Only then can you begin another merge.\n"
+msgstr "Предыдущее слияние не завершено из-за конфликта.\n\nДля файла %s возник конфликт слияния.\n\nРазрешите конфликт, добавьте файл в индекс и закоммитьте. Только после этого можно начать следующее слияние.\n"
#: lib/merge.tcl:55
#, tcl-format
"\n"
"File %s is modified.\n"
"\n"
-"You should complete the current commit before starting a merge. Doing so "
-"will help you abort a failed merge, should the need arise.\n"
-msgstr ""
-"Изменения не сохранены.\n"
-"\n"
-"Файл %s изменен.\n"
-"\n"
-"Подготовьте и сохраните изменения перед началом слияния. В случае "
-"необходимости это позволит прервать операцию слияния.\n"
+"You should complete the current commit before starting a merge. Doing so will help you abort a failed merge, should the need arise.\n"
+msgstr "Вы находитесь в процессе изменений.\n\nФайл %s изменён.\n\nВы должны завершить текущий коммит перед началом слияния. В случае необходимости, это позволит прервать операцию слияния.\n"
#: lib/merge.tcl:107
#, tcl-format
#: lib/merge.tcl:120
#, tcl-format
msgid "Merging %s and %s..."
-msgstr "Слияние %s и %s..."
+msgstr "Слияние %s и %s…"
#: lib/merge.tcl:131
msgid "Merge completed successfully."
"Cannot abort while amending.\n"
"\n"
"You must finish amending this commit.\n"
-msgstr ""
-"Невозможно прервать исправление.\n"
-"\n"
-"Завершите текущее исправление сохраненного состояния.\n"
+msgstr "Невозможно прервать исправление.\n\nЗавершите текущее исправление коммита.\n"
#: lib/merge.tcl:222
msgid ""
"Aborting the current merge will cause *ALL* uncommitted changes to be lost.\n"
"\n"
"Continue with aborting the current merge?"
-msgstr ""
-"Прервать операцию слияния?\n"
-"\n"
-"Прерывание этой операции приведет к потере *ВСЕХ* несохраненных изменений.\n"
-"\n"
-"Продолжить?"
+msgstr "Прервать операцию слияния?\n\nПрерывание текущего слияния приведет к потере *ВСЕХ* несохраненных изменений.\n\nПродолжить?"
#: lib/merge.tcl:228
msgid ""
"Resetting the changes will cause *ALL* uncommitted changes to be lost.\n"
"\n"
"Continue with resetting the current changes?"
-msgstr ""
-"Прервать операцию слияния?\n"
-"\n"
-"Прерывание этой операции приведет к потере *ВСЕХ* несохраненных изменений.\n"
-"\n"
-"Продолжить?"
+msgstr "Сбросить изменения?\n\nСброс изменений приведет к потере *ВСЕХ* несохраненных изменений.\n\nПродолжить?"
#: lib/merge.tcl:239
msgid "Aborting"
#: lib/mergetool.tcl:9
msgid "Force resolution to this branch?"
-msgstr "Использовать версию этой ветви для разрешения конфликта?"
+msgstr "Использовать версию из этой ветки для разрешения конфликта?"
#: lib/mergetool.tcl:10
msgid "Force resolution to the other branch?"
-msgstr "Ð\98Ñ\81полÑ\8cзоваÑ\82Ñ\8c веÑ\80Ñ\81иÑ\8e дÑ\80Ñ\83гой веÑ\82ви для разрешения конфликта?"
+msgstr "Ð\98Ñ\81полÑ\8cзоваÑ\82Ñ\8c веÑ\80Ñ\81иÑ\8e из дÑ\80Ñ\83гой веÑ\82ки для разрешения конфликта?"
#: lib/mergetool.tcl:14
#, tcl-format
"%s will be overwritten.\n"
"\n"
"This operation can be undone only by restarting the merge."
-msgstr ""
-"Внимание! Список изменений показывает только конфликтующие отличия.\n"
-"\n"
-"%s будет переписан.\n"
-"\n"
-"Это действие можно отменить только перезапуском операции слияния."
+msgstr "Внимание! Список изменений показывает только конфликтующие отличия.\n\n%s будет переписан.\n\nЭто действие можно отменить только перезапуском операции слияния."
#: lib/mergetool.tcl:45
#, tcl-format
msgid "File %s seems to have unresolved conflicts, still stage?"
-msgstr ""
-"Файл %s, похоже, содержит необработанные конфликты. Продолжить подготовку к "
-"сохранению?"
+msgstr "Похоже, что файл %s содержит неразрешенные конфликты. Продолжить индексацию?"
#: lib/mergetool.tcl:60
#, tcl-format
#: lib/mergetool.tcl:141
msgid "Cannot resolve deletion or link conflicts using a tool"
-msgstr ""
-"Программа слияния не обрабатывает конфликты с удалением или участием ссылок"
+msgstr "Программа слияния не обрабатывает конфликты с удалением или участием ссылок"
#: lib/mergetool.tcl:146
msgid "Conflict file does not exist"
#: lib/mergetool.tcl:264
#, tcl-format
msgid "Not a GUI merge tool: '%s'"
-msgstr "'%s' не является программой слияния"
+msgstr "«%s» не является программой слияния"
#: lib/mergetool.tcl:268
#, tcl-format
msgid "Unsupported merge tool '%s'"
-msgstr "Ð\9dеизвеÑ\81Ñ\82наÑ\8f пÑ\80огÑ\80амма Ñ\81лиÑ\8fниÑ\8f '%s'"
+msgstr "Ð\9dеподдеÑ\80живаемаÑ\8f пÑ\80огÑ\80амма Ñ\81лиÑ\8fниÑ\8f «%s»"
#: lib/mergetool.tcl:303
msgid "Merge tool is already running, terminate it?"
msgid ""
"Error retrieving versions:\n"
"%s"
-msgstr ""
-"Ошибка получения версий:\n"
-"%s"
+msgstr "Ошибка получения версий:\n%s"
#: lib/mergetool.tcl:343
#, tcl-format
"Could not start the merge tool:\n"
"\n"
"%s"
-msgstr ""
-"Ошибка запуска программы слияния:\n"
-"\n"
-"%s"
+msgstr "Ошибка запуска программы слияния:\n\n%s"
#: lib/mergetool.tcl:347
msgid "Running merge tool..."
-msgstr "Запуск программы слияния..."
+msgstr "Запуск программы слияния…"
#: lib/mergetool.tcl:375 lib/mergetool.tcl:383
msgid "Merge tool failed."
#: lib/option.tcl:11
#, tcl-format
msgid "Invalid global encoding '%s'"
-msgstr "Ð\9eÑ\88ибка в глобалÑ\8cной Ñ\83Ñ\81Ñ\82ановке кодиÑ\80овки '%s'"
+msgstr "Ð\9dевеÑ\80наÑ\8f глобалÑ\8cнаÑ\8f кодиÑ\80овка «%s»"
#: lib/option.tcl:19
#, tcl-format
msgid "Invalid repo encoding '%s'"
-msgstr "Неверная кодировка репозитория: '%s'"
+msgstr "Неверная кодировка репозитория «%s»"
#: lib/option.tcl:117
msgid "Restore Defaults"
#: lib/option.tcl:141
msgid "Summarize Merge Commits"
-msgstr "Суммарный комментарий при слиянии"
+msgstr "Суммарное сообщение при слиянии"
#: lib/option.tcl:142
msgid "Merge Verbosity"
#: lib/option.tcl:147
msgid "Prune Tracking Branches During Fetch"
-msgstr "ЧиÑ\81Ñ\82ка веÑ\82вей Ñ\81лежениÑ\8f пÑ\80и полÑ\83чении изменений"
+msgstr "ЧиÑ\81Ñ\82ка оÑ\82Ñ\81леживаемÑ\8bÑ\85 веÑ\82ок пÑ\80и извлечении изменений"
#: lib/option.tcl:148
msgid "Match Tracking Branches"
-msgstr "Ð\98мÑ\8f новой веÑ\82ви взÑ\8fÑ\82Ñ\8c из имен веÑ\82вей Ñ\81лежениÑ\8f"
+msgstr "Такое же имÑ\8f, как и Ñ\83 оÑ\82Ñ\81леживаемой веÑ\82ки"
#: lib/option.tcl:149
msgid "Blame Copy Only On Changed Files"
#: lib/option.tcl:153
msgid "Commit Message Text Width"
-msgstr "Ширина текста комментария"
+msgstr "Ширина текста сообщения коммита"
#: lib/option.tcl:154
msgid "New Branch Name Template"
-msgstr "Шаблон длÑ\8f имени новой веÑ\82ви"
+msgstr "Шаблон длÑ\8f имени новой веÑ\82ки"
#: lib/option.tcl:155
msgid "Default File Contents Encoding"
msgid "Choose %s"
msgstr "Выберите %s"
-# carbon copy
#: lib/option.tcl:264
msgid "pt."
msgstr "pt."
#: lib/remote.tcl:173
msgid "Fetch from"
-msgstr "Ð\9fолÑ\83чение из"
+msgstr "Ð\98звлечение из"
#: lib/remote.tcl:215
msgid "Push to"
#: lib/remote_add.tcl:28 lib/tools_dlg.tcl:36
msgid "Add"
-msgstr ""
+msgstr "Добавить"
#: lib/remote_add.tcl:37
msgid "Remote Details"
#: lib/remote_add.tcl:65
msgid "Fetch Immediately"
-msgstr "Скачать сразу"
+msgstr "Сразу извлечь изменения"
#: lib/remote_add.tcl:71
msgid "Initialize Remote Repository and Push"
#: lib/remote_add.tcl:114
#, tcl-format
msgid "'%s' is not an acceptable remote name."
-msgstr "Недопустимое название внешнего репозитория '%s'."
+msgstr "«%s» не является допустимым именем внешнего репозитория."
#: lib/remote_add.tcl:125
#, tcl-format
msgid "Failed to add remote '%s' of location '%s'."
-msgstr "Не удалось добавить '%s' из '%s'. "
+msgstr "Не удалось добавить «%s» из «%s». "
#: lib/remote_add.tcl:133 lib/transport.tcl:6
#, tcl-format
msgid "fetch %s"
-msgstr "полÑ\83чение %s"
+msgstr "извлечение %s"
#: lib/remote_add.tcl:134
#, tcl-format
msgid "Fetching the %s"
-msgstr "Ð\9fолÑ\83чение %s"
+msgstr "Ð\98звлечение %s"
#: lib/remote_add.tcl:157
#, tcl-format
msgid "Do not know how to initialize repository at location '%s'."
-msgstr "Невозможно инициализировать репозиторий в '%s'."
+msgstr "Невозможно инициализировать репозиторий в «%s»."
#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:63
#: lib/transport.tcl:81
#: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
msgid "Delete Branch Remotely"
-msgstr "Удаление веÑ\82ви во внешнем репозитории"
+msgstr "Удаление веÑ\82ки во внешнем репозитории"
#: lib/remote_branch_delete.tcl:47
msgid "From Repository"
#: lib/remote_branch_delete.tcl:84
msgid "Branches"
-msgstr "Ð\92еÑ\82ви"
+msgstr "Ð\92еÑ\82ки"
#: lib/remote_branch_delete.tcl:109
msgid "Delete Only If"
#: lib/remote_branch_delete.tcl:152
msgid "A branch is required for 'Merged Into'."
-msgstr "Для опции 'Слияние с' требуется указать ветвь."
+msgstr "Для операции «Слияние с» требуется указать ветку."
#: lib/remote_branch_delete.tcl:184
#, tcl-format
"The following branches are not completely merged into %s:\n"
"\n"
" - %s"
-msgstr ""
-"Следующие ветви могут быть объединены с %s при помощи операции слияния:\n"
-"\n"
-" - %s"
+msgstr "Следующие ветки могут быть объединены с %s при помощи операции слияния:\n\n - %s"
#: lib/remote_branch_delete.tcl:189
#, tcl-format
msgid ""
"One or more of the merge tests failed because you have not fetched the "
"necessary commits. Try fetching from %s first."
-msgstr ""
-"Некоторые тесты на слияние не прошли, потому что Вы не получили необходимые "
-"состояния. Попытайтесь получить их из %s."
+msgstr "Некоторые тесты на слияние не прошли, потому что вы не извлекли необходимые коммиты. Попытайтесь извлечь их из %s."
#: lib/remote_branch_delete.tcl:207
msgid "Please select one or more branches to delete."
-msgstr "УкажиÑ\82е однÑ\83 или неÑ\81колÑ\8cко веÑ\82вей для удаления."
+msgstr "УкажиÑ\82е однÑ\83 или неÑ\81колÑ\8cко веÑ\82ок для удаления."
#: lib/remote_branch_delete.tcl:226
#, tcl-format
msgid "Deleting branches from %s"
-msgstr "Удаление веÑ\82вей из %s"
+msgstr "Удаление веÑ\82ок из %s"
#: lib/remote_branch_delete.tcl:292
msgid "No repository selected."
#: lib/remote_branch_delete.tcl:297
#, tcl-format
msgid "Scanning %s..."
-msgstr "Перечитывание %s... "
+msgstr "Перечитывание %s…"
#: lib/search.tcl:21
msgid "Find:"
#: lib/sshkey.tcl:78
msgid "Generating..."
-msgstr "Создание..."
+msgstr "Создание…"
#: lib/sshkey.tcl:84
#, tcl-format
"Could not start ssh-keygen:\n"
"\n"
"%s"
-msgstr ""
-"Ошибка запуска ssh-keygen:\n"
-"\n"
-"%s"
+msgstr "Ошибка запуска ssh-keygen:\n\n%s"
#: lib/sshkey.tcl:111
msgid "Generation failed."
#: lib/status_bar.tcl:83
#, tcl-format
msgid "%s ... %*i of %*i %s (%3i%%)"
-msgstr "%s ... %*i из %*i %s (%3i%%)"
+msgstr "%s … %*i из %*i %s (%3i%%)"
#: lib/tools.tcl:75
#, tcl-format
#: lib/tools_dlg.tcl:48
msgid "Use '/' separators to create a submenu tree:"
-msgstr "Используйте '/' для создания подменю"
+msgstr "Используйте «/» для создания подменю"
#: lib/tools_dlg.tcl:61
msgid "Command:"
#: lib/tools_dlg.tcl:129
#, tcl-format
msgid "Tool '%s' already exists."
-msgstr "Вспомогательная операция '%s' уже существует."
+msgstr "Вспомогательная операция «%s» уже существует."
#: lib/tools_dlg.tcl:151
#, tcl-format
msgid ""
"Could not add tool:\n"
"%s"
-msgstr ""
-"Ошибка добавления программы:\n"
-"%s"
+msgstr "Ошибка добавления программы:\n%s"
#: lib/tools_dlg.tcl:190
msgid "Remove Tool"
#: lib/transport.tcl:7
#, tcl-format
msgid "Fetching new changes from %s"
-msgstr "Ð\9fолÑ\83чение изменений из %s "
+msgstr "Ð\98звлечение изменений из %s "
-# carbon copy
#: lib/transport.tcl:18
#, tcl-format
msgid "remote prune %s"
#: lib/transport.tcl:19
#, tcl-format
msgid "Pruning tracking branches deleted from %s"
-msgstr "ЧиÑ\81Ñ\82ка веÑ\82вей Ñ\81лежениÑ\8f, Ñ\83даленных из %s"
+msgstr "ЧиÑ\81Ñ\82ка оÑ\82Ñ\81леживаемÑ\8bÑ\85 веÑ\82ок, Ñ\83далÑ\91нных из %s"
#: lib/transport.tcl:26
#, tcl-format
#: lib/transport.tcl:100
msgid "Push Branches"
-msgstr "Ð\9eÑ\82пÑ\80авиÑ\82Ñ\8c изменениÑ\8f в веÑ\82вÑ\8fÑ\85"
+msgstr "Ð\9eÑ\82пÑ\80авиÑ\82Ñ\8c веÑ\82ки"
#: lib/transport.tcl:114
msgid "Source Branches"
-msgstr "Ð\98Ñ\81Ñ\85однÑ\8bе веÑ\82ви"
+msgstr "Ð\98Ñ\81Ñ\85однÑ\8bе веÑ\82ки"
#: lib/transport.tcl:131
msgid "Destination Repository"
#: lib/transport.tcl:171
msgid "Force overwrite existing branch (may discard changes)"
-msgstr "Ð\9dамеÑ\80енно пеÑ\80епиÑ\81аÑ\82Ñ\8c Ñ\81Ñ\83Ñ\89еÑ\81Ñ\82вÑ\83Ñ\8eÑ\89Ñ\83Ñ\8e веÑ\82вÑ\8c (возможна потеря изменений)"
+msgstr "Ð\9fÑ\80инÑ\83диÑ\82елÑ\8cно пеÑ\80езапиÑ\81аÑ\82Ñ\8c Ñ\81Ñ\83Ñ\89еÑ\81Ñ\82вÑ\83Ñ\8eÑ\89Ñ\83Ñ\8e веÑ\82кÑ\83 (возможна потеря изменений)"
#: lib/transport.tcl:175
msgid "Use thin pack (for slow network connections)"
command_close_pipe
command_bidi_pipe
command_close_bidi_pipe
+ get_record
);
BEGIN {
"files will not be compressed.\n";
}
File::Find::find({ wanted => \&gc_directory, no_chdir => 1},
- "$ENV{GIT_DIR}/svn");
+ Git::SVN::svn_dir());
}
########################### utility functions #########################
return unless verify_ref('HEAD^0');
return if $ENV{GIT_DIR} !~ m#^(?:.*/)?\.git$#;
- my $index = $ENV{GIT_INDEX_FILE} || "$ENV{GIT_DIR}/index";
+ my $index = command_oneline(qw(rev-parse --git-path index));
return if -f $index;
return if command_oneline(qw/rev-parse --is-inside-work-tree/) eq 'false';
sub get_commit_entry {
my ($treeish) = shift;
my %log_entry = ( log => '', tree => get_tree_from_treeish($treeish) );
- my $commit_editmsg = "$ENV{GIT_DIR}/COMMIT_EDITMSG";
- my $commit_msg = "$ENV{GIT_DIR}/COMMIT_MSG";
+ my @git_path = qw(rev-parse --git-path);
+ my $commit_editmsg = command_oneline(@git_path, 'COMMIT_EDITMSG');
+ my $commit_msg = command_oneline(@git_path, 'COMMIT_MSG');
open my $log_fh, '>', $commit_editmsg or croak $!;
my $type = command_oneline(qw/cat-file -t/, $treeish);
{
require Encode;
# SVN requires messages to be UTF-8 when entering the repo
- local $/;
open $log_fh, '<', $commit_msg or croak $!;
binmode $log_fh;
- chomp($log_entry{log} = <$log_fh>);
+ chomp($log_entry{log} = get_record($log_fh, undef));
my $enc = Git::config('i18n.commitencoding') || 'UTF-8';
my $msg = $log_entry{log};
orig_cwd = xgetcwd();
for (i = 0; i < ARRAY_SIZE(env_names); i++) {
orig_env[i] = getenv(env_names[i]);
- if (orig_env[i])
- orig_env[i] = xstrdup(orig_env[i]);
+ orig_env[i] = xstrdup_or_null(orig_env[i]);
}
}
setenv(GIT_WORK_TREE_ENVIRONMENT, cmd, 1);
if (envchanged)
*envchanged = 1;
+ } else if (!strcmp(cmd, "--super-prefix")) {
+ if (*argc < 2) {
+ fprintf(stderr, "No prefix given for --super-prefix.\n" );
+ usage(git_usage_string);
+ }
+ setenv(GIT_SUPER_PREFIX_ENVIRONMENT, (*argv)[1], 1);
+ if (envchanged)
+ *envchanged = 1;
+ (*argv)++;
+ (*argc)--;
+ } else if (skip_prefix(cmd, "--super-prefix=", &cmd)) {
+ setenv(GIT_SUPER_PREFIX_ENVIRONMENT, cmd, 1);
+ if (envchanged)
+ *envchanged = 1;
} else if (!strcmp(cmd, "--bare")) {
char *cwd = xgetcwd();
is_bare_repository_cfg = 1;
* RUN_SETUP for reading from the configuration file.
*/
#define NEED_WORK_TREE (1<<3)
+#define SUPPORT_SUPER_PREFIX (1<<4)
struct cmd_struct {
const char *cmd;
}
commit_pager_choice();
+ if (!help && get_super_prefix()) {
+ if (!(p->option & SUPPORT_SUPER_PREFIX))
+ die("%s doesn't support --super-prefix", p->cmd);
+ if (prefix)
+ die("can't use --super-prefix from a subdirectory");
+ }
+
if (!help && p->option & NEED_WORK_TREE)
setup_work_tree();
{ "init-db", cmd_init_db },
{ "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY },
{ "log", cmd_log, RUN_SETUP },
- { "ls-files", cmd_ls_files, RUN_SETUP },
+ { "ls-files", cmd_ls_files, RUN_SETUP | SUPPORT_SUPER_PREFIX },
{ "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
{ "ls-tree", cmd_ls_tree, RUN_SETUP },
{ "mailinfo", cmd_mailinfo },
const char *tmp;
int status;
+ if (get_super_prefix())
+ die("%s doesn't support --super-prefix", argv[0]);
+
if (use_pager == -1)
use_pager = check_pager_config(argv[0]);
commit_pager_choice();
return $str;
}
-# Sanitize for use in XHTML + application/xml+xhtm (valid XML 1.0)
+# Sanitize for use in XHTML + application/xml+xhtml (valid XML 1.0)
sub sanitize {
my $str = shift;
my $line = shift;
$line = esc_html($line, -nbsp=>1);
- $line =~ s{\b([0-9a-fA-F]{8,40})\b}{
+ $line =~ s{
+ \b
+ (
+ # The output of "git describe", e.g. v2.10.0-297-gf6727b0
+ # or hadoop-20160921-113441-20-g094fb7d
+ (?<!-) # see strbuf_check_tag_ref(). Tags can't start with -
+ [A-Za-z0-9.-]+
+ (?!\.) # refs can't end with ".", see check_refname_format()
+ -g[0-9a-fA-F]{7,40}
+ |
+ # Just a normal looking Git SHA1
+ [0-9a-fA-F]{7,40}
+ )
+ \b
+ }{
$cgi->a({-href => href(action=>"object", hash=>$1),
-class => "text"}, $1);
- }eg;
+ }egx;
return $line;
}
{ 'B', "\n[GNUPG:] BADSIG " },
{ 'U', "\n[GNUPG:] TRUST_NEVER" },
{ 'U', "\n[GNUPG:] TRUST_UNDEFINED" },
+ { 'E', "\n[GNUPG:] ERRSIG "},
+ { 'X', "\n[GNUPG:] EXPSIG "},
+ { 'Y', "\n[GNUPG:] EXPKEYSIG "},
+ { 'R', "\n[GNUPG:] REVKEYSIG "},
};
void parse_gpg_output(struct signature_check *sigc)
/* The trust messages are not followed by key/signer information */
if (sigc->result != 'U') {
sigc->key = xmemdupz(found, 16);
- found += 17;
- next = strchrnul(found, '\n');
- sigc->signer = xmemdupz(found, next - found);
+ /* The ERRSIG message is not followed by signer information */
+ if (sigc-> result != 'E') {
+ found += 17;
+ next = strchrnul(found, '\n');
+ sigc->signer = xmemdupz(found, next - found);
+ }
}
}
}
{
static int bufno;
static char hexbuffer[4][GIT_SHA1_HEXSZ + 1];
- return sha1_to_hex_r(hexbuffer[3 & ++bufno], sha1);
+ bufno = (bufno + 1) % ARRAY_SIZE(hexbuffer);
+ return sha1_to_hex_r(hexbuffer[bufno], sha1);
}
char *oid_to_hex(const struct object_id *oid)
hdr_str(hdr, content_type, buf.buf);
end_headers(hdr);
- packet_write(1, "# service=git-%s\n", svc->name);
+ packet_write_fmt(1, "# service=git-%s\n", svc->name);
packet_flush(1);
argv[0] = svc->name;
cred.protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
cred.host = xstrdup(srvc->host);
- if (srvc->user)
- cred.username = xstrdup(srvc->user);
- if (srvc->pass)
- cred.password = xstrdup(srvc->pass);
+ cred.username = xstrdup_or_null(srvc->user);
+ cred.password = xstrdup_or_null(srvc->pass);
credential_fill(&cred);
} else {
struct mailmap_info *mi = xcalloc(1, sizeof(struct mailmap_info));
debug_mm("mailmap: adding (complex) entry for '%s'\n", old_email);
- if (new_name)
- mi->name = xstrdup(new_name);
- if (new_email)
- mi->email = xstrdup(new_email);
+ mi->name = xstrdup_or_null(new_name);
+ mi->email = xstrdup_or_null(new_email);
string_list_insert(&me->namemap, old_name)->util = mi;
}
return -1;
idx_name = pack_bitmap_filename(packfile);
- fd = git_open_noatime(idx_name);
+ fd = git_open(idx_name);
free(idx_name);
if (fd < 0)
STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
};
static int index;
- struct strbuf *sb = &pathname_array[3 & ++index];
+ struct strbuf *sb = &pathname_array[index];
+ index = (index + 1) % ARRAY_SIZE(pathname_array);
strbuf_reset(sb);
return sb;
}
{
struct pathspec_item *item;
const char *entry = argv ? *argv : NULL;
- int i, n, prefixlen, nr_exclude = 0;
+ int i, n, prefixlen, warn_empty_string, nr_exclude = 0;
memset(pathspec, 0, sizeof(*pathspec));
}
n = 0;
- while (argv[n])
+ warn_empty_string = 1;
+ while (argv[n]) {
+ if (*argv[n] == '\0' && warn_empty_string) {
+ warning(_("empty strings as pathspecs will be made invalid in upcoming releases. "
+ "please use . instead if you meant to match all paths"));
+ warn_empty_string = 0;
+ }
n++;
+ }
pathspec->nr = n;
ALLOC_ARRAY(pathspec->items, n);
command_bidi_pipe command_close_bidi_pipe
version exec_path html_path hash_object git_cmd_try
remote_refs prompt
- get_tz_offset
+ get_tz_offset get_record
credential credential_read credential_write
temp_acquire temp_is_locked temp_release temp_reset temp_path);
return sprintf("%s%02d%02d", $sign, (gmtime(abs($t - $gm)))[2,1]);
}
+=item get_record ( FILEHANDLE, INPUT_RECORD_SEPARATOR )
+
+Read one record from FILEHANDLE delimited by INPUT_RECORD_SEPARATOR,
+removing any trailing INPUT_RECORD_SEPARATOR.
+
+=cut
+
+sub get_record {
+ my ($fh, $rs) = @_;
+ local $/ = $rs;
+ my $rec = <$fh>;
+ chomp $rec if defined $rs;
+ $rec;
+}
=item prompt ( PROMPT , ISPASSWORD )
=cut
+# Very close to Mail::Address's parser, but we still have minor
+# differences in some cases (see t9000 for examples).
sub parse_mailboxes {
my $re_comment = qr/\((?:[^)]*)\)/;
my $re_quote = qr/"(?:[^\"\\]|\\.)*"/;
# 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 } @_;
+ my $end_of_addr_seen = 0;
# add a delimiter to simplify treatment for the last mailbox
push @tokens, ",";
if ($token =~ /^[,;]$/) {
# if buffer still contains undeterminated strings
# append it at the end of @address or @phrase
- if (@address) {
- push @address, @buffer;
- } else {
+ if ($end_of_addr_seen) {
push @phrase, @buffer;
+ } else {
+ push @address, @buffer;
}
my $str_phrase = join ' ', @phrase;
push @addr_list, $str_mailbox if ($str_mailbox);
@phrase = @address = @comment = @buffer = ();
+ $end_of_addr_seen = 0;
} elsif ($token =~ /^\(/) {
push @comment, $token;
} elsif ($token eq "<") {
push @phrase, (splice @address), (splice @buffer);
} elsif ($token eq ">") {
+ $end_of_addr_seen = 1;
push @address, (splice @buffer);
- } elsif ($token eq "@") {
+ } elsif ($token eq "@" && !$end_of_addr_seen) {
push @address, (splice @buffer), "@";
- } elsif ($token eq ".") {
- push @address, (splice @buffer), ".";
} else {
push @buffer, $token;
}
(++$min, $max);
}
+sub svn_dir {
+ command_oneline(qw(rev-parse --git-path svn));
+}
+
sub tmp_config {
my (@args) = @_;
- my $old_def_config = "$ENV{GIT_DIR}/svn/config";
- my $config = "$ENV{GIT_DIR}/svn/.metadata";
+ my $svn_dir = svn_dir();
+ my $old_def_config = "$svn_dir/config";
+ my $config = "$svn_dir/.metadata";
if (! -f $config && -f $old_def_config) {
rename $old_def_config, $config or
die "Failed rename $old_def_config => $config: $!\n";
if ($memo_backend > 0) {
tie %$hash => 'Git::SVN::Memoize::YAML', "$path.yaml";
} else {
- tie %$hash => 'Memoize::Storable', "$path.db", 'nstore';
+ # first verify that any existing file can actually be loaded
+ # (it may have been saved by an incompatible version)
+ my $db = "$path.db";
+ if (-e $db) {
+ use Storable qw(retrieve);
+
+ if (!eval { retrieve($db); 1 }) {
+ unlink $db or die "unlink $db failed: $!";
+ }
+ }
+ tie %$hash => 'Memoize::Storable', $db, 'nstore';
}
}
return if $memoized;
$memoized = 1;
- my $cache_path = "$ENV{GIT_DIR}/svn/.caches/";
+ my $cache_path = svn_dir() . '/.caches/';
mkpath([$cache_path]) unless -d $cache_path;
my %lookup_svn_merge_cache;
sub clear_memoized_mergeinfo_caches {
die "Only call this method in non-memoized context" if ($memoized);
- my $cache_path = "$ENV{GIT_DIR}/svn/.caches/";
+ my $cache_path = svn_dir() . '/.caches/';
return unless -d $cache_path;
for my $cache_file (("$cache_path/lookup_svn_merge",
"refs/remotes/$prefix$default_ref_id";
}
$_[1] = $repo_id;
- my $dir = "$ENV{GIT_DIR}/svn/$ref_id";
+ my $svn_dir = svn_dir();
+ my $dir = "$svn_dir/$ref_id";
- # Older repos imported by us used $GIT_DIR/svn/foo instead of
- # $GIT_DIR/svn/refs/remotes/foo when tracking refs/remotes/foo
+ # Older repos imported by us used $svn_dir/foo instead of
+ # $svn_dir/refs/remotes/foo when tracking refs/remotes/foo
if ($ref_id =~ m{^refs/remotes/(.+)}) {
- my $old_dir = "$ENV{GIT_DIR}/svn/$1";
+ my $old_dir = "$svn_dir/$1";
if (-d $old_dir && ! -d $dir) {
$dir = $old_dir;
}
mkpath([$dir]);
my $obj = bless {
ref_id => $ref_id, dir => $dir, index => "$dir/index",
- config => "$ENV{GIT_DIR}/svn/config",
+ config => "$svn_dir/config",
map_root => "$dir/.rev_map", repo_id => $repo_id }, $class;
# Ensure it gets canonicalized
use Carp qw/croak/;
use Git qw/command command_oneline command_noisy command_output_pipe
command_input_pipe command_close_pipe
- command_bidi_pipe command_close_bidi_pipe/;
+ command_bidi_pipe command_close_bidi_pipe
+ get_record/;
+
BEGIN {
@ISA = qw(SVN::Delta::Editor);
}
push @diff_tree, "-l$_rename_limit" if defined $_rename_limit;
push @diff_tree, $tree_a, $tree_b;
my ($diff_fh, $ctx) = command_output_pipe(@diff_tree);
- local $/ = "\0";
my $state = 'meta';
my @mods;
- while (<$diff_fh>) {
- chomp $_; # this gets rid of the trailing "\0"
+ while (defined($_ = get_record($diff_fh, "\0"))) {
if ($state eq 'meta' && /^:(\d{6})\s(\d{6})\s
($::sha1)\s($::sha1)\s
([MTCRAD])\d*$/xo) {
my ($fh, $ctx) = command_output_pipe(qw/ls-tree --name-only -r -z/,
$self->{tree_b});
- local $/ = "\0";
- while (<$fh>) {
- chomp;
+ while (defined($_ = get_record($fh, "\0"))) {
my @dn = split m#/#, $_;
while (pop @dn) {
delete $rm->{join '/', @dn};
use File::Basename qw/dirname/;
use Git qw/command command_oneline command_noisy command_output_pipe
command_input_pipe command_close_pipe
- command_bidi_pipe command_close_bidi_pipe/;
+ command_bidi_pipe command_close_bidi_pipe
+ get_record/;
BEGIN {
@ISA = qw(SVN::Delta::Editor);
}
my $printed_warning;
chomp(my $empty_blob = `git hash-object -t blob --stdin < /dev/null`);
my ($ls, $ctx) = command_output_pipe(qw/ls-tree -r -z/, $cmt);
- local $/ = "\0";
my $pfx = defined($switch_path) ? $switch_path : $git_svn->path;
$pfx .= '/' if length($pfx);
- while (<$ls>) {
- chomp;
+ while (defined($_ = get_record($ls, "\0"))) {
s/\A100644 blob $empty_blob\t//o or next;
unless ($printed_warning) {
print STDERR "Scanning for empty symlinks, ",
my ($ls, $ctx) = command_output_pipe(qw/ls-tree
-r --name-only -z/,
$tree);
- local $/ = "\0";
- while (<$ls>) {
- chomp;
+ while (defined($_ = get_record($ls, "\0"))) {
my $rmpath = "$gpath/$_";
$self->{gii}->remove($rmpath);
print "\tD\t$rmpath\n" unless $::_q;
my ($ls, $ctx) = command_output_pipe(qw/ls-tree
-r --name-only -z/,
$self->{c});
- local $/ = "\0";
- while (<$ls>) {
- chomp;
+ while (defined($_ = get_record($ls, "\0"))) {
$self->{gii}->remove($_);
print "\tD\t$_\n" unless $::_q;
push @deleted_gpath, $gpath;
command_noisy
command_output_pipe
command_close_pipe
+ command_oneline
);
+use Git::SVN;
sub migrate_from_v0 {
my $git_dir = $ENV{GIT_DIR};
chomp;
my ($id, $orig_ref) = ($_, $_);
next unless $id =~ s#^refs/heads/(.+)-HEAD$#$1#;
- next unless -f "$git_dir/$id/info/url";
+ my $info_url = command_oneline(qw(rev-parse --git-path),
+ "$id/info/url");
+ next unless -f $info_url;
my $new_ref = "refs/remotes/$id";
if (::verify_ref("$new_ref^0")) {
print STDERR "W: $orig_ref is probably an old ",
my $git_dir = $ENV{GIT_DIR};
my $migrated = 0;
return $migrated unless -d $git_dir;
- my $svn_dir = "$git_dir/svn";
+ my $svn_dir = Git::SVN::svn_dir();
# just in case somebody used 'svn' as their $id at some point...
return $migrated if -d $svn_dir && ! -f "$svn_dir/info/url";
my $x = $_;
next unless $x =~ s#^refs/remotes/##;
chomp $x;
- next unless -f "$git_dir/$x/info/url";
- my $u = eval { ::file_to_s("$git_dir/$x/info/url") };
+ my $info_url = command_oneline(qw(rev-parse --git-path),
+ "$x/info/url");
+ next unless -f $info_url;
+ my $u = eval { ::file_to_s($info_url) };
next unless $u;
- my $dn = dirname("$git_dir/svn/$x");
+ my $dn = dirname("$svn_dir/$x");
mkpath([$dn]) unless -d $dn;
if ($x eq 'svn') { # they used 'svn' as GIT_SVN_ID:
- mkpath(["$git_dir/svn/svn"]);
+ mkpath(["$svn_dir/svn"]);
print STDERR " - $git_dir/$x/info => ",
- "$git_dir/svn/$x/info\n";
- rename "$git_dir/$x/info", "$git_dir/svn/$x/info" or
+ "$svn_dir/$x/info\n";
+ rename "$git_dir/$x/info", "$svn_dir/$x/info" or
croak "$!: $x";
# don't worry too much about these, they probably
# don't exist with repos this old (save for index,
# and we can easily regenerate that)
foreach my $f (qw/unhandled.log index .rev_db/) {
- rename "$git_dir/$x/$f", "$git_dir/svn/$x/$f";
+ rename "$git_dir/$x/$f", "$svn_dir/$x/$f";
}
} else {
- print STDERR " - $git_dir/$x => $git_dir/svn/$x\n";
- rename "$git_dir/$x", "$git_dir/svn/$x" or
- croak "$!: $x";
+ print STDERR " - $git_dir/$x => $svn_dir/$x\n";
+ rename "$git_dir/$x", "$svn_dir/$x" or croak "$!: $x";
}
$migrated++;
}
push @dir, $_;
}
}
+ my $svn_dir = Git::SVN::svn_dir();
foreach (@dir) {
my $x = $_;
- $x =~ s!^\Q$ENV{GIT_DIR}\E/svn/!!o;
+ $x =~ s!^\Q$svn_dir\E/!!o;
read_old_urls($l_map, $x, $_);
}
}
my @cfg = command(qw/config -l/);
return if grep /^svn-remote\..+\.url=/, @cfg;
my %l_map;
- read_old_urls(\%l_map, '', "$ENV{GIT_DIR}/svn");
+ read_old_urls(\%l_map, '', Git::SVN::svn_dir());
my $migrated = 0;
require Git::SVN;
}
}
if (@emptied) {
- my $file = $ENV{GIT_CONFIG} || "$ENV{GIT_DIR}/config";
+ my $file = $ENV{GIT_CONFIG} ||
+ command_oneline(qw(rev-parse --git-path config));
print STDERR <<EOF;
The following [svn-remote] sections in your config file ($file) are empty
and can be safely removed:
write_or_die(fd, "0000", 4);
}
+int packet_flush_gently(int fd)
+{
+ packet_trace("0000", 4, 1);
+ if (write_in_full(fd, "0000", 4) == 4)
+ return 0;
+ return error("flush packet write failed");
+}
+
void packet_buf_flush(struct strbuf *buf)
{
packet_trace("0000", 4, 1);
strbuf_add(buf, "0000", 4);
}
-#define hex(a) (hexchar[(a) & 15])
-static void format_packet(struct strbuf *out, const char *fmt, va_list args)
+static void set_packet_header(char *buf, const int size)
{
static char hexchar[] = "0123456789abcdef";
+
+ #define hex(a) (hexchar[(a) & 15])
+ buf[0] = hex(size >> 12);
+ buf[1] = hex(size >> 8);
+ buf[2] = hex(size >> 4);
+ buf[3] = hex(size);
+ #undef hex
+}
+
+static void format_packet(struct strbuf *out, const char *fmt, va_list args)
+{
size_t orig_len, n;
orig_len = out->len;
if (n > LARGE_PACKET_MAX)
die("protocol error: impossibly long line");
- out->buf[orig_len + 0] = hex(n >> 12);
- out->buf[orig_len + 1] = hex(n >> 8);
- out->buf[orig_len + 2] = hex(n >> 4);
- out->buf[orig_len + 3] = hex(n);
+ set_packet_header(&out->buf[orig_len], n);
packet_trace(out->buf + orig_len + 4, n - 4, 1);
}
-void packet_write(int fd, const char *fmt, ...)
+static int packet_write_fmt_1(int fd, int gently,
+ const char *fmt, va_list args)
+{
+ struct strbuf buf = STRBUF_INIT;
+ ssize_t count;
+
+ format_packet(&buf, fmt, args);
+ count = write_in_full(fd, buf.buf, buf.len);
+ if (count == buf.len)
+ return 0;
+
+ if (!gently) {
+ check_pipe(errno);
+ die_errno("packet write with format failed");
+ }
+ return error("packet write with format failed");
+}
+
+void packet_write_fmt(int fd, const char *fmt, ...)
{
- static struct strbuf buf = STRBUF_INIT;
va_list args;
- strbuf_reset(&buf);
va_start(args, fmt);
- format_packet(&buf, fmt, args);
+ packet_write_fmt_1(fd, 0, fmt, args);
+ va_end(args);
+}
+
+int packet_write_fmt_gently(int fd, const char *fmt, ...)
+{
+ int status;
+ va_list args;
+
+ va_start(args, fmt);
+ status = packet_write_fmt_1(fd, 1, fmt, args);
va_end(args);
- write_or_die(fd, buf.buf, buf.len);
+ return status;
+}
+
+static int packet_write_gently(const int fd_out, const char *buf, size_t size)
+{
+ static char packet_write_buffer[LARGE_PACKET_MAX];
+ size_t packet_size;
+
+ if (size > sizeof(packet_write_buffer) - 4)
+ return error("packet write failed - data exceeds max packet size");
+
+ packet_trace(buf, size, 1);
+ packet_size = size + 4;
+ set_packet_header(packet_write_buffer, packet_size);
+ memcpy(packet_write_buffer + 4, buf, size);
+ if (write_in_full(fd_out, packet_write_buffer, packet_size) == packet_size)
+ return 0;
+ return error("packet write failed");
}
void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
va_end(args);
}
+int write_packetized_from_fd(int fd_in, int fd_out)
+{
+ static char buf[LARGE_PACKET_DATA_MAX];
+ int err = 0;
+ ssize_t bytes_to_write;
+
+ while (!err) {
+ bytes_to_write = xread(fd_in, buf, sizeof(buf));
+ if (bytes_to_write < 0)
+ return COPY_READ_ERROR;
+ if (bytes_to_write == 0)
+ break;
+ err = packet_write_gently(fd_out, buf, bytes_to_write);
+ }
+ if (!err)
+ err = packet_flush_gently(fd_out);
+ return err;
+}
+
+int write_packetized_from_buf(const char *src_in, size_t len, int fd_out)
+{
+ int err = 0;
+ size_t bytes_written = 0;
+ size_t bytes_to_write;
+
+ while (!err) {
+ if ((len - bytes_written) > LARGE_PACKET_DATA_MAX)
+ bytes_to_write = LARGE_PACKET_DATA_MAX;
+ else
+ bytes_to_write = len - bytes_written;
+ if (bytes_to_write == 0)
+ break;
+ err = packet_write_gently(fd_out, src_in + bytes_written, bytes_to_write);
+ bytes_written += bytes_to_write;
+ }
+ if (!err)
+ err = packet_flush_gently(fd_out);
+ return err;
+}
+
static int get_packet_data(int fd, char **src_buf, size_t *src_size,
void *dst, unsigned size, int options)
{
{
return packet_read_line_generic(-1, src, src_len, dst_len);
}
+
+ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out)
+{
+ int packet_len;
+
+ size_t orig_len = sb_out->len;
+ size_t orig_alloc = sb_out->alloc;
+
+ for (;;) {
+ strbuf_grow(sb_out, LARGE_PACKET_DATA_MAX);
+ packet_len = packet_read(fd_in, NULL, NULL,
+ /* strbuf_grow() above always allocates one extra byte to
+ * store a '\0' at the end of the string. packet_read()
+ * writes a '\0' extra byte at the end, too. Let it know
+ * that there is already room for the extra byte.
+ */
+ sb_out->buf + sb_out->len, LARGE_PACKET_DATA_MAX+1,
+ PACKET_READ_GENTLE_ON_EOF);
+ if (packet_len <= 0)
+ break;
+ sb_out->len += packet_len;
+ }
+
+ if (packet_len < 0) {
+ if (orig_alloc == 0)
+ strbuf_release(sb_out);
+ else
+ strbuf_setlen(sb_out, orig_len);
+ return packet_len;
+ }
+ return sb_out->len - orig_len;
+}
* side can't, we stay with pure read/write interfaces.
*/
void packet_flush(int fd);
-void packet_write(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
+void packet_write_fmt(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
void packet_buf_flush(struct strbuf *buf);
void packet_buf_write(struct strbuf *buf, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
+int packet_flush_gently(int fd);
+int packet_write_fmt_gently(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
+int write_packetized_from_fd(int fd_in, int fd_out);
+int write_packetized_from_buf(const char *src_in, size_t len, int fd_out);
/*
* Read a packetized line into the buffer, which must be at least size bytes
*/
char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size);
+/*
+ * Reads a stream of variable sized packets until a flush packet is detected.
+ */
+ssize_t read_packetized_to_strbuf(int fd_in, struct strbuf *sb_out);
+
#define DEFAULT_PACKET_MAX 1000
#define LARGE_PACKET_MAX 65520
+#define LARGE_PACKET_DATA_MAX (LARGE_PACKET_MAX - 4)
extern char packet_buffer[LARGE_PACKET_MAX];
#endif
switch (c->signature_check.result) {
case 'G':
case 'B':
+ case 'E':
case 'U':
case 'N':
+ case 'X':
+ case 'Y':
+ case 'R':
strbuf_addch(sb, c->signature_check.result);
}
break;
static int ce_compare_data(const struct cache_entry *ce, struct stat *st)
{
int match = -1;
- int fd = open(ce->name, O_RDONLY);
+ static int cloexec = O_CLOEXEC;
+ int fd = open(ce->name, O_RDONLY | cloexec);
+
+ if ((cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) {
+ /* Try again w/o O_CLOEXEC: the kernel might not support it */
+ cloexec &= ~O_CLOEXEC;
+ fd = open(ce->name, O_RDONLY | cloexec);
+ }
if (fd >= 0) {
unsigned char sha1[20];
hashcpy(update->new_sha1, new_sha1);
if (flags & REF_HAVE_OLD)
hashcpy(update->old_sha1, old_sha1);
- if (msg)
- update->msg = xstrdup(msg);
+ update->msg = xstrdup_or_null(msg);
return update;
}
struct child_to_clean {
pid_t pid;
+ struct child_process *process;
struct child_to_clean *next;
};
static struct child_to_clean *children_to_clean;
while (children_to_clean) {
struct child_to_clean *p = children_to_clean;
children_to_clean = p->next;
+
+ if (p->process && !in_signal) {
+ struct child_process *process = p->process;
+ if (process->clean_on_exit_handler) {
+ trace_printf(
+ "trace: run_command: running exit handler for pid %"
+ PRIuMAX, (uintmax_t)p->pid
+ );
+ process->clean_on_exit_handler(process);
+ }
+ }
+
kill(p->pid, sig);
if (!in_signal)
free(p);
cleanup_children(SIGTERM, 0);
}
-static void mark_child_for_cleanup(pid_t pid)
+static void mark_child_for_cleanup(pid_t pid, struct child_process *process)
{
struct child_to_clean *p = xmalloc(sizeof(*p));
p->pid = pid;
+ p->process = process;
p->next = children_to_clean;
children_to_clean = p;
if (cmd->pid < 0)
error_errno("cannot fork() for %s", cmd->argv[0]);
else if (cmd->clean_on_exit)
- mark_child_for_cleanup(cmd->pid);
+ mark_child_for_cleanup(cmd->pid, cmd);
/*
* Wait for child's execvp. If the execvp succeeds (or if fork()
if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
error_errno("cannot spawn %s", cmd->argv[0]);
if (cmd->clean_on_exit && cmd->pid >= 0)
- mark_child_for_cleanup(cmd->pid);
+ mark_child_for_cleanup(cmd->pid, cmd);
argv_array_clear(&nargv);
cmd->argv = sargv;
return !pthread_equal(main_thread, pthread_self());
}
-void NORETURN async_exit(int code)
+static void NORETURN async_exit(int code)
{
pthread_exit((void *)(intptr_t)code);
}
return process_is_async;
}
-void NORETURN async_exit(int code)
+static void NORETURN async_exit(int code)
{
exit(code);
}
#endif
+void check_pipe(int err)
+{
+ if (err == EPIPE) {
+ if (in_async())
+ async_exit(141);
+
+ signal(SIGPIPE, SIG_DFL);
+ raise(SIGPIPE);
+ /* Should never happen, but just in case... */
+ exit(141);
+ }
+}
+
int start_async(struct async *async)
{
int need_in, need_out;
exit(!!async->proc(proc_in, proc_out, async->data));
}
- mark_child_for_cleanup(async->pid);
+ mark_child_for_cleanup(async->pid, NULL);
if (need_in)
close(fdin[0]);
unsigned stdout_to_stderr:1;
unsigned use_shell:1;
unsigned clean_on_exit:1;
+ void (*clean_on_exit_handler)(struct child_process *process);
+ void *clean_on_exit_handler_cbdata;
};
#define CHILD_PROCESS_INIT { NULL, ARGV_ARRAY_INIT, ARGV_ARRAY_INIT }
int start_async(struct async *async);
int finish_async(struct async *async);
int in_async(void);
-void NORETURN async_exit(int code);
+void check_pipe(int err);
/**
* This callback should initialize the child process and preload the
hint->status = REF_STATUS_REMOTE_REJECT;
ret = -1;
}
- if (msg)
- hint->remote_status = xstrdup(msg);
+ hint->remote_status = xstrdup_or_null(msg);
/* start our next search from the next ref */
hint = hint->next;
}
#include "merge-recursive.h"
#include "refs.h"
#include "argv-array.h"
+#include "quote.h"
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
const char sign_off_header[] = "Signed-off-by: ";
static const char cherry_picked_prefix[] = "(cherry picked from commit ";
-static GIT_PATH_FUNC(git_path_todo_file, SEQ_TODO_FILE)
-static GIT_PATH_FUNC(git_path_opts_file, SEQ_OPTS_FILE)
-static GIT_PATH_FUNC(git_path_seq_dir, SEQ_DIR)
-static GIT_PATH_FUNC(git_path_head_file, SEQ_HEAD_FILE)
+GIT_PATH_FUNC(git_path_seq_dir, "sequencer")
+
+static GIT_PATH_FUNC(git_path_todo_file, "sequencer/todo")
+static GIT_PATH_FUNC(git_path_opts_file, "sequencer/opts")
+static GIT_PATH_FUNC(git_path_head_file, "sequencer/head")
+
+/*
+ * A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
+ * GIT_AUTHOR_DATE that will be used for the commit that is currently
+ * being rebased.
+ */
+static GIT_PATH_FUNC(rebase_path_author_script, "rebase-merge/author-script")
+/*
+ * The following files are written by git-rebase just after parsing the
+ * command-line (and are only consumed, not modified, by the sequencer).
+ */
+static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, "rebase-merge/gpg_sign_opt")
+
+/* We will introduce the 'interactive rebase' mode later */
+static inline int is_rebase_i(const struct replay_opts *opts)
+{
+ return 0;
+}
+
+static const char *get_dir(const struct replay_opts *opts)
+{
+ return git_path_seq_dir();
+}
+
+static const char *get_todo_path(const struct replay_opts *opts)
+{
+ return git_path_todo_file();
+}
static int is_rfc2822_line(const char *buf, int len)
{
return 1;
}
-static void remove_sequencer_state(void)
+static const char *gpg_sign_opt_quoted(struct replay_opts *opts)
+{
+ static struct strbuf buf = STRBUF_INIT;
+
+ strbuf_reset(&buf);
+ if (opts->gpg_sign)
+ sq_quotef(&buf, "-S%s", opts->gpg_sign);
+ return buf.buf;
+}
+
+int sequencer_remove_state(struct replay_opts *opts)
{
- struct strbuf seq_dir = STRBUF_INIT;
+ struct strbuf dir = STRBUF_INIT;
+ int i;
+
+ free(opts->gpg_sign);
+ free(opts->strategy);
+ for (i = 0; i < opts->xopts_nr; i++)
+ free(opts->xopts[i]);
+ free(opts->xopts);
+
+ strbuf_addf(&dir, "%s", get_dir(opts));
+ remove_dir_recursively(&dir, 0);
+ strbuf_release(&dir);
- strbuf_addstr(&seq_dir, git_path(SEQ_DIR));
- remove_dir_recursively(&seq_dir, 0);
- strbuf_release(&seq_dir);
+ return 0;
}
static const char *action_name(const struct replay_opts *opts)
{
- return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
+ return opts->action == REPLAY_REVERT ? N_("revert") : N_("cherry-pick");
}
struct commit_message {
const char *message;
};
+static const char *short_commit_name(struct commit *commit)
+{
+ return find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV);
+}
+
static int get_message(struct commit *commit, struct commit_message *out)
{
const char *abbrev, *subject;
int subject_len;
out->message = logmsg_reencode(commit, NULL, get_commit_output_encoding());
- abbrev = find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV);
+ abbrev = short_commit_name(commit);
subject_len = find_commit_subject(out->message, &subject);
}
}
-static int write_message(struct strbuf *msgbuf, const char *filename)
+static int write_message(const void *buf, size_t len, const char *filename,
+ int append_eol)
{
static struct lock_file msg_file;
int msg_fd = hold_lock_file_for_update(&msg_file, filename, 0);
if (msg_fd < 0)
- return error_errno(_("Could not lock '%s'"), filename);
- if (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)
- return error_errno(_("Could not write to %s"), filename);
- strbuf_release(msgbuf);
- if (commit_lock_file(&msg_file) < 0)
- return error(_("Error wrapping up %s."), filename);
+ return error_errno(_("could not lock '%s'"), filename);
+ if (write_in_full(msg_fd, buf, len) < 0) {
+ rollback_lock_file(&msg_file);
+ return error_errno(_("could not write to '%s'"), filename);
+ }
+ if (append_eol && write(msg_fd, "\n", 1) < 0) {
+ rollback_lock_file(&msg_file);
+ return error_errno(_("could not write eol to '%s"), filename);
+ }
+ if (commit_lock_file(&msg_file) < 0) {
+ rollback_lock_file(&msg_file);
+ return error(_("failed to finalize '%s'."), filename);
+ }
return 0;
}
+/*
+ * Reads a file that was presumably written by a shell script, i.e. with an
+ * end-of-line marker that needs to be stripped.
+ *
+ * Note that only the last end-of-line marker is stripped, consistent with the
+ * behavior of "$(cat path)" in a shell script.
+ *
+ * Returns 1 if the file was read, 0 if it could not be read or does not exist.
+ */
+static int read_oneliner(struct strbuf *buf,
+ const char *path, int skip_if_empty)
+{
+ int orig_len = buf->len;
+
+ if (!file_exists(path))
+ return 0;
+
+ if (strbuf_read_file(buf, path, 0) < 0) {
+ warning_errno(_("could not read '%s'"), path);
+ return 0;
+ }
+
+ if (buf->len > orig_len && buf->buf[buf->len - 1] == '\n') {
+ if (--buf->len > orig_len && buf->buf[buf->len - 1] == '\r')
+ --buf->len;
+ buf->buf[buf->len] = '\0';
+ }
+
+ if (skip_if_empty && buf->len == orig_len)
+ return 0;
+
+ return 1;
+}
+
static struct tree *empty_tree(void)
{
return lookup_tree(EMPTY_TREE_SHA1_BIN);
static int error_dirty_index(struct replay_opts *opts)
{
if (read_cache_unmerged())
- return error_resolve_conflict(action_name(opts));
+ return error_resolve_conflict(_(action_name(opts)));
- /* Different translation strings for cherry-pick and revert */
- if (opts->action == REPLAY_PICK)
- error(_("Your local changes would be overwritten by cherry-pick."));
- else
- error(_("Your local changes would be overwritten by revert."));
+ error(_("your local changes would be overwritten by %s."),
+ _(action_name(opts)));
if (advice_commit_before_merge)
- advise(_("Commit your changes or stash them to proceed."));
+ advise(_("commit your changes or stash them to proceed."));
return -1;
}
if (checkout_fast_forward(from, to, 1))
return -1; /* the callee should have complained already */
- strbuf_addf(&sb, _("%s: fast-forward"), action_name(opts));
+ strbuf_addf(&sb, _("%s: fast-forward"), _(action_name(opts)));
transaction = ref_transaction_begin(&err);
if (!transaction ||
struct merge_options o;
struct tree *result, *next_tree, *base_tree, *head_tree;
int clean;
- const char **xopt;
+ char **xopt;
static struct lock_file index_lock;
hold_locked_index(&index_lock, 1);
write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
/* TRANSLATORS: %s will be "revert" or "cherry-pick" */
return error(_("%s: Unable to write new index file"),
- action_name(opts));
+ _(action_name(opts)));
rollback_lock_file(&index_lock);
if (opts->signoff)
struct commit *head_commit;
if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
- return error(_("Could not resolve HEAD commit\n"));
+ return error(_("could not resolve HEAD commit\n"));
head_commit = lookup_commit(head_sha1);
if (!cache_tree_fully_valid(active_cache_tree))
if (cache_tree_update(&the_index, 0))
- return error(_("Unable to update cache tree\n"));
+ return error(_("unable to update cache tree\n"));
return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.oid.hash);
}
+/*
+ * Read the author-script file into an environment block, ready for use in
+ * run_command(), that can be free()d afterwards.
+ */
+static char **read_author_script(void)
+{
+ struct strbuf script = STRBUF_INIT;
+ int i, count = 0;
+ char *p, *p2, **env;
+ size_t env_size;
+
+ if (strbuf_read_file(&script, rebase_path_author_script(), 256) <= 0)
+ return NULL;
+
+ for (p = script.buf; *p; p++)
+ if (skip_prefix(p, "'\\\\''", (const char **)&p2))
+ strbuf_splice(&script, p - script.buf, p2 - p, "'", 1);
+ else if (*p == '\'')
+ strbuf_splice(&script, p-- - script.buf, 1, "", 0);
+ else if (*p == '\n') {
+ *p = '\0';
+ count++;
+ }
+
+ env_size = (count + 1) * sizeof(*env);
+ strbuf_grow(&script, env_size);
+ memmove(script.buf + env_size, script.buf, script.len);
+ p = script.buf + env_size;
+ env = (char **)strbuf_detach(&script, NULL);
+
+ for (i = 0; i < count; i++) {
+ env[i] = p;
+ p += strlen(p) + 1;
+ }
+ env[count] = NULL;
+
+ return env;
+}
+
+static const char staged_changes_advice[] =
+N_("you have staged changes in your working tree\n"
+"If these changes are meant to be squashed into the previous commit, run:\n"
+"\n"
+" git commit --amend %s\n"
+"\n"
+"If they are meant to go into a new commit, run:\n"
+"\n"
+" git commit %s\n"
+"\n"
+"In both cases, once you're done, continue with:\n"
+"\n"
+" git rebase --continue\n");
+
/*
* If we are cherry-pick, and if the merge did not result in
* hand-editing, we will hit this commit and inherit the original
* author date and name.
+ *
* If we are revert, or if our cherry-pick results in a hand merge,
* we had better say that the current user is responsible for that.
+ *
+ * An exception is when run_git_commit() is called during an
+ * interactive rebase: in that case, we will want to retain the
+ * author metadata.
*/
static int run_git_commit(const char *defmsg, struct replay_opts *opts,
- int allow_empty)
+ int allow_empty, int edit, int amend,
+ int cleanup_commit_message)
{
+ char **env = NULL;
struct argv_array array;
int rc;
const char *value;
+ if (is_rebase_i(opts)) {
+ env = read_author_script();
+ if (!env) {
+ const char *gpg_opt = gpg_sign_opt_quoted(opts);
+
+ return error(_(staged_changes_advice),
+ gpg_opt, gpg_opt);
+ }
+ }
+
argv_array_init(&array);
argv_array_push(&array, "commit");
argv_array_push(&array, "-n");
+ if (amend)
+ argv_array_push(&array, "--amend");
if (opts->gpg_sign)
argv_array_pushf(&array, "-S%s", opts->gpg_sign);
if (opts->signoff)
argv_array_push(&array, "-s");
- if (!opts->edit) {
- argv_array_push(&array, "-F");
- argv_array_push(&array, defmsg);
- if (!opts->signoff &&
- !opts->record_origin &&
- git_config_get_value("commit.cleanup", &value))
- argv_array_push(&array, "--cleanup=verbatim");
- }
+ if (defmsg)
+ argv_array_pushl(&array, "-F", defmsg, NULL);
+ if (cleanup_commit_message)
+ argv_array_push(&array, "--cleanup=strip");
+ if (edit)
+ argv_array_push(&array, "-e");
+ else if (!cleanup_commit_message &&
+ !opts->signoff && !opts->record_origin &&
+ git_config_get_value("commit.cleanup", &value))
+ argv_array_push(&array, "--cleanup=verbatim");
if (allow_empty)
argv_array_push(&array, "--allow-empty");
if (opts->allow_empty_message)
argv_array_push(&array, "--allow-empty-message");
- rc = run_command_v_opt(array.argv, RUN_GIT_CMD);
+ rc = run_command_v_opt_cd_env(array.argv, RUN_GIT_CMD, NULL,
+ (const char *const *)env);
argv_array_clear(&array);
+ free(env);
+
return rc;
}
const unsigned char *ptree_sha1;
if (parse_commit(commit))
- return error(_("Could not parse commit %s\n"),
+ return error(_("could not parse commit %s\n"),
oid_to_hex(&commit->object.oid));
if (commit->parents) {
struct commit *parent = commit->parents->item;
if (parse_commit(parent))
- return error(_("Could not parse parent commit %s\n"),
+ return error(_("could not parse parent commit %s\n"),
oid_to_hex(&parent->object.oid));
ptree_sha1 = parent->tree->object.oid.hash;
} else {
return 1;
}
-static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
+enum todo_command {
+ TODO_PICK = 0,
+ TODO_REVERT
+};
+
+static const char *todo_command_strings[] = {
+ "pick",
+ "revert"
+};
+
+static const char *command_to_string(const enum todo_command command)
+{
+ if (command < ARRAY_SIZE(todo_command_strings))
+ return todo_command_strings[command];
+ die("Unknown command: %d", command);
+}
+
+
+static int do_pick_commit(enum todo_command command, struct commit *commit,
+ struct replay_opts *opts)
{
unsigned char head[20];
struct commit *base, *next, *parent;
* to work on.
*/
if (write_cache_as_tree(head, 0, NULL))
- return error(_("Your index file is unmerged."));
+ return error(_("your index file is unmerged."));
} else {
unborn = get_sha1("HEAD", head);
if (unborn)
hashcpy(head, EMPTY_TREE_SHA1_BIN);
- if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD", 0))
+ if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD", 0, 0))
return error_dirty_index(opts);
}
discard_cache();
struct commit_list *p;
if (!opts->mainline)
- return error(_("Commit %s is a merge but no -m option was given."),
+ return error(_("commit %s is a merge but no -m option was given."),
oid_to_hex(&commit->object.oid));
for (cnt = 1, p = commit->parents;
cnt++)
p = p->next;
if (cnt != opts->mainline || !p)
- return error(_("Commit %s does not have parent %d"),
+ return error(_("commit %s does not have parent %d"),
oid_to_hex(&commit->object.oid), opts->mainline);
parent = p->item;
} else if (0 < opts->mainline)
- return error(_("Mainline was specified but commit %s is not a merge."),
+ return error(_("mainline was specified but commit %s is not a merge."),
oid_to_hex(&commit->object.oid));
else
parent = commit->parents->item;
return fast_forward_to(commit->object.oid.hash, head, unborn, opts);
if (parent && parse_commit(parent) < 0)
- /* TRANSLATORS: The first %s will be "revert" or
- "cherry-pick", the second %s a SHA1 */
+ /* TRANSLATORS: The first %s will be a "todo" command like
+ "revert" or "pick", the second %s a SHA1. */
return error(_("%s: cannot parse parent commit %s"),
- action_name(opts), oid_to_hex(&parent->object.oid));
+ command_to_string(command),
+ oid_to_hex(&parent->object.oid));
if (get_message(commit, &msg) != 0)
- return error(_("Cannot get commit message for %s"),
+ return error(_("cannot get commit message for %s"),
oid_to_hex(&commit->object.oid));
/*
* reverse of it if we are revert.
*/
- if (opts->action == REPLAY_REVERT) {
+ if (command == TODO_REVERT) {
base = commit;
base_label = msg.label;
next = parent;
}
}
- if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REPLAY_REVERT) {
+ if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) {
res = do_recursive_merge(base, next, base_label, next_label,
head, &msgbuf, opts);
if (res < 0)
return res;
- res |= write_message(&msgbuf, git_path_merge_msg());
+ res |= write_message(msgbuf.buf, msgbuf.len,
+ git_path_merge_msg(), 0);
} else {
struct commit_list *common = NULL;
struct commit_list *remotes = NULL;
- res = write_message(&msgbuf, git_path_merge_msg());
+ res = write_message(msgbuf.buf, msgbuf.len,
+ git_path_merge_msg(), 0);
commit_list_insert(base, &common);
commit_list_insert(next, &remotes);
- res |= try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts,
+ res |= try_merge_command(opts->strategy,
+ opts->xopts_nr, (const char **)opts->xopts,
common, sha1_to_hex(head), remotes);
free_commit_list(common);
free_commit_list(remotes);
}
+ strbuf_release(&msgbuf);
/*
* If the merge was clean or if it failed due to conflict, we write
* However, if the merge did not even start, then we don't want to
* write it at all.
*/
- if (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1) &&
+ if (command == TODO_PICK && !opts->no_commit && (res == 0 || res == 1) &&
update_ref(NULL, "CHERRY_PICK_HEAD", commit->object.oid.hash, NULL,
REF_NODEREF, UPDATE_REFS_MSG_ON_ERR))
res = -1;
- if (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1) &&
+ if (command == TODO_REVERT && ((opts->no_commit && res == 0) || res == 1) &&
update_ref(NULL, "REVERT_HEAD", commit->object.oid.hash, NULL,
REF_NODEREF, UPDATE_REFS_MSG_ON_ERR))
res = -1;
if (res) {
- error(opts->action == REPLAY_REVERT
+ error(command == TODO_REVERT
? _("could not revert %s... %s")
: _("could not apply %s... %s"),
- find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV),
- msg.subject);
+ short_commit_name(commit), msg.subject);
print_advice(res == 1, opts);
rerere(opts->allow_rerere_auto);
goto leave;
goto leave;
}
if (!opts->no_commit)
- res = run_git_commit(git_path_merge_msg(), opts, allow);
+ res = run_git_commit(opts->edit ? NULL : git_path_merge_msg(),
+ opts, allow, opts->edit, 0, 0);
leave:
free_message(commit, &msg);
if (read_index_preload(&the_index, NULL) < 0) {
rollback_lock_file(&index_lock);
return error(_("git %s: failed to read the index"),
- action_name(opts));
+ _(action_name(opts)));
}
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
if (the_index.cache_changed && index_fd >= 0) {
if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK)) {
rollback_lock_file(&index_lock);
return error(_("git %s: failed to refresh the index"),
- action_name(opts));
+ _(action_name(opts)));
}
}
rollback_lock_file(&index_lock);
return 0;
}
-static int format_todo(struct strbuf *buf, struct commit_list *todo_list,
- struct replay_opts *opts)
+struct todo_item {
+ enum todo_command command;
+ struct commit *commit;
+ const char *arg;
+ int arg_len;
+ size_t offset_in_buf;
+};
+
+struct todo_list {
+ struct strbuf buf;
+ struct todo_item *items;
+ int nr, alloc, current;
+};
+
+#define TODO_LIST_INIT { STRBUF_INIT }
+
+static void todo_list_release(struct todo_list *todo_list)
{
- struct commit_list *cur = NULL;
- const char *sha1_abbrev = NULL;
- const char *action_str = opts->action == REPLAY_REVERT ? "revert" : "pick";
- const char *subject;
- int subject_len;
+ strbuf_release(&todo_list->buf);
+ free(todo_list->items);
+ todo_list->items = NULL;
+ todo_list->nr = todo_list->alloc = 0;
+}
- for (cur = todo_list; cur; cur = cur->next) {
- const char *commit_buffer = get_commit_buffer(cur->item, NULL);
- sha1_abbrev = find_unique_abbrev(cur->item->object.oid.hash, DEFAULT_ABBREV);
- subject_len = find_commit_subject(commit_buffer, &subject);
- strbuf_addf(buf, "%s %s %.*s\n", action_str, sha1_abbrev,
- subject_len, subject);
- unuse_commit_buffer(cur->item, commit_buffer);
- }
- return 0;
+static struct todo_item *append_new_todo(struct todo_list *todo_list)
+{
+ ALLOC_GROW(todo_list->items, todo_list->nr + 1, todo_list->alloc);
+ return todo_list->items + todo_list->nr++;
}
-static struct commit *parse_insn_line(char *bol, char *eol, struct replay_opts *opts)
+static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
{
unsigned char commit_sha1[20];
- enum replay_action action;
char *end_of_object_name;
- int saved, status, padding;
-
- if (starts_with(bol, "pick")) {
- action = REPLAY_PICK;
- bol += strlen("pick");
- } else if (starts_with(bol, "revert")) {
- action = REPLAY_REVERT;
- bol += strlen("revert");
- } else
- return NULL;
+ int i, saved, status, padding;
+
+ /* left-trim */
+ bol += strspn(bol, " \t");
+
+ for (i = 0; i < ARRAY_SIZE(todo_command_strings); i++)
+ if (skip_prefix(bol, todo_command_strings[i], &bol)) {
+ item->command = i;
+ break;
+ }
+ if (i >= ARRAY_SIZE(todo_command_strings))
+ return -1;
/* Eat up extra spaces/ tabs before object name */
padding = strspn(bol, " \t");
if (!padding)
- return NULL;
+ return -1;
bol += padding;
- end_of_object_name = bol + strcspn(bol, " \t\n");
+ end_of_object_name = (char *) bol + strcspn(bol, " \t\n");
saved = *end_of_object_name;
*end_of_object_name = '\0';
status = get_sha1(bol, commit_sha1);
*end_of_object_name = saved;
- /*
- * Verify that the action matches up with the one in
- * opts; we don't support arbitrary instructions
- */
- if (action != opts->action) {
- if (action == REPLAY_REVERT)
- error((opts->action == REPLAY_REVERT)
- ? _("Cannot revert during another revert.")
- : _("Cannot revert during a cherry-pick."));
- else
- error((opts->action == REPLAY_REVERT)
- ? _("Cannot cherry-pick during a revert.")
- : _("Cannot cherry-pick during another cherry-pick."));
- return NULL;
- }
+ item->arg = end_of_object_name + strspn(end_of_object_name, " \t");
+ item->arg_len = (int)(eol - item->arg);
if (status < 0)
- return NULL;
+ return -1;
- return lookup_commit_reference(commit_sha1);
+ item->commit = lookup_commit_reference(commit_sha1);
+ return !item->commit;
}
-static int parse_insn_buffer(char *buf, struct commit_list **todo_list,
- struct replay_opts *opts)
+static int parse_insn_buffer(char *buf, struct todo_list *todo_list)
{
- struct commit_list **next = todo_list;
- struct commit *commit;
- char *p = buf;
- int i;
+ struct todo_item *item;
+ char *p = buf, *next_p;
+ int i, res = 0;
- for (i = 1; *p; i++) {
+ for (i = 1; *p; i++, p = next_p) {
char *eol = strchrnul(p, '\n');
- commit = parse_insn_line(p, eol, opts);
- if (!commit)
- return error(_("Could not parse line %d."), i);
- next = commit_list_append(commit, next);
- p = *eol ? eol + 1 : eol;
+
+ next_p = *eol ? eol + 1 /* skip LF */ : eol;
+
+ if (p != eol && eol[-1] == '\r')
+ eol--; /* strip Carriage Return */
+
+ item = append_new_todo(todo_list);
+ item->offset_in_buf = p - todo_list->buf.buf;
+ if (parse_insn_line(item, p, eol)) {
+ res = error(_("invalid line %d: %.*s"),
+ i, (int)(eol - p), p);
+ item->command = -1;
+ }
}
- if (!*todo_list)
- return error(_("No commits parsed."));
- return 0;
+ if (!todo_list->nr)
+ return error(_("no commits parsed."));
+ return res;
}
-static int read_populate_todo(struct commit_list **todo_list,
+static int read_populate_todo(struct todo_list *todo_list,
struct replay_opts *opts)
{
- struct strbuf buf = STRBUF_INIT;
+ const char *todo_file = get_todo_path(opts);
int fd, res;
- fd = open(git_path_todo_file(), O_RDONLY);
+ strbuf_reset(&todo_list->buf);
+ fd = open(todo_file, O_RDONLY);
if (fd < 0)
- return error_errno(_("Could not open %s"),
- git_path_todo_file());
- if (strbuf_read(&buf, fd, 0) < 0) {
+ return error_errno(_("could not open '%s'"), todo_file);
+ if (strbuf_read(&todo_list->buf, fd, 0) < 0) {
close(fd);
- strbuf_release(&buf);
- return error(_("Could not read %s."), git_path_todo_file());
+ return error(_("could not read '%s'."), todo_file);
}
close(fd);
- res = parse_insn_buffer(buf.buf, todo_list, opts);
- strbuf_release(&buf);
+ res = parse_insn_buffer(todo_list->buf.buf, todo_list);
if (res)
- return error(_("Unusable instruction sheet: %s"),
- git_path_todo_file());
+ return error(_("unusable instruction sheet: '%s'"), todo_file);
+
+ if (!is_rebase_i(opts)) {
+ enum todo_command valid =
+ opts->action == REPLAY_PICK ? TODO_PICK : TODO_REVERT;
+ int i;
+
+ for (i = 0; i < todo_list->nr; i++)
+ if (valid == todo_list->items[i].command)
+ continue;
+ else if (valid == TODO_PICK)
+ return error(_("cannot cherry-pick during a revert."));
+ else
+ return error(_("cannot revert during a cherry-pick."));
+ }
+
+ return 0;
+}
+
+static int git_config_string_dup(char **dest,
+ const char *var, const char *value)
+{
+ if (!value)
+ return config_error_nonbool(var);
+ free(*dest);
+ *dest = xstrdup(value);
return 0;
}
else if (!strcmp(key, "options.mainline"))
opts->mainline = git_config_int(key, value);
else if (!strcmp(key, "options.strategy"))
- git_config_string(&opts->strategy, key, value);
+ git_config_string_dup(&opts->strategy, key, value);
else if (!strcmp(key, "options.gpg-sign"))
- git_config_string(&opts->gpg_sign, key, value);
+ git_config_string_dup(&opts->gpg_sign, key, value);
else if (!strcmp(key, "options.strategy-option")) {
ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
opts->xopts[opts->xopts_nr++] = xstrdup(value);
} else
- return error(_("Invalid key: %s"), key);
+ return error(_("invalid key: %s"), key);
if (!error_flag)
- return error(_("Invalid value for %s: %s"), key, value);
+ return error(_("invalid value for %s: %s"), key, value);
return 0;
}
-static int read_populate_opts(struct replay_opts **opts)
+static int read_populate_opts(struct replay_opts *opts)
{
+ if (is_rebase_i(opts)) {
+ struct strbuf buf = STRBUF_INIT;
+
+ if (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {
+ if (!starts_with(buf.buf, "-S"))
+ strbuf_reset(&buf);
+ else {
+ free(opts->gpg_sign);
+ opts->gpg_sign = xstrdup(buf.buf + 2);
+ }
+ }
+ strbuf_release(&buf);
+
+ return 0;
+ }
+
if (!file_exists(git_path_opts_file()))
return 0;
/*
* about this case, though, because we wrote that file ourselves, so we
* are pretty certain that it is syntactically correct.
*/
- if (git_config_from_file(populate_opts_cb, git_path_opts_file(), *opts) < 0)
- return error(_("Malformed options sheet: %s"),
+ if (git_config_from_file(populate_opts_cb, git_path_opts_file(), opts) < 0)
+ return error(_("malformed options sheet: '%s'"),
git_path_opts_file());
return 0;
}
-static int walk_revs_populate_todo(struct commit_list **todo_list,
+static int walk_revs_populate_todo(struct todo_list *todo_list,
struct replay_opts *opts)
{
+ enum todo_command command = opts->action == REPLAY_PICK ?
+ TODO_PICK : TODO_REVERT;
+ const char *command_string = todo_command_strings[command];
struct commit *commit;
- struct commit_list **next;
if (prepare_revs(opts))
return -1;
- next = todo_list;
- while ((commit = get_revision(opts->revs)))
- next = commit_list_append(commit, next);
+ while ((commit = get_revision(opts->revs))) {
+ struct todo_item *item = append_new_todo(todo_list);
+ const char *commit_buffer = get_commit_buffer(commit, NULL);
+ const char *subject;
+ int subject_len;
+
+ item->command = command;
+ item->commit = commit;
+ item->arg = NULL;
+ item->arg_len = 0;
+ item->offset_in_buf = todo_list->buf.len;
+ subject_len = find_commit_subject(commit_buffer, &subject);
+ strbuf_addf(&todo_list->buf, "%s %s %.*s\n", command_string,
+ short_commit_name(commit), subject_len, subject);
+ unuse_commit_buffer(commit, commit_buffer);
+ }
return 0;
}
return -1;
}
else if (mkdir(git_path_seq_dir(), 0777) < 0)
- return error_errno(_("Could not create sequencer directory %s"),
+ return error_errno(_("could not create sequencer directory '%s'"),
git_path_seq_dir());
return 0;
}
fd = hold_lock_file_for_update(&head_lock, git_path_head_file(), 0);
if (fd < 0) {
rollback_lock_file(&head_lock);
- return error_errno(_("Could not lock HEAD"));
+ return error_errno(_("could not lock HEAD"));
}
strbuf_addf(&buf, "%s\n", head);
if (write_in_full(fd, buf.buf, buf.len) < 0) {
rollback_lock_file(&head_lock);
- return error_errno(_("Could not write to %s"),
+ return error_errno(_("could not write to '%s'"),
git_path_head_file());
}
if (commit_lock_file(&head_lock) < 0) {
rollback_lock_file(&head_lock);
- return error(_("Error wrapping up %s."), git_path_head_file());
+ return error(_("failed to finalize '%s'."), git_path_head_file());
}
return 0;
}
return reset_for_rollback(head_sha1);
}
-static int sequencer_rollback(struct replay_opts *opts)
+int sequencer_rollback(struct replay_opts *opts)
{
FILE *f;
unsigned char sha1[20];
return rollback_single_pick();
}
if (!f)
- return error_errno(_("cannot open %s"), git_path_head_file());
+ return error_errno(_("cannot open '%s'"), git_path_head_file());
if (strbuf_getline_lf(&buf, f)) {
- error(_("cannot read %s: %s"), git_path_head_file(),
+ error(_("cannot read '%s': %s"), git_path_head_file(),
ferror(f) ? strerror(errno) : _("unexpected end of file"));
fclose(f);
goto fail;
}
if (reset_for_rollback(sha1))
goto fail;
- remove_sequencer_state();
strbuf_release(&buf);
- return 0;
+ return sequencer_remove_state(opts);
fail:
strbuf_release(&buf);
return -1;
}
-static int save_todo(struct commit_list *todo_list, struct replay_opts *opts)
+static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
{
static struct lock_file todo_lock;
- struct strbuf buf = STRBUF_INIT;
- int fd;
+ const char *todo_path = get_todo_path(opts);
+ int next = todo_list->current, offset, fd;
- fd = hold_lock_file_for_update(&todo_lock, git_path_todo_file(), 0);
+ fd = hold_lock_file_for_update(&todo_lock, todo_path, 0);
if (fd < 0)
- return error_errno(_("Could not lock '%s'"),
- git_path_todo_file());
- if (format_todo(&buf, todo_list, opts) < 0) {
- strbuf_release(&buf);
- return error(_("Could not format %s."), git_path_todo_file());
- }
- if (write_in_full(fd, buf.buf, buf.len) < 0) {
- strbuf_release(&buf);
- return error_errno(_("Could not write to %s"),
- git_path_todo_file());
- }
- if (commit_lock_file(&todo_lock) < 0) {
- strbuf_release(&buf);
- return error(_("Error wrapping up %s."), git_path_todo_file());
- }
- strbuf_release(&buf);
+ return error_errno(_("could not lock '%s'"), todo_path);
+ offset = next < todo_list->nr ?
+ todo_list->items[next].offset_in_buf : todo_list->buf.len;
+ if (write_in_full(fd, todo_list->buf.buf + offset,
+ todo_list->buf.len - offset) < 0)
+ return error_errno(_("could not write to '%s'"), todo_path);
+ if (commit_lock_file(&todo_lock) < 0)
+ return error(_("failed to finalize '%s'."), todo_path);
return 0;
}
return res;
}
-static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
+static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
{
- struct commit_list *cur;
int res;
setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
if (read_and_refresh_cache(opts))
return -1;
- for (cur = todo_list; cur; cur = cur->next) {
- if (save_todo(cur, opts))
+ while (todo_list->current < todo_list->nr) {
+ struct todo_item *item = todo_list->items + todo_list->current;
+ if (save_todo(todo_list, opts))
return -1;
- res = do_pick_commit(cur->item, opts);
+ res = do_pick_commit(item->command, item->commit, opts);
+ todo_list->current++;
if (res)
return res;
}
* Sequence of picks finished successfully; cleanup by
* removing the .git/sequencer directory
*/
- remove_sequencer_state();
- return 0;
+ return sequencer_remove_state(opts);
}
static int continue_single_pick(void)
return run_command_v_opt(argv, RUN_GIT_CMD);
}
-static int sequencer_continue(struct replay_opts *opts)
+int sequencer_continue(struct replay_opts *opts)
{
- struct commit_list *todo_list = NULL;
+ struct todo_list todo_list = TODO_LIST_INIT;
+ int res;
- if (!file_exists(git_path_todo_file()))
+ if (read_and_refresh_cache(opts))
+ return -1;
+
+ if (!file_exists(get_todo_path(opts)))
return continue_single_pick();
- if (read_populate_opts(&opts) ||
- read_populate_todo(&todo_list, opts))
+ if (read_populate_opts(opts))
return -1;
+ if ((res = read_populate_todo(&todo_list, opts)))
+ goto release_todo_list;
/* Verify that the conflict has been resolved */
if (file_exists(git_path_cherry_pick_head()) ||
file_exists(git_path_revert_head())) {
- int ret = continue_single_pick();
- if (ret)
- return ret;
+ res = continue_single_pick();
+ if (res)
+ goto release_todo_list;
}
- if (index_differs_from("HEAD", 0))
- return error_dirty_index(opts);
- todo_list = todo_list->next;
- return pick_commits(todo_list, opts);
+ if (index_differs_from("HEAD", 0, 0)) {
+ res = error_dirty_index(opts);
+ goto release_todo_list;
+ }
+ todo_list.current++;
+ res = pick_commits(&todo_list, opts);
+release_todo_list:
+ todo_list_release(&todo_list);
+ return res;
}
static int single_pick(struct commit *cmit, struct replay_opts *opts)
{
setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
- return do_pick_commit(cmit, opts);
+ return do_pick_commit(opts->action == REPLAY_PICK ?
+ TODO_PICK : TODO_REVERT, cmit, opts);
}
int sequencer_pick_revisions(struct replay_opts *opts)
{
- struct commit_list *todo_list = NULL;
+ struct todo_list todo_list = TODO_LIST_INIT;
unsigned char sha1[20];
- int i;
-
- if (opts->subcommand == REPLAY_NONE)
- assert(opts->revs);
+ int i, res;
+ assert(opts->revs);
if (read_and_refresh_cache(opts))
return -1;
- /*
- * Decide what to do depending on the arguments; a fresh
- * cherry-pick should be handled differently from an existing
- * one that is being continued
- */
- if (opts->subcommand == REPLAY_REMOVE_STATE) {
- remove_sequencer_state();
- return 0;
- }
- if (opts->subcommand == REPLAY_ROLLBACK)
- return sequencer_rollback(opts);
- if (opts->subcommand == REPLAY_CONTINUE)
- return sequencer_continue(opts);
-
for (i = 0; i < opts->revs->pending.nr; i++) {
unsigned char sha1[20];
const char *name = opts->revs->pending.objects[i].name;
create_seq_dir() < 0)
return -1;
if (get_sha1("HEAD", sha1) && (opts->action == REPLAY_REVERT))
- return error(_("Can't revert as initial commit"));
+ return error(_("can't revert as initial commit"));
if (save_head(sha1_to_hex(sha1)))
return -1;
if (save_opts(opts))
return -1;
- return pick_commits(todo_list, opts);
+ res = pick_commits(&todo_list, opts);
+ todo_list_release(&todo_list);
+ return res;
}
void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag)
#ifndef SEQUENCER_H
#define SEQUENCER_H
-#define SEQ_DIR "sequencer"
-#define SEQ_HEAD_FILE "sequencer/head"
-#define SEQ_TODO_FILE "sequencer/todo"
-#define SEQ_OPTS_FILE "sequencer/opts"
+const char *git_path_seq_dir(void);
#define APPEND_SIGNOFF_DEDUP (1u << 0)
REPLAY_PICK
};
-enum replay_subcommand {
- REPLAY_NONE,
- REPLAY_REMOVE_STATE,
- REPLAY_CONTINUE,
- REPLAY_ROLLBACK
-};
-
struct replay_opts {
enum replay_action action;
- enum replay_subcommand subcommand;
/* Boolean options */
int edit;
int mainline;
- const char *gpg_sign;
+ char *gpg_sign;
/* Merge strategy */
- const char *strategy;
- const char **xopts;
+ char *strategy;
+ char **xopts;
size_t xopts_nr, xopts_alloc;
/* Only used by REPLAY_NONE */
struct rev_info *revs;
};
+#define REPLAY_OPTS_INIT { -1 }
int sequencer_pick_revisions(struct replay_opts *opts);
+int sequencer_continue(struct replay_opts *opts);
+int sequencer_rollback(struct replay_opts *opts);
+int sequencer_remove_state(struct replay_opts *opts);
extern const char sign_off_header[];
int fd;
path = xstrfmt("%s/info/alternates", relative_base);
- fd = git_open_noatime(path);
+ fd = git_open(path);
free(path);
if (fd < 0)
return;
struct pack_idx_header *hdr;
size_t idx_size;
uint32_t version, nr, i, *index;
- int fd = git_open_noatime(path);
+ int fd = git_open(path);
struct stat st;
if (fd < 0)
while (pack_max_fds <= pack_open_fds && close_one_pack())
; /* nothing */
- p->pack_fd = git_open_noatime(p->pack_name);
+ p->pack_fd = git_open(p->pack_name);
if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
return -1;
pack_open_fds++;
strbuf_release(&path);
}
+static int approximate_object_count_valid;
+
+/*
+ * Give a fast, rough count of the number of objects in the repository. This
+ * ignores loose objects completely. If you have a lot of them, then either
+ * you should repack because your performance will be awful, or they are
+ * all unreachable objects about to be pruned, in which case they're not really
+ * interesting as a measure of repo size in the first place.
+ */
+unsigned long approximate_object_count(void)
+{
+ static unsigned long count;
+ if (!approximate_object_count_valid) {
+ struct packed_git *p;
+
+ prepare_packed_git();
+ count = 0;
+ for (p = packed_git; p; p = p->next) {
+ if (open_pack_index(p))
+ continue;
+ count += p->num_objects;
+ }
+ }
+ return count;
+}
+
static void *get_next_packed_git(const void *p)
{
return ((const struct packed_git *)p)->next;
void reprepare_packed_git(void)
{
+ approximate_object_count_valid = 0;
prepare_packed_git_run_once = 0;
prepare_packed_git();
}
return hashcmp(sha1, real_sha1) ? -1 : 0;
}
-int git_open_noatime(const char *name)
+int git_open(const char *name)
{
- static int sha1_file_open_flag = O_NOATIME;
+ static int sha1_file_open_flag = O_NOATIME | O_CLOEXEC;
for (;;) {
int fd;
if (fd >= 0)
return fd;
- /* Might the failure be due to O_NOATIME? */
- if (errno != ENOENT && sha1_file_open_flag) {
- sha1_file_open_flag = 0;
+ /* Try again w/o O_CLOEXEC: the kernel might not support it */
+ if ((sha1_file_open_flag & O_CLOEXEC) && errno == EINVAL) {
+ sha1_file_open_flag &= ~O_CLOEXEC;
continue;
}
+ /* Might the failure be due to O_NOATIME? */
+ if (errno != ENOENT && (sha1_file_open_flag & O_NOATIME)) {
+ sha1_file_open_flag &= ~O_NOATIME;
+ continue;
+ }
return -1;
}
}
struct alternate_object_database *alt;
int most_interesting_errno;
- fd = git_open_noatime(sha1_file_name(sha1));
+ fd = git_open(sha1_file_name(sha1));
if (fd >= 0)
return fd;
most_interesting_errno = errno;
prepare_alt_odb();
for (alt = alt_odb_list; alt; alt = alt->next) {
const char *path = alt_sha1_path(alt, sha1);
- fd = git_open_noatime(path);
+ fd = git_open(path);
if (fd >= 0)
return fd;
if (most_interesting_errno == ENOENT)
return has_sha1_file(oid->hash);
}
+int has_object_file_with_flags(const struct object_id *oid, int flags)
+{
+ return has_sha1_file_with_flags(oid->hash, flags);
+}
+
static void check_tree(const void *buf, size_t size)
{
struct tree_desc desc;
return ret;
}
+/*
+ * Return the slot of the most-significant bit set in "val". There are various
+ * ways to do this quickly with fls() or __builtin_clzl(), but speed is
+ * probably not a big deal here.
+ */
+static unsigned msb(unsigned long val)
+{
+ unsigned r = 0;
+ while (val >>= 1)
+ r++;
+ return r;
+}
+
int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
{
int status, exists;
+ if (len < 0) {
+ unsigned long count = approximate_object_count();
+ /*
+ * Add one because the MSB only tells us the highest bit set,
+ * not including the value of all the _other_ bits (so "15"
+ * is only one off of 2^4, but the MSB is the 3rd bit.
+ */
+ len = msb(count) + 1;
+ /*
+ * We now know we have on the order of 2^len objects, which
+ * expects a collision at 2^(len/2). But we also care about hex
+ * chars, not bits, and there are 4 bits per hex. So all
+ * together we need to divide by 2; but we also want to round
+ * odd numbers up, hence adding one before dividing.
+ */
+ len = (len + 1) / 2;
+ /*
+ * For very small repos, we stick with our regular fallback.
+ */
+ if (len < FALLBACK_DEFAULT_ABBREV)
+ len = FALLBACK_DEFAULT_ABBREV;
+ }
+
sha1_to_hex_r(hex, sha1);
if (len == 40 || !len)
return 40;
const char *find_unique_abbrev(const unsigned char *sha1, int len)
{
- static char hex[GIT_SHA1_HEXSZ + 1];
+ static int bufno;
+ static char hexbuffer[4][GIT_SHA1_HEXSZ + 1];
+ char *hex = hexbuffer[3 & ++bufno];
find_unique_abbrev_r(hex, sha1, len);
return hex;
}
{
int fd = *(int *)cb;
if (graft->nr_parent == -1)
- packet_write(fd, "shallow %s\n", oid_to_hex(&graft->oid));
+ packet_write_fmt(fd, "shallow %s\n", oid_to_hex(&graft->oid));
return 0;
}
As the names depend on the tests' file names, it is safe to
run the tests with this option in parallel.
+--verbose-log::
+ Write verbose output to the same logfile as `--tee`, but do
+ _not_ write it to stdout. Unlike `--tee --verbose`, this option
+ is safe to use when stdout is being consumed by a TAP parser
+ like `prove`. Implies `--tee` and `--verbose`.
+
--with-dashes::
By default tests are run without dashed forms of
commands (like git-commit) in the PATH (it only uses
{
struct index_state istate;
struct cache_tree *another = cache_tree();
+ setup_git_directory();
if (read_cache() < 0)
die("unable to read index file");
istate = the_index;
int cmd_main(int ac, const char **av)
{
+ setup_git_directory();
hold_locked_index(&index_lock, 1);
if (read_cache() < 0)
die("unable to read index file");
--- /dev/null
+#!/bin/sh
+
+test_description='performance of tag-following with many tags
+
+This tests a fairly pathological case, so rather than rely on a real-world
+case, we will construct our own repository. The situation is roughly as
+follows.
+
+The parent repository has a large number of tags which are disconnected from
+the rest of history. That makes them candidates for tag-following, but we never
+actually grab them (and thus they will impact each subsequent fetch).
+
+The child repository is a clone of parent, without the tags, and is at least
+one commit behind the parent (meaning that we will fetch one object and then
+examine the tags to see if they need followed). Furthermore, it has a large
+number of packs.
+
+The exact values of "large" here are somewhat arbitrary; I picked values that
+start to show a noticeable performance problem on my machine, but without
+taking too long to set up and run the tests.
+'
+. ./perf-lib.sh
+
+# make a long nonsense history on branch $1, consisting of $2 commits, each
+# with a unique file pointing to the blob at $2.
+create_history () {
+ perl -le '
+ my ($branch, $n, $blob) = @ARGV;
+ for (1..$n) {
+ print "commit refs/heads/$branch";
+ print "committer nobody <nobody@example.com> now";
+ print "data 4";
+ print "foo";
+ print "M 100644 $blob $_";
+ }
+ ' "$@" |
+ git fast-import --date-format=now
+}
+
+# make a series of tags, one per commit in the revision range given by $@
+create_tags () {
+ git rev-list "$@" |
+ perl -lne 'print "create refs/tags/$. $_"' |
+ git update-ref --stdin
+}
+
+# create $1 nonsense packs, each with a single blob
+create_packs () {
+ perl -le '
+ my ($n) = @ARGV;
+ for (1..$n) {
+ print "blob";
+ print "data <<EOF";
+ print "$_";
+ print "EOF";
+ }
+ ' "$@" |
+ git fast-import &&
+
+ git cat-file --batch-all-objects --batch-check='%(objectname)' |
+ while read sha1
+ do
+ echo $sha1 | git pack-objects .git/objects/pack/pack
+ done
+}
+
+test_expect_success 'create parent and child' '
+ git init parent &&
+ git -C parent commit --allow-empty -m base &&
+ git clone parent child &&
+ git -C parent commit --allow-empty -m trigger-fetch
+'
+
+test_expect_success 'populate parent tags' '
+ (
+ cd parent &&
+ blob=$(echo content | git hash-object -w --stdin) &&
+ create_history cruft 3000 $blob &&
+ create_tags cruft &&
+ git branch -D cruft
+ )
+'
+
+test_expect_success 'create child packs' '
+ (
+ cd child &&
+ git config gc.auto 0 &&
+ git config gc.autopacklimit 0 &&
+ create_packs 500
+ )
+'
+
+test_perf 'fetch' '
+ # make sure there is something to fetch on each iteration
+ git -C child update-ref -d refs/remotes/origin/master &&
+ git -C child fetch
+'
+
+test_done
git add doublewarn &&
git commit -m "nowarn" &&
for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >doublewarn &&
- test $(git add doublewarn 2>&1 | grep "CRLF will be replaced by LF" | wc -l) = 1
+ git add doublewarn 2>err &&
+ if test_have_prereq C_LOCALE_OUTPUT
+ then
+ test $(grep "CRLF will be replaced by LF" err | wc -l) = 1
+ fi
'
. ./test-lib.sh
-cat <<EOF >rot13.sh
+TEST_ROOT="$(pwd)"
+
+cat <<EOF >"$TEST_ROOT/rot13.sh"
#!$SHELL_PATH
tr \
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \
'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
EOF
-chmod +x rot13.sh
+chmod +x "$TEST_ROOT/rot13.sh"
+
+generate_random_characters () {
+ LEN=$1
+ NAME=$2
+ test-genrandom some-seed $LEN |
+ perl -pe "s/./chr((ord($&) % 26) + ord('a'))/sge" >"$TEST_ROOT/$NAME"
+}
+
+file_size () {
+ cat "$1" | wc -c | sed "s/^[ ]*//"
+}
+
+filter_git () {
+ rm -f rot13-filter.log &&
+ git "$@" 2>git-stderr.log &&
+ rm -f git-stderr.log
+}
+
+# Compare two files and ensure that `clean` and `smudge` respectively are
+# called at least once if specified in the `expect` file. The actual
+# invocation count is not relevant because their number can vary.
+# c.f. http://public-inbox.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/
+test_cmp_count () {
+ expect=$1
+ actual=$2
+ for FILE in "$expect" "$actual"
+ do
+ sort "$FILE" | uniq -c | sed "s/^[ ]*//" |
+ sed "s/^\([0-9]\) IN: clean/x IN: clean/" |
+ sed "s/^\([0-9]\) IN: smudge/x IN: smudge/" >"$FILE.tmp" &&
+ mv "$FILE.tmp" "$FILE"
+ done &&
+ test_cmp "$expect" "$actual"
+}
+
+# Compare two files but exclude all `clean` invocations because Git can
+# call `clean` zero or more times.
+# c.f. http://public-inbox.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/
+test_cmp_exclude_clean () {
+ expect=$1
+ actual=$2
+ for FILE in "$expect" "$actual"
+ do
+ grep -v "IN: clean" "$FILE" >"$FILE.tmp" &&
+ mv "$FILE.tmp" "$FILE"
+ done &&
+ test_cmp "$expect" "$actual"
+}
+
+# Check that the contents of two files are equal and that their rot13 version
+# is equal to the committed content.
+test_cmp_committed_rot13 () {
+ test_cmp "$1" "$2" &&
+ "$TEST_ROOT/rot13.sh" <"$1" >expected &&
+ git cat-file blob :"$2" >actual &&
+ test_cmp expected actual
+}
test_expect_success setup '
git config filter.rot13.smudge ./rot13.sh &&
cat test >test.i &&
git add test test.t test.i &&
rm -f test test.t test.i &&
- git checkout -- test test.t test.i
+ git checkout -- test test.t test.i &&
+
+ echo "content-test2" >test2.o &&
+ echo "content-test3 - filename with special characters" >"test3 '\''sq'\'',\$x.o"
'
script='s/^\$Id: \([0-9a-f]*\) \$/\1/p'
test_expect_success check '
- cmp test.o test &&
- cmp test.o test.t &&
+ test_cmp test.o test &&
+ test_cmp test.o test.t &&
# ident should be stripped in the repository
git diff --raw --exit-code :test :test.i &&
embedded=$(sed -ne "$script" test.i) &&
test "z$id" = "z$embedded" &&
- git cat-file blob :test.t > test.r &&
+ git cat-file blob :test.t >test.r &&
- ./rot13.sh < test.o > test.t &&
- cmp test.r test.t
+ ./rot13.sh <test.o >test.t &&
+ test_cmp test.r test.t
'
# If an expanded ident ever gets into the repository, we want to make sure that
# delete the files and check them out again, using a smudge filter
# that will count the args and echo the command-line back to us
- git config filter.argc.smudge "sh ./argc.sh %f" &&
+ test_config filter.argc.smudge "sh ./argc.sh %f" &&
rm "$normal" "$special" &&
git checkout -- "$normal" "$special" &&
test_cmp expect "$special" &&
# do the same thing, but with more args in the filter expression
- git config filter.argc.smudge "sh ./argc.sh %f --my-extra-arg" &&
+ test_config filter.argc.smudge "sh ./argc.sh %f --my-extra-arg" &&
rm "$normal" "$special" &&
git checkout -- "$normal" "$special" &&
'
test_expect_success 'required filter should filter data' '
- git config filter.required.smudge ./rot13.sh &&
- git config filter.required.clean ./rot13.sh &&
- git config filter.required.required true &&
+ test_config filter.required.smudge ./rot13.sh &&
+ test_config filter.required.clean ./rot13.sh &&
+ test_config filter.required.required true &&
echo "*.r filter=required" >.gitattributes &&
rm -f test.r &&
git checkout -- test.r &&
- cmp test.o test.r &&
+ test_cmp test.o test.r &&
./rot13.sh <test.o >expected &&
git cat-file blob :test.r >actual &&
- cmp expected actual
+ test_cmp expected actual
'
test_expect_success 'required filter smudge failure' '
- git config filter.failsmudge.smudge false &&
- git config filter.failsmudge.clean cat &&
- git config filter.failsmudge.required true &&
+ test_config filter.failsmudge.smudge false &&
+ test_config filter.failsmudge.clean cat &&
+ test_config filter.failsmudge.required true &&
echo "*.fs filter=failsmudge" >.gitattributes &&
'
test_expect_success 'required filter clean failure' '
- git config filter.failclean.smudge cat &&
- git config filter.failclean.clean false &&
- git config filter.failclean.required true &&
+ test_config filter.failclean.smudge cat &&
+ test_config filter.failclean.clean false &&
+ test_config filter.failclean.required true &&
echo "*.fc filter=failclean" >.gitattributes &&
'
test_expect_success 'filtering large input to small output should use little memory' '
- git config filter.devnull.clean "cat >/dev/null" &&
- git config filter.devnull.required true &&
+ test_config filter.devnull.clean "cat >/dev/null" &&
+ test_config filter.devnull.required true &&
for i in $(test_seq 1 30); do printf "%1048576d" 1; done >30MB &&
echo "30MB filter=devnull" >.gitattributes &&
GIT_MMAP_LIMIT=1m GIT_ALLOC_LIMIT=1m git add 30MB
test_expect_success 'filter that does not read is fine' '
test-genrandom foo $((128 * 1024 + 1)) >big &&
echo "big filter=epipe" >.gitattributes &&
- git config filter.epipe.clean "echo xyzzy" &&
+ test_config filter.epipe.clean "echo xyzzy" &&
git add big &&
git cat-file blob :big >actual &&
echo xyzzy >expect &&
'
test_expect_success EXPENSIVE 'filter large file' '
- git config filter.largefile.smudge cat &&
- git config filter.largefile.clean cat &&
+ test_config filter.largefile.smudge cat &&
+ test_config filter.largefile.clean cat &&
for i in $(test_seq 1 2048); do printf "%1048576d" 1; done >2GB &&
echo "2GB filter=largefile" >.gitattributes &&
git add 2GB 2>err &&
- ! test -s err &&
+ test_must_be_empty err &&
rm -f 2GB &&
git checkout -- 2GB 2>err &&
- ! test -s err
+ test_must_be_empty err
'
test_expect_success "filter: clean empty file" '
- git config filter.in-repo-header.clean "echo cleaned && cat" &&
- git config filter.in-repo-header.smudge "sed 1d" &&
+ test_config filter.in-repo-header.clean "echo cleaned && cat" &&
+ test_config filter.in-repo-header.smudge "sed 1d" &&
echo "empty-in-worktree filter=in-repo-header" >>.gitattributes &&
>empty-in-worktree &&
'
test_expect_success "filter: smudge empty file" '
- git config filter.empty-in-repo.clean "cat >/dev/null" &&
- git config filter.empty-in-repo.smudge "echo smudged && cat" &&
+ test_config filter.empty-in-repo.clean "cat >/dev/null" &&
+ test_config filter.empty-in-repo.smudge "echo smudged && cat" &&
echo "empty-in-repo filter=empty-in-repo" >>.gitattributes &&
echo dead data walking >empty-in-repo &&
test_line_count = 0 count
'
+test_expect_success PERL 'required process filter should filter data' '
+ test_config_global filter.protocol.process "$TEST_DIRECTORY/t0021/rot13-filter.pl clean smudge" &&
+ test_config_global filter.protocol.required true &&
+ rm -rf repo &&
+ mkdir repo &&
+ (
+ cd repo &&
+ git init &&
+
+ echo "git-stderr.log" >.gitignore &&
+ echo "*.r filter=protocol" >.gitattributes &&
+ git add . &&
+ git commit . -m "test commit 1" &&
+ git branch empty-branch &&
+
+ cp "$TEST_ROOT/test.o" test.r &&
+ cp "$TEST_ROOT/test2.o" test2.r &&
+ mkdir testsubdir &&
+ cp "$TEST_ROOT/test3 '\''sq'\'',\$x.o" "testsubdir/test3 '\''sq'\'',\$x.r" &&
+ >test4-empty.r &&
+
+ S=$(file_size test.r) &&
+ S2=$(file_size test2.r) &&
+ S3=$(file_size "testsubdir/test3 '\''sq'\'',\$x.r") &&
+
+ filter_git add . &&
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ IN: clean test.r $S [OK] -- OUT: $S . [OK]
+ IN: clean test2.r $S2 [OK] -- OUT: $S2 . [OK]
+ IN: clean test4-empty.r 0 [OK] -- OUT: 0 [OK]
+ IN: clean testsubdir/test3 '\''sq'\'',\$x.r $S3 [OK] -- OUT: $S3 . [OK]
+ STOP
+ EOF
+ test_cmp_count expected.log rot13-filter.log &&
+
+ filter_git commit . -m "test commit 2" &&
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ IN: clean test.r $S [OK] -- OUT: $S . [OK]
+ IN: clean test2.r $S2 [OK] -- OUT: $S2 . [OK]
+ IN: clean test4-empty.r 0 [OK] -- OUT: 0 [OK]
+ IN: clean testsubdir/test3 '\''sq'\'',\$x.r $S3 [OK] -- OUT: $S3 . [OK]
+ IN: clean test.r $S [OK] -- OUT: $S . [OK]
+ IN: clean test2.r $S2 [OK] -- OUT: $S2 . [OK]
+ IN: clean test4-empty.r 0 [OK] -- OUT: 0 [OK]
+ IN: clean testsubdir/test3 '\''sq'\'',\$x.r $S3 [OK] -- OUT: $S3 . [OK]
+ STOP
+ EOF
+ test_cmp_count expected.log rot13-filter.log &&
+
+ rm -f test2.r "testsubdir/test3 '\''sq'\'',\$x.r" &&
+
+ filter_git checkout --quiet --no-progress . &&
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK]
+ IN: smudge testsubdir/test3 '\''sq'\'',\$x.r $S3 [OK] -- OUT: $S3 . [OK]
+ STOP
+ EOF
+ test_cmp_exclude_clean expected.log rot13-filter.log &&
+
+ filter_git checkout --quiet --no-progress empty-branch &&
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ IN: clean test.r $S [OK] -- OUT: $S . [OK]
+ STOP
+ EOF
+ test_cmp_exclude_clean expected.log rot13-filter.log &&
+
+ filter_git checkout --quiet --no-progress master &&
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ IN: smudge test.r $S [OK] -- OUT: $S . [OK]
+ IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK]
+ IN: smudge test4-empty.r 0 [OK] -- OUT: 0 [OK]
+ IN: smudge testsubdir/test3 '\''sq'\'',\$x.r $S3 [OK] -- OUT: $S3 . [OK]
+ STOP
+ EOF
+ test_cmp_exclude_clean expected.log rot13-filter.log &&
+
+ test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.r &&
+ test_cmp_committed_rot13 "$TEST_ROOT/test2.o" test2.r &&
+ test_cmp_committed_rot13 "$TEST_ROOT/test3 '\''sq'\'',\$x.o" "testsubdir/test3 '\''sq'\'',\$x.r"
+ )
+'
+
+test_expect_success PERL 'required process filter takes precedence' '
+ test_config_global filter.protocol.clean false &&
+ test_config_global filter.protocol.process "$TEST_DIRECTORY/t0021/rot13-filter.pl clean" &&
+ test_config_global filter.protocol.required true &&
+ rm -rf repo &&
+ mkdir repo &&
+ (
+ cd repo &&
+ git init &&
+
+ echo "*.r filter=protocol" >.gitattributes &&
+ cp "$TEST_ROOT/test.o" test.r &&
+ S=$(file_size test.r) &&
+
+ # Check that the process filter is invoked here
+ filter_git add . &&
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ IN: clean test.r $S [OK] -- OUT: $S . [OK]
+ STOP
+ EOF
+ test_cmp_count expected.log rot13-filter.log
+ )
+'
+
+test_expect_success PERL 'required process filter should be used only for "clean" operation only' '
+ test_config_global filter.protocol.process "$TEST_DIRECTORY/t0021/rot13-filter.pl clean" &&
+ rm -rf repo &&
+ mkdir repo &&
+ (
+ cd repo &&
+ git init &&
+
+ echo "*.r filter=protocol" >.gitattributes &&
+ cp "$TEST_ROOT/test.o" test.r &&
+ S=$(file_size test.r) &&
+
+ filter_git add . &&
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ IN: clean test.r $S [OK] -- OUT: $S . [OK]
+ STOP
+ EOF
+ test_cmp_count expected.log rot13-filter.log &&
+
+ rm test.r &&
+
+ filter_git checkout --quiet --no-progress . &&
+ # If the filter would be used for "smudge", too, we would see
+ # "IN: smudge test.r 57 [OK] -- OUT: 57 . [OK]" here
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ STOP
+ EOF
+ test_cmp_exclude_clean expected.log rot13-filter.log
+ )
+'
+
+test_expect_success PERL 'required process filter should process multiple packets' '
+ test_config_global filter.protocol.process "$TEST_DIRECTORY/t0021/rot13-filter.pl clean smudge" &&
+ test_config_global filter.protocol.required true &&
+
+ rm -rf repo &&
+ mkdir repo &&
+ (
+ cd repo &&
+ git init &&
+
+ # Generate data requiring 1, 2, 3 packets
+ S=65516 && # PKTLINE_DATA_MAXLEN -> Maximal size of a packet
+ generate_random_characters $(($S )) 1pkt_1__.file &&
+ generate_random_characters $(($S +1)) 2pkt_1+1.file &&
+ generate_random_characters $(($S*2-1)) 2pkt_2-1.file &&
+ generate_random_characters $(($S*2 )) 2pkt_2__.file &&
+ generate_random_characters $(($S*2+1)) 3pkt_2+1.file &&
+
+ for FILE in "$TEST_ROOT"/*.file
+ do
+ cp "$FILE" . &&
+ "$TEST_ROOT/rot13.sh" <"$FILE" >"$FILE.rot13"
+ done &&
+
+ echo "*.file filter=protocol" >.gitattributes &&
+ filter_git add *.file .gitattributes &&
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ IN: clean 1pkt_1__.file $(($S )) [OK] -- OUT: $(($S )) . [OK]
+ IN: clean 2pkt_1+1.file $(($S +1)) [OK] -- OUT: $(($S +1)) .. [OK]
+ IN: clean 2pkt_2-1.file $(($S*2-1)) [OK] -- OUT: $(($S*2-1)) .. [OK]
+ IN: clean 2pkt_2__.file $(($S*2 )) [OK] -- OUT: $(($S*2 )) .. [OK]
+ IN: clean 3pkt_2+1.file $(($S*2+1)) [OK] -- OUT: $(($S*2+1)) ... [OK]
+ STOP
+ EOF
+ test_cmp_count expected.log rot13-filter.log &&
+
+ rm -f *.file &&
+
+ filter_git checkout --quiet --no-progress -- *.file &&
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ IN: smudge 1pkt_1__.file $(($S )) [OK] -- OUT: $(($S )) . [OK]
+ IN: smudge 2pkt_1+1.file $(($S +1)) [OK] -- OUT: $(($S +1)) .. [OK]
+ IN: smudge 2pkt_2-1.file $(($S*2-1)) [OK] -- OUT: $(($S*2-1)) .. [OK]
+ IN: smudge 2pkt_2__.file $(($S*2 )) [OK] -- OUT: $(($S*2 )) .. [OK]
+ IN: smudge 3pkt_2+1.file $(($S*2+1)) [OK] -- OUT: $(($S*2+1)) ... [OK]
+ STOP
+ EOF
+ test_cmp_exclude_clean expected.log rot13-filter.log &&
+
+ for FILE in *.file
+ do
+ test_cmp_committed_rot13 "$TEST_ROOT/$FILE" $FILE
+ done
+ )
+'
+
+test_expect_success PERL 'required process filter with clean error should fail' '
+ test_config_global filter.protocol.process "$TEST_DIRECTORY/t0021/rot13-filter.pl clean smudge" &&
+ test_config_global filter.protocol.required true &&
+ rm -rf repo &&
+ mkdir repo &&
+ (
+ cd repo &&
+ git init &&
+
+ echo "*.r filter=protocol" >.gitattributes &&
+
+ cp "$TEST_ROOT/test.o" test.r &&
+ echo "this is going to fail" >clean-write-fail.r &&
+ echo "content-test3-subdir" >test3.r &&
+
+ test_must_fail git add .
+ )
+'
+
+test_expect_success PERL 'process filter should restart after unexpected write failure' '
+ test_config_global filter.protocol.process "$TEST_DIRECTORY/t0021/rot13-filter.pl clean smudge" &&
+ rm -rf repo &&
+ mkdir repo &&
+ (
+ cd repo &&
+ git init &&
+
+ echo "*.r filter=protocol" >.gitattributes &&
+
+ cp "$TEST_ROOT/test.o" test.r &&
+ cp "$TEST_ROOT/test2.o" test2.r &&
+ echo "this is going to fail" >smudge-write-fail.o &&
+ cp smudge-write-fail.o smudge-write-fail.r &&
+
+ S=$(file_size test.r) &&
+ S2=$(file_size test2.r) &&
+ SF=$(file_size smudge-write-fail.r) &&
+
+ git add . &&
+ rm -f *.r &&
+
+ rm -f rot13-filter.log &&
+ git checkout --quiet --no-progress . 2>git-stderr.log &&
+
+ grep "smudge write error at" git-stderr.log &&
+ grep "error: external filter" git-stderr.log &&
+
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ IN: smudge smudge-write-fail.r $SF [OK] -- OUT: $SF [WRITE FAIL]
+ START
+ init handshake complete
+ IN: smudge test.r $S [OK] -- OUT: $S . [OK]
+ IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK]
+ STOP
+ EOF
+ test_cmp_exclude_clean expected.log rot13-filter.log &&
+
+ test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.r &&
+ test_cmp_committed_rot13 "$TEST_ROOT/test2.o" test2.r &&
+
+ # Smudge failed
+ ! test_cmp smudge-write-fail.o smudge-write-fail.r &&
+ "$TEST_ROOT/rot13.sh" <smudge-write-fail.o >expected &&
+ git cat-file blob :smudge-write-fail.r >actual &&
+ test_cmp expected actual
+ )
+'
+
+test_expect_success PERL 'process filter should not be restarted if it signals an error' '
+ test_config_global filter.protocol.process "$TEST_DIRECTORY/t0021/rot13-filter.pl clean smudge" &&
+ rm -rf repo &&
+ mkdir repo &&
+ (
+ cd repo &&
+ git init &&
+
+ echo "*.r filter=protocol" >.gitattributes &&
+
+ cp "$TEST_ROOT/test.o" test.r &&
+ cp "$TEST_ROOT/test2.o" test2.r &&
+ echo "this will cause an error" >error.o &&
+ cp error.o error.r &&
+
+ S=$(file_size test.r) &&
+ S2=$(file_size test2.r) &&
+ SE=$(file_size error.r) &&
+
+ git add . &&
+ rm -f *.r &&
+
+ filter_git checkout --quiet --no-progress . &&
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ IN: smudge error.r $SE [OK] -- OUT: 0 [ERROR]
+ IN: smudge test.r $S [OK] -- OUT: $S . [OK]
+ IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK]
+ STOP
+ EOF
+ test_cmp_exclude_clean expected.log rot13-filter.log &&
+
+ test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.r &&
+ test_cmp_committed_rot13 "$TEST_ROOT/test2.o" test2.r &&
+ test_cmp error.o error.r
+ )
+'
+
+test_expect_success PERL 'process filter abort stops processing of all further files' '
+ test_config_global filter.protocol.process "$TEST_DIRECTORY/t0021/rot13-filter.pl clean smudge" &&
+ rm -rf repo &&
+ mkdir repo &&
+ (
+ cd repo &&
+ git init &&
+
+ echo "*.r filter=protocol" >.gitattributes &&
+
+ cp "$TEST_ROOT/test.o" test.r &&
+ cp "$TEST_ROOT/test2.o" test2.r &&
+ echo "error this blob and all future blobs" >abort.o &&
+ cp abort.o abort.r &&
+
+ SA=$(file_size abort.r) &&
+
+ git add . &&
+ rm -f *.r &&
+
+ # Note: This test assumes that Git filters files in alphabetical
+ # order ("abort.r" before "test.r").
+ filter_git checkout --quiet --no-progress . &&
+ cat >expected.log <<-EOF &&
+ START
+ init handshake complete
+ IN: smudge abort.r $SA [OK] -- OUT: 0 [ABORT]
+ STOP
+ EOF
+ test_cmp_exclude_clean expected.log rot13-filter.log &&
+
+ test_cmp "$TEST_ROOT/test.o" test.r &&
+ test_cmp "$TEST_ROOT/test2.o" test2.r &&
+ test_cmp abort.o abort.r
+ )
+'
+
+test_expect_success PERL 'invalid process filter must fail (and not hang!)' '
+ test_config_global filter.protocol.process cat &&
+ test_config_global filter.protocol.required true &&
+ rm -rf repo &&
+ mkdir repo &&
+ (
+ cd repo &&
+ git init &&
+
+ echo "*.r filter=protocol" >.gitattributes &&
+
+ cp "$TEST_ROOT/test.o" test.r &&
+ test_must_fail git add . 2>git-stderr.log &&
+ grep "does not support filter protocol version" git-stderr.log
+ )
+'
+
test_done
--- /dev/null
+#!/usr/bin/perl
+#
+# Example implementation for the Git filter protocol version 2
+# See Documentation/gitattributes.txt, section "Filter Protocol"
+#
+# The script takes the list of supported protocol capabilities as
+# arguments ("clean", "smudge", etc).
+#
+# This implementation supports special test cases:
+# (1) If data with the pathname "clean-write-fail.r" is processed with
+# a "clean" operation then the write operation will die.
+# (2) If data with the pathname "smudge-write-fail.r" is processed with
+# a "smudge" operation then the write operation will die.
+# (3) If data with the pathname "error.r" is processed with any
+# operation then the filter signals that it cannot or does not want
+# to process the file.
+# (4) If data with the pathname "abort.r" is processed with any
+# operation then the filter signals that it cannot or does not want
+# to process the file and any file after that is processed with the
+# same command.
+#
+
+use strict;
+use warnings;
+
+my $MAX_PACKET_CONTENT_SIZE = 65516;
+my @capabilities = @ARGV;
+
+open my $debug, ">>", "rot13-filter.log" or die "cannot open log file: $!";
+
+sub rot13 {
+ my $str = shift;
+ $str =~ y/A-Za-z/N-ZA-Mn-za-m/;
+ return $str;
+}
+
+sub packet_bin_read {
+ my $buffer;
+ my $bytes_read = read STDIN, $buffer, 4;
+ if ( $bytes_read == 0 ) {
+ # EOF - Git stopped talking to us!
+ print $debug "STOP\n";
+ exit();
+ }
+ elsif ( $bytes_read != 4 ) {
+ die "invalid packet: '$buffer'";
+ }
+ my $pkt_size = hex($buffer);
+ if ( $pkt_size == 0 ) {
+ return ( 1, "" );
+ }
+ elsif ( $pkt_size > 4 ) {
+ my $content_size = $pkt_size - 4;
+ $bytes_read = read STDIN, $buffer, $content_size;
+ if ( $bytes_read != $content_size ) {
+ die "invalid packet ($content_size bytes expected; $bytes_read bytes read)";
+ }
+ return ( 0, $buffer );
+ }
+ else {
+ die "invalid packet size: $pkt_size";
+ }
+}
+
+sub packet_txt_read {
+ my ( $res, $buf ) = packet_bin_read();
+ unless ( $buf =~ s/\n$// ) {
+ die "A non-binary line MUST be terminated by an LF.";
+ }
+ return ( $res, $buf );
+}
+
+sub packet_bin_write {
+ my $buf = shift;
+ print STDOUT sprintf( "%04x", length($buf) + 4 );
+ print STDOUT $buf;
+ STDOUT->flush();
+}
+
+sub packet_txt_write {
+ packet_bin_write( $_[0] . "\n" );
+}
+
+sub packet_flush {
+ print STDOUT sprintf( "%04x", 0 );
+ STDOUT->flush();
+}
+
+print $debug "START\n";
+$debug->flush();
+
+( packet_txt_read() eq ( 0, "git-filter-client" ) ) || die "bad initialize";
+( packet_txt_read() eq ( 0, "version=2" ) ) || die "bad version";
+( packet_bin_read() eq ( 1, "" ) ) || die "bad version end";
+
+packet_txt_write("git-filter-server");
+packet_txt_write("version=2");
+packet_flush();
+
+( packet_txt_read() eq ( 0, "capability=clean" ) ) || die "bad capability";
+( packet_txt_read() eq ( 0, "capability=smudge" ) ) || die "bad capability";
+( packet_bin_read() eq ( 1, "" ) ) || die "bad capability end";
+
+foreach (@capabilities) {
+ packet_txt_write( "capability=" . $_ );
+}
+packet_flush();
+print $debug "init handshake complete\n";
+$debug->flush();
+
+while (1) {
+ my ($command) = packet_txt_read() =~ /^command=([^=]+)$/;
+ print $debug "IN: $command";
+ $debug->flush();
+
+ my ($pathname) = packet_txt_read() =~ /^pathname=([^=]+)$/;
+ print $debug " $pathname";
+ $debug->flush();
+
+ # Flush
+ packet_bin_read();
+
+ my $input = "";
+ {
+ binmode(STDIN);
+ my $buffer;
+ my $done = 0;
+ while ( !$done ) {
+ ( $done, $buffer ) = packet_bin_read();
+ $input .= $buffer;
+ }
+ print $debug " " . length($input) . " [OK] -- ";
+ $debug->flush();
+ }
+
+ my $output;
+ if ( $pathname eq "error.r" or $pathname eq "abort.r" ) {
+ $output = "";
+ }
+ elsif ( $command eq "clean" and grep( /^clean$/, @capabilities ) ) {
+ $output = rot13($input);
+ }
+ elsif ( $command eq "smudge" and grep( /^smudge$/, @capabilities ) ) {
+ $output = rot13($input);
+ }
+ else {
+ die "bad command '$command'";
+ }
+
+ print $debug "OUT: " . length($output) . " ";
+ $debug->flush();
+
+ if ( $pathname eq "error.r" ) {
+ print $debug "[ERROR]\n";
+ $debug->flush();
+ packet_txt_write("status=error");
+ packet_flush();
+ }
+ elsif ( $pathname eq "abort.r" ) {
+ print $debug "[ABORT]\n";
+ $debug->flush();
+ packet_txt_write("status=abort");
+ packet_flush();
+ }
+ else {
+ packet_txt_write("status=success");
+ packet_flush();
+
+ if ( $pathname eq "${command}-write-fail.r" ) {
+ print $debug "[WRITE FAIL]\n";
+ $debug->flush();
+ die "${command} write error";
+ }
+
+ while ( length($output) > 0 ) {
+ my $packet = substr( $output, 0, $MAX_PACKET_CONTENT_SIZE );
+ packet_bin_write($packet);
+ # dots represent the number of packets
+ print $debug ".";
+ if ( length($output) > $MAX_PACKET_CONTENT_SIZE ) {
+ $output = substr( $output, $MAX_PACKET_CONTENT_SIZE );
+ }
+ else {
+ $output = "";
+ }
+ }
+ packet_flush();
+ print $debug " [OK]\n";
+ $debug->flush();
+ packet_flush();
+ }
+}
'
test_expect_success 'unambiguously abbreviated option with "="' '
- test-parse-options --int=2 >output 2>output.err &&
- test_must_be_empty output.err &&
- test_cmp expect output
+ test-parse-options --expect="integer: 2" --int=2
'
test_expect_success 'ambiguously abbreviated option' '
test_expect_code 129 test-parse-options --strin 123
'
-cat >expect <<\EOF
-boolean: 0
-integer: 0
-magnitude: 0
-timestamp: 0
-string: 123
-abbrev: 7
-verbose: -1
-quiet: 0
-dry run: no
-file: (not set)
-EOF
-
test_expect_success 'non ambiguous option (after two options it abbreviates)' '
- test-parse-options --st 123 >output 2>output.err &&
- test_must_be_empty output.err &&
- test_cmp expect output
+ test-parse-options --expect="string: 123" --st 123
'
cat >typo.err <<\EOF
test_cmp typo.err output.err
'
-cat >expect <<\EOF
-boolean: 0
-integer: 0
-magnitude: 0
-timestamp: 0
-string: (not set)
-abbrev: 7
-verbose: -1
-quiet: 0
-dry run: no
-file: (not set)
-arg 00: --quux
-EOF
-
test_expect_success 'keep some options as arguments' '
- test-parse-options --quux >output 2>output.err &&
- test_must_be_empty output.err &&
- test_cmp expect output
+ test-parse-options --expect="arg 00: --quux" --quux
'
cat >expect <<\EOF
test_cmp expect output
'
-cat >expect <<\EOF
-boolean: 6
-integer: 0
-magnitude: 0
-timestamp: 0
-string: (not set)
-abbrev: 7
-verbose: -1
-quiet: 0
-dry run: no
-file: (not set)
-EOF
-
test_expect_success 'OPT_BIT() works' '
- test-parse-options -bb --or4 >output 2>output.err &&
- test_must_be_empty output.err &&
- test_cmp expect output
+ test-parse-options --expect="boolean: 6" -bb --or4
'
test_expect_success 'OPT_NEGBIT() works' '
- test-parse-options -bb --no-neg-or4 >output 2>output.err &&
- test_must_be_empty output.err &&
- test_cmp expect output
+ test-parse-options --expect="boolean: 6" -bb --no-neg-or4
'
test_expect_success 'OPT_COUNTUP() with PARSE_OPT_NODASH works' '
- test-parse-options + + + + + + >output 2>output.err &&
- test_must_be_empty output.err &&
- test_cmp expect output
+ test-parse-options --expect="boolean: 6" + + + + + +
'
-cat >expect <<\EOF
-boolean: 0
-integer: 12345
-magnitude: 0
-timestamp: 0
-string: (not set)
-abbrev: 7
-verbose: -1
-quiet: 0
-dry run: no
-file: (not set)
-EOF
-
test_expect_success 'OPT_NUMBER_CALLBACK() works' '
- test-parse-options -12345 >output 2>output.err &&
- test_must_be_empty output.err &&
- test_cmp expect output
+ test-parse-options --expect="integer: 12345" -12345
'
cat >expect <<\EOF
test_cmp expect output
'
-cat >expect <<\EOF
-boolean: 0
-integer: 0
-magnitude: 0
-timestamp: 0
-string: (not set)
-abbrev: 7
-verbose: -1
-quiet: 3
-dry run: no
-file: (not set)
-EOF
-
test_expect_success 'multiple quiet levels' '
- test-parse-options -q -q -q >output 2>output.err &&
- test_must_be_empty output.err &&
- test_cmp expect output
+ test-parse-options --expect="quiet: 3" -q -q -q
'
-cat >expect <<\EOF
-boolean: 0
-integer: 0
-magnitude: 0
-timestamp: 0
-string: (not set)
-abbrev: 7
-verbose: 3
-quiet: 0
-dry run: no
-file: (not set)
-EOF
-
test_expect_success 'multiple verbose levels' '
- test-parse-options -v -v -v >output 2>output.err &&
- test_must_be_empty output.err &&
- test_cmp expect output
+ test-parse-options --expect="verbose: 3" -v -v -v
'
-cat >expect <<\EOF
-boolean: 0
-integer: 0
-magnitude: 0
-timestamp: 0
-string: (not set)
-abbrev: 7
-verbose: -1
-quiet: 0
-dry run: no
-file: (not set)
-EOF
-
test_expect_success '--no-quiet sets --quiet to 0' '
- test-parse-options --no-quiet >output 2>output.err &&
- test_must_be_empty output.err &&
- test_cmp expect output
+ test-parse-options --expect="quiet: 0" --no-quiet
'
-cat >expect <<\EOF
-boolean: 0
-integer: 0
-magnitude: 0
-timestamp: 0
-string: (not set)
-abbrev: 7
-verbose: -1
-quiet: 0
-dry run: no
-file: (not set)
-EOF
-
test_expect_success '--no-quiet resets multiple -q to 0' '
- test-parse-options -q -q -q --no-quiet >output 2>output.err &&
- test_must_be_empty output.err &&
- test_cmp expect output
+ test-parse-options --expect="quiet: 0" -q -q -q --no-quiet
'
-cat >expect <<\EOF
-boolean: 0
-integer: 0
-magnitude: 0
-timestamp: 0
-string: (not set)
-abbrev: 7
-verbose: 0
-quiet: 0
-dry run: no
-file: (not set)
-EOF
-
test_expect_success '--no-verbose sets verbose to 0' '
- test-parse-options --no-verbose >output 2>output.err &&
- test_must_be_empty output.err &&
- test_cmp expect output
+ test-parse-options --expect="verbose: 0" --no-verbose
'
-cat >expect <<\EOF
-boolean: 0
-integer: 0
-magnitude: 0
-timestamp: 0
-string: (not set)
-abbrev: 7
-verbose: 0
-quiet: 0
-dry run: no
-file: (not set)
-EOF
-
test_expect_success '--no-verbose resets multiple verbose to 0' '
- test-parse-options -v -v -v --no-verbose >output 2>output.err &&
- test_must_be_empty output.err &&
- test_cmp expect output
+ test-parse-options --expect="verbose: 0" -v -v -v --no-verbose
'
test_done
test_git_path GIT_COMMON_DIR=bar packed-refs bar/packed-refs
test_git_path GIT_COMMON_DIR=bar shallow bar/shallow
-# In the tests below, the distinction between $PWD and $(pwd) is important:
-# on Windows, $PWD is POSIX style (/c/foo), $(pwd) has drive letter (c:/foo).
+# In the tests below, $(pwd) must be used because it is a native path on
+# Windows and avoids MSYS's path mangling (which simplifies "foo/../bar" and
+# strips the dot from trailing "/.").
test_submodule_relative_url "../" "../foo" "../submodule" "../../submodule"
test_submodule_relative_url "../" "../foo/bar" "../submodule" "../../foo/submodule"
test_submodule_relative_url "../" "./foo" "../submodule" "../submodule"
test_submodule_relative_url "../" "./foo/bar" "../submodule" "../foo/submodule"
test_submodule_relative_url "../../../" "../foo/bar" "../sub/a/b/c" "../../../../foo/sub/a/b/c"
-test_submodule_relative_url "../" "$PWD/addtest" "../repo" "$(pwd)/repo"
+test_submodule_relative_url "../" "$(pwd)/addtest" "../repo" "$(pwd)/repo"
test_submodule_relative_url "../" "foo/bar" "../submodule" "../foo/submodule"
test_submodule_relative_url "../" "foo" "../submodule" "../submodule"
test_submodule_relative_url "(null)" "../foo/bar" "../sub/a/b/c" "../foo/sub/a/b/c"
+test_submodule_relative_url "(null)" "../foo/bar" "../sub/a/b/c/" "../foo/sub/a/b/c"
+test_submodule_relative_url "(null)" "../foo/bar/" "../sub/a/b/c" "../foo/sub/a/b/c"
test_submodule_relative_url "(null)" "../foo/bar" "../submodule" "../foo/submodule"
test_submodule_relative_url "(null)" "../foo/submodule" "../submodule" "../foo/submodule"
test_submodule_relative_url "(null)" "../foo" "../submodule" "../submodule"
test_submodule_relative_url "(null)" "./foo/bar" "../submodule" "foo/submodule"
test_submodule_relative_url "(null)" "./foo" "../submodule" "submodule"
test_submodule_relative_url "(null)" "//somewhere else/repo" "../subrepo" "//somewhere else/subrepo"
-test_submodule_relative_url "(null)" "$PWD/subsuper_update_r" "../subsubsuper_update_r" "$(pwd)/subsubsuper_update_r"
-test_submodule_relative_url "(null)" "$PWD/super_update_r2" "../subsuper_update_r" "$(pwd)/subsuper_update_r"
-test_submodule_relative_url "(null)" "$PWD/." "../." "$(pwd)/."
-test_submodule_relative_url "(null)" "$PWD" "./." "$(pwd)/."
-test_submodule_relative_url "(null)" "$PWD/addtest" "../repo" "$(pwd)/repo"
-test_submodule_relative_url "(null)" "$PWD" "./å äö" "$(pwd)/å äö"
-test_submodule_relative_url "(null)" "$PWD/." "../submodule" "$(pwd)/submodule"
-test_submodule_relative_url "(null)" "$PWD/submodule" "../submodule" "$(pwd)/submodule"
-test_submodule_relative_url "(null)" "$PWD/home2/../remote" "../bundle1" "$(pwd)/home2/../bundle1"
-test_submodule_relative_url "(null)" "$PWD/submodule_update_repo" "./." "$(pwd)/submodule_update_repo/."
+test_submodule_relative_url "(null)" "$(pwd)/subsuper_update_r" "../subsubsuper_update_r" "$(pwd)/subsubsuper_update_r"
+test_submodule_relative_url "(null)" "$(pwd)/super_update_r2" "../subsuper_update_r" "$(pwd)/subsuper_update_r"
+test_submodule_relative_url "(null)" "$(pwd)/." "../." "$(pwd)/."
+test_submodule_relative_url "(null)" "$(pwd)" "./." "$(pwd)/."
+test_submodule_relative_url "(null)" "$(pwd)/addtest" "../repo" "$(pwd)/repo"
+test_submodule_relative_url "(null)" "$(pwd)" "./å äö" "$(pwd)/å äö"
+test_submodule_relative_url "(null)" "$(pwd)/." "../submodule" "$(pwd)/submodule"
+test_submodule_relative_url "(null)" "$(pwd)/submodule" "../submodule" "$(pwd)/submodule"
+test_submodule_relative_url "(null)" "$(pwd)/home2/../remote" "../bundle1" "$(pwd)/home2/../bundle1"
+test_submodule_relative_url "(null)" "$(pwd)/submodule_update_repo" "./." "$(pwd)/submodule_update_repo/."
test_submodule_relative_url "(null)" "file:///tmp/repo" "../subrepo" "file:///tmp/subrepo"
test_submodule_relative_url "(null)" "foo/bar" "../submodule" "foo/submodule"
test_submodule_relative_url "(null)" "foo" "../submodule" "submodule"
test_expect_success 'warn ambiguity when no candidate matches type hint' '
test_must_fail git rev-parse --verify 000000000^{commit} 2>actual &&
- grep "short SHA1 000000000 is ambiguous" actual
+ test_i18ngrep "short SHA1 000000000 is ambiguous" actual
'
test_expect_success 'disambiguate tree-ish' '
)
'
+test_expect_success '"add" default branch of a bare repo' '
+ (
+ git clone --bare . bare2 &&
+ cd bare2 &&
+ git worktree add ../there3 master
+ )
+'
+
test_expect_success 'checkout with grafts' '
test_when_finished rm .git/info/grafts &&
test_commit abc &&
. ./test-lib.sh
test_expect_success 'intent to add' '
+ test_commit 1 &&
+ git rm 1.t &&
+ echo hello >1.t &&
echo hello >file &&
echo hello >elif &&
git add -N file &&
- git add elif
+ git add elif &&
+ git add -N 1.t
+'
+
+test_expect_success 'git status' '
+ git status --porcelain | grep -v actual >actual &&
+ cat >expect <<-\EOF &&
+ DA 1.t
+ A elif
+ A file
+ EOF
+ test_cmp expect actual
'
test_expect_success 'check result of "add -N"' '
git add -N nitfol &&
git commit -m second &&
test $(git ls-tree HEAD -- nitfol | wc -l) = 0 &&
- test $(git diff --name-only HEAD -- nitfol | wc -l) = 1
+ test $(git diff --name-only HEAD -- nitfol | wc -l) = 1 &&
+ test $(git diff --name-only --ita-invisible-in-index HEAD -- nitfol | wc -l) = 0 &&
+ test $(git diff --name-only --ita-invisible-in-index -- nitfol | wc -l) = 1
'
test_expect_success 'can commit with an unrelated i-t-a entry in index' '
)
'
+test_expect_success 'commit: ita entries ignored in empty intial commit check' '
+ git init empty-intial-commit &&
+ (
+ cd empty-intial-commit &&
+ : >one &&
+ git add -N one &&
+ test_must_fail git commit -m nothing-new-here
+ )
+'
+
+test_expect_success 'commit: ita entries ignored in empty commit check' '
+ git init empty-subsequent-commit &&
+ (
+ cd empty-subsequent-commit &&
+ test_commit one &&
+ : >two &&
+ git add -N two &&
+ test_must_fail git commit -m nothing-new-here
+ )
+'
+
test_done
--- /dev/null
+#!/bin/sh
+
+test_description='Test ls-files recurse-submodules feature
+
+This test verifies the recurse-submodules feature correctly lists files from
+submodules.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup directory structure and submodules' '
+ echo a >a &&
+ mkdir b &&
+ echo b >b/b &&
+ git add a b &&
+ git commit -m "add a and b" &&
+ git init submodule &&
+ echo c >submodule/c &&
+ git -C submodule add c &&
+ git -C submodule commit -m "add c" &&
+ git submodule add ./submodule &&
+ git commit -m "added submodule"
+'
+
+test_expect_success 'ls-files correctly outputs files in submodule' '
+ cat >expect <<-\EOF &&
+ .gitmodules
+ a
+ b/b
+ submodule/c
+ EOF
+
+ git ls-files --recurse-submodules >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'ls-files correctly outputs files in submodule with -z' '
+ lf_to_nul >expect <<-\EOF &&
+ .gitmodules
+ a
+ b/b
+ submodule/c
+ EOF
+
+ git ls-files --recurse-submodules -z >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'ls-files does not output files not added to a repo' '
+ cat >expect <<-\EOF &&
+ .gitmodules
+ a
+ b/b
+ submodule/c
+ EOF
+
+ echo a >not_added &&
+ echo b >b/not_added &&
+ echo c >submodule/not_added &&
+ git ls-files --recurse-submodules >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'ls-files recurses more than 1 level' '
+ cat >expect <<-\EOF &&
+ .gitmodules
+ a
+ b/b
+ submodule/.gitmodules
+ submodule/c
+ submodule/subsub/d
+ EOF
+
+ git init submodule/subsub &&
+ echo d >submodule/subsub/d &&
+ git -C submodule/subsub add d &&
+ git -C submodule/subsub commit -m "add d" &&
+ git -C submodule submodule add ./subsub &&
+ git -C submodule commit -m "added subsub" &&
+ git ls-files --recurse-submodules >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--recurse-submodules and pathspecs setup' '
+ echo e >submodule/subsub/e.txt &&
+ git -C submodule/subsub add e.txt &&
+ git -C submodule/subsub commit -m "adding e.txt" &&
+ echo f >submodule/f.TXT &&
+ echo g >submodule/g.txt &&
+ git -C submodule add f.TXT g.txt &&
+ git -C submodule commit -m "add f and g" &&
+ echo h >h.txt &&
+ mkdir sib &&
+ echo sib >sib/file &&
+ git add h.txt sib/file &&
+ git commit -m "add h and sib/file" &&
+ git init sub &&
+ echo sub >sub/file &&
+ git -C sub add file &&
+ git -C sub commit -m "add file" &&
+ git submodule add ./sub &&
+ git commit -m "added sub" &&
+
+ cat >expect <<-\EOF &&
+ .gitmodules
+ a
+ b/b
+ h.txt
+ sib/file
+ sub/file
+ submodule/.gitmodules
+ submodule/c
+ submodule/f.TXT
+ submodule/g.txt
+ submodule/subsub/d
+ submodule/subsub/e.txt
+ EOF
+
+ git ls-files --recurse-submodules >actual &&
+ test_cmp expect actual &&
+ cat actual &&
+ git ls-files --recurse-submodules "*" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--recurse-submodules and pathspecs' '
+ cat >expect <<-\EOF &&
+ h.txt
+ submodule/g.txt
+ submodule/subsub/e.txt
+ EOF
+
+ git ls-files --recurse-submodules "*.txt" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--recurse-submodules and pathspecs' '
+ cat >expect <<-\EOF &&
+ h.txt
+ submodule/f.TXT
+ submodule/g.txt
+ submodule/subsub/e.txt
+ EOF
+
+ git ls-files --recurse-submodules ":(icase)*.txt" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--recurse-submodules and pathspecs' '
+ cat >expect <<-\EOF &&
+ h.txt
+ submodule/f.TXT
+ submodule/g.txt
+ EOF
+
+ git ls-files --recurse-submodules ":(icase)*.txt" ":(exclude)submodule/subsub/*" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--recurse-submodules and pathspecs' '
+ cat >expect <<-\EOF &&
+ sub/file
+ EOF
+
+ git ls-files --recurse-submodules "sub" >actual &&
+ test_cmp expect actual &&
+ git ls-files --recurse-submodules "sub/" >actual &&
+ test_cmp expect actual &&
+ git ls-files --recurse-submodules "sub/file" >actual &&
+ test_cmp expect actual &&
+ git ls-files --recurse-submodules "su*/file" >actual &&
+ test_cmp expect actual &&
+ git ls-files --recurse-submodules "su?/file" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--recurse-submodules and pathspecs' '
+ cat >expect <<-\EOF &&
+ sib/file
+ sub/file
+ EOF
+
+ git ls-files --recurse-submodules "s??/file" >actual &&
+ test_cmp expect actual &&
+ git ls-files --recurse-submodules "s???file" >actual &&
+ test_cmp expect actual &&
+ git ls-files --recurse-submodules "s*file" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--recurse-submodules does not support --error-unmatch' '
+ test_must_fail git ls-files --recurse-submodules --error-unmatch 2>actual &&
+ test_i18ngrep "does not support --error-unmatch" actual
+'
+
+test_incompatible_with_recurse_submodules () {
+ test_expect_success "--recurse-submodules and $1 are incompatible" "
+ test_must_fail git ls-files --recurse-submodules $1 2>actual &&
+ test_i18ngrep 'unsupported mode' actual
+ "
+}
+
+test_incompatible_with_recurse_submodules --deleted
+test_incompatible_with_recurse_submodules --modified
+test_incompatible_with_recurse_submodules --others
+test_incompatible_with_recurse_submodules --stage
+test_incompatible_with_recurse_submodules --killed
+test_incompatible_with_recurse_submodules --unmerged
+
+test_done
echo content >extra_file &&
git add extra_file &&
test_must_fail git revert HEAD 2>errors &&
- test_i18ngrep "Your local changes would be overwritten by " errors
+ test_i18ngrep "your local changes would be overwritten by " errors
'
test_i18ncmp expect actual
'
+test_expect_success 'rm empty string should invoke warning' '
+ git rm -rf "" 2>output &&
+ test_i18ngrep "warning: empty strings" output
+'
+
test_done
test_i18ncmp expect.err actual.err
'
+test_expect_success 'git add empty string should invoke warning' '
+ git add "" 2>output &&
+ test_i18ngrep "warning: empty strings" output
+'
+
test_expect_success 'git add --chmod=[+-]x stages correctly' '
rm -f foo1 &&
echo foo >foo1 &&
'
test_expect_success 'git add --chmod=[+-]x changes index with already added file' '
+ rm -f foo3 xfoo3 &&
echo foo >foo3 &&
git add foo3 &&
git add --chmod=+x foo3 &&
test_cmp expected current
'
-test_expect_success 'the same with --ws-error-highlight' '
+test_expect_success 'ws-error-highlight test setup' '
+
git reset --hard &&
{
echo "0. blank-at-eol " &&
echo "2. and a new line "
} >x &&
- git -c color.diff=always diff --ws-error-highlight=default,old |
- test_decode_color >current &&
-
- cat >expected <<-\EOF &&
+ cat >expect.default-old <<-\EOF &&
<BOLD>diff --git a/x b/x<RESET>
<BOLD>index d0233a2..700886e 100644<RESET>
<BOLD>--- a/x<RESET>
<GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
EOF
- test_cmp expected current &&
-
- git -c color.diff=always diff --ws-error-highlight=all |
- test_decode_color >current &&
-
- cat >expected <<-\EOF &&
+ cat >expect.all <<-\EOF &&
<BOLD>diff --git a/x b/x<RESET>
<BOLD>index d0233a2..700886e 100644<RESET>
<BOLD>--- a/x<RESET>
<GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
EOF
- test_cmp expected current &&
-
- git -c color.diff=always diff --ws-error-highlight=none |
- test_decode_color >current &&
-
- cat >expected <<-\EOF &&
+ cat >expect.none <<-\EOF
<BOLD>diff --git a/x b/x<RESET>
<BOLD>index d0233a2..700886e 100644<RESET>
<BOLD>--- a/x<RESET>
<GREEN>+2. and a new line <RESET>
EOF
- test_cmp expected current
+'
+
+test_expect_success 'test --ws-error-highlight option' '
+
+ git -c color.diff=always diff --ws-error-highlight=default,old |
+ test_decode_color >current &&
+ test_cmp expect.default-old current &&
+
+ git -c color.diff=always diff --ws-error-highlight=all |
+ test_decode_color >current &&
+ test_cmp expect.all current &&
+
+ git -c color.diff=always diff --ws-error-highlight=none |
+ test_decode_color >current &&
+ test_cmp expect.none current
+
+'
+
+test_expect_success 'test diff.wsErrorHighlight config' '
+
+ git -c color.diff=always -c diff.wsErrorHighlight=default,old diff |
+ test_decode_color >current &&
+ test_cmp expect.default-old current &&
+
+ git -c color.diff=always -c diff.wsErrorHighlight=all diff |
+ test_decode_color >current &&
+ test_cmp expect.all current &&
+
+ git -c color.diff=always -c diff.wsErrorHighlight=none diff |
+ test_decode_color >current &&
+ test_cmp expect.none current
+
+'
+
+test_expect_success 'option overrides diff.wsErrorHighlight' '
+
+ git -c color.diff=always -c diff.wsErrorHighlight=none \
+ diff --ws-error-highlight=default,old |
+ test_decode_color >current &&
+ test_cmp expect.default-old current &&
+
+ git -c color.diff=always -c diff.wsErrorHighlight=default \
+ diff --ws-error-highlight=all |
+ test_decode_color >current &&
+ test_cmp expect.all current &&
+
+ git -c color.diff=always -c diff.wsErrorHighlight=all \
+ diff --ws-error-highlight=none |
+ test_decode_color >current &&
+ test_cmp expect.none current
+
'
test_done
test_expect_success 'compare diagnostic; ensure file is still here' '
echo "error: git diff header lacks filename information (line 4)" >expected &&
test_path_is_file f &&
- test_cmp expected actual
+ test_i18ncmp expected actual
'
test_done
test_must_fail git rev-list --bisect --first-parent HEAD
'
+test_expect_success '--header shows a NUL after each commit' '
+ # We know that there is no Q in the true payload; names and
+ # addresses of the authors and the committers do not have
+ # any, and object names or header names do not, either.
+ git rev-list --header --max-count=2 HEAD |
+ nul_to_q |
+ grep "^Q" >actual &&
+ cat >expect <<-EOF &&
+ Q$(git rev-parse HEAD~1)
+ Q
+ EOF
+ test_cmp expect actual
+'
+
test_done
test_cmp expect3 actual
'
+test_expect_success '--fork-point works with empty reflog' '
+ git -c core.logallrefupdates=false branch no-reflog base &&
+ git merge-base --fork-point no-reflog derived &&
+ test_cmp expect3 actual
+'
+
test_expect_success 'merge-base --octopus --all for complex tree' '
# Best common ancestor for JE, JAA and JDD is JC
# JE
git add --intent-to-add intent1.add intent2.add &&
cat >expect <<-EOF &&
- 1 AM N... 000000 100644 100644 $_z40 $EMPTY_BLOB intent1.add
- 1 AM N... 000000 100644 100644 $_z40 $EMPTY_BLOB intent2.add
+ 1 .A N... 000000 000000 100644 $_z40 $_z40 intent1.add
+ 1 .A N... 000000 000000 100644 $_z40 $_z40 intent2.add
EOF
git status --porcelain=v2 >actual &&
test_description='signed commit tests'
. ./test-lib.sh
+GNUPGHOME_NOT_USED=$GNUPGHOME
. "$TEST_DIRECTORY/lib-gpg.sh"
test_expect_success GPG 'create signed commits' '
test_cmp expect actual
'
-test_expect_success GPG 'show unknown signature with custom format' '
+test_expect_success GPG 'show untrusted signature with custom format' '
cat >expect <<-\EOF &&
U
61092E85B7227189
test_cmp expect actual
'
+test_expect_success GPG 'show unknown signature with custom format' '
+ cat >expect <<-\EOF &&
+ E
+ 61092E85B7227189
+
+ EOF
+ GNUPGHOME="$GNUPGHOME_NOT_USED" git log -1 --format="%G?%n%GK%n%GS" eighth-signed-alt >actual &&
+ test_cmp expect actual
+'
+
test_expect_success GPG 'show lack of signature with custom format' '
cat >expect <<-\EOF &&
N
test_cmp expected actual
'
+test_expect_success 'with non-trailer lines mixed with Signed-off-by' '
+ cat >patch <<-\EOF &&
+
+ this is not a trailer
+ this is not a trailer
+ Signed-off-by: a <a@example.com>
+ this is not a trailer
+ EOF
+ cat >expected <<-\EOF &&
+
+ this is not a trailer
+ this is not a trailer
+ Signed-off-by: a <a@example.com>
+ this is not a trailer
+ token: value
+ EOF
+ git interpret-trailers --trailer "token: value" patch >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with non-trailer lines mixed with cherry picked from' '
+ cat >patch <<-\EOF &&
+
+ this is not a trailer
+ this is not a trailer
+ (cherry picked from commit x)
+ this is not a trailer
+ EOF
+ cat >expected <<-\EOF &&
+
+ this is not a trailer
+ this is not a trailer
+ (cherry picked from commit x)
+ this is not a trailer
+ token: value
+ EOF
+ git interpret-trailers --trailer "token: value" patch >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with non-trailer lines mixed with a configured trailer' '
+ cat >patch <<-\EOF &&
+
+ this is not a trailer
+ this is not a trailer
+ My-trailer: x
+ this is not a trailer
+ EOF
+ cat >expected <<-\EOF &&
+
+ this is not a trailer
+ this is not a trailer
+ My-trailer: x
+ this is not a trailer
+ token: value
+ EOF
+ test_config trailer.my.key "My-trailer: " &&
+ git interpret-trailers --trailer "token: value" patch >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with non-trailer lines mixed with a non-configured trailer' '
+ cat >patch <<-\EOF &&
+
+ this is not a trailer
+ this is not a trailer
+ I-am-not-configured: x
+ this is not a trailer
+ EOF
+ cat >expected <<-\EOF &&
+
+ this is not a trailer
+ this is not a trailer
+ I-am-not-configured: x
+ this is not a trailer
+
+ token: value
+ EOF
+ test_config trailer.my.key "My-trailer: " &&
+ git interpret-trailers --trailer "token: value" patch >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with all non-configured trailers' '
+ cat >patch <<-\EOF &&
+
+ I-am-not-configured: x
+ I-am-also-not-configured: x
+ EOF
+ cat >expected <<-\EOF &&
+
+ I-am-not-configured: x
+ I-am-also-not-configured: x
+ token: value
+ EOF
+ test_config trailer.my.key "My-trailer: " &&
+ git interpret-trailers --trailer "token: value" patch >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'with non-trailer lines only' '
+ cat >patch <<-\EOF &&
+
+ this is not a trailer
+ EOF
+ cat >expected <<-\EOF &&
+
+ this is not a trailer
+
+ token: value
+ EOF
+ git interpret-trailers --trailer "token: value" patch >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'line with leading whitespace is not trailer' '
+ q_to_tab >patch <<-\EOF &&
+
+ Qtoken: value
+ EOF
+ q_to_tab >expected <<-\EOF &&
+
+ Qtoken: value
+
+ token: value
+ EOF
+ git interpret-trailers --trailer "token: value" patch >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'multiline field treated as one trailer for 25% check' '
+ q_to_tab >patch <<-\EOF &&
+
+ Signed-off-by: a <a@example.com>
+ name: value on
+ Qmultiple lines
+ this is not a trailer
+ this is not a trailer
+ this is not a trailer
+ this is not a trailer
+ this is not a trailer
+ this is not a trailer
+ EOF
+ q_to_tab >expected <<-\EOF &&
+
+ Signed-off-by: a <a@example.com>
+ name: value on
+ Qmultiple lines
+ this is not a trailer
+ this is not a trailer
+ this is not a trailer
+ this is not a trailer
+ this is not a trailer
+ this is not a trailer
+ name: value
+ EOF
+ git interpret-trailers --trailer "name: value" patch >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'multiline field treated as atomic for placement' '
+ q_to_tab >patch <<-\EOF &&
+
+ another: trailer
+ name: value on
+ Qmultiple lines
+ another: trailer
+ EOF
+ q_to_tab >expected <<-\EOF &&
+
+ another: trailer
+ name: value on
+ Qmultiple lines
+ name: value
+ another: trailer
+ EOF
+ test_config trailer.name.where after &&
+ git interpret-trailers --trailer "name: value" patch >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'multiline field treated as atomic for replacement' '
+ q_to_tab >patch <<-\EOF &&
+
+ another: trailer
+ name: value on
+ Qmultiple lines
+ another: trailer
+ EOF
+ q_to_tab >expected <<-\EOF &&
+
+ another: trailer
+ another: trailer
+ name: value
+ EOF
+ test_config trailer.name.ifexists replace &&
+ git interpret-trailers --trailer "name: value" patch >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'multiline field treated as atomic for difference check' '
+ q_to_tab >patch <<-\EOF &&
+
+ another: trailer
+ name: first line
+ Qsecond line
+ another: trailer
+ EOF
+ test_config trailer.name.ifexists addIfDifferent &&
+
+ q_to_tab >trailer <<-\EOF &&
+ name: first line
+ Qsecond line
+ EOF
+ q_to_tab >expected <<-\EOF &&
+
+ another: trailer
+ name: first line
+ Qsecond line
+ another: trailer
+ EOF
+ git interpret-trailers --trailer "$(cat trailer)" patch >actual &&
+ test_cmp expected actual &&
+
+ q_to_tab >trailer <<-\EOF &&
+ name: first line
+ QQQQQsecond line
+ EOF
+ q_to_tab >expected <<-\EOF &&
+
+ another: trailer
+ name: first line
+ Qsecond line
+ another: trailer
+ name: first line
+ QQQQQsecond line
+ EOF
+ git interpret-trailers --trailer "$(cat trailer)" patch >actual &&
+ test_cmp expected actual &&
+
+ q_to_tab >trailer <<-\EOF &&
+ name: first line *DIFFERENT*
+ Qsecond line
+ EOF
+ q_to_tab >expected <<-\EOF &&
+
+ another: trailer
+ name: first line
+ Qsecond line
+ another: trailer
+ name: first line *DIFFERENT*
+ Qsecond line
+ EOF
+ git interpret-trailers --trailer "$(cat trailer)" patch >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'multiline field treated as atomic for neighbor check' '
+ q_to_tab >patch <<-\EOF &&
+
+ another: trailer
+ name: first line
+ Qsecond line
+ another: trailer
+ EOF
+ test_config trailer.name.where after &&
+ test_config trailer.name.ifexists addIfDifferentNeighbor &&
+
+ q_to_tab >trailer <<-\EOF &&
+ name: first line
+ Qsecond line
+ EOF
+ q_to_tab >expected <<-\EOF &&
+
+ another: trailer
+ name: first line
+ Qsecond line
+ another: trailer
+ EOF
+ git interpret-trailers --trailer "$(cat trailer)" patch >actual &&
+ test_cmp expected actual &&
+
+ q_to_tab >trailer <<-\EOF &&
+ name: first line
+ QQQQQsecond line
+ EOF
+ q_to_tab >expected <<-\EOF &&
+
+ another: trailer
+ name: first line
+ Qsecond line
+ name: first line
+ QQQQQsecond line
+ another: trailer
+ EOF
+ git interpret-trailers --trailer "$(cat trailer)" patch >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'with config setup' '
git config trailer.ack.key "Acked-by: " &&
cat >expected <<-\EOF &&
q["Jane\" Doe" <jdoe@example.com>],
q[Doe, jane <jdoe@example.com>],
q["Jane Doe <jdoe@example.com>],
- q['Jane 'Doe' <jdoe@example.com>]);
+ q['Jane 'Doe' <jdoe@example.com>],
+ q[Jane@:;\.,()<>Doe <jdoe@example.com>],
+ q[Jane <jdoe@example.com> Doe],
+ q[<jdoe@example.com> Jane Doe]);
my @known_failure_list = (q[Jane\ Doe <jdoe@example.com>],
q["Doe, Ja"ne <jdoe@example.com>],
q["Doe, Katarina" Jane <jdoe@example.com>],
- q[Jane@:;\.,()<>Doe <jdoe@example.com>],
q[Jane jdoe@example.com],
- q[<jdoe@example.com> Jane Doe],
- q[Jane <jdoe@example.com> Doe],
q["Jane "Kat"a" ri"na" ",Doe" <jdoe@example.com>],
q[Jane Doe],
q[Jane "Doe <jdoe@example.com>"],
test_cmp expected commandline1
'
+test_expect_success $PREREQ 'setup expect for cc trailer' "
+cat >expected-cc <<\EOF
+!recipient@example.com!
+!author@example.com!
+!one@example.com!
+!two@example.com!
+!three@example.com!
+!four@example.com!
+!five@example.com!
+EOF
+"
+
+test_expect_success $PREREQ 'cc trailer with various syntax' '
+ test_commit cc-trailer &&
+ test_when_finished "git reset --hard HEAD^" &&
+ git commit --amend -F - <<-EOF &&
+ Test Cc: trailers.
+
+ Cc: one@example.com
+ Cc: <two@example.com> # this is part of the name
+ Cc: <three@example.com>, <four@example.com> # not.five@example.com
+ Cc: "Some # Body" <five@example.com> [part.of.name.too]
+ EOF
+ clean_fake_sendmail &&
+ git send-email -1 --to=recipient@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" &&
+ test_cmp expected-cc commandline1
+'
+
test_expect_success $PREREQ 'setup expect' "
cat >expected-show-all-headers <<\EOF
0001-Second.patch
done,*)
# do not redirect again
;;
-*' --tee '*|*' --va'*)
+*' --tee '*|*' --va'*|*' --verbose-log '*)
mkdir -p "$TEST_OUTPUT_DIRECTORY/test-results"
BASE="$TEST_OUTPUT_DIRECTORY/test-results/$(basename "$0" .sh)"
+
+ # Make this filename available to the sub-process in case it is using
+ # --verbose-log.
+ GIT_TEST_TEE_OUTPUT_FILE=$BASE.out
+ export GIT_TEST_TEE_OUTPUT_FILE
+
+ # Truncate before calling "tee -a" to get rid of the results
+ # from any previous runs.
+ >"$GIT_TEST_TEE_OUTPUT_FILE"
+
(GIT_TEST_TEE_STARTED=done ${SHELL_PATH} "$0" "$@" 2>&1;
- echo $? > $BASE.exit) | tee $BASE.out
- test "$(cat $BASE.exit)" = 0
+ echo $? >"$BASE.exit") | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
+ test "$(cat "$BASE.exit")" = 0
exit
;;
esac
trace=t
verbose=t
shift ;;
+ --verbose-log)
+ verbose_log=t
+ shift ;;
*)
echo "error: unknown test option '$1'" >&2; exit 1 ;;
esac
say_color info "$*"
}
+if test -n "$HARNESS_ACTIVE"
+then
+ if test "$verbose" = t || test -n "$verbose_only"
+ then
+ printf 'Bail out! %s\n' \
+ 'verbose mode forbidden under TAP harness; try --verbose-log'
+ exit 1
+ fi
+fi
+
test "${test_description}" != "" ||
error "Test script did not set test_description."
exec 5>&1
exec 6<&0
-if test "$verbose" = "t"
+if test "$verbose_log" = "t"
+then
+ exec 3>>"$GIT_TEST_TEE_OUTPUT_FILE" 4>&3
+elif test "$verbose" = "t"
then
exec 4>&2 3>&1
else
#include "commit.h"
#include "tempfile.h"
#include "trailer.h"
+#include "list.h"
/*
* Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
*/
static struct conf_info default_conf_info;
struct trailer_item {
- struct trailer_item *previous;
- struct trailer_item *next;
- const char *token;
- const char *value;
+ struct list_head list;
+ /*
+ * If this is not a trailer line, the line is stored in value
+ * (excluding the terminating newline) and token is NULL.
+ */
+ char *token;
+ char *value;
+};
+
+struct arg_item {
+ struct list_head list;
+ char *token;
+ char *value;
struct conf_info conf;
};
-static struct trailer_item *first_conf_item;
+static LIST_HEAD(conf_head);
static char *separators = ":";
#define TRAILER_ARG_STRING "$ARG"
+static const char *git_generated_prefixes[] = {
+ "Signed-off-by: ",
+ "(cherry picked from commit ",
+ NULL
+};
+
+/* Iterate over the elements of the list. */
+#define list_for_each_dir(pos, head, is_reverse) \
+ for (pos = is_reverse ? (head)->prev : (head)->next; \
+ pos != (head); \
+ pos = is_reverse ? pos->prev : pos->next)
+
static int after_or_end(enum action_where where)
{
return (where == WHERE_AFTER) || (where == WHERE_END);
return len;
}
-static int same_token(struct trailer_item *a, struct trailer_item *b)
+static int same_token(struct trailer_item *a, struct arg_item *b)
{
- size_t a_len = token_len_without_separator(a->token, strlen(a->token));
- size_t b_len = token_len_without_separator(b->token, strlen(b->token));
- size_t min_len = (a_len > b_len) ? b_len : a_len;
+ size_t a_len, b_len, min_len;
+
+ if (!a->token)
+ return 0;
+
+ a_len = token_len_without_separator(a->token, strlen(a->token));
+ b_len = token_len_without_separator(b->token, strlen(b->token));
+ min_len = (a_len > b_len) ? b_len : a_len;
return !strncasecmp(a->token, b->token, min_len);
}
-static int same_value(struct trailer_item *a, struct trailer_item *b)
+static int same_value(struct trailer_item *a, struct arg_item *b)
{
return !strcasecmp(a->value, b->value);
}
-static int same_trailer(struct trailer_item *a, struct trailer_item *b)
+static int same_trailer(struct trailer_item *a, struct arg_item *b)
{
return same_token(a, b) && same_value(a, b);
}
}
static void free_trailer_item(struct trailer_item *item)
+{
+ free(item->token);
+ free(item->value);
+ free(item);
+}
+
+static void free_arg_item(struct arg_item *item)
{
free(item->conf.name);
free(item->conf.key);
free(item->conf.command);
- free((char *)item->token);
- free((char *)item->value);
+ free(item->token);
+ free(item->value);
free(item);
}
static void print_tok_val(FILE *outfile, const char *tok, const char *val)
{
- char c = last_non_space_char(tok);
+ char c;
+
+ if (!tok) {
+ fprintf(outfile, "%s\n", val);
+ return;
+ }
+
+ c = last_non_space_char(tok);
if (!c)
return;
if (strchr(separators, c))
fprintf(outfile, "%s%c %s\n", tok, separators[0], val);
}
-static void print_all(FILE *outfile, struct trailer_item *first, int trim_empty)
+static void print_all(FILE *outfile, struct list_head *head, int trim_empty)
{
+ struct list_head *pos;
struct trailer_item *item;
- for (item = first; item; item = item->next) {
+ list_for_each(pos, head) {
+ item = list_entry(pos, struct trailer_item, list);
if (!trim_empty || strlen(item->value) > 0)
print_tok_val(outfile, item->token, item->value);
}
}
-static void update_last(struct trailer_item **last)
-{
- if (*last)
- while ((*last)->next != NULL)
- *last = (*last)->next;
-}
-
-static void update_first(struct trailer_item **first)
+static struct trailer_item *trailer_from_arg(struct arg_item *arg_tok)
{
- if (*first)
- while ((*first)->previous != NULL)
- *first = (*first)->previous;
+ struct trailer_item *new = xcalloc(sizeof(*new), 1);
+ new->token = arg_tok->token;
+ new->value = arg_tok->value;
+ arg_tok->token = arg_tok->value = NULL;
+ free_arg_item(arg_tok);
+ return new;
}
static void add_arg_to_input_list(struct trailer_item *on_tok,
- struct trailer_item *arg_tok,
- struct trailer_item **first,
- struct trailer_item **last)
-{
- if (after_or_end(arg_tok->conf.where)) {
- arg_tok->next = on_tok->next;
- on_tok->next = arg_tok;
- arg_tok->previous = on_tok;
- if (arg_tok->next)
- arg_tok->next->previous = arg_tok;
- update_last(last);
- } else {
- arg_tok->previous = on_tok->previous;
- on_tok->previous = arg_tok;
- arg_tok->next = on_tok;
- if (arg_tok->previous)
- arg_tok->previous->next = arg_tok;
- update_first(first);
- }
+ struct arg_item *arg_tok)
+{
+ int aoe = after_or_end(arg_tok->conf.where);
+ struct trailer_item *to_add = trailer_from_arg(arg_tok);
+ if (aoe)
+ list_add(&to_add->list, &on_tok->list);
+ else
+ list_add_tail(&to_add->list, &on_tok->list);
}
static int check_if_different(struct trailer_item *in_tok,
- struct trailer_item *arg_tok,
- int check_all)
+ struct arg_item *arg_tok,
+ int check_all,
+ struct list_head *head)
{
enum action_where where = arg_tok->conf.where;
+ struct list_head *next_head;
do {
- if (!in_tok)
- return 1;
if (same_trailer(in_tok, arg_tok))
return 0;
/*
* if we want to add a trailer after another one,
* we have to check those before this one
*/
- in_tok = after_or_end(where) ? in_tok->previous : in_tok->next;
+ next_head = after_or_end(where) ? in_tok->list.prev
+ : in_tok->list.next;
+ if (next_head == head)
+ break;
+ in_tok = list_entry(next_head, struct trailer_item, list);
} while (check_all);
return 1;
}
-static void remove_from_list(struct trailer_item *item,
- struct trailer_item **first,
- struct trailer_item **last)
-{
- struct trailer_item *next = item->next;
- struct trailer_item *previous = item->previous;
-
- if (next) {
- item->next->previous = previous;
- item->next = NULL;
- } else if (last)
- *last = previous;
-
- if (previous) {
- item->previous->next = next;
- item->previous = NULL;
- } else if (first)
- *first = next;
-}
-
-static struct trailer_item *remove_first(struct trailer_item **first)
-{
- struct trailer_item *item = *first;
- *first = item->next;
- if (item->next) {
- item->next->previous = NULL;
- item->next = NULL;
- }
- return item;
-}
-
-static const char *apply_command(const char *command, const char *arg)
+static char *apply_command(const char *command, const char *arg)
{
struct strbuf cmd = STRBUF_INIT;
struct strbuf buf = STRBUF_INIT;
struct child_process cp = CHILD_PROCESS_INIT;
const char *argv[] = {NULL, NULL};
- const char *result;
+ char *result;
strbuf_addstr(&cmd, command);
if (arg)
return result;
}
-static void apply_item_command(struct trailer_item *in_tok, struct trailer_item *arg_tok)
+static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg_tok)
{
if (arg_tok->conf.command) {
const char *arg;
}
static void apply_arg_if_exists(struct trailer_item *in_tok,
- struct trailer_item *arg_tok,
+ struct arg_item *arg_tok,
struct trailer_item *on_tok,
- struct trailer_item **in_tok_first,
- struct trailer_item **in_tok_last)
+ struct list_head *head)
{
switch (arg_tok->conf.if_exists) {
case EXISTS_DO_NOTHING:
- free_trailer_item(arg_tok);
+ free_arg_item(arg_tok);
break;
case EXISTS_REPLACE:
apply_item_command(in_tok, arg_tok);
- add_arg_to_input_list(on_tok, arg_tok,
- in_tok_first, in_tok_last);
- remove_from_list(in_tok, in_tok_first, in_tok_last);
+ add_arg_to_input_list(on_tok, arg_tok);
+ list_del(&in_tok->list);
free_trailer_item(in_tok);
break;
case EXISTS_ADD:
apply_item_command(in_tok, arg_tok);
- add_arg_to_input_list(on_tok, arg_tok,
- in_tok_first, in_tok_last);
+ add_arg_to_input_list(on_tok, arg_tok);
break;
case EXISTS_ADD_IF_DIFFERENT:
apply_item_command(in_tok, arg_tok);
- if (check_if_different(in_tok, arg_tok, 1))
- add_arg_to_input_list(on_tok, arg_tok,
- in_tok_first, in_tok_last);
+ if (check_if_different(in_tok, arg_tok, 1, head))
+ add_arg_to_input_list(on_tok, arg_tok);
else
- free_trailer_item(arg_tok);
+ free_arg_item(arg_tok);
break;
case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR:
apply_item_command(in_tok, arg_tok);
- if (check_if_different(on_tok, arg_tok, 0))
- add_arg_to_input_list(on_tok, arg_tok,
- in_tok_first, in_tok_last);
+ if (check_if_different(on_tok, arg_tok, 0, head))
+ add_arg_to_input_list(on_tok, arg_tok);
else
- free_trailer_item(arg_tok);
+ free_arg_item(arg_tok);
break;
}
}
-static void apply_arg_if_missing(struct trailer_item **in_tok_first,
- struct trailer_item **in_tok_last,
- struct trailer_item *arg_tok)
+static void apply_arg_if_missing(struct list_head *head,
+ struct arg_item *arg_tok)
{
- struct trailer_item **in_tok;
enum action_where where;
+ struct trailer_item *to_add;
switch (arg_tok->conf.if_missing) {
case MISSING_DO_NOTHING:
- free_trailer_item(arg_tok);
+ free_arg_item(arg_tok);
break;
case MISSING_ADD:
where = arg_tok->conf.where;
- in_tok = after_or_end(where) ? in_tok_last : in_tok_first;
apply_item_command(NULL, arg_tok);
- if (*in_tok) {
- add_arg_to_input_list(*in_tok, arg_tok,
- in_tok_first, in_tok_last);
- } else {
- *in_tok_first = arg_tok;
- *in_tok_last = arg_tok;
- }
- break;
+ to_add = trailer_from_arg(arg_tok);
+ if (after_or_end(where))
+ list_add_tail(&to_add->list, head);
+ else
+ list_add(&to_add->list, head);
}
}
-static int find_same_and_apply_arg(struct trailer_item **in_tok_first,
- struct trailer_item **in_tok_last,
- struct trailer_item *arg_tok)
+static int find_same_and_apply_arg(struct list_head *head,
+ struct arg_item *arg_tok)
{
+ struct list_head *pos;
struct trailer_item *in_tok;
struct trailer_item *on_tok;
- struct trailer_item *following_tok;
enum action_where where = arg_tok->conf.where;
int middle = (where == WHERE_AFTER) || (where == WHERE_BEFORE);
int backwards = after_or_end(where);
- struct trailer_item *start_tok = backwards ? *in_tok_last : *in_tok_first;
+ struct trailer_item *start_tok;
+
+ if (list_empty(head))
+ return 0;
- for (in_tok = start_tok; in_tok; in_tok = following_tok) {
- following_tok = backwards ? in_tok->previous : in_tok->next;
+ start_tok = list_entry(backwards ? head->prev : head->next,
+ struct trailer_item,
+ list);
+
+ list_for_each_dir(pos, head, backwards) {
+ in_tok = list_entry(pos, struct trailer_item, list);
if (!same_token(in_tok, arg_tok))
continue;
on_tok = middle ? in_tok : start_tok;
- apply_arg_if_exists(in_tok, arg_tok, on_tok,
- in_tok_first, in_tok_last);
+ apply_arg_if_exists(in_tok, arg_tok, on_tok, head);
return 1;
}
return 0;
}
-static void process_trailers_lists(struct trailer_item **in_tok_first,
- struct trailer_item **in_tok_last,
- struct trailer_item **arg_tok_first)
+static void process_trailers_lists(struct list_head *head,
+ struct list_head *arg_head)
{
- struct trailer_item *arg_tok;
- struct trailer_item *next_arg;
-
- if (!*arg_tok_first)
- return;
+ struct list_head *pos, *p;
+ struct arg_item *arg_tok;
- for (arg_tok = *arg_tok_first; arg_tok; arg_tok = next_arg) {
+ list_for_each_safe(pos, p, arg_head) {
int applied = 0;
+ arg_tok = list_entry(pos, struct arg_item, list);
- next_arg = arg_tok->next;
- remove_from_list(arg_tok, arg_tok_first, NULL);
+ list_del(pos);
- applied = find_same_and_apply_arg(in_tok_first,
- in_tok_last,
- arg_tok);
+ applied = find_same_and_apply_arg(head, arg_tok);
if (!applied)
- apply_arg_if_missing(in_tok_first,
- in_tok_last,
- arg_tok);
+ apply_arg_if_missing(head, arg_tok);
}
}
return 0;
}
-static void duplicate_conf(struct conf_info *dst, struct conf_info *src)
+static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)
{
*dst = *src;
- if (src->name)
- dst->name = xstrdup(src->name);
- if (src->key)
- dst->key = xstrdup(src->key);
- if (src->command)
- dst->command = xstrdup(src->command);
+ dst->name = xstrdup_or_null(src->name);
+ dst->key = xstrdup_or_null(src->key);
+ dst->command = xstrdup_or_null(src->command);
}
-static struct trailer_item *get_conf_item(const char *name)
+static struct arg_item *get_conf_item(const char *name)
{
- struct trailer_item *item;
- struct trailer_item *previous;
+ struct list_head *pos;
+ struct arg_item *item;
/* Look up item with same name */
- for (previous = NULL, item = first_conf_item;
- item;
- previous = item, item = item->next) {
+ list_for_each(pos, &conf_head) {
+ item = list_entry(pos, struct arg_item, list);
if (!strcasecmp(item->conf.name, name))
return item;
}
/* Item does not already exists, create it */
- item = xcalloc(sizeof(struct trailer_item), 1);
+ item = xcalloc(sizeof(*item), 1);
duplicate_conf(&item->conf, &default_conf_info);
item->conf.name = xstrdup(name);
- if (!previous)
- first_conf_item = item;
- else {
- previous->next = item;
- item->previous = previous;
- }
+ list_add_tail(&item->list, &conf_head);
return item;
}
static int git_trailer_config(const char *conf_key, const char *value, void *cb)
{
const char *trailer_item, *variable_name;
- struct trailer_item *item;
+ struct arg_item *item;
struct conf_info *conf;
char *name = NULL;
enum trailer_info_type type;
return 0;
}
-static int parse_trailer(struct strbuf *tok, struct strbuf *val, const char *trailer)
-{
- size_t len;
- struct strbuf seps = STRBUF_INIT;
- strbuf_addstr(&seps, separators);
- strbuf_addch(&seps, '=');
- len = strcspn(trailer, seps.buf);
- strbuf_release(&seps);
- if (len == 0) {
- int l = strlen(trailer);
- while (l > 0 && isspace(trailer[l - 1]))
- l--;
- return error(_("empty trailer token in trailer '%.*s'"), l, trailer);
- }
- if (len < strlen(trailer)) {
- strbuf_add(tok, trailer, len);
- strbuf_trim(tok);
- strbuf_addstr(val, trailer + len + 1);
- strbuf_trim(val);
- } else {
- strbuf_addstr(tok, trailer);
- strbuf_trim(tok);
- }
- return 0;
-}
-
-static const char *token_from_item(struct trailer_item *item, char *tok)
+static const char *token_from_item(struct arg_item *item, char *tok)
{
if (item->conf.key)
return item->conf.key;
return item->conf.name;
}
-static struct trailer_item *new_trailer_item(struct trailer_item *conf_item,
- char *tok, char *val)
-{
- struct trailer_item *new = xcalloc(sizeof(*new), 1);
- new->value = val ? val : xstrdup("");
-
- if (conf_item) {
- duplicate_conf(&new->conf, &conf_item->conf);
- new->token = xstrdup(token_from_item(conf_item, tok));
- free(tok);
- } else {
- duplicate_conf(&new->conf, &default_conf_info);
- new->token = tok;
- }
-
- return new;
-}
-
-static int token_matches_item(const char *tok, struct trailer_item *item, int tok_len)
+static int token_matches_item(const char *tok, struct arg_item *item, int tok_len)
{
if (!strncasecmp(tok, item->conf.name, tok_len))
return 1;
return item->conf.key ? !strncasecmp(tok, item->conf.key, tok_len) : 0;
}
-static struct trailer_item *create_trailer_item(const char *string)
+/*
+ * Return the location of the first separator in line, or -1 if there is no
+ * separator.
+ */
+static int find_separator(const char *line, const char *separators)
{
- struct strbuf tok = STRBUF_INIT;
- struct strbuf val = STRBUF_INIT;
- struct trailer_item *item;
- int tok_len;
+ int loc = strcspn(line, separators);
+ if (!line[loc])
+ return -1;
+ return loc;
+}
- if (parse_trailer(&tok, &val, string))
- return NULL;
+/*
+ * Obtain the token, value, and conf from the given trailer.
+ *
+ * separator_pos must not be 0, since the token cannot be an empty string.
+ *
+ * If separator_pos is -1, interpret the whole trailer as a token.
+ */
+static void parse_trailer(struct strbuf *tok, struct strbuf *val,
+ const struct conf_info **conf, const char *trailer,
+ int separator_pos)
+{
+ struct arg_item *item;
+ int tok_len;
+ struct list_head *pos;
- tok_len = token_len_without_separator(tok.buf, tok.len);
+ if (separator_pos != -1) {
+ strbuf_add(tok, trailer, separator_pos);
+ strbuf_trim(tok);
+ strbuf_addstr(val, trailer + separator_pos + 1);
+ strbuf_trim(val);
+ } else {
+ strbuf_addstr(tok, trailer);
+ strbuf_trim(tok);
+ }
/* Lookup if the token matches something in the config */
- for (item = first_conf_item; item; item = item->next) {
- if (token_matches_item(tok.buf, item, tok_len))
- return new_trailer_item(item,
- strbuf_detach(&tok, NULL),
- strbuf_detach(&val, NULL));
+ tok_len = token_len_without_separator(tok->buf, tok->len);
+ if (conf)
+ *conf = &default_conf_info;
+ list_for_each(pos, &conf_head) {
+ item = list_entry(pos, struct arg_item, list);
+ if (token_matches_item(tok->buf, item, tok_len)) {
+ char *tok_buf = strbuf_detach(tok, NULL);
+ if (conf)
+ *conf = &item->conf;
+ strbuf_addstr(tok, token_from_item(item, tok_buf));
+ free(tok_buf);
+ break;
+ }
}
+}
- return new_trailer_item(NULL,
- strbuf_detach(&tok, NULL),
- strbuf_detach(&val, NULL));
+static struct trailer_item *add_trailer_item(struct list_head *head, char *tok,
+ char *val)
+{
+ struct trailer_item *new = xcalloc(sizeof(*new), 1);
+ new->token = tok;
+ new->value = val;
+ list_add_tail(&new->list, head);
+ return new;
}
-static void add_trailer_item(struct trailer_item **first,
- struct trailer_item **last,
- struct trailer_item *new)
+static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
+ const struct conf_info *conf)
{
- if (!new)
- return;
- if (!*last) {
- *first = new;
- *last = new;
- } else {
- (*last)->next = new;
- new->previous = *last;
- *last = new;
- }
+ struct arg_item *new = xcalloc(sizeof(*new), 1);
+ new->token = tok;
+ new->value = val;
+ duplicate_conf(&new->conf, conf);
+ list_add_tail(&new->list, arg_head);
}
-static struct trailer_item *process_command_line_args(struct string_list *trailers)
+static void process_command_line_args(struct list_head *arg_head,
+ struct string_list *trailers)
{
- struct trailer_item *arg_tok_first = NULL;
- struct trailer_item *arg_tok_last = NULL;
struct string_list_item *tr;
- struct trailer_item *item;
+ struct arg_item *item;
+ struct strbuf tok = STRBUF_INIT;
+ struct strbuf val = STRBUF_INIT;
+ const struct conf_info *conf;
+ struct list_head *pos;
- /* Add a trailer item for each configured trailer with a command */
- for (item = first_conf_item; item; item = item->next) {
- if (item->conf.command) {
- struct trailer_item *new = new_trailer_item(item, NULL, NULL);
- add_trailer_item(&arg_tok_first, &arg_tok_last, new);
- }
+ /*
+ * In command-line arguments, '=' is accepted (in addition to the
+ * separators that are defined).
+ */
+ char *cl_separators = xstrfmt("=%s", separators);
+
+ /* Add an arg item for each configured trailer with a command */
+ list_for_each(pos, &conf_head) {
+ item = list_entry(pos, struct arg_item, list);
+ if (item->conf.command)
+ add_arg_item(arg_head,
+ xstrdup(token_from_item(item, NULL)),
+ xstrdup(""),
+ &item->conf);
}
- /* Add a trailer item for each trailer on the command line */
+ /* Add an arg item for each trailer on the command line */
for_each_string_list_item(tr, trailers) {
- struct trailer_item *new = create_trailer_item(tr->string);
- add_trailer_item(&arg_tok_first, &arg_tok_last, new);
+ int separator_pos = find_separator(tr->string, cl_separators);
+ if (separator_pos == 0) {
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_addstr(&sb, tr->string);
+ strbuf_trim(&sb);
+ error(_("empty trailer token in trailer '%.*s'"),
+ (int) sb.len, sb.buf);
+ strbuf_release(&sb);
+ } else {
+ parse_trailer(&tok, &val, &conf, tr->string,
+ separator_pos);
+ add_arg_item(arg_head,
+ strbuf_detach(&tok, NULL),
+ strbuf_detach(&val, NULL),
+ conf);
+ }
}
- return arg_tok_first;
+ free(cl_separators);
}
static struct strbuf **read_input_file(const char *file)
static int find_trailer_start(struct strbuf **lines, int count)
{
int start, end_of_title, only_spaces = 1;
+ int recognized_prefix = 0, trailer_lines = 0, non_trailer_lines = 0;
+ /*
+ * Number of possible continuation lines encountered. This will be
+ * reset to 0 if we encounter a trailer (since those lines are to be
+ * considered continuations of that trailer), and added to
+ * non_trailer_lines if we encounter a non-trailer (since those lines
+ * are to be considered non-trailers).
+ */
+ int possible_continuation_lines = 0;
/* The first paragraph is the title and cannot be trailers */
for (start = 0; start < count; start++) {
end_of_title = start;
/*
- * Get the start of the trailers by looking starting from the end
- * for a line with only spaces before lines with one separator.
+ * Get the start of the trailers by looking starting from the end for a
+ * blank line before a set of non-blank lines that (i) are all
+ * trailers, or (ii) contains at least one Git-generated trailer and
+ * consists of at least 25% trailers.
*/
for (start = count - 1; start >= end_of_title; start--) {
- if (lines[start]->buf[0] == comment_line_char)
+ const char **p;
+ int separator_pos;
+
+ if (lines[start]->buf[0] == comment_line_char) {
+ non_trailer_lines += possible_continuation_lines;
+ possible_continuation_lines = 0;
continue;
+ }
if (contains_only_spaces(lines[start]->buf)) {
if (only_spaces)
continue;
- return start + 1;
+ non_trailer_lines += possible_continuation_lines;
+ if (recognized_prefix &&
+ trailer_lines * 3 >= non_trailer_lines)
+ return start + 1;
+ if (trailer_lines && !non_trailer_lines)
+ return start + 1;
+ return count;
}
- if (strcspn(lines[start]->buf, separators) < lines[start]->len) {
- if (only_spaces)
- only_spaces = 0;
- continue;
+ only_spaces = 0;
+
+ for (p = git_generated_prefixes; *p; p++) {
+ if (starts_with(lines[start]->buf, *p)) {
+ trailer_lines++;
+ possible_continuation_lines = 0;
+ recognized_prefix = 1;
+ goto continue_outer_loop;
+ }
+ }
+
+ separator_pos = find_separator(lines[start]->buf, separators);
+ if (separator_pos >= 1 && !isspace(lines[start]->buf[0])) {
+ struct list_head *pos;
+
+ trailer_lines++;
+ possible_continuation_lines = 0;
+ if (recognized_prefix)
+ continue;
+ list_for_each(pos, &conf_head) {
+ struct arg_item *item;
+ item = list_entry(pos, struct arg_item, list);
+ if (token_matches_item(lines[start]->buf, item,
+ separator_pos)) {
+ recognized_prefix = 1;
+ break;
+ }
+ }
+ } else if (isspace(lines[start]->buf[0]))
+ possible_continuation_lines++;
+ else {
+ non_trailer_lines++;
+ non_trailer_lines += possible_continuation_lines;
+ possible_continuation_lines = 0;
}
- return count;
+continue_outer_loop:
+ ;
}
- return only_spaces ? count : 0;
+ return count;
}
/* Get the index of the end of the trailers */
static int process_input_file(FILE *outfile,
struct strbuf **lines,
- struct trailer_item **in_tok_first,
- struct trailer_item **in_tok_last)
+ struct list_head *head)
{
int count = 0;
int patch_start, trailer_start, trailer_end, i;
+ struct strbuf tok = STRBUF_INIT;
+ struct strbuf val = STRBUF_INIT;
+ struct trailer_item *last = NULL;
/* Get the line count */
while (lines[count])
/* Parse trailer lines */
for (i = trailer_start; i < trailer_end; i++) {
- if (lines[i]->buf[0] != comment_line_char) {
- struct trailer_item *new = create_trailer_item(lines[i]->buf);
- add_trailer_item(in_tok_first, in_tok_last, new);
+ int separator_pos;
+ if (lines[i]->buf[0] == comment_line_char)
+ continue;
+ if (last && isspace(lines[i]->buf[0])) {
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_addf(&sb, "%s\n%s", last->value, lines[i]->buf);
+ strbuf_strip_suffix(&sb, "\n");
+ free(last->value);
+ last->value = strbuf_detach(&sb, NULL);
+ continue;
+ }
+ separator_pos = find_separator(lines[i]->buf, separators);
+ if (separator_pos >= 1) {
+ parse_trailer(&tok, &val, NULL, lines[i]->buf,
+ separator_pos);
+ last = add_trailer_item(head,
+ strbuf_detach(&tok, NULL),
+ strbuf_detach(&val, NULL));
+ } else {
+ strbuf_addbuf(&val, lines[i]);
+ strbuf_strip_suffix(&val, "\n");
+ add_trailer_item(head,
+ NULL,
+ strbuf_detach(&val, NULL));
+ last = NULL;
}
}
return trailer_end;
}
-static void free_all(struct trailer_item **first)
+static void free_all(struct list_head *head)
{
- while (*first) {
- struct trailer_item *item = remove_first(first);
- free_trailer_item(item);
+ struct list_head *pos, *p;
+ list_for_each_safe(pos, p, head) {
+ list_del(pos);
+ free_trailer_item(list_entry(pos, struct trailer_item, list));
}
}
void process_trailers(const char *file, int in_place, int trim_empty, struct string_list *trailers)
{
- struct trailer_item *in_tok_first = NULL;
- struct trailer_item *in_tok_last = NULL;
- struct trailer_item *arg_tok_first;
+ LIST_HEAD(head);
+ LIST_HEAD(arg_head);
struct strbuf **lines;
int trailer_end;
FILE *outfile = stdout;
outfile = create_in_place_tempfile(file);
/* Print the lines before the trailers */
- trailer_end = process_input_file(outfile, lines, &in_tok_first, &in_tok_last);
+ trailer_end = process_input_file(outfile, lines, &head);
- arg_tok_first = process_command_line_args(trailers);
+ process_command_line_args(&arg_head, trailers);
- process_trailers_lists(&in_tok_first, &in_tok_last, &arg_tok_first);
+ process_trailers_lists(&head, &arg_head);
- print_all(outfile, in_tok_first, trim_empty);
+ print_all(outfile, &head, trim_empty);
- free_all(&in_tok_first);
+ free_all(&head);
/* Print the lines after the trailers as is */
print_lines(outfile, lines, trailer_end, INT_MAX);
}
}
-static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg, int porcelain)
+static void print_ref_status(char flag, const char *summary,
+ struct ref *to, struct ref *from, const char *msg,
+ int porcelain, int summary_width)
{
if (porcelain) {
if (from)
else
fprintf(stdout, "%s\n", summary);
} else {
- fprintf(stderr, " %c %-*s ", flag, TRANSPORT_SUMMARY_WIDTH, summary);
+ fprintf(stderr, " %c %-*s ", flag, summary_width, summary);
if (from)
fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name));
else
}
}
-static void print_ok_ref_status(struct ref *ref, int porcelain)
+static void print_ok_ref_status(struct ref *ref, int porcelain, int summary_width)
{
if (ref->deletion)
- print_ref_status('-', "[deleted]", ref, NULL, NULL, porcelain);
+ print_ref_status('-', "[deleted]", ref, NULL, NULL,
+ porcelain, summary_width);
else if (is_null_oid(&ref->old_oid))
print_ref_status('*',
(starts_with(ref->name, "refs/tags/") ? "[new tag]" :
"[new branch]"),
- ref, ref->peer_ref, NULL, porcelain);
+ ref, ref->peer_ref, NULL, porcelain, summary_width);
else {
struct strbuf quickref = STRBUF_INIT;
char type;
strbuf_add_unique_abbrev(&quickref, ref->new_oid.hash,
DEFAULT_ABBREV);
- print_ref_status(type, quickref.buf, ref, ref->peer_ref, msg, porcelain);
+ print_ref_status(type, quickref.buf, ref, ref->peer_ref, msg,
+ porcelain, summary_width);
strbuf_release(&quickref);
}
}
-static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain)
+static int print_one_push_status(struct ref *ref, const char *dest, int count,
+ int porcelain, int summary_width)
{
if (!count) {
char *url = transport_anonymize_url(dest);
switch(ref->status) {
case REF_STATUS_NONE:
- print_ref_status('X', "[no match]", ref, NULL, NULL, porcelain);
+ print_ref_status('X', "[no match]", ref, NULL, NULL,
+ porcelain, summary_width);
break;
case REF_STATUS_REJECT_NODELETE:
print_ref_status('!', "[rejected]", ref, NULL,
- "remote does not support deleting refs", porcelain);
+ "remote does not support deleting refs",
+ porcelain, summary_width);
break;
case REF_STATUS_UPTODATE:
print_ref_status('=', "[up to date]", ref,
- ref->peer_ref, NULL, porcelain);
+ ref->peer_ref, NULL, porcelain, summary_width);
break;
case REF_STATUS_REJECT_NONFASTFORWARD:
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
- "non-fast-forward", porcelain);
+ "non-fast-forward", porcelain, summary_width);
break;
case REF_STATUS_REJECT_ALREADY_EXISTS:
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
- "already exists", porcelain);
+ "already exists", porcelain, summary_width);
break;
case REF_STATUS_REJECT_FETCH_FIRST:
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
- "fetch first", porcelain);
+ "fetch first", porcelain, summary_width);
break;
case REF_STATUS_REJECT_NEEDS_FORCE:
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
- "needs force", porcelain);
+ "needs force", porcelain, summary_width);
break;
case REF_STATUS_REJECT_STALE:
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
- "stale info", porcelain);
+ "stale info", porcelain, summary_width);
break;
case REF_STATUS_REJECT_SHALLOW:
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
- "new shallow roots not allowed", porcelain);
+ "new shallow roots not allowed",
+ porcelain, summary_width);
break;
case REF_STATUS_REMOTE_REJECT:
print_ref_status('!', "[remote rejected]", ref,
- ref->deletion ? NULL : ref->peer_ref,
- ref->remote_status, porcelain);
+ ref->deletion ? NULL : ref->peer_ref,
+ ref->remote_status, porcelain, summary_width);
break;
case REF_STATUS_EXPECTING_REPORT:
print_ref_status('!', "[remote failure]", ref,
- ref->deletion ? NULL : ref->peer_ref,
- "remote failed to report status", porcelain);
+ ref->deletion ? NULL : ref->peer_ref,
+ "remote failed to report status",
+ porcelain, summary_width);
break;
case REF_STATUS_ATOMIC_PUSH_FAILED:
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
- "atomic push failed", porcelain);
+ "atomic push failed", porcelain, summary_width);
break;
case REF_STATUS_OK:
- print_ok_ref_status(ref, porcelain);
+ print_ok_ref_status(ref, porcelain, summary_width);
break;
}
return 1;
}
+static int measure_abbrev(const struct object_id *oid, int sofar)
+{
+ char hex[GIT_SHA1_HEXSZ + 1];
+ int w = find_unique_abbrev_r(hex, oid->hash, DEFAULT_ABBREV);
+
+ return (w < sofar) ? sofar : w;
+}
+
+int transport_summary_width(const struct ref *refs)
+{
+ int maxw = -1;
+
+ for (; refs; refs = refs->next) {
+ maxw = measure_abbrev(&refs->old_oid, maxw);
+ maxw = measure_abbrev(&refs->new_oid, maxw);
+ }
+ if (maxw < 0)
+ maxw = FALLBACK_DEFAULT_ABBREV;
+ return (2 * maxw + 3);
+}
+
void transport_print_push_status(const char *dest, struct ref *refs,
int verbose, int porcelain, unsigned int *reject_reasons)
{
int n = 0;
unsigned char head_sha1[20];
char *head;
+ int summary_width = transport_summary_width(refs);
head = resolve_refdup("HEAD", RESOLVE_REF_READING, head_sha1, NULL);
if (verbose) {
for (ref = refs; ref; ref = ref->next)
if (ref->status == REF_STATUS_UPTODATE)
- n += print_one_push_status(ref, dest, n, porcelain);
+ n += print_one_push_status(ref, dest, n,
+ porcelain, summary_width);
}
for (ref = refs; ref; ref = ref->next)
if (ref->status == REF_STATUS_OK)
- n += print_one_push_status(ref, dest, n, porcelain);
+ n += print_one_push_status(ref, dest, n,
+ porcelain, summary_width);
*reject_reasons = 0;
for (ref = refs; ref; ref = ref->next) {
if (ref->status != REF_STATUS_NONE &&
ref->status != REF_STATUS_UPTODATE &&
ref->status != REF_STATUS_OK)
- n += print_one_push_status(ref, dest, n, porcelain);
+ n += print_one_push_status(ref, dest, n,
+ porcelain, summary_width);
if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) {
if (head != NULL && !strcmp(head, ref->name))
*reject_reasons |= REJECT_NON_FF_HEAD;
#define TRANSPORT_PUSH_ATOMIC 8192
#define TRANSPORT_PUSH_OPTIONS 16384
-#define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
-#define TRANSPORT_SUMMARY(x) (int)(TRANSPORT_SUMMARY_WIDTH + strlen(x) - gettext_width(x)), (x)
+extern int transport_summary_width(const struct ref *refs);
/* Returns a transport suitable for the url */
struct transport *transport_get(struct remote *, const char *);
#include "string-list.h"
#include "parse-options.h"
#include "argv-array.h"
+#include "prio-queue.h"
static const char * const upload_pack_usage[] = {
N_("git upload-pack [<options>] <dir>"),
static int reachable(struct commit *want)
{
- struct commit_list *work = NULL;
+ struct prio_queue work = { compare_commits_by_commit_date };
- commit_list_insert_by_date(want, &work);
- while (work) {
+ prio_queue_put(&work, want);
+ while (work.nr) {
struct commit_list *list;
- struct commit *commit = pop_commit(&work);
+ struct commit *commit = prio_queue_get(&work);
if (commit->object.flags & THEY_HAVE) {
want->object.flags |= COMMON_KNOWN;
for (list = commit->parents; list; list = list->next) {
struct commit *parent = list->item;
if (!(parent->object.flags & REACHABLE))
- commit_list_insert_by_date(parent, &work);
+ prio_queue_put(&work, parent);
}
}
want->object.flags |= REACHABLE;
clear_commit_marks(want, REACHABLE);
- free_commit_list(work);
+ clear_prio_queue(&work);
return (want->object.flags & COMMON_KNOWN);
}
if (multi_ack == 2 && got_common
&& !got_other && ok_to_give_up()) {
sent_ready = 1;
- packet_write(1, "ACK %s ready\n", last_hex);
+ packet_write_fmt(1, "ACK %s ready\n", last_hex);
}
if (have_obj.nr == 0 || multi_ack)
- packet_write(1, "NAK\n");
+ packet_write_fmt(1, "NAK\n");
if (no_done && sent_ready) {
- packet_write(1, "ACK %s\n", last_hex);
+ packet_write_fmt(1, "ACK %s\n", last_hex);
return 0;
}
if (stateless_rpc)
const char *hex = sha1_to_hex(sha1);
if (multi_ack == 2) {
sent_ready = 1;
- packet_write(1, "ACK %s ready\n", hex);
+ packet_write_fmt(1, "ACK %s ready\n", hex);
} else
- packet_write(1, "ACK %s continue\n", hex);
+ packet_write_fmt(1, "ACK %s continue\n", hex);
}
break;
default:
got_common = 1;
memcpy(last_hex, sha1_to_hex(sha1), 41);
if (multi_ack == 2)
- packet_write(1, "ACK %s common\n", last_hex);
+ packet_write_fmt(1, "ACK %s common\n", last_hex);
else if (multi_ack)
- packet_write(1, "ACK %s continue\n", last_hex);
+ packet_write_fmt(1, "ACK %s continue\n", last_hex);
else if (have_obj.nr == 1)
- packet_write(1, "ACK %s\n", last_hex);
+ packet_write_fmt(1, "ACK %s\n", last_hex);
break;
}
continue;
if (!strcmp(line, "done")) {
if (have_obj.nr > 0) {
if (multi_ack)
- packet_write(1, "ACK %s\n", last_hex);
+ packet_write_fmt(1, "ACK %s\n", last_hex);
return 0;
}
- packet_write(1, "NAK\n");
+ packet_write_fmt(1, "NAK\n");
return -1;
}
die("git upload-pack: expected SHA1 list, got '%s'", line);
while (result) {
struct object *object = &result->item->object;
if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {
- packet_write(1, "shallow %s",
- oid_to_hex(&object->oid));
+ packet_write_fmt(1, "shallow %s",
+ oid_to_hex(&object->oid));
register_shallow(object->oid.hash);
shallow_nr++;
}
struct object *object = shallows->objects[i].item;
if (object->flags & NOT_SHALLOW) {
struct commit_list *parents;
- packet_write(1, "unshallow %s",
- oid_to_hex(&object->oid));
+ packet_write_fmt(1, "unshallow %s",
+ oid_to_hex(&object->oid));
object->flags &= ~CLIENT_SHALLOW;
/*
* We want to _register_ "object" as shallow, but we
struct strbuf symref_info = STRBUF_INIT;
format_symref_info(&symref_info, cb_data);
- packet_write(1, "%s %s%c%s%s%s%s%s agent=%s\n",
+ packet_write_fmt(1, "%s %s%c%s%s%s%s%s agent=%s\n",
oid_to_hex(oid), refname_nons,
0, capabilities,
(allow_unadvertised_object_request & ALLOW_TIP_SHA1) ?
git_user_agent_sanitized());
strbuf_release(&symref_info);
} else {
- packet_write(1, "%s %s\n", oid_to_hex(oid), refname_nons);
+ packet_write_fmt(1, "%s %s\n", oid_to_hex(oid), refname_nons);
}
capabilities = NULL;
if (!peel_ref(refname, peeled.hash))
- packet_write(1, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
+ packet_write_fmt(1, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
return 0;
}
for (i = 0; worktrees[i]; i++) {
struct worktree *wt = worktrees[i];
+ if (wt->is_bare)
+ continue;
if (wt->is_detached && !strcmp(symref, "HEAD")) {
if (is_worktree_being_rebased(wt, target)) {
#include "cache.h"
#include "run-command.h"
-static void check_pipe(int err)
-{
- if (err == EPIPE) {
- if (in_async())
- async_exit(141);
-
- signal(SIGPIPE, SIG_DFL);
- raise(SIGPIPE);
- /* Should never happen, but just in case... */
- exit(141);
- }
-}
-
/*
* Some cases use stdio, but want to flush after the write
* to get error handling (and to get better interactive
#include "strbuf.h"
#include "utf8.h"
#include "worktree.h"
+#include "lockfile.h"
static const char cut_line[] =
"------------------------ >8 ------------------------\n";
switch (p->status) {
case DIFF_STATUS_ADDED:
- die("BUG: worktree status add???");
+ d->mode_worktree = p->two->mode;
break;
case DIFF_STATUS_DELETED:
setup_revisions(0, NULL, &rev, NULL);
rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
DIFF_OPT_SET(&rev.diffopt, DIRTY_SUBMODULES);
+ rev.diffopt.ita_invisible_in_index = 1;
if (!s->show_untracked_files)
DIFF_OPT_SET(&rev.diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
if (s->ignore_submodule_arg) {
setup_revisions(0, NULL, &rev, &opt);
DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
+ rev.diffopt.ita_invisible_in_index = 1;
if (s->ignore_submodule_arg) {
handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
} else {
if (!ce_path_match(ce, &s->pathspec, NULL))
continue;
+ if (ce_intent_to_add(ce))
+ continue;
it = string_list_insert(&s->change, ce->name);
d = it->util;
if (!d) {
init_revisions(&rev, NULL);
DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
+ rev.diffopt.ita_invisible_in_index = 1;
memset(&opt, 0, sizeof(opt));
opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
break;
}
}
+
+/**
+ * Returns 1 if there are unstaged changes, 0 otherwise.
+ */
+int has_unstaged_changes(int ignore_submodules)
+{
+ struct rev_info rev_info;
+ int result;
+
+ init_revisions(&rev_info, NULL);
+ if (ignore_submodules)
+ 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.
+ */
+int has_uncommitted_changes(int ignore_submodules)
+{
+ struct rev_info rev_info;
+ int result;
+
+ if (is_cache_unborn())
+ return 0;
+
+ init_revisions(&rev_info, NULL);
+ if (ignore_submodules)
+ 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.
+ */
+int require_clean_work_tree(const char *action, const char *hint, int ignore_submodules, int gently)
+{
+ struct lock_file *lock_file = xcalloc(1, sizeof(*lock_file));
+ int err = 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(ignore_submodules)) {
+ /* TRANSLATORS: the action is e.g. "pull with rebase" */
+ error(_("cannot %s: You have unstaged changes."), _(action));
+ err = 1;
+ }
+
+ if (has_uncommitted_changes(ignore_submodules)) {
+ if (err)
+ error(_("additionally, your index contains uncommitted changes."));
+ else
+ error(_("cannot %s: Your index contains uncommitted changes."),
+ _(action));
+ err = 1;
+ }
+
+ if (err) {
+ if (hint)
+ error("%s", hint);
+ if (!gently)
+ exit(128);
+ }
+
+ return err;
+}
__attribute__((format (printf, 3, 4)))
void status_printf(struct wt_status *s, const char *color, const char *fmt, ...);
+/* The following functions expect that the caller took care of reading the index. */
+int has_unstaged_changes(int ignore_submodules);
+int has_uncommitted_changes(int ignore_submodules);
+int require_clean_work_tree(const char *action, const char *hint,
+ int ignore_submodules, int gently);
+
#endif /* STATUS_H */